+ver 5.28:
+ Fix issue with GATT device discovery and probing.
+ Fix issue with bearer selection for dual-mode devices.
+ Fix issue with device removal while connected.
+ Fix issue with device name setting from inquiry response.
+ Fix issue with missing termination of name characteristic.
+ Fix issue with UTF-8 length handling for device name.
+ Fix issue with AVCTP key auto release handling.
+ Fix issue with AVCTP key press repetition handling.
+ Fix issue with payload sizes and GATT notifications.
+ Fix issue with memory corruption and GATT notifications.
+ Add support for HID proxy switching and CSR 8510 A10 devices.
+ Add support for Broadcom hex2hcd conversion utility.
+
ver 5.27:
Fix issue with endian handling and management interface.
Fix issue with pending GATT operations when disconnecting.
if SYSTEMD
systemdsystemunitdir = @SYSTEMD_SYSTEMUNITDIR@
-#if USBBT
-#systemdsystemunit_DATA = src/common/bluetooth.service
-#else
systemdsystemunit_DATA = src/bluetooth.service
-#endif
dbussystembusdir = @DBUS_SYSTEMBUSDIR@
dbussystembus_DATA = src/org.bluez.service
endif
-if USBBT
-#EXTRA_DIST += src/common/bluetooth.service.in src/org.bluez.service
-else
EXTRA_DIST += src/bluetooth.service.in src/org.bluez.service
-endif
plugindir = $(libdir)/bluetooth/plugins
lib_LTLIBRARIES += lib/libbluetooth.la
lib_libbluetooth_la_SOURCES = $(lib_headers) $(lib_sources)
-lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 21:0:18
+lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 21:1:18
lib_libbluetooth_la_DEPENDENCIES = $(local_headers)
endif
src_libshared_mainloop_la_SOURCES = $(shared_sources) \
src/shared/io-mainloop.c \
src/shared/timeout-mainloop.c \
- monitor/mainloop.h monitor/mainloop.c
+ src/shared/mainloop.h src/shared/mainloop.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
+ attrib/gattrib.h attrib/gattrib.c \
+ attrib/gatt-service.h attrib/gatt-service.c
btio_sources = btio/btio.h btio/btio.c
src/sdpd-server.c src/sdpd-request.c \
src/sdpd-service.c src/sdpd-database.c \
src/attrib-server.h src/attrib-server.c \
+ src/gatt-database.h src/gatt-database.c \
src/sdp-xml.h src/sdp-xml.c \
src/sdp-client.h src/sdp-client.c \
src/textfile.h src/textfile.c \
src/agent.h src/agent.c \
src/error.h src/error.c \
src/adapter.h src/adapter.c \
+ src/adapter_le_vsc_features.h src/adapter_le_vsc_features.c \
src/profile.h src/profile.c \
src/service.h src/service.c \
src/gatt-dbus.h src/gatt-dbus.c \
src_bluetoothd_LDFLAGS = $(AM_LDFLAGS) -Wl,--export-dynamic \
-Wl,--version-script=$(srcdir)/src/bluetooth.ver
-if USBBT
-src_bluetoothd_DEPENDENCIES = lib/libbluetooth-internal.la \
- gdbus/libgdbus-internal.la \
- src/libshared-glib.la \
- src/common/bluetooth.service
-else
src_bluetoothd_DEPENDENCIES = lib/libbluetooth-internal.la \
gdbus/libgdbus-internal.la \
src/libshared-glib.la \
src/bluetooth.service
-endif
src_bluetoothd_CFLAGS = $(AM_CFLAGS) -DBLUETOOTH_PLUGIN_BUILTIN \
-DPLUGINDIR=\""$(build_plugindir)"\"
AM_CFLAGS += @DBUS_CFLAGS@ @GLIB_CFLAGS@
-AM_CPPFLAGS = -I$(builddir)/lib -I$(srcdir)/gdbus
+AM_CPPFLAGS = -I$(builddir)/lib
unit_tests += unit/test-eir
dbussessionbus_DATA = obexd/src/org.bluez.obex.service
endif
-# Start of __TIZEN_PATCH__
-
-# Even though systemd is not used,
-# obexd should be launched using dbus auto activation.
-dbussessionbusdir = @DBUS_SESSIONBUSDIR@
-dbussessionbus_DATA = obexd/src/org.bluez.obex.service
-
-# End of __TIZEN_PATCH__
-
EXTRA_DIST += obexd/src/obex.service.in obexd/src/org.bluez.obex.service
obex_plugindir = $(libdir)/obex/plugins
obexd_src_obexd_CFLAGS = $(AM_CFLAGS) @GLIB_CFLAGS@ @DBUS_CFLAGS@ \
@ICAL_CFLAGS@ -DOBEX_PLUGIN_BUILTIN \
-DPLUGINDIR=\""$(obex_plugindir)"\" \
- -fPIC -D_FILE_OFFSET_BITS=64
+ -fPIC -D_FILE_OFFSET_BITS=64 -pie
obexd_src_obexd_CPPFLAGS = -I$(builddir)/lib -I$(builddir)/obexd/src \
-I$(srcdir)/obexd/src -I$(srcdir)/btio \
if TIZEN_UNUSED_PLUGIN
builtin_modules += neard
builtin_sources += plugins/neard.c
+endif
+if TIZEN_SAP_PLUGIN
builtin_modules += sap
builtin_sources += profiles/sap/main.c profiles/sap/manager.h \
profiles/sap/manager.c profiles/sap/server.h \
profiles/sap/server.c profiles/sap/sap.h \
- profiles/sap/sap-dummy.c
+ profiles/sap/sap-u8500.c
-noinst_LIBRARIES += profiles/sap/libsap.a
-profiles_sap_libsap_a_SOURCES = profiles/sap/sap.h profiles/sap/sap-u8500.c
+#noinst_LIBRARIES += profiles/sap/libsap.a
+#profiles_sap_libsap_a_SOURCES = profiles/sap/sap.h profiles/sap/sap-u8500
endif
endif
endif
if EXPERIMENTAL
-if TIZEN_UNUSED_PLUGIN
+if TIZEN_HEALTH_PLUGIN
builtin_modules += health
builtin_sources += profiles/health/mcap.h profiles/health/mcap.c \
profiles/health/hdp_main.c profiles/health/hdp_types.h \
-no-undefined @UDEV_LIBS@
plugins_sixaxis_la_CFLAGS = $(AM_CFLAGS) -fvisibility=hidden @UDEV_CFLAGS@
endif
-
-if SERVICE
-plugin_LTLIBRARIES += plugins/service.la
-plugins_service_la_SOURCES = plugins/service.c
-plugins_service_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \
- -no-undefined
-plugins_service_la_CFLAGS = $(AM_CFLAGS) -fvisibility=hidden
-endif
client_bluetoothctl_SOURCES = client/main.c \
client/display.h client/display.c \
client/agent.h client/agent.c \
+ client/gatt.h client/gatt.c \
monitor/uuid.h monitor/uuid.c
client_bluetoothctl_LDADD = gdbus/libgdbus-internal.la @GLIB_LIBS@ @DBUS_LIBS@ \
-lreadline
tools_hci_tester_LDADD = src/libshared-glib.la @GLIB_LIBS@
endif
-#tools/mpris-proxy tools/mcaptest
-
if TOOLS
bin_PROGRAMS += tools/hciattach tools/hciconfig tools/hcitool tools/hcidump \
tools/rfcomm tools/rctest tools/l2test tools/l2ping \
tools/sdptool tools/ciptool tools/bccmd \
- tools/bluemoon
+ tools/bluemoon tools/hex2hcd tools/mpris-proxy
tools_hciattach_SOURCES = tools/hciattach.c tools/hciattach.h \
tools/hciattach_st.c \
tools/hciattach_qualcomm.c \
tools/hciattach_intel.c \
tools/hciattach_bcm43xx.c
-# src/log.c
+# src/log.c
# tools/hciattach_bcm43xx.c
tools_hciattach_LDADD = lib/libbluetooth-internal.la
tools_bluemoon_SOURCES = tools/bluemoon.c monitor/bt.h
tools_bluemoon_LDADD = src/libshared-mainloop.la
-#tools_mpris_proxy_SOURCES = tools/mpris-proxy.c
-#tools_mpris_proxy_LDADD = gdbus/libgdbus-internal.la @GLIB_LIBS@ @DBUS_LIBS@
+tools_hex2hcd_SOURCES = tools/hex2hcd.c
+
+tools_mpris_proxy_SOURCES = tools/mpris-proxy.c
+tools_mpris_proxy_LDADD = gdbus/libgdbus-internal.la @GLIB_LIBS@ @DBUS_LIBS@
-#tools_mcaptest_SOURCES = tools/mcaptest.c \
-# btio/btio.h btio/btio.c \
-# src/log.c src/log.h \
-# profiles/health/mcap.h profiles/health/mcap.c
-#tools_mcaptest_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ -lrt
+tools_mcaptest_SOURCES = tools/mcaptest.c \
+ btio/btio.h btio/btio.c \
+ src/log.c src/log.h \
+ profiles/health/mcap.h profiles/health/mcap.c
+tools_mcaptest_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ -lrt
dist_man_MANS += tools/hciattach.1 tools/hciconfig.1 \
tools/hcitool.1 tools/hcidump.1 \
tools/scotest tools/amptest tools/hwdb \
tools/hcieventmask tools/hcisecfilter \
tools/btmgmt tools/btinfo tools/btattach \
- tools/btsnoop tools/btproxy tools/btiotest \
- tools/cltest tools/seq2bseq tools/hex2hcd \
- tools/ibeacon tools/btgatt-client \
- tools/btgatt-server tools/mpris-player
+ tools/btsnoop tools/btproxy \
+ tools/btiotest tools/mcaptest tools/cltest \
+ tools/oobtest tools/seq2bseq tools/ibeacon \
+ tools/btgatt-client tools/btgatt-server
tools_bdaddr_SOURCES = tools/bdaddr.c src/oui.h src/oui.c
tools_bdaddr_LDADD = lib/libbluetooth-internal.la @UDEV_LIBS@
tools_hcieventmask_LDADD = lib/libbluetooth-internal.la
-tools_btmgmt_SOURCES = tools/btmgmt.c src/uuid-helper.c
-tools_btmgmt_LDADD = lib/libbluetooth-internal.la src/libshared-mainloop.la
+tools_btmgmt_SOURCES = tools/btmgmt.c src/uuid-helper.c client/display.c
+tools_btmgmt_LDADD = lib/libbluetooth-internal.la src/libshared-mainloop.la \
+ -lreadline -lncurses
tools_btinfo_SOURCES = tools/btinfo.c monitor/bt.h
tools_btinfo_LDADD = src/libshared-mainloop.la
tools_btiotest_SOURCES = tools/btiotest.c btio/btio.h btio/btio.c
tools_btiotest_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+tools_mcaptest_SOURCES = tools/mcaptest.c \
+ btio/btio.h btio/btio.c \
+ src/log.c src/log.h \
+ profiles/health/mcap.h profiles/health/mcap.c
+tools_mcaptest_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+
tools_cltest_SOURCES = tools/cltest.c
-tools_mpris_player_SOURCES = tools/mpris-player.c
-tools_mpris_player_LDADD = gdbus/libgdbus-internal.la @GLIB_LIBS@ @DBUS_LIBS@
tools_cltest_LDADD = lib/libbluetooth-internal.la src/libshared-mainloop.la
-tools_seq2bseq_SOURCES = tools/seq2bseq.c
+tools_oobtest_SOURCES = tools/oobtest.c
+tools_oobtest_LDADD = lib/libbluetooth-internal.la src/libshared-mainloop.la
-tools_hex2hcd_SOURCES = tools/hex2hcd.c
+tools_seq2bseq_SOURCES = tools/seq2bseq.c
tools_ibeacon_SOURCES = tools/ibeacon.c monitor/bt.h
tools_ibeacon_LDADD = src/libshared-mainloop.la
ATT/GATT (new shared stack)
===========================
-- shared/att currently doesn't handle signed writes. It should be extended to
- support signing outgoing and verify incoming ATT PDUs.
-
- Priority: Medium
- Complexity: C1
-
- Add complete GATT test coverage in unit/test-gatt following the GATT test
spec. This could use shared/gatt-client and shared/gatt-server at the same
time to test both against eachother. We should definitely have tests for
Priority: High
Complexity: C4
+- Write an example using client D-Bus API using C.
+
+ Priority: High
+ Complexity: C2
+
+- Write an example using client D-Bus API using python.
+
+ Priority: High
+ Complexity: C2
+
- Define packed structs for ATT protocol PDUs in shared/att-types to improve
readability. We should probably do this once there are extensive unit tests
for gatt-client/gatt-server so that we don't accidentally break working code.
Priority: Medium
Complexity: C1
-- Introduce a handler interface to shared/gatt-client which can be used by the
- upper layer to determine when the link has been disconnected or an ATT
- protocol request times out.
+- Persist client attribute cache across reboots.
Priority: Medium
- Complexity: C2
-
-- Introduce long-term caching of attributes to shared/gatt-client, such that the
- services, characteristics, and descriptors obtained from a peripheral are
- remembered in the case of bonding. This may involve storing data about GATT
- services to disk.
-
- Priority: Low
Complexity: C4
- Move all daemon plugins and profiles that are GATT based to use
shared/gatt-client instead of attrib/*. This is a complicated task that
potentially needs a new plugin/profile probing interface and a lot of
- rewriting that can cause regressions in existing functionality. The biggest
- challenge here is that an instance of bt_att (shared/att) and GAttrib
- (attrib/gattrib) cannot coexist on the same file descriptor, since they will
- cause ATT protocol violations by breaking the sequential request-response
- structure. A special shared/gatt-client-gattrib implementation may be
- necessary to move each profile/plugin to the new API before actually switching
- to the shared/att based implementation.
+ rewriting that can cause regressions in existing functionality.
Priority: Medium
Complexity: C4
-- Implement the client portion of doc/gatt-api.txt using shared/gatt-client once
- plugin/profile code uses it.
-
- Priority: Medium
- Complexity: C2
-
- Introduce a way for shared/gatt-server to check security permissions on the
current connection through bt_att.
Priority: Medium
Complexity: C4
+- Send out indications from the "Service Changed" characteristic upon
+ reconnection if a bonded device is not connected when the local database is
+ modified.
+
+ Priority: High
+ Complexity: C2
+
+- Unify the GATT server and client D-Bus implementations into a single module.
+ While these don't share a lot of code, keeping them all in src/gatt-dbus seems
+ to make more sense from an organizational perspective.
+
+ Priority: Low
+ Complexity: C1
+
+- Isolate all GATT code inside the daemon into its own module and perform
+ interaction with other modules (e.g. src/device.c) via callbacks. This
+ includes client/server management, tracking incoming/outgoing connections for
+ ATT, and callbacks to perform profile probing.
+
+ Priority: Low
+ Complexity: C4
+
+- Support included services in the GATT D-Bus client API.
+
+ Priority: Medium
+ Complexity: C1
+
+- The recently added support for ATT signed writes requires the following kernel
+ modules to be enabled:
+
+ CONFIG_CRYPTO_USER_API
+ CONFIG_CRYPTO_USER_API_HASH
+ CONFIG_CRYPTO_USER_API_SKCIPHER
+
+ Currently, if these are not enabled, bt_att_new silently returns NULL. We
+ should handle this more gracefully by not supporting signed writes if we can't
+ initialize bt_crypto while succeeding bt_att initialization regardless.
+
+ This behavior should be documented in the README.
+
+ Priority: High
+ Complexity: C1
+
+
ATT/GATT (old/outdated)
=======================
with_cflags="$with_cflags -Wredundant-decls"
with_cflags="$with_cflags -Wcast-align"
with_cflags="$with_cflags -Wswitch-enum"
+ with_cflags="$with_cflags -Wformat -Wformat-security"
with_cflags="$with_cflags -DG_DISABLE_DEPRECATED"
with_cflags="$with_cflags -DGLIB_VERSION_MIN_REQUIRED=GLIB_VERSION_2_28"
with_cflags="$with_cflags -DGLIB_VERSION_MAX_ALLOWED=GLIB_VERSION_2_28"
BLUEZ_COMMON_CFLAGS := -DVERSION=\"$(BLUEZ_VERSION)\" \
-DANDROID_VERSION=$(ANDROID_VERSION) \
-DANDROID_STORAGEDIR=\"/data/misc/bluetooth\" \
+ -DHAVE_LINUX_IF_ALG_H \
+ -DHAVE_LINUX_TYPES_H \
# Enable warnings enabled in autotools build
BLUEZ_COMMON_CFLAGS += -Wall -Wextra \
LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
LOCAL_MODULE := bluetooth.default
-LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := SHARED_LIBRARIES
LOCAL_REQUIRED_MODULES := bluetoothd bluetoothd-snoop init.bluetooth.rc
+ifeq ($(ANDROID_GE_5_0_0), 1)
+LOCAL_MODULE_RELATIVE_PATH := hw
+else
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+endif
+
include $(BUILD_SHARED_LIBRARY)
#
bluez/btio/btio.c \
bluez/lib/bluetooth.c \
bluez/lib/hci.c \
+ bluez/src/shared/util.c \
+ bluez/src/shared/queue.c \
LOCAL_C_INCLUDES += \
$(LOCAL_PATH)/bluez \
LOCAL_SRC_FILES := \
bluez/monitor/main.c \
- bluez/monitor/mainloop.c \
bluez/monitor/display.c \
bluez/monitor/hcidump.c \
bluez/monitor/control.c \
bluez/src/shared/queue.c \
bluez/src/shared/crypto.c \
bluez/src/shared/btsnoop.c \
+ bluez/src/shared/mainloop.c \
bluez/lib/hci.c \
bluez/lib/bluetooth.c \
LOCAL_SRC_FILES := \
bluez/tools/btproxy.c \
- bluez/monitor/mainloop.c \
+ bluez/src/shared/mainloop.c \
bluez/src/shared/util.c \
LOCAL_C_INCLUDES := \
LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) -Wno-declaration-after-statement
LOCAL_LDFLAGS := -ldl
-LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := audio.a2dp.default
+ifeq ($(ANDROID_GE_5_0_0), 1)
+LOCAL_MODULE_RELATIVE_PATH := hw
+else
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+endif
+
include $(BUILD_SHARED_LIBRARY)
#
LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS) -Wno-declaration-after-statement
-LOCAL_MODULE_RELATIVE_PATH := hw
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE := audio.sco.default
+ifeq ($(ANDROID_GE_5_0_0), 1)
+LOCAL_MODULE_RELATIVE_PATH := hw
+else
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/hw
+endif
+
include $(BUILD_SHARED_LIBRARY)
#
LOCAL_SRC_FILES := \
bluez/android/bluetoothd-snoop.c \
- bluez/monitor/mainloop.c \
+ bluez/src/shared/mainloop.c \
bluez/src/shared/btsnoop.c \
bluez/android/log.c \
bluez/tools/btmgmt.c \
bluez/lib/bluetooth.c \
bluez/lib/sdp.c \
- bluez/monitor/mainloop.c \
+ bluez/src/shared/mainloop.c \
bluez/src/shared/io-mainloop.c \
bluez/src/shared/mgmt.c \
bluez/src/shared/queue.c \
bluez/src/shared/util.c \
bluez/src/shared/gap.c \
bluez/src/uuid-helper.c \
+ bluez/client/display.c \
LOCAL_C_INCLUDES := \
$(LOCAL_PATH)/bluez \
+ $(LOCAL_PATH)/bluez/android/compat \
LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
include $(BUILD_EXECUTABLE)
#
+# hciconfig
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ bluez/tools/hciconfig.c \
+ bluez/tools/csr.c \
+ bluez/lib/bluetooth.c \
+ bluez/lib/hci.c \
+
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/bluez \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_STATIC_LIBRARIES := \
+ bluetooth-headers \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := hciconfig
+
+LOCAL_ADDITIONAL_DEPENDENCIES := $(LOCAL_PATH)/bluez/configure.ac
+
+include $(BUILD_EXECUTABLE)
+
+#
# l2ping
#
bluez/lib/bluetooth.c \
bluez/lib/hci.c \
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/bluez \
+
LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
LOCAL_STATIC_LIBRARIES := \
bluez/lib/bluetooth.c \
bluez/lib/hci.c \
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/bluez \
+
LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
LOCAL_STATIC_LIBRARIES := \
bluez/lib/bluetooth.c \
bluez/lib/hci.c \
+LOCAL_C_INCLUDES := \
+ $(LOCAL_PATH)/bluez \
+
LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
LOCAL_STATIC_LIBRARIES := \
android_avdtptest_SOURCES = android/avdtptest.c \
src/log.h src/log.c \
btio/btio.h btio/btio.c \
+ src/shared/util.h src/shared/util.c \
+ src/shared/queue.h src/shared/queue.c \
android/avdtp.h android/avdtp.c
android_avdtptest_CFLAGS = $(AM_CFLAGS)
android_avdtptest_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
endif
EXTRA_DIST += android/Android.mk android/README \
+ android/compat/readline/history.h \
+ android/compat/readline/readline.h \
+ android/compat/wordexp.h \
android/bluetoothd-wrapper.c \
android/log.c \
android/bluetoothd.te \
android/pics-scpp.txt \
android/pics-dis.txt \
android/pics-avdtp.txt \
+ android/pics-gavdp.txt \
android/pixit-l2cap.txt \
android/pixit-gap.txt \
android/pixit-did.txt \
android/pixit-rfcomm.txt \
android/pixit-spp.txt \
android/pixit-avdtp.txt \
+ android/pixit-gavdp.txt \
+ android/pixit-sdp.txt \
android/pts-rfcomm.txt \
android/pts-spp.txt \
android/pts-l2cap.txt \
android/pts-hogp.txt \
android/pts-scpp.txt \
android/pts-dis.txt \
- android/pts-avdtp.txt
+ android/pts-avdtp.txt \
+ android/pts-gavdp.txt \
+ android/pts-sdp.txt
BlueZ.
For Android 5.0 Lollipop:
-<TBD>
+https://android-review.googlesource.com/99761
+https://android-review.googlesource.com/100297
+https://android-review.googlesource.com/102882
+https://android-review.googlesource.com/132733
+https://android-review.googlesource.com/132763
For Android 4.4 KitKat:
https://android-review.googlesource.com/82757
#include "lib/sdp.h"
#include "lib/sdp_lib.h"
#include "profiles/audio/a2dp-codecs.h"
+#include "src/shared/queue.h"
#include "src/log.h"
#include "hal-msg.h"
#include "ipc-common.h"
static struct ipc *hal_ipc = NULL;
static struct ipc *audio_ipc = NULL;
+static struct queue *lseps = NULL;
+
struct a2dp_preset {
void *data;
int8_t len;
struct a2dp_endpoint *endpoint = data;
if (endpoint->sep)
- avdtp_unregister_sep(endpoint->sep);
+ avdtp_unregister_sep(lseps, endpoint->sep);
if (endpoint->caps)
preset_free(endpoint->caps);
gpointer user_data)
{
struct a2dp_device *dev = user_data;
+ struct avdtp *session;
uint16_t imtu, omtu;
GError *gerr = NULL;
int fd;
fd = g_io_channel_unix_get_fd(chan);
/* FIXME: Add proper version */
- dev->session = avdtp_new(fd, imtu, omtu, 0x0100);
- if (!dev->session)
+ session = avdtp_new(fd, imtu, omtu, 0x0100, lseps);
+ if (!session)
goto failed;
+ dev->session = session;
+
avdtp_add_disconnect_cb(dev->session, disconnect_cb, dev);
/* Proceed to stream setup if initiator */
endpoint = g_new0(struct a2dp_endpoint, 1);
endpoint->id = g_slist_length(endpoints) + 1;
endpoint->codec = codec;
- endpoint->sep = avdtp_register_sep(AVDTP_SEP_TYPE_SOURCE,
+ endpoint->sep = avdtp_register_sep(lseps, AVDTP_SEP_TYPE_SOURCE,
AVDTP_MEDIA_TYPE_AUDIO,
codec, FALSE, &sep_ind,
&sep_cfm, endpoint);
ipc_cleanup(audio_ipc);
audio_ipc = NULL;
+
+ queue_destroy(lseps, NULL);
}
static bool bt_audio_register(ipc_disconnect_cb disconnect)
bacpy(&adapter_addr, addr);
+ lseps = queue_new();
+
server = bt_io_listen(connect_cb, NULL, NULL, NULL, &err,
BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
BT_IO_OPT_PSM, AVDTP_PSM,
#include <fcntl.h>
#include <netinet/in.h>
-#include <bluetooth/sdp.h>
-
#include <glib.h>
+#include "lib/sdp.h"
+
#include "src/log.h"
#include "src/uinput.h"
#include "lib/bluetooth.h"
#include "src/log.h"
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
#include "avdtp.h"
#include "../profiles/audio/a2dp-codecs.h"
#define MAX_SEID 0x3E
+static unsigned int seids;
#ifndef MAX
# define MAX(x, y) ((x) > (y) ? (x) : (y))
uint16_t version;
- struct avdtp_server *server;
-
guint auth_id;
GIOChannel *io;
guint io_id;
GSList *seps; /* Elements of type struct avdtp_remote_sep * */
+ struct queue *lseps; /* Elements of type struct avdtp_local_sep * */
GSList *streams; /* Elements of type struct avdtp_stream * */
bool shutdown;
};
-static GSList *lseps = NULL;
-
static int send_request(struct avdtp *session, gboolean priority,
struct avdtp_stream *stream, uint8_t signal_id,
void *buffer, size_t size);
g_slist_free_full(session->seps, sep_free);
g_slist_free_full(session->disconnect, g_free);
+ /* Free copy of the SEP list */
+ session->lseps = NULL;
+
g_free(session->buf);
g_free(session);
return session;
}
-static struct avdtp_local_sep *find_local_sep_by_seid(uint8_t seid)
+static bool match_by_seid(const void *data, const void *user_data)
{
- GSList *l;
+ const struct avdtp_local_sep *sep = data;
+ uint8_t seid = PTR_TO_UINT(user_data);
- for (l = lseps; l != NULL; l = g_slist_next(l)) {
- struct avdtp_local_sep *sep = l->data;
-
- if (sep->info.seid == seid)
- return sep;
- }
+ return sep->info.seid == seid;
+}
- return NULL;
+static struct avdtp_local_sep *find_local_sep_by_seid(struct avdtp *session,
+ uint8_t seid)
+{
+ return queue_find(session->lseps, match_by_seid, INT_TO_PTR(seid));
}
struct avdtp_remote_sep *avdtp_find_remote_sep(struct avdtp *session,
signal_id, NULL, 0);
}
+static void copy_seps(void *data, void *user_data)
+{
+ struct avdtp_local_sep *sep = data;
+ struct seid_info **p = user_data;
+
+ memcpy(*p, &sep->info, sizeof(struct seid_info));
+ *p = *p + 1;
+}
+
static gboolean avdtp_discover_cmd(struct avdtp *session, uint8_t transaction,
void *buf, int size)
{
- GSList *l;
- unsigned int rsp_size, sep_count, i;
- struct seid_info *seps;
+ unsigned int rsp_size, sep_count;
+ struct seid_info *seps, *p;
gboolean ret;
- sep_count = g_slist_length(lseps);
+ sep_count = queue_length(session->lseps);
if (sep_count == 0) {
uint8_t err = AVDTP_NOT_SUPPORTED_COMMAND;
rsp_size = sep_count * sizeof(struct seid_info);
seps = g_new0(struct seid_info, sep_count);
+ p = seps;
- for (l = lseps, i = 0; l != NULL; l = l->next, i++) {
- struct avdtp_local_sep *sep = l->data;
-
- memcpy(&seps[i], &sep->info, sizeof(struct seid_info));
- }
+ queue_foreach(session->lseps, copy_seps, &p);
ret = avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
AVDTP_DISCOVER, seps, rsp_size);
goto failed;
}
- sep = find_local_sep_by_seid(req->acp_seid);
+ sep = find_local_sep_by_seid(session, req->acp_seid);
if (!sep) {
err = AVDTP_BAD_ACP_SEID;
goto failed;
return FALSE;
}
- sep = find_local_sep_by_seid(req->acp_seid);
+ sep = find_local_sep_by_seid(session, req->acp_seid);
if (!sep) {
err = AVDTP_BAD_ACP_SEID;
goto failed;
memset(buf, 0, sizeof(buf));
- sep = find_local_sep_by_seid(req->acp_seid);
+ sep = find_local_sep_by_seid(session, req->acp_seid);
if (!sep) {
err = AVDTP_BAD_ACP_SEID;
goto failed;
return FALSE;
}
- sep = find_local_sep_by_seid(req->acp_seid);
+ sep = find_local_sep_by_seid(session, req->acp_seid);
if (!sep) {
err = AVDTP_BAD_ACP_SEID;
goto failed;
for (i = 0; i < seid_count; i++, seid++) {
failed_seid = seid->seid;
- sep = find_local_sep_by_seid(req->first_seid.seid);
+ sep = find_local_sep_by_seid(session, req->first_seid.seid);
if (!sep || !sep->stream) {
err = AVDTP_BAD_ACP_SEID;
goto failed;
return FALSE;
}
- sep = find_local_sep_by_seid(req->acp_seid);
+ sep = find_local_sep_by_seid(session, req->acp_seid);
if (!sep || !sep->stream) {
err = AVDTP_BAD_ACP_SEID;
goto failed;
for (i = 0; i < seid_count; i++, seid++) {
failed_seid = seid->seid;
- sep = find_local_sep_by_seid(req->first_seid.seid);
+ sep = find_local_sep_by_seid(session, req->first_seid.seid);
if (!sep || !sep->stream) {
err = AVDTP_BAD_ACP_SEID;
goto failed;
return FALSE;
}
- sep = find_local_sep_by_seid(req->acp_seid);
+ sep = find_local_sep_by_seid(session, req->acp_seid);
if (!sep || !sep->stream)
return TRUE;
return FALSE;
}
- sep = find_local_sep_by_seid(req->acp_seid);
+ sep = find_local_sep_by_seid(session, req->acp_seid);
if (!sep || !sep->stream) {
err = AVDTP_BAD_ACP_SEID;
goto failed;
return err;
}
-struct avdtp *avdtp_new(int fd, size_t imtu, size_t omtu, uint16_t version)
+struct avdtp *avdtp_new(int fd, size_t imtu, size_t omtu, uint16_t version,
+ struct queue *lseps)
{
struct avdtp *session;
GIOCondition cond = G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL;
int new_fd;
+ if (!lseps)
+ return NULL;
+
new_fd = dup(fd);
if (new_fd < 0) {
error("dup(): %s (%d)", strerror(errno), errno);
(GIOFunc) session_cb, session,
NULL);
+ session->lseps = lseps;
+
return avdtp_ref(session);
}
g_io_channel_unref(io);
return TRUE;
-
}
gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock,
&req, sizeof(req));
}
-struct avdtp_local_sep *avdtp_register_sep(uint8_t type, uint8_t media_type,
+struct avdtp_local_sep *avdtp_register_sep(struct queue *lseps, uint8_t type,
+ uint8_t media_type,
uint8_t codec_type,
gboolean delay_reporting,
struct avdtp_sep_ind *ind,
void *user_data)
{
struct avdtp_local_sep *sep;
+ uint8_t seid = util_get_uid(&seids, MAX_SEID);
- if (g_slist_length(lseps) > MAX_SEID)
+ if (!seid)
return NULL;
sep = g_new0(struct avdtp_local_sep, 1);
sep->state = AVDTP_STATE_IDLE;
- sep->info.seid = g_slist_length(lseps) + 1;
+ sep->info.seid = seid;
sep->info.type = type;
sep->info.media_type = media_type;
sep->codec = codec_type;
DBG("SEP %p registered: type:%d codec:%d seid:%d", sep,
sep->info.type, sep->codec, sep->info.seid);
- lseps = g_slist_append(lseps, sep);
+
+ if (!queue_push_tail(lseps, sep)) {
+ g_free(sep);
+ return NULL;
+ }
return sep;
}
sep->vndcodec_codec = codec_id;
}
-int avdtp_unregister_sep(struct avdtp_local_sep *sep)
+int avdtp_unregister_sep(struct queue *lseps, struct avdtp_local_sep *sep)
{
if (!sep)
return -EINVAL;
- lseps = g_slist_remove(lseps, sep);
-
if (sep->stream)
release_stream(sep->stream, sep->stream->session);
DBG("SEP %p unregistered: type:%d codec:%d seid:%d", sep,
sep->info.type, sep->codec, sep->info.seid);
+ util_clear_uid(&seids, sep->info.seid);
+ queue_remove(lseps, sep);
g_free(sep);
return 0;
struct avdtp_error *err, void *user_data);
typedef void (*avdtp_disconnect_cb_t) (void *user_data);
-struct avdtp *avdtp_new(int fd, size_t imtu, size_t omtu, uint16_t version);
+struct avdtp *avdtp_new(int fd, size_t imtu, size_t omtu, uint16_t version,
+ struct queue *lseps);
unsigned int avdtp_add_disconnect_cb(struct avdtp *session,
avdtp_disconnect_cb_t cb,
int avdtp_delay_report(struct avdtp *session, struct avdtp_stream *stream,
uint16_t delay);
-struct avdtp_local_sep *avdtp_register_sep(uint8_t type, uint8_t media_type,
+struct avdtp_local_sep *avdtp_register_sep(struct queue *lseps, uint8_t type,
+ uint8_t media_type,
uint8_t codec_type,
gboolean delay_reporting,
struct avdtp_sep_ind *ind,
struct avdtp_remote_sep *avdtp_find_remote_sep(struct avdtp *session,
struct avdtp_local_sep *lsep);
-int avdtp_unregister_sep(struct avdtp_local_sep *sep);
+int avdtp_unregister_sep(struct queue *lseps, struct avdtp_local_sep *sep);
avdtp_state_t avdtp_sep_get_state(struct avdtp_local_sep *sep);
#include <stdbool.h>
#include <errno.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/hci_lib.h>
-
#include <glib.h>
-#include "src/shared/util.h"
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+
#include "btio/btio.h"
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
#include "avdtp.h"
static GMainLoop *mainloop = NULL;
static guint media_player = 0;
static guint media_recorder = 0;
static guint idle_id = 0;
+static struct queue *lseps = NULL;
static bool fragment = false;
return;
}
- avdtp = avdtp_new(fd, imtu, omtu, version);
+ avdtp = avdtp_new(fd, imtu, omtu, version, lseps);
if (!avdtp) {
printf("Failed to create avdtp instance\n");
g_main_loop_quit(mainloop);
}
}
- local_sep = avdtp_register_sep(dev_role, AVDTP_MEDIA_TYPE_AUDIO,
+ local_sep = avdtp_register_sep(lseps, dev_role, AVDTP_MEDIA_TYPE_AUDIO,
0x00, TRUE, &sep_ind, &sep_cfm, NULL);
if (!local_sep) {
printf("Failed to register sep\n");
exit(0);
}
+ queue_push_tail(lseps, local_sep);
+
if (!bacmp(&dst, BDADDR_ANY)) {
printf("Listening...\n");
io = do_listen(&err);
printf("Done\n");
+ queue_destroy(lseps, NULL);
+
avdtp_unref(avdtp);
avdtp = NULL;
goto done;
}
- number = pdu->params[0];
+ number = rsp->number;
if (number > 0)
- attrs = &pdu->params[1];
+ attrs = rsp->params;
err = 0;
#include "src/log.h"
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
#include "lib/uuid.h"
+
#include "src/shared/util.h"
#include "src/shared/queue.h"
#include "android/bas.h"
#define ATT_NOTIFICATION_HEADER_SIZE 3
+#define ATT_READ_RESPONSE_HEADER_SIZE 1
struct bt_bas {
int ref_count;
free(req);
}
-static void value_cb(const guint8 *pdu, guint16 len, gpointer user_data)
+static void notification_cb(const guint8 *pdu, guint16 len, gpointer user_data)
{
DBG("Battery Level at %u", pdu[ATT_NOTIFICATION_HEADER_SIZE]);
}
+static void read_value_cb(guint8 status, const guint8 *pdu, guint16 len,
+ gpointer user_data)
+{
+ DBG("Battery Level at %u", pdu[ATT_READ_RESPONSE_HEADER_SIZE]);
+}
+
static void ccc_written_cb(guint8 status, const guint8 *pdu,
guint16 plen, gpointer user_data)
{
DBG("Battery Level: notification enabled");
bas->id = g_attrib_register(bas->attrib, ATT_OP_HANDLE_NOTIFY,
- bas->handle, value_cb, bas, NULL);
+ bas->handle, notification_cb, bas,
+ NULL);
}
static void write_ccc(struct bt_bas *bas, GAttrib *attrib, uint16_t handle,
DBG("Battery handle: 0x%04x", bas->handle);
+ read_char(bas, bas->attrib, bas->handle, read_value_cb, bas);
+
start = chr->value_handle + 1;
end = bas->primary->range.end;
#include "utils.h"
#include "bluetooth.h"
-/*
- * bits in bitmask as defined in table 6.3 of Multi Profile Specification
- * HFP AG + A2DP SRC + AVRCP TG + PAN (NAP/PANU) + PBAP PSE
- */
-#define MPS_DEFAULT_MPSD ((1ULL << 0) | (1ULL << 2) | (1ULL << 4) | \
- (1ULL << 6) | (1ULL << 8) | (1ULL << 10) | \
- (1ULL << 12) | (1ULL << 26) | (1ULL << 28) | \
- (1ULL << 30) | (1ULL << 32) | (1ULL << 34) | \
- (1ULL << 36))
-
-/*
- * bits in bitmask as defined in table 6.4 of Multi Profile Specification
- * HFP AG + A2DP SRC + AVRCP TG + PAN (NAP/PANU) + PBAP PSE
- */
-#define MPS_DEFAULT_MPMD ((1ULL << 1) | (1ULL << 3) | (1ULL << 5) | \
- (1ULL << 6) | (1ULL << 8) | (1ULL << 10) | \
- (1ULL << 12) | (1ULL << 15) | (1ULL << 17))
-
-/*
- * bits in bitmask as defined in table 6.5 of Multi Profile Specification
- * Note that in this table spec starts bit positions from 1 (bit 0 unused?)
- */
-#define MPS_DEFAULT_DEPS ((1 << 1) | (1 << 2) | (1 << 3))
-
-/* MPSD bit dependent on HFP AG support */
-#define MPS_MPSD_HFP_AG_DEP ((1ULL << 0) | (1ULL << 2) | (1ULL << 4) | \
- (1ULL << 6) | (1ULL << 8) | (1ULL << 10) | \
- (1ULL << 12) | (1ULL << 14) | (1ULL << 16) | \
- (1ULL << 18) | (1ULL << 26) | (1ULL << 28) | \
- (1ULL << 30))
-
-/* MPMD bit dependent on HFP AG support */
-#define MPS_MPMD_HFP_AG_DEP (1ULL << 6)
-
#define DUT_MODE_FILE "/sys/kernel/debug/bluetooth/hci%u/dut_mode"
#define SETTINGS_FILE ANDROID_STORAGEDIR"/settings"
bool in_white_list;
+ bool connected;
+
char *name;
char *friendly_name;
unsigned int confirm_id; /* mgtm command id if command pending */
bool valid_remote_csrk;
+ bool remote_csrk_auth;
uint8_t remote_csrk[16];
uint32_t remote_sign_cnt;
bool valid_local_csrk;
+ bool local_csrk_auth;
uint8_t local_csrk[16];
uint32_t local_sign_cnt;
uint16_t gatt_ccc;
bt_paired_device_cb cb = data;
struct device *dev = user_data;
- cb(&dev->bdaddr, dev->bdaddr_type);
+ cb(&dev->bdaddr);
}
static void update_device_state(struct device *dev, uint8_t addr_type,
}
static void update_device(struct device *dev, int8_t rssi,
- const struct eir_data *eir, uint8_t bdaddr_type)
+ const struct eir_data *eir)
{
uint8_t buf[IPC_MTU];
struct hal_ev_remote_device_props *ev = (void *) buf;
old_type = get_device_android_type(dev);
- if (bdaddr_type == BDADDR_BREDR) {
- dev->bredr = true;
- } else {
- dev->le = true;
- dev->bdaddr_type = bdaddr_type;
- }
-
new_type = get_device_android_type(dev);
if (old_type != new_type) {
dev = get_device(bdaddr, bdaddr_type);
- if (bdaddr_type == BDADDR_BREDR)
+ if (bdaddr_type == BDADDR_BREDR) {
+ dev->bredr = true;
dev->bredr_seen = time(NULL);
- else
+ } else {
+ dev->le = true;
+ dev->bdaddr_type = bdaddr_type;
dev->le_seen = time(NULL);
+ }
/*
* Device found event needs to be send also for known device if this is
if (is_new_device(dev, eir.flags, bdaddr_type))
update_new_device(dev, rssi, &eir);
else
- update_device(dev, rssi, &eir, bdaddr_type);
+ update_device(dev, rssi, &eir);
eir_data_free(&eir);
/* Notify Gatt if its registered for LE events */
if (bdaddr_type != BDADDR_BREDR && gatt_device_found_cb) {
- bdaddr_t *addr;
- uint8_t addr_type;
+ const bdaddr_t *addr;
/*
* If RPA is set it means that IRK was received and ID address
* it needs to be used also in GATT notifications. Also GATT
* HAL implementation is using RPA for devices matching.
*/
- if (bacmp(&dev->rpa, BDADDR_ANY)) {
+ if (bacmp(&dev->rpa, BDADDR_ANY))
addr = &dev->rpa;
- addr_type = dev->rpa_type;
- } else {
+ else
addr = &dev->bdaddr;
- addr_type = dev->bdaddr_type;
- }
- gatt_device_found_cb(addr, addr_type, rssi, data_len, data,
- connectable, dev->le_bonded);
+ gatt_device_found_cb(addr, rssi, data_len, data, connectable,
+ dev->le_bonded);
}
if (!dev->bredr_paired && !dev->le_paired)
const struct mgmt_ev_device_connected *ev = param;
struct hal_ev_acl_state_changed hal_ev;
struct device *dev;
+ char addr[18];
if (length < sizeof(*ev)) {
error("Too short device connected event (%u bytes)", length);
return;
}
+ ba2str(&ev->addr.bdaddr, addr);
+ DBG("%s type %u", addr, ev->addr.type);
+
update_found_device(&ev->addr.bdaddr, ev->addr.type, 0, false, false,
&ev->eir[0], le16_to_cpu(ev->eir_len));
if (!dev)
return;
+ dev->connected = true;
+
get_device_android_addr(dev, hal_ev.bdaddr);
ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
if (device_is_paired(dev, type) && !device_is_bonded(dev))
update_device_state(dev, type, HAL_STATUS_SUCCESS, false,
false, false);
+
+ dev->connected = false;
}
static uint8_t status_mgmt2hal(uint8_t mgmt)
dev->local_csrk[i]);
g_key_file_set_string(key_file, addr, "LocalCSRK", key_str);
+
+ g_key_file_set_boolean(key_file, addr, "LocalCSRKAuthenticated",
+ dev->local_csrk_auth);
}
if (dev->valid_remote_csrk) {
dev->remote_csrk[i]);
g_key_file_set_string(key_file, addr, "RemoteCSRK", key_str);
+
+ g_key_file_set_boolean(key_file, addr,
+ "RemoteCSRKAuthenticated",
+ dev->remote_csrk_auth);
}
data = g_key_file_to_data(key_file, &length, NULL);
if (!dev)
return;
- switch (ev->key.master) {
+ switch (ev->key.type) {
case 0x00:
+ case 0x02:
memcpy(dev->local_csrk, ev->key.val, 16);
dev->local_sign_cnt = 0;
dev->valid_local_csrk = true;
+ dev->local_csrk_auth = ev->key.type == 0x02;
break;
case 0x01:
+ case 0x03:
memcpy(dev->remote_csrk, ev->key.val, 16);
dev->remote_sign_cnt = 0;
dev->valid_remote_csrk = true;
+ dev->remote_csrk_auth = ev->key.type == 0x03;
break;
default:
- error("Unknown CSRK key type 02%02x", ev->key.master);
+ error("Unknown CSRK key type 02%02x", ev->key.type);
return;
}
dev->bredr = g_key_file_get_boolean(key_file, peer, "BREDR",
NULL);
- str = g_key_file_get_string(key_file, peer, "LinkKey", NULL);
- if (str) {
- g_free(str);
- dev->bredr_paired = true;
- dev->bredr_bonded = true;
- }
-
- str = g_key_file_get_string(key_file, peer, "LongTermKey", NULL);
- if (str) {
- g_free(str);
- dev->le_paired = true;
- dev->le_bonded = true;
- }
-
- str = g_key_file_get_string(key_file, peer, "SlaveLongTermKey", NULL);
- if (str) {
- g_free(str);
- dev->le_paired = true;
- dev->le_bonded = true;
- }
-
str = g_key_file_get_string(key_file, peer, "LocalCSRK", NULL);
if (str) {
int i;
dev->local_sign_cnt = g_key_file_get_integer(key_file, peer,
"LocalCSRKSignCounter", NULL);
+
+ dev->local_csrk_auth = g_key_file_get_boolean(key_file, peer,
+ "LocalCSRKAuthenticated", NULL);
}
str = g_key_file_get_string(key_file, peer, "RemoteCSRK", NULL);
dev->remote_sign_cnt = g_key_file_get_integer(key_file, peer,
"RemoteCSRKSignCounter", NULL);
+
+ dev->remote_csrk_auth = g_key_file_get_boolean(key_file, peer,
+ "RemoteCSRKAuthenticated",
+ NULL);
}
str = g_key_file_get_string(key_file, peer, "GattCCC", NULL);
struct mgmt_ltk_info *slave_ltk_info;
struct device *dev;
+ dev = create_device_from_info(key_file, devs[i]);
+
key_info = get_key_info(key_file, devs[i]);
irk_info = get_irk_info(key_file, devs[i]);
ltk_info = get_ltk_info(key_file, devs[i], true);
slave_ltk_info = get_ltk_info(key_file, devs[i], false);
- if (!key_info && !ltk_info && !slave_ltk_info) {
+ /*
+ * Skip devices that have no permanent keys
+ * (CSRKs are loaded by create_device_from_info())
+ */
+ if (!dev->valid_local_csrk && !dev->valid_remote_csrk &&
+ !key_info && !ltk_info &&
+ !slave_ltk_info && !irk_info) {
error("Failed to load keys for %s, skipping", devs[i]);
-
+ free_device(dev);
continue;
}
- if (key_info)
+ if (key_info) {
keys = g_slist_prepend(keys, key_info);
+ dev->bredr_paired = true;
+ dev->bredr_bonded = true;
+ }
if (irk_info)
irks = g_slist_prepend(irks, irk_info);
if (slave_ltk_info)
ltks = g_slist_prepend(ltks, slave_ltk_info);
- dev = create_device_from_info(key_file, devs[i]);
+ if (dev->valid_local_csrk || dev->valid_remote_csrk ||
+ irk_info || ltk_info || slave_ltk_info) {
+ dev->le_paired = true;
+ dev->le_bonded = true;
+ }
bonded_devices = g_slist_prepend(bonded_devices, dev);
}
error("Failed to set class of device");
}
-static sdp_record_t *mps_record(void)
-{
- sdp_data_t *mpsd_features, *mpmd_features, *dependencies;
- sdp_list_t *svclass_id, *pfseq, *root;
- uuid_t root_uuid, svclass_uuid;
- sdp_profile_desc_t profile;
- sdp_record_t *record;
- uint64_t mpsd_feat, mpmd_feat;
- uint16_t deps;
-
- record = sdp_record_alloc();
- if (!record)
- return NULL;
-
- sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
- root = sdp_list_append(NULL, &root_uuid);
- sdp_set_browse_groups(record, root);
-
- sdp_uuid16_create(&svclass_uuid, MPS_SVCLASS_ID);
- svclass_id = sdp_list_append(NULL, &svclass_uuid);
- sdp_set_service_classes(record, svclass_id);
-
- sdp_uuid16_create(&profile.uuid, MPS_PROFILE_ID);
- profile.version = 0x0100;
- pfseq = sdp_list_append(NULL, &profile);
- sdp_set_profile_descs(record, pfseq);
-
- mpsd_feat = MPS_DEFAULT_MPSD;
- mpmd_feat = MPS_DEFAULT_MPMD;
-
- /* TODO should be configurable based on HFP AG support */
- if (false) {
- mpsd_feat &= MPS_MPSD_HFP_AG_DEP;
- mpmd_feat &= MPS_MPMD_HFP_AG_DEP;
- }
-
- mpsd_features = sdp_data_alloc(SDP_UINT64, &mpsd_feat);
- sdp_attr_add(record, SDP_ATTR_MPSD_SCENARIOS, mpsd_features);
-
- mpmd_features = sdp_data_alloc(SDP_UINT64, &mpmd_feat);
- sdp_attr_add(record, SDP_ATTR_MPMD_SCENARIOS, mpmd_features);
-
- deps = MPS_DEFAULT_DEPS;
- dependencies = sdp_data_alloc(SDP_UINT16, &deps);
- sdp_attr_add(record, SDP_ATTR_MPS_DEPENDENCIES, dependencies);
-
- sdp_set_info_attr(record, "Multi Profile", 0, 0);
-
- sdp_list_free(pfseq, NULL);
- sdp_list_free(root, NULL);
- sdp_list_free(svclass_id, NULL);
-
- return record;
-}
-
-static void add_mps_record(void)
+static void enable_mps(void)
{
- sdp_record_t *rec;
+ uuid_t uuid, *uuid128;
- rec = mps_record();
- if (!rec) {
- error("Failed to allocate MPS record");
+ sdp_uuid16_create(&uuid, MPS_SVCLASS_ID);
+ uuid128 = sdp_uuid_to_uuid128(&uuid);
+ if (!uuid128)
return;
- }
- if (bt_adapter_add_record(rec, 0) < 0) {
- error("Failed to register MPS record");
- sdp_record_free(rec);
- }
+ register_mps(true);
+ adapter.uuids = g_slist_prepend(adapter.uuids, uuid128);
+ add_uuid(0, uuid128);
}
static void clear_auto_connect_list_complete(uint8_t status,
set_io_capability();
set_device_id();
- add_mps_record();
+ enable_mps();
missing_settings = adapter.current_settings ^
adapter.supported_settings;
if (missing_settings & MGMT_SETTING_SSP)
set_mode(MGMT_OP_SET_SSP, 0x01);
- if (missing_settings & MGMT_SETTING_SECURE_CONN)
- set_mode(MGMT_OP_SET_SECURE_CONN, 0x01);
-
if (missing_settings & MGMT_SETTING_BONDABLE)
set_mode(MGMT_OP_SET_BONDABLE, 0x01);
return true;
}
-bool bt_get_csrk(const bdaddr_t *addr, enum bt_csrk_type type, uint8_t key[16],
- uint32_t *sign_cnt)
+bool bt_get_csrk(const bdaddr_t *addr, bool local, uint8_t key[16],
+ uint32_t *sign_cnt, bool *authenticated)
{
struct device *dev;
- bool local = (type == LOCAL_CSRK);
dev = find_device(addr);
if (!dev)
return false;
if (local && dev->valid_local_csrk) {
- memcpy(key, dev->local_csrk, 16);
- *sign_cnt = dev->local_sign_cnt;
+ if (key)
+ memcpy(key, dev->local_csrk, 16);
+
+ if (sign_cnt)
+ *sign_cnt = dev->local_sign_cnt;
+
+ if (authenticated)
+ *authenticated = dev->local_csrk_auth;
} else if (!local && dev->valid_remote_csrk) {
- memcpy(key, dev->remote_csrk, 16);
- *sign_cnt = dev->remote_sign_cnt;
+ if (key)
+ memcpy(key, dev->remote_csrk, 16);
+
+ if (sign_cnt)
+ *sign_cnt = dev->remote_sign_cnt;
+
+ if (authenticated)
+ *authenticated = dev->remote_csrk_auth;
} else {
return false;
}
return true;
}
-static void store_sign_counter(struct device *dev, enum bt_csrk_type type)
+static void store_sign_counter(struct device *dev, bool local)
{
const char *sign_cnt_s;
uint32_t sign_cnt;
GKeyFile *key_file;
- bool local = (type == LOCAL_CSRK);
gsize length = 0;
char addr[18];
g_key_file_free(key_file);
}
-void bt_update_sign_counter(const bdaddr_t *addr, enum bt_csrk_type type,
- uint32_t val)
+void bt_update_sign_counter(const bdaddr_t *addr, bool local, uint32_t val)
{
struct device *dev;
if (!dev)
return;
- if (type == LOCAL_CSRK)
+ if (local)
dev->local_sign_cnt = val;
else
dev->remote_sign_cnt = val;
- store_sign_counter(dev, type);
+ store_sign_counter(dev, local);
}
static uint8_t set_adapter_scan_mode(const void *buf, uint16_t len)
bt_unpaired_device_cb cb = data;
struct mgmt_addr_info *addr = user_data;
- cb(&addr->bdaddr, addr->type);
+ cb(&addr->bdaddr);
}
static void unpair_device_complete(uint8_t status, uint16_t length,
false, false);
/* Cast rp->addr to (void *) since queue_foreach don't take const */
- queue_foreach(unpaired_cb_list, send_unpaired_notification,
+
+ if (!dev->le_paired && !dev->bredr_paired)
+ queue_foreach(unpaired_cb_list, send_unpaired_notification,
(void *)&rp->addr);
}
{
const struct hal_cmd_get_connection_state *cmd = buf;
struct hal_rsp_get_connection_state rsp;
+ struct device *dev;
char address[18];
bdaddr_t bdaddr;
android2bdaddr(cmd->bdaddr, &bdaddr);
ba2str(&bdaddr, address);
- DBG("%s", address);
-
- /* TODO */
+ dev = find_device_android(cmd->bdaddr);
+ if (dev && dev->connected)
+ rsp.connection_state = 1;
+ else
+ rsp.connection_state = 0;
- rsp.connection_state = 0;
+ DBG("%s %u", address, rsp.connection_state);
ipc_send_rsp_full(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
HAL_OP_GET_CONNECTION_STATE, sizeof(rsp), &rsp,
goto failed;
}
+ /* Requested mode is set now, let's enable secure connection */
+ if (missing_settings & MGMT_SETTING_SECURE_CONN)
+ set_mode(MGMT_OP_SET_SECURE_CONN, 0x01);
+
/* Set initial default name */
if (!adapter.name) {
adapter.name = g_strdup(bt_config_get_model());
*
*/
-enum bt_csrk_type {
- LOCAL_CSRK,
- REMOTE_CSRK,
-};
-
typedef void (*bt_bluetooth_ready)(int err, const bdaddr_t *addr);
bool bt_bluetooth_start(int index, bool mgmt_dbg, bt_bluetooth_ready cb);
int bt_adapter_add_record(sdp_record_t *rec, uint8_t svc_hint);
void bt_adapter_remove_record(uint32_t handle);
-typedef void (*bt_le_device_found)(const bdaddr_t *addr, uint8_t addr_type,
- int rssi, uint16_t eir_len,
- const void *eir, bool connectable,
- bool bonded);
+typedef void (*bt_le_device_found)(const bdaddr_t *addr, int rssi,
+ uint16_t eir_len, const void *eir,
+ bool connectable, bool bonded);
bool bt_le_register(bt_le_device_found cb);
void bt_le_unregister(void);
bool bt_read_device_rssi(const bdaddr_t *addr, bt_read_device_rssi_done cb,
void *user_data);
-bool bt_get_csrk(const bdaddr_t *addr, enum bt_csrk_type type,
- uint8_t key[16], uint32_t *sign_cnt);
+bool bt_get_csrk(const bdaddr_t *addr, bool local, uint8_t key[16],
+ uint32_t *sign_cnt, bool *authenticated);
-void bt_update_sign_counter(const bdaddr_t *addr, enum bt_csrk_type type,
- uint32_t val);
+void bt_update_sign_counter(const bdaddr_t *addr, bool local, uint32_t val);
void bt_store_gatt_ccc(const bdaddr_t *addr, uint16_t value);
void bt_auto_connect_remove(const bdaddr_t *addr);
-typedef void (*bt_unpaired_device_cb)(const bdaddr_t *addr, uint8_t type);
+typedef void (*bt_unpaired_device_cb)(const bdaddr_t *addr);
bool bt_unpaired_register(bt_unpaired_device_cb cb);
void bt_unpaired_unregister(bt_unpaired_device_cb cb);
-typedef void (*bt_paired_device_cb)(const bdaddr_t *addr, uint8_t type);
+typedef void (*bt_paired_device_cb)(const bdaddr_t *addr);
bool bt_paired_register(bt_paired_device_cb cb);
void bt_paired_unregister(bt_paired_device_cb cb);
bool bt_is_pairing(const bdaddr_t *addr);
#include "lib/hci.h"
#include "lib/mgmt.h"
-#include "monitor/mainloop.h"
+#include "src/shared/mainloop.h"
#include "src/shared/btsnoop.h"
#include "src/log.h"
}
/* send_pass_through_cmd */
-
static void send_pass_through_cmd_c(int argc, const char **argv,
enum_func *enum_func, void **user)
{
+ if (argc == 3) {
+ *user = NULL;
+ *enum_func = enum_devices;
+ }
}
static void send_pass_through_cmd_p(int argc, const char **argv)
RETURN_IF_NULL(if_rc);
VERIFY_ADDR_ARG(2, &addr);
- if (argc <= 4) {
- haltest_error("No key code specified");
+ if (argc < 4) {
+ haltest_error("No key code specified\n");
return;
}
key_code = (uint8_t) atoi(argv[3]);
- if (argc <= 5) {
- haltest_error("No key state specified");
+ if (argc < 5) {
+ haltest_error("No key state specified\n");
return;
}
static struct method methods[] = {
STD_METHOD(init),
- STD_METHODCH(send_pass_through_cmd,
- "<bd_addr> <key_code> <key_state>"),
+ STD_METHODCH(send_pass_through_cmd, "<bd_addr> <key_code> <key_state>"),
STD_METHOD(cleanup),
END_METHOD
};
*
* BlueZ - Bluetooth protocol stack for Linux
*
- * Copyright (C) 2010 Nokia Corporation
- * Copyright (C) 2010 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 1987-2011 Free Software Foundation, Inc.
*
*
* This program is free software; you can redistribute it and/or modify
*
*/
-#ifdef __TIZEN_PATCH__
-GSList *attrib_client_register(DBusConnection *connection,
- struct btd_device *device, int psm,
- GAttrib *attrib, GSList *primaries);
-void attrib_client_unregister(GSList *services);
+#ifndef _HISTORY_H_
+#define _HISTORY_H_
+
+static inline void add_history(const char *c)
+{
+}
-#define GATT_FIND_INFO_RESP_OPCODE_LEN 1
#endif
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 1987-2011 Free Software Foundation, 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
+ *
+ */
+
+#ifndef _READLINE_H_
+#define _READLINE_H_
+
+typedef void (*rl_vcpfunc_t)(char *c);
+typedef void (*rl_compdisp_func_t)(char **c, int i, int j);
+typedef char *(*rl_compentry_func_t)(const char *c, int i);
+typedef char **(*rl_completion_func_t)(const char *c, int i, int j);
+
+#define RL_STATE_DONE 0x1000000
+#define RL_ISSTATE(x) (rl_readline_state & (x))
+
+static int rl_end;
+static int rl_point;
+static int rl_readline_state;
+static int rl_erase_empty_line;
+static int rl_attempted_completion_over;
+static char *rl_prompt;
+static char *rl_line_buffer;
+static rl_compdisp_func_t rl_completion_display_matches_hook;
+static rl_completion_func_t rl_attempted_completion_function;
+
+static inline void rl_callback_handler_install(const char *c, rl_vcpfunc_t f)
+{
+ printf("readline not available\n");
+ exit(1);
+}
+
+static inline int rl_set_prompt(const char *c)
+{
+ return -1;
+}
+
+static inline void rl_replace_line(const char *c, int i)
+{
+}
+
+static inline void rl_redisplay(void)
+{
+}
+
+static inline char **rl_completion_matches(const char *c, rl_compentry_func_t f)
+{
+ return NULL;
+}
+
+static inline int rl_insert_text(const char *c)
+{
+ return -1;
+}
+
+static inline int rl_crlf(void)
+{
+ return -1;
+}
+
+static inline void rl_callback_read_char(void)
+{
+}
+
+static inline int rl_message(const char *c, ...)
+{
+ return -1;
+}
+
+static inline void rl_callback_handler_remove(void)
+{
+}
+
+static inline char *rl_copy_text(int i, int j)
+{
+ return NULL;
+}
+
+static inline void rl_save_prompt(void)
+{
+}
+
+static inline void rl_restore_prompt(void)
+{
+}
+
+static inline int rl_forced_update_display(void)
+{
+ return -1;
+}
+
+#endif
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 1991-2013 Free Software Foundation, Inc.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef _WORDEXP_H_
+#define _WORDEXP_H_
+
+#define WRDE_NOCMD 0
+
+typedef struct {
+ size_t we_wordc;
+ char **we_wordv;
+ size_t we_offs;
+} wordexp_t;
+
+static inline int wordexp(const char *c, wordexp_t *w, int _i)
+{
+ return -1;
+}
+
+static inline void wordfree(wordexp_t *__wordexp)
+{
+}
+
+#endif
#include "src/log.h"
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
#include "lib/uuid.h"
+
#include "src/shared/util.h"
#include "src/shared/queue.h"
#include "utils.h"
#include "src/shared/util.h"
#include "src/shared/queue.h"
+#include "src/shared/att.h"
#include "src/shared/gatt-db.h"
#include "attrib/gattrib.h"
#include "attrib/att.h"
struct gatt_device {
bdaddr_t bdaddr;
- uint8_t bdaddr_type;
gatt_device_state_t state;
guint ind_id;
int ref;
- int conn_cnt;
struct queue *autoconnect_apps;
return queue_find(gatt_apps, match_app_by_id, INT_TO_PTR(id));
}
-static bool match_by_value(const void *data, const void *user_data)
-{
- return data == user_data;
-}
-
static bool match_device_by_bdaddr(const void *data, const void *user_data)
{
const struct gatt_device *dev = data;
struct notification_data *notification = data;
struct gatt_app *app;
+ if (!notification)
+ return;
+
if (--notification->ref)
return;
* triggered afterwards, but once client unregisters, device stays if
* used by others. Then just unregister single handle.
*/
- if (!queue_find(gatt_devices, match_by_value, dev))
+ if (!queue_find(gatt_devices, NULL, dev))
return;
if (notification->notif_id && dev)
{
char bda[18];
+ if (dev->state == state)
+ return;
+
ba2str(&dev->bdaddr, bda);
DBG("gatt: Device %s state changed %s -> %s", bda,
device_state_str[dev->state], device_state_str[state]);
{
/* For LE devices use auto connect feature if possible */
if (bt_kernel_conn_control()) {
- const bdaddr_t *bdaddr;
-
- /*
- * If address type is random it might be that IRK was received
- * and random is just for faking Android Framework. ID address
- * should be used for connection if present.
- */
- if (dev->bdaddr_type == BDADDR_LE_RANDOM) {
- bdaddr = bt_get_id_addr(&dev->bdaddr, NULL);
- if (!bdaddr)
- return -EINVAL;
- } else {
- bdaddr = &dev->bdaddr;
- }
-
- return bt_auto_connect_add(bdaddr);
- }
-
- /* Trigger discovery if not already started */
- if (!scanning) {
- if (!bt_le_discovery_start()) {
+ if (!bt_auto_connect_add(bt_get_id_addr(&dev->bdaddr, NULL)))
+ return false;
+ } else {
+ /* Trigger discovery if not already started */
+ if (!scanning && !bt_le_discovery_start()) {
error("gatt: Could not start scan");
return false;
}
}
+ device_set_state(dev, DEVICE_CONNECT_INIT);
return true;
}
g_attrib_unregister(device->attrib, device->server_id);
if (device->ind_id > 0)
- g_attrib_unregister(device->attrib,
- device->ind_id);
+ g_attrib_unregister(device->attrib, device->ind_id);
device->attrib = NULL;
g_attrib_cancel_all(attrib);
device_set_state(device, DEVICE_DISCONNECTED);
- if (!queue_isempty(device->autoconnect_apps)) {
+ if (!queue_isempty(device->autoconnect_apps))
auto_connect_le(device);
- device_set_state(device, DEVICE_CONNECT_INIT);
- } else {
+ else
bt_auto_connect_remove(&device->bdaddr);
- }
}
static void destroy_gatt_app(void *data)
{
struct gatt_app *app = data;
+ if (!app)
+ return;
+
/*
* First we want to get all notifications and unregister them.
* We don't pass unregister_notification to queue_destroy,
free(app);
}
-enum pend_req_state {
- REQUEST_INIT,
- REQUEST_PENDING,
- REQUEST_DONE,
-};
-
struct pending_request {
struct gatt_db_attribute *attrib;
int length;
uint8_t *filter_value;
uint16_t filter_vlen;
- enum pend_req_state state;
+ bool completed;
uint8_t error;
};
{
struct pending_request *entry = data;
+ if (!entry)
+ return;
+
free(entry->value);
free(entry->filter_value);
free(entry);
return device_ref(dev);
}
-static void send_client_connection_notify(struct app_connection *connection,
+static void send_client_connect_status_notify(struct app_connection *conn,
int32_t status)
{
struct hal_ev_gatt_client_connect ev;
- if (connection->app->func) {
- connection->app->func(&connection->device->bdaddr,
+ if (conn->app->func) {
+ conn->app->func(&conn->device->bdaddr,
status == GATT_SUCCESS ? 0 : -ENOTCONN,
- connection->device->attrib);
+ conn->device->attrib);
return;
}
- ev.client_if = connection->app->id;
- ev.conn_id = connection->id;
+ ev.client_if = conn->app->id;
+ ev.conn_id = conn->id;
ev.status = status;
- bdaddr2android(&connection->device->bdaddr, &ev.bda);
+ bdaddr2android(&conn->device->bdaddr, &ev.bda);
ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT, HAL_EV_GATT_CLIENT_CONNECT,
sizeof(ev), &ev);
}
-static void send_server_connection_notify(struct app_connection *connection,
+static void send_server_connection_state_notify(struct app_connection *conn,
bool connected)
{
struct hal_ev_gatt_server_connection ev;
- if (connection->app->func) {
- connection->app->func(&connection->device->bdaddr,
+ if (conn->app->func) {
+ conn->app->func(&conn->device->bdaddr,
connected ? 0 : -ENOTCONN,
- connection->device->attrib);
+ conn->device->attrib);
return;
}
- ev.server_if = connection->app->id;
- ev.conn_id = connection->id;
+ ev.server_if = conn->app->id;
+ ev.conn_id = conn->id;
ev.connected = connected;
- bdaddr2android(&connection->device->bdaddr, &ev.bdaddr);
+ bdaddr2android(&conn->device->bdaddr, &ev.bdaddr);
ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
HAL_EV_GATT_SERVER_CONNECTION, sizeof(ev), &ev);
}
-static void send_client_disconnection_notify(struct app_connection *connection,
+static void send_client_disconnect_status_notify(struct app_connection *conn,
int32_t status)
{
struct hal_ev_gatt_client_disconnect ev;
- if (connection->app->func) {
- connection->app->func(&connection->device->bdaddr, -ENOTCONN,
- connection->device->attrib);
+ if (conn->app->func) {
+ conn->app->func(&conn->device->bdaddr, -ENOTCONN,
+ conn->device->attrib);
return;
}
- ev.client_if = connection->app->id;
- ev.conn_id = connection->id;
+ ev.client_if = conn->app->id;
+ ev.conn_id = conn->id;
ev.status = status;
- bdaddr2android(&connection->device->bdaddr, &ev.bda);
+ bdaddr2android(&conn->device->bdaddr, &ev.bda);
ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
HAL_EV_GATT_CLIENT_DISCONNECT, sizeof(ev), &ev);
}
-static void send_app_disconnect_notify(struct app_connection *connection,
+static void notify_app_disconnect_status(struct app_connection *conn,
int32_t status)
{
- if (!connection->app)
+ if (!conn->app)
return;
- if (connection->app->type == GATT_CLIENT)
- send_client_disconnection_notify(connection, status);
+ if (conn->app->type == GATT_CLIENT)
+ send_client_disconnect_status_notify(conn, status);
else
- send_server_connection_notify(connection, !!status);
+ send_server_connection_state_notify(conn, !!status);
}
-static void send_app_connect_notify(struct app_connection *connection,
+static void notify_app_connect_status(struct app_connection *conn,
int32_t status)
{
- if (!connection->app)
+ if (!conn->app)
return;
- if (connection->app->type == GATT_CLIENT)
- send_client_connection_notify(connection, status);
- else if (connection->app->type == GATT_SERVER)
- send_server_connection_notify(connection, !status);
+ if (conn->app->type == GATT_CLIENT)
+ send_client_connect_status_notify(conn, status);
+ else
+ send_server_connection_state_notify(conn, !status);
}
-static void disconnect_notify_by_device(void *data, void *user_data)
+static void destroy_connection(void *data)
{
struct app_connection *conn = data;
- struct gatt_device *dev = user_data;
- if (dev != conn->device || !conn->app)
+ if (!conn)
return;
- if (dev->state == DEVICE_CONNECTED)
- send_app_disconnect_notify(conn, GATT_SUCCESS);
- else if (dev->state == DEVICE_CONNECT_INIT ||
- dev->state == DEVICE_CONNECT_READY)
- send_app_connect_notify(conn, GATT_FAILURE);
-}
-
-static void destroy_connection(void *data)
-{
- struct app_connection *conn = data;
-
if (conn->timeout_id > 0)
g_source_remove(conn->timeout_id);
- if (!queue_find(gatt_devices, match_by_value, conn->device))
- goto cleanup;
+ switch (conn->device->state) {
+ case DEVICE_CONNECTED:
+ notify_app_disconnect_status(conn, GATT_SUCCESS);
+ break;
+ case DEVICE_CONNECT_INIT:
+ case DEVICE_CONNECT_READY:
+ notify_app_connect_status(conn, GATT_FAILURE);
+ break;
+ case DEVICE_DISCONNECTED:
+ break;
+ }
- conn->device->conn_cnt--;
- if (conn->device->conn_cnt == 0)
+ if (!queue_find(app_connections, match_connection_by_device,
+ conn->device))
connection_cleanup(conn->device);
-cleanup:
queue_destroy(conn->transactions, free);
device_unref(conn->device);
free(conn);
}
-static void device_disconnect_clients(struct gatt_device *dev)
-{
- /* Notify disconnection to all clients */
- queue_foreach(app_connections, disconnect_notify_by_device, dev);
-
- /* Remove all clients by given device's */
- queue_remove_all(app_connections, match_connection_by_device, dev,
- destroy_connection);
-}
-
static gboolean disconnected_cb(GIOChannel *io, GIOCondition cond,
gpointer user_data)
{
if (!getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &len))
DBG("%s (%d)", strerror(err), err);
- device_disconnect_clients(dev);
+ queue_remove_all(app_connections, match_connection_by_device, dev,
+ destroy_connection);
return FALSE;
}
+static bool get_local_mtu(struct gatt_device *dev, uint16_t *mtu)
+{
+ GIOChannel *io;
+ uint16_t imtu, omtu;
+
+ io = g_attrib_get_channel(dev->attrib);
+
+ if (!bt_io_get(io, NULL, BT_IO_OPT_IMTU, &imtu, BT_IO_OPT_OMTU, &omtu,
+ BT_IO_OPT_INVALID)) {
+ error("gatt: Failed to get local MTU");
+ return false;
+ }
+
+ /*
+ * Limit MTU to MIN(IMTU, OMTU). This is to avoid situation where
+ * local OMTU < MIN(remote MTU, IMTU)
+ */
+ if (mtu)
+ *mtu = MIN(imtu, omtu);
+
+ return true;
+}
+
+static bool update_mtu(struct gatt_device *device, uint16_t rmtu)
+{
+ uint16_t mtu, lmtu;
+
+ if (!get_local_mtu(device, &lmtu))
+ return false;
+
+ DBG("remote_mtu:%d local_mtu:%d", rmtu, lmtu);
+
+ if (rmtu < ATT_DEFAULT_LE_MTU) {
+ error("gatt: remote MTU invalid (%u bytes)", rmtu);
+ return false;
+ }
+
+ mtu = MIN(lmtu, rmtu);
+
+ if (mtu == ATT_DEFAULT_LE_MTU)
+ return true;
+
+ if (!g_attrib_set_mtu(device->attrib, mtu)) {
+ error("gatt: Failed to set MTU");
+ return false;
+ }
+
+ return true;
+}
+
static void att_handler(const uint8_t *ipdu, uint16_t len, gpointer user_data);
static void exchange_mtu_cb(guint8 status, const guint8 *pdu, guint16 plen,
gpointer user_data)
{
struct gatt_device *device = user_data;
- GIOChannel *io;
- GError *gerr = NULL;
- uint16_t rmtu, mtu, imtu;
+ uint16_t rmtu;
+
+ DBG("");
if (status) {
error("gatt: MTU exchange: %s", att_ecode2str(status));
goto failed;
}
- if (rmtu < ATT_DEFAULT_LE_MTU) {
- error("gatt: MTU exchange: mtu error");
- goto failed;
- }
-
- io = g_attrib_get_channel(device->attrib);
-
- bt_io_get(io, &gerr, BT_IO_OPT_IMTU, &imtu, BT_IO_OPT_INVALID);
- if (gerr) {
- error("gatt: Could not get imtu: %s", gerr->message);
- g_error_free(gerr);
-
- return;
- }
-
- mtu = MIN(rmtu, imtu);
- if (mtu != imtu && !g_attrib_set_mtu(device->attrib, mtu)) {
- error("gatt: MTU exchange failed");
- goto failed;
- }
-
- DBG("MTU exchange succeeded: rmtu:%d, old mtu:%d, new mtu:%d", rmtu,
- imtu, mtu);
+ update_mtu(device, rmtu);
failed:
device_unref(device);
static void send_exchange_mtu_request(struct gatt_device *device)
{
- GIOChannel *io;
- GError *gerr = NULL;
- uint16_t imtu;
-
- io = g_attrib_get_channel(device->attrib);
-
- bt_io_get(io, &gerr, BT_IO_OPT_IMTU, &imtu, BT_IO_OPT_INVALID);
- if (gerr) {
- error("gatt: Could not get imtu: %s", gerr->message);
- g_error_free(gerr);
+ uint16_t mtu;
+ if (!get_local_mtu(device, &mtu))
return;
- }
- if (!gatt_exchange_mtu(device->attrib, imtu, exchange_mtu_cb,
+ DBG("mtu %u", mtu);
+
+ if (!gatt_exchange_mtu(device->attrib, mtu, exchange_mtu_cb,
device_ref(device)))
device_unref(device);
}
break;
}
- if (length)
- g_attrib_send(dev->attrib, 0, pdu, length,
- confirmation_cb, NULL, NULL);
+ g_attrib_send(dev->attrib, 0, pdu, length, confirmation_cb, NULL, NULL);
}
static struct app_connection *create_connection(struct gatt_device *device,
}
new_conn->device = device_ref(device);
- new_conn->device->conn_cnt++;
return new_conn;
}
static guint search_dev_for_srvc(struct app_connection *conn, bt_uuid_t *uuid)
{
- struct discover_srvc_data *cb_data =
- new0(struct discover_srvc_data, 1);
+ struct discover_srvc_data *cb_data;
+ cb_data = new0(struct discover_srvc_data, 1);
if (!cb_data) {
error("gatt: Cannot allocate cb data");
return 0;
discover_srvc_by_uuid_cb, cb_data);
}
-
if (conn->app)
return gatt_discover_primary(conn->device->attrib, NULL,
discover_srvc_all_cb, cb_data);
int32_t status;
};
-static void send_app_connect_notifications(void *data, void *user_data)
+static void notify_app_connect_status_by_device(void *data, void *user_data)
{
struct app_connection *conn = data;
struct connect_data *con_data = user_data;
if (conn->device == con_data->dev)
- send_app_connect_notify(conn, con_data->status);
+ notify_app_connect_status(conn, con_data->status);
}
static struct app_connection *find_conn_without_app(struct gatt_device *dev)
static struct app_connection *find_conn(const bdaddr_t *addr, int32_t app_id)
{
struct app_connection conn_match;
- struct gatt_device *dev = NULL;
+ struct gatt_device *dev;
struct gatt_app *app;
/* Check if app is registered */
struct gatt_device *dev = user_data;
uint16_t resp_length = 0;
size_t length;
-
uint8_t *opdu = g_attrib_get_buffer(dev->attrib, &length);
/*
*/
resp_length = enc_confirmation(opdu, length);
- if (resp_length)
- g_attrib_send(dev->attrib, 0, opdu, resp_length, NULL, NULL,
- NULL);
+ g_attrib_send(dev->attrib, 0, opdu, resp_length, NULL, NULL, NULL);
}
static void connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
struct connect_data data;
struct att_range range;
uint32_t status;
+ GError *err = NULL;
GAttrib *attrib;
- uint16_t mtu;
- uint16_t cid;
+ uint16_t mtu, cid;
if (dev->state != DEVICE_CONNECT_READY) {
error("gatt: Device not in a connecting state!?");
goto reply;
}
- if (!bt_io_get(io, &gerr, BT_IO_OPT_IMTU, &mtu, BT_IO_OPT_CID, &cid,
- BT_IO_OPT_INVALID) || cid == ATT_CID)
+ if (!bt_io_get(io, &err, BT_IO_OPT_IMTU, &mtu, BT_IO_OPT_CID, &cid,
+ BT_IO_OPT_INVALID)) {
+ error("gatt: Could not get imtu or cid: %s", err->message);
+ device_set_state(dev, DEVICE_DISCONNECTED);
+ status = GATT_FAILURE;
+ g_error_free(err);
+ goto reply;
+ }
+
+ /* on BR/EDR MTU must not be less then minimal allowed MTU */
+ if (cid != ATT_CID && mtu < ATT_DEFAULT_L2CAP_MTU) {
+ error("gatt: MTU too small (%u bytes)", mtu);
+ device_set_state(dev, DEVICE_DISCONNECTED);
+ status = GATT_FAILURE;
+ goto reply;
+ }
+
+ DBG("mtu %u cid %u", mtu, cid);
+
+ /* on LE we always start with default MTU */
+ if (cid == ATT_CID)
mtu = ATT_DEFAULT_LE_MTU;
attrib = g_attrib_new(io, mtu);
/* Send exchange mtu request as we assume being client and server */
/* TODO: Dont exchange mtu if no client apps */
- send_exchange_mtu_request(dev);
+
+ /* MTU exchange shall not be used on BR/EDR - Vol 3. Part G. 4.3.1 */
+ if (cid == ATT_CID)
+ send_exchange_mtu_request(dev);
/*
* Service Changed Characteristic and CCC Descriptor handles
*/
queue_foreach(dev->autoconnect_apps, create_app_connection, dev);
- if (!dev->conn_cnt) {
+ if (!queue_find(app_connections, match_connection_by_device, dev)) {
struct app_connection *conn;
if (!dev->attrib)
data.dev = dev;
data.status = status;
- queue_foreach(app_connections, send_app_connect_notifications, &data);
+ queue_foreach(app_connections, notify_app_connect_status_by_device,
+ &data);
device_unref(dev);
/* Check if we should restart scan */
DBG("Connection attempt to: %s", addr);
- /*
- * If address type is random it might be that IRK was received and
- * random is just for faking Android Framework. ID address should be
- * used for connection if present.
- */
- if (dev->bdaddr_type == BDADDR_LE_RANDOM) {
- bdaddr = bt_get_id_addr(&dev->bdaddr, &bdaddr_type);
- if (!bdaddr)
- return -EINVAL;
- } else {
- bdaddr = &dev->bdaddr;
- bdaddr_type = dev->bdaddr_type;
- }
+ bdaddr = bt_get_id_addr(&dev->bdaddr, &bdaddr_type);
/*
* This connection will help us catch any PDUs that comes before
* pairing finishes
*/
io = bt_io_connect(connect_cb, device_ref(dev), NULL, &gerr,
- BT_IO_OPT_SOURCE_BDADDR,
- &adapter_addr,
- BT_IO_OPT_SOURCE_TYPE, BDADDR_LE_PUBLIC,
- BT_IO_OPT_DEST_BDADDR, bdaddr,
- BT_IO_OPT_DEST_TYPE, bdaddr_type,
- BT_IO_OPT_CID, ATT_CID,
- BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
- BT_IO_OPT_INVALID);
+ BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+ BT_IO_OPT_SOURCE_TYPE, BDADDR_LE_PUBLIC,
+ BT_IO_OPT_DEST_BDADDR, bdaddr,
+ BT_IO_OPT_DEST_TYPE, bdaddr_type,
+ BT_IO_OPT_CID, ATT_CID,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+ BT_IO_OPT_INVALID);
if (!io) {
error("gatt: Failed bt_io_connect(%s): %s", addr,
gerr->message);
/* Keep this, so we can cancel the connection */
dev->att_io = io;
+ device_set_state(dev, DEVICE_CONNECT_READY);
+
return 0;
}
bt_le_discovery_start();
}
-static void le_device_found_handler(const bdaddr_t *addr, uint8_t addr_type,
- int rssi, uint16_t eir_len,
- const void *eir,
- bool connectable, bool bonded)
+static void le_device_found_handler(const bdaddr_t *addr, int rssi,
+ uint16_t eir_len, const void *eir,
+ bool connectable, bool bonded)
{
uint8_t buf[IPC_MTU];
struct hal_ev_gatt_client_scan_result *ev = (void *) buf;
return;
device_set_state(dev, DEVICE_CONNECT_READY);
- dev->bdaddr_type = addr_type;
/*
* We are ok to perform connect now. Stop discovery
if (app) {
ev.client_if = app->id;
ev.status = GATT_SUCCESS;
- } else
+ } else {
ev.status = GATT_FAILURE;
+ }
/* We should send notification with given in cmd UUID */
memcpy(ev.app_uuid, cmd->uuid, sizeof(ev.app_uuid));
status = HAL_STATUS_FAILED;
goto reply;
}
+
scanning = true;
status = HAL_STATUS_SUCCESS;
status);
}
-static void trigger_disconnection(struct app_connection *connection)
-{
- /* Notify client */
- if (queue_remove(app_connections, connection))
- send_app_disconnect_notify(connection, GATT_SUCCESS);
-
- destroy_connection(connection);
-}
-
-static void app_disconnect_devices(struct gatt_app *client)
-{
- struct app_connection *conn;
-
- /* find every connection for client record and trigger disconnect */
- conn = queue_remove_if(app_connections, match_connection_by_app,
- client);
- while (conn) {
- trigger_disconnection(conn);
-
- conn = queue_remove_if(app_connections,
- match_connection_by_app, client);
- }
-}
-
static int connect_bredr(struct gatt_device *dev)
{
BtIOSecLevel sec_level;
BT_IO_SEC_LOW;
io = bt_io_connect(connect_cb, device_ref(dev), NULL, &gerr,
- BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
- BT_IO_OPT_SOURCE_TYPE, BDADDR_BREDR,
- BT_IO_OPT_DEST_BDADDR, &dev->bdaddr,
- BT_IO_OPT_DEST_TYPE, BDADDR_BREDR,
- BT_IO_OPT_PSM, ATT_PSM,
- BT_IO_OPT_SEC_LEVEL, sec_level,
- BT_IO_OPT_INVALID);
+ BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
+ BT_IO_OPT_SOURCE_TYPE, BDADDR_BREDR,
+ BT_IO_OPT_DEST_BDADDR, &dev->bdaddr,
+ BT_IO_OPT_DEST_TYPE, BDADDR_BREDR,
+ BT_IO_OPT_PSM, ATT_PSM,
+ BT_IO_OPT_SEC_LEVEL, sec_level,
+ BT_IO_OPT_INVALID);
if (!io) {
error("gatt: Failed bt_io_connect(%s): %s", addr,
gerr->message);
return 0;
}
-static bool trigger_connection(struct app_connection *connection)
+static bool trigger_connection(struct app_connection *conn, bool direct)
{
- bool ret;
-
- switch (connection->device->state) {
+ switch (conn->device->state) {
case DEVICE_DISCONNECTED:
/*
* If device was last seen over BR/EDR connect over it.
* Note: Connection state is handled in connect_bredr() func
*/
- if (bt_device_last_seen_bearer(&connection->device->bdaddr) ==
+ if (bt_device_last_seen_bearer(&conn->device->bdaddr) ==
BDADDR_BREDR)
- return connect_bredr(connection->device) == 0;
+ return connect_bredr(conn->device) == 0;
- /* For LE use auto connect feature */
- ret = auto_connect_le(connection->device);
- if (ret)
- device_set_state(connection->device,
- DEVICE_CONNECT_INIT);
- break;
+ if (direct)
+ return connect_le(conn->device) == 0;
+
+ return auto_connect_le(conn->device);
case DEVICE_CONNECTED:
- send_app_connect_notify(connection, GATT_SUCCESS);
- ret = true;
- break;
+ notify_app_connect_status(conn, GATT_SUCCESS);
+ return true;
case DEVICE_CONNECT_READY:
case DEVICE_CONNECT_INIT:
default:
/* In those cases connection is already triggered. */
- ret = true;
- break;
+ return true;
}
-
- return ret;
}
static void remove_autoconnect_device(struct gatt_device *dev)
* Make sure that there is no devices in auto connect list for this
* application
*/
- queue_foreach(gatt_devices, clear_autoconnect_devices, INT_TO_PTR(client_if));
+ queue_foreach(gatt_devices, clear_autoconnect_devices,
+ INT_TO_PTR(client_if));
cl = queue_remove_if(gatt_apps, match_app_by_id, INT_TO_PTR(client_if));
if (!cl) {
}
/* Destroy app connections with proper notifications for this app. */
- app_disconnect_devices(cl);
+ queue_remove_all(app_connections, match_connection_by_app, cl,
+ destroy_connection);
destroy_gatt_app(cl);
return HAL_STATUS_SUCCESS;
DBG("");
- listening_client = queue_find(listen_apps, match_by_value,
+ listening_client = queue_find(listen_apps, NULL,
INT_TO_PTR(cmd->client_if));
if (listening_client) {
HAL_OP_GATT_CLIENT_UNREGISTER, status);
}
-static uint8_t handle_connect(int32_t app_id, const bdaddr_t *addr)
+static uint8_t handle_connect(int32_t app_id, const bdaddr_t *addr, bool direct)
{
struct app_connection conn_match;
struct app_connection *conn;
return HAL_STATUS_NOMEM;
}
- if (!trigger_connection(conn))
+ if (!trigger_connection(conn, direct))
return HAL_STATUS_FAILED;
return HAL_STATUS_SUCCESS;
uint8_t status;
bdaddr_t addr;
- DBG("");
+ DBG("is_direct:%u transport:%u", cmd->is_direct, cmd->transport);
android2bdaddr(&cmd->bdaddr, &addr);
- /* TODO handle is_direct flag */
-
/* TODO handle transport flag */
- status = handle_connect(cmd->client_if, &addr);
+ status = handle_connect(cmd->client_if, &addr, cmd->is_direct);
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_CONNECT,
status);
DBG("");
/* TODO: should we care to match also bdaddr when conn_id is unique? */
- conn = find_connection_by_id(cmd->conn_id);
- if (conn)
- trigger_disconnection(conn);
+ conn = queue_remove_if(app_connections, match_connection_by_id,
+ INT_TO_PTR(cmd->conn_id));
+ destroy_connection(conn);
status = HAL_STATUS_SUCCESS;
goto reply;
}
- listening_client = queue_find(listen_apps, match_by_value,
+ listening_client = queue_find(listen_apps, NULL,
INT_TO_PTR(cmd->client_if));
/* Start listening */
if (cmd->start) {
*/
if (advertising_cnt > 1) {
advertising_cnt--;
- queue_remove(listen_apps,
- INT_TO_PTR(cmd->client_if));
+ queue_remove(listen_apps, INT_TO_PTR(cmd->client_if));
status = HAL_STATUS_SUCCESS;
goto reply;
}
send_client_incl_service_notify(&service->id, incl, conn->id);
}
-static bool search_included_services(struct app_connection *connection,
+static bool search_included_services(struct app_connection *conn,
struct service *service)
{
struct get_included_data *data;
}
data->prim = service;
- data->conn = connection;
+ data->conn = conn;
if (service->primary) {
start = service->prim.range.start;
end = service->incl.range.end;
}
- gatt_find_included(connection->device->attrib, start, end,
- get_included_cb, data);
+ gatt_find_included(conn->device->attrib, start, end, get_included_cb,
+ data);
return true;
}
incl_service = queue_peek_head(prim_service->included);
} else {
uint8_t inst_id = cmd->incl_srvc_id[0].inst_id;
+
incl_service = queue_find(prim_service->included,
match_srvc_by_higher_inst_id,
INT_TO_PTR(inst_id));
}
static void convert_send_client_char_notify(const struct characteristic *ch,
- int32_t conn_id,
- const struct service *service)
+ int32_t conn_id,
+ const struct service *service)
{
struct hal_gatt_srvc_id srvc;
struct hal_gatt_gatt_id charac;
/* Store end handle to use later for descriptors discovery */
if (characteristics->next) {
struct gatt_char *next = characteristics->next->data;
+
ch->end_handle = next->handle - 1;
} else {
ch->end_handle = srvc->primary ? srvc->prim.range.end :
struct discover_char_data *data = user_data;
struct service *srvc = data->service;
+ if (status) {
+ error("gatt: Failed to get characteristics: %s",
+ att_ecode2str(status));
+ convert_send_client_char_notify(NULL, data->conn_id, srvc);
+ goto done;
+ }
+
if (queue_isempty(srvc->chars))
cache_all_srvc_chars(srvc, characteristics);
convert_send_client_char_notify(queue_peek_head(srvc->chars),
data->conn_id, srvc);
+done:
free(data);
}
free(data);
}
-static bool build_descr_cache(struct app_connection *connection,
- struct service *srvc,
- struct characteristic *ch)
+static bool build_descr_cache(struct app_connection *conn, struct service *srvc,
+ struct characteristic *ch)
{
struct discover_desc_data *cb_data;
uint16_t start, end;
if (!cb_data)
return false;
- cb_data->conn = connection;
+ cb_data->conn = conn;
cb_data->srvc = srvc;
cb_data->ch = ch;
- if (!gatt_discover_desc(connection->device->attrib, start, end, NULL,
+ if (!gatt_discover_desc(conn->device->attrib, start, end, NULL,
gatt_discover_desc_cb, cb_data)) {
free(cb_data);
return false;
*/
if (status != HAL_STATUS_SUCCESS)
send_client_read_char_notify(GATT_FAILURE, NULL, 0,
- cmd->conn_id, &srvc_id, &char_id,
- cmd->srvc_id.is_primary);
+ cmd->conn_id, &srvc_id,
+ &char_id,
+ cmd->srvc_id.is_primary);
}
static void send_client_write_char_notify(int32_t status, int32_t conn_id,
memset(csrk, 0, 16);
- if (!bt_get_csrk(&dev->bdaddr, LOCAL_CSRK, csrk, &sign_cnt)) {
+ if (!bt_get_csrk(&dev->bdaddr, true, csrk, &sign_cnt, NULL)) {
error("gatt: Could not get csrk key");
return 0;
}
res = gatt_signed_write_cmd(dev->attrib, handle, value, vlen, crypto,
csrk, sign_cnt, NULL, NULL);
if (!res) {
- error("gatt: Could write signed cmd");
+ error("gatt: Signed write command failed");
return 0;
}
- bt_update_sign_counter(&dev->bdaddr, LOCAL_CSRK, ++sign_cnt);
+ bt_update_sign_counter(&dev->bdaddr, true, ++sign_cnt);
return res;
}
goto failed;
}
- if (get_sec_level(conn->device) != BT_SECURITY_LOW) {
- error("gatt: Cannot write signed on encrypted link");
- status = HAL_STATUS_FAILED;
- goto failed;
- }
-
- res = signed_write_cmd(conn->device, ch->ch.value_handle,
- cmd->value, cmd->len);
+ if (get_sec_level(conn->device) > BT_SECURITY_LOW)
+ res = gatt_write_cmd(conn->device->attrib,
+ ch->ch.value_handle, cmd->value,
+ cmd->len, NULL, NULL);
+ else
+ res = signed_write_cmd(conn->device,
+ ch->ch.value_handle, cmd->value,
+ cmd->len);
break;
default:
error("gatt: Write type %d unsupported", cmd->write_type);
}
static uint8_t test_read_write(bdaddr_t *bdaddr, bt_uuid_t *uuid, uint16_t op,
- uint16_t u2,uint16_t u3,
+ uint16_t u2, uint16_t u3,
uint16_t u4, uint16_t u5)
{
guint16 length = 0;
return HAL_STATUS_UNSUPPORTED;
}
- if (!length)
+ if (!g_attrib_send(dev->attrib, 0, pdu, length, test_command_result,
+ NULL, NULL))
return HAL_STATUS_FAILED;
- g_attrib_send(dev->attrib, 0, pdu, length, test_command_result, NULL,
- NULL);
-
return HAL_STATUS_SUCCESS;
}
break;
case GATT_CLIENT_TEST_CMD_CONNECT:
/* TODO u1 holds device type, for now assume BLE */
- status = handle_connect(test_client_if, &bdaddr);
+ status = handle_connect(test_client_if, &bdaddr, false);
break;
case GATT_CLIENT_TEST_CMD_DISCONNECT:
app = queue_find(gatt_apps, match_app_by_id,
INT_TO_PTR(test_client_if));
- if (app)
- app_disconnect_devices(app);
+ queue_remove_all(app_connections, match_connection_by_app, app,
+ destroy_connection);
status = HAL_STATUS_SUCCESS;
break;
/* TODO: Handle transport flag */
- status = handle_connect(cmd->server_if, &addr);
+ status = handle_connect(cmd->server_if, &addr, cmd->is_direct);
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_SERVER_CONNECT,
status);
DBG("");
/* TODO: should we care to match also bdaddr when conn_id is unique? */
- conn = find_connection_by_id(cmd->conn_id);
- if (conn)
- trigger_disconnection(conn);
+ conn = queue_remove_if(app_connections, match_connection_by_id,
+ INT_TO_PTR(cmd->conn_id));
+ destroy_connection(conn);
status = HAL_STATUS_SUCCESS;
{
const struct pending_request *pending_request = data;
- return pending_request->state == REQUEST_PENDING;
+ return !pending_request->completed;
}
static void send_dev_complete_response(struct gatt_device *device,
return;
}
+ val = queue_peek_head(device->pending_requests);
+ if (!val) {
+ error = ATT_ECODE_ATTR_NOT_FOUND;
+ goto done;
+ }
+
+ if (val->error) {
+ error = val->error;
+ goto done;
+ }
+
switch (opcode) {
case ATT_OP_READ_BY_TYPE_REQ: {
struct att_data_list *adl;
val = queue_pop_head(device->pending_requests);
}
- adl = att_data_list_alloc(queue_length(temp), sizeof(uint16_t) +
- length);
+ adl = att_data_list_alloc(queue_length(temp),
+ sizeof(uint16_t) + length);
- if (val)
- destroy_pending_request(val);
+ destroy_pending_request(val);
val = queue_pop_head(temp);
while (val) {
break;
}
case ATT_OP_READ_BLOB_REQ:
- val = queue_pop_head(device->pending_requests);
- if (val->error) {
- error = val->error;
- goto done;
- }
-
len = enc_read_blob_resp(val->value, val->length, val->offset,
rsp, mtu);
- destroy_pending_request(val);
break;
case ATT_OP_READ_REQ:
- val = queue_pop_head(device->pending_requests);
- if (val->error) {
- error = val->error;
- goto done;
- }
-
len = enc_read_resp(val->value, val->length, rsp, mtu);
- destroy_pending_request(val);
break;
case ATT_OP_READ_BY_GROUP_REQ: {
struct att_data_list *adl;
break;
}
case ATT_OP_EXEC_WRITE_REQ:
- val = queue_pop_head(device->pending_requests);
- if (val->error) {
- error = val->error;
- goto done;
- }
-
len = enc_exec_write_resp(rsp);
- destroy_pending_request(val);
break;
case ATT_OP_WRITE_REQ:
- val = queue_pop_head(device->pending_requests);
- if (val->error) {
- error = val->error;
- goto done;
- }
-
len = enc_write_resp(rsp);
- destroy_pending_request(val);
break;
case ATT_OP_PREP_WRITE_REQ: {
uint16_t handle;
- val = queue_pop_head(device->pending_requests);
- if (val->error) {
- error = val->error;
- goto done;
- }
-
handle = gatt_db_attribute_get_handle(val->attrib);
-
len = enc_prep_write_resp(handle, val->offset, val->value,
val->length, rsp, mtu);
- destroy_pending_request(val);
break;
}
default:
BT_IO_OPT_INVALID))
return ATT_ECODE_UNLIKELY;
- DBG("opcode %u permissions %u sec_level %u", opcode, permissions,
+ DBG("opcode 0x%02x permissions %u sec_level %u", opcode, permissions,
sec_level);
switch (opcode) {
if (!(permissions & GATT_PERM_WRITE_SIGNED))
return ATT_ECODE_WRITE_NOT_PERM;
- if ((permissions & GATT_PERM_WRITE_SIGNED_MITM) &&
- sec_level < BT_SECURITY_HIGH)
+ if (permissions & GATT_PERM_WRITE_SIGNED_MITM) {
+ bool auth;
+
+ if (bt_get_csrk(&device->bdaddr, true, NULL, NULL,
+ &auth) && auth)
+ break;
+
return ATT_ECODE_AUTHENTICATION;
+ }
break;
case ATT_OP_READ_BY_TYPE_REQ:
case ATT_OP_READ_REQ:
resp_data->length = length;
resp_data->error = error;
- resp_data->state = REQUEST_DONE;
+ resp_data->completed = true;
if (!length)
return;
{
struct pending_request *resp_data = data;
struct request_processing_data *process_data = user_data;
+ struct bt_att *att = g_attrib_get_att(process_data->device->attrib);
struct gatt_db_attribute *attrib;
uint32_t permissions;
uint8_t error;
attrib = resp_data->attrib;
if (!attrib) {
resp_data->error = ATT_ECODE_ATTR_NOT_FOUND;
- resp_data->state = REQUEST_DONE;
+ resp_data->completed = true;
return;
}
- gatt_db_attribute_get_permissions(attrib, &permissions);
+ permissions = gatt_db_attribute_get_permissions(attrib);
/*
* Check if it is attribute we didn't declare permissions, like service
permissions);
if (error != 0) {
resp_data->error = error;
- resp_data->state = REQUEST_DONE;
+ resp_data->completed = true;
return;
}
- resp_data->state = REQUEST_PENDING;
-
gatt_db_attribute_read(attrib, resp_data->offset, process_data->opcode,
- &process_data->device->bdaddr,
- attribute_read_cb, resp_data);
+ att, attribute_read_cb, resp_data);
}
static void process_dev_pending_requests(struct gatt_device *device,
return transaction;
}
+static bool get_dst_addr(struct bt_att *att, bdaddr_t *dst)
+{
+ GIOChannel *io = NULL;
+ GError *gerr = NULL;
+
+ io = g_io_channel_unix_new(bt_att_get_fd(att));
+ if (!io)
+ return false;
+
+ bt_io_get(io, &gerr, BT_IO_OPT_DEST_BDADDR, dst, BT_IO_OPT_INVALID);
+ if (gerr) {
+ error("gatt: bt_io_get: %s", gerr->message);
+ g_error_free(gerr);
+ g_io_channel_unref(io);
+ return false;
+ }
+
+ g_io_channel_unref(io);
+ return true;
+}
+
static void read_cb(struct gatt_db_attribute *attrib, unsigned int id,
- uint16_t offset, uint8_t opcode, bdaddr_t *bdaddr,
+ uint16_t offset, uint8_t opcode, struct bt_att *att,
void *user_data)
{
struct pending_trans_data *transaction;
struct gatt_app *app;
struct app_connection *conn;
int32_t app_id = PTR_TO_INT(user_data);
+ bdaddr_t bdaddr;
DBG("id %u", id);
goto failed;
}
- conn = find_conn(bdaddr, app->id);
+ if (!get_dst_addr(att, &bdaddr)) {
+ error("gatt: read_cb, could not obtain dst BDADDR");
+ goto failed;
+ }
+
+ conn = find_conn(&bdaddr, app->id);
if (!conn) {
error("gatt: read_cb, cound not found connection");
goto failed;
if (!transaction)
goto failed;
- bdaddr2android(bdaddr, ev.bdaddr);
+ bdaddr2android(&bdaddr, ev.bdaddr);
ev.conn_id = conn->id;
ev.attr_handle = gatt_db_attribute_get_handle(attrib);
ev.offset = offset;
static void write_cb(struct gatt_db_attribute *attrib, unsigned int id,
uint16_t offset, const uint8_t *value, size_t len,
- uint8_t opcode, bdaddr_t *bdaddr, void *user_data)
+ uint8_t opcode, struct bt_att *att, void *user_data)
{
uint8_t buf[IPC_MTU];
struct hal_ev_gatt_server_request_write *ev = (void *) buf;
struct gatt_app *app;
int32_t app_id = PTR_TO_INT(user_data);
struct app_connection *conn;
+ bdaddr_t bdaddr;
DBG("id %u", id);
goto failed;
}
- conn = find_conn(bdaddr, app->id);
+ if (!get_dst_addr(att, &bdaddr)) {
+ error("gatt: write_cb, could not obtain dst BDADDR");
+ goto failed;
+ }
+
+ conn = find_conn(&bdaddr, app->id);
if (!conn) {
error("gatt: write_cb could not found connection");
goto failed;
memset(ev, 0, sizeof(*ev));
- bdaddr2android(bdaddr, &ev->bdaddr);
+ bdaddr2android(&bdaddr, &ev->bdaddr);
ev->attr_handle = gatt_db_attribute_get_handle(attrib);
ev->offset = offset;
uint16_t lp = ATT_PSM;
record = sdp_record_alloc();
- if (record == NULL)
+ if (!record)
return NULL;
sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
HAL_OP_GATT_SERVER_DELETE_SERVICE, status);
}
+static void indication_confirmation_cb(guint8 status, const guint8 *pdu,
+ guint16 len, gpointer user_data)
+{
+ struct hal_ev_gatt_server_indication_sent ev;
+
+ ev.status = status;
+ ev.conn_id = PTR_TO_UINT(user_data);
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_SERVER_INDICATION_SENT, sizeof(ev), &ev);
+}
+
static void handle_server_send_indication(const void *buf, uint16_t len)
{
const struct hal_cmd_gatt_server_send_indication *cmd = buf;
pdu = g_attrib_get_buffer(conn->device->attrib, &mtu);
if (cmd->confirm) {
- /* TODO: Add data to track confirmation for this request */
length = enc_indication(cmd->attribute_handle,
- (uint8_t *)cmd->value, cmd->len, pdu,
+ (uint8_t *) cmd->value, cmd->len, pdu,
mtu);
- confirmation_cb = ignore_confirmation_cb;
+ confirmation_cb = indication_confirmation_cb;
} else {
length = enc_notification(cmd->attribute_handle,
- (uint8_t *)cmd->value, cmd->len,
- pdu, mtu);
+ (uint8_t *) cmd->value,
+ cmd->len, pdu, mtu);
}
- if (length == 0) {
- error("gatt: Failed to encode indication");
+ if (!g_attrib_send(conn->device->attrib, 0, pdu, length,
+ confirmation_cb, UINT_TO_PTR(conn->id), NULL)) {
+ error("gatt: Failed to send indication");
status = HAL_STATUS_FAILED;
} else {
- g_attrib_send(conn->device->attrib, 0, pdu, length,
- confirmation_cb, NULL, NULL);
status = HAL_STATUS_SUCCESS;
}
+ /* Here we confirm failed indications and all notifications */
+ if (status || !confirmation_cb)
+ indication_confirmation_cb(status, NULL, 0,
+ UINT_TO_PTR(conn->id));
+
reply:
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
HAL_OP_GATT_SERVER_SEND_INDICATION, status);
/* Cast status to uint8_t, due to (byte) cast in java layer. */
req->error = err_to_att((uint8_t) cmd->status);
- req->state = REQUEST_DONE;
+ req->completed = true;
/*
* FIXME: Handle situation when not all server applications
return ATT_ECODE_INSUFF_RESOURCES;
}
- data->state = REQUEST_INIT;
data->attrib = attrib;
if (!queue_push_tail(device->pending_requests, data)) {
free(data);
data->offset = offset;
data->attrib = attrib;
- data->state = REQUEST_INIT;
if (!queue_push_tail(dev->pending_requests, data)) {
free(data);
return ATT_ECODE_INSUFF_RESOURCES;
static uint8_t mtu_att_handle(const uint8_t *cmd, uint16_t cmd_len,
struct gatt_device *dev)
{
- uint16_t mtu, imtu, omtu;
+ uint16_t rmtu, mtu, len;
size_t length;
- GIOChannel *io;
- GError *gerr = NULL;
- uint16_t len;
uint8_t *rsp;
DBG("");
- len = dec_mtu_req(cmd, cmd_len, &mtu);
+ len = dec_mtu_req(cmd, cmd_len, &rmtu);
if (!len)
return ATT_ECODE_INVALID_PDU;
- if (mtu < ATT_DEFAULT_LE_MTU)
- return ATT_ECODE_REQ_NOT_SUPP;
+ /* MTU exchange shall not be used on BR/EDR - Vol 3. Part G. 4.3.1 */
+ if (get_cid(dev) != ATT_CID)
+ return ATT_ECODE_UNLIKELY;
- io = g_attrib_get_channel(dev->attrib);
+ if (!get_local_mtu(dev, &mtu))
+ return ATT_ECODE_UNLIKELY;
- bt_io_get(io, &gerr,
- BT_IO_OPT_IMTU, &imtu,
- BT_IO_OPT_OMTU, &omtu,
- BT_IO_OPT_INVALID);
- if (gerr) {
- error("bt_io_get: %s", gerr->message);
- g_error_free(gerr);
+ if (!update_mtu(dev, rmtu))
return ATT_ECODE_UNLIKELY;
- }
rsp = g_attrib_get_buffer(dev->attrib, &length);
- /* Respond with our IMTU */
- len = enc_mtu_resp(imtu, rsp, length);
- if (!len)
+ /* Respond with our MTU */
+ len = enc_mtu_resp(mtu, rsp, length);
+ if (!g_attrib_send(dev->attrib, 0, rsp, len, NULL, NULL, NULL))
return ATT_ECODE_UNLIKELY;
- g_attrib_send(dev->attrib, 0, rsp, len, NULL, NULL, NULL);
-
- /* Limit OMTU to received value */
- mtu = MIN(mtu, omtu);
- g_attrib_set_mtu(dev->attrib, mtu);
-
return 0;
}
static uint8_t find_info_handle(const uint8_t *cmd, uint16_t cmd_len,
- uint8_t *rsp, size_t rsp_size,
- uint16_t *length)
+ uint8_t *rsp, size_t rsp_size, uint16_t *length)
{
struct gatt_db_attribute *attrib;
struct queue *q, *temp;
int iterator = 0;
uint16_t start, end;
uint16_t len, queue_len;
+ uint8_t format;
uint8_t ret = 0;
DBG("");
memcpy(&value[2], &type->value, len);
}
- len = enc_find_info_resp(len == 2 ? ATT_FIND_INFO_RESP_FMT_16BIT :
- ATT_FIND_INFO_RESP_FMT_128BIT, adl, rsp,
- rsp_size);
+ if (len == 2)
+ format = ATT_FIND_INFO_RESP_FMT_16BIT;
+ else
+ format = ATT_FIND_INFO_RESP_FMT_128BIT;
+
+ len = enc_find_info_resp(format, adl, rsp, rsp_size);
if (!len)
ret = ATT_ECODE_UNLIKELY;
return ret;
}
+struct find_by_type_request_data {
+ struct gatt_device *device;
+ uint8_t *search_value;
+ size_t search_vlen;
+ uint8_t error;
+};
+
+static void find_by_type_request_cb(struct gatt_db_attribute *attrib,
+ void *user_data)
+{
+ struct find_by_type_request_data *find_data = user_data;
+ struct pending_request *request_data;
+
+ if (find_data->error)
+ return;
+
+ request_data = new0(struct pending_request, 1);
+ if (!request_data) {
+ find_data->error = ATT_ECODE_INSUFF_RESOURCES;
+ return;
+ }
+
+ request_data->filter_value = malloc0(find_data->search_vlen);
+ if (!request_data->filter_value) {
+ destroy_pending_request(request_data);
+ find_data->error = ATT_ECODE_INSUFF_RESOURCES;
+ return;
+ }
+
+ request_data->attrib = attrib;
+ request_data->filter_vlen = find_data->search_vlen;
+ memcpy(request_data->filter_value, find_data->search_value,
+ find_data->search_vlen);
+
+ if (!queue_push_tail(find_data->device->pending_requests,
+ request_data)) {
+ destroy_pending_request(request_data);
+ find_data->error = ATT_ECODE_INSUFF_RESOURCES;
+ }
+}
+
static uint8_t find_by_type_request(const uint8_t *cmd, uint16_t cmd_len,
struct gatt_device *device)
{
uint8_t search_value[cmd_len];
size_t search_vlen;
uint16_t start, end;
- struct queue *q;
bt_uuid_t uuid;
uint16_t len;
+ struct find_by_type_request_data data;
DBG("");
if (start > end || start == 0)
return ATT_ECODE_INVALID_HANDLE;
- q = queue_new();
- if (!q)
- return ATT_ECODE_UNLIKELY;
+ data.error = 0;
+ data.search_vlen = search_vlen;
+ data.search_value = search_value;
+ data.device = device;
- gatt_db_find_by_type(gatt_db, start, end, &uuid, q);
-
- if (queue_isempty(q)) {
+ if (gatt_db_find_by_type(gatt_db, start, end, &uuid,
+ find_by_type_request_cb, &data) == 0) {
size_t mtu;
uint8_t *rsp = g_attrib_get_buffer(device->attrib, &mtu);
len = enc_error_resp(ATT_OP_FIND_BY_TYPE_REQ, start,
ATT_ECODE_ATTR_NOT_FOUND, rsp, mtu);
g_attrib_send(device->attrib, 0, rsp, len, NULL, NULL, NULL);
- queue_destroy(q, NULL);
return 0;
}
- while (queue_peek_head(q)) {
- struct gatt_db_attribute *attrib = queue_pop_head(q);
- struct pending_request *data;
+ if (!data.error)
+ process_dev_pending_requests(device, ATT_OP_FIND_BY_TYPE_REQ);
- data = new0(struct pending_request, 1);
- if (!data) {
- queue_destroy(q, NULL);
- return ATT_ECODE_INSUFF_RESOURCES;
- }
-
- data->filter_value = malloc0(search_vlen);
- if (!data->filter_value) {
- destroy_pending_request(data);
- queue_destroy(q, NULL);
- return ATT_ECODE_INSUFF_RESOURCES;
- }
-
- data->state = REQUEST_INIT;
- data->attrib = attrib;
- data->filter_vlen = search_vlen;
- memcpy(data->filter_value, search_value, search_vlen);
-
- queue_push_tail(device->pending_requests, data);
- }
-
- queue_destroy(q, NULL);
-
- process_dev_pending_requests(device, ATT_OP_FIND_BY_TYPE_REQ);
-
- return 0;
+ return data.error;
}
static void write_confirm(struct gatt_db_attribute *attrib,
if (!attrib)
return;
- if (!gatt_db_attribute_get_permissions(attrib, &permissions))
- return;
+ permissions = gatt_db_attribute_get_permissions(attrib);
if (check_device_permissions(dev, cmd[0], permissions))
return;
- gatt_db_attribute_write(attrib, 0, value, vlen, cmd[0], &dev->bdaddr,
- write_confirm, NULL);
+ gatt_db_attribute_write(attrib, 0, value, vlen, cmd[0],
+ g_attrib_get_att(dev->attrib),
+ write_confirm, NULL);
}
static void write_signed_cmd_request(const uint8_t *cmd, uint16_t cmd_len,
return;
}
- if (!bt_get_csrk(&dev->bdaddr, REMOTE_CSRK, csrk, &sign_cnt)) {
+ if (!bt_get_csrk(&dev->bdaddr, false, csrk, &sign_cnt, NULL)) {
error("gatt: No valid csrk from remote device");
return;
}
if (!attrib)
return;
- gatt_db_attribute_get_permissions(attrib, &permissions);
+ permissions = gatt_db_attribute_get_permissions(attrib);
if (check_device_permissions(dev, cmd[0], permissions))
return;
return;
}
/* Signature OK, proceed with write */
- bt_update_sign_counter(&dev->bdaddr, REMOTE_CSRK, r_sign_cnt);
+ bt_update_sign_counter(&dev->bdaddr, false, r_sign_cnt);
gatt_db_attribute_write(attrib, 0, value, vlen, cmd[0],
- &dev->bdaddr, write_confirm, NULL);
+ g_attrib_get_att(dev->attrib),
+ write_confirm, NULL);
}
}
data->attrib = attrib;
data->error = error;
- data->state = REQUEST_DONE;
+ data->completed = true;
}
static uint8_t write_req_request(const uint8_t *cmd, uint16_t cmd_len,
if (!attrib)
return ATT_ECODE_ATTR_NOT_FOUND;
- gatt_db_attribute_get_permissions(attrib, &permissions);
+ permissions = gatt_db_attribute_get_permissions(attrib);
error = check_device_permissions(dev, cmd[0], permissions);
if (error)
return ATT_ECODE_INSUFF_RESOURCES;
data->attrib = attrib;
- data->state = REQUEST_PENDING;
if (!queue_push_tail(dev->pending_requests, data)) {
free(data);
}
if (!gatt_db_attribute_write(attrib, 0, value, vlen, cmd[0],
- &dev->bdaddr, attribute_write_cb,
- data)) {
+ g_attrib_get_att(dev->attrib),
+ attribute_write_cb, data)) {
queue_remove(dev->pending_requests, data);
free(data);
return ATT_ECODE_UNLIKELY;
if (!attrib)
return ATT_ECODE_ATTR_NOT_FOUND;
- gatt_db_attribute_get_permissions(attrib, &permissions);
+ permissions = gatt_db_attribute_get_permissions(attrib);
error = check_device_permissions(dev, cmd[0], permissions);
if (error)
data->attrib = attrib;
data->offset = offset;
- data->state = REQUEST_PENDING;
if (!queue_push_tail(dev->pending_requests, data)) {
free(data);
data->length = vlen;
if (!gatt_db_attribute_write(attrib, offset, value, vlen, cmd[0],
- &dev->bdaddr, attribute_write_cb,
- data)) {
+ g_attrib_get_att(dev->attrib),
+ attribute_write_cb, data)) {
queue_remove(dev->pending_requests, data);
g_free(data->value);
free(data);
ev->trans_id = transaction->id;
ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
- HAL_EV_GATT_SERVER_REQUEST_EXEC_WRITE,
- sizeof(*ev), ev);
+ HAL_EV_GATT_SERVER_REQUEST_EXEC_WRITE, sizeof(*ev), ev);
}
static uint8_t write_execute_request(const uint8_t *cmd, uint16_t cmd_len,
if (!data)
return ATT_ECODE_INSUFF_RESOURCES;
- data->state = REQUEST_PENDING;
if (!queue_push_tail(dev->pending_requests, data)) {
free(data);
return ATT_ECODE_INSUFF_RESOURCES;
break;
case ATT_OP_WRITE_REQ:
status = write_req_request(ipdu, len, dev);
- if (!status)
- return;
break;
case ATT_OP_WRITE_CMD:
write_cmd_request(ipdu, len, dev);
return;
case ATT_OP_PREP_WRITE_REQ:
status = write_prep_request(ipdu, len, dev);
- if (!status)
- return;
break;
case ATT_OP_FIND_BY_TYPE_REQ:
status = find_by_type_request(ipdu, len, dev);
break;
case ATT_OP_EXEC_WRITE_REQ:
status = write_execute_request(ipdu, len, dev);
- if (!status)
- return;
break;
case ATT_OP_READ_MULTI_REQ:
default:
DBG("Unsupported request 0x%02x", ipdu[0]);
status = ATT_ECODE_REQ_NOT_SUPP;
- goto done;
+ break;
}
done:
resp_length = enc_error_resp(ipdu[0], 0x0000, status, opdu,
length);
- if (resp_length)
- g_attrib_send(dev->attrib, 0, opdu, resp_length, NULL, NULL,
- NULL);
+ g_attrib_send(dev->attrib, 0, opdu, resp_length, NULL, NULL, NULL);
}
static void connect_confirm(GIOChannel *io, void *user_data)
{
struct gatt_device *dev;
- uint8_t dst_type;
bdaddr_t dst;
GError *gerr = NULL;
DBG("");
- bt_io_get(io, &gerr,
- BT_IO_OPT_DEST_BDADDR, &dst,
- BT_IO_OPT_DEST_TYPE, &dst_type,
- BT_IO_OPT_INVALID);
+ bt_io_get(io, &gerr, BT_IO_OPT_DEST_BDADDR, &dst, BT_IO_OPT_INVALID);
if (gerr) {
error("gatt: bt_io_get: %s", gerr->message);
g_error_free(gerr);
}
}
- dev->bdaddr_type = dst_type;
-
if (!bt_io_accept(io, connect_cb, device_ref(dev), NULL, NULL)) {
error("gatt: failed to accept connection");
device_unref(dev);
static void device_name_read_cb(struct gatt_db_attribute *attrib,
unsigned int id, uint16_t offset,
- uint8_t opcode, bdaddr_t *bdaddr,
+ uint8_t opcode, struct bt_att *att,
void *user_data)
{
const char *name = bt_get_adapter_name();
NULL, NULL, NULL);
if (gap_srvc_data.priv) {
uint8_t value;
+
/* Store privacy into db */
value = PERIPHERAL_PRIVACY_DISABLE;
gatt_db_attribute_write(gap_srvc_data.priv, 0,
static void device_info_read_cb(struct gatt_db_attribute *attrib,
unsigned int id, uint16_t offset,
- uint8_t opcode, bdaddr_t *bdaddr,
+ uint8_t opcode, struct bt_att *att,
void *user_data)
{
char *buf = user_data;
static void device_info_read_system_id_cb(struct gatt_db_attribute *attrib,
unsigned int id, uint16_t offset,
- uint8_t opcode, bdaddr_t *bdaddr,
+ uint8_t opcode, struct bt_att *att,
void *user_data)
{
uint8_t pdu[8];
static void device_info_read_pnp_id_cb(struct gatt_db_attribute *attrib,
unsigned int id, uint16_t offset,
- uint8_t opcode, bdaddr_t *bdaddr,
+ uint8_t opcode, struct bt_att *att,
void *user_data)
{
uint8_t pdu[7];
/* Device Information Service */
bt_uuid16_create(&uuid, 0x180a);
- service = gatt_db_add_service(gatt_db, &uuid, true, 15);
+ service = gatt_db_add_service(gatt_db, &uuid, true, 17);
/* User data are not const hence (void *) cast is used */
data = bt_config_get_name();
static void gatt_srvc_change_write_cb(struct gatt_db_attribute *attrib,
unsigned int id, uint16_t offset,
const uint8_t *value, size_t len,
- uint8_t opcode, bdaddr_t *bdaddr,
+ uint8_t opcode, struct bt_att *att,
void *user_data)
{
struct gatt_device *dev;
+ bdaddr_t bdaddr;
- dev = find_device_by_addr(bdaddr);
+ if (!get_dst_addr(att, &bdaddr)) {
+ error("gatt: srvc_change_write_cb, could not obtain BDADDR");
+ return;
+ }
+
+ dev = find_device_by_addr(&bdaddr);
if (!dev) {
error("gatt: Could not find device ?!");
return;
}
- if (!bt_device_is_bonded(bdaddr)) {
+ if (!bt_device_is_bonded(&bdaddr)) {
gatt_db_attribute_write_result(attrib, id,
ATT_ECODE_AUTHORIZATION);
return;
}
/* Set services changed indication value */
- bt_store_gatt_ccc(bdaddr, get_le16(value));
+ bt_store_gatt_ccc(&bdaddr, get_le16(value));
gatt_db_attribute_write_result(attrib, id, 0);
}
static void gatt_srvc_change_read_cb(struct gatt_db_attribute *attrib,
unsigned int id, uint16_t offset,
- uint8_t opcode, bdaddr_t *bdaddr,
+ uint8_t opcode, struct bt_att *att,
void *user_data)
{
struct gatt_device *dev;
uint8_t pdu[2];
+ bdaddr_t bdaddr;
- dev = find_device_by_addr(bdaddr);
+ if (!get_dst_addr(att, &bdaddr)) {
+ error("gatt: srvc_change_read_cb, could not obtain BDADDR");
+ return;
+ }
+
+ dev = find_device_by_addr(&bdaddr);
if (!dev) {
error("gatt: Could not find device ?!");
return;
return true;
}
-static void gatt_paired_cb(const bdaddr_t *addr, uint8_t type)
+static void gatt_paired_cb(const bdaddr_t *addr)
{
struct gatt_device *dev;
char address[18];
if (!dev)
return;
- if (dev->bdaddr_type != type)
- return;
-
ba2str(addr, address);
DBG("Paired device %s", address);
search_dev_for_srvc(conn, NULL);
}
-static void gatt_unpaired_cb(const bdaddr_t *addr, uint8_t type)
+static void gatt_unpaired_cb(const bdaddr_t *addr)
{
struct gatt_device *dev;
char address[18];
if (!dev)
return;
- if (dev->bdaddr_type != type)
- return;
-
ba2str(addr, address);
DBG("Unpaired device %s", address);
ipc_unregister(hal_ipc, HAL_SERVICE_ID_GATT);
hal_ipc = NULL;
- queue_destroy(gatt_apps, destroy_gatt_app);
- gatt_apps = NULL;
-
queue_destroy(app_connections, destroy_connection);
app_connections = NULL;
+ queue_destroy(gatt_apps, destroy_gatt_app);
+ gatt_apps = NULL;
+
queue_destroy(gatt_devices, destroy_device);
gatt_devices = NULL;
{
uint8_t status;
- status = handle_connect(id, addr);
+ status = handle_connect(id, addr, false);
return status != HAL_STATUS_FAILED;
}
match.device = device;
match.app = app;
- conn = queue_find(app_connections, match_connection_by_device_and_app,
- &match);
+ conn = queue_remove_if(app_connections,
+ match_connection_by_device_and_app, &match);
if (!conn)
return false;
- trigger_disconnection(conn);
+ destroy_connection(conn);
return true;
}
if (queue_isempty(dev->autoconnect_apps))
device_ref(dev);
- if (!queue_find(dev->autoconnect_apps, match_by_value,
- INT_TO_PTR(id)))
+ if (!queue_find(dev->autoconnect_apps, NULL, INT_TO_PTR(id)))
return queue_push_head(dev->autoconnect_apps, INT_TO_PTR(id));
return true;
Command parameters: Socket type (1 octet)
Service name (256 octets)
Service UUID (16 octets)
- Channel (2 octets)
+ Channel (4 octets)
Socket flags (1 octet)
Response parameters: File descriptor (inline)
Command parameters: Remote address (6 octets)
Socket type (1 octet)
Service UUID (16 octets)
- Channel (2 octets)
+ Channel (4 octets)
Socket flags (1 octet)
Response parameters: File descriptor (inline)
Valid State values: 0x00 = Disconnected
0x01 = Connecting
- 0x02 = SLC Connected
- 0x03 = Disconnecting
+ 0x02 = Connected
+ 0x03 = SLC Connected
+ 0x04 = Disconnecting
Peer Features is a bitmask of the supported features. Currently
available bits:
#define HAL_HF_CLIENT_CONN_STATE_DISCONNECTED 0x00
#define HAL_HF_CLIENT_CONN_STATE_CONNECTING 0x01
-#define HAL_HF_CLIENT_CONN_STATE_SLC_CONNECTED 0x02
-#define HAL_HF_CLIENT_CONN_STATE_DISCONNECTING 0x03
+#define HAL_HF_CLIENT_CONN_STATE_CONNECTED 0x02
+#define HAL_HF_CLIENT_CONN_STATE_SLC_CONNECTED 0x03
+#define HAL_HF_CLIENT_CONN_STATE_DISCONNECTING 0x04
#define HAL_HF_CLIENT_PEER_FEAT_3WAY 0x00000001
#define HAL_HF_CLIENT_PEER_FEAT_ECNR 0x00000002
goto failed;
}
+ device_set_state(dev, HAL_HF_CLIENT_CONN_STATE_CONNECTED);
+
return;
failed:
int num_active;
int num_held;
int setup_state;
- bool call_hanging_up;
+ guint call_hanging_up;
uint8_t negotiated_codec;
uint8_t proposed_codec;
if (dev->audio_state == HAL_EV_HANDSFREE_AUDIO_STATE_CONNECTED)
bt_sco_disconnect(sco);
- g_source_remove(dev->ring);
+ if (dev->ring)
+ g_source_remove(dev->ring);
+
g_free(dev->clip);
+ if (dev->call_hanging_up)
+ g_source_remove(dev->call_hanging_up);
+
set_audio_state(dev, HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED);
set_state(dev, HAL_EV_HANDSFREE_CONN_STATE_DISCONNECTED);
uint8_t buf[IPC_MTU];
struct hal_ev_handsfree_unknown_at *ev = (void *) buf;
- if (dev->state != HAL_EV_HANDSFREE_CONN_STATE_SLC_CONNECTED) {
- hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
- hfp_gw_disconnect(dev->gw);
- return;
- }
-
bdaddr2android(&dev->bdaddr, ev->bdaddr);
/* copy while string including terminating NULL */
ev->len = strlen(command) + 1;
- memcpy(ev->buf, command, ev->len);
if (ev->len > IPC_MTU - sizeof(*ev)) {
hfp_gw_send_result(dev->gw, HFP_RESULT_ERROR);
return;
}
+ memcpy(ev->buf, command, ev->len);
+
ipc_send_notif(hal_ipc, HAL_SERVICE_ID_HANDSFREE,
HAL_EV_HANDSFREE_UNKNOWN_AT, sizeof(*ev) + ev->len, ev);
}
return;
}
- if (status != SCO_STATUS_OK) {
- error("handsfree: audio connect failed");
-
- set_audio_state(dev, HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED);
-
- if (!codec_negotiation_supported(dev))
- return;
-
- /* If other failed, try connecting with CVSD */
- if (dev->negotiated_codec != CODEC_ID_CVSD) {
- info("handsfree: trying fallback with CVSD");
- select_codec(dev, CODEC_ID_CVSD);
- }
+ if (status == SCO_STATUS_OK) {
+ set_audio_state(dev, HAL_EV_HANDSFREE_AUDIO_STATE_CONNECTED);
+ return;
+ }
+ /* Try fallback to CVSD first */
+ if (codec_negotiation_supported(dev) &&
+ dev->negotiated_codec != CODEC_ID_CVSD) {
+ info("handsfree: trying fallback with CVSD");
+ select_codec(dev, CODEC_ID_CVSD);
return;
}
- set_audio_state(dev, HAL_EV_HANDSFREE_AUDIO_STATE_CONNECTED);
+ error("handsfree: audio connect failed");
+ set_audio_state(dev, HAL_EV_HANDSFREE_AUDIO_STATE_DISCONNECTED);
}
static bool connect_sco(struct hf_device *dev)
static void register_post_slc_at(struct hf_device *dev)
{
+ hfp_gw_set_command_handler(dev->gw, at_cmd_unknown, dev, NULL);
+
if (dev->hsp) {
hfp_gw_register(dev->gw, at_cmd_ckpd, "+CKPD", dev, NULL);
hfp_gw_register(dev->gw, at_cmd_vgs, "+VGS", dev, NULL);
if (!hfp_context_get_number(result, &val) || val > 1)
break;
+ dev->indicators_enabled = val;
+
+ /* skip bfr if present */
+ hfp_context_get_number(result, &val);
+
if (hfp_context_has_next(result))
break;
- dev->indicators_enabled = val;
-
hfp_gw_send_result(dev->gw, HFP_RESULT_OK);
if (dev->features & HFP_HF_FEAT_3WAY)
g_io_channel_set_close_on_unref(chan, FALSE);
hfp_gw_set_close_on_unref(dev->gw, true);
- hfp_gw_set_command_handler(dev->gw, at_cmd_unknown, dev, NULL);
hfp_gw_set_disconnect_handler(dev->gw, disconnect_watch, dev, NULL);
if (dev->hsp) {
static void sdp_hsp_search_cb(sdp_list_t *recs, int err, gpointer data)
{
struct hf_device *dev = data;
- sdp_list_t *protos, *classes;
+ sdp_list_t *protos;
GError *gerr = NULL;
GIOChannel *io;
- uuid_t uuid;
+ uuid_t class;
int channel;
DBG("");
goto fail;
}
- if (!recs || !recs->data) {
- info("handsfree: no HSP SDP records found");
- goto fail;
+ sdp_uuid16_create(&class, HEADSET_SVCLASS_ID);
+
+ /* Find record with proper service class */
+ for (; recs; recs = recs->next) {
+ sdp_record_t *rec = recs->data;
+
+ if (rec && !sdp_uuid_cmp(&rec->svclass, &class))
+ break;
}
- if (sdp_get_service_classes(recs->data, &classes) < 0 || !classes) {
- error("handsfree: unable to get service classes from record");
+ if (!recs || !recs->data) {
+ info("handsfree: no valid HSP SDP records found");
goto fail;
}
if (sdp_get_access_protos(recs->data, &protos) < 0) {
error("handsfree: unable to get access protocols from record");
- sdp_list_free(classes, free);
goto fail;
}
/* TODO read remote version? */
/* TODO read volume control support */
- memcpy(&uuid, classes->data, sizeof(uuid));
- sdp_list_free(classes, free);
-
- if (!sdp_uuid128_to_uuid(&uuid) || uuid.type != SDP_UUID16 ||
- uuid.value.uuid16 != HEADSET_SVCLASS_ID) {
- sdp_list_free(protos, NULL);
- error("handsfree: invalid service record or not HSP");
- goto fail;
- }
-
channel = sdp_get_proto_port(protos, RFCOMM_UUID);
sdp_list_foreach(protos, (sdp_list_func_t) sdp_list_free, NULL);
sdp_list_free(protos, NULL);
static void sdp_hfp_search_cb(sdp_list_t *recs, int err, gpointer data)
{
struct hf_device *dev = data;
- sdp_list_t *protos, *classes;
+ sdp_list_t *protos;
GError *gerr = NULL;
GIOChannel *io;
- uuid_t uuid;
+ uuid_t class;
int channel;
DBG("");
goto fail;
}
+ sdp_uuid16_create(&class, HANDSFREE_SVCLASS_ID);
+
+ /* Find record with proper service class */
+ for (; recs; recs = recs->next) {
+ sdp_record_t *rec = recs->data;
+
+ if (rec && !sdp_uuid_cmp(&rec->svclass, &class))
+ break;
+ }
+
if (!recs || !recs->data) {
info("handsfree: no HFP SDP records found, trying HSP");
return;
}
- if (sdp_get_service_classes(recs->data, &classes) < 0 || !classes) {
- error("handsfree: unable to get service classes from record");
- goto fail;
- }
-
if (sdp_get_access_protos(recs->data, &protos) < 0) {
error("handsfree: unable to get access protocols from record");
- sdp_list_free(classes, free);
- goto fail;
- }
-
- /* TODO read remote version? */
-
- memcpy(&uuid, classes->data, sizeof(uuid));
- sdp_list_free(classes, free);
-
- if (!sdp_uuid128_to_uuid(&uuid) || uuid.type != SDP_UUID16 ||
- uuid.value.uuid16 != HANDSFREE_SVCLASS_ID) {
- sdp_list_free(protos, NULL);
- error("handsfree: invalid service record or not HFP");
goto fail;
}
goto fail;
}
+ /* TODO read remote version? */
+
io = bt_io_connect(connect_cb, dev, NULL, &gerr,
BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
BT_IO_OPT_DEST_BDADDR, &dev->bdaddr,
static void phone_state_dialing(struct hf_device *dev, int num_active,
int num_held)
{
+ if (dev->call_hanging_up) {
+ g_source_remove(dev->call_hanging_up);
+ dev->call_hanging_up = 0;
+ }
+
update_indicator(dev, IND_CALLSETUP, 2);
if (num_active == 0 && num_held > 0)
static void phone_state_alerting(struct hf_device *dev, int num_active,
int num_held)
{
+ if (dev->call_hanging_up) {
+ g_source_remove(dev->call_hanging_up);
+ dev->call_hanging_up = 0;
+ }
+
update_indicator(dev, IND_CALLSETUP, 3);
}
if (dev->setup_state == HAL_HANDSFREE_CALL_STATE_INCOMING) {
if (dev->num_active != num_active ||
dev->num_held != num_held) {
+ if (dev->num_active == num_held &&
+ dev->num_held == num_active)
+ return;
/*
* calls changed while waiting call ie. due to
* termination of active call
}
}
+static gboolean hang_up_cb(gpointer user_data)
+{
+ struct hf_device *dev = user_data;
+
+ DBG("");
+
+ dev->call_hanging_up = 0;
+
+ return FALSE;
+}
+
static void phone_state_idle(struct hf_device *dev, int num_active,
int num_held)
{
connect_audio(dev);
}
- if (num_held > dev->num_held)
+ if (num_held >= dev->num_held && num_held != 0)
update_indicator(dev, IND_CALLHELD, 1);
update_indicator(dev, IND_CALLSETUP, 0);
- if (num_active == dev->num_active && num_held == dev->num_held)
- dev->call_hanging_up = true;
-
+ if (num_active == 0 && num_held == 0 &&
+ num_active == dev->num_active &&
+ num_held == dev->num_held)
+ dev->call_hanging_up = g_timeout_add(800, hang_up_cb,
+ dev);
break;
case HAL_HANDSFREE_CALL_STATE_DIALING:
case HAL_HANDSFREE_CALL_STATE_ALERTING:
num_held ? (num_active ? 1 : 2) : 0);
update_indicator(dev, IND_CALLSETUP, 0);
+
+ /* disconnect SCO if we hang up while dialing or alerting */
+ if (num_active == 0 && num_held == 0)
+ disconnect_sco(dev);
break;
case HAL_HANDSFREE_CALL_STATE_IDLE:
-
if (dev->call_hanging_up) {
- dev->call_hanging_up = false;
+ g_source_remove(dev->call_hanging_up);
+ dev->call_hanging_up = 0;
return;
}
/* TODO */
default:
status = HAL_STATUS_FAILED;
- break;
+ goto done;
}
/*
}
}
-static void hid_unpaired_cb(const bdaddr_t *addr, uint8_t type)
+static void hid_unpaired_cb(const bdaddr_t *addr)
{
GSList *l;
struct hid_device *dev;
#include <sys/stat.h>
#include <fcntl.h>
-#include <bluetooth/bluetooth.h>
-
#include <glib.h>
-#include "src/log.h"
-
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
#include "lib/uuid.h"
+
#include "src/shared/util.h"
#include "src/shared/uhid.h"
#include "src/shared/queue.h"
+#include "src/log.h"
#include "attrib/att.h"
#include "attrib/gattrib.h"
uint16_t setrep_id;
struct bt_scpp *scpp;
struct bt_dis *dis;
- struct bt_bas *bas;
+ struct queue *bas;
GSList *instances;
struct queue *gatt_op;
};
bt_hog_detach(hog);
+ queue_destroy(hog->bas, (void *) bt_bas_unref);
g_slist_free_full(hog->instances, hog_free);
bt_scpp_unref(hog->scpp);
bt_dis_unref(hog->dis);
- bt_bas_unref(hog->bas);
bt_uhid_unref(hog->uhid);
g_slist_free_full(hog->reports, report_free);
g_free(hog->name);
return NULL;
}
+ hog->bas = queue_new();
+ if (!hog->bas) {
+ queue_destroy(hog->gatt_op, NULL);
+ hog_free(hog);
+ return NULL;
+ }
+
hog->uhid = bt_uhid_new_default();
if (!hog->uhid) {
hog_free(hog);
queue_destroy(hog->gatt_op, NULL);
+ queue_destroy(hog->bas, NULL);
return NULL;
}
static void find_included_cb(uint8_t status, GSList *services, void *user_data)
{
struct gatt_request *req = user_data;
- struct bt_hog *hog = req->user_data;
- struct gatt_included *include;
GSList *l;
DBG("");
destroy_gatt_req(req);
- if (hog->primary)
- return;
-
if (status) {
const char *str = att_ecode2str(status);
DBG("Find included failed: %s", str);
return;
}
- if (!services) {
- DBG("No included service found");
- return;
- }
-
for (l = services; l; l = l->next) {
- include = l->data;
+ struct gatt_included *include = l->data;
- if (strcmp(include->uuid, HOG_UUID) == 0)
- break;
+ DBG("included: handle %x, uuid %s",
+ include->handle, include->uuid);
}
-
- if (!l) {
- for (l = services; l; l = l->next) {
- include = l->data;
-
- find_included(hog, hog->attrib,
- include->range.start,
- include->range.end, find_included_cb,
- hog);
- }
- return;
- }
-
- hog->primary = g_new0(struct gatt_primary, 1);
- memcpy(hog->primary->uuid, include->uuid, sizeof(include->uuid));
- memcpy(&hog->primary->range, &include->range, sizeof(include->range));
-
- discover_char(hog, hog->attrib, hog->primary->range.start,
- hog->primary->range.end, NULL,
- char_discovered_cb, hog);
}
static void hog_attach_scpp(struct bt_hog *hog, struct gatt_primary *primary)
static void hog_attach_bas(struct bt_hog *hog, struct gatt_primary *primary)
{
- if (hog->bas) {
- bt_bas_attach(hog->bas, hog->attrib);
+ struct bt_bas *instance;
+
+ instance = bt_bas_new(primary);
+ if (!instance)
return;
- }
- hog->bas = bt_bas_new(primary);
- if (hog->bas)
- bt_bas_attach(hog->bas, hog->attrib);
+ bt_bas_attach(instance, hog->attrib);
+ queue_push_head(hog->bas, instance);
}
static void hog_attach_hog(struct bt_hog *hog, struct gatt_primary *primary)
discover_char(hog, hog->attrib, primary->range.start,
primary->range.end, NULL,
char_discovered_cb, hog);
+ find_included(hog, hog->attrib, primary->range.start,
+ primary->range.end, find_included_cb, hog);
return;
}
if (!instance)
return;
+ find_included(instance, hog->attrib, primary->range.start,
+ primary->range.end, find_included_cb, instance);
+
bt_hog_attach(instance, hog->attrib);
hog->instances = g_slist_append(hog->instances, instance);
}
if (strcmp(primary->uuid, HOG_UUID) == 0)
hog_attach_hog(hog, primary);
}
-
- if (hog->primary)
- return;
-
- for (l = services; l; l = l->next) {
- primary = l->data;
-
- find_included(hog, hog->attrib, primary->range.start,
- primary->range.end, find_included_cb, hog);
- }
}
bool bt_hog_attach(struct bt_hog *hog, void *gatt)
if (hog->dis)
bt_dis_attach(hog->dis, gatt);
- if (hog->bas)
- bt_bas_attach(hog->bas, gatt);
+ queue_foreach(hog->bas, (void *) bt_bas_attach, gatt);
for (l = hog->instances; l; l = l->next) {
struct bt_hog *instance = l->data;
if (!hog->attrib)
return;
+ queue_foreach(hog->bas, (void *) bt_bas_detach, NULL);
+
for (l = hog->instances; l; l = l->next) {
struct bt_hog *instance = l->data;
if (hog->dis)
bt_dis_detach(hog->dis);
- if (hog->bas)
- bt_bas_detach(hog->bas);
-
queue_foreach(hog->gatt_op, (void *) cancel_gatt_req, NULL);
g_attrib_unref(hog->attrib);
hog->attrib = NULL;
#endif
#include <fcntl.h>
+#include <stdarg.h>
#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
#include <unistd.h>
#include <stdbool.h>
#include <time.h>
#include <glib.h>
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+
#include "src/log.h"
#include "src/sdpd.h"
#include "src/shared/util.h"
-#include "lib/bluetooth.h"
-
#include "ipc-common.h"
#include "ipc.h"
#include "bluetooth.h"
#define DEFAULT_NAME "BlueZ for Android"
#define STARTUP_GRACE_SECONDS 5
-#define SHUTDOWN_GRACE_SECONDS 10
+#define SHUTDOWN_GRACE_SECONDS 5
static char *config_vendor = NULL;
static char *config_model = NULL;
#define BNEP_PANU_INTERFACE "bt-pan"
#define BNEP_NAP_INTERFACE "bt-pan%d"
-static bdaddr_t adapter_addr;
-static GSList *devices = NULL;
-static uint8_t local_role = HAL_PAN_ROLE_NONE;
-static struct ipc *hal_ipc = NULL;
-
struct pan_device {
char iface[16];
bdaddr_t dst;
guint watch;
};
-static struct {
- uint32_t record_id;
- GIOChannel *io;
- bool bridge;
-} nap_dev = {
- .record_id = 0,
- .io = NULL,
- .bridge = false,
-};
+static bdaddr_t adapter_addr;
+static GSList *devices = NULL;
+static uint8_t local_role = HAL_PAN_ROLE_NONE;
+static uint32_t nap_rec_id = 0;
+static uint32_t panu_rec_id = 0;
+static GIOChannel *nap_io = NULL;
+static bool nap_bridge_mode = false;
+static struct ipc *hal_ipc = NULL;
static int set_forward_delay(int sk)
{
DBG("%s", BNEP_BRIDGE);
- if (nap_dev.bridge)
+ if (nap_bridge_mode)
return 0;
sk = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
close(sk);
- nap_dev.bridge = err == 0;
+ nap_bridge_mode = err == 0;
return err;
}
DBG("%s", BNEP_BRIDGE);
- if (!nap_dev.bridge)
+ if (!nap_bridge_mode)
return 0;
bridge_if_down();
if (err < 0)
return err;
- nap_dev.bridge = false;
+ nap_bridge_mode = false;
return 0;
}
return FALSE;
}
+
static gboolean nap_setup_cb(GIOChannel *chan, GIOCondition cond,
gpointer user_data)
{
/* Highest known control command id BNEP_FILTER_MULT_ADDR_RSP 0x06 */
if (req->type == BNEP_CONTROL &&
- req->ctrl > BNEP_FILTER_MULT_ADDR_RSP) {
+ req->ctrl > BNEP_FILTER_MULT_ADDR_RSP) {
error("cmd not understood");
bnep_send_ctrl_rsp(sk, BNEP_CONTROL, BNEP_CMD_NOT_UNDERSTOOD,
req->ctrl);
}
rsp = bnep_setup_decode(req, &dst_role, &src_role);
- if (rsp) {
+ if (rsp != BNEP_SUCCESS) {
error("bnep_setup_decode failed");
goto failed;
}
- rsp = bnep_setup_chk(dst_role, src_role);
- if (rsp) {
- error("benp_setup_chk failed");
- goto failed;
- }
-
err = nap_create_bridge();
if (err < 0) {
error("pan: Failed to create bridge: %s (%d)", strerror(-err),
if (bnep_server_add(sk, dst_role, BNEP_BRIDGE, dev->iface,
&dev->dst) < 0) {
- nap_remove_bridge();
- error("server_connadd failed");
+ error("pan: server_connadd failed");
rsp = BNEP_CONN_NOT_ALLOWED;
goto failed;
}
nap_remove_bridge();
- if (nap_dev.io) {
- g_io_channel_shutdown(nap_dev.io, FALSE, NULL);
- g_io_channel_unref(nap_dev.io);
- nap_dev.io = NULL;
+ if (nap_io) {
+ g_io_channel_shutdown(nap_io, FALSE, NULL);
+ g_io_channel_unref(nap_io);
+ nap_io = NULL;
}
}
DBG("");
- nap_dev.io = bt_io_listen(NULL, nap_confirm_cb, NULL, NULL, &gerr,
+ nap_io = bt_io_listen(NULL, nap_confirm_cb, NULL, NULL, &gerr,
BT_IO_OPT_SOURCE_BDADDR, &adapter_addr,
BT_IO_OPT_PSM, BNEP_PSM,
BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
BT_IO_OPT_IMTU, BNEP_MTU,
BT_IO_OPT_INVALID);
- if (!nap_dev.io) {
+ if (!nap_io) {
destroy_nap_device();
error("%s", gerr->message);
g_error_free(gerr);
- return -EINVAL;
+ return -EIO;
}
return 0;
{ bt_pan_disconnect, false, sizeof(struct hal_cmd_pan_disconnect) },
};
-static sdp_record_t *pan_record(void)
+static sdp_record_t *nap_record(void)
{
sdp_list_t *svclass, *pfseq, *apseq, *root, *aproto;
- uuid_t root_uuid, pan, l2cap, bnep;
+ uuid_t root_uuid, nap, l2cap, bnep;
sdp_profile_desc_t profile[1];
sdp_list_t *proto[2];
sdp_data_t *v, *p;
record->attrlist = NULL;
record->pattern = NULL;
- sdp_uuid16_create(&pan, NAP_SVCLASS_ID);
- svclass = sdp_list_append(NULL, &pan);
+ sdp_uuid16_create(&nap, NAP_SVCLASS_ID);
+ svclass = sdp_list_append(NULL, &nap);
sdp_set_service_classes(record, svclass);
sdp_uuid16_create(&profile[0].uuid, NAP_PROFILE_ID);
return record;
}
+static sdp_record_t *panu_record(void)
+{
+ sdp_list_t *svclass, *pfseq, *apseq, *root, *aproto;
+ uuid_t root_uuid, panu, l2cap, bnep;
+ sdp_profile_desc_t profile[1];
+ sdp_list_t *proto[2];
+ sdp_data_t *v, *p;
+ uint16_t psm = BNEP_PSM, version = 0x0100;
+ uint16_t security = 0x0001, type = 0xfffe;
+ uint32_t rate = 0;
+ const char *desc = "PAN User", *name = "Network Service";
+ sdp_record_t *record;
+ uint16_t ptype[] = { 0x0800, /* IPv4 */ 0x0806, /* ARP */ };
+ sdp_data_t *head, *pseq, *data;
+
+ record = sdp_record_alloc();
+ if (!record)
+ return NULL;
+
+ record->attrlist = NULL;
+ record->pattern = NULL;
+
+ sdp_uuid16_create(&panu, PANU_SVCLASS_ID);
+ svclass = sdp_list_append(NULL, &panu);
+ sdp_set_service_classes(record, svclass);
+
+ sdp_uuid16_create(&profile[0].uuid, PANU_PROFILE_ID);
+ profile[0].version = 0x0100;
+ pfseq = sdp_list_append(NULL, &profile[0]);
+ sdp_set_profile_descs(record, pfseq);
+ sdp_set_info_attr(record, name, NULL, desc);
+ sdp_attr_add_new(record, SDP_ATTR_NET_ACCESS_TYPE, SDP_UINT16, &type);
+ sdp_attr_add_new(record, SDP_ATTR_MAX_NET_ACCESSRATE,
+ SDP_UINT32, &rate);
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(record, root);
+
+ sdp_uuid16_create(&l2cap, L2CAP_UUID);
+ proto[0] = sdp_list_append(NULL, &l2cap);
+ p = sdp_data_alloc(SDP_UINT16, &psm);
+ proto[0] = sdp_list_append(proto[0], p);
+ apseq = sdp_list_append(NULL, proto[0]);
+
+ sdp_uuid16_create(&bnep, BNEP_UUID);
+ proto[1] = sdp_list_append(NULL, &bnep);
+ v = sdp_data_alloc(SDP_UINT16, &version);
+ proto[1] = sdp_list_append(proto[1], v);
+
+ head = sdp_data_alloc(SDP_UINT16, &ptype[0]);
+ data = sdp_data_alloc(SDP_UINT16, &ptype[1]);
+ sdp_seq_append(head, data);
+
+ pseq = sdp_data_alloc(SDP_SEQ16, head);
+ proto[1] = sdp_list_append(proto[1], pseq);
+ apseq = sdp_list_append(apseq, proto[1]);
+ aproto = sdp_list_append(NULL, apseq);
+ sdp_set_access_protos(record, aproto);
+ sdp_add_lang_attr(record);
+ sdp_attr_add_new(record, SDP_ATTR_SECURITY_DESC, SDP_UINT16, &security);
+
+ sdp_data_free(p);
+ sdp_data_free(v);
+ sdp_list_free(apseq, NULL);
+ sdp_list_free(root, NULL);
+ sdp_list_free(aproto, NULL);
+ sdp_list_free(proto[0], NULL);
+ sdp_list_free(proto[1], NULL);
+ sdp_list_free(svclass, NULL);
+ sdp_list_free(pfseq, NULL);
+
+ return record;
+}
+
bool bt_pan_register(struct ipc *ipc, const bdaddr_t *addr, uint8_t mode)
{
- sdp_record_t *rec;
+ sdp_record_t *nap_rec, *panu_rec;
int err;
DBG("");
bacpy(&adapter_addr, addr);
- rec = pan_record();
- if (!rec) {
- error("Failed to allocate PAN record");
+ nap_rec = nap_record();
+ if (bt_adapter_add_record(nap_rec, SVC_HINT_NETWORKING) < 0) {
+ sdp_record_free(nap_rec);
+ error("Failed to allocate PAN-NAP sdp record");
return false;
}
- if (bt_adapter_add_record(rec, SVC_HINT_NETWORKING) < 0) {
- error("Failed to register PAN record");
- sdp_record_free(rec);
+ panu_rec = panu_record();
+ if (bt_adapter_add_record(panu_rec, SVC_HINT_NETWORKING) < 0) {
+ sdp_record_free(nap_rec);
+ sdp_record_free(panu_rec);
+ error("Failed to allocate PAN-PANU sdp record");
return false;
}
err = bnep_init();
if (err < 0) {
- error("bnep init failed");
- bt_adapter_remove_record(rec->handle);
+ error("Failed to init BNEP");
+ bt_adapter_remove_record(nap_rec->handle);
+ bt_adapter_remove_record(panu_rec->handle);
return false;
}
err = register_nap_server();
if (err < 0) {
- error("Failed to register NAP");
- bt_adapter_remove_record(rec->handle);
+ error("Failed to register NAP server");
+ bt_adapter_remove_record(nap_rec->handle);
+ bt_adapter_remove_record(panu_rec->handle);
bnep_cleanup();
return false;
}
- nap_dev.record_id = rec->handle;
+ nap_rec_id = nap_rec->handle;
+ panu_rec_id = panu_rec->handle;
hal_ipc = ipc;
ipc_register(hal_ipc, HAL_SERVICE_ID_PAN, cmd_handlers,
ipc_unregister(hal_ipc, HAL_SERVICE_ID_PAN);
hal_ipc = NULL;
- bt_adapter_remove_record(nap_dev.record_id);
- nap_dev.record_id = 0;
+ bt_adapter_remove_record(nap_rec_id);
+ nap_rec_id = 0;
+ bt_adapter_remove_record(panu_rec_id);
+ panu_rec_id = 0;
destroy_nap_device();
}
A2DP PICS for the PTS tool.
-PTS version: 5.3
+PTS version: 6.0
* - different than PTS defaults
# - not yet implemented/supported
M - mandatory if such role selected
O - optional
+ Profile Version
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_A2DP_0_1 False A2DP 1.0 (C.1)
+TSPC_A2DP_0_2 False A2DP 1.2 (C.1)
+TSPC_A2DP_0_3 True (*) A2DP 1.3 (C.1)
+-------------------------------------------------------------------------------
+C.1: It is mandatory to select one of the profile versions.
+-------------------------------------------------------------------------------
+
+
Roles
-------------------------------------------------------------------------------
Parameter Name Selected Description
-------------------------------------------------------------------------------
-TSPC_A2DP_1_1 True Role: Source (C.1)
-TSPC_A2DP_1_2 False (*) Role: Sink (C.1)
+TSPC_A2DP_1_1 True (*) Role: Source (C.1)
+TSPC_A2DP_1_2 False Role: Sink (C.1)
-------------------------------------------------------------------------------
C.1: It is mandatory to support at least one of the defined roles.
-------------------------------------------------------------------------------
TSPC_A2DP_2_8 True (*) SRC: Initiate suspend (O)
TSPC_A2DP_2_9 True (*) SRC: Accept suspend (O)
TSPC_A2DP_2_10 True SRC: SBC encoder (M)
-TSPC_A2DP_2_10a False SRC: Encode Audio Stream (O)
+TSPC_A2DP_2_10a False SRC: Encode and Forward Audio Stream (O)
TSPC_A2DP_2_11 False SRC: SBC Configurations in 16 KHz sampling (O)
TSPC_A2DP_2_12 False SRC: SBC Configurations in 32 KHz sampling (O)
-TSPC_A2DP_2_13 True SRC: SBC Configurations in 44.1 KHz sampling
- (C.1)
-TSPC_A2DP_2_14 False (*) SRC: SBC Configurations in 48 KHz sampling
+TSPC_A2DP_2_13 True (*) SRC: SBC Configurations in 44.1 KHz sampling
(C.1)
+TSPC_A2DP_2_14 True (*) SRC: SBC Configurations in 48 KHz sampling (C.1)
+TSPC_A2DP_2_15 False SRC: Delay Reporting (C.2)
+TSPC_A2DP_2_16 False SRC: SRC video playback via Bluetooth VDP (C.3)
+TSPC_A2DP_2_17 False SRC: SRC video playback on a local video
+ display (C.3)
-------------------------------------------------------------------------------
C.1: At least one of the values shall be supported.
+C.2: Mandatory if A2DP 0/3 AND (2/16 OR 2/17) is supported, otherwise excluded.
+C.3: Optional to support if A2DP 0/3 is supported, otherwise excluded.
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
Parameter Name Selected Description
-------------------------------------------------------------------------------
-TSPC_A2DP_3_1 True SRC: SBC encoder Codec (M)
-TSPC_A2DP_3_2 True (*) SRC: Additional encoder Codec (O)
+TSPC_A2DP_3_1 True SRC: SBC encoder (M)
+TSPC_A2DP_3_1a False SRC: Encode and Forward SBC Audio Stream (O)
+TSPC_A2DP_3_2 False SRC: Optional codec (O)
+TSPC_A2DP_3_3 False SRC: MPEG-1,2 Audio decoder (C.1)
+TSPC_A2DP_3_4 False SRC: MPEG-1,2 Audio encoder (C.1)
+TSPC_A2DP_3_5 False SRC: MPEG-2,4 AAC decoder (C.1)
+TSPC_A2DP_3_6 False SRC: MPEG-2,4 AAC encoder (C.1)
+TSPC_A2DP_3_7 False SRC: ATRAC family decoder (C.1)
+TSPC_A2DP_3_8 False SRC: ATRAC family encoder (C.1)
+-------------------------------------------------------------------------------
+C.1: At least one of the implementations shall be supported if 12/2 (Optional
+ codec) is supported, else excluded.
+-------------------------------------------------------------------------------
+
+
+ Supported Codec Features in SRC
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_A2DP_3a_1 True SRC: Channel Mode - Mono (M)
+TSPC_A2DP_3a_2 True (*) SRC: Channel Mode - Dual Channel (C.1)
+TSPC_A2DP_3a_3 True (*) SRC: Channel Mode - Stereo (C.1)
+TSPC_A2DP_3a_4 True (*) SRC: Channel Mode - Joint Stereo (C.1)
+TSPC_A2DP_3a_5 True SRC: Block Length 4 (M)
+TSPC_A2DP_3a_6 True SRC: Block Length 8 (M)
+TSPC_A2DP_3a_7 True SRC: Block Length 12 (M)
+TSPC_A2DP_3a_8 True SRC: Block Length 16 (M)
+TSPC_A2DP_3a_9 True (*) SRC: Subbands - 4 (O)
+TSPC_A2DP_3a_10 True SRC: Subbands - 8 (M)
+TSPC_A2DP_3a_11 True (*) SRC: Allocation - SNR (O)
+TSPC_A2DP_3a_12 True SRC: Allocation - Loudness (M)
+-------------------------------------------------------------------------------
+C.1: At least one of the values shall be supported.
-------------------------------------------------------------------------------
TSPC_A2DP_4_8 False SNK: Initiate suspend (O)
TSPC_A2DP_4_9 False SNK: Accept suspend (O)
TSPC_A2DP_4_10 False (*) SNK: SBC decoder (M)
-TSPC_A2DP_4_10a False (*) SNK: Decode Audio Stream (O)
+TSPC_A2DP_4_10a False SNK: Decode and Forward Audio Stream (O)
TSPC_A2DP_4_11 False SNK: SBC Configurations in 16 KHz sampling (O)
TSPC_A2DP_4_12 False SNK: SBC Configurations in 32 KHz sampling (O)
TSPC_A2DP_4_13 False (*) SNK: SBC Configurations in 44.1 KHz sampling (M)
TSPC_A2DP_4_14 False (*) SNK: SBC Configurations in 48 KHz sampling (M)
+TSPC_A2DP_4_15 False SNK: Delay Reporting (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support if A2DP 0/3 is supported, otherwise excluded.
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
Parameter Name Selected Description
-------------------------------------------------------------------------------
-TSPC_A2DP_5_1 False (*) SNK: SBC decoder Codec (M)
-TSPC_A2DP_5_2 False SNK: Additional decoder Coded (O)
-TSPC_ALL False Enable all test cases when set to False.
+TSPC_A2DP_5_1 False (*) SNK: SBC decoder (M)
+TSPC_A2DP_5_1a False SNK: Decode and Forward SBC Audio Stream (O)
+TSPC_A2DP_5_2 False SNK: Optional codec decoder (O)
+TSPC_A2DP_5_3 False SNK: MPEG-1,2 Audio (C.1)
+TSPC_A2DP_5_4 False SNK: MPEG-2,4 AAC (C.1)
+TSPC_A2DP_5_5 False SNK: ATRAC family (C.1)
+-------------------------------------------------------------------------------
+C.1: At least one codec shall be supported if Table 13/2 (Optional codec
+ decoder) is supported, otherwise excluded.
+-------------------------------------------------------------------------------
+
+
+ Supported Codec Features in SNK
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_A2DP_5a_1 False (*) SNK: Channel Mode - Mono (M)
+TSPC_A2DP_5a_2 False (*) SNK: Channel Mode - Dual Channel (M)
+TSPC_A2DP_5a_3 False (*) SNK: Channel Mode - Stereo (M)
+TSPC_A2DP_5a_4 False (*) SNK: Channel Mode - Joint Stereo (M)
+TSPC_A2DP_5a_5 False (*) SNK: Block Length 4 (M)
+TSPC_A2DP_5a_6 False (*) SNK: Block Length 8 (M)
+TSPC_A2DP_5a_7 False (*) SNK: Block Length 12 (M)
+TSPC_A2DP_5a_8 False (*) SNK: Block Length 16 (M)
+TSPC_A2DP_5a_9 False (*) SNK: Subbands - 4 (M)
+TSPC_A2DP_5a_10 False (*) SNK: Subbands - 8 (M)
+TSPC_A2DP_5a_11 False (*) SNK: Allocation - SNR (M)
+TSPC_A2DP_5a_12 False (*) SNK: Allocation - Loudness (M)
-------------------------------------------------------------------------------
AVCTP PICS for the PTS tool.
-PTS version: 5.3
+PTS version: 6.0
* - different than PTS defaults
# - not yet implemented/supported
AVDTP PICS for the PTS tool.
-PTS version: 5.3
+PTS version: 6.0
* - different than PTS defaults
# - not yet implemented/supported
AVRCP PICS for the PTS tool.
-PTS version: 5.3
+PTS version: 6.0
* - different than PTS defaults
# - not yet implemented/supported
Parameter Name Selected Description
-------------------------------------------------------------------------------
TSPC_AVRCP_2_1 False (*) CT: Initiating connection establishment (M)
-TSPC_AVRCP_2_2 False (*) CT: Accepting connection establishment (M)
+TSPC_AVRCP_2_2 False (*) CT: Accepting connection establishment for
+ control initiated by TG (M)
TSPC_AVRCP_2_3 False (*) CT: Initiating connection release (M)
-TSPC_AVRCP_2_4 False (*) CT: Accepting connection release (M)
+TSPC_AVRCP_2_4 False (*) CT: Accepting connection release for control
+ initiated by TG (M)
TSPC_AVRCP_2_5 False CT: Sending UNIT INFO (O)
TSPC_AVRCP_2_6 False CT: Sending SUBUNIT INFO (O)
-TSPC_AVRCP_2_7 False CT: Sending PASS THROUGH command category 1
+TSPC_AVRCP_2_7 False CT: Sending PASS THROUGH command category 1
(C.1)
TSPC_AVRCP_2_8 False CT: Sending PASS THROUGH command category 2
(C.1)
(C.10)
TSPC_AVRCP_2_15 False CT: Set Player Application Setting Value (C.10)
TSPC_AVRCP_2_16 False CT: Get Player Application Setting
- Attribute (O)
-TSPC_AVRCP_2_17 False CT: Get Player Application Setting Value (O)
+ Attribute Text (O)
+TSPC_AVRCP_2_17 False CT: Get Player Application Setting Value Text
+ (O)
TSPC_AVRCP_2_18 False CT: Inform Displayable Character Set (O)
TSPC_AVRCP_2_19 False CT: Inform Battery Status of CT (O)
TSPC_AVRCP_2_20 False CT: Get Element Attributes (O)
TSPC_AVRCP_2_53 False CT: PASSTHROUGH operation supporting press
and hold (O)
TSPC_AVRCP_2_54 False CT: Cover Art (O)
-TSPC_AVRCP_2_55 False CT: GetCapabilities, Cover Art (C.10)
-TSPC_AVRCP_2_56 False CT: GetImageProperties, Cover Art (C.10)
-TSPC_AVRCP_2_57 False CT: GetImage, Cover Art (C.9)
-TSPC_AVRCP_2_58 False CT: GetLinkedThumbnail, CoverArt (C.9)
+TSPC_AVRCP_2_55 False CT: GetImageProperties (C.14)
+TSPC_AVRCP_2_56 False CT: GetImage (C.13)
+TSPC_AVRCP_2_57 False CT: GetLinkedThumbnail (C.13)
-------------------------------------------------------------------------------
C.1: Mandatory to support at least one of the defined categories
(TSPC_AVRCP_2_7 through TSPC_AVRCP_2_10).
-C.2: Mandatory if TSPC_AVRCP_2_20 is supported, otherwise Optional.
+C.2: Mandatory to support at least one of TSPC_AVRCP_2_23 or TSPC_AVRC_2_24
+ if TSPC_AVRCP_2_20 is supported, otherwise Optional.
C.3: Mandatory if TSPC_AVRCP_2_8 is supported, otherwise Excluded.
C.4: Mandatory if TSPC_AVRCP_2_32 is supported, otherwise Excluded.
C.5: Mandatory if TSPC_AVRCP_2_27 is supported, otherwise Excluded.
or TSPC_AVRCP_2_15 must be supported if Player Application Settings
feature is supported, in accordance with Player Application Settings
support requirements.
-C.11: Mandatory if TSPC_AVRCP_2_7 or (TSPC_AVRCP_2_8 AND TSPC_AVRCP_2_49)
- or TSPC_AVRCP_2_9 is supported, otherwise Optional.
+C.11: Mandatory if TSPC_AVRCP_2_20 or TSPC_AVRCP_2_49 is supported, otherwise
+ Optional.
C.12: Mandatory if Basic Group Navigation Feature supported. If any item
TSPC_AVRCP_2_25 or TSPC_AVRCP_2_26 is supported it is mandatory to
support both, in accordance with Basic Group Navigation support
requirements, otherwise Excluded.
+C.13: Mandatory to support at least one of the functions if TSPC_AVRCP_2_54
+ (Cover Art) is support, otherwise Excluded.
+C.14: Optional if TSPC_AVRCP_2_54 (Cover Art) is supported, otherwise Excluded.
-------------------------------------------------------------------------------
TSPC_AVRCP_3_31 False CT: category 1 - Operation id: F2 (C.1)
TSPC_AVRCP_3_32 False CT: category 1 - Operation id: F3 (C.1)
TSPC_AVRCP_3_33 False CT: category 1 - Operation id: F4 (C.1)
-TSPC_AVRCP_3_33a False CT: category 1 - Operation id: F5 (C.1)
TSPC_AVRCP_3_34 False CT: category 1 - Operation id: vendor_unique
(C.1)
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
Parameter Name Selected Description
-------------------------------------------------------------------------------
-TSPC_AVRCP_4_1 False CT: category 2 - Operation id: 0 (C.2)
-TSPC_AVRCP_4_2 False CT: category 2 - Operation id: 1 (C.2)
-TSPC_AVRCP_4_3 False CT: category 2 - Operation id: 2 (C.2)
-TSPC_AVRCP_4_4 False CT: category 2 - Operation id: 3 (C.2)
-TSPC_AVRCP_4_5 False CT: category 2 - Operation id: 4 (C.2)
-TSPC_AVRCP_4_6 False CT: category 2 - Operation id: 5 (C.2)
-TSPC_AVRCP_4_7 False CT: category 2 - Operation id: 6 (C.2)
-TSPC_AVRCP_4_8 False CT: category 2 - Operation id: 7 (C.2)
-TSPC_AVRCP_4_9 False CT: category 2 - Operation id: 8 (C.2)
-TSPC_AVRCP_4_10 False CT: category 2 - Operation id: 9 (C.2)
-TSPC_AVRCP_4_11 False CT: category 2 - Operation id: dot (C.2)
-TSPC_AVRCP_4_12 False CT: category 2 - Operation id: enter (C.2)
-TSPC_AVRCP_4_13 False CT: category 2 - Operation id: clear (C.2)
+TSPC_AVRCP_4_1 False CT: category 2 - Operation id: 0 (C.1)
+TSPC_AVRCP_4_2 False CT: category 2 - Operation id: 1 (C.1)
+TSPC_AVRCP_4_3 False CT: category 2 - Operation id: 2 (C.1)
+TSPC_AVRCP_4_4 False CT: category 2 - Operation id: 3 (C.1)
+TSPC_AVRCP_4_5 False CT: category 2 - Operation id: 4 (C.1)
+TSPC_AVRCP_4_6 False CT: category 2 - Operation id: 5 (C.1)
+TSPC_AVRCP_4_7 False CT: category 2 - Operation id: 6 (C.1)
+TSPC_AVRCP_4_8 False CT: category 2 - Operation id: 7 (C.1)
+TSPC_AVRCP_4_9 False CT: category 2 - Operation id: 8 (C.1)
+TSPC_AVRCP_4_10 False CT: category 2 - Operation id: 9 (C.1)
+TSPC_AVRCP_4_11 False CT: category 2 - Operation id: dot (C.1)
+TSPC_AVRCP_4_12 False CT: category 2 - Operation id: enter (C.1)
+TSPC_AVRCP_4_13 False CT: category 2 - Operation id: clear (C.1)
TSPC_AVRCP_4_14 False CT: category 2 - Operation id: sound_select
- (C.2)
+ (C.1)
TSPC_AVRCP_4_15 False CT: category 2 - Operation id: input_select
- (C.2)
+ (C.1)
TSPC_AVRCP_4_16 False CT: category 2 - Operation id:
- display_information (C.2)
-TSPC_AVRCP_4_17 False CT: category 2 - Operation id: help (C.2)
-TSPC_AVRCP_4_18 False CT: category 2 - Operation id: power (C.2)
-TSPC_AVRCP_4_19 False CT: category 2 - Operation id: volume_up (C.2)
-TSPC_AVRCP_4_20 False CT: category 2 - Operation id: volume_down (C.2)
-TSPC_AVRCP_4_21 False CT: category 2 - Operation id: mute (C.2)
-TSPC_AVRCP_4_22 False CT: category 2 - Operation id: F1 (C.2)
-TSPC_AVRCP_4_23 False CT: category 2 - Operation id: F2 (C.2)
-TSPC_AVRCP_4_24 False CT: category 2 - Operation id: F3 (C.2)
-TSPC_AVRCP_4_25 False CT: category 2 - Operation id: F4 (C.2)
-TSPC_AVRCP_4_25a False CT: category 2 - Operation id: F5 (C.2)
+ display_information (C.1)
+TSPC_AVRCP_4_17 False CT: category 2 - Operation id: help (C.1)
+TSPC_AVRCP_4_18 False CT: category 2 - Operation id: power (C.1)
+TSPC_AVRCP_4_19 False CT: category 2 - Operation id: volume_up (C.1)
+TSPC_AVRCP_4_20 False CT: category 2 - Operation id: volume_down (C.1)
+TSPC_AVRCP_4_21 False CT: category 2 - Operation id: mute (C.1)
+TSPC_AVRCP_4_22 False CT: category 2 - Operation id: F1 (C.1)
+TSPC_AVRCP_4_23 False CT: category 2 - Operation id: F2 (C.1)
+TSPC_AVRCP_4_24 False CT: category 2 - Operation id: F3 (C.1)
+TSPC_AVRCP_4_25 False CT: category 2 - Operation id: F4 (C.1)
TSPC_AVRCP_4_26 False CT: category 2 - Operation id: vendor_unique
- (C.2)
+ (C.1)
-------------------------------------------------------------------------------
-C.2: Mandatory to support at least one of these operation_ids if the device
+C.1: Mandatory to support at least one of these operation_ids if the device
supports category 2 (TSPC_AVRCP_2_8).
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
Parameter Name Selected Description
-------------------------------------------------------------------------------
-TSPC_AVRCP_5_1 False CT: category 3 - Operation id: 0 (C.3)
-TSPC_AVRCP_5_2 False CT: category 3 - Operation id: 1 (C.3)
-TSPC_AVRCP_5_3 False CT: category 3 - Operation id: 2 (C.3)
-TSPC_AVRCP_5_4 False CT: category 3 - Operation id: 3 (C.3)
-TSPC_AVRCP_5_5 False CT: category 3 - Operation id: 4 (C.3)
-TSPC_AVRCP_5_6 False CT: category 3 - Operation id: 5 (C.3)
-TSPC_AVRCP_5_7 False CT: category 3 - Operation id: 6 (C.3)
-TSPC_AVRCP_5_8 False CT: category 3 - Operation id: 7 (C.3)
-TSPC_AVRCP_5_9 False CT: category 3 - Operation id: 8 (C.3)
-TSPC_AVRCP_5_10 False CT: category 3 - Operation id: 9 (C.3)
-TSPC_AVRCP_5_11 False CT: category 3 - Operation id: dot (C.3)
-TSPC_AVRCP_5_12 False CT: category 3 - Operation id: enter (C.3)
-TSPC_AVRCP_5_13 False CT: category 3 - Operation id: clear (C.3)
-TSPC_AVRCP_5_14 False CT: category 3 - Operation id: channel up (C.3)
+TSPC_AVRCP_5_1 False CT: category 3 - Operation id: 0 (C.1)
+TSPC_AVRCP_5_2 False CT: category 3 - Operation id: 1 (C.1)
+TSPC_AVRCP_5_3 False CT: category 3 - Operation id: 2 (C.1)
+TSPC_AVRCP_5_4 False CT: category 3 - Operation id: 3 (C.1)
+TSPC_AVRCP_5_5 False CT: category 3 - Operation id: 4 (C.1)
+TSPC_AVRCP_5_6 False CT: category 3 - Operation id: 5 (C.1)
+TSPC_AVRCP_5_7 False CT: category 3 - Operation id: 6 (C.1)
+TSPC_AVRCP_5_8 False CT: category 3 - Operation id: 7 (C.1)
+TSPC_AVRCP_5_9 False CT: category 3 - Operation id: 8 (C.1)
+TSPC_AVRCP_5_10 False CT: category 3 - Operation id: 9 (C.1)
+TSPC_AVRCP_5_11 False CT: category 3 - Operation id: dot (C.1)
+TSPC_AVRCP_5_12 False CT: category 3 - Operation id: enter (C.1)
+TSPC_AVRCP_5_13 False CT: category 3 - Operation id: clear (C.1)
+TSPC_AVRCP_5_14 False CT: category 3 - Operation id: channel up (C.1)
TSPC_AVRCP_5_15 False CT: category 3 - Operation id: channel down
- (C.3)
+ (C.1)
TSPC_AVRCP_5_16 False CT: category 3 - Operation id: previous channel
- (C.3)
+ (C.1)
TSPC_AVRCP_5_17 False CT: category 3 - Operation id: sound_select
- (C.3)
+ (C.1)
TSPC_AVRCP_5_18 False CT: category 3 - Operation id: input_select
- (C.3)
+ (C.1)
TSPC_AVRCP_5_19 False CT: category 3 - Operation id:
- display_information (C.3)
-TSPC_AVRCP_5_20 False CT: category 3 - Operation id: help (C.3)
-TSPC_AVRCP_5_21 False CT: category 3 - Operation id: power (C.3)
-TSPC_AVRCP_5_22 False CT: category 3 - Operation id: angle (C.3)
-TSPC_AVRCP_5_23 False CT: category 3 - Operation id: subpicture(C.3)
-TSPC_AVRCP_5_24 False CT: category 3 - Operation id: F1 (C.3)
-TSPC_AVRCP_5_25 False CT: category 3 - Operation id: F2 (C.3)
-TSPC_AVRCP_5_26 False CT: category 3 - Operation id: F3 (C.3)
-TSPC_AVRCP_5_27 False CT: category 3 - Operation id: F4 (C.3)
-TSPC_AVRCP_5_27a False CT: category 3 - Operation id: F5 (C.3)
+ display_information (C.1)
+TSPC_AVRCP_5_20 False CT: category 3 - Operation id: help (C.1)
+TSPC_AVRCP_5_21 False CT: category 3 - Operation id: power (C.1)
+TSPC_AVRCP_5_22 False CT: category 3 - Operation id: angle (C.1)
+TSPC_AVRCP_5_23 False CT: category 3 - Operation id: subpicture(C.1)
+TSPC_AVRCP_5_24 False CT: category 3 - Operation id: F1 (C.1)
+TSPC_AVRCP_5_25 False CT: category 3 - Operation id: F2 (C.1)
+TSPC_AVRCP_5_26 False CT: category 3 - Operation id: F3 (C.1)
+TSPC_AVRCP_5_27 False CT: category 3 - Operation id: F4 (C.1)
TSPC_AVRCP_5_28 False CT: category 3 - Operation id: vendor_unique
- (C.3)
+ (C.1)
-------------------------------------------------------------------------------
-C.3: Mandatory to support at least one of these operation_ids if the device
+C.1: Mandatory to support at least one of these operation_ids if the device
supports category 3 (TSPC_AVRCP_2_9).
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
Parameter Name Selected Description
-------------------------------------------------------------------------------
-TSPC_AVRCP_6_1 False CT: category 4 - Operation id: select (C.4)
-TSPC_AVRCP_6_2 False CT: category 4 - Operation id: up (C.4)
-TSPC_AVRCP_6_3 False CT: category 4 - Operation id: down (C.4)
-TSPC_AVRCP_6_4 False CT: category 4 - Operation id: left (C.4)
-TSPC_AVRCP_6_5 False CT: category 4 - Operation id: right (C.4)
-TSPC_AVRCP_6_6 False CT: category 4 - Operation id: right up (C.4)
-TSPC_AVRCP_6_7 False CT: category 4 - Operation id: right down (C.4)
-TSPC_AVRCP_6_8 False CT: category 4 - Operation id: left up (C.4)
-TSPC_AVRCP_6_9 False CT: category 4 - Operation id: left down (C.4)
-TSPC_AVRCP_6_10 False CT: category 4 - Operation id: root menu (C.4)
-TSPC_AVRCP_6_11 False CT: category 4 - Operation id: setup menu (C.4)
+TSPC_AVRCP_6_1 False CT: category 4 - Operation id: select (C.1)
+TSPC_AVRCP_6_2 False CT: category 4 - Operation id: up (C.1)
+TSPC_AVRCP_6_3 False CT: category 4 - Operation id: down (C.1)
+TSPC_AVRCP_6_4 False CT: category 4 - Operation id: left (C.1)
+TSPC_AVRCP_6_5 False CT: category 4 - Operation id: right (C.1)
+TSPC_AVRCP_6_6 False CT: category 4 - Operation id: right up (C.1)
+TSPC_AVRCP_6_7 False CT: category 4 - Operation id: right down (C.1)
+TSPC_AVRCP_6_8 False CT: category 4 - Operation id: left up (C.1)
+TSPC_AVRCP_6_9 False CT: category 4 - Operation id: left down (C.1)
+TSPC_AVRCP_6_10 False CT: category 4 - Operation id: root menu (C.1)
+TSPC_AVRCP_6_11 False CT: category 4 - Operation id: setup menu (C.1)
TSPC_AVRCP_6_12 False CT: category 4 - Operation id: contents menu
- (C.4)
+ (C.1)
TSPC_AVRCP_6_13 False CT: category 4 - Operation id: favorite menu
- (C.4)
-TSPC_AVRCP_6_14 False CT: category 4 - Operation id: exit (C.4)
-TSPC_AVRCP_6_15 False CT: category 4 - Operation id: 0 (C.4)
-TSPC_AVRCP_6_16 False CT: category 4 - Operation id: 1 (C.4)
-TSPC_AVRCP_6_17 False CT: category 4 - Operation id: 2 (C.4)
-TSPC_AVRCP_6_18 False CT: category 4 - Operation id: 3 (C.4)
-TSPC_AVRCP_6_19 False CT: category 4 - Operation id: 4 (C.4)
-TSPC_AVRCP_6_20 False CT: category 4 - Operation id: 5 (C.4)
-TSPC_AVRCP_6_21 False CT: category 4 - Operation id: 6 (C.4)
-TSPC_AVRCP_6_22 False CT: category 4 - Operation id: 7 (C.4)
-TSPC_AVRCP_6_23 False CT: category 4 - Operation id: 8 (C.4)
-TSPC_AVRCP_6_24 False CT: category 4 - Operation id: 9 (C.4)
-TSPC_AVRCP_6_25 False CT: category 4 - Operation id: dot (C.4)
-TSPC_AVRCP_6_26 False CT: category 4 - Operation id: enter (C.4)
-TSPC_AVRCP_6_27 False CT: category 4 - Operation id: clear (C.4)
+ (C.1)
+TSPC_AVRCP_6_14 False CT: category 4 - Operation id: exit (C.1)
+TSPC_AVRCP_6_15 False CT: category 4 - Operation id: 0 (C.1)
+TSPC_AVRCP_6_16 False CT: category 4 - Operation id: 1 (C.1)
+TSPC_AVRCP_6_17 False CT: category 4 - Operation id: 2 (C.1)
+TSPC_AVRCP_6_18 False CT: category 4 - Operation id: 3 (C.1)
+TSPC_AVRCP_6_19 False CT: category 4 - Operation id: 4 (C.1)
+TSPC_AVRCP_6_20 False CT: category 4 - Operation id: 5 (C.1)
+TSPC_AVRCP_6_21 False CT: category 4 - Operation id: 6 (C.1)
+TSPC_AVRCP_6_22 False CT: category 4 - Operation id: 7 (C.1)
+TSPC_AVRCP_6_23 False CT: category 4 - Operation id: 8 (C.1)
+TSPC_AVRCP_6_24 False CT: category 4 - Operation id: 9 (C.1)
+TSPC_AVRCP_6_25 False CT: category 4 - Operation id: dot (C.1)
+TSPC_AVRCP_6_26 False CT: category 4 - Operation id: enter (C.1)
+TSPC_AVRCP_6_27 False CT: category 4 - Operation id: clear (C.1)
TSPC_AVRCP_6_28 False CT: category 4 - Operation id:
- display_information (C.4)
-TSPC_AVRCP_6_29 False CT: category 4 - Operation id: help (C.4)
-TSPC_AVRCP_6_30 False CT: category 4 - Operation id: page up (C.4)
-TSPC_AVRCP_6_31 False CT: category 4 - Operation id: page down (C.4)
-TSPC_AVRCP_6_32 False CT: category 4 - Operation id: power (C.4)
-TSPC_AVRCP_6_33 False CT: category 4 - Operation id: F1 (C.4)
-TSPC_AVRCP_6_34 False CT: category 4 - Operation id: F2 (C.4)
-TSPC_AVRCP_6_35 False CT: category 4 - Operation id: F3 (C.4)
-TSPC_AVRCP_6_36 False CT: category 4 - Operation id: F4 (C.4)
-TSPC_AVRCP_6_36a False CT: category 4 - Operation id: F5 (C.4)
+ display_information (C.1)
+TSPC_AVRCP_6_29 False CT: category 4 - Operation id: help (C.1)
+TSPC_AVRCP_6_30 False CT: category 4 - Operation id: page up (C.1)
+TSPC_AVRCP_6_31 False CT: category 4 - Operation id: page down (C.1)
+TSPC_AVRCP_6_32 False CT: category 4 - Operation id: power (C.1)
+TSPC_AVRCP_6_33 False CT: category 4 - Operation id: F1 (C.1)
+TSPC_AVRCP_6_34 False CT: category 4 - Operation id: F2 (C.1)
+TSPC_AVRCP_6_35 False CT: category 4 - Operation id: F3 (C.1)
+TSPC_AVRCP_6_36 False CT: category 4 - Operation id: F4 (C.1)
TSPC_AVRCP_6_37 False CT: category 4 - Operation id: vendor_unique
- (C.4)
+ (C.1)
-------------------------------------------------------------------------------
-C.4: Mandatory to support at least one of these operation_ids if the device
- supports category 4 (TSPC_AVRCP_2_9).
+C.1: Mandatory to support at least one of these operation_ids if the device
+ supports category 4 (TSPC_AVRCP_2_10).
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
Parameter Name Selected Description
-------------------------------------------------------------------------------
-TSPC_AVRCP_7_1 True (*) TG: Initiating connection establishment (O)
-TSPC_AVRCP_7_2 True TG: Accept connection establishment (M)
+TSPC_AVRCP_7_1 True (*) TG: Initiating connection establishment for
+ Control (O)
+TSPC_AVRCP_7_2 True TG: Accept connection establishment for Control
+ initiated by CT (M)
TSPC_AVRCP_7_3 True (*) TG: Initiating connection release (M)
TSPC_AVRCP_7_4 True TG: Accepting connection release (M)
TSPC_AVRCP_7_5 True TG: Receiving UNIT INFO (M)
TSPC_AVRCP_7_10 False TG: Receiving PASS THROUGH command category 4
(C.1)
TSPC_AVRCP_7_11 True (*) TG: Get Capabilities Response (C.3)
-TSPC_AVRCP_7_12 False TG: List Player Application Settings (C.14)
+TSPC_AVRCP_7_12 False TG: List Player Application Settings Attributes
+ Response (C.14)
TSPC_AVRCP_7_13 False TG: List Player Application Setting Values
- (C.14)
+ Response (C.14)
TSPC_AVRCP_7_14 False TG: Get Current Player Application Settings
- (C.14)
-TSPC_AVRCP_7_15 False TG: Set Player Application Setting Value (C.14)
+ Value Response (C.14)
+TSPC_AVRCP_7_15 False TG: Set Player Application Setting Value
+ Response (C.14)
TSPC_AVRCP_7_16 False TG: Get Player Application Setting Attribute
+ Text Response (O)
+TSPC_AVRCP_7_17 False TG: Get Player Application Setting Value Text
+ Response (O)
+TSPC_AVRCP_7_18 False TG: Inform Displayable Character Set Response
(O)
-TSPC_AVRCP_7_17 False TG: Get Player Application Setting Value (O)
-TSPC_AVRCP_7_18 False TG: Inform Displayable Character Set (O)
TSPC_AVRCP_7_19 False TG: Inform Battery Status Of CT Response (O)
TSPC_AVRCP_7_20 True (*) TG: Get Element Attributes Response (C.3)
TSPC_AVRCP_7_21 True (*) TG: Get Play Status Response (C.2)
(O)
TSPC_AVRCP_7_30 False TG: Notify Event Response:
PLAYER_APPLICATION_SETTING_CHANGED (O)
-TSPC_AVRCP_7_31 True (*) TG: Request ContinuingResponse (C.2)
+TSPC_AVRCP_7_31 True (*) TG: Request Continuing Response (C.2)
TSPC_AVRCP_7_32 True (*) TG: Abort ContinuingResponse Response (C.2)
TSPC_AVRCP_7_34 False TG: Next Group (C.15)
TSPC_AVRCP_7_35 False TG: Previous Group (C.15)
TSPC_AVRCP_7_40 False TG: EVENT_ADDRESSED_PLAYER_CHANGED (C.8)
TSPC_AVRCP_7_41 False TG: Supports Multiple Players (O)
TSPC_AVRCP_7_42 False TG: Browsing (O)
-TSPC_AVRCP_7_42a False TG: Supports initiation of browsing channel
- establishment (O)
+TSPC_AVRCP_7_42a False TG: Initiating connection establishment for
+ browsing channel (O)
TSPC_AVRCP_7_43 False TG: SetBrowsedPlayer (C.6)
TSPC_AVRCP_7_44 False TG: ChangePath (C.6)
TSPC_AVRCP_7_45 False TG: GetFolderItems(Filesystem) (C.6)
TSPC_AVRCP_7_66 False TG: PASSTHROUGH operation supporting press
and hold (O)
TSPC_AVRCP_7_67 False TG: Cover Art (O)
-TSPC_AVRCP_7_68 False TG: GetImageProperties, Cover Art (C.16)
-TSPC_AVRCP_7_69 False TG: GetImage, Cover Art (C.16)
-TSPC_AVRCP_7_70 False TG: GetLinkedThumbnail, Cover Art (C.16)
+TSPC_AVRCP_7_68 False TG: GetImageProperties (C.16)
+TSPC_AVRCP_7_69 False TG: GetImage (C.16)
+TSPC_AVRCP_7_70 False TG: GetLinkedThumbnail (C.16)
-------------------------------------------------------------------------------
C.1: Mandatory to support at least one of the categories. Supported
operation_id's are shown in Table 8 to Table 11.
7/34 or 7/35 is supported it is mandatory to support both,
in accordance with Basic Group Navigation support requirements,
otherwise Excluded.
+C.16: Mandatory if 7/67 (Cover Art) is supported, otherwise Excluded.
-------------------------------------------------------------------------------
Target Profile Version
TSPC_AVRCP_8_31 False TG: category 1 - Operation id: F2 (O)
TSPC_AVRCP_8_32 False TG: category 1 - Operation id: F3 (O)
TSPC_AVRCP_8_33 False TG: category 1 - Operation id: F4 (O)
-TSPC_AVRCP_8_34 False TG: category 1 - Operation id: F5 (O)
-TSPC_AVRCP_8_35 False TG: category 1 - Operation id: vendor unique (O)
+TSPC_AVRCP_8_33a False TG: category 1 - Operation id: F5 (O)
+TSPC_AVRCP_8_34 False TG: category 1 - Operation id: vendor unique (O)
-------------------------------------------------------------------------------
TSPC_AVRCP_9_19 True TG: category 2 - Operation id: volume up (C.2)
TSPC_AVRCP_9_20 True TG: category 2 - Operation id: volume down (C.2)
TSPC_AVRCP_9_21 False TG: category 2 - Operation id: mute (O)
-TSPC_AVRCP_9_22 False TG: category 2 - Operation id: F1 (O)
-TSPC_AVRCP_9_23 False TG: category 2 - Operation id: F2 (O)
-TSPC_AVRCP_9_24 False TG: category 2 - Operation id: F3 (O)
-TSPC_AVRCP_9_25 False TG: category 2 - Operation id: F4 (O)
-TSPC_AVRCP_9_26 False TG: category 2 - Operation id: F5 (O)
-TSPC_AVRCP_9_27 False TG: category 2 - Operation id: vendor unique (O)
+TSPC_AVRCP_9_24 False TG: category 2 - Operation id: F1 (O)
+TSPC_AVRCP_9_25 False TG: category 2 - Operation id: F2 (O)
+TSPC_AVRCP_9_26 False TG: category 2 - Operation id: F3 (O)
+TSPC_AVRCP_9_27 False TG: category 2 - Operation id: F4 (O)
+TSPC_AVRCP_9_27a False TG: category 2 - Operation id: F5 (O)
+TSPC_AVRCP_9_28 False TG: category 2 - Operation id: vendor unique (O)
-------------------------------------------------------------------------------
C.2: Mandatory to support if the device supports category 2 (TSPC_AVRCP_7_8).
-------------------------------------------------------------------------------
TSPC_AVRCP_10_11 False TG: category 3 - Operation id: dot (O)
TSPC_AVRCP_10_12 False TG: category 3 - Operation id: enter (O)
TSPC_AVRCP_10_13 False TG: category 3 - Operation id: clear (O)
-TSPC_AVRCP_10_14 False (*) TG: category 3 - Operation id: channel up (C.3)
-TSPC_AVRCP_10_15 False (*) TG: category 3 - Operation id: channel down
+TSPC_AVRCP_10_14 False (*) TG: category 3 - Operation id: channel up (C.3)
+TSPC_AVRCP_10_15 False (*) TG: category 3 - Operation id: channel down
(C.3)
TSPC_AVRCP_10_16 False TG: category 3 - Operation id: previous channel
(O)
information (O)
TSPC_AVRCP_10_20 False TG: category 3 - Operation id: help (O)
TSPC_AVRCP_10_21 False TG: category 3 - Operation id: power (O)
-TSPC_AVRCP_10_22 False TG: category 3 - Operation id: angle (O)
-TSPC_AVRCP_10_23 False TG: category 3 - Operation id: subpicture (O)
-TSPC_AVRCP_10_24 False TG: category 3 - Operation id: F1 (O)
-TSPC_AVRCP_10_25 False TG: category 3 - Operation id: F2 (O)
-TSPC_AVRCP_10_26 False TG: category 3 - Operation id: F3 (O)
-TSPC_AVRCP_10_27 False TG: category 3 - Operation id: F4 (O)
-TSPC_AVRCP_10_28 False TG: category 3 - Operation id: F5 (O)
-TSPC_AVRCP_10_29 False TG: category 3 - Operation id: vendor unique (O)
+TSPC_AVRCP_10_21a False TG: category 3 - Operation id: angle (O)
+TSPC_AVRCP_10_21b False TG: category 3 - Operation id: subpicture (O)
+TSPC_AVRCP_10_22 False TG: category 3 - Operation id: F1 (O)
+TSPC_AVRCP_10_23 False TG: category 3 - Operation id: F2 (O)
+TSPC_AVRCP_10_24 False TG: category 3 - Operation id: F3 (O)
+TSPC_AVRCP_10_25 False TG: category 3 - Operation id: F4 (O)
+TSPC_AVRCP_10_25a False TG: category 3 - Operation id: F5 (O)
+TSPC_AVRCP_10_26 False TG: category 3 - Operation id: vendor unique (O)
-------------------------------------------------------------------------------
C.3: Mandatory to support if the device supports category 3 (TSPC_AVRCP_7_9).
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
Parameter Name Selected Description
-------------------------------------------------------------------------------
-TSPC_AVRCP_11_1 False (*) TG: category 4 - Operation id: select (C.4)
-TSPC_AVRCP_11_2 False (*) TG: category 4 - Operation id: up (C.4)
-TSPC_AVRCP_11_3 False (*) TG: category 4 - Operation id: down (C.4)
-TSPC_AVRCP_11_4 False (*) TG: category 4 - Operation id: left (C.4)
-TSPC_AVRCP_11_5 False (*) TG: category 4 - Operation id: right (C.4)
+TSPC_AVRCP_11_1 False (*) TG: category 4 - Operation id: select (C.4)
+TSPC_AVRCP_11_2 False (*) TG: category 4 - Operation id: up (C.4)
+TSPC_AVRCP_11_3 False (*) TG: category 4 - Operation id: down (C.4)
+TSPC_AVRCP_11_4 False (*) TG: category 4 - Operation id: left (C.4)
+TSPC_AVRCP_11_5 False (*) TG: category 4 - Operation id: right (C.4)
TSPC_AVRCP_11_6 False TG: category 4 - Operation id: right up (O)
TSPC_AVRCP_11_7 False TG: category 4 - Operation id: right down (O)
TSPC_AVRCP_11_8 False TG: category 4 - Operation id: left up (O)
TSPC_AVRCP_11_9 False TG: category 4 - Operation id: left down (O)
-TSPC_AVRCP_11_10 False (*) TG: category 4 - Operation id: root menu (C.4)
+TSPC_AVRCP_11_10 False (*) TG: category 4 - Operation id: root menu (C.4)
TSPC_AVRCP_11_11 False TG: category 4 - Operation id: setup menu (O)
TSPC_AVRCP_11_12 False TG: category 4 - Operation id: contents menu (O)
TSPC_AVRCP_11_13 False TG: category 4 - Operation id: favorite menu (O)
TSPC_AVRCP_11_34 False TG: category 4 - Operation id: F2 (O)
TSPC_AVRCP_11_35 False TG: category 4 - Operation id: F3 (O)
TSPC_AVRCP_11_36 False TG: category 4 - Operation id: F4 (O)
-TSPC_AVRCP_11_37 False TG: category 4 - Operation id: F5 (O)
-TSPC_AVRCP_11_38 False TG: category 4 - Operation id: vendor unique (O)
+TSPC_AVRCP_11_36a False TG: category 4 - Operation id: F5 (O)
+TSPC_AVRCP_11_37 False TG: category 4 - Operation id: vendor unique (O)
-TSPC_AVRCP_12_1 True General discoverable mode
-TSPC_AVRCP_13_1 True General discoverable mode
+TSPC_AVRCP_12_1 True General discoverable mode (M)
+TSPC_AVRCP_13_1 True General discoverable mode (M)
TSPC_AVRCP_14_1 False OBEX Connect operation (C.1)
TSPC_AVRCP_14_2 False OBEX Get operation (C.1)
TSPC_AVRCP_14_3 False OBEX Disconnect operation (C.1)
DID PICS for the PTS tool.
-PTS version: 5.3
+PTS version: 6.0
* - different than PTS defaults
# - not yet implemented/supported
DIS PICS for the PTS tool.
-PTS version: 5.3
+PTS version: 6.0
* - different than PTS defaults
GAP PICS for the PTS tool.
-PTS version: 5.3
+PTS version: 6.0
* - different than PTS defaults
-------------------------------------------------------------------------------
Parameter Name Selected Description
-------------------------------------------------------------------------------
-TSPC_GAP_23_1 False (*) Peripheral: Non-Connectable Mode (C.1)
+TSPC_GAP_23_1 True Peripheral: Non-Connectable Mode (C.1)
TSPC_GAP_23_2 True Peripheral: Directed Connectable Mode (O)
TSPC_GAP_23_3 True Peripheral: Undirected Connectable Mode (M)
TSPC_GAP_23_4 True Peripheral: Connection Parameter Update
-------------------------------------------------------------------------------
Parameter Name Selected Description
-------------------------------------------------------------------------------
+TSPC_GATT_1_1 True GATT Client Role (O)
+TSPC_GATT_1_2 True GATT Server Role (O)
TSPC_SM_1_1 True Master Role (Initiator)
TSPC_SM_1_2 True Slave Role (Responder)
TSPC_SM_2_4 True OOB supported (O)
-TSPC_ALL False Turns on all
-TSPC_GATT_1_1 True GATT Client Role
-------------------------------------------------------------------------------
GATT PICS for the PTS tool.
-PTS version: 5.3
+PTS version: 6.0
* - different than PTS defaults
-------------------------------------------------------------------------------
TSPC_GATT_1_1 True Generic Attribute Profile Client (C.1)
TSPC_GATT_1_2 True Generic Attribute Profile Server (C.2)
-TSPC_GATT_1A_1 True Complete GATT client (C.3)
-TSPC_GATT_1A_2 True Complete GATT server (C.4)
+TSPC_GATT_1_3 True Complete GATT client (C.3)
+TSPC_GATT_1_4 True Complete GATT server (C.4)
-------------------------------------------------------------------------------
C.1: Optional to support IF TSPC_GATT_2_2; else IF TSPC_GATT_2_1 it is mandatory
to support at least one of TSPC_GATT_1_1 OR TSPC_GATT_1_2
-------------------------------------------------------------------------------
Parameter Name Selected Description
-------------------------------------------------------------------------------
-TSPC_GATT_3_1 True Client: Exchange MTU (C.1)
+TSPC_GATT_3_1 True Client: Exchange MTU (C.2)
TSPC_GATT_3_2 True Client: Discover All Primary Services (C.1)
TSPC_GATT_3_3 True Client: Discover Primary Services Service
UUID (C.1)
TSPC_GATT_3B_37 True Client: Characteristic Format: utf16s (M)
TSPC_GATT_3B_38 True Client: Characteristic Format: struct (M)
-------------------------------------------------------------------------------
-C.1: Mandatory IF TSPC_GATT_1A_1 is supported, otherwise Optional
+C.1: Mandatory IF TSPC_GATT_1_3 is supported, otherwise Optional
+C.2: Mandatory IF TSPC_GATT_1_3 AND TSPC_GATT_2_2 is supported, otherwise
+ Excluded
-------------------------------------------------------------------------------
removed, otherwise Optional
C.2: Mandatory IF GATT TSPC_GATT_4_13 is supported, otherwise Optional
C.3: Mandatory IF GATT TSPC_GATT_4_15 is supported, otherwise Optional
-C.4: Mandatory IF GATT TSPC_GATT_1A_2 is supported, otherwise Optional
+C.4: Mandatory IF GATT TSPC_GATT_1_4 is supported, otherwise Optional
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
+ Attribute Protocol Client Messages
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GATT_8_1 True Support for Multiple ATT bearers from same
+ device (O)
+-------------------------------------------------------------------------------
+
+
Attribute Protocol Client Messages
-------------------------------------------------------------------------------
Parameter Name Selected Description
TSPC_ATT_3_22 True Prepare Write Request (O)
TSPC_ATT_3_24 True Execute Write Request (C.8)
TSPC_ATT_3_26 True Handle Value Notification (M)
+TSPC_ATT_3_28 True Handle Value Confirmation (M)
-------------------------------------------------------------------------------
C.6: Mandatory IF TSPC_ATT_3_16 is supported, otherwise Excluded
C.8: Mandatory IF TSPC_ATT_3_22 is supported, otherwise Excluded
TSPC_ATT_4_20 True Write Command (O)
TSPC_ATT_4_21 True Signed Write Command (O)
TSPC_ATT_4_23 True Prepare Write Response (C.4)
-TSPC_ATT_4_25 True Execute Write Response (C.5)
+TSPC_ATT_4_25 True Execute Write Response (C.4)
TSPC_ATT_4_26 True Handle Value Notification (O)
+TSPC_ATT_4_27 True Handle Value Indication (O)
-------------------------------------------------------------------------------
C.2: Mandatory IF TSPC_ATT_4_14 is supported, otherwise Excluded
C.3: Mandatory IF TSPC_ATT_4_18 is supported, otherwise Excluded
--- /dev/null
+GAVDP PICS for the PTS tool.
+
+PTS version: 6.0
+
+* - different than PTS defaults
+# - not yet implemented/supported
+
+M - mandatory
+O - optional
+
+ Role
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAVDP_1_1 True (*) Initiator (O.1)
+TSPC_GAVDP_1_2 True (*) Initiator (O.1)
+-------------------------------------------------------------------------------
+
+
+ GAVDP Procedures (Initiator)
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAVDP_2_1 True Connection Establishment (M)
+TSPC_GAVDP_2_2 True (*) Transfer Control -Suspend (O)
+TSPC_GAVDP_2_3 False Transfer Control – Change Parameters (O)
+-------------------------------------------------------------------------------
+
+
+ GAVDP Procedures (Acceptor)
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_GAVDP_2_1 True Connection Establishment (M)
+TSPC_GAVDP_2_2 True (*) Transfer Control -Suspend (O)
+TSPC_GAVDP_2_3 False Transfer Control – Change Parameters (O)
+-------------------------------------------------------------------------------
HDP PICS for the PTS tool.
-PTS version: 5.3
+PTS version: 6.0
* - different than PTS defaults
# - not yet implemented/supported
HFP PICS for the PTS tool.
-PTS version: 5.3
+PTS version: 6.0
* - different than PTS defaults
# - not yet implemented/supported
TSPC_HFP_2_2 True Phone Status Information (M)
TSPC_HFP_2_3 True Audio connection handling (M)
TSPC_HFP_2_3a False Audio connection establishment independent of
- call processing (O)
+ a call processing (O)
TSPC_HFP_2_3b True (*) eSCO support in Audio Connection (C.10)
TSPC_HFP_2_3c True (*) Codec negotiation (C.7)
TSPC_HFP_2_4a False Accept an incoming voice call
TSPC_HFP_2_21c True (*) Enhanced Call Status with limited network
notification (C.4)
TSPC_HFP_2_22 False Support for automatic link loss recovery (O)
-TSPC_HFP_2_23 True (*) Individual Indicator Activation (C.9)
+TSPC_HFP_2_23 True Individual Indicator Activation (C.9)
TSPC_HFP_2_24 True (*) Wide Band Speech service (C.8)
TSPC_HFP_2_25 False Support roaming function (O)
TSPC_HFP_2_26 False HF Indicators (C.11)
TSPC_HFP_2_27 False Support CVSD eSCO s4 setting (C.12)
-------------------------------------------------------------------------------
C.1: The AG must support one of item TSPC_HFP_2_4a or TSPC_HFP_2_4b
-C.2: Mandatory if TSPC_HFP_2_12is TRUE; otherwise excluded
+C.2: Mandatory if TSPC_HFP_2_12 is TRUE; otherwise excluded
C.3: Optional if TSPC_HFP_2_12 is TRUE; otherwise excluded
C.4: The AG must support one of item TSPC_HFP_2_21a or TSPC_HFP_2_21c
C.5: Mandatory if TSPC_HFP_2_18a or TSPC_HFP_2_18b; otherwise optional
C.7: Mandatory if TSPC_HFP_2_24 otherwise excluded
C.8: Excluded if TSPC_HFP_0_1 otherwise optional
C.9: Excluded if TSPC_HFP_0_1 otherwise mandatory
-C.10: Mandatory if TSPC_HFP_2_24 otherwise optional
+C.10: Mandatory if TSPC_HFP_2_27 or TSPC_HFP_2_24 otherwise optional
C.11: Optional IF HFP v1.5 (TSPC_HFP_0_1) OR HFP v1.6 (TSPC_HFP_0_2) is NOT
supported, otherwise Excluded.
C.12: Excluded IF HFP v1.5 (TSPC_HFP_0_1) OR HFP v1.6 (TSPC_HFP_0_2) is
muting) (O)
TSPC_HFP_3_5 False (*) Reject an incoming voice call (M)
TSPC_HFP_3_6 False (*) Terminate a call (M)
-
TSPC_HFP_3_7 False (*) Audio connection transfer during an ongoing
call (M)
TSPC_HFP_3_7a False HF-initiated Audio transfer to AG during
TSPC_HFP_3_21a False Enhanced Call Status (O)
TSPC_HFP_3_21b False Enhanced Call Control (C.2)
TSPC_HFP_3_22 False Support for automatic link loss recovery (O)
-TSPC_HFP_3_23 False Individual Indicator Activation (C.6)
+TSPC_HFP_3_23 False (*) Individual Indicator Activation (C.6)
TSPC_HFP_3_24 False Wide Band Speech service (C.6)
TSPC_HFP_3_25 False HF Indicators (C.8)
-TSPC_HFP_3_36 False Support CVSD eSCO S4 setting (C.9)
+TSPC_HFP_3_26 False Support CVSD eSCO S4 setting (C.9)
-------------------------------------------------------------------------------
C.1: Mandatory if TSPC_HFP_3_12; otherwise excluded
C.2: Optional if TSPC_HFP_3_12; otherwise excluded
C.3: Mandatory if TSPC_HFP_3_18a or TSPC_HFP_3_18b, otherwise optional
-C.4: Mandatory if TSPC_HFP_3_18a, otherwise optional
+C.4: Mandatory if TSPC_HFP_3_18b, otherwise optional
C.5: Mandatory if TSPC_HFP_3_24 otherwise excluded
C.6: Excluded if TSPC_HFP_0_1 otherwise optional
-C.7: Mandatory if TSPC_HFP_3_24 otherwise optional
+C.7: Mandatory if TSPC_HFP_3_26 or TSPC_HFP_3_24 otherwise optional
C.8: Optional IF HFP v1.5 (TSPC_HFP_0_1) OR HFP v1.6 (TSPC_HFP_0_2) is NOT
supported, otherwise Excluded.
C.9: Excluded IF HFP v1.5 (TSPC_HFP_0_1) OR HFP v1.6 (TSPC_HFP_0_2) is
-------------------------------------------------------------------------------
C.1: Mandatory if Wide band speech service is supported TSPC_HFP_2_24 or
TSPC_HFP_3_24, otherwise excluded
-C.2: Mandatory IF TPSC_HFP_2_3b (eSCO support in Audio Connection - AG) OR
- TSPC_HFP_3_3b (eSCO support in Audio Connection - HF); otherwise
- Excluded.
+C.2: Mandatory IF TPSC_HFP_2_3b OR TSPC_HFP_3_3b; otherwise Excluded.
-------------------------------------------------------------------------------
HID PICS for the PTS tool.
-PTS version: 5.3
+PTS version: 6.0
* - different than PTS defaults
# - not yet implemented/supported
HOGP PICS for the PTS tool.
-PTS version: 5.3
+PTS version: 6.0
* - different than PTS defaults
# - not yet implemented/supported
TSPC_HOGP_11_11 True Report characteristic configuration with 0x0001
(M.1)
TSPC_HOGP_11_11a True Report characteristic configuration with 0x0000
- (M.1)
+ (O)
TSPC_HOGP_11_12 True Read HID Information characteristic (M.1)
TSPC_HOGP_11_13 False (*) Suspend State (O)
TSPC_HOGP_11_14 False (*) Exit Suspend State (C.1)
-------------------------------------------------------------------------------
Parameter Name Selected Description
-------------------------------------------------------------------------------
-TSPC_HOGP_13_1 True Attribute Protocol supported over LE Transport
-TSPC_HOGP_13_2 True Generic Attribute Profile Client
-TSPC_HOGP_13_3 True Discover All Primary Services
-TSPC_HOGP_13_4 False (*) Discover Primary Services by Service UUID
-TSPC_HOGP_13_5 True Find Included Services
-TSPC_HOGP_13_6 True Discover All Characteristics of a Service
-TSPC_HOGP_13_7 False (*) Discover Characteristics by UUID
-TSPC_HOGP_13_8 True Discover All Characteristic Descriptors
-TSPC_HOGP_13_9 True Read Characteristic Value
-TSPC_HOGP_13_10 True Read using Characteristic UUID
-TSPC_HOGP_13_11 True Read Long Characteristic Value
-TSPC_HOGP_13_12 True Read Characteristic Descriptors
-TSPC_HOGP_13_13 True Write without Response
-TSPC_HOGP_13_14 True Write Characteristic Value
-TSPC_HOGP_13_15 True Write Characteristic Descriptors
-TSPC_HOGP_13_16 True Notifications
-TSPC_HOGP_13_17 True Exchange MTU
+TSPC_HOGP_13_1 True Attribute Protocol supported over LE Transport (M)
+TSPC_HOGP_13_2 True Generic Attribute Profile Client (M)
+TSPC_HOGP_13_3 True Discover All Primary Services (C.1)
+TSPC_HOGP_13_4 False (*) Discover Primary Services by Service UUID (C.1)
+TSPC_HOGP_13_5 True Find Included Services (M)
+TSPC_HOGP_13_6 True Discover All Characteristics of a Service (C.2)
+TSPC_HOGP_13_7 False (*) Discover Characteristics by UUID (C.2)
+TSPC_HOGP_13_8 True Discover All Characteristic Descriptors (M)
+TSPC_HOGP_13_9 True Read Characteristic Value (M)
+TSPC_HOGP_13_10 True Read using Characteristic UUID (O)
+TSPC_HOGP_13_11 True Read Long Characteristic Value (M)
+TSPC_HOGP_13_12 True Read Characteristic Descriptors (M)
+TSPC_HOGP_13_13 True Write without Response (M)
+TSPC_HOGP_13_14 True Write Characteristic Value (M)
+TSPC_HOGP_13_15 True Write Characteristic Descriptors (M)
+TSPC_HOGP_13_16 True Notifications (M)
+TSPC_HOGP_13_17 True Exchange MTU (M)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support at least one of these features.
+C.2: Mandatory to support at least one of these features.
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
Parameter Name Selected Description
-------------------------------------------------------------------------------
-TSPC_HOGP_14_1 False (*) Attribute Protocol supported over LE Transport
-TSPC_HOGP_14_2 False (*) Generic Attribute Profile Client
-TSPC_HOGP_14_3 False (*) Discover All Primary Services
-TSPC_HOGP_14_4 False (*) Discover Primary Services by Service UUID
-TSPC_HOGP_14_5 False (*) Discover All Characteristics of a Service
-TSPC_HOGP_14_6 False (*) Discover Characteristics by UUID
-TSPC_HOGP_14_7 False (*) Discover All Characteristic Descriptors
-TSPC_HOGP_14_8 False (*) Read Characteristic Value
-TSPC_HOGP_14_9 False (*) Read using Characteristic UUID
-TSPC_HOGP_14_10 False (*) Read Characteristic Descriptors
-TSPC_HOGP_14_11 False (*) Write without Response
-TSPC_HOGP_14_12 False (*) Write Characteristic Value
-TSPC_HOGP_14_13 False (*) Write Characteristic Descriptors
-TSPC_HOGP_14_14 False (*) Notifications
+TSPC_HOGP_14_1 False (*) Attribute Protocol supported over LE Transport (M)
+TSPC_HOGP_14_2 False (*) Generic Attribute Profile Client (M)
+TSPC_HOGP_14_3 False (*) Discover All Primary Services (C.1)
+TSPC_HOGP_14_4 False (*) Discover Primary Services by Service UUID (C.1)
+TSPC_HOGP_14_5 False (*) Discover All Characteristics of a Service (O)
+TSPC_HOGP_14_6 False (*) Discover Characteristics by UUID (O)
+TSPC_HOGP_14_7 False (*) Discover All Characteristic Descriptors (M)
+TSPC_HOGP_14_8 False (*) Read Characteristic Value (M)
+TSPC_HOGP_14_9 False (*) Read using Characteristic UUID (M)
+TSPC_HOGP_14_10 False (*) Read Characteristic Descriptors (M)
+TSPC_HOGP_14_11 False (*) Write without Response (M)
+TSPC_HOGP_14_12 False (*) Write Characteristic Value (M)
+TSPC_HOGP_14_13 False (*) Write Characteristic Descriptors (M)
+TSPC_HOGP_14_14 False (*) Notifications (M)
-------------------------------------------------------------------------------
Parameter Name Selected Description
-------------------------------------------------------------------------------
TSPC_HOGP_16_1 True No Security Requirements (LE Security Level 1,
- No Security)
+ No Security) (M)
TSPC_HOGP_16_2 True Unauthenticated no MITM protection (LE Security
- Level 2, Just Works)
+ Level 2, Just Works) (M)
TSPC_HOGP_16_3 True Authenticated MITM protection (LE Security
- Level 3, Passkey)
+ Level 3, Passkey) (O)
-------------------------------------------------------------------------------
HSP PICS for the PTS tool.
-PTS version: 5.3
+PTS version: 6.0
* - different than PTS defaults
# - not yet implemented/supported
TSPC_HSP_3_12 False Remote microphone gain control (C.2)
TSPC_HSP_3_13 False HS informs AG about local changes of microphone
gain (O)
-TSPC_HSP_3_14 False (*) Microphone gain setting storage by HS (O)
-TSPC_HSP_3_15 False Connection handling with Detach/Page (M)
+TSPC_HSP_3_14 False Microphone gain setting storage by HS (O)
+TSPC_HSP_3_15 False (*) Connection handling with Detach/Page (M)
TSPC_HSP_3_16 False Connection handling with Park Mode (C.3)
-------------------------------------------------------------------------------
C.1: Mandatory if TSPC_HSP_3_10 is supported, otherwise optional
IOPT PICS for the PTS tool.
-PTS version: 5.3
+PTS version: 6.0
* - different than PTS defaults
# - not yet implemented/supported
TSPC_support_ False Support for: Extended
ExtendedServiceDiscoveryProfile_L2CAP SDP. Version: L2CAP
-TSPC_support_FAXProfile_DE False Support for: FAX Profile
+TSPC_support_FAXProfile_DT False Support for: FAX Profile
Role: Data Terminal
TSPC_support_FAXProfile_GW False Support for: FAX Profile
TSPC_support_HealthDeviceProfile_Source False Support for: HDP
Role: Source
-TSPC_support_NewHandsFreeProfile_AG True Support for: HFP
+TSPC_support_NewHandsFreeProfile_AG True (*) Support for: HFP
Role: Audio gateway
TSPC_support_NewHandsFreeProfile_HF False Support for: HFP
MCAP PICS for the PTS tool.
-PTS version: 5.3
+PTS version: 6.0
* - different than PTS defaults
# - not yet implemented/supported
OPP PICS for the PTS tool.
-PTS version: 5.3
+PTS version: 6.0
* - different than PTS defaults
# - not yet implemented/supported
PAN PICS for the PTS tool.
-PTS version: 5.3
+PTS version: 6.0
* - different than PTS defaults
# - not yet implemented/supported
TSPC_PAN_1a_1 True BNEP: BNEP Connection Setup (M)
TSPC_PAN_1a_2 True BNEP: BNEP Data Packet Reception (M)
TSPC_PAN_1a_3 True BNEP: BNEP Data Packet Transmission (M)
+TSPC_PAN_1a_3a True BNEP: BNEP Compressed Packet Transmission (O)
+TSPC_PAN_1a_3b True BNEP: BNEP Compressed Packet Transmission
+ Source Only (O)
TSPC_PAN_1a_4 True BNEP: BNEP Control Message Processing (M)
TSPC_PAN_1a_5 True BNEP: BNEP Extension Header Processing (M)
TSPC_PAN_1a_6 False BNEP: Network Protocol Filter Message
PBAP PICS for the PTS tool.
-PTS version: 5.3
+PTS version: 6.0
* - different than PTS defaults
# - not yet implemented/supported
M - mandatory
O - optional
- Major Profile Version (X.Y)
+
+ Roles
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_1_1 False Role: PCE (C.1)
+TSPC_PBAP_1_2 True (*) Role: PSE (C.1)
+-------------------------------------------------------------------------------
+C1: It is mandatory to support at least one of the defined roles.
+-------------------------------------------------------------------------------
+
+
+ Client Major Profile Version (X.Y)
-------------------------------------------------------------------------------
Parameter Name Selected Description
-------------------------------------------------------------------------------
-TSPC_PBAP_0_1 False Role: PBAP 1.0 (C.1)
-TSPC_PBAP_0_2 True (*) Role: PBAP 1.1 (C.1)
-TSPC_PBAP_0_3 False Role: PBAP 1.2 (C.1)
+TSPC_PBAP_2a_1 False PBAP 1.0 (C.1)
+TSPC_PBAP_2a_2 False PBAP 1.1 (C.1)
+TSPC_PBAP_2a_3 False PBAP 1.2 (C.1)
-------------------------------------------------------------------------------
C.1: Mandatory to support one and only one major profile version.
-------------------------------------------------------------------------------
- Roles
+ Client Minor Profile Version (X.Y)
-------------------------------------------------------------------------------
Parameter Name Selected Description
-------------------------------------------------------------------------------
-TSPC_PBAP_1_1 False Role: PCE (C.1)
-TSPC_PBAP_1_2 True (*) Role: PSE (C.1)
+TSPC_PBAP_2b_1 False PBAP 1.1.1 (C.1)
-------------------------------------------------------------------------------
-C1: It is mandatory to support at least one of the defined roles.
+C.1: Optional if 2a/2 (PBAP 1.1) is supported, otherwise Excluded.
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
- Supported features ( PSE )
+ Server Major Profile Version (X.Y)
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_9a_1 False PBAP 1.0 (C.1)
+TSPC_PBAP_9a_2 True (*) PBAP 1.1 (C.1)
+TSPC_PBAP_9a_3 False PBAP 1.2 (C.1)
+-------------------------------------------------------------------------------
+C.1: Mandatory to support one and only one major profile version.
+-------------------------------------------------------------------------------
+
+
+ Server Minor Profile Version (X.Y.Z)
+-------------------------------------------------------------------------------
+Parameter Name Selected Description
+-------------------------------------------------------------------------------
+TSPC_PBAP_9b_1 False PBAP 1.1.1 (C.1)
+-------------------------------------------------------------------------------
+C.1: Optional if 9a/2 (PBAP 1.1) is supported, otherwise Excluded.
+-------------------------------------------------------------------------------
+
+
+ Supported features (PSE)
-------------------------------------------------------------------------------
Parameter Name Selected Description
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
Parameter Name Selected Description
-------------------------------------------------------------------------------
-TSPC_PBAP_26_1 False (*) PSE: GOEP v2.0 or later (M)
+TSPC_PBAP_26_1 False PSE: GOEP v2.0 or later (M)
TSPC_PBAP_26_2 False (*) PSE: GOEP v2 Backwards Compatibility (M)
TSPC_PBAP_26_3 False PSE: OBEX over L2CAP (M)
TSPC_PBAP_26_4 False PSE: OBEX SRM (M)
RFCOMM PICS for the PTS tool.
-PTS version: 5.3
+PTS version: 6.0
* - different than PTS defaults
# - not yet implemented/supported
ScPP PICS for the PTS tool.
-PTS version: 5.3
+PTS version: 6.0
* - different than PTS defaults
+SDP PICS for the PTS tool.
-* - different than BITE defaults
+PTS version: 6.0
+
+* - different than PTS defaults
# - not yet implemented/supported
M - mandatory
-------------------------------------------------------------------------------
Item Selected Description
-------------------------------------------------------------------------------
-E.1.1 True Support for 128 bit UUID (M)
-E.1.2 True Support for 32 bit UUID (M)
-E.1.3 True Support for 16 bit UUID (M)
+TSPC_SDP_1_1 True Support for 128 bit UUID (M)
+TSPC_SDP_1_2 True Support for 32 bit UUID (M)
+TSPC_SDP_1_3 True Support for 16 bit UUID (M)
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
Item Selected Description
-------------------------------------------------------------------------------
-E.1b.1 True Support for server role (C.1)
-E.1b.2 True Support for client role (C.1)
+TSPC_SDP_1b_1 True (*) Support for server role (C.1)
+TSPC_SDP_1b_2 True (*) Support for client role (C.1)
-------------------------------------------------------------------------------
C.1 Mandatory to support at least one of the roles
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
Item Selected Description
-------------------------------------------------------------------------------
-E.2.1 True Support for respond on search of single
+TSPC_SDP_2_1 True (*) Support for respond on search of single
Service, using ServiceSearchRequest (C.2)
-E.2.2 True Support for respond on search of Service,
+TSPC_SDP_2_2 True (*) Support for respond on search of Service,
using continuation state (O)
-E.2.3 True Search for services using the continuation
+TSPC_SDP_2_3 True (*) Search for services using the continuation
state (C.1)
-------------------------------------------------------------------------------
C.1 Mandatory to support if the client role is supported (1b/2)
-------------------------------------------------------------------------------
Item Selected Description
-------------------------------------------------------------------------------
-E.3.1 True Support for error response on Service search
+TSPC_SDP_3_1 True Support for error response on Service search
request (M)
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
Item Selected Description
-------------------------------------------------------------------------------
-E.4.1 True Support for respond on search of
+TSPC_SDP_4_1 True Support for respond on search of
Attribute(s) (M)
-E.4.2 True Support for respond on search of
+TSPC_SDP_4_2 True (*) Support for respond on search of
Attribute, using continuation state (O)
-E.4.3 True Support for respond on search on
+TSPC_SDP_4_3 True (*) Support for respond on search on
attribute AdditionalProtocolDescriptorList (O)
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
Item Selected Description
-------------------------------------------------------------------------------
-E.5.1 True Support for error response on Attribute
+TSPC_SDP_5_1 True Support for error response on Attribute
search request (M)
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
Item Selected Description
-------------------------------------------------------------------------------
-E.6.1 True Support for respond on search for Service(s)
+TSPC_SDP_6_1 True Support for respond on search for Service(s)
and Attribute(s) (M)
-E.6.2 True Support for respond on search of Attribute,
+TSPC_SDP_6_2 True (*) Support for respond on search of Attribute,
using continuation state (O)
-E.6.3 True Support for respond on search on attribute
+TSPC_SDP_6_3 True (*) Support for respond on search on attribute
AdditionalProtocolDescriptorList on existing
service (O)
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
Item Selected Description
-------------------------------------------------------------------------------
-E.7.1 True Support for error response on Service and
+TSPC_SDP_7_1 True Support for error response on Service and
Attribute request (M)
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
Item Selected Description
-------------------------------------------------------------------------------
-E.8.1 True Support for browsing, using
+TSPC_SDP_8_1 True (*) Support for browsing, using
SDP_ServiceSearchRequest and
SDP_ServiceAttributeRequest (O)
-E.8.2 True Support for browsing, using
+TSPC_SDP_8_2 True (*) Support for browsing, using
SDP_ServiceSearchAttributeRequest (O)
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
Item Selected Description
-------------------------------------------------------------------------------
-E.9.1 True ServiceID (O)
-E.9.2 True ProtocolDescriptorList (O)
-E.9.3 True ServiceRecordState (O)
-E.9.4 True ServiceInfoTimeToLive (O)
-E.9.5 True BrowseGroupList (O)
-E.9.6 True LanguageBaseAttributeIdList (O)
-E.9.7 True ServiceAvailability (O)
-E.9.8 True IconURL (O)
-E.9.9 True ServiceName (O)
-E.9.10 True ServiceDescription (O)
-E.9.11 True ProviderName (O)
-E.9.12 True VersionNumberList (O)
-E.9.13 True ServiceDataBaseState (O)
-E.9.14 True BluetoothProfileDescriptorList (O)
-E.9.15 True DocumentationURL (O)
-E.9.16 True ClientExecutableURL (O)
-E.9.17 True AdditionalProtocolDescriptorList (C.1)
-E.9.18 True ServiceRecordHandle (M)
-E.9.19 True ServiceClassIDList (M)
+TSPC_SDP_9_1 True (*) ServiceID (O)
+TSPC_SDP_9_2 True (*) ProtocolDescriptorList (O)
+TSPC_SDP_9_3 True (*) ServiceRecordState (O)
+TSPC_SDP_9_4 True (*) ServiceInfoTimeToLive (O)
+TSPC_SDP_9_5 True (*) BrowseGroupList (O)
+TSPC_SDP_9_6 True (*) LanguageBaseAttributeIdList (O)
+TSPC_SDP_9_7 True (*) ServiceAvailability (O)
+TSPC_SDP_9_8 True (*) IconURL (O)
+TSPC_SDP_9_9 True (*) ServiceName (O)
+TSPC_SDP_9_10 True (*) ServiceDescription (O)
+TSPC_SDP_9_11 True (*) ProviderName (O)
+TSPC_SDP_9_12 True (*) VersionNumberList (O)
+TSPC_SDP_9_13 True (*) ServiceDataBaseState (O)
+TSPC_SDP_9_14 True (*) BluetoothProfileDescriptorList (O)
+TSPC_SDP_9_15 True (*) DocumentationURL (O)
+TSPC_SDP_9_16 True (*) ClientExecutableURL (O)
+TSPC_SDP_9_17 True (*) AdditionalProtocolDescriptorList (C.1)
+TSPC_SDP_9_18 True ServiceRecordHandle (M)
+TSPC_SDP_9_19 True ServiceClassIDList (M)
-------------------------------------------------------------------------------
C.1: Optional if 9/2 is supported, otherwise excluded
-------------------------------------------------------------------------------
SM PICS for the PTS tool.
-PTS version: 5.3
+PTS version: 6.0
* - different than PTS defaults
SPP PICS for the PTS tool.
-PTS version: 5.3
+PTS version: 6.0
* - different than PTS defaults
# - not yet implemented/supported
A2DP PIXIT for the PTS tool.
-PTS version: 5.3
+PTS version: 6.0
* - different than PTS defaults
& - should be set to IUT Bluetooth address
-------------------------------------------------------------------------------
Parameter Name Value
-------------------------------------------------------------------------------
-TSPX_security_enabled FALSE
+TSPX_security_enabled TRUE (*)
TSPX_bd_addr_iut 112233445566 (*&)
TSPX_SRC_class_of_device 080418
TSPX_SNK_class_of_device 04041C
TSPX_use_implicit_send TRUE
TSPX_media_directory C:\Program Files\Bluetooth SIG\Bluetooth PTS\
bin\audio (#)
-TSPX_no_avrcp TRUE
+TSPX_no_avrcp FALSE (*)
TSPX_auth_password 0000
TSPX_auth_user_id PTS
TSPX_rfcomm_channel 8
AVCTP PIXIT for the PTS tool.
-PTS version: 5.3
+PTS version: 6.0
* - different than PTS defaults
& - should be set to IUT Bluetooth address
AVDTP PIXIT for the PTS tool.
-PTS version: 5.3
+PTS version: 6.0
* - different than PTS defaults
& - should be set to IUT Bluetooth address
AVRCP PIXIT for the PTS tool.
-PTS version: 5.3
+PTS version: 6.0
* - different than PTS defaults
& - should be set to IUT Bluetooth address
TSPX_delete_link_key FALSE
TSPX_time_guard 300000
TSPX_avrcp_only FALSE
-TSPX_search_string tomorrow
+TSPX_search_string 3
TSPX_max_avc_fragments 10
TSPX_establish_avdtp_stream TRUE
TSPX_use_implicit_send TRUE
DID PIXIT for the PTS tool.
-PTS version: 5.3
+PTS version: 6.0
* - different than PTS defaults
& - should be set to IUT Bluetooth address
DIS PIXIT for the PTS tool.
-PTS version: 5.3
+PTS version: 6.0
* - different than PTS defaults
& - should be set to IUT Bluetooth address
TSPX_security_enabled TRUE (*)
TSPX_iut_setup_att_over_br_edr FALSE
TSPX_tester_appearance 0000
+TSPX_iut_use_resolvable_random_address FALSE
-------------------------------------------------------------------------------
GAP PIXIT for the PTS tool.
-PTS version: 5.3
+PTS version: 6.0
* - different than PTS defaults
& - should be set to IUT Bluetooth address
+# - should be set to IUT name
Required PIXIT settings
-------------------------------------------------------------------------------
TSPX_gen_disc_scan_min 10240
TSPX_database_file Database-GAP.sig
TSPX_iut_rx_mtu 23
-TSPX_iut_private_address_interval 10000
-TSPX_iut_privacy_enabled True (*)
+TSPX_iut_private_address_interval 30000 (*)
+TSPX_iut_privacy_enabled False
TSPX_psm 1001
TSPX_iut_valid_connection_interval_min 00C8
TSPX_iut_valid_conneciton_interval_max 0960
TSPX_encryption_before_service_request False
TSPX_tester_appearance 0000
TSPX_iut_advertising_data_in_broadcasting_mode [set to default value]
+TSPX_iut_device_name_in_adv_packet_for_random_address PTS-66DE (#)
-------------------------------------------------------------------------------
GATT PIXIT for the PTS tool.
-PTS version: 5.3
+PTS version: 6.0
* - different than PTS defaults
& - should be set to IUT Bluetooth address
TSPX_delete_ltk FALSE
TSPX_characteristic_readable FALSE
TSPX_tester_appearance 0000
+TSPX_iut_use_resolvable_random_access FALSE
-------------------------------------------------------------------------------
--- /dev/null
+GAVDP PIXIT for the PTS tool.
+
+PTS version: 6.0
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+^ - should be set accordingly
+# - should be set to PTS's bin/audio folder
+
+ Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name Value
+-------------------------------------------------------------------------------
+TSPX_SNK_class_of_device 04041C
+TSPX_SRC_class_of_device 080418
+TSPX_bd_addr_iut 112233445566 (*&)
+TSPX_delete_link_key FALSE
+TSPX_media_directory C:\Program Files\Bluetooth SIG\Bluetooth PTS\
+ bin\audio (#)
+TSPX_no_avrcp FALSE
+TSPX_pin_code 0000
+TSPX_security_enabled FALSE
+TSPX_tester_av_role
+TSPX_time_guard 300000
+TSPX_use_implicit_send TRUE
+TSPX_auth_password 0000
+TSPX_auth_user_id PTS
+TSPX_rfcomm_channel 8
+TSPX_l2cap_psm 1011
+TSPX_no_confirmations FALSE
+TSPX_cover_art_uuid
+-------------------------------------------------------------------------------
HDP PIXIT for the PTS tool.
-PTS version: 5.3
+PTS version: 6.0
* - different than PTS defaults
& - should be set to IUT Bluetooth address
TSPX_security_enabled TRUE
TSPX_delete_link_key FALSE
TSPX_bd_addr_iut 112233445566 (*&)
-TSPX_sink_device_class_of_device 00900
-TSPX_source_device_class_of_device 00900
+TSPX_sink_device_class_of_device 000900
+TSPX_source_device_class_of_device 000900
TSPX_pin_code 0000
TSPX_use_dynamic_pin FALSE
TSPX_use_implicit_send TRUE
HFP PIXIT for the PTS tool.
-PTS version: 5.3
+PTS version: 6.0
* - different than PTS defaults
& - should be set to IUT Bluetooth address
HID PIXIT for the PTS tool.
-PTS version: 5.3
+PTS version: 6.0
* - different than PTS defaults
& - should be set to IUT Bluetooth address
TSPX_time_reconnect 30000
TSPX_secure_simple_pairing_pass_key_confirmation False
TSPX_hid_report_id 1
+TSPX_hid_report_data ff00 (*)
-------------------------------------------------------------------------------
HOGP PIXIT for the PTS tool.
-PTS version: 5.3
+PTS version: 6.0
* - different than PTS defaults
& - should be set to IUT Bluetooth address
TSPX_output_report_data 001234567890EF
TSPX_feature_report_data 872D3F45EA
TSPX_tester_appearance 03C0
+TSPX_iut_use_resolvable_random_address FALSE
-------------------------------------------------------------------------------
HSP PIXIT for the PTS tool.
-PTS version: 5.3
+PTS version: 6.0
* - different than PTS defaults
& - should be set to IUT Bluetooth address
IOPT PIXIT for the PTS tool.
-PTS version: 5.3
+PTS version: 6.0
* - different than PTS defaults
& - should be set to IUT Bluetooth address
MCAP PIXIT for the PTS tool.
-PTS version: 5.3
+PTS version: 6.0
* - different than PTS defaults
& - should be set to IUT Bluetooth address
-------------------------------------------------------------------------------
TSPX_bd_addr_iut 112233445566 (*&)
TSPX_delete_link_key FALSE
-TSPX_MCAP_DC_max 1
+TSPX_MCAP_DC_max 1 (*)
TSPX_MCAP_l2cap_psm_control 1003
TSPX_MCAP_l2cap_psm_control_B
TSPX_MCAP_l2cap_psm_data 1005
OPP PIXIT for the PTS tool.
-PTS version: 5.3
+PTS version: 6.0
* - different than PTS defaults
& - should be set to IUT Bluetooth address
PAN PIXIT for the PTS tool.
-PTS version: 5.3
+PTS version: 6.0
* - different than PTS defaults
-& - should be set to IUT or PTS Bluetooth address respectively
+& - should be set to IUT or PTS Bluetooth/MAC address respectively
Required PIXIT settings
-------------------------------------------------------------------------------
TSPX_secure_simple_pairing_pass_key_confirmation False
TSPX_iut_friendly_bt_name gprs-pc
TSPX_PTS_role_when_iut_is_PANU default
+TSPX_auth_password 0000
+TSPX_auth_user_id PTS
+TSPX_l2cap_psm 000F
+TSPX_rfcomm_channel 8
+TSPX_no_confirmations FALSE
+TSPX_UUID_dest_address 0000
+TSPX_UUID_source_address 0000
+TSPX_MAC_dest_address 112233445566 (*&)
+TSPX_MAC_source_address 112233445566 (*&)
-------------------------------------------------------------------------------
PBAP PIXIT for the PTS tool.
-PTS version: 5.3
+PTS version: 6.0
* - different than PTS defaults
& - should be set to IUT Bluetooth address
RFCOMM PIXIT for the PTS tool.
-PTS version: 5.3
+PTS version: 6.0
* - different than PTS defaults
& - should be set to IUT Bluetooth address
ScPP PIXIT for the PTS tool.
-PTS version: 5.3
+PTS version: 6.0
* - different than PTS defaults
& - should be set to IUT Bluetooth address
TSPX_delete_ltk FALSE
TSPX_security_enabled FALSE
TSPX_tester_appearance 0000
+TSPX_iut_use_resolvable_random_address FALSE
-------------------------------------------------------------------------------
--- /dev/null
+SDP PIXIT for the PTS tool.
+
+PTS version: 6.0
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+^ - should be set accordingly
+# - should be set according to the reported phone number's type
+
+ Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name Value
+-------------------------------------------------------------------------------
+TSPX_sdp_service_search_pattern 0100
+TSPX_sdp_service_search_pattern_no_results EEEE
+TSPX_sdp_service_search_additional_protocol_descriptor_list
+TSPX_sdp_service_search_bluetooth_profile_descriptor_list
+TSPX_sdp_service_search_pattern_browse_group_list
+TSPX_sdp_service_search_pattern_client_exe_url
+TSPX_sdp_service_search_pattern_documentation_url
+TSPX_sdp_service_search_pattern_icon_url
+TSPX_sdp_service_search_pattern_language_base_attribute_id_list
+TSPX_sdp_service_search_pattern_protocol_descriptor_list
+TSPX_sdp_service_search_pattern_provider_name
+TSPX_sdp_service_search_pattern_service_availability
+TSPX_sdp_service_search_pattern_service_data_base_state 1000(*)
+TSPX_sdp_service_search_pattern_service_description
+TSPX_sdp_service_search_pattern_service_id
+TSPX_sdp_service_search_pattern_service_info_time_to_live
+TSPX_sdp_service_search_pattern_version_number_list 1000(*)
+TSPX_sdp_service_search_pattern_service_name
+TSPX_sdp_service_search_pattern_service_record_state
+TSPX_sdp_unsupported_attribute_id
+TSPX_security_enabled FALSE
+TSPX_delete_link_key FALSE
+TSPX_bd_addr_iut 112233445566(*&)
+TSPX_class_of_device_pts 200404
+TSPX_class_of_device_test_pts_initiator TRUE
+TSPX_limited_inquiry_used FALSE
+TSPX_pin_code 0000
+TSPX_time_guard 200000
+TSPX_device_search_time 20
+TSPX_use_implicit_send TRUE
+TSPX_secure_simple_pairing_pass_key_confirmation FALSE
+-------------------------------------------------------------------------------
SM PIXIT for the PTS tool.
-PTS version: 5.3
+PTS version: 6.0
* - different than PTS defaults
& - should be set to IUT Bluetooth address
SPP PIXIT for the PTS tool.
-PTS version: 5.3
+PTS version: 6.0
* - different than PTS defaults
& - should be set to IUT Bluetooth address
PTS test results for A2DP
-PTS version: 5.3
-Tested: 19-November-2014
+PTS version: 6.0
+Tested: 14-January-2015
Android version: 5.0
Results:
Test Name Result Notes
-------------------------------------------------------------------------------
TC_SRC_CC_BV_09_I PASS Start streaming
-TC_SRC_CC_BV_10_I N/A
+TC_SRC_CC_BV_10_I PASS
TC_SRC_REL_BV_01_I PASS Connect to PTS from IUT. When requested
disconnect from IUT
TC_SRC_REL_BV_02_I PASS
TC_SRC_SET_BV_03_I PASS Start streaming
TC_SRC_SET_BV_04_I PASS Start streaming
TC_SRC_SET_BV_05_I PASS IUT must be moved out of range
+ JIRA issue BA-314
TC_SRC_SET_BV_06_I PASS IUT must be moved out of range
TC_SRC_SUS_BV_01_I PASS Stop streaming
TC_SRC_SUS_BV_02_I PASS
TC_SRC_SDP_BV_01_I PASS
TC_SRC_AS_BV_01_I PASS Requires checking if the output on the IUT is
correct
+TC_SRC_AS_BV_02_I N/A
+TC_SRC_AS_BV_03_I N/A
-------------------------------------------------------------------------------
PTS test results for AVCTP
-PTS version: 5.3
-Tested: 03-December-2014
+PTS version: 6.0
+Tested: 11-February-2015
Android version: 5.0
Results:
TC_CT_CCM_BI_01_C N/A
TC_CT_NFR_BV_01_C N/A
TC_CT_NFR_BV_04_C PASS haltest: rc set_volume 5
+ Note: IUT must be connectable and discoverable
TC_CT_FRA_BV_01_C N/A
TC_CT_FRA_BV_04_C N/A
-------------------------------------------------------------------------------
PTS test results for AVDTP
-PTS version: 5.3
-Tested: 24-November-2014
+PTS version: 6.0
+Tested: 16-January-2015
Android version: 5.0
Results:
TC_ACP_SNK_SIG_SMG_ESR04_BI_28_C PASS avdtptest -d SINK -l
TC_ACP_SNK_SIG_SMG_ESR05_BI_15_C N/A
TC_ACP_SNK_SIG_SYN_BV_01_C PASS avdtptest -d SINK -l
-TC_ACP_SNK_SIG_SYN_BV_02_C PASS avdtptest -d SINK -l -p
TC_ACP_SNK_SIG_SYN_BV_03_C PASS avdtptest -d SINK -l
-TC_ACP_SNK_SIG_SYN_BV_04_C PASS avdtptest -d SINK -l -p
TC_ACP_SNK_TRA_BTR_BI_01_C PASS avdtptest -d SINK -l
TC_ACP_SNK_TRA_BTR_BV_02_C PASS avdtptest -d SINK -l
TC_ACP_SNK_TRA_MUX_BI_01_C N/A
PTS test results for AVRCP
-PTS version: 5.3
-Tested: 20-November-2014
+PTS version: 6.0
+Tested: 21-January-2015
Android version: 5.0
Results:
TC_CT_VLH_BI_04_C PASS adb logcat: check VOLUME_CHANGED value
TC_CT_VLH_BV_01_C PASS Send SetAbsolute Volume command by pressing
volume up or down buttons
-TC_CT_VLH_BV_01_I PASS adb logcat: check VOLUME_CHANGED value
-TC_CT_VLH_BV_02_I PASS Send SetAbsolute Volume command by pressing
+TC_CT_VLH_BV_03_C PASS adb logcat: check VOLUME_CHANGED value
+TC_CT_VLH_BV_01_I PASS Send SetAbsolute Volume command by pressing
+ volume up or down buttons
+TC_CT_VLH_BV_02_I PASS Send SetAbsolute Volume command by pressing
volume up or down buttons
-TC_CT_VLH_BV_03_C PASS
-------------------------------------------------------------------------------
TC_TG_PTT_BV_03_I N/A
TC_TG_PTT_BV_04_I N/A
TC_TG_PTT_BV_05_I N/A
-TC_TG_RCR_BV_02_C PASS One can use: lame --ta/tl for creating metadata
-TC_TG_RCR_BV_04_C PASS
+TC_TG_RCR_BV_02_C PASS Use modified media metadata (artist, title,
+ album etc.) to be larger than 512 byte.
+TC_TG_RCR_BV_04_C PASS Use modified media metadata (artist, title,
+ album etc.) to be larger than 512 byte.
TC_TG_VLH_BI_01_C N/A
TC_TG_VLH_BI_02_C N/A
TC_TG_VLH_BV_01_I N/A
PTS test results for DID
-PTS version: 5.3
-Tested: 07-November-2014
+PTS version: 6.0
+Tested: 22-January-2015
Android version: 5.0
Results:
PTS test results for DIS
-PTS version: 5.3
-Tested: 26-November-2014
+PTS version: 6.0
+Tested: 22-January-2015
Android version: 5.0
Results:
PTS test results for GAP
-PTS version: 5.3
-Tested: 05-December-2014
+PTS version: 6.0
+Tested: 19-January-2015
Android version: 5.0
Kernel version: 3.18
btmgmt discov off
<answer NO to non-connectable adv question>
btmgmt discov limited 30
+ btmgmt advertising on
TC_DISC_LIMM_BV_04_C PASS btmgmt connectable on
btmgmt discov off
btmgmt power off
btmgmt bredr off
btmgmt power on
btmgmt discov limited 30
+ btmgmt advertising on
TC_DISC_GENM_BV_01_C PASS btmgmt connectable on
btmgmt discov on
+ btmgmt advertising on
<answer NO to non-connectable adv question>
TC_DISC_GENM_BV_02_C PASS btmgmt connectable on
btmgmt advertising on
btmgmt discov on
TC_DISC_GENM_BV_03_C PASS btmgmt connectable on
btmgmt discov on
+ btmgmt advertising on
<answer NO to non-connectable adv question>
TC_DISC_GENM_BV_04_C PASS btmgmt connectable on
btmgmt power off
PTS AD flags must have bit 1 and bit 0 unset
TC_IDLE_GIN_BV_01_C PASS Start discovery from IUT
TC_IDLE_LIN_BV_01_C PASS hcitool scan --iac=liac
-TC_IDLE_NAMP_BV_01_C PASS possible to PASS using haltest following steps:
- gattc - register client, connect to PTS, search
- all services, get characteristic and then read
- characteristic (name)
-TC_IDLE_NAMP_BV_02_C PASS PTS issue #12679
- haltest: gatts connect
+TC_IDLE_NAMP_BV_01_C PASS haltest: gattc register_client
+ gattc listen 1
+ gattc search_service 1 1800
+ gattc get_characteristic 1 {1800,0,1}
+ gattc read_characteristic 1 {1800,0,1} {2a00,1}
+TC_IDLE_NAMP_BV_02_C PASS btmgmt advertising on
TC_CONN_NCON_BV_01_C PASS btmgmt connectable off
btmgmt advertising on
<answer NO to non-connectable adv question>
TC_CONN_SCEP_BV_01_C PASS 'gattc connect' prior to pressing OK on PTS
TC_CONN_SCEP_BV_02_C N/A
TC_CONN_DCEP_BV_01_C PASS 'gattc connect' prior to pressing OK on PTS
-TC_CONN_DCEP_BV_02_C INC Test Spec. Errata approved - PTS issue #12600
- is claimed to be resolved in upcoming ETS 8400
+TC_CONN_DCEP_BV_02_C N/A
TC_CONN_DCEP_BV_03_C PASS gattc connect
TC_CONN_DCEP_BV_04_C N/A
-TC_CONN_CPUP_BV_01_C PASS gattc register_client
- gattc listen
-TC_CONN_CPUP_BV_02_C PASS gattc register_client
- gattc listen
-TC_CONN_CPUP_BV_03_C PASS gattc register_client
- gattc listen
+TC_CONN_CPUP_BV_01_C PASS btmgmt advertising on
+TC_CONN_CPUP_BV_02_C PASS btmgmt advertising on
+TC_CONN_CPUP_BV_03_C PASS btmgmt advertising on
TC_CONN_CPUP_BV_04_C PASS gattc register_client
gattc connect
gattc disconnect
0x0960
gattc disconnect <client_if> <pts_bdaddr>
<conn_id>
-TC_CONN_TERM_BV_01_C PASS
+TC_CONN_TERM_BV_01_C PASS gattc register_client
+ gattc listen
+ gattc disconnect
TC_CONN_PRDA_BV_01_C PASS gattc register_client
gattc listen
gattc disconnect
-TC_CONN_PRDA_BV_02_C INC PTS issue #12310
- Note: PTS issues #12207 & #12310 are claimed
- to be resolved by the ETS provided in PTS issue
- #12312 however it does not solve the problem
-TC_BOND_NBON_BV_01_C PASS
+TC_CONN_PRDA_BV_02_C INC PTS issue #12950
+ gattc register_client
+ bluetooth create_bond
+ gattc connect
+TC_BOND_NBON_BV_01_C PASS haltest:
+ gattc register_client
+ gattc connect
+ gatt disconnect
+ gattc connect
+ gatt disconnect
TC_BOND_NBON_BV_02_C PASS haltest: gattc register_client
gattc connect <client_id> <address>
bluetooth create_bond <address>
- bluetooth remove_bond <address>
+ gattc connect <client_id> <address>
+ bluetooth create_bond <address>
TC_BOND_NBON_BV_03_C PASS haltest: gattc listen
TC_BOND_BON_BV_01_C PASS PTS issue #12503
- possible to pass without MITM:
- btmgmt power on
- btmgmt le on
- btmgmt ssp on
- btmgmt connectable on
- btmgmt discov on
- btmgmt advertising on
- btmgmt pairable on
- To bond with PTS execute
- btmgmt pair -t 0x01 -c 0x03 <PTS addr>
-TC_BOND_BON_BV_02_C PASS
-TC_BOND_BON_BV_03_C PASS PTS issue #12678
+ haltest:
+ bluetooth set_adapter_property
+ BT_PROPERTY_ADAPTER_SCAN_MODE
+ BT_SCAN_MODE_CONNECTABLE
gattc register_client
gattc listen 1
+ bluetooth create_bond <pts_address>
+TC_BOND_BON_BV_02_C PASS gattc regicter_client
+ gattc scan
+ gattc connect
+ bluetooth create_bond
+ gattc connect
+ gattc test_command 226 <addr> <uuid> 1
+TC_BOND_BON_BV_03_C PASS gattc register_client
+ gattc listen 1
TC_BOND_BON_BV_04_C PASS haltest: gattc_register_client
gattc connect <client_id> <address>
gattc disconnect
gatts add_service 2 <uuid> 3
gatts add_characteristic 2 1b <uuid> 10 68
gatts start_service 2 1b 1
- gattc listen
+ gattc listen 1
PTS asks for handle with Insufficient auth
- bluetooth ssp_reply <addr> <passkey>
- gatts send_response
-TC_SEC_AUT_BV_12_C INC PTS issue #12657
- haltest: gatts register_server
- gatts add_service 2 <uuid> 3
- gatts add_characteristic 2 <service_handle>
- <uuid> 10 68
- gatts start_service 2 <service_handle> 1
- gatts connect <server_if> <addr>
+ gatts send_response 1 1 0 1d 0 0x1234
+TC_SEC_AUT_BV_12_C PASS haltest: gatts register_server
+ gatts add_service 1 <uuid> 3
+ gatts add_characteristic 1 1b <uuid> 10 68
+ gatts start_service 1 1b 1
+ gatts connect 1 <addr>
PTS asks for handle with Insufficient auth
- bluetooth ssp_reply <addr> <passkey>
- gatts send_response
-TC_SEC_AUT_BV_13_C INC PTS issue #12657
- haltest: gatts register_server
- gatts add_service 2 <uuid> 3
- gatts add_characteristic 2 <service_handle>
- <uuid> 10 68
- gatts start_service 2 <service_handle> 1
- gatts connect <server_if> <addr>
+ gatts send_response 1 1 0 1d 0 0x1234
+TC_SEC_AUT_BV_13_C PASS haltest: gatts register_server
+ gatts add_service 1 <uuid> 3
+ gatts add_characteristic 1 1b <uuid> 10 68
+ gatts start_service 1 1b 1
+ gatts connect 1 <addr>
PTS asks for handle with Insufficient auth
- bluetooth ssp_reply <addr> <passkey>
- gatts send_response
-TC_SEC_AUT_BV_14_C INC PTS issue #12657
- haltest:gattc register_client
+ gatts send_response 1 1 0 1d 0 0x1234
+TC_SEC_AUT_BV_14_C PASS haltest: gattc register_client
gatts register_server
gatts add_service 2 <uuid> 3
gatts add_characteristic 2 1b <uuid> 10 68
gatts start_service 2 1b 1
- gattc listen
+ gattc listen 1
PTS asks for handle with Insufficient auth
- bluetooth ssp_reply <addr> <passkey>
- gatts send_response
+ gatts send_response 1 1 0 1d 0 0x1234
TC_SEC_AUT_BV_15_C N/A
TC_SEC_AUT_BV_16_C N/A
-TC_SEC_AUT_BV_17_C PASS
+TC_SEC_AUT_BV_17_C PASS haltest: gattc register_client
+ gattc connect
+ gattc search_service
+ gattc get_characteristic
+ gattc read_characteristic
+ bluetooth create_bond
TC_SEC_AUT_BV_18_C PASS haltest: gattc register_client
gattc listen
gattc search_service
gattc read_characteristic
TC_SEC_AUT_BV_19_C PASS
TC_SEC_AUT_BV_20_C PASS haltest: gattc register_client
- gattc listen <client_id> 1
- Confirm bonding
- gattc search_service
- gattc get_characteristic
- gattc read_characteristic
- gattc listen <client_id> 0
- Click OK on PTS popup to disconnect
- bluetooth remove bond
- gattc listen <client_id> 1
- gattc read_characteristic
+ gattc listen 1 1
+ gattc search_service 2
+ gattc get_characteristic 2 {1801,1,1}
+ gattc read_characteristic 2 {1801,1,1} {2a05,1}
+ gattc read_characteristic 2 {1801,1,1} {2a05,1}
+ 1
TC_SEC_AUT_BV_21_C PASS haltest: gattc register_client
gattc connect
bluetooth create_bond
haltest: gattc register_client
gattc listen
gattc test_command 226 <addr> <u1> 1
-TC_SEC_AUT_BV_23_C INC PTS issue #12657
- haltest: gatts register_server
- gatts add_service 2 <uuid> 3
- gatts add_characteristic 2 <service_handle>
- <uuid> 10 34
- gatts start_service 2 <service_handle> 1
- gattc register_client
- gattc listen
- bluetooth ssp_reply
- gatts send_response
-TC_SEC_AUT_BV_24_C INC PTS issue #12657
- haltest: gatts register_server
+TC_SEC_AUT_BV_23_C PASS haltest: gattc register_client
+ gatts register_server
gatts add_service 2 <uuid> 3
- gatts add_characteristic 2 <service_handle>
- <uuid> 10 34
- gatts start_service 2 <service_handle> 1
- gatts connect <PTS addr>
- bluetooth ssp_reply
+ gatts add_characteristic 2 1b <uuid> 10 34
+ gatts start_service 2 1b 1
+ gattc listen 1
+ PTS asks for handle with insufficient encryption
+ gatts send_response 3 1 0 1d 0 0x1234
+TC_SEC_AUT_BV_24_C PASS haltest: gatts register_server
+ gatts add_service 1 <uuid> 3
+ gatts add_characteristic 1 1b <uuid> 10 34
+ gatts start_service 1 1b 1
+ gatts connect
gatts disconnect
gatts connect
PTS asks for handle with insufficient encryption
- gatts send_response
+ gatts send_response 2 1 0 1d 0 0x1234
TC_SEC_CSIGN_BV_01_C PASS haltest:
gattc connect
bluetooth create_bond
gattc connect
gattc write_characteristic: <write_type> 4
gattc disconnect
-TC_SEC_CSIGN_BV_02_C INC PTS issue #12675
- haltest:
- gatts add_service
- gatts add_chaaracteristic:
- <properties> 66
- <permissions> 129
- gatts start_service
+TC_SEC_CSIGN_BV_02_C PASS haltest: gattc register_client
+ gatts register_server
+ gatts add_service 2 <uuid> 3
+ gatts add_characteristic 2 1b <uuid> 66 129
+ gatts start_service 2 1b 1
+ gattc listen 1
gatts disconnect
- gattc disconnect
-TC_SEC_CSIGN_BI_01_C INC PTS issue #12675
- haltest:
- gatts add_service
- gatts add_chaaracteristic:
- <properties> 66
- <permissions> 129
- gatts start_service
+TC_SEC_CSIGN_BI_01_C PASS gattc register_client
+ gatts register_server
+ gatts add_service 2 <uuid> 3
+ gatts add_characteristic 2 1b <uuid> 66 129
+ gatts start_service 2 1b 1
+ gattc listen 1
gatts disconnect
gattc disconnect
-TC_SEC_CSIGN_BI_02_C INC PTS issue #12675
- haltest:
- gatts add_service
- gatts add_chaaracteristic:
- <properties> 66
- <permissions> 129
- gatts start_service
+TC_SEC_CSIGN_BI_02_C PASS gattc register_client
+ gatts register_server
+ gatts add_service 2 <uuid> 3
+ gatts add_characteristic 2 1b <uuid> 66 129
+ gatts start_service 2 1b 1
+ gattc listen 1
gatts disconnect
gattc disconnect
-TC_SEC_CSIGN_BI_03_C INC PTS issue #12675
- haltest:
- gatts add_service
- gatts add_characteristic:
- <properties> 64
- <permissions> 128
- gatts start_service
- gattc listen
- bluetooth ssp_reply
+TC_SEC_CSIGN_BI_03_C PASS gattc register_client
+ gatts register_server
+ gatts add_service 2 <uuid> 3
+ gatts add_characteristic 2 1b <uuid> 66 129
+ gatts start_service 2 1b 1
+ gattc listen 1
gatts disconnect
+ gattc disconnect
bluetooth remove_bond
-TC_SEC_CSIGN_BI_04_C INC PTS issue #12675
- haltest:
- gatts add_service
- gatts add_characteristic:
- <properties> 64
- <permissions> 256
- gatts start_service
- gattc listen
- bluetooth ssp_reply
+TC_SEC_CSIGN_BI_04_C PASS gattc register_client
+ gatts register_server
+ gatts add_service 2 <uuid> 3
+ gatts add_characteristic 2 1b <uuid> 64 256
+ gatts start_service 2 1b 1
+ gattc listen 1
gatts disconnect
+ gattc disconnect
TC_PRIV_CONN_BV_01_C N/A
TC_PRIV_CONN_BV_02_C N/A
TC_PRIV_CONN_BV_03_C N/A
TC_PRIV_CONN_BV_07_C N/A
TC_PRIV_CONN_BV_08_C N/A
TC_PRIV_CONN_BV_09_C N/A
-TC_PRIV_CONN_BV_10_C PASS PTS issue #12312
- Note: currently for this test following PIXITs
- are required to be be changed:
+TC_PRIV_CONN_BV_10_C PASS PTS issue #12951
+ Note: PIXITs required to be changed:
TSPX_using_public_device_address: FALSE
TSPX_using_random_device_address: TRUE
-TC_PRIV_CONN_BV_11_C INC PTS issue #12310, JIRA #BA-186
+ echo 30 > /sys/kernel/debug/bluetooth/hci0/
+ rpa_timeout
+ btmgmt power off
+ btmgmt privacy on
+ btmgmt power on
+TC_PRIV_CONN_BV_11_C INC PTS issue #12952
+ JIRA #BA-186
TC_ADV_BV_01_C N/A
TC_ADV_BV_02_C PASS gattc register_client
gattc listen 1 1
TC_GAT_BV_06_C N/A
TC_GAT_BV_07_C N/A
TC_GAT_BV_08_C N/A
-TC_DM_NCON_BV_01_C PASS btmgmt connectable off
-TC_DM_CON_BV_01_C PASS btmgmt connectable on
-TC_DM_NBON_BV_01_C PASS
+TC_DM_NCON_BV_01_C PASS bluetooth set_adapter_property
+ BT_PROPERTY_ADAPTER_SCAN_MODE
+ BT_SCAN_MODE_NONE
+ gattc register_client
+ gattc listen 1
+TC_DM_CON_BV_01_C PASS bluetooth set_adapter_property
+ BT_PROPERTY_ADAPTER_SCAN_MODE
+ BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE
+ gattc register_client
+ gattc listen 1
+TC_DM_NBON_BV_01_C PASS btmgmt bondable off
TC_DM_BON_BV_01_C PASS haltest:
create_bond and remove_bond when requested
TC_DM_GIN_BV_01_C PASS
TC_DM_LIN_BV_01_C PASS
TC_DM_NAD_BV_01_C PASS Start discovery from IUT
TC_DM_NAD_BV_02_C PASS
-TC_DM_LEP_BV_01_C PASS gattc register_client
+TC_DM_LEP_BV_01_C PASS PTS issue #12949
+ bluetooth set_adapter_property
+ BT_PROPERTY_ADAPTER_SCAN_MODE
+ BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE
+ gattc register_client
gattc listen 1 1
TC_DM_LEP_BV_02_C PASS Use basic rate PTS dongle
haltest:
bluetooth set_adapter_property
TC_DM_LEP_BV_04_C PASS l2test -n <PTS bdaddr>
TC_DM_LEP_BV_05_C PASS btmgmt find -b
- l2test -n 00:1B:DC:06:06:22
-TC_DM_LEP_BV_06_C PASS
-TC_DM_LEP_BV_07_C PASS
-TC_DM_LEP_BV_08_C PASS
+ l2test -n <PTS bdaddr>
+TC_DM_LEP_BV_06_C PASS gattc connect
+TC_DM_LEP_BV_07_C PASS PTS issue #12949
+ bluetooth set_adapter_property
+ BT_PROPERTY_ADAPTER_SCAN_MODE
+ BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE
+ gattc register_client
+ gattc listen 1 1
+TC_DM_LEP_BV_08_C PASS PTS issue #12949
+ bluetooth set_adapter_property
+ BT_PROPERTY_ADAPTER_SCAN_MODE
+ BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE
+ gattc register_client
+ gattc listen 1 1
TC_DM_LEP_BV_09_C PASS haltest:
bluetooth enable
bluetooth set_adapter_property
gattc connect
l2test -n -P 31 <PTS addr>
disconnect
-TC_DM_LEP_BV_10_C PASS haltest:
+TC_DM_LEP_BV_10_C PASS PTS issue #12949
+ haltest:
bluetooth enable
bluetooth set_adapter_property
BT_PROPERTY_ADAPTER_SCAN_MODE
BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE
gattc register_client
gattc connect
+ gattc disconnect
-------------------------------------------------------------------------------
PTS test results for GATT
-PTS version: 5.3
-Tested: 05-December-2014
+PTS version: 6.0
+Tested: 02-February-2015
Android version: 5.0
Results:
gattc search_service
gattc get_characteristic
gattc write_characteristic: type 3
-TC_GAC_SR_BV_01_C PASS PTS issue #12357
- NOTE: tested with ETS attached to errata
+TC_GAC_SR_BV_01_C PASS PTS issue #13073
+ TSE #6271
haltest:
gatts add_service
gatts add_chaaracteristic:
gattc connect
gattc refresh
TC_GAD_CL_BV_03_C PASS haltest:
- gattc register_client
- gattc scan
- gattc connect
- gattc search_service
+ gattc register_client
+ gattc scan
+ gattc connect
+ gattc search_service
gattc get_included_service
gattc_disconnect
+ gattc connect
+ gattc refresh
TC_GAD_CL_BV_04_C PASS haltest:
when requested: gattc get_characteristic
TC_GAD_CL_BV_05_C PASS haltest:
gattc search_service
gattc get_characteristic: srvc_id based on
handle from logs
- gattc write_characteristic 2 <long_value>
+ gattc write_characteristic 3 <value>
gattc execute_write
gattc disconnect
TC_GAW_CL_BI_33_C PASS haltest:
gatts add_descriptor: <permmisions> 17
gatts start_service
gatts send_response: <status> 12
-TC_GAW_SR_BI_32_C PASS haltest:
+TC_GAW_SR_BI_32_C PASS PTS issue #12823
+ haltest:
gatts add_service
gatts add_characteristic:
<properties> 10 <permissions> 17
gattc get_characteristic: srvc_id based on
handle from logs
gattc read_characcteristic
- gattc disconnect
+ wait for 30 sec timeout
TC_GAT_CL_BV_02_C PASS haltest:
gattc connect
gattc search_service
handle from logs
gattc write_characcteristic 2 <short_value>
gattc disconnect
+ wait for 30 sec timeout
TC_GAT_SR_BV_01_C PASS haltest:
gatts add_service
gatts add_characteristic:
--- /dev/null
+PTS test results for GAVDP
+
+PTS version: 6.0
+Tested: 20-February-2015
+Android version: 5.0
+
+Results:
+PASS test passed
+FAIL test failed
+INC test is inconclusive
+N/A test is disabled due to PICS setup
+NONE test result is none
+
+-------------------------------------------------------------------------------
+Test Name Result Notes
+-------------------------------------------------------------------------------
+TC_ACP_APP_CON_BV_01_C PASS
+TC_ACP_APP_TRC_BV_01_C N/A
+TC_ACP_APP_TRC_BV_02_C PASS
+TC_INT_APP_CON_BV_01_C PASS
+TC_INT_APP_TRC_BV_01_C N/A
+TC_INT_APP_TRC_BV_02_C PASS
+-------------------------------------------------------------------------------
PTS test results for HDP
-PTS version: 5.3
-Tested: 07-November-2014
+PTS version: 6.0
+Tested: 16-February-2015
Android version: 5.0
Results:
Test Name Result Notes
-------------------------------------------------------------------------------
TC_SRC_CON_BV_01_I PASS haltest:
- bluetooth enable
-
- bluetooth set_adapter_property
- BT_PROPERTY_ADAPTER_SCAN_MODE
- BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE
-
hl register_application <args>
for instance:
hl register_application health intel heartrate
for instance:
bluetooth ssp_reply <bdaddr>
BT_SSP_VARIANT_CONSENT 1
-TC_SRC_CON_BV_02_I PASS
+ Note: IUT must be discoverable, connectable
+TC_SRC_CON_BV_02_I PASS Note: IUT must be in discoverable mode
TC_SRC_CON_BV_03_I PASS when prompted: bluetooth ssp_reply <args>
TC_SRC_CON_BV_04_I PASS haltest:
hl connect_channel <app_id> <bd_addr>
when prompted: bluetooth ssp_reply <args>
TC_SRC_CON_BV_05_I PASS when prompted: bluetooth ssp_reply <args>
+ Note: IUT must be in connectable mode
TC_SRC_CON_BV_06_I PASS haltest:
hl connect_channel <app_id> <bd_addr>
<mdep_cfg_index>
when prompted: bluetooth ssp_reply <args>
-TC_SRC_CON_BV_07_I PASS when prompted: bluetooth start_discovery
-TC_SRC_CON_BV_08_I PASS when prompted: bluetooth ssp_reply <args>
+TC_SRC_CON_BV_07_I PASS bluetooth start_discovery
+ Note: PTS HDP device must be discovered
+TC_SRC_CON_BV_08_I PASS bluetooth remove_bond <PTS addr>
+ when prompted: bluetooth ssp_reply <args>
TC_SRC_CON_BV_09_I PASS haltest:
hl connect_channel <app_id> <bd_addr>
<mdep_cfg_index>
when prompted: bluetooth ssp_reply <args>
TC_SRC_CC_BV_02_C PASS when prompted: bluetooth ssp_reply <args>
+ Note: IUT must be discoverable, connectable
TC_SRC_CC_BV_03_C PASS haltest:
hl register_application bluez-android Bluez
bluez-hdp health-device-profile 1
<mdep_cfg_index>
when prompted: bluetooth ssp_reply <args>
-
TC_SRC_CC_BV_05_C PASS haltest:
hl register_application bluez-android Bluez
bluez-hdp health-device-profile 1
BTHL_MDEP_ROLE_SOURCE 4100
BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter
+ Note: IUT must be discoverable, connectable
TC_SRC_CC_BV_07_C PASS haltest:
hl register_application bluez-android Bluez
bluez-hdp health-device-profile 2
BTHL_CHANNEL_TYPE_STREAMING pulse-oximeter
when prompted: bluetooth ssp_reply <args>
+ Note: IUT must be discoverable, connectable
TC_SRC_CC_BI_12_C PASS haltest:
hl register_application bluez-android Bluez
bluez-hdp health-device-profile 1
BTHL_MDEP_ROLE_SOURCE 4100
BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter
+ Note: IUT must be discoverable, connectable
TC_SRC_HCT_BV_01_I PASS haltest:
hl register_application bluez-android Bluez
bluez-hdp health-device-profile 1
bluez-hdp health-device-profile 1
BTHL_MDEP_ROLE_SOURCE 4100
BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter
+ Note: IUT must be discoverable, connectable
TC_SRC_HCT_BV_03_I N/A
TC_SRC_HCT_BV_04_I PASS haltest:
hl register_application bluez-android Bluez
BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter
when prompted: bluetooth ssp_reply <args>
+ Note: IUT must be discoverable, connectable
TC_SRC_HCT_BV_05_C N/A
TC_SRC_HCT_BV_06_C PASS haltest:
hl register_application bluez-android Bluez
BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter
when prompted: bluetooth ssp_reply <args>
+ Note: IUT must be discoverable, connectable
TC_SRC_HCT_BV_07_C PASS haltest:
hl register_application bluez-android Bluez
bluez-hdp health-device-profile 1
bluez-hdp health-device-profile 1
BTHL_MDEP_ROLE_SOURCE 4100
BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter
+ Note: IUT must be discoverable, connectable
TC_SRC_DEP_BV_01_I N/A
TC_SRC_DEP_BV_02_I N/A
TC_SNK_CON_BV_01_I PASS haltest:
for instance:
bluetooth ssp_reply <bdaddr>
BT_SSP_VARIANT_CONSENT 1
-TC_SNK_CON_BV_02_I PASS
+ Note: IUT must be discoverable, connectable
+TC_SNK_CON_BV_02_I PASS Note: IUT must be discoverable, connectable
TC_SNK_CON_BV_03_I PASS when prompted: bluetooth ssp_reply <args>
+ Note: IUT must be discoverable, connectable
TC_SNK_CON_BV_04_I PASS haltest:
hl connect_channel <app_id> <bd_addr>
<mdep_cfg_index>
when prompted: bluetooth ssp_reply <args>
+ Note: IUT must be discoverable, connectable
TC_SNK_CON_BV_05_I PASS when prompted: bluetooth ssp_reply <args>
TC_SNK_CON_BV_06_I PASS haltest:
hl connect_channel <app_id> <bd_addr>
<mdep_cfg_index>
when prompted: bluetooth ssp_reply <args>
-TC_SNK_CON_BV_07_I PASS when prompted: bluetooth start_discovery
-TC_SNK_CON_BV_08_I PASS when prompted: bluetooth ssp_reply <args>
+TC_SNK_CON_BV_07_I PASS bluetooth start_discovery
+TC_SNK_CON_BV_08_I PASS bluetooth remove_bond <PTS addr>
+
+ when prompted: bluetooth ssp_reply <args>
+ Note: IUT must be discoverable, connectable
TC_SNK_CON_BV_09_I PASS haltest:
hl connect_channel <app_id> <bd_addr>
<mdep_cfg_index>
when prompted: bluetooth ssp_reply <args>
TC_SNK_CC_BV_02_C PASS when prompted: bluetooth ssp_reply <args>
+ Note: IUT must be discoverable, connectable
TC_SNK_CC_BV_04_C PASS haltest:
hl register_application bluez-android Bluez
bluez-hdp health-device-profile 1
<mdep_cfg_index>
when prompted: bluetooth ssp_reply <args>
-
TC_SNK_CC_BV_06_C PASS haltest:
hl register_application bluez-android Bluez
bluez-hdp health-device-profile 2
when prompted:
hl connect_channel <app_id> <bd_addr>
<mdep_cfg_index>
-
TC_SNK_CC_BV_10_C PASS haltest:
hl register_application bluez-android Bluez
bluez-hdp health-device-profile 2
pulse-oximeter
BTHL_MDEP_ROLE_SINK 4100 BTHL_CHANNEL_TYPE_STREAMING
pulse-oximeter
+ Note: IUT must be discoverable, connectable
TC_SNK_CC_BI_11_C PASS haltest:
hl register_application bluez-android Bluez
bluez-hdp health-device-profile 1
BTHL_MDEP_ROLE_SINK 4100
BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter
+ Note: IUT must be discoverable, connectable
TC_SNK_HCT_BV_01_I PASS haltest:
hl register_application bluez-android Bluez
bluez-hdp health-device-profile 1
bluez-hdp health-device-profile 1
BTHL_MDEP_ROLE_SINK 4100
BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter
+ Note: IUT must be discoverable, connectable
TC_SNK_HCT_BV_03_I N/A
TC_SNK_HCT_BV_04_I PASS haltest:
hl register_application bluez-android Bluez
BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter
when prompted: bluetooth ssp_reply <args>
+ Note: IUT must be discoverable, connectable
TC_SNK_HCT_BV_05_C N/A
TC_SNK_HCT_BV_06_C PASS haltest:
hl register_application bluez-android Bluez
bluez-hdp health-device-profile 1
BTHL_MDEP_ROLE_SINK 4100
BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter
+ Note: IUT must be discoverable, connectable
TC_SNK_HCT_BV_07_C PASS haltest:
hl register_application bluez-android Bluez
bluez-hdp health-device-profile 1
BTHL_MDEP_ROLE_SINK 4100
BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter
+ Note: IUT must be discoverable, connectable
TC_SNK_DE_BV_01_I N/A
TC_SNK_DE_BV_02_I PASS haltest:
hl register_application bluez-android Bluez
bluez-hdp health-device-profile 1
BTHL_MDEP_ROLE_SINK 4100
BTHL_CHANNEL_TYPE_RELIABLE pulse-oximeter
+ Note: IUT must be discoverable, connectable
TC_SNK_DEP_BV_03_I N/A
TC_SNK_DEP_BV_04_I N/A
-------------------------------------------------------------------------------
PTS test results for HFP
-PTS version: 5.3
-Tested: 02-December-2014
+PTS version: 6.0
+Tested: 16-February-2015
Android version: 5.0
Results:
TC_AG_OCM_BV_02_I PASS
TC_AG_OCL_BV_01_I PASS
TC_AG_OCL_BV_02_I PASS
-TC_AG_TWC_BV_01_I PASS JIRA #BA-281
+TC_AG_TWC_BV_01_I PASS
TC_AG_TWC_BV_02_I PASS
TC_AG_TWC_BV_03_I PASS
TC_AG_TWC_BV_04_I PASS
TC_AG_SLC_BV_07_I PASS
TC_AG_SLC_BV_09_I N/A
TC_AG_SLC_BV_10_I N/A
-TC_AG_ACC_BV_08_I PASS PTS issue #12039
+TC_AG_ACC_BV_08_I PASS
TC_AG_ACC_BV_09_I PASS
-TC_AG_ACC_BV_10_I PASS PTS issue #12039
-TC_AG_ACC_BV_11_I PASS PTS issue #12039
+TC_AG_ACC_BV_10_I PASS
+TC_AG_ACC_BV_11_I PASS
TC_AG_ACC_BI_12_I PASS
TC_AG_ACC_BI_13_I PASS
TC_AG_ACC_BI_14_I PASS
PTS test results for HID
-PTS version: 5.3
-Tested: 26-November-2014
+PTS version: 6.0
+Tested: 30-January-2015
Android version: 5.0
Results:
TC_HOS_HCR_BV_03_I N/A
TC_HOS_HCR_BV_04_I N/A
TC_HOS_HDT_BV_01_I PASS
-TC_HOS_HDT_BV_02_I PASS from shell execute:
- haltest
- bluetooth enable
- hidhost connect <PTS bdaddr>
- hidhost send_data <PTS bdaddr> ff00
+TC_HOS_HDT_BV_02_I PASS haltest: hidhost connect <addr>
+ hidhost send_data <addr> ff00
+ NOTE: PTS displays wrong report data on popup
+ PTS issue #13021
TC_HOS_HDT_BV_03_I N/A
TC_HOS_HDT_BV_04_I N/A
TC_HOS_HID_BV_01_C N/A
TC_HOS_HID_BV_08_C N/A
TC_HOS_HID_BV_09_C N/A
TC_HOS_HID_BV_10_C N/A
-TC_HOS_DAT_BV_01_C PASS from shell execute:
- haltest
- bluetooth enable
- hidhost connect <PTS bdaddr>
- hidhost send_data <PTS bdaddr> ff00
+TC_HOS_DAT_BV_01_C PASS haltest: hidhost connect <addr>
+ hidhost send_data <addr> ff00
+ NOTE: PTS displays wrong report data on popup
+ PTS issue #13021
TC_HOS_DAT_BV_02_C N/A
TC_HOS_DAT_BI_01_C N/A
TC_HOS_DAT_BI_02_C N/A
PTS test results for HoG
-PTS version: 5.3
-Tested: 01-December-2014
+PTS version: 6.0
+Tested: 24-February-2015
Android version: 5.0
Results:
-------------------------------------------------------------------------------
Test Name Result Notes
-------------------------------------------------------------------------------
-TC_HGDS_HH_BV_01_I PASS PTS issue #12119
- This issue affects all PTS test cases
+TC_HGDS_HH_BV_01_I PASS
TC_HGDS_HH_BV_02_I PASS
TC_HGDS_HH_BV_03_I PASS
TC_HGDS_HD_BV_01_I N/A
TC_HGCF_BH_BV_04_I N/A
TC_HGCF_BH_BV_05_I N/A
TC_HGCF_BH_BV_06_I N/A
-TC_HGNF_RH_BV_01_I PASS
+TC_HGNF_RH_BV_01_I PASS PTS issue #12878
TC_HGNF_RH_BI_01_I PASS
TC_HGNF_RH_BI_01_I PASS
TC_HGNF_BH_BV_02_I N/A
PTS test results for HSP
-PTS version: 5.3
-Tested: 24-November-2014
+PTS version: 6.0
+Tested: 12-February-2015
Android version: 5.0
Results:
PTS test results for IOPT
-PTS version: 5.3
-Tested: 18-November-2014
+PTS version: 6.0
+Tested: 03-February-2015
Android version: 5.0
Results:
Test Name Result Notes
-------------------------------------------------------------------------------
TC_COD_BV_01_I PASS IUT must be discoverable
-TC_COD_BV_02_I N/A Under PTS 5.1 test shall be disabled as there is
- matching test case in HFP test suit (test No. 15)
- PICS settings for HFP shall be disabled for IOPT
-TC_SDSS_BV_02_I PASS
-TC_SDAS_BV_03_I PASS
-TC_SDR_BV_04_I PASS for every PTS bt profile:
- haltest: bluetooth get_remote_service_record <addr>
- <requred 128 sdp uuid>
+TC_COD_BV_02_I N/A
+TC_SDSS_BV_02_I PASS Note: HDP sink record should be registered before test
+ run, e.g. register health app via HDPSample.apk
+TC_SDAS_BV_03_I PASS Note: HDP sink record should be registered before test
+ run, e.g. register health app via HDPSample.apk
+TC_SDR_BV_04_I PASS For every asked to check PTS bt profile:
+ haltest: bluetooth get_remote_service_record <PTS addr>
+ <profile uuid>
+ Note: 0000xxxx - acceptable 16bit uuid format
-------------------------------------------------------------------------------
-------------------------------------------------------------------------------
Test Name Result Notes
-------------------------------------------------------------------------------
+ For all tests daemon should be stopped then:
+ setprop ctl.start hciattach
TC_COS_CED_BV_01_C PASS l2test -n -P 4113 <bdaddr>
TC_COS_CED_BV_03_C PASS l2test -y -N 1 -P 4113 <bdaddr>
TC_COS_CED_BV_04_C PASS l2test -n -P 4113 <bdaddr>
TC_ECF_BV_06_C N/A
TC_ECF_BV_07_C N/A
TC_ECF_BV_08_C N/A
- NOTE: for LE tests daemon should be stopped
- then:
- setprop ctl.start hciattach
TC_LE_CPU_BV_01_C PASS l2test -n -V le_public -J 4
TC_LE_CPU_BV_02_C PASS l2test -n -V le_public -J 4 <braddr>
TC_LE_CPU_BI_01_C PASS l2test -n -V le_public -J 4 <braddr>
PTS test results for MCAP
-PTS version: 5.3
-Tested: 27-November-2014
+PTS version: 6.0
+Tested: 10-February-2015
Android version: 5.0
Results:
-------------------------------------------------------------------------------
Test Name Result Notes
-------------------------------------------------------------------------------
-TC_MCAP_CE_BV_01_C INC PTS issue #12693
- mcaptest -C 4099 -D 4101 -dc <PTS addr>
-TC_MCAP_CE_BV_02_C INC PTS issue #12693
- mcaptest -C 4099 -D 4101
-TC_MCAP_CE_BV_03_C INC PTS issue #12693
- mcaptest -C 4099 -D 4101 -c <PTS addr>
-TC_MCAP_CE_BV_04_C INC PTS issue #12693
- mcaptest -C 4099 -D 4101 -d
+TC_MCAP_CE_BV_01_C PASS mcaptest -C 4099 -D 4101 -f 2 -dc <PTS addr>
+TC_MCAP_CE_BV_02_C PASS mcaptest -C 4099 -D 4101 -f 2
+TC_MCAP_CE_BV_03_C PASS mcaptest -C 4099 -D 4101 -f 2 -c <PTS addr>
+TC_MCAP_CE_BV_04_C PASS mcaptest -C 4099 -D 4101 -f 2 -d
TC_MCAP_CM_ABT_BV_01_C N/A
-TC_MCAP_CM_ABT_BV_02_C INC PTS issue #12693
- mcaptest -C 4099 -D 4101
+TC_MCAP_CM_ABT_BV_02_C PASS mcaptest -C 4099 -D 4101 -f 2
TC_MCAP_CM_ABT_BV_03_C N/A
TC_MCAP_CM_DEL_BV_01_C N/A
-TC_MCAP_CM_DEL_BV_02_C INC PTS issue #12693
- mcaptest -C 4099 -D 4101
+TC_MCAP_CM_DEL_BV_02_C PASS mcaptest -C 4099 -D 4101 -f 2
TC_MCAP_CM_DEL_BV_03_C N/A
-TC_MCAP_CM_DEL_BV_04_C INC PTS issue #12693
- mcaptest -C 4099 -D 4101
-TC_MCAP_CM_DIS_BV_01_C PASS mcaptest -C 4099 -D 4101 -e 2 -f 2
+TC_MCAP_CM_DEL_BV_04_C PASS mcaptest -C 4099 -D 4101 -f 2
+TC_MCAP_CM_DIS_BV_01_C PASS mcaptest -C 4099 -D 4101 -ab -e 2 -f 2
TC_MCAP_CM_DIS_BV_02_C PASS mcaptest -C 4099 -D 4101
-TC_MCAP_CM_DIS_BV_03_C INC PTS issue #12693
- mcaptest -C 4099 -D 4101
-TC_MCAP_CM_DIS_BV_04_C PASS mcaptest -C 4099 -D 4101 -e 2 -f 2
+TC_MCAP_CM_DIS_BV_03_C PASS mcaptest -C 4099 -D 4101 -n -f 2
+TC_MCAP_CM_DIS_BV_04_C PASS mcaptest -C 4099 -D 4101 -ab -e 2 -f 2
TC_MCAP_CM_DIS_BV_05_C PASS mcaptest -C 4099 -D 4101
TC_MCAP_CM_REC_BV_01_C N/A
-TC_MCAP_CM_REC_BV_02_C INC PTS issue #12693
- mcaptest -C 4099 -D 4101
+TC_MCAP_CM_REC_BV_02_C PASS mcaptest -C 4099 -D 4101 -n -f 2
TC_MCAP_CM_REC_BV_03_C N/A
-TC_MCAP_CM_REC_BV_04_C INC PTS issue #12693
- mcaptest -C 4099 -D 4101
- Note: It may be necessary to confirm pairing
- twice (second time when IUT is back in
- range).
+TC_MCAP_CM_REC_BV_04_C PASS mcaptest -C 4099 -D 4101 -n -f 2
TC_MCAP_CM_REC_BV_05_C N/A
-TC_MCAP_CM_REC_BV_06_C INC PTS issue #12693
- mcaptest -C 4099 -D 4101
+TC_MCAP_CM_REC_BV_06_C PASS mcaptest -C 4099 -D 4101 -f 2
TC_MCAP_CS_ERR_BI_01_C N/A
TC_MCAP_CS_ERR_BI_02_C N/A
TC_MCAP_CS_ERR_BI_03_C N/A
TC_MCAP_CS_R_BV_02_I N/A
TC_MCAP_CS_R_BV_03_C N/A
TC_MCAP_CS_T_BV_04_C N/A
-TC_MCAP_ERR_BI_01_C INC PTS issue #12693
- mcaptest -C 4099 -D 4101
-TC_MCAP_ERR_BI_02_C INC PTS issue #12693
- mcaptest -C 4099 -D 4101 -d
-TC_MCAP_ERR_BI_03_C INC PTS issue #12693
- mcaptest -C 4099 -D 4101
-TC_MCAP_ERR_BI_04_C INC PTS issue #12693
- mcaptest -C 4099 -D 4101 -d
-TC_MCAP_ERR_BI_05_C INC PTS issue #12693
- mcaptest -C 4099 -D 4101
-TC_MCAP_ERR_BI_06_C INC PTS issue #12693
- mcaptest -C 4099 -D 4101 -d
-TC_MCAP_ERR_BI_07_C INC PTS issue #12693
- mcaptest -C 4099 -D 4101
-TC_MCAP_ERR_BI_08_C INC PTS issue #12693
- mcaptest -C 4099 -D 4101 -d
-TC_MCAP_ERR_BI_09_C INC PTS issue #12693
- mcaptest -C 4099 -D 4101
-TC_MCAP_ERR_BI_10_C INC PTS issue #12693
- mcaptest -C 4099 -D 4101
-TC_MCAP_ERR_BI_11_C INC PTS issue #12693
- mcaptest -C 4099 -D 4101 -d
-TC_MCAP_ERR_BI_12_C INC PTS issue #12693
- mcaptest -C 4099 -D 4101 -d
-TC_MCAP_ERR_BI_13_C INC PTS issue #12693
- mcaptest -C 4099 -D 4101
-TC_MCAP_ERR_BI_14_C INC PTS issue #12693
- mcaptest -C 4099 -D 4101
-TC_MCAP_ERR_BI_15_C INC PTS issue #12693
- mcaptest -C 4099 -D 4101 -d
-TC_MCAP_ERR_BI_16_C INC PTS issue #12693
- mcaptest -C 4099 -D 4101 -u
-TC_MCAP_ERR_BI_17_C INC PTS issue #12693
- mcaptest -C 4099 -D 4101 -d
-TC_MCAP_ERR_BI_18_C INC PTS issue #12693
- mcaptest -C 4099 -D 4101 -d
+TC_MCAP_ERR_BI_01_C PASS mcaptest -C 4099 -D 4101 -f 2
+TC_MCAP_ERR_BI_02_C PASS mcaptest -C 4099 -D 4101 -dn -f 2
+TC_MCAP_ERR_BI_03_C PASS mcaptest -C 4099 -D 4101 -f 2
+TC_MCAP_ERR_BI_04_C PASS mcaptest -C 4099 -D 4101 -dn -f 2
+TC_MCAP_ERR_BI_05_C PASS mcaptest -C 4099 -D 4101 -f 2
+TC_MCAP_ERR_BI_06_C PASS mcaptest -C 4099 -D 4101 -dn -f 2
+TC_MCAP_ERR_BI_07_C PASS mcaptest -C 4099 -D 4101 -f 2
+TC_MCAP_ERR_BI_08_C PASS mcaptest -C 4099 -D 4101 -dn -f 2
+TC_MCAP_ERR_BI_09_C PASS mcaptest -C 4099 -D 4101 -f 2
+TC_MCAP_ERR_BI_10_C PASS mcaptest -C 4099 -D 4101 -f 2
+TC_MCAP_ERR_BI_11_C PASS mcaptest -C 4099 -D 4101 -dn -f 2
+TC_MCAP_ERR_BI_12_C PASS mcaptest -C 4099 -D 4101 -dn -f 2
+TC_MCAP_ERR_BI_13_C PASS mcaptest -C 4099 -D 4101 -f 2
+TC_MCAP_ERR_BI_14_C PASS mcaptest -C 4099 -D 4101 -f 2
+TC_MCAP_ERR_BI_15_C PASS mcaptest -C 4099 -D 4101 -dn -f 2
+TC_MCAP_ERR_BI_16_C PASS mcaptest -C 4099 -D 4101 -u -f 2
+TC_MCAP_ERR_BI_17_C PASS mcaptest -C 4099 -D 4101 -dn -f 2
+TC_MCAP_ERR_BI_18_C PASS mcaptest -C 4099 -D 4101 -dn -f 2
TC_MCAP_ERR_BI_19_C N/A
-TC_MCAP_ERR_BI_20_C INC PTS issue #12693
- mcaptest -C 4099 -D 4101 -g
+TC_MCAP_ERR_BI_20_C PASS mcaptest -C 4099 -D 4101 -g -f 2
TC_MCAP_INV_BI_01_C PASS mcaptest -C 4099 -D 4101 -dc <PTS addr>
-TC_MCAP_INV_BI_02_C INC PTS issue #12693
- mcaptest -C 4099 -D 4101 -d
-TC_MCAP_INV_BI_03_C INC PTS issue #12693
- mcaptest -C 4099 -D 4101 -dc <PTS addr>
-TC_MCAP_INV_BI_04_C INC PTS issue #12693
- mcaptest -C 4099 -D 4101 -c <PTS addr>
-TC_MCAP_INV_BI_05_C INC PTS issue #12693
- mcaptest -C 4099 -D 4101
-TC_MCAP_INV_BI_06_C INC PTS issue #12693
- mcaptest -C 4099 -D 4101
-TC_MCAP_INV_BI_07_C INC PTS issue #12693
- mcaptest -C 4099 -D 4101
+TC_MCAP_INV_BI_02_C PASS mcaptest -C 4099 -D 4101 -dn -f 2
+TC_MCAP_INV_BI_03_C PASS mcaptest -C 4099 -D 4101 -d -f 2 -c <PTS addr>
+TC_MCAP_INV_BI_04_C PASS mcaptest -C 4099 -D 4101 -f 2 -c <PTS addr>
+TC_MCAP_INV_BI_05_C PASS mcaptest -C 4099 -D 4101 -f 2
+TC_MCAP_INV_BI_06_C PASS mcaptest -C 4099 -D 4101 -f 2
+TC_MCAP_INV_BI_07_C PASS mcaptest -C 4099 -D 4101 -f 2
-------------------------------------------------------------------------------
NONE test result is none
Note: Do not use AOSP Music player. Use e.g. MortPlayer or Poweramp.
-For full tests conformance, "Touch sounds" on IUT should be disbaled
+For full tests conformance, "Touch sounds" on IUT should be disabled
(Settings > Sound¬ification > Other sounds)
-------------------------------------------------------------------------------
Test Name Result Notes
-------------------------------------------------------------------------------
TC_AG_PSE_HFPB_CTH_SD_BV_01_I PASS
-TC_AG_SRC_HFAV_ACT_SD_BV_01_I INC JIRA issue #BA-295
+TC_AG_SRC_HFAV_ACT_SD_BV_01_I PASS PTS issue #13138
TC_AG_SRC_HFAV_ACT_SD_BV_02_I PASS
TC_AG_SRC_HFAV_ACT_SD_BV_03_I PASS
TC_AG_SRC_HFAV_CLH_SD_BV_01_I PASS
TC_AG_SRC_HFAV_CLH_SD_BV_03_I PASS
TC_AG_SRC_HFAV_CLH_SD_BV_04_I PASS
TC_AG_SRC_HFAV_CLH_SD_BV_05_I PASS
-TC_AG_SRC_HFAV_CLH_SD_BV_06_I INC JIRA issue #BA-296
+TC_AG_SRC_HFAV_CLH_SD_BV_06_I PASS PTS issue #13138
TC_AVP_CTH_SD_BI_01_I PASS
TC_AVP_CTH_SD_BI_02_I PASS
TC_HF_SNK_HFAV_ACT_SD_BV_01_I N/A
TC_SRC_TG_HFAV_CLH_MD_BV_01_I PASS
TC_SRC_TG_HFAV_CLH_MD_BV_02_I PASS
TC_SRC_TG_HFAV_CLH_MD_BV_03_I PASS
-TC_SRC_TG_HFAV_CLH_MD_BV_04_I FAIL JIRA issue #BA-294
+TC_SRC_TG_HFAV_CLH_MD_BV_04_I PASS
TC_SRC_TG_HFAV_CLH_MD_BV_05_I PASS
TC_SRC_TG_HFAV_CLH_MD_BV_06_I PASS
TC_PAIRING_HF_SNK_CT N/A Pairing helper for MD tests
PTS test results for OPP
-PTS version: 5.3
-Tested: 17-November-2014
+PTS version: 6.0
+Tested: 30-January-2015
Android version: 5.0
Results:
PTS test results for PAN
-PTS version: 5.3
-Tested: 17-November-2014
+PTS version: 6.0
+Tested: 17-February-2015
Android version: 5.0
Results:
--------------------------------------------------------------------------------
Test Name Result Notes
--------------------------------------------------------------------------------
-TC_BNEP_GN_BROADCAST_0_BV_03_C N/A
-TC_GN_Ipv4_Autonet_BV_01_I N/A
-TC_GN_Ipv6_Autonet_BV_02_I N/A
-TC_GN_IP_DHCP_BV_03_I N/A
-TC_GN_IP_LLMNR_BV_01_I N/A
-TC_GN_IP_LLMNR_BV_02_I N/A
-TC_GN_IP_DNS_BV_01_I N/A
-TC_GN_IP_APP_BV_01_I N/A
-TC_GN_IP_APP_BV_02_I N/A
-TC_GN_IP_APP_BV_03_I N/A
-TC_GN_IP_APP_BV_04_I N/A
-TC_GN_IP_APP_BV_05_I N/A
-TC_SDP_GN_BV_02_C N/A
-TC_MISC_GN_UUID_BV_01_C N/A
-TC_MISC_GN_UUID_BV_02_C N/A
-TC_BNEP_NAP_BROADCAST_0_BV_01_C N/A
-TC_BNEP_NAP_BROADCAST_0_BV_02_C N/A
-TC_BNEP_NAP_FORWARD_UNICAST_BV_05_C N/A
-TC_BNEP_NAP_FORWARD_UNICAST_BV_06_C N/A
-TC_BNEP_NAP_MULTICAST_0_BV_03_C N/A
-TC_BNEP_NAP_MULTICAST_0_BV_04_C N/A
-TC_BNEP_BRIDGE_RX_BV_02_I INC PTS issue #12464
-TC_BNEP_BRIDGE_TX_BV_01_I PASS
-TC_NAP_Ipv4_Autonet_BV_01_I N/A
-TC_NAP_Ipv6_Autonet_BV_02_I N/A
-TC_NAP_IP_DHCP_BV_03_I N/A
-TC_NAP_IP_LLMNR_BV_01_I N/A
-TC_NAP_IP_LLMNR_BV_02_I N/A
-TC_NAP_IP_DNS_BV_01_I N/A
-TC_NAP_IP_APP_BV_01_I N/A
-TC_NAP_IP_APP_BV_02_I N/A
-TC_NAP_IP_APP_BV_03_I N/A
-TC_NAP_IP_APP_BV_04_I N/A
-TC_NAP_IP_APP_BV_05_I N/A
-TC_SDP_NAP_BV_01_C PASS
-TC_MISC_NAP_UUID_BV_01_C PASS
-TC_MISC_NAP_UUID_BV_02_C PASS
-TC_BNEP_PANU_BROADCAST_0_BV_04_C N/A
-TC_PANU_Ipv4_Autonet_BV_01_I PASS After DHCP request fail send
- DHCP address request.
- From IUT: send address request
- dhcpcd -r 169.254.x.x bt-pan
-TC_PANU_Ipv6_Autonet_BV_02_I N/A
-TC_PANU_IP_LLMNR_BV_01_I N/A SE #3558, TSE #4382, TCW #448
-TC_PANU_IP_LLMNR_BV_02_I N/A
-TC_PANU_IP_DHCP_BV_03_I N/A
-TC_PANU_IP_DNS_BV_01_I N/A
-TC_PANU_IP_APP_BV_01_I N/A
-TC_PANU_IP_APP_BV_02_I N/A
-TC_PANU_IP_APP_BV_03_I N/A
-TC_PANU_IP_APP_BV_04_I N/A
-TC_PANU_IP_APP_BV_05_I PASS
-TC_SDP_PANU_BV_01_C N/A
-TC_MISC_PANU_UUID_BV_01_C N/A
-TC_MISC_PANU_UUID_BV_02_C N/A
-TC_MISC_ROLE_BV_01_C N/A
-TC_MISC_ROLE_BV_BV_02_C N/A
+TC_BNEP_BROADCAST_0_BV_01_C N/A
+TC_BNEP_BROADCAST_0_BV_02_C N/A
+TC_BNEP_MULTICAST_0_BV_03_C N/A
+TC_BNEP_MULTICAST_0_BV_04_C N/A
+TC_BNEP_FORWARD_UNICAST_BV_05_C N/A
+TC_BNEP_FORWARD_UNICAST_BV_06_C N/A
+TC_BNEP_EXTENSION_0_BV_07_C_TESTER_1 N/A
+TC_BNEP_EXTENSION_0_BV_07_C_TESTER_2 N/A
+TC_BNEP_FORWARD_BV_08_C_TESTER_1 N/A
+TC_BNEP_FORWARD_BV_08_C_TESTER_2 N/A
+TC_BNEP_FORWARD_BROADCAST_BV_09_C_TESTER_1 N/A
+TC_BNEP_FORWARD_BROADCAST_BV_09_C_TESTER_2 N/A
+TC_BNEP_FORWARD_BROADCAST_BV_09_C_TESTER_3 N/A
+TC_BNEP_FILTER_BV_10_C_TESTER_1 N/A
+TC_BNEP_FILTER_BV_10_C_TESTER_2 N/A
+TC_BNEP_FILTER_BV_11_C_TESTER_1 N/A
+TC_BNEP_FILTER_BV_11_C_TESTER_2 N/A
+TC_BNEP_FILTER_BV_12_C_TESTER_1 N/A
+TC_BNEP_FILTER_BV_12_C_TESTER_2 N/A
+TC_BNEP_FILTER_BV_13_C_TESTER_1 N/A
+TC_BNEP_FILTER_BV_13_C_TESTER_2 N/A
+TC_BNEP_FILTER_BV_14_C_TESTER_1 N/A
+TC_BNEP_FILTER_BV_14_C_TESTER_2 N/A
+TC_BNEP_FILTER_BV_15_C_TESTER_1 N/A
+TC_BNEP_FILTER_BV_15_C_TESTER_2 N/A
+TC_BRIDGE_TX_BV_01_I PASS
+TC_BRIDGE_RX_BV_02_I PASS
+ To initiate general ethernet
+ use for e.g. ping.
+TC_IPv4_AUTONET_BV_01_I PASS
+ To initiate general ethernet
+ use for e.g. ping.
+TC_IPv6_AUTONET_BV_02_I N/A
+TC_IP_DHCP_BV_03_I PASS
+TC_IP_LLMNR_BV_01_I N/A
+TC_IP_LLMNR_BV_02_I N/A
+TC_IP_DNS_BV_01_I N/A
+TC_IP_APP_BV_03_I ip neighbour add <PTS IP addr>
+ lladdr <PTS HW addr>
+ dev bt-pan
+ ping -c 1 <PTS IP addr>
+ Note: Add ARP record if bt-pan
+ connection is
+ established
+TC_IP_APP_BV_05_I ip neighbour add <PTS IP addr>
+ lladdr <PTS HW addr>
+ dev bt-pan
+ Note: Add of ARP record should
+ be done immediately
+ after establish of
+ bt-pan, because PTS
+ treat other than ICMP
+ frames as error.
+TC_MISC_ROLE_BV_01_C N/A
+TC_MISC_ROLE_BV_02_C N/A
+TC_MISC_UUID_BV_01_C PASS
+TC_MISC_UUID_BV_02_C PASS
+TC_SDP_NAP_BV_01_C PASS
+TC_SDP_GN_BV_01_C N/A
+TC_SDP_PANU_BV_01_C PASS
--------------------------------------------------------------------------------
PTS test results for PBAP
-PTS version: 5.3
-Tested: 17-November-2014
+PTS version: 6.0
+Tested: 30-January-2015
Android version: 5.0
Results:
PTS test results for RFCOMM
-PTS version: 5.3
-Tested: 19-November-2014
+PTS version: 6.0
+Tested: 02-February-2015
Android version: 5.0
Kernel version: 3.19
TC_RFC_BV_01_C PASS rctest -n -P 1 <btaddr>
TC_RFC_BV_02_C PASS rctest -r -P 1
TC_RFC_BV_03_C PASS rctest -r -P 1
-TC_RFC_BV_04_C PASS Note: use ETS provided in PTS issue #12414
- rctest -r -P 1
+TC_RFC_BV_04_C PASS rctest -r -P 1
TC_RFC_BV_05_C PASS rctest -n -P 4 <btaddr>
Note: test requires IUT to connect on the given
channel. sdptool browse <btaddr> to check the
channel.
TC_RFC_BV_06_C PASS rctest -r -P 1
TC_RFC_BV_07_C PASS rctest -r -P 1
-TC_RFC_BV_08_C PASS Note: use ETS provided in PTS issue #12397
- rctest -r -P 1
+TC_RFC_BV_08_C PASS rctest -r -P 1
TC_RFC_BV_11_C PASS rctest -r -P 1
-TC_RFC_BV_13_C PASS Note: use ETS provided in PTS issue #12397
- rctest -r -P 1
+TC_RFC_BV_13_C PASS rctest -r -P 1
TC_RFC_BV_14_C N/A
TC_RFC_BV_15_C PASS rctest -r -P 1
TC_RFC_BV_17_C PASS rctest -d -P 1
TC_RFC_BV_19_C PASS
-TC_RFC_BV_21_C INC PTS issue #12421
-TC_RFC_BV_22_C INC PTS issue #12421
+TC_RFC_BV_21_C PASS PTS issue #13011
+ rctest -w -N 10 -P 1
+TC_RFC_BV_22_C PASS PTS issue #13011
+ rctest -w -N 10 -P 1
TC_RFC_BV_25_C PASS rctest -r -P 1
-------------------------------------------------------------------------------
PTS test results for ScPP
-PTS version: 5.3
-Tested: 25-November-2014
+PTS version: 6.0
+Tested: 02-February-2015
Android version: 5.0
Results:
-------------------------------------------------------------------------------
Test Name Result Notes
-------------------------------------------------------------------------------
-TC_SPDS_SC_BV_01_I PASS PTS issue #12119
-TC_SPDC_SC_BV_01_I PASS PTS issue #12119
-TC_SPDC_SC_BV_02_I PASS PTS issue #12119
-TC_SPDC_SC_BV_03_I PASS PTS issue #12119
-TC_SPWF_SC_BV_01_I PASS PTS issue #12119
-TC_SPCF_SC_BV_01_I PASS PTS issue #12119
-TC_SPNF_SC_BV_01_I PASS PTS issue #12119
+TC_SPDS_SC_BV_01_I PASS
+TC_SPDC_SC_BV_01_I PASS
+TC_SPDC_SC_BV_02_I PASS
+TC_SPDC_SC_BV_03_I PASS
+TC_SPWF_SC_BV_01_I PASS
+TC_SPCF_SC_BV_01_I PASS
+TC_SPNF_SC_BV_01_I PASS
-------------------------------------------------------------------------------
--- /dev/null
+PTS test results for SDP
+
+PTS version: 6.0
+Tested: 23-February-2015
+Android version: 5.0
+
+Results:
+PASS test passed
+FAIL test failed
+INC test is inconclusive
+N/A test is disabled due to PICS setup
+NONE test result is none
+
+Note: from haltest:
+bluetooth set_adapter_property BT_PROPERTY_ADAPTER_SCAN_MODE
+ BT_SCAN_MODE_CONNECTABLE
+bluetooth enable
+socket listen BTSOCK_L2CAP BlueZ 0
+
+-------------------------------------------------------------------------------
+Test Name Result Notes
+-------------------------------------------------------------------------------
+TC_SERVER_BRW_BV_01_C PASS
+TC_SERVER_BRW_BV_01_C PASS
+TC_SERVER_SA_BI_01_C PASS
+TC_SERVER_SA_BI_02_C PASS
+TC_SERVER_SA_BI_03_C PASS
+TC_SERVER_SA_BV_01_C PASS
+TC_SERVER_SA_BV_03_C PASS
+TC_SERVER_SA_BV_04_C PASS
+TC_SERVER_SA_BV_05_C PASS
+TC_SERVER_SA_BV_06_C PASS
+TC_SERVER_SA_BV_07_C PASS
+TC_SERVER_SA_BV_08_C PASS
+TC_SERVER_SA_BV_09_C PASS
+TC_SERVER_SA_BV_10_C PASS
+TC_SERVER_SA_BV_11_C PASS
+TC_SERVER_SA_BV_12_C PASS
+TC_SERVER_SA_BV_13_C PASS
+TC_SERVER_SA_BV_14_C PASS
+TC_SERVER_SA_BV_15_C PASS
+TC_SERVER_SA_BV_16_C PASS
+TC_SERVER_SA_BV_17_C PASS
+TC_SERVER_SA_BV_18_C PASS
+TC_SERVER_SA_BV_19_C PASS
+TC_SERVER_SA_BV_20_C PASS
+TC_SERVER_SA_BV_21_C PASS
+TC_SERVER_SS_BI_01_C PASS
+TC_SERVER_SS_BI_02_C PASS
+TC_SERVER_SS_BV_01_C PASS
+TC_SERVER_SS_BV_03_C PASS PTS issue #12840
+TC_SERVER_SS_BV_04_C PASS
+TC_SERVER_SSA_BI_01_C PASS
+TC_SERVER_SSA_BI_02_C PASS
+TC_SERVER_SSA_BV_01_C PASS
+TC_SERVER_SSA_BV_02_C PASS
+TC_SERVER_SSA_BV_03_C PASS
+TC_SERVER_SSA_BV_04_C PASS
+TC_SERVER_SSA_BV_06_C PASS
+TC_SERVER_SSA_BV_07_C PASS
+TC_SERVER_SSA_BV_08_C PASS
+TC_SERVER_SSA_BV_09_C PASS
+TC_SERVER_SSA_BV_10_C PASS
+TC_SERVER_SSA_BV_11_C PASS
+TC_SERVER_SSA_BV_12_C PASS
+TC_SERVER_SSA_BV_13_C PASS
+TC_SERVER_SSA_BV_14_C PASS
+TC_SERVER_SSA_BV_15_C PASS
+TC_SERVER_SSA_BV_16_C PASS
+TC_SERVER_SSA_BV_17_C PASS
+TC_SERVER_SSA_BV_18_C PASS
+TC_SERVER_SSA_BV_19_C PASS
+TC_SERVER_SSA_BV_20_C PASS
+TC_SERVER_SSA_BV_21_C PASS
+TC_SERVER_SSA_BV_22_C PASS
+TC_SERVER_SSA_BV_23_C PASS
+-------------------------------------------------------------------------------
PTS test results for SM
-PTS version: 5.3
-Tested: 28-November-2014
+PTS version: 6.0
+Tested: 11-February-2015
Android version: 5.0
-kernel version: 3.18
+kernel version: 3.20
Results:
PASS test passed
btmgmt pair -c 0x03 -t 0x01 <addr>
TC_JW_BV_01_C PASS btmgmt pairable off
btmgmt pair -c 0x03 -t 0x01 <addr>
-TC_JW_BV_02_C PASS
+TC_JW_BV_02_C PASS btmgmt advertising on
TC_JW_BV_05_C PASS btmgmt pair -c 0x03 -t 0x01 <addr>
-TC_JW_BI_01_C PASS
-TC_JW_BI_02_C PASS
+TC_JW_BI_01_C PASS btmgmt pair -c 0x03 -t 0x01 <addr>
+TC_JW_BI_02_C PASS btmgmt pairable on
TC_JW_BI_03_C PASS bluetoothd is NOT running
btmgmt power on
btmgmt le on
TC_OOB_BV_02_C N/A
TC_OOB_BV_03_C N/A
TC_OOB_BV_04_C N/A
-TC_OOB_BV_05_C PASS
-TC_OOB_BV_06_C PASS
-TC_OOB_BV_07_C PASS
-TC_OOB_BV_08_C PASS
+TC_OOB_BV_05_C PASS btmgmt pair -c 0x04 -t 0x01 <addr>
+ Note: Enter valid passkey in PTS
+TC_OOB_BV_06_C PASS btmgmt advertising on
+ btmgmt monitor
+ Note: Enter valid passkey in PTS
+TC_OOB_BV_07_C PASS btmgmt pair -c 0x04 -t 0x01 <addr>
+TC_OOB_BV_08_C PASS btmgmt advertising on
+ btmgmt monitor
+ Note: Accept pairing in btmgmt monitor
TC_OOB_BV_09_C N/A
TC_OOB_BV_10_C N/A
TC_OOB_BI_01_C N/A
TC_OOB_BI_02_C N/A
-TC_EKS_BV_01_C PASS
-TC_EKS_BV_02_C PASS
+TC_EKS_BV_01_C PASS btmgmt pair -c 0x04 -t 0x01 <addr>
+ Note: Enter valid passkey in PTS
+TC_EKS_BV_02_C PASS btmgmt advertising on
+ btmgmt monitor
+ Note: Accept pairing in btmgmt monitor
TC_EKS_BI_01_C PASS btmgmt io-cap 0x03
-TC_EKS_BI_02_C PASS
+ btmgmt pair -c 0x03 -t 0x01 <addr>
+TC_EKS_BI_02_C PASS btmgmt advertising on
TC_SIGN_BV_01_C INC PTS issue #12305
-TC_SIGN_BV_03_C PASS haltest
- gattc listen
-
-TC_SIGN_BI_01_C PASS haltest
- gattc listen
+TC_SIGN_BV_03_C PASS haltest:
+ gattc register_client 1234
+ gattc listen 1 1
+ Note: IUT must be connectable and discoverable
+TC_SIGN_BI_01_C PASS haltest:
+ gattc register client 1234
+ gattc listen 1 1
+ Note: IUT must be connectable and discoverable
TC_KDU_BV_01_C PASS btmgmt pairable on
+ btmgmt advertising on
+ btmgmt connectable on
TC_KDU_BV_02_C PASS PTS issue #12302
Note: Can pass it with following instructions:
btmgmt privacy on
Set PIXIT TSPX_peer_type to 01
TC_KDU_BV_06_C PASS btmgmt pair -c 0x03 -t 0x01 <addr>
TC_KDU_BV_07_C PASS btmgmt pairable on
-TC_SIP_BV_01_C PASS btmgmt pair -c 0x03 -t 0x01 <addr>
-TC_SIP_BV_02_C PASS l2test -n -J4 -V le_public <addr>
-TC_SIE_BV_01_C PASS btmgmt pair -c 0x03 -t 0x01 <addr>
+TC_SIP_BV_01_C PASS btmgmt advertising on
+ btmgmt pair -c 0x03 -t 0x01 <addr>
+TC_SIP_BV_02_C PASS btmgmt advertising off
+ l2test -n -J4 -V le_public <addr>
+TC_SIE_BV_01_C PASS btmgmt advertising on
+ btmgmt pair -c 0x03 -t 0x01 <addr>
-------------------------------------------------------------------------------
PTS test results for SPP
-PTS version: 5.3
-Tested: 12-November-2014
+PTS version: 6.0
+Tested: 29-January-2015
Android version: 5.0
Results:
-------------------------------------------------------------------------------
Test Name Result Notes
-------------------------------------------------------------------------------
-TC_DevA_APP_BV_01_C PASS rctest -n -P <channel> <bdaddr>
- Note: IUT should sdp query PTS in order to
- check given channel for COM5
-TC_DevB_APP_BV_02_C PASS haltest: socket listen BTSOCK_RFCOMM
- <srvc_name> <uuid>
+TC_DevA_APP_BV_01_C PASS haltest: socket connect <PTS addr>
+ BTSOCK_RFCOMM 00001101 0
+TC_DevB_APP_BV_02_C PASS haltest: socket listen BTSOCK_RFCOMM SerialPort
+ 00001101
+ Note: IUT must be in connectable, discoverable
+ mode.
TC_APP_BV_03_C N/A Missing in PTS
PTS issue #12388
Note: tests BV_03 : BV_6 currently not supported
#include "src/log.h"
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
#include "lib/uuid.h"
+
#include "src/shared/util.h"
#include "src/shared/queue.h"
static struct rfcomm_channel servers[RFCOMM_CHANNEL_MAX + 1];
+static uint32_t test_sdp_record_uuid16 = 0;
+static uint32_t test_sdp_record_uuid32 = 0;
+static uint32_t test_sdp_record_uuid128 = 0;
+
static int rfsock_set_buffer(struct rfcomm_sock *rfsock)
{
socklen_t len = sizeof(int);
return HAL_STATUS_FAILED;
}
+static uint32_t add_test_record(uuid_t *uuid)
+{
+ sdp_record_t *record;
+ sdp_list_t *svclass_id;
+ sdp_list_t *seq, *pbg_seq, *proto_seq, *ap_seq;
+ sdp_list_t *proto, *proto1, *aproto;
+ uuid_t l2cap_uuid, pbg_uuid, ap_uuid;
+
+ record = sdp_record_alloc();
+ if (!record)
+ return 0;
+
+ record->handle = sdp_next_handle();
+
+ svclass_id = sdp_list_append(NULL, uuid);
+ sdp_set_service_classes(record, svclass_id);
+
+ sdp_uuid16_create(&l2cap_uuid, L2CAP_UUID);
+ proto = sdp_list_append(NULL, &l2cap_uuid);
+ seq = sdp_list_append(NULL, proto);
+
+ proto_seq = sdp_list_append(NULL, seq);
+ sdp_set_access_protos(record, proto_seq);
+
+ sdp_uuid16_create(&pbg_uuid, PUBLIC_BROWSE_GROUP);
+ pbg_seq = sdp_list_append(NULL, &pbg_uuid);
+ sdp_set_browse_groups(record, pbg_seq);
+
+ /* Additional Protocol Descriptor List */
+ sdp_uuid16_create(&ap_uuid, L2CAP_UUID);
+ proto1 = sdp_list_append(NULL, &ap_uuid);
+ ap_seq = sdp_list_append(NULL, proto1);
+ aproto = sdp_list_append(NULL, ap_seq);
+ sdp_set_add_access_protos(record, aproto);
+
+ sdp_set_service_id(record, *uuid);
+ sdp_set_record_state(record, 0);
+ sdp_set_service_ttl(record, 0);
+ sdp_set_service_avail(record, 0);
+ sdp_set_url_attr(record, "http://www.bluez.org",
+ "http://www.bluez.org", "http://www.bluez.org");
+
+ sdp_list_free(proto, NULL);
+ sdp_list_free(seq, NULL);
+ sdp_list_free(proto_seq, NULL);
+ sdp_list_free(pbg_seq, NULL);
+ sdp_list_free(svclass_id, NULL);
+
+ if (bt_adapter_add_record(record, 0) < 0) {
+ sdp_record_free(record);
+ return 0;
+ }
+
+ return record->handle;
+}
+
+static void test_sdp_cleanup(void)
+{
+ if (test_sdp_record_uuid16) {
+ bt_adapter_remove_record(test_sdp_record_uuid16);
+ test_sdp_record_uuid16 = 0;
+ }
+
+ if (test_sdp_record_uuid32) {
+ bt_adapter_remove_record(test_sdp_record_uuid32);
+ test_sdp_record_uuid32 = 0;
+ }
+
+ if (test_sdp_record_uuid128) {
+ bt_adapter_remove_record(test_sdp_record_uuid128);
+ test_sdp_record_uuid128 = 0;
+ }
+}
+
+static void test_sdp_init(void)
+{
+ char uuid128[] = {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+ uuid_t u;
+
+ sdp_uuid16_create(&u, 0xffff);
+ test_sdp_record_uuid16 = add_test_record(&u);
+
+ sdp_uuid32_create(&u, 0xffffffff);
+ test_sdp_record_uuid32 = add_test_record(&u);
+
+ sdp_uuid128_create(&u, uuid128);
+ test_sdp_record_uuid128 = add_test_record(&u);
+}
+
+static uint8_t l2cap_listen(int chan, const uint8_t *name, const uint8_t *uuid,
+ uint8_t flags, int *hal_sock)
+{
+ /* TODO be more strict here? */
+ if (strcmp("BlueZ", (const char *) name)) {
+ error("socket: Only SDP test supported on L2CAP");
+ return HAL_STATUS_UNSUPPORTED;
+ }
+
+ test_sdp_cleanup();
+ test_sdp_init();
+
+ *hal_sock = -1;
+
+ return HAL_STATUS_SUCCESS;
+}
+
static void handle_listen(const void *buf, uint16_t len)
{
const struct hal_cmd_socket_listen *cmd = buf;
status = rfcomm_listen(cmd->channel, cmd->name, cmd->uuid,
cmd->flags, &hal_sock);
break;
- case HAL_SOCK_SCO:
case HAL_SOCK_L2CAP:
+ status = l2cap_listen(cmd->channel, cmd->name, cmd->uuid,
+ cmd->flags, &hal_sock);
+ break;
+ case HAL_SOCK_SCO:
status = HAL_STATUS_UNSUPPORTED;
break;
default:
DBG("");
+ test_sdp_cleanup();
+
g_list_free_full(connections, cleanup_rfsock);
for (ch = 0; ch <= RFCOMM_CHANNEL_MAX; ch++)
#include <signal.h>
#include <string.h>
#include <libgen.h>
-#include <sys/poll.h>
+#include <poll.h>
#include <sys/wait.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/stat.h>
-#include "monitor/mainloop.h"
+#ifndef WAIT_ANY
+#define WAIT_ANY (-1)
+#endif
+
+#include "src/shared/mainloop.h"
static char exec_dir[PATH_MAX];
#define ATT_HANDLE_SIZE 2
+#define L2CAP_ATT_ERROR 0x01
#define L2CAP_ATT_EXCHANGE_MTU_REQ 0x02
#define L2CAP_ATT_EXCHANGE_MTU_RSP 0x03
+#define L2CAP_ATT_FIND_BY_TYPE_REQ 0x06
#define L2CAP_ATT_READ_REQ 0x0a
#define L2CAP_ATT_READ_RSP 0x0b
#define L2CAP_ATT_WRITE_REQ 0x12
#define GATT_STATUS_FAILURE 0x00000101
#define GATT_STATUS_INS_AUTH 0x08
+#define GATT_ERR_INVAL_ATTR_VALUE_LEN 0x0D
+
#define GATT_SERVER_DISCONNECTED 0
#define GATT_SERVER_CONNECTED 1
static struct queue *list; /* List of gatt test cases */
-static int srvc1_handle;
-static int inc_srvc1_handle;
-static int char1_handle;
+static uint16_t srvc1_handle;
+static uint16_t inc_srvc1_handle;
+static uint16_t char1_handle;
+
+static struct iovec char1_handle_v = {
+ .iov_base = &char1_handle,
+ .iov_len = sizeof(char1_handle),
+};
struct set_att_data {
char *to;
};
struct att_write_req_data {
- int *attr_handle;
+ uint16_t *attr_handle;
uint8_t *value;
};
static uint8_t value_1[] = {0x01};
static uint8_t att_write_req_value_1[] = {0x00, 0x01, 0x02, 0x03};
+static struct iovec att_write_req_value_1_v = {
+ .iov_base = att_write_req_value_1,
+ .iov_len = sizeof(att_write_req_value_1),
+};
struct gatt_connect_data {
const int app_id;
struct add_included_service_data {
int app_id;
- int *inc_srvc_handle;
- int *srvc_handle;
+ uint16_t *inc_srvc_handle;
+ uint16_t *srvc_handle;
};
struct add_char_data {
int app_id;
- int *srvc_handle;
+ uint16_t *srvc_handle;
bt_uuid_t *uuid;
int properties;
int permissions;
struct add_desc_data {
int app_id;
- int *srvc_handle;
+ uint16_t *srvc_handle;
bt_uuid_t *uuid;
int permissions;
};
struct start_srvc_data {
int app_id;
- int *srvc_handle;
+ uint16_t *srvc_handle;
int transport;
};
struct stop_srvc_data {
int app_id;
- int *srvc_handle;
+ uint16_t *srvc_handle;
};
struct delete_srvc_data {
int app_id;
- int *srvc_handle;
+ uint16_t *srvc_handle;
};
struct send_indication_data {
int app_id;
- int *attr_handle;
+ uint16_t *attr_handle;
int conn_id;
int len;
int confirm;
&emu_remote_ble_device_type },
};
+static struct bt_action_data prop_test_remote_ble_bdaddr_req = {
+ .addr = &emu_remote_bdaddr_val,
+ .prop_type = BT_PROPERTY_BDADDR,
+ .prop = &prop_emu_remotes_default_set[0],
+};
+
static bt_scan_mode_t setprop_scan_mode_conn_val =
BT_SCAN_MODE_CONNECTABLE_DISCOVERABLE;
.num_handles = 1
};
-static int srvc_bad_handle = -1;
+static uint16_t srvc_bad_handle = 0xffff;
static struct add_included_service_data add_inc_service_data_1 = {
.app_id = APP1_ID,
.srvc_handle = &srvc_bad_handle
};
-static int srvc_indication_handle_1 = 0x01;
+static uint16_t srvc_indication_handle_1 = 0x01;
static struct send_indication_data send_indication_data_1 = {
.app_id = APP1_ID,
.response = &response_2,
};
+static struct send_resp_data send_resp_data_2_error = {
+ .conn_id = CONN1_ID,
+ .trans_id = TRANS1_ID,
+ .status = GATT_ERR_INVAL_ATTR_VALUE_LEN,
+ .response = &response_2,
+};
+
#define SEARCH_SERVICE_SINGLE_SUCCESS_PDUS \
raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28), \
raw_pdu(0x11, 0x06, 0x01, 0x00, 0x10, 0x00, 0x00, 0x18), \
end_pdu
};
+static struct iovec search_service_4[] = {
+ raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28),
+ raw_pdu(0x11, 0x06, 0x01, 0x00, 0x00, 0x00, 0x00, 0x18),
+ end_pdu
+};
+
static struct iovec get_characteristic_1[] = {
SEARCH_SERVICE_SINGLE_SUCCESS_PDUS,
READ_BY_TYPE_SINGLE_CHARACTERISTIC_PDUS,
end_pdu
};
+static struct iovec get_characteristic_2[] = {
+ SEARCH_SERVICE_SINGLE_SUCCESS_PDUS,
+ raw_pdu(0x08, 0x01, 0x00, 0x10, 0x00, 0x03, 0x28),
+ raw_pdu(0x09, 0x07, 0x00, 0x00, 0x04, 0x00, 0x00, 0x19, 0x00),
+ end_pdu
+};
+
+static struct iovec get_descriptor_0[] = {
+ SEARCH_SERVICE_SINGLE_SUCCESS_PDUS,
+ READ_BY_TYPE_SINGLE_CHARACTERISTIC_PDUS,
+ raw_pdu(0x04, 0x01, 0x00, 0x10, 0x00),
+ raw_pdu(0x05, 0x01, 0x00, 0x00, 0x00, 0x29),
+ end_pdu
+};
+
static struct iovec get_descriptor_1[] = {
SEARCH_SERVICE_SINGLE_SUCCESS_PDUS,
READ_BY_TYPE_SINGLE_CHARACTERISTIC_PDUS,
end_pdu
};
+static struct iovec get_included_0[] = {
+ SEARCH_SERVICE_SINGLE_SUCCESS_PDUS,
+ raw_pdu(0x08, 0x01, 0x00, 0x10, 0x00, 0x02, 0x28),
+ raw_pdu(0x09, 0x08, 0x00, 0x00, 0x15, 0x00, 0x19, 0x00, 0xff, 0xfe),
+ end_pdu
+};
+
static struct iovec get_included_1[] = {
SEARCH_SERVICE_SINGLE_SUCCESS_PDUS,
raw_pdu(0x08, 0x01, 0x00, 0x10, 0x00, 0x02, 0x28),
end_pdu
};
-static struct att_write_req_data att_write_req_data_1 = {
- .attr_handle = &char1_handle,
- .value = att_write_req_value_1,
+static struct iovec search_range_1[] = {
+ raw_pdu(0x01, 0xff, 0xff, 0xff),
+ end_pdu
};
-/* att commands define raw pdus */
-static struct iovec att_read_req = raw_pdu(0x0a, 0x00, 0x00);
-static struct iovec att_write_req_1 = raw_pdu(0x12, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00);
-
-static void gatt_att_pdu_modify(void)
-{
- struct test_data *data = tester_get_data();
- struct step *current_data_step = queue_peek_head(data->steps);
- struct iovec *store_pdu = current_data_step->set_data_to;
- struct step *step = g_new0(struct step, 1);
- unsigned char *raw_pdu = store_pdu->iov_base;
- int set_data_len = current_data_step->set_data_len;
-
- switch (raw_pdu[0]) {
- case L2CAP_ATT_READ_REQ: {
- uint16_t handle = *((int *)current_data_step->set_data);
-
- memcpy(raw_pdu + 1, &handle, set_data_len);
- tester_debug("gatt: modify pdu read request handle to 0x%02x",
- handle);
-
- break;
- }
-
- case L2CAP_ATT_WRITE_REQ: {
- struct att_write_req_data *pdu_set_data =
- current_data_step->set_data;
- uint16_t handle = *((int *)(pdu_set_data->attr_handle));
- uint8_t *value = pdu_set_data->value;
-
- memcpy(raw_pdu + 1, &handle, sizeof(handle));
- memcpy(raw_pdu + 3, value, set_data_len - sizeof(handle));
-
- tester_debug("gatt: modify pdu write request handle to 0x%02x",
- handle);
-
- break;
- }
- default:
- tester_debug("modify att pdu with opcode 0x%02x not handled",
- raw_pdu[0]);
+static struct iovec primary_type = raw_pdu(0x00, 0x28);
- break;
- }
+/* att commands define raw pdus */
+static struct iovec att_read_req_op_v = raw_pdu(L2CAP_ATT_READ_REQ);
+static struct iovec att_write_req_op_v = raw_pdu(L2CAP_ATT_WRITE_REQ);
+static struct iovec att_find_by_type_req_op_v =
+ raw_pdu(L2CAP_ATT_FIND_BY_TYPE_REQ);
- schedule_action_verification(step);
-}
+static struct iovec svc_change_ccc_handle_v = raw_pdu(0x1c, 0x00);
+static struct iovec svc_change_ccc_value_v = raw_pdu(0x00, 0x01);
static void gatt_client_register_action(void)
{
tester_debug("Received att pdu with opcode 0x%02x", pdu[0]);
switch (pdu[0]) {
+ case L2CAP_ATT_ERROR:
+ step = g_new0(struct step, 1);
+
+ step->callback = CB_EMU_ATT_ERROR;
+ step->callback_result.error = pdu[4];
+
+ schedule_callback_verification(step);
+ break;
case L2CAP_ATT_EXCHANGE_MTU_REQ:
tester_print("Exchange MTU request received.");
step->callback = CB_EMU_VALUE_INDICATION;
schedule_callback_verification(step);
- break;
+ goto respond;
case L2CAP_ATT_HANDLE_VALUE_NOTIFY:
step = g_new0(struct step, 1);
break;
}
+respond:
if (memcmp(gatt_pdu->iov_base, data, len)) {
tester_print("Incoming data mismatch");
break;
struct bthost *bthost = hciemu_client_get_host(data->hciemu);
struct step *current_data_step = queue_peek_head(data->steps);
struct iovec *pdu = current_data_step->set_data;
+ struct iovec *pdu2 = current_data_step->set_data_2;
+ struct iovec *pdu3 = current_data_step->set_data_3;
struct step *step = g_new0(struct step, 1);
if (cid_data.handle && cid_data.cid) {
- bthost_send_cid_v(bthost, cid_data.handle, cid_data.cid,
- pdu, 1);
+ struct iovec rsp[3];
+ size_t len = 0;
+
+ if (!pdu) {
+ step->action_status = BT_STATUS_FAIL;
+ goto done;
+ }
+
+ rsp[0].iov_base = pdu->iov_base;
+ rsp[0].iov_len = pdu->iov_len;
+ len++;
+
+ if (pdu2) {
+ rsp[1].iov_base = pdu2->iov_base;
+ rsp[1].iov_len = pdu2->iov_len;
+ len++;
+ }
+
+ if (pdu3) {
+ rsp[2].iov_base = pdu3->iov_base;
+ rsp[2].iov_len = pdu3->iov_len;
+ len++;
+ }
+
+ bthost_send_cid_v(bthost, cid_data.handle, cid_data.cid, rsp,
+ len);
step->action_status = BT_STATUS_SUCCESS;
} else {
tester_debug("No connection set up");
step->action_status = BT_STATUS_FAIL;
}
+done:
schedule_action_verification(step);
}
ACTION_SUCCESS(bluetooth_disable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
),
+ TEST_CASE_BREDRLE("Gatt Client - Search Service - Incorrect rsp",
+ ACTION_SUCCESS(init_pdus, search_service_4),
+ ACTION_SUCCESS(bluetooth_enable_action, NULL),
+ CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+ ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
+ ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
+ ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
+ CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
+ ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+ CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
+ ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
+ ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
+ CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS,
+ prop_emu_remotes_default_set,
+ CONN1_ID, APP1_ID),
+ ACTION_SUCCESS(gatt_client_search_services, &search_services_1),
+ CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID),
+ ACTION_SUCCESS(bluetooth_disable_action, NULL),
+ CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+ ),
TEST_CASE_BREDRLE("Gatt Client - Get Characteristic - Single",
ACTION_SUCCESS(init_pdus, get_characteristic_1),
ACTION_SUCCESS(bluetooth_enable_action, NULL),
ACTION_SUCCESS(bluetooth_disable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
),
+ TEST_CASE_BREDRLE("Gatt Client - Get Characteristic - Incorrect rsp",
+ ACTION_SUCCESS(init_pdus, get_characteristic_2),
+ ACTION_SUCCESS(bluetooth_enable_action, NULL),
+ CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+ ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
+ ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
+ ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
+ CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
+ ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+ CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
+ ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
+ ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
+ CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS,
+ prop_emu_remotes_default_set,
+ CONN1_ID, APP1_ID),
+ ACTION_SUCCESS(gatt_client_search_services, &search_services_1),
+ CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID),
+ ACTION_SUCCESS(gatt_client_get_characteristic_action,
+ &get_char_data_1),
+ CALLBACK_GATTC_GET_CHARACTERISTIC_CB(GATT_STATUS_FAILURE,
+ CONN1_ID, &service_1, NULL, 0),
+ ACTION_SUCCESS(bluetooth_disable_action, NULL),
+ CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+ ),
TEST_CASE_BREDRLE("Gatt Client - Get Characteristic - None",
ACTION_SUCCESS(init_pdus, get_characteristic_1),
ACTION_SUCCESS(bluetooth_enable_action, NULL),
ACTION_SUCCESS(bluetooth_disable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
),
+ TEST_CASE_BREDRLE("Gatt Client - Get Descriptor - Incorrect rsp",
+ ACTION_SUCCESS(init_pdus, get_descriptor_0),
+ ACTION_SUCCESS(bluetooth_enable_action, NULL),
+ CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+ ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
+ ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
+ ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
+ CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
+ ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+ CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
+ ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
+ ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
+ CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS,
+ prop_emu_remotes_default_set,
+ CONN1_ID, APP1_ID),
+ ACTION_SUCCESS(gatt_client_search_services, &search_services_1),
+ CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID),
+ ACTION_SUCCESS(gatt_client_get_characteristic_action,
+ &get_char_data_1),
+ CALLBACK_GATTC_GET_CHARACTERISTIC_CB(GATT_STATUS_SUCCESS,
+ CONN1_ID, &service_1, &characteristic_1, 4),
+ ACTION_SUCCESS(gatt_client_get_descriptor_action,
+ &get_desc_data_1),
+ CALLBACK_GATTC_GET_DESCRIPTOR(GATT_STATUS_FAILURE, CONN1_ID,
+ &service_1, &characteristic_1, NULL),
+ ACTION_SUCCESS(bluetooth_disable_action, NULL),
+ CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+ ),
TEST_CASE_BREDRLE("Gatt Client - Get Descriptor - Single",
ACTION_SUCCESS(init_pdus, get_descriptor_1),
ACTION_SUCCESS(bluetooth_enable_action, NULL),
ACTION_SUCCESS(bluetooth_disable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
),
+ TEST_CASE_BREDRLE("Gatt Client - Get Included Services - Incorrect rsp",
+ ACTION_SUCCESS(init_pdus, get_included_0),
+ ACTION_SUCCESS(bluetooth_enable_action, NULL),
+ CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+ ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
+ ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
+ ACTION_SUCCESS(gatt_client_register_action, &app1_uuid),
+ CALLBACK_STATUS(CB_GATTC_REGISTER_CLIENT, BT_STATUS_SUCCESS),
+ ACTION_SUCCESS(gatt_client_start_scan_action, NULL),
+ CLLBACK_GATTC_SCAN_RES(prop_emu_remotes_default_set, 1, TRUE),
+ ACTION_SUCCESS(gatt_client_stop_scan_action, NULL),
+ ACTION_SUCCESS(gatt_client_connect_action, &app1_conn_req),
+ CALLBACK_GATTC_CONNECT(GATT_STATUS_SUCCESS,
+ prop_emu_remotes_default_set,
+ CONN1_ID, APP1_ID),
+ ACTION_SUCCESS(gatt_client_search_services, &search_services_1),
+ CALLBACK_GATTC_SEARCH_COMPLETE(GATT_STATUS_SUCCESS, CONN1_ID),
+ ACTION_SUCCESS(gatt_client_get_included_action,
+ &get_incl_data_1),
+ CALLBACK_GATTC_GET_INCLUDED(GATT_STATUS_FAILURE, CONN1_ID,
+ &service_1, NULL),
+ ACTION_SUCCESS(bluetooth_disable_action, NULL),
+ CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+ ),
TEST_CASE_BREDRLE("Gatt Client - Get Included Service - 16 UUID",
ACTION_SUCCESS(init_pdus, get_included_1),
ACTION_SUCCESS(bluetooth_enable_action, NULL),
ACTION_SUCCESS(gatt_server_send_indication_action,
&send_indication_data_1),
CALLBACK(CB_EMU_VALUE_INDICATION),
+ CALLBACK_GATTS_NOTIF_CONF(CONN1_ID, GATT_STATUS_SUCCESS),
ACTION_SUCCESS(bluetooth_disable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
),
CONN1_ID, APP1_ID),
ACTION_SUCCESS(gatt_server_send_indication_action,
&send_indication_data_2),
+ CALLBACK_GATTS_NOTIF_CONF(CONN1_ID, GATT_STATUS_SUCCESS),
CALLBACK(CB_EMU_VALUE_NOTIFICATION),
ACTION_SUCCESS(bluetooth_disable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
CALLBACK_GATTS_CONNECTION(GATT_SERVER_CONNECTED,
prop_emu_remotes_default_set,
CONN1_ID, APP1_ID),
- MODIFY_DATA(GATT_STATUS_SUCCESS, gatt_att_pdu_modify,
- &char1_handle, &att_read_req,
- ATT_HANDLE_SIZE),
- ACTION_SUCCESS(gatt_remote_send_raw_pdu_action, &att_read_req),
+ PROCESS_DATA(GATT_STATUS_SUCCESS,
+ gatt_remote_send_raw_pdu_action,
+ &att_read_req_op_v, &char1_handle_v, NULL),
CALLBACK_GATTS_REQUEST_READ(CONN1_ID, TRANS1_ID,
prop_emu_remotes_default_set,
&char1_handle, 0, false),
CALLBACK_GATTS_CONNECTION(GATT_SERVER_CONNECTED,
prop_emu_remotes_default_set,
CONN1_ID, APP1_ID),
- MODIFY_DATA(GATT_STATUS_SUCCESS, gatt_att_pdu_modify,
- &att_write_req_data_1, &att_write_req_1,
- sizeof(att_write_req_value_1) +
- ATT_HANDLE_SIZE),
- ACTION_SUCCESS(gatt_remote_send_raw_pdu_action,
- &att_write_req_1),
+ PROCESS_DATA(GATT_STATUS_SUCCESS,
+ gatt_remote_send_raw_pdu_action,
+ &att_write_req_op_v, &char1_handle_v,
+ &att_write_req_value_1_v),
CALLBACK_GATTS_REQUEST_WRITE(CONN1_ID, TRANS1_ID,
prop_emu_remotes_default_set,
&char1_handle, 0,
ACTION_SUCCESS(bluetooth_disable_action, NULL),
CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
),
+ TEST_CASE_BREDRLE("Gatt Server - Find By Type - Attribute not found",
+ ACTION_SUCCESS(bluetooth_enable_action, NULL),
+ CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+ ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
+ ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
+ ACTION_SUCCESS(gatt_server_register_action, &app1_uuid),
+ CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS),
+ ACTION_SUCCESS(gatt_server_add_service_action,
+ &add_service_data_5),
+ CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID,
+ &service_add_1, NULL,
+ &srvc1_handle),
+ ACTION_SUCCESS(gatt_server_add_char_action, &add_char_data_2),
+ CALLBACK_GATTS_CHARACTERISTIC_ADDED(GATT_STATUS_SUCCESS,
+ APP1_ID, &app1_uuid,
+ &srvc1_handle, NULL,
+ &char1_handle),
+ ACTION_SUCCESS(gatt_server_start_srvc_action,
+ &start_srvc_data_2),
+ CALLBACK_GATTS_SERVICE_STARTED(GATT_STATUS_SUCCESS, APP1_ID,
+ &srvc1_handle),
+ ACTION_SUCCESS(bt_start_discovery_action, NULL),
+ CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+ BT_DISCOVERY_STARTED),
+ CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_le_set, 2),
+ ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
+ ACTION_SUCCESS(gatt_server_connect_action, &app1_conn_req),
+ CALLBACK_GATTS_CONNECTION(GATT_SERVER_CONNECTED,
+ prop_emu_remotes_default_set,
+ CONN1_ID, APP1_ID),
+ PROCESS_DATA(GATT_STATUS_SUCCESS,
+ gatt_remote_send_raw_pdu_action,
+ &att_find_by_type_req_op_v,
+ &search_range_1,
+ &primary_type),
+ CALLBACK_ERROR(CB_EMU_ATT_ERROR, 0x0a),
+ ACTION_SUCCESS(bluetooth_disable_action, NULL),
+ CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+ ),
+ /* This tests embeded ccc */
+ TEST_CASE_BREDRLE("Gatt Server - Srvc change write req. success",
+ ACTION_SUCCESS(bluetooth_enable_action, NULL),
+ CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+ ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
+ ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
+ ACTION_SUCCESS(gatt_server_register_action, &app1_uuid),
+ CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS),
+ ACTION_SUCCESS(bt_start_discovery_action, NULL),
+ CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+ BT_DISCOVERY_STARTED),
+ CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_le_set, 2),
+ ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
+ ACTION_SUCCESS(gatt_server_connect_action, &app1_conn_req),
+ CALLBACK_GATTS_CONNECTION(GATT_SERVER_CONNECTED,
+ prop_emu_remotes_default_set,
+ CONN1_ID, APP1_ID),
+ /* For CCC we need to be bonded */
+ ACTION_SUCCESS(bt_create_bond_action,
+ &prop_test_remote_ble_bdaddr_req),
+ CALLBACK_BOND_STATE(BT_BOND_STATE_BONDED,
+ &prop_emu_remotes_default_set[0], 1),
+ /* Write and receive confirmation */
+ PROCESS_DATA(GATT_STATUS_SUCCESS,
+ gatt_remote_send_raw_pdu_action,
+ &att_write_req_op_v, &svc_change_ccc_handle_v,
+ &svc_change_ccc_value_v),
+ CALLBACK(CB_EMU_WRITE_RESPONSE),
+ /* Shutdown */
+ ACTION_SUCCESS(bluetooth_disable_action, NULL),
+ CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+ ),
+ TEST_CASE_BREDRLE("Gatt Server - Send error resp to write char request",
+ ACTION_SUCCESS(bluetooth_enable_action, NULL),
+ CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_ON),
+ ACTION_SUCCESS(emu_setup_powered_remote_action, NULL),
+ ACTION_SUCCESS(emu_set_ssp_mode_action, NULL),
+ ACTION_SUCCESS(emu_set_connect_cb_action, gatt_conn_cb),
+ ACTION_SUCCESS(gatt_server_register_action, &app1_uuid),
+ CALLBACK_STATUS(CB_GATTS_REGISTER_SERVER, BT_STATUS_SUCCESS),
+ ACTION_SUCCESS(gatt_server_add_service_action,
+ &add_service_data_5),
+ CALLBACK_GATTS_SERVICE_ADDED(GATT_STATUS_SUCCESS, APP1_ID,
+ &service_add_1, NULL,
+ &srvc1_handle),
+ ACTION_SUCCESS(gatt_server_add_char_action, &add_char_data_2),
+ CALLBACK_GATTS_CHARACTERISTIC_ADDED(GATT_STATUS_SUCCESS,
+ APP1_ID, &app1_uuid,
+ &srvc1_handle, NULL,
+ &char1_handle),
+ ACTION_SUCCESS(gatt_server_start_srvc_action,
+ &start_srvc_data_2),
+ CALLBACK_GATTS_SERVICE_STARTED(GATT_STATUS_SUCCESS, APP1_ID,
+ &srvc1_handle),
+ ACTION_SUCCESS(bt_start_discovery_action, NULL),
+ CALLBACK_STATE(CB_BT_DISCOVERY_STATE_CHANGED,
+ BT_DISCOVERY_STARTED),
+ CALLBACK_DEVICE_FOUND(prop_emu_remotes_default_le_set, 2),
+ ACTION_SUCCESS(bt_cancel_discovery_action, NULL),
+ ACTION_SUCCESS(gatt_server_connect_action, &app1_conn_req),
+ CALLBACK_GATTS_CONNECTION(GATT_SERVER_CONNECTED,
+ prop_emu_remotes_default_set,
+ CONN1_ID, APP1_ID),
+ PROCESS_DATA(GATT_STATUS_SUCCESS,
+ gatt_remote_send_raw_pdu_action,
+ &att_write_req_op_v, &char1_handle_v,
+ &att_write_req_value_1_v),
+ CALLBACK_GATTS_REQUEST_WRITE(CONN1_ID, TRANS1_ID,
+ prop_emu_remotes_default_set,
+ &char1_handle, 0,
+ sizeof(att_write_req_value_1),
+ true, false,
+ att_write_req_value_1),
+ ACTION_SUCCESS(gatt_server_send_response_action,
+ &send_resp_data_2_error),
+ CALLBACK_ERROR(CB_EMU_ATT_ERROR, GATT_ERR_INVAL_ATTR_VALUE_LEN),
+ ACTION_SUCCESS(bluetooth_disable_action, NULL),
+ CALLBACK_STATE(CB_BT_ADAPTER_STATE_CHANGED, BT_STATE_OFF),
+ ),
};
struct queue *get_gatt_tests(void)
DBG_CB(CB_GATTS_REQUEST_WRITE),
DBG_CB(CB_GATTS_REQUEST_EXEC_WRITE),
DBG_CB(CB_GATTS_RESPONSE_CONFIRMATION),
+ DBG_CB(CB_GATTS_INDICATION_SEND),
/* Map client */
DBG_CB(CB_MAP_CLIENT_REMOTE_MAS_INSTANCES),
DBG_CB(CB_EMU_VALUE_NOTIFICATION),
DBG_CB(CB_EMU_READ_RESPONSE),
DBG_CB(CB_EMU_WRITE_RESPONSE),
+ DBG_CB(CB_EMU_ATT_ERROR),
};
static gboolean check_callbacks_called(gpointer user_data)
return false;
}
+ if (exp->callback_result.error != step->callback_result.error) {
+ tester_debug("Err mismatch: %d vs %d",
+ exp->callback_result.error,
+ step->callback_result.error);
+ return false;
+ }
+
if (exp->store_srvc_handle)
memcpy(exp->store_srvc_handle,
step->callback_result.srvc_handle,
schedule_callback_verification(step);
}
+static void gatts_indication_send_cb(int conn_id, int status)
+{
+ struct step *step = g_new0(struct step, 1);
+
+ step->callback = CB_GATTS_INDICATION_SEND;
+
+ step->callback_result.conn_id = conn_id;
+ step->callback_result.status = status;
+
+ schedule_callback_verification(step);
+}
+
static const btgatt_server_callbacks_t btgatt_server_callbacks = {
.register_server_cb = gatts_register_server_cb,
.connection_cb = gatts_connection_cb,
.request_read_cb = gatts_request_read_cb,
.request_write_cb = gatts_request_write_cb,
.request_exec_write_cb = NULL,
- .response_confirmation_cb = NULL
+ .response_confirmation_cb = NULL,
+ .indication_sent_cb = gatts_indication_send_cb,
+ .congestion_cb = NULL,
};
static const btgatt_callbacks_t btgatt_callbacks = {
.action_status = status, \
.action = modif_fun, \
.set_data = from, \
- .set_data_to = to, \
+ .set_data_2 = to, \
.set_data_len = len, \
}
+#define PROCESS_DATA(status, proc_fun, data1, data2, data3) { \
+ .action_status = status, \
+ .action = proc_fun, \
+ .set_data = data1, \
+ .set_data_2 = data2, \
+ .set_data_3 = data3, \
+ }
+
#define ACTION(status, act_fun, data_set) { \
.action_status = status, \
.action = act_fun, \
.callback_result.status = cb_res, \
}
+#define CALLBACK_ERROR(cb, cb_err) { \
+ .callback = cb, \
+ .callback_result.error = cb_err, \
+ }
+
#define CALLBACK_ADAPTER_PROPS(props, prop_cnt) { \
.callback = CB_BT_ADAPTER_PROPERTIES, \
.callback_result.properties = props, \
.callback_result.gatt_app_id = cb_server_id, \
}
+#define CALLBACK_GATTS_NOTIF_CONF(cb_conn_id, cb_status) { \
+ .callback = CB_GATTS_INDICATION_SEND, \
+ .callback_result.conn_id = cb_conn_id, \
+ .callback_result.status = cb_status, \
+ }
+
#define CALLBACK_GATTS_SERVICE_ADDED(cb_res, cb_server_id, cb_service, \
cb_srvc_handle, \
cb_store_srvc_handle) { \
CB_GATTS_REQUEST_WRITE,
CB_GATTS_REQUEST_EXEC_WRITE,
CB_GATTS_RESPONSE_CONFIRMATION,
+ CB_GATTS_INDICATION_SEND,
/* Map client */
CB_MAP_CLIENT_REMOTE_MAS_INSTANCES,
CB_EMU_VALUE_NOTIFICATION,
CB_EMU_READ_RESPONSE,
CB_EMU_WRITE_RESPONSE,
+ CB_EMU_ATT_ERROR,
} expected_bt_callback_t;
struct test_data {
int offset;
bool is_long;
int connected;
- int *attr_handle;
- int *srvc_handle;
- int *inc_srvc_handle;
- int *char_handle;
- int *desc_handle;
+ uint16_t *attr_handle;
+ uint16_t *srvc_handle;
+ uint16_t *inc_srvc_handle;
+ uint16_t *char_handle;
+ uint16_t *desc_handle;
btgatt_srvc_id_t *service;
btgatt_gatt_id_t *characteristic;
btgatt_gatt_id_t *descriptor;
uint8_t *value;
bool need_rsp;
bool is_prep;
+ uint8_t error;
btpan_control_state_t ctrl_state;
btpan_connection_state_t conn_state;
struct bt_callback_data callback_result;
void *set_data;
- void *set_data_to;
+ void *set_data_2;
+ void *set_data_3;
int set_data_len;
- int *store_srvc_handle;
- int *store_char_handle;
- int *store_desc_handle;
+ uint16_t *store_srvc_handle;
+ uint16_t *store_char_handle;
+ uint16_t *store_desc_handle;
};
struct test_case {
#include <stdint.h>
#include <stdlib.h>
-#include <bluetooth/bluetooth.h>
-
#include <glib.h>
-#include "src/shared/util.h"
+#include "lib/bluetooth.h"
#include "lib/uuid.h"
+
+#include "src/shared/util.h"
#include "att.h"
static inline void put_uuid_le(const bt_uuid_t *src, void *dst)
};
#ifdef __TIZEN_PATCH__
+void get_uuid(uint8_t type, const void *val, bt_uuid_t *uuid);
+
static inline bt_uuid_t att_get_uuid(const void *ptr, uint8_t len)
{
bt_uuid_t uuid;
uint16_t dec_exec_write_req(const uint8_t *pdu, size_t len, uint8_t *flags);
uint16_t enc_exec_write_resp(uint8_t *pdu);
uint16_t dec_exec_write_resp(const uint8_t *pdu, size_t len);
-#ifdef __TIZEN_PATCH__
-void get_uuid(uint8_t type, const void *val, bt_uuid_t *uuid);
-#endif
+++ /dev/null
-/*
- *
- * BlueZ - Bluetooth protocol stack for Linux
- *
- * Copyright (C) 2010 Nokia Corporation
- * Copyright (C) 2010 Marcel Holtmann <marcel@holtmann.org>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- */
-
-#ifdef __TIZEN_PATCH__
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <errno.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <stdint.h>
-#include <stdbool.h>
-#include <string.h>
-#include <glib.h>
-
-#include <bluetooth/bluetooth.h>
-
-#include "lib/uuid.h"
-#include "src/adapter.h"
-#include "src/device.h"
-#include "src/log.h"
-#include "gdbus/gdbus.h"
-#include "src/error.h"
-#include "src/dbus-common.h"
-#include "btio/btio.h"
-#include "src/storage.h"
-
-
-#include "att.h"
-#include "gattrib.h"
-#include "src/attio.h"
-#include "gatt.h"
-#include "client.h"
-#include "src/shared/util.h"
-
-#define CHAR_INTERFACE "org.bluez.Characteristic"
-
-#define ATT_MAX_MTU 256
-struct format {
- guint8 format;
- guint8 exponent;
- guint16 unit;
- guint8 namespace;
- guint16 desc;
-} __attribute__ ((packed));
-
-struct query {
- DBusMessage *msg;
- GSList *list;
-};
-
-struct gatt_service {
- struct btd_device *dev;
- struct gatt_primary *prim;
- DBusConnection *conn;
- GAttrib *attrib;
- guint attioid;
- int psm;
- char *path;
- GSList *chars;
- GSList *offline_chars;
- GSList *watchers;
- struct query *query;
-};
-
-struct characteristic {
- struct gatt_service *gatt;
- char *path;
- uint16_t handle;
- uint16_t end;
- uint8_t perm;
- char type[MAX_LEN_UUID_STR + 1];
- char *name;
- char *desc;
- struct format *format;
- uint8_t *value;
- size_t vlen;
-};
-
-struct query_data {
- struct gatt_service *gatt;
- struct characteristic *chr;
- uint16_t handle;
-};
-
-struct watcher {
- guint id;
- char *name;
- char *path;
- struct gatt_service *gatt;
-};
-
-typedef enum {
- BT_IO_L2CAP,
-} BtIOType;
-
-struct discover_charecteristics {
- GAttrib *attrib;
- bt_uuid_t *uuid;
- uint16_t end;
- GSList *characteristics;
- gatt_cb_t cb;
- void *user_data;
-};
-
-static GSList *gatt_services = NULL;
-
-static void characteristic_free(void *user_data)
-{
- struct characteristic *chr = user_data;
-
- g_free(chr->path);
- g_free(chr->desc);
- g_free(chr->format);
- g_free(chr->value);
- g_free(chr->name);
- g_free(chr);
-}
-
-static void watcher_free(void *user_data)
-{
- struct watcher *watcher = user_data;
-
- g_free(watcher->path);
- g_free(watcher->name);
- g_free(watcher);
-}
-
-static void gatt_service_free(struct gatt_service *gatt)
-{
- g_slist_free_full(gatt->watchers, watcher_free);
- g_slist_free_full(gatt->chars, characteristic_free);
- g_slist_free(gatt->offline_chars);
- g_free(gatt->path);
- btd_device_unref(gatt->dev);
- dbus_connection_unref(gatt->conn);
- g_free(gatt);
-}
-
-static 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;
- }
-}
-
-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);
- sba = (bdaddr_t *)btd_adapter_get_address(adapter);
- dba = (bdaddr_t *)device_get_address(device);
- *bdaddr_type = btd_device_get_bdaddr_type(device);
-}
-
-static int characteristic_handle_cmp(gconstpointer a, gconstpointer b)
-{
- const struct characteristic *chr = a;
- uint16_t handle = GPOINTER_TO_UINT(b);
-
- return chr->handle - handle;
-}
-
-static int watcher_cmp(gconstpointer a, gconstpointer b)
-{
- const struct watcher *watcher = a;
- const struct watcher *match = b;
- int ret;
-
- ret = g_strcmp0(watcher->name, match->name);
- if (ret != 0)
- return ret;
-
- return g_strcmp0(watcher->path, match->path);
-}
-
-
-static void watcher_exit(DBusConnection *conn, void *user_data)
-{
- struct watcher *watcher = user_data;
- struct gatt_service *gatt = watcher->gatt;
-
- 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,
- const uint8_t *value, size_t vlen)
-{
- chr->value = g_try_realloc(chr->value, vlen);
- if (chr->value == NULL)
- return -ENOMEM;
-
- memcpy(chr->value, value, vlen);
- chr->vlen = vlen;
-
- return 0;
-}
-
-static void update_watchers(gpointer data, gpointer user_data)
-{
- struct watcher *w = data;
- struct characteristic *chr = user_data;
- DBusConnection *conn = w->gatt->conn;
- DBusMessage *msg;
-
- msg = dbus_message_new_method_call(w->name, w->path,
- "org.bluez.Watcher", "ValueChanged");
- if (msg == NULL)
- return;
-
- dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &chr->path,
- DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
- &chr->value, chr->vlen, DBUS_TYPE_INVALID);
-
- dbus_message_set_no_reply(msg, TRUE);
- g_dbus_send_message(conn, msg);
-}
-
-static void events_handler(const uint8_t *pdu, uint16_t len,
- gpointer user_data)
-{
- struct gatt_service *gatt = user_data;
- struct characteristic *chr;
- GSList *l;
- uint8_t opdu[ATT_MAX_MTU];
- guint handle;
- uint16_t olen;
-
- if (len < 3) {
- DBG("Malformed notification/indication packet (opcode 0x%02x)",
- pdu[0]);
- return;
- }
-
- handle = get_le16(&pdu[1]);
-
- l = g_slist_find_custom(gatt->chars, GUINT_TO_POINTER(handle),
- characteristic_handle_cmp);
- if (!l)
- return;
-
- chr = l->data;
-
- if (chr == NULL) {
- DBG("Attribute handle 0x%02x not found", handle);
- return;
- }
-
- switch (pdu[0]) {
- case ATT_OP_HANDLE_IND:
- olen = enc_confirmation(opdu, sizeof(opdu));
- g_attrib_send(gatt->attrib, 0, opdu, olen,
- NULL, NULL, NULL);
- case ATT_OP_HANDLE_NOTIFY:
- if (characteristic_set_value(chr, &pdu[3], len - 3) < 0)
- DBG("Can't change Characteristic 0x%02x", handle);
-
- g_slist_foreach(gatt->watchers, update_watchers, chr);
- break;
- }
-}
-
-static void offline_char_written(gpointer user_data)
-{
- struct characteristic *chr = user_data;
- struct gatt_service *gatt = chr->gatt;
-
- gatt->offline_chars = g_slist_remove(gatt->offline_chars, chr);
-
- remove_attio(gatt);
-}
-
-static void offline_char_write(gpointer data, gpointer user_data)
-{
- struct characteristic *chr = data;
- GAttrib *attrib = user_data;
-
- gatt_write_cmd(attrib, chr->handle, chr->value, chr->vlen,
- offline_char_written, chr);
-}
-
-static void char_discovered_cb(uint8_t status, GSList *characteristics,
- void *user_data);
-
-static void attio_connected(GAttrib *attrib, gpointer user_data)
-{
- struct gatt_service *gatt = user_data;
-
- gatt->attrib = g_attrib_ref(attrib);
-
- g_attrib_register(gatt->attrib, ATT_OP_HANDLE_NOTIFY, GATTRIB_ALL_HANDLES,
- events_handler, gatt, NULL);
- g_attrib_register(gatt->attrib, ATT_OP_HANDLE_IND, GATTRIB_ALL_HANDLES,
- 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;
- }
-}
-
-static DBusMessage *register_watcher(DBusConnection *conn,
- DBusMessage *msg, void *data)
-{
- const char *sender = dbus_message_get_sender(msg);
- struct gatt_service *gatt = data;
- struct watcher *watcher;
- char *path;
-
- DBG("register_watcher");
- if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
- DBUS_TYPE_INVALID))
- return btd_error_invalid_args(msg);
-
- watcher = g_new0(struct watcher, 1);
- watcher->name = g_strdup(sender);
- watcher->gatt = gatt;
- watcher->path = g_strdup(path);
- watcher->id = g_dbus_add_disconnect_watch(conn, sender, watcher_exit,
- watcher, watcher_free);
-
- if (gatt->attioid == 0)
- gatt->attioid = btd_device_add_attio_callback(gatt->dev,
- attio_connected,
- attio_disconnected,
- gatt);
-
- gatt->watchers = g_slist_append(gatt->watchers, watcher);
-
- return dbus_message_new_method_return(msg);
-}
-
-static DBusMessage *unregister_watcher(DBusConnection *conn,
- DBusMessage *msg, void *data)
-{
- const char *sender = dbus_message_get_sender(msg);
- struct gatt_service *gatt = data;
- struct watcher *watcher, *match;
- GSList *l;
- char *path;
- DBG("unregister_watcher");
-
- if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
- DBUS_TYPE_INVALID))
- return btd_error_invalid_args(msg);
-
- match = g_new0(struct watcher, 1);
- match->name = g_strdup(sender);
- match->path = g_strdup(path);
- l = g_slist_find_custom(gatt->watchers, match, watcher_cmp);
- watcher_free(match);
- if (!l)
- return btd_error_not_authorized(msg);
-
- watcher = l->data;
- gatt->watchers = g_slist_remove(gatt->watchers, watcher);
- g_dbus_remove_watch(conn, watcher->id);
- remove_attio(gatt);
-
- return dbus_message_new_method_return(msg);
-}
-
-static void write_char_cb(guint8 status, const guint8 *pdu, guint16 plen,
- gpointer user_data)
-{
- struct characteristic *chr = user_data;
- struct gatt_service *gatt = chr->gatt;
- DBusMessage *reply;
-
- if (status != 0) {
- const char *str = att_ecode2str(status);
- g_printerr("Characteristic write failed: %s\n", str);
-
- reply = btd_error_failed(gatt->query->msg, str);
- goto done;
- }
-
- if (!dec_write_resp(pdu, plen)) {
- g_printerr("Protocol error\n");
- reply = btd_error_failed(gatt->query->msg,
- "Attribute Protocol Error");
- goto done;
- }
-
- reply = dbus_message_new_method_return(gatt->query->msg);
-
-done:
- dbus_connection_send(gatt->conn, reply, NULL);
- dbus_connection_flush(gatt->conn);
- dbus_message_unref(reply);
- g_free(gatt->query);
- gatt->query = NULL;
-
- return;
-}
-
-static DBusMessage *set_value(DBusConnection *conn, DBusMessage *msg,
- DBusMessageIter *iter, struct characteristic *chr)
-{
- struct gatt_service *gatt = chr->gatt;
- DBusMessageIter sub;
- uint8_t *value;
- int len;
-
- if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY ||
- dbus_message_iter_get_element_type(iter) != DBUS_TYPE_BYTE)
- return btd_error_invalid_args(msg);
-
- dbus_message_iter_recurse(iter, &sub);
-
- dbus_message_iter_get_fixed_array(&sub, &value, &len);
-
- characteristic_set_value(chr, value, len);
-
- if (gatt->attrib)
- gatt_write_cmd(gatt->attrib, chr->handle, value, len,
- NULL, NULL);
- else
- gatt->offline_chars = g_slist_append(gatt->offline_chars, chr);
-
- return dbus_message_new_method_return(msg);
-}
-
-static DBusMessage *set_property(DBusConnection *conn,
- DBusMessage *msg, void *data)
-{
- struct characteristic *chr = data;
- DBusMessageIter iter;
- DBusMessageIter sub;
- const char *property;
-
- 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);
- dbus_message_iter_next(&iter);
-
- if (g_str_equal("Value", property))
- return set_value(conn, msg, &sub, chr);
-
- return btd_error_invalid_args(msg);
-}
-
-static DBusMessage *set_value_request(DBusConnection *conn, DBusMessage *msg,
- DBusMessageIter *iter, struct characteristic *chr)
-{
- struct gatt_service *gatt = chr->gatt;
- DBusMessageIter sub;
- uint8_t *value;
- int len;
- struct query *query;
-
- if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY ||
- dbus_message_iter_get_element_type(iter) != DBUS_TYPE_BYTE)
- return btd_error_invalid_args(msg);
-
- dbus_message_iter_recurse(iter, &sub);
-
- dbus_message_iter_get_fixed_array(&sub, &value, &len);
-
- characteristic_set_value(chr, value, len);
-
- if (gatt->attrib) {
- query = g_new0(struct query, 1);
- query->msg = dbus_message_ref(msg);
- gatt->query = query;
- gatt_write_char(gatt->attrib, chr->handle, value, len,
- write_char_cb, chr);
- } else {
- gatt->offline_chars = g_slist_append(gatt->offline_chars, chr);
- }
-
- return dbus_message_new_method_return(msg);
-}
-
-static DBusMessage *set_property_request(DBusConnection *conn,
- DBusMessage *msg, void *data)
-{
- struct characteristic *chr = data;
- DBusMessageIter iter;
- DBusMessageIter sub;
- const char *property;
-
- 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);
- dbus_message_iter_next(&iter);
-
- if (g_str_equal("Value", property))
- return set_value_request(conn, msg, &sub, chr);
-
- return btd_error_invalid_args(msg);
-}
-
-static void append_char_dict(DBusMessageIter *iter, struct characteristic *chr)
-{
- DBusMessageIter dict;
- const char *name = "";
- char *uuid;
- uint16_t permission;
-
- 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);
-
- uuid = g_strdup(chr->type);
- dict_append_entry(&dict, "UUID", DBUS_TYPE_STRING, &uuid);
- g_free(uuid);
-
- /* FIXME: Translate UUID to name. */
- dict_append_entry(&dict, "Name", DBUS_TYPE_STRING, &name);
-
- if (chr->desc)
- dict_append_entry(&dict, "Description", DBUS_TYPE_STRING,
- &chr->desc);
-
- if (chr->value)
- dict_append_array(&dict, "Value", DBUS_TYPE_BYTE, &chr->value,
- chr->vlen);
-
- if (chr->perm) {
- permission = chr->perm;
- dict_append_entry(&dict, "Permission", DBUS_TYPE_UINT16, &permission);
- }
-
- /* FIXME: Missing Format, Value and Representation */
-
- dbus_message_iter_close_container(iter, &dict);
-}
-
-static DBusMessage *get_properties(DBusConnection *conn, DBusMessage *msg,
- void *data)
-{
- struct characteristic *chr = data;
- DBusMessage *reply;
- DBusMessageIter iter;
-
- reply = dbus_message_new_method_return(msg);
- if (!reply)
- return NULL;
-
- dbus_message_iter_init_append(reply, &iter);
-
- append_char_dict(&iter, chr);
-
- return reply;
-}
-
-static void read_char_cb(guint8 status, const guint8 *pdu, guint16 plen,
- gpointer user_data)
-{
- struct characteristic *chr = user_data;
- struct gatt_service *gatt = chr->gatt;
- DBusMessage *reply;
- uint8_t *value = NULL;
- int vlen;
-
- if (status != 0) {
- const char *str = att_ecode2str(status);
- g_printerr("Characteristic value/descriptor read failed: %s\n",
- str);
- reply = btd_error_failed(gatt->query->msg, str);
- goto done;
- }
-
- value = g_malloc(ATT_MAX_MTU * sizeof(unsigned char));
-
- vlen = dec_read_resp(pdu, plen, value, ATT_MAX_MTU);
- if (vlen <= 0) {
- g_printerr("Attribute Protocol Error\n");
- reply = btd_error_failed(gatt->query->msg,
- "Attribute Protocol Error");
- goto done;
- }
-
- reply = dbus_message_new_method_return(gatt->query->msg);
- dbus_message_append_args(reply,
- DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &value, vlen,
- DBUS_TYPE_INVALID);
-done:
- dbus_connection_send(gatt->conn, reply, NULL);
- dbus_connection_flush(gatt->conn);
- dbus_message_unref(reply);
- g_free(gatt->query);
- gatt->query = NULL;
-
- if (value)
- g_free(value);
- return;
-}
-
-static DBusMessage *read_char(DBusConnection *conn, DBusMessage *msg,
- void *data)
-{
- struct query *query;
-
- struct characteristic *chr = data;
- struct gatt_service *gatt = chr->gatt;
-
- query = g_new0(struct query, 1);
- query->msg = dbus_message_ref(msg);
- gatt->query = query;
-
- gatt_read_char(gatt->attrib, chr->handle, read_char_cb, chr);
-
- return NULL;
-}
-
-static void discover_char_descriptor_cb(guint8 status, const guint8 *pdu, guint16 plen,
- gpointer user_data)
-{
- struct characteristic *chr = user_data;
- struct gatt_service *gatt = chr->gatt;
- DBusMessage *reply;
- guint8 format;
- uint8_t *data;
-
- if (status != 0) {
- const char *str = att_ecode2str(status);
-
- g_printerr("Discover Characteristic Descriptor failed: %s\n", str);
-
- reply = btd_error_failed(gatt->query->msg, str);
- goto done;
- }
-
- if (!dec_find_info_resp(pdu, plen, &format)) {
- g_printerr("Att Protocol error\n");
-
- reply = btd_error_failed(gatt->query->msg,
- "Attribute Protocol Error");
- goto done;
- }
-
- data = (uint8_t *)pdu + GATT_FIND_INFO_RESP_OPCODE_LEN;
-
- /* Since "user description" and "presentation format" descriptors have 16-bit UUIDs.
- Only format 0x01 is supported*/
- reply = dbus_message_new_method_return(gatt->query->msg);
-
- dbus_message_append_args(reply, DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
- &data, (plen - GATT_FIND_INFO_RESP_OPCODE_LEN), DBUS_TYPE_INVALID);
-
-done:
- dbus_connection_send(gatt->conn, reply, NULL);
- dbus_connection_flush(gatt->conn);
- dbus_message_unref(reply);
- dbus_message_unref(gatt->query->msg);
- g_free(gatt->query);
- gatt->query = NULL;
-
- return;
-}
-
-static DBusMessage *discover_char_descriptor(DBusConnection *conn, DBusMessage *msg,
- void *data)
-{
- struct characteristic *chr = data;
- struct gatt_service *gatt = chr->gatt;
- struct query *query;
-
- if (gatt->query)
- return btd_error_busy(msg);
-
- query = g_new0(struct query, 1);
- query->msg = dbus_message_ref(msg);
- gatt->query = query;
-
- if (gatt->attioid == 0) {
- gatt->attioid = btd_device_add_attio_callback(gatt->dev,
- NULL, attio_disconnected, gatt);
- }
-
- if (gatt->attrib) {
- gatt_find_info(gatt->attrib, chr->handle+1,
- chr->end, discover_char_descriptor_cb, chr);
- }
-
- return NULL;
-}
-
-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) },
- { GDBUS_METHOD("SetPropertyRequest",
- GDBUS_ARGS({ "name", "s" }, { "value", "v" }), NULL,
- set_property_request) },
- { GDBUS_ASYNC_METHOD("ReadCharacteristic", NULL,
- GDBUS_ARGS({ "value", "ay" }),
- read_char) },
- { GDBUS_ASYNC_METHOD("DiscoverCharacteristicsDescriptor",
- NULL, GDBUS_ARGS({ "CharacteristicsDescriptors", "ay" }),
- discover_char_descriptor)},
- { }
-};
-
-static char *characteristic_list_to_string(GSList *chars)
-{
- GString *characteristics;
- GSList *l;
-
- characteristics = g_string_new(NULL);
-
- for (l = chars; l; l = l->next) {
- struct characteristic *chr = l->data;
- char chr_str[64];
-
- memset(chr_str, 0, sizeof(chr_str));
-
- snprintf(chr_str, sizeof(chr_str), "%04X#%02X#%04X#%s ",
- chr->handle, chr->perm, chr->end, chr->type);
-
- characteristics = g_string_append(characteristics, chr_str);
- }
-
- return g_string_free(characteristics, FALSE);
-}
-
-static void store_characteristics(const bdaddr_t *sba, const bdaddr_t *dba,
- uint8_t bdaddr_type, uint16_t start,
- GSList *chars)
-{
- char *characteristics;
-
- characteristics = characteristic_list_to_string(chars);
-
- write_device_characteristics(sba, dba, bdaddr_type, start,
- characteristics);
-
- g_free(characteristics);
-}
-
-static void register_characteristic(gpointer data, gpointer user_data)
-{
- struct characteristic *chr = data;
- DBusConnection *conn = chr->gatt->conn;
- const char *gatt_path = user_data;
- DBG("register_characteristic");
-
- chr->path = g_strdup_printf("%s/characteristic%04x", gatt_path,
- chr->handle);
-
- g_dbus_register_interface(conn, chr->path, CHAR_INTERFACE,
- char_methods, NULL, NULL, chr, NULL);
-
- DBG("Registered: %s", chr->path);
-}
-
-static GSList *string_to_characteristic_list(struct gatt_service *gatt,
- const char *str)
-{
- GSList *l = NULL;
- char **chars;
- int i;
-
- if (str == NULL)
- return NULL;
-
- chars = g_strsplit(str, " ", 0);
- if (chars == NULL)
- return NULL;
-
- for (i = 0; chars[i]; i++) {
- struct characteristic *chr;
- int ret;
-
- chr = g_new0(struct characteristic, 1);
-
- ret = sscanf(chars[i], "%04hX#%02hhX#%04hX#%s", &chr->handle,
- &chr->perm, &chr->end, chr->type);
- if (ret < 4) {
- g_free(chr);
- continue;
- }
-
- chr->gatt = gatt;
- l = g_slist_append(l, chr);
- }
-
- g_strfreev(chars);
-
- return l;
-}
-
-static GSList *load_characteristics(struct gatt_service *gatt, uint16_t start)
-{
- GSList *chrs_list;
- bdaddr_t sba, dba;
- uint8_t bdaddr_type = BDADDR_LE_PUBLIC;
- char *str;
-
- gatt_get_address(gatt, &sba, &dba, &bdaddr_type);
- DBG("load_characteristics");
-
- str = read_device_characteristics(&sba, &dba, bdaddr_type, start);
- if (str == NULL)
- return NULL;
-
- chrs_list = string_to_characteristic_list(gatt, str);
-
- free(str);
-
- return chrs_list;
-}
-
-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;
-
- str = g_malloc0(MAX_LEN_UUID_STR + len * 2 + 1);
-
- bt_uuid16_create(&uuid, type);
- bt_uuid_to_string(&uuid, str, MAX_LEN_UUID_STR);
-
- str[MAX_LEN_UUID_STR - 1] = '#';
-
- 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, NULL);
-
- bdaddr_type = btd_device_get_bdaddr_type(device);
-
- write_device_attribute(&sba, &dba, bdaddr_type, handle, str);
-
- g_free(str);
-}
-
-static void query_list_append(struct gatt_service *gatt, struct query_data *data)
-{
- struct query *query = gatt->query;
-
- query->list = g_slist_append(query->list, data);
-}
-
-static void query_list_remove(struct gatt_service *gatt, struct query_data *data)
-{
- struct query *query = gatt->query;
-
- query->list = g_slist_remove(query->list, data);
- if (query->list != NULL)
- return;
-
- g_free(query);
- gatt->query = NULL;
-
- remove_attio(gatt);
-}
-
-static void update_char_desc(guint8 status, const guint8 *pdu, guint16 len,
- gpointer user_data)
-{
- struct query_data *current = user_data;
- struct gatt_service *gatt = current->gatt;
- struct characteristic *chr = current->chr;
-
- if (status == 0) {
-
- g_free(chr->desc);
-
- chr->desc = g_malloc(len);
- memcpy(chr->desc, pdu + 1, len - 1);
- chr->desc[len - 1] = '\0';
-
- store_attribute(gatt, current->handle,
- GATT_CHARAC_USER_DESC_UUID,
- (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,
- 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,
- BT_IO_OPT_SEC_LEVEL, level,
- BT_IO_OPT_INVALID)) {
- gatt_read_char(gatt->attrib, current->handle,
- update_char_desc, current);
- return;
- }
- }
-
- query_list_remove(gatt, current);
- g_free(current);
-}
-
-static void update_char_format(guint8 status, const guint8 *pdu, guint16 len,
- gpointer user_data)
-{
- struct query_data *current = user_data;
- struct gatt_service *gatt = current->gatt;
- struct characteristic *chr = current->chr;
-
- if (status != 0)
- goto done;
-
- if (len < 8)
- goto done;
-
- g_free(chr->format);
-
- chr->format = g_new0(struct format, 1);
- memcpy(chr->format, pdu + 1, 7);
-
- store_attribute(gatt, current->handle, GATT_CHARAC_FMT_UUID,
- (void *) chr->format, sizeof(*chr->format));
-
-done:
- query_list_remove(gatt, current);
- g_free(current);
-}
-
-static void update_char_value(guint8 status, const guint8 *pdu,
- guint16 len, gpointer user_data)
-{
- struct query_data *current = user_data;
- struct gatt_service *gatt = current->gatt;
- struct characteristic *chr = current->chr;
-
- if (status == 0)
- characteristic_set_value(chr, pdu + 1, len - 1);
- else if (status == ATT_ECODE_INSUFF_ENC) {
- GIOChannel *io = g_attrib_get_channel(gatt->attrib);
-
- if (bt_io_set(io, BT_IO_L2CAP,
- BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_HIGH,
- BT_IO_OPT_INVALID)) {
- gatt_read_char(gatt->attrib, chr->handle,
- update_char_value, current);
- return;
- }
- }
-
- query_list_remove(gatt, current);
- g_free(current);
-}
-
-static int uuid_desc16_cmp(bt_uuid_t *uuid, guint16 desc)
-{
- bt_uuid_t u16;
-
- bt_uuid16_create(&u16, desc);
-
- return bt_uuid_cmp(uuid, &u16);
-}
-
-static void descriptor_cb(guint8 status, const guint8 *pdu, guint16 plen,
- gpointer user_data)
-{
- struct query_data *current = user_data;
- struct gatt_service *gatt = current->gatt;
- struct att_data_list *list;
- guint8 format;
- int i;
-
- if (status != 0)
- goto done;
-
- DBG("Find Information Response received");
-
- list = dec_find_info_resp(pdu, plen, &format);
- if (list == NULL)
- goto done;
-
- for (i = 0; i < list->num; i++) {
- guint16 handle;
- bt_uuid_t uuid;
- uint8_t *info = list->data[i];
- struct query_data *qfmt;
-
- handle = get_le16(info);
-
- if (format == 0x01) {
- get_uuid(BT_UUID16, &info[2], &uuid);
-/* API is not present in bluez 5.25
- uuid = att_get_uuid16(&info[2]);*/
- } else {
- /* Currently, only "user description" and "presentation
- * format" descriptors are used, and both have 16-bit
- * UUIDs. Therefore there is no need to support format
- * 0x02 yet. */
- continue;
- }
- qfmt = g_new0(struct query_data, 1);
- qfmt->gatt = current->gatt;
- qfmt->chr = current->chr;
- qfmt->handle = handle;
-
- if (uuid_desc16_cmp(&uuid, GATT_CHARAC_USER_DESC_UUID) == 0) {
- query_list_append(gatt, qfmt);
- gatt_read_char(gatt->attrib, handle, update_char_desc,
- qfmt);
- } else if (uuid_desc16_cmp(&uuid, GATT_CHARAC_FMT_UUID) == 0) {
- query_list_append(gatt, qfmt);
- gatt_read_char(gatt->attrib, handle,
- update_char_format, qfmt);
- } else
- g_free(qfmt);
- }
-
- att_data_list_free(list);
-done:
- query_list_remove(gatt, current);
- g_free(current);
-}
-
-static void update_all_chars(gpointer data, gpointer user_data)
-{
- struct query_data *qdesc, *qvalue;
- struct characteristic *chr = data;
- struct gatt_service *gatt = user_data;
-
- qdesc = g_new0(struct query_data, 1);
- qdesc->gatt = gatt;
- qdesc->chr = chr;
-
- query_list_append(gatt, qdesc);
-
- gatt_find_info(gatt->attrib, chr->handle + 1, chr->end, descriptor_cb,
- qdesc);
-
- qvalue = g_new0(struct query_data, 1);
- qvalue->gatt = gatt;
- qvalue->chr = chr;
-
- query_list_append(gatt, qvalue);
-
- gatt_read_char(gatt->attrib, chr->handle, 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(uint8_t status, GSList *characteristics,
- void *user_data)
-{
- DBusMessage *reply;
- struct query_data *current = user_data;
- struct gatt_service *gatt = current->gatt;
- struct gatt_primary *prim = gatt->prim;
- uint16_t *previous_end = NULL;
- GSList *l;
- bdaddr_t sba, dba;
- uint8_t bdaddr_type = BDADDR_LE_PUBLIC;
-
- DBG("char_discovered_cb status %d", status);
- if (status != 0) {
- const char *str = att_ecode2str(status);
-
- DBG("Discover all characteristics failed: %s", str);
- reply = btd_error_failed(gatt->query->msg, str);
- goto fail;
- }
-
- for (l = characteristics; l; l = l->next) {
- struct gatt_char *current_chr = l->data;
- struct characteristic *chr;
- guint handle = current_chr->value_handle;
- GSList *lchr;
-
- lchr = g_slist_find_custom(gatt->chars,
- GUINT_TO_POINTER(handle), characteristic_handle_cmp);
- if (lchr)
- continue;
-
- chr = g_new0(struct characteristic, 1);
- chr->gatt = gatt;
- chr->perm = current_chr->properties;
- chr->handle = current_chr->value_handle;
- strncpy(chr->type, current_chr->uuid, sizeof(chr->type));
-
- if (previous_end)
- *previous_end = current_chr->handle;
-
- previous_end = &chr->end;
-
- gatt->chars = g_slist_append(gatt->chars, chr);
- register_characteristic(chr, gatt->path);
- }
-
- if (previous_end)
- *previous_end = prim->range.end;
-
- 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 DBusMessage *discover_char(DBusConnection *conn, DBusMessage *msg,
- void *data)
-{
- struct gatt_service *gatt = data;
- struct query *query;
- struct query_data *qchr;
-
- if (gatt->query)
- return btd_error_busy(msg);
-
- DBG("discover_char BP0");
-
- query = g_new0(struct query, 1);
-
- qchr = g_new0(struct query_data, 1);
- qchr->gatt = gatt;
-
- query->msg = dbus_message_ref(msg);
-
- 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;
-
- query_list_append(gatt, qchr);
-
- return NULL;
-}
-
-static DBusMessage *prim_get_properties(DBusConnection *conn, DBusMessage *msg,
- void *data)
-{
- struct gatt_service *gatt = data;
- DBusMessage *reply;
- DBusMessageIter iter;
- DBusMessageIter dict;
- GSList *l;
- char **chars;
- const char *uuid;
- int i;
-
- DBG("prim_get_properties");
-
- reply = dbus_message_new_method_return(msg);
- if (!reply)
- return NULL;
-
- dbus_message_iter_init_append(reply, &iter);
-
- dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
- DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
- DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
- DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
-
- chars = g_new0(char *, g_slist_length(gatt->chars) + 1);
-
- for (i = 0, l = gatt->chars; l; l = l->next, i++) {
- struct characteristic *chr = l->data;
- chars[i] = chr->path;
- }
-
- dict_append_array(&dict, "Characteristics", DBUS_TYPE_OBJECT_PATH,
- &chars, i);
- uuid = gatt->prim->uuid;
- dict_append_entry(&dict, "UUID", DBUS_TYPE_STRING, &uuid);
-
- g_free(chars);
-
- dbus_message_iter_close_container(&iter, &dict);
-
- return reply;
-}
-
-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 gatt_primary *prim,
- int psm)
-{
- struct gatt_service *gatt;
- const char *device_path;
-
- device_path = device_get_path(device);
-
- gatt = g_new0(struct gatt_service, 1);
- gatt->dev = btd_device_ref(device);
- gatt->prim = prim;
- gatt->psm = psm;
- gatt->conn = dbus_connection_ref(conn);
- gatt->path = g_strdup_printf("%s/service%04x", device_path,
- prim->range.start);
-
- g_dbus_register_interface(gatt->conn, gatt->path,
- CHAR_INTERFACE, prim_methods,
- NULL, NULL, gatt, NULL);
-
- gatt->chars = load_characteristics(gatt, prim->range.start);
- g_slist_foreach(gatt->chars, register_characteristic, gatt->path);
-
- return gatt;
-}
-
-GSList *attrib_client_register(DBusConnection *connection,
- struct btd_device *device, int psm,
- GAttrib *attrib, GSList *primaries)
-{
- GSList *l, *services;
-
- for (l = primaries, services = NULL; l; l = l->next) {
- struct gatt_primary *prim = l->data;
- struct gatt_service *gatt;
-
- gatt = primary_register(connection, device, prim, psm);
-
- DBG("Registered: %s", gatt->path);
-
- services = g_slist_append(services, g_strdup(gatt->path));
- gatt_services = g_slist_append(gatt_services, gatt);
- }
-
- return services;
-}
-
-static void primary_unregister(struct gatt_service *gatt)
-{
- GSList *l;
-
- for (l = gatt->chars; l; l = l->next) {
- struct characteristic *chr = l->data;
- g_dbus_unregister_interface(gatt->conn, chr->path,
- CHAR_INTERFACE);
- }
-
- g_dbus_unregister_interface(gatt->conn, gatt->path, CHAR_INTERFACE);
-
- remove_attio(gatt);
-}
-
-static int path_cmp(gconstpointer data, gconstpointer user_data)
-{
- const char *path = data;
- const char *gatt_path = user_data;
-
- return g_strcmp0(path, gatt_path);
-}
-
-void attrib_client_unregister(GSList *services)
-{
- GSList *l, *left;
-
- for (l = gatt_services, left = NULL; l; l = l->next) {
- struct gatt_service *gatt = l->data;
-
- if (!g_slist_find_custom(services, gatt->path, path_cmp)) {
- left = g_slist_append(left, gatt);
- continue;
- }
-
- primary_unregister(gatt);
- gatt_service_free(gatt);
- }
-
- g_slist_free(gatt_services);
- gatt_services = left;
-}
-#endif /* __TIZEN_PATCH__ */
#include <glib.h>
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
#include "src/adapter.h"
#include "src/shared/util.h"
-#include "lib/uuid.h"
#include "attrib/gattrib.h"
#include "attrib/att.h"
#include "attrib/gatt.h"
struct attrib_cb *cb;
GSList *l = NULL;
+#ifdef __TIZEN_PATCH__
+ if (opt == GATT_OPT_INVALID)
+ return NULL;
+#endif
info = g_new0(struct gatt_info, 1);
l = g_slist_append(l, info);
{
uint16_t handle;
- /* When handles are assigned for primary servies of 128 bit categary,
- * handles are assigned from (0xffff - num_elements),
- * ex: start handle 0xfffb, end handle 0xffff
- * the below conditions enters infinite loop once the handle value reaches to 0xffff,
- * in the next increament it becomes 0x0000, which is always less than end handle(0xffff),
- * hence starts deleting the attributes from 0x0000 to 0xffff */
-#ifdef __TIZEN_PATCH__
- for (handle = start_handle; (handle != 0 && handle <= end_handle); handle++)
-#else
- for (handle = start_handle; handle <= end_handle; handle++)
-#endif
+ /* For a 128-bit category primary service below handle should be checked
+ * for both non-zero as well as >= 0xffff. As on last iteration the
+ * handle will turn to 0 from 0xffff and loop will be infinite.
+ */
+ for (handle = start_handle; (handle != 0 && handle <= end_handle);
+ handle++) {
if (attrib_db_del(adapter, handle) < 0)
error("Can't delete handle 0x%04x", handle);
+ }
}
#ifdef __TIZEN_PATCH__
return -1;
}
-bool gatt_send_noty_ind(struct btd_adapter *adapter, bt_uuid_t *uuid,
+bool gatt_send_noty_ind(struct btd_adapter *adapter, const bt_uuid_t *uuid,
uint8_t *value, size_t len)
{
struct attribute *a;
uint16_t end_handle);
bool gatt_update_db(struct btd_adapter *adapter, const bt_uuid_t *uuid,
uint8_t *value, size_t len);
-bool gatt_send_noty_ind(struct btd_adapter *adapter, bt_uuid_t *uuid,
+bool gatt_send_noty_ind(struct btd_adapter *adapter, const bt_uuid_t *uuid,
uint8_t *value, size_t len);
bool gatt_send_service_changed_ind(struct btd_adapter *adapter, bt_uuid_t *uuid,
uint16_t start_handle, uint16_t end_handle);
#include <stdint.h>
#include <stdlib.h>
+
#include <glib.h>
-#include <bluetooth/sdp.h>
-#include <bluetooth/sdp_lib.h>
-#include "src/shared/util.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
#include "lib/uuid.h"
+
+#include "src/shared/util.h"
#include "att.h"
#include "gattrib.h"
#include "gatt.h"
GAttrib *attrib;
unsigned int id;
bt_uuid_t uuid;
+ uint16_t start;
GSList *primaries;
gatt_cb_t cb;
void *user_data;
unsigned int id;
int refs;
int err;
+ uint16_t start_handle;
uint16_t end_handle;
GSList *includes;
gatt_cb_t cb;
unsigned int id;
bt_uuid_t *uuid;
uint16_t end;
+ uint16_t start;
GSList *characteristics;
gatt_cb_t cb;
void *user_data;
GAttrib *attrib;
unsigned int id;
bt_uuid_t *uuid;
+ uint16_t start;
uint16_t end;
GSList *descriptors;
gatt_cb_t cb;
if (range->end == 0xffff)
goto done;
+ /*
+ * If last handle is lower from previous start handle then it is smth
+ * wrong. Let's stop search, otherwise we might enter infinite loop.
+ */
+ if (range->end < dp->start) {
+ err = ATT_ECODE_UNLIKELY;
+ goto done;
+ }
+
+ dp->start = range->end + 1;
+
buf = g_attrib_get_buffer(dp->attrib, &buflen);
- oplen = encode_discover_primary(range->end + 1, 0xffff, &dp->uuid,
+ oplen = encode_discover_primary(dp->start, 0xffff, &dp->uuid,
buf, buflen);
if (oplen == 0)
att_data_list_free(list);
err = 0;
+ /*
+ * If last handle is lower from previous start handle then it is smth
+ * wrong. Let's stop search, otherwise we might enter infinite loop.
+ */
+ if (end < dp->start) {
+ err = ATT_ECODE_UNLIKELY;
+ goto done;
+ }
+
+ dp->start = end + 1;
+
if (end != 0xffff) {
size_t buflen;
uint8_t *buf = g_attrib_get_buffer(dp->attrib, &buflen);
- guint16 oplen = encode_discover_primary(end + 1, 0xffff, NULL,
+ guint16 oplen = encode_discover_primary(dp->start, 0xffff, NULL,
buf, buflen);
+
g_attrib_send(dp->attrib, dp->id, buf, oplen, primary_all_cb,
discover_primary_ref(dp),
discover_primary_unref);
dp->attrib = g_attrib_ref(attrib);
dp->cb = func;
dp->user_data = user_data;
+ dp->start = 0x0001;
if (uuid) {
dp->uuid = *uuid;
att_data_list_free(list);
+ /*
+ * If last handle is lower from previous start handle then it is smth
+ * wrong. Let's stop search, otherwise we might enter infinite loop.
+ */
+ if (last_handle < isd->start_handle) {
+ isd->err = ATT_ECODE_UNLIKELY;
+ goto done;
+ }
+
+ isd->start_handle = last_handle + 1;
+
if (last_handle < isd->end_handle)
- find_included(isd, last_handle + 1);
+ find_included(isd, isd->start_handle);
done:
if (isd->err == 0)
isd = g_new0(struct included_discovery, 1);
isd->attrib = g_attrib_ref(attrib);
+ isd->start_handle = start;
isd->end_handle = end;
isd->cb = func;
isd->user_data = user_data;
{
struct discover_char *dc = user_data;
struct att_data_list *list;
- unsigned int i, err = ATT_ECODE_ATTR_NOT_FOUND;
+ unsigned int i, err = 0;
uint16_t last = 0;
uint8_t type;
+ /* We have all the characteristic now, lets send it up */
+ if (status == ATT_ECODE_ATTR_NOT_FOUND) {
+ err = dc->characteristics ? 0 : status;
+ goto done;
+ }
+
if (status) {
err = status;
goto done;
att_data_list_free(list);
- if (last != 0 && (last + 1 < dc->end)) {
+ /*
+ * If last handle is lower from previous start handle then it is smth
+ * wrong. Let's stop search, otherwise we might enter infinite loop.
+ */
+ if (last < dc->start) {
+ err = ATT_ECODE_UNLIKELY;
+ goto done;
+ }
+
+ dc->start = last + 1;
+
+ if (last != 0 && (dc->start < dc->end)) {
bt_uuid_t uuid;
guint16 oplen;
size_t buflen;
bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
- oplen = enc_read_by_type_req(last + 1, dc->end, &uuid, buf,
+ oplen = enc_read_by_type_req(dc->start, dc->end, &uuid, buf,
buflen);
if (oplen == 0)
}
done:
- err = (dc->characteristics ? 0 : err);
dc->cb(err, dc->characteristics, dc->user_data);
}
dc->cb = func;
dc->user_data = user_data;
dc->end = end;
+ dc->start = start;
dc->uuid = g_memdup(uuid, sizeof(bt_uuid_t));
dc->id = g_attrib_send(attrib, 0, buf, plen, char_discovered_cb,
{
struct discover_desc *dd = user_data;
struct att_data_list *list;
- unsigned int i, err = ATT_ECODE_ATTR_NOT_FOUND;
+ unsigned int i, err = 0;
guint8 format;
uint16_t last = 0xffff;
uint8_t type;
gboolean uuid_found = FALSE;
+ if (status == ATT_ECODE_ATTR_NOT_FOUND) {
+ err = dd->descriptors ? 0 : status;
+ goto done;
+ }
+
if (status) {
err = status;
goto done;
att_data_list_free(list);
+ /*
+ * If last handle is lower from previous start handle then it is smth
+ * wrong. Let's stop search, otherwise we might enter infinite loop.
+ */
+ if (last < dd->start) {
+ err = ATT_ECODE_UNLIKELY;
+ goto done;
+ }
+
+ dd->start = last + 1;
+
if (last < dd->end && !uuid_found) {
guint16 oplen;
size_t buflen;
buf = g_attrib_get_buffer(dd->attrib, &buflen);
- oplen = enc_find_info_req(last + 1, dd->end, buf, buflen);
+ oplen = enc_find_info_req(dd->start, dd->end, buf, buflen);
if (oplen == 0)
return;
}
done:
- err = (dd->descriptors ? 0 : err);
dd->cb(err, dd->descriptors, dd->user_data);
}
dd->attrib = g_attrib_ref(attrib);
dd->cb = func;
dd->user_data = user_data;
+ dd->start = start;
dd->end = end;
dd->uuid = g_memdup(uuid, sizeof(bt_uuid_t));
*
*/
-#include <bluetooth/sdp.h>
-
/*
* GATT Characteristic Property bit field
* Reference: Core SPEC 4.1 page 2183 (Table 3.5: Characteristic Properties
#ifdef __TIZEN_PATCH__
guint gatt_find_info(GAttrib *attrib, uint16_t start, uint16_t end,
GAttribResultFunc func, gpointer user_data);
+guint gatt_read_char_by_offset(GAttrib *attrib, uint16_t handle, uint16_t offset,
+ GAttribResultFunc func, gpointer user_data);
+gboolean gatt_register_internet_protocol_service(struct btd_adapter *adapter);
+gboolean gatt_unregister_internet_protocol_service(struct btd_adapter *adapter);
#endif
\ No newline at end of file
#include "config.h"
#endif
+#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
-#include <glib.h>
-#include <stdio.h>
+#include <glib.h>
-#include <bluetooth/bluetooth.h>
+#include "lib/bluetooth.h"
#include "btio/btio.h"
#include "src/log.h"
if (!attr->att)
goto fail;
+ bt_att_set_close_on_unref(attr->att, true);
+ g_io_channel_set_close_on_unref(io, FALSE);
+
if (!bt_att_set_mtu(attr->att, mtu))
goto fail;
#ifdef __TIZEN_PATCH__
void g_attrib_channel_unref(GAttrib *attrib);
+struct bt_att *g_attrib_get_att(GAttrib *attrib);
#endif
gboolean g_attrib_set_destroy_function(GAttrib *attrib,
#endif
#include <errno.h>
-#include <glib.h>
#include <stdlib.h>
#include <unistd.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/hci_lib.h>
+#include <glib.h>
-#include "src/shared/util.h"
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+#include "lib/sdp.h"
#include "lib/uuid.h"
+
+#include "src/shared/util.h"
#include "att.h"
#include "btio/btio.h"
#include "gattrib.h"
#include <readline/readline.h>
#include <readline/history.h>
-#include "src/shared/util.h"
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
#include "lib/uuid.h"
+
+#include "src/shared/util.h"
#include "btio/btio.h"
#include "att.h"
#include "gattrib.h"
#endif
#include <stdlib.h>
-#include <glib.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/hci_lib.h>
+#include <glib.h>
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+#include "lib/sdp.h"
#include "lib/uuid.h"
+
#include "btio/btio.h"
#include "att.h"
#include "gattrib.h"
<annotation name="com.tizen.smack" value="obexd::public" />
<annotation name="com.tizen.smack" value="bt_agent::public" />
</method>
+ <method name="RegisterProfile2">
+ <annotation name="com.tizen.smack" value="bt-service::platform" />
+ </method>
</interface>
</node>
</dbus>
#include <sys/types.h>
#include <sys/socket.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/l2cap.h>
-#include <bluetooth/rfcomm.h>
-#include <bluetooth/sco.h>
-
#include <glib.h>
+#include "lib/bluetooth.h"
+#include "lib/l2cap.h"
+#include "lib/rfcomm.h"
+#include "lib/sco.h"
+
#include "btio.h"
#ifndef BT_FLUSHABLE
#include <stdio.h>
#include <stdlib.h>
#include <readline/readline.h>
-#include <gdbus.h>
+#include "gdbus/gdbus.h"
#include "display.h"
#include "agent.h"
{
const char *device, *uuid;
char *str;
- int fd;
rl_printf("Authorize service\n");
dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &device,
- DBUS_TYPE_STRING, &uuid,
- DBUS_TYPE_UNIX_FD, &fd, DBUS_TYPE_INVALID);
+ DBUS_TYPE_STRING, &uuid, DBUS_TYPE_INVALID);
str = g_strdup_printf("Authorize service %s (yes/no): ", uuid);
agent_prompt(str);
GDBUS_ARGS({ "device", "o" }),
NULL, request_authorization) },
{ GDBUS_ASYNC_METHOD("AuthorizeService",
- GDBUS_ARGS({ "device", "o" }, { "uuid", "s" },
- {"fd", "h"}),
+ GDBUS_ARGS({ "device", "o" }, { "uuid", "s" }),
NULL, authorize_service) },
{ GDBUS_METHOD("Cancel", NULL, NULL, cancel_request) },
{ }
#include <stdlib.h>
#include <stdarg.h>
#include <stdbool.h>
+#include <ctype.h>
#include <readline/readline.h>
#include "display.h"
free(saved_line);
}
}
+
+void rl_hexdump(const unsigned char *buf, size_t len)
+{
+ static const char hexdigits[] = "0123456789abcdef";
+ char str[68];
+ size_t i;
+
+ if (!len)
+ return;
+
+ str[0] = ' ';
+
+ for (i = 0; i < len; i++) {
+ str[((i % 16) * 3) + 1] = ' ';
+ str[((i % 16) * 3) + 2] = hexdigits[buf[i] >> 4];
+ str[((i % 16) * 3) + 3] = hexdigits[buf[i] & 0xf];
+ str[(i % 16) + 51] = isprint(buf[i]) ? buf[i] : '.';
+
+ if ((i + 1) % 16 == 0) {
+ str[49] = ' ';
+ str[50] = ' ';
+ str[67] = '\0';
+ rl_printf("%s\n", str);
+ str[0] = ' ';
+ }
+ }
+
+ if (i % 16 > 0) {
+ size_t j;
+ for (j = (i % 16); j < 16; j++) {
+ str[(j * 3) + 1] = ' ';
+ str[(j * 3) + 2] = ' ';
+ str[(j * 3) + 3] = ' ';
+ str[j + 51] = ' ';
+ }
+ str[49] = ' ';
+ str[50] = ' ';
+ str[67] = '\0';
+ rl_printf("%s\n", str);
+ }
+}
#define COLOR_BOLDWHITE "\x1B[1;37m"
void rl_printf(const char *fmt, ...) __attribute__((format(printf, 1, 2)));
+void rl_hexdump(const unsigned char *buf, size_t len);
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2014 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 <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <sys/uio.h>
+
+#include <readline/readline.h>
+#include <readline/history.h>
+#include <glib.h>
+
+#include "gdbus/gdbus.h"
+#include "monitor/uuid.h"
+#include "display.h"
+#include "gatt.h"
+
+/* String display constants */
+#define COLORED_NEW COLOR_GREEN "NEW" COLOR_OFF
+#define COLORED_CHG COLOR_YELLOW "CHG" COLOR_OFF
+#define COLORED_DEL COLOR_RED "DEL" COLOR_OFF
+
+static GList *services;
+static GList *characteristics;
+static GList *descriptors;
+
+static void print_service(GDBusProxy *proxy, const char *description)
+{
+ DBusMessageIter iter;
+ const char *uuid, *text;
+ dbus_bool_t primary;
+
+ if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE)
+ return;
+
+ dbus_message_iter_get_basic(&iter, &uuid);
+
+ if (g_dbus_proxy_get_property(proxy, "Primary", &iter) == FALSE)
+ return;
+
+ dbus_message_iter_get_basic(&iter, &primary);
+
+ text = uuidstr_to_str(uuid);
+ if (!text)
+ text = uuid;
+
+ rl_printf("%s%s%sService %s %s %s\n",
+ description ? "[" : "",
+ description ? : "",
+ description ? "] " : "",
+ g_dbus_proxy_get_path(proxy),
+ text, primary ? "(Primary)" : "(Secondary)");
+}
+
+void gatt_add_service(GDBusProxy *proxy)
+{
+ services = g_list_append(services, proxy);
+
+ print_service(proxy, COLORED_NEW);
+}
+
+void gatt_remove_service(GDBusProxy *proxy)
+{
+ services = g_list_remove(services, proxy);
+
+ print_service(proxy, COLORED_DEL);
+}
+
+static void print_characteristic(GDBusProxy *proxy, const char *description)
+{
+ DBusMessageIter iter;
+ const char *uuid, *text;
+
+ if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE)
+ return;
+
+ dbus_message_iter_get_basic(&iter, &uuid);
+
+ text = uuidstr_to_str(uuid);
+ if (!text)
+ text = uuid;
+
+ rl_printf("%s%s%sCharacteristic %s %s\n",
+ description ? "[" : "",
+ description ? : "",
+ description ? "] " : "",
+ g_dbus_proxy_get_path(proxy),
+ text);
+}
+
+static gboolean characteristic_is_child(GDBusProxy *characteristic)
+{
+ GList *l;
+ DBusMessageIter iter;
+ const char *service, *path;
+
+ if (!g_dbus_proxy_get_property(characteristic, "Service", &iter))
+ return FALSE;
+
+ dbus_message_iter_get_basic(&iter, &service);
+
+ for (l = services; l; l = g_list_next(l)) {
+ GDBusProxy *proxy = l->data;
+
+ path = g_dbus_proxy_get_path(proxy);
+
+ if (!strcmp(path, service))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+void gatt_add_characteristic(GDBusProxy *proxy)
+{
+ if (!characteristic_is_child(proxy))
+ return;
+
+ characteristics = g_list_append(characteristics, proxy);
+
+ print_characteristic(proxy, COLORED_NEW);
+}
+
+void gatt_remove_characteristic(GDBusProxy *proxy)
+{
+ if (!characteristic_is_child(proxy))
+ return;
+
+ characteristics = g_list_remove(characteristics, proxy);
+
+ print_characteristic(proxy, COLORED_DEL);
+}
+
+static void print_descriptor(GDBusProxy *proxy, const char *description)
+{
+ DBusMessageIter iter;
+ const char *uuid, *text;
+
+ if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE)
+ return;
+
+ dbus_message_iter_get_basic(&iter, &uuid);
+
+ text = uuidstr_to_str(uuid);
+ if (!text)
+ text = uuid;
+
+ rl_printf("%s%s%sDescriptor %s %s\n",
+ description ? "[" : "",
+ description ? : "",
+ description ? "] " : "",
+ g_dbus_proxy_get_path(proxy),
+ text);
+}
+
+static gboolean descriptor_is_child(GDBusProxy *characteristic)
+{
+ GList *l;
+ DBusMessageIter iter;
+ const char *service, *path;
+
+ if (!g_dbus_proxy_get_property(characteristic, "Characteristic", &iter))
+ return FALSE;
+
+ dbus_message_iter_get_basic(&iter, &service);
+
+ for (l = characteristics; l; l = g_list_next(l)) {
+ GDBusProxy *proxy = l->data;
+
+ path = g_dbus_proxy_get_path(proxy);
+
+ if (!strcmp(path, service))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+void gatt_add_descriptor(GDBusProxy *proxy)
+{
+ if (!descriptor_is_child(proxy))
+ return;
+
+ descriptors = g_list_append(descriptors, proxy);
+
+ print_descriptor(proxy, COLORED_NEW);
+}
+
+void gatt_remove_descriptor(GDBusProxy *proxy)
+{
+ if (!descriptor_is_child(proxy))
+ return;
+
+ descriptors = g_list_remove(descriptors, proxy);
+
+ print_descriptor(proxy, COLORED_DEL);
+}
+
+static void list_attributes(const char *path, GList *source)
+{
+ GList *l;
+
+ for (l = source; l; l = g_list_next(l)) {
+ GDBusProxy *proxy = l->data;
+ const char *proxy_path;
+
+ proxy_path = g_dbus_proxy_get_path(proxy);
+
+ if (!g_str_has_prefix(proxy_path, path))
+ continue;
+
+ if (source == services) {
+ print_service(proxy, NULL);
+ list_attributes(proxy_path, characteristics);
+ } else if (source == characteristics) {
+ print_characteristic(proxy, NULL);
+ list_attributes(proxy_path, descriptors);
+ } else if (source == descriptors)
+ print_descriptor(proxy, NULL);
+ }
+}
+
+void gatt_list_attributes(const char *path)
+{
+ list_attributes(path, services);
+}
+
+static GDBusProxy *select_proxy(const char *path, GList *source)
+{
+ GList *l;
+
+ for (l = source; l; l = g_list_next(l)) {
+ GDBusProxy *proxy = l->data;
+
+ if (strcmp(path, g_dbus_proxy_get_path(proxy)) == 0)
+ return proxy;
+ }
+
+ return NULL;
+}
+
+GDBusProxy *gatt_select_attribute(const char *path)
+{
+ GDBusProxy *proxy;
+
+ proxy = select_proxy(path, services);
+ if (proxy)
+ return proxy;
+
+ proxy = select_proxy(path, characteristics);
+ if (proxy)
+ return proxy;
+
+ return select_proxy(path, descriptors);
+}
+
+static char *attribute_generator(const char *text, int state, GList *source)
+{
+ static int index, len;
+ GList *list;
+
+ if (!state) {
+ index = 0;
+ len = strlen(text);
+ }
+
+ for (list = g_list_nth(source, index); list;
+ list = g_list_next(list)) {
+ GDBusProxy *proxy = list->data;
+ const char *path;
+
+ index++;
+
+ path = g_dbus_proxy_get_path(proxy);
+
+ if (!strncmp(path, text, len))
+ return strdup(path);
+ }
+
+ return NULL;
+}
+
+char *gatt_attribute_generator(const char *text, int state)
+{
+ static GList *list = NULL;
+
+ if (!state) {
+ GList *list1;
+
+ if (list) {
+ g_list_free(list);
+ list = NULL;
+ }
+
+ list1 = g_list_copy(characteristics);
+ list1 = g_list_concat(list1, g_list_copy(descriptors));
+
+ list = g_list_copy(services);
+ list = g_list_concat(list, list1);
+ }
+
+ return attribute_generator(text, state, list);
+}
+
+static void read_reply(DBusMessage *message, void *user_data)
+{
+ DBusError error;
+ DBusMessageIter iter, array;
+ uint8_t *value;
+ int len;
+
+ dbus_error_init(&error);
+
+ if (dbus_set_error_from_message(&error, message) == TRUE) {
+ rl_printf("Failed to read: %s\n", error.name);
+ dbus_error_free(&error);
+ return;
+ }
+
+ dbus_message_iter_init(message, &iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+ rl_printf("Invalid response to read\n");
+ return;
+ }
+
+ dbus_message_iter_recurse(&iter, &array);
+ dbus_message_iter_get_fixed_array(&array, &value, &len);
+
+ if (len < 0) {
+ rl_printf("Unable to parse value\n");
+ return;
+ }
+
+ rl_hexdump(value, len);
+}
+
+static void read_attribute(GDBusProxy *proxy)
+{
+ if (g_dbus_proxy_method_call(proxy, "ReadValue", NULL, read_reply,
+ NULL, NULL) == FALSE) {
+ rl_printf("Failed to read\n");
+ return;
+ }
+
+ rl_printf("Attempting to read %s\n", g_dbus_proxy_get_path(proxy));
+}
+
+void gatt_read_attribute(GDBusProxy *proxy)
+{
+ const char *iface;
+
+ iface = g_dbus_proxy_get_interface(proxy);
+ if (!strcmp(iface, "org.bluez.GattCharacteristic1") ||
+ !strcmp(iface, "org.bluez.GattDescriptor1")) {
+ read_attribute(proxy);
+ return;
+ }
+
+ rl_printf("Unable to read attribute %s\n",
+ g_dbus_proxy_get_path(proxy));
+}
+
+static void write_reply(DBusMessage *message, void *user_data)
+{
+ DBusError error;
+
+ dbus_error_init(&error);
+
+ if (dbus_set_error_from_message(&error, message) == TRUE) {
+ rl_printf("Failed to write: %s\n", error.name);
+ dbus_error_free(&error);
+ return;
+ }
+}
+
+static void write_setup(DBusMessageIter *iter, void *user_data)
+{
+ struct iovec *iov = user_data;
+ DBusMessageIter array;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array);
+ dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
+ &iov->iov_base, iov->iov_len);
+ dbus_message_iter_close_container(iter, &array);
+}
+
+static void write_attribute(GDBusProxy *proxy, char *arg)
+{
+ struct iovec iov;
+ uint8_t value[512];
+ char *entry;
+ int i;
+
+ for (i = 0; (entry = strsep(&arg, " \t")) != NULL; i++) {
+ long int val;
+ char *endptr = NULL;
+
+ if (*entry == '\0')
+ continue;
+
+ if (i > 512) {
+ rl_printf("Too much data\n");
+ return;
+ }
+
+ val = strtol(entry, &endptr, 0);
+ if (!endptr || *endptr != '\0' || val > UINT8_MAX) {
+ rl_printf("Invalid value at index %d\n", i);
+ return;
+ }
+
+ value[i] = val;
+ }
+
+ iov.iov_base = value;
+ iov.iov_len = i;
+
+ if (g_dbus_proxy_method_call(proxy, "WriteValue", write_setup,
+ write_reply, &iov, NULL) == FALSE) {
+ rl_printf("Failed to write\n");
+ return;
+ }
+
+ rl_printf("Attempting to write %s\n", g_dbus_proxy_get_path(proxy));
+}
+
+void gatt_write_attribute(GDBusProxy *proxy, const char *arg)
+{
+ const char *iface;
+
+ iface = g_dbus_proxy_get_interface(proxy);
+ if (!strcmp(iface, "org.bluez.GattCharacteristic1") ||
+ !strcmp(iface, "org.bluez.GattDescriptor1")) {
+ write_attribute(proxy, (char *) arg);
+ return;
+ }
+
+ rl_printf("Unable to write attribute %s\n",
+ g_dbus_proxy_get_path(proxy));
+}
+
+static void notify_reply(DBusMessage *message, void *user_data)
+{
+ bool enable = GPOINTER_TO_UINT(user_data);
+ DBusError error;
+
+ dbus_error_init(&error);
+
+ if (dbus_set_error_from_message(&error, message) == TRUE) {
+ rl_printf("Failed to %s notify: %s\n",
+ enable ? "start" : "stop", error.name);
+ dbus_error_free(&error);
+ return;
+ }
+
+ rl_printf("Notify %s\n", enable == TRUE ? "started" : "stopped");
+}
+
+static void notify_attribute(GDBusProxy *proxy, bool enable)
+{
+ const char *method;
+
+ if (enable == TRUE)
+ method = "StartNotify";
+ else
+ method = "StopNotify";
+
+ if (g_dbus_proxy_method_call(proxy, method, NULL, notify_reply,
+ GUINT_TO_POINTER(enable), NULL) == FALSE) {
+ rl_printf("Failed to %s notify\n", enable ? "start" : "stop");
+ return;
+ }
+}
+
+void gatt_notify_attribute(GDBusProxy *proxy, bool enable)
+{
+ const char *iface;
+
+ iface = g_dbus_proxy_get_interface(proxy);
+ if (!strcmp(iface, "org.bluez.GattCharacteristic1")) {
+ notify_attribute(proxy, enable);
+ return;
+ }
+
+ rl_printf("Unable to notify attribute %s\n",
+ g_dbus_proxy_get_path(proxy));
+}
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2014 Intel Corporation. All rights reserved.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+void gatt_add_service(GDBusProxy *proxy);
+void gatt_remove_service(GDBusProxy *proxy);
+
+void gatt_add_characteristic(GDBusProxy *proxy);
+void gatt_remove_characteristic(GDBusProxy *proxy);
+
+void gatt_add_descriptor(GDBusProxy *proxy);
+void gatt_remove_descriptor(GDBusProxy *proxy);
+
+void gatt_list_attributes(const char *device);
+GDBusProxy *gatt_select_attribute(const char *path);
+char *gatt_attribute_generator(const char *text, int state);
+
+void gatt_read_attribute(GDBusProxy *proxy);
+void gatt_write_attribute(GDBusProxy *proxy, const char *arg);
+void gatt_notify_attribute(GDBusProxy *proxy, bool enable);
#include <readline/readline.h>
#include <readline/history.h>
#include <glib.h>
-#include <gdbus.h>
+#include "gdbus/gdbus.h"
#include "monitor/uuid.h"
#include "agent.h"
#include "display.h"
+#include "gatt.h"
/* String display constants */
#define COLORED_NEW COLOR_GREEN "NEW" COLOR_OFF
static char *auto_register_agent = NULL;
static GDBusProxy *default_ctrl;
+static GDBusProxy *default_dev;
+static GDBusProxy *default_attr;
static GList *ctrl_list;
static GList *dev_list;
dbus_uint32_t valu32;
dbus_uint16_t valu16;
dbus_int16_t vals16;
+ unsigned char byte;
const char *valstr;
DBusMessageIter subiter;
- int type;
if (iter == NULL) {
rl_printf("%s%s is nil\n", label, name);
dbus_message_iter_get_basic(iter, &vals16);
rl_printf("%s%s: %d\n", label, name, vals16);
break;
+ case DBUS_TYPE_BYTE:
+ dbus_message_iter_get_basic(iter, &byte);
+ rl_printf("%s%s: 0x%02x\n", label, name, byte);
+ break;
+ case DBUS_TYPE_VARIANT:
+ dbus_message_iter_recurse(iter, &subiter);
+ print_iter(label, name, &subiter);
+ break;
case DBUS_TYPE_ARRAY:
dbus_message_iter_recurse(iter, &subiter);
- rl_printf("%s%s:\n", label, name);
-
- do {
- type = dbus_message_iter_get_arg_type(&subiter);
- if (type == DBUS_TYPE_INVALID)
- break;
-
- if (type == DBUS_TYPE_STRING) {
- dbus_message_iter_get_basic(&subiter, &valstr);
- rl_printf("\t%s\n", valstr);
- }
-
+ while (dbus_message_iter_get_arg_type(&subiter) !=
+ DBUS_TYPE_INVALID) {
+ print_iter(label, name, &subiter);
dbus_message_iter_next(&subiter);
- } while(true);
-
+ }
+ break;
+ case DBUS_TYPE_DICT_ENTRY:
+ dbus_message_iter_recurse(iter, &subiter);
+ dbus_message_iter_get_basic(&subiter, &valstr);
+ dbus_message_iter_next(&subiter);
+ print_iter(label, valstr, &subiter);
break;
default:
rl_printf("%s%s has unsupported type\n", label, name);
return FALSE;
}
+static gboolean service_is_child(GDBusProxy *service)
+{
+ GList *l;
+ DBusMessageIter iter;
+ const char *device, *path;
+
+ if (g_dbus_proxy_get_property(service, "Device", &iter) == FALSE)
+ return FALSE;
+
+ dbus_message_iter_get_basic(&iter, &device);
+
+ for (l = dev_list; l; l = g_list_next(l)) {
+ GDBusProxy *proxy = l->data;
+
+ path = g_dbus_proxy_get_path(proxy);
+
+ if (!strcmp(path, device))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
static void proxy_added(GDBusProxy *proxy, void *user_data)
{
const char *interface;
agent_register(dbus_conn, agent_manager,
auto_register_agent);
}
+ } else if (!strcmp(interface, "org.bluez.GattService1")) {
+ if (service_is_child(proxy))
+ gatt_add_service(proxy);
+ } else if (!strcmp(interface, "org.bluez.GattCharacteristic1")) {
+ gatt_add_characteristic(proxy);
+ } else if (!strcmp(interface, "org.bluez.GattDescriptor1")) {
+ gatt_add_descriptor(proxy);
}
}
+static void set_default_device(GDBusProxy *proxy, const char *attribute)
+{
+ char *desc = NULL;
+ DBusMessageIter iter;
+ const char *path;
+
+ default_dev = proxy;
+
+ if (proxy == NULL) {
+ default_attr = NULL;
+ goto done;
+ }
+
+ if (!g_dbus_proxy_get_property(proxy, "Alias", &iter)) {
+ if (!g_dbus_proxy_get_property(proxy, "Address", &iter))
+ goto done;
+ }
+
+ path = g_dbus_proxy_get_path(proxy);
+
+ dbus_message_iter_get_basic(&iter, &desc);
+ desc = g_strdup_printf(COLOR_BLUE "[%s%s%s]" COLOR_OFF "# ", desc,
+ attribute ? ":" : "",
+ attribute ? attribute + strlen(path) : "");
+
+done:
+ rl_set_prompt(desc ? desc : PROMPT_ON);
+ rl_redisplay();
+ g_free(desc);
+}
+
+static void set_default_attribute(GDBusProxy *proxy)
+{
+ const char *path;
+
+ default_attr = proxy;
+
+ path = g_dbus_proxy_get_path(proxy);
+
+ set_default_device(default_dev, path);
+}
+
static void proxy_removed(GDBusProxy *proxy, void *user_data)
{
const char *interface;
dev_list = g_list_remove(dev_list, proxy);
print_device(proxy, COLORED_DEL);
+
+ if (default_dev == proxy)
+ set_default_device(NULL, NULL);
}
} else if (!strcmp(interface, "org.bluez.Adapter1")) {
ctrl_list = g_list_remove(ctrl_list, proxy);
if (default_ctrl == proxy) {
default_ctrl = NULL;
+ set_default_device(NULL, NULL);
g_list_free(dev_list);
dev_list = NULL;
if (auto_register_agent)
agent_unregister(dbus_conn, NULL);
}
+ } else if (!strcmp(interface, "org.bluez.GattService1")) {
+ if (service_is_child(proxy)) {
+ gatt_remove_service(proxy);
+
+ if (default_attr == proxy)
+ set_default_attribute(NULL);
+ }
+ } else if (!strcmp(interface, "org.bluez.GattCharacteristic1")) {
+ gatt_remove_characteristic(proxy);
+
+ if (default_attr == proxy)
+ set_default_attribute(NULL);
+ } else if (!strcmp(interface, "org.bluez.GattDescriptor1")) {
+ gatt_remove_descriptor(proxy);
+
+ if (default_attr == proxy)
+ set_default_attribute(NULL);
}
}
} else
str = g_strdup("");
+ if (strcmp(name, "Connected") == 0) {
+ dbus_bool_t connected;
+
+ dbus_message_iter_get_basic(iter, &connected);
+
+ if (connected && default_dev == NULL)
+ set_default_device(proxy, NULL);
+ else if (!connected && default_dev == proxy)
+ set_default_device(NULL, NULL);
+ }
+
print_iter(str, name, iter);
g_free(str);
}
print_iter(str, name, iter);
g_free(str);
+ } else if (proxy == default_attr) {
+ char *str;
+
+ str = g_strdup_printf("[" COLORED_CHG "] Attribute %s ",
+ g_dbus_proxy_get_path(proxy));
+
+ print_iter(str, name, iter);
+ g_free(str);
}
}
}
}
-static void cmd_info(const char *arg)
+static struct GDBusProxy *find_device(const char *arg)
{
GDBusProxy *proxy;
- DBusMessageIter iter;
- const char *address;
if (!arg || !strlen(arg)) {
+ if (default_dev)
+ return default_dev;
rl_printf("Missing device address argument\n");
- return;
+ return NULL;
}
proxy = find_proxy_by_address(dev_list, arg);
if (!proxy) {
rl_printf("Device %s not available\n", arg);
- return;
+ return NULL;
}
+ return proxy;
+}
+
+static void cmd_info(const char *arg)
+{
+ GDBusProxy *proxy;
+ DBusMessageIter iter;
+ const char *address;
+
+ proxy = find_device(arg);
+ if (!proxy)
+ return;
+
if (g_dbus_proxy_get_property(proxy, "Address", &iter) == FALSE)
return;
{
GDBusProxy *proxy;
- if (!arg || !strlen(arg)) {
- rl_printf("Missing device address argument\n");
+ proxy = find_device(arg);
+ if (!proxy)
return;
- }
-
- proxy = find_proxy_by_address(dev_list, arg);
- if (!proxy) {
- rl_printf("Device %s not available\n", arg);
- return;
- }
if (g_dbus_proxy_method_call(proxy, "Pair", NULL, pair_reply,
NULL, NULL) == FALSE) {
dbus_bool_t trusted;
char *str;
- if (!arg || !strlen(arg)) {
- rl_printf("Missing device address argument\n");
- return;
- }
-
- proxy = find_proxy_by_address(dev_list, arg);
- if (!proxy) {
- rl_printf("Device %s not available\n", arg);
+ proxy = find_device(arg);
+ if (!proxy)
return;
- }
trusted = TRUE;
dbus_bool_t trusted;
char *str;
- if (!arg || !strlen(arg)) {
- rl_printf("Missing device address argument\n");
+ proxy = find_device(arg);
+ if (!proxy)
return;
- }
-
- proxy = find_proxy_by_address(dev_list, arg);
- if (!proxy) {
- rl_printf("Device %s not available\n", arg);
- return;
- }
trusted = FALSE;
dbus_bool_t blocked;
char *str;
- if (!arg || !strlen(arg)) {
- rl_printf("Missing device address argument\n");
- return;
- }
-
- proxy = find_proxy_by_address(dev_list, arg);
- if (!proxy) {
- rl_printf("Device %s not available\n", arg);
+ proxy = find_device(arg);
+ if (!proxy)
return;
- }
blocked = TRUE;
dbus_bool_t blocked;
char *str;
- if (!arg || !strlen(arg)) {
- rl_printf("Missing device address argument\n");
+ proxy = find_device(arg);
+ if (!proxy)
return;
- }
-
- proxy = find_proxy_by_address(dev_list, arg);
- if (!proxy) {
- rl_printf("Device %s not available\n", arg);
- return;
- }
blocked = FALSE;
static void connect_reply(DBusMessage *message, void *user_data)
{
+ GDBusProxy *proxy = user_data;
DBusError error;
dbus_error_init(&error);
}
rl_printf("Connection successful\n");
+
+ set_default_device(proxy, NULL);
}
static void cmd_connect(const char *arg)
}
if (g_dbus_proxy_method_call(proxy, "Connect", NULL, connect_reply,
- NULL, NULL) == FALSE) {
+ proxy, NULL) == FALSE) {
rl_printf("Failed to connect\n");
return;
}
static void disconn_reply(DBusMessage *message, void *user_data)
{
+ GDBusProxy *proxy = user_data;
DBusError error;
dbus_error_init(&error);
}
rl_printf("Successful disconnected\n");
+
+ if (proxy != default_dev)
+ return;
+
+ set_default_device(NULL, NULL);
}
static void cmd_disconn(const char *arg)
{
GDBusProxy *proxy;
+ proxy = find_device(arg);
+ if (!proxy)
+ return;
+
+ if (g_dbus_proxy_method_call(proxy, "Disconnect", NULL, disconn_reply,
+ proxy, NULL) == FALSE) {
+ rl_printf("Failed to disconnect\n");
+ return;
+ }
+
+ rl_printf("Attempting to disconnect from %s\n", arg);
+}
+
+static void cmd_list_attributes(const char *arg)
+{
+ GDBusProxy *proxy;
+
+ proxy = find_device(arg);
+ if (!proxy)
+ return;
+
+ gatt_list_attributes(g_dbus_proxy_get_path(proxy));
+}
+
+static void cmd_select_attribute(const char *arg)
+{
+ GDBusProxy *proxy;
+
if (!arg || !strlen(arg)) {
- rl_printf("Missing device address argument\n");
+ rl_printf("Missing attribute argument\n");
return;
}
- proxy = find_proxy_by_address(dev_list, arg);
+ if (!default_dev) {
+ rl_printf("No device connected\n");
+ return;
+ }
+
+ proxy = gatt_select_attribute(arg);
+ if (proxy)
+ set_default_attribute(proxy);
+}
+
+static struct GDBusProxy *find_attribute(const char *arg)
+{
+ GDBusProxy *proxy;
+
+ if (!arg || !strlen(arg)) {
+ if (default_attr)
+ return default_attr;
+ rl_printf("Missing attribute argument\n");
+ return NULL;
+ }
+
+ proxy = gatt_select_attribute(arg);
if (!proxy) {
- rl_printf("Device %s not available\n", arg);
+ rl_printf("Attribute %s not available\n", arg);
+ return NULL;
+ }
+
+ return proxy;
+}
+
+static void cmd_attribute_info(const char *arg)
+{
+ GDBusProxy *proxy;
+ DBusMessageIter iter;
+ const char *iface, *uuid, *text;
+
+ proxy = find_attribute(arg);
+ if (!proxy)
return;
+
+ if (g_dbus_proxy_get_property(proxy, "UUID", &iter) == FALSE)
+ return;
+
+ dbus_message_iter_get_basic(&iter, &uuid);
+
+ text = uuidstr_to_str(uuid);
+ if (!text)
+ text = g_dbus_proxy_get_path(proxy);
+
+ iface = g_dbus_proxy_get_interface(proxy);
+ if (!strcmp(iface, "org.bluez.GattService1")) {
+ rl_printf("Service - %s\n", text);
+
+ print_property(proxy, "UUID");
+ print_property(proxy, "Primary");
+ print_property(proxy, "Characteristics");
+ print_property(proxy, "Includes");
+ } else if (!strcmp(iface, "org.bluez.GattCharacteristic1")) {
+ rl_printf("Characteristic - %s\n", text);
+
+ print_property(proxy, "UUID");
+ print_property(proxy, "Service");
+ print_property(proxy, "Value");
+ print_property(proxy, "Notifying");
+ print_property(proxy, "Flags");
+ print_property(proxy, "Descriptors");
+ } else if (!strcmp(iface, "org.bluez.GattDescriptor1")) {
+ rl_printf("Descriptor - %s\n", text);
+
+ print_property(proxy, "UUID");
+ print_property(proxy, "Characteristic");
+ print_property(proxy, "Value");
}
+}
- if (g_dbus_proxy_method_call(proxy, "Disconnect", NULL, disconn_reply,
- NULL, NULL) == FALSE) {
- rl_printf("Failed to disconnect\n");
+static void cmd_read(const char *arg)
+{
+ if (!default_attr) {
+ rl_printf("No attribute selected\n");
return;
}
- rl_printf("Attempting to disconnect from %s\n", arg);
+ gatt_read_attribute(default_attr);
+}
+
+static void cmd_write(const char *arg)
+{
+ if (!arg || !strlen(arg)) {
+ rl_printf("Missing data argument\n");
+ return;
+ }
+
+ if (!default_attr) {
+ rl_printf("No attribute selected\n");
+ return;
+ }
+
+ gatt_write_attribute(default_attr, arg);
+}
+
+static void cmd_notify(const char *arg)
+{
+ dbus_bool_t enable;
+
+ if (parse_argument_on_off(arg, &enable) == FALSE)
+ return;
+
+ if (!default_attr) {
+ rl_printf("No attribute selected\n");
+ return;
+ }
+
+ gatt_notify_attribute(default_attr, enable ? true : false);
}
static void cmd_version(const char *arg)
return generic_generator(text, state, dev_list, "Address");
}
+static char *attribute_generator(const char *text, int state)
+{
+ return gatt_attribute_generator(text, state);
+}
+
static char *capability_generator(const char *text, int state)
{
static int index, len;
{ "default-agent",NULL, cmd_default_agent,
"Set agent as the default one" },
{ "scan", "<on/off>", cmd_scan, "Scan for devices" },
- { "info", "<dev>", cmd_info, "Device information",
+ { "info", "[dev]", cmd_info, "Device information",
dev_generator },
- { "pair", "<dev>", cmd_pair, "Pair with device",
+ { "pair", "[dev]", cmd_pair, "Pair with device",
dev_generator },
- { "trust", "<dev>", cmd_trust, "Trust device",
+ { "trust", "[dev]", cmd_trust, "Trust device",
dev_generator },
- { "untrust", "<dev>", cmd_untrust, "Untrust device",
+ { "untrust", "[dev]", cmd_untrust, "Untrust device",
dev_generator },
- { "block", "<dev>", cmd_block, "Block device",
+ { "block", "[dev]", cmd_block, "Block device",
dev_generator },
- { "unblock", "<dev>", cmd_unblock, "Unblock device",
+ { "unblock", "[dev]", cmd_unblock, "Unblock device",
dev_generator },
{ "remove", "<dev>", cmd_remove, "Remove device",
dev_generator },
{ "connect", "<dev>", cmd_connect, "Connect device",
dev_generator },
- { "disconnect", "<dev>", cmd_disconn, "Disconnect device",
+ { "disconnect", "[dev]", cmd_disconn, "Disconnect device",
+ dev_generator },
+ { "list-attributes", "[dev]", cmd_list_attributes, "List attributes",
dev_generator },
+ { "select-attribute", "<attribute>", cmd_select_attribute,
+ "Select attribute", attribute_generator },
+ { "attribute-info", "[attribute]", cmd_attribute_info,
+ "Select attribute", attribute_generator },
+ { "read", NULL, cmd_read, "Read attribute value" },
+ { "write", "<data=[xx xx ...]>", cmd_write,
+ "Write attribute value" },
+ { "notify", "<on/off>", cmd_notify, "Notify attribute value" },
{ "version", NULL, cmd_version, "Display version" },
{ "quit", NULL, cmd_quit, "Quit program" },
{ "exit", NULL, cmd_quit },
AC_PREREQ(2.60)
-AC_INIT(bluez, 5.27)
+AC_INIT(bluez, 5.28)
AM_INIT_AUTOMAKE([foreign subdir-objects color-tests silent-rules
tar-pax no-dist-gzip dist-xz])
AC_CHECK_LIB(dl, dlopen, dummy=yes,
AC_MSG_ERROR(dynamic linking loader is required))
+AC_CHECK_HEADERS(linux/types.h linux/if_alg.h)
+
PKG_CHECK_MODULES(GLIB, glib-2.0 >= 2.28, dummy=yes,
AC_MSG_ERROR(GLib >= 2.28 is required))
AC_SUBST(GLIB_CFLAGS)
AC_ARG_ENABLE(udev, AC_HELP_STRING([--disable-udev],
[disable udev device support]), [enable_udev=${enableval}])
if (test "${enable_tools}" != "no" && test "${enable_udev}" != "no"); then
- PKG_CHECK_MODULES(UDEV, libudev >= 143, dummy=yes,
- AC_MSG_ERROR(libudev >= 143 is required))
+ PKG_CHECK_MODULES(UDEV, libudev >= 172, dummy=yes,
+ AC_MSG_ERROR(libudev >= 172 is required))
AC_SUBST(UDEV_CFLAGS)
AC_SUBST(UDEV_LIBS)
AC_CHECK_LIB(udev, udev_hwdb_new,
AM_CONDITIONAL(MANPAGES, test "${enable_manpages}" = "yes")
AC_ARG_ENABLE(experimental, AC_HELP_STRING([--enable-experimental],
- [enable experimental plugins (SAP, NFC, ...)]),
+ [enable experimental plugins (NFC, ...)]),
[enable_experimental=${enableval}])
AM_CONDITIONAL(EXPERIMENTAL, test "${enable_experimental}" = "yes")
[enable wearable profile]), [enable_wearable=${enableval}])
AM_CONDITIONAL(WEARABLE, test "${enable_wearable}" = "yes")
-AC_ARG_ENABLE(usbbt, AC_HELP_STRING([--enable-usbbt],
- [enable usb bluetooth]), [enable_usbbt=${enableval}])
-AM_CONDITIONAL(USBBT, test "${enable_usbbt}" = "yes")
-
AC_ARG_ENABLE(autopair, AC_HELP_STRING([--enable-autopair],
[Enable Autopair Plugin]), [enable_autopair=${enableval}])
AM_CONDITIONAL(AUTOPAIR, test "${enable_autopair}" = "yes")
[Enable HID Plugin]), [enable_hid=${enableval}])
AM_CONDITIONAL(TIZEN_HID_PLUGIN, test "${enable_hid}" = "yes")
+AM_CONDITIONAL(TIZEN_HEALTH_PLUGIN, test "${enable_health}" = "yes")
+
AC_ARG_ENABLE(tizenunusedplugin, AC_HELP_STRING([--enable-tizenunusedplugin],
[Enable Unused Plugin]), [enable_tizenunusedplugin=${enableval}])
AM_CONDITIONAL(TIZEN_UNUSED_PLUGIN, test "${enable_tizenunusedplugin}" = "yes")
+
+AC_ARG_ENABLE(sap, AC_HELP_STRING([--enable-sap],
+ [Enable SAP Plugin]), [enable_sap=${enableval}])
+AM_CONDITIONAL(TIZEN_SAP_PLUGIN, test "${enable_sap}" = "yes")
+
# End of __TIZEN_PATCH__
AC_ARG_ENABLE(sixaxis, AC_HELP_STRING([--enable-sixaxis],
AM_CONDITIONAL(SIXAXIS, test "${enable_sixaxis}" = "yes" &&
test "${enable_udev}" != "no")
-AC_ARG_ENABLE(service, AC_HELP_STRING([--enable-service],
- [enable service plugin]), [enable_service=${enableval}])
-AM_CONDITIONAL(SERVICE, test "${enable_service}" = "yes")
-
if (test "${prefix}" = "NONE"); then
dnl no prefix and no localstatedir, so default to /var
if (test "$localstatedir" = '${prefix}/var'); then
Indicates that a device LE discovery procedure is active.
- boolean Advertising [readonly]
-
- Indicates that a advertising procedure is active.
-
string Version [readonly]
The Bluetooth version.
device. The UUID provided is the remote service
UUID for the profile.
- Possible errors: org.bluez.Error.DoesNotExist
- org.bluez.Error.AlreadyConnected
- org.bluez.Error.ConnectFailed
+ Possible errors: org.bluez.Error.Failed
+ org.bluez.Error.InProgress
+ org.bluez.Error.InvalidArguments
+ org.bluez.Error.NotAvailable
+ org.bluez.Error.NotReady
void DisconnectProfile(string uuid)
as long as the profile is registered this will always
succeed.
- Possible errors: org.bluez.Error.DoesNotExist
- org.bluez.Error.Failed
- org.bluez.Error.NotConnected
+ Possible errors: org.bluez.Error.Failed
+ org.bluez.Error.InProgress
+ org.bluez.Error.InvalidArguments
org.bluez.Error.NotSupported
#ifdef __TIZEN_PATCH__
Indicates if the remote device is paired.
+#ifdef __TIZEN_PATCH__
+ byte Connected [readonly]
+#else
boolean Connected [readonly]
+#endif
Indicates if the remote device is currently connected.
A PropertiesChanged signal indicate changes to this
BlueZ acts as a proxy, translating ATT operations to D-Bus method calls and
Properties (or the opposite). Support for D-Bus Object Manager is mandatory for
external services to allow seamless GATT declarations (Service, Characteristic
-and Descriptors) discovery.
+and Descriptors) discovery. Each GATT service tree is required to export a D-Bus
+Object Manager at its root that is solely responsible for the objects that
+belong to that service.
Releasing a registered GATT service is not defined yet. Any API extension
should avoid breaking the defined API, and if possible keep an unified GATT
Possible Errors: org.bluez.Error.Failed
org.bluez.Error.InProgress
- org.bluez.Error.ReadNotPermitted
+ org.bluez.Error.NotPermitted
org.bluez.Error.NotAuthorized
- org.bluez.Error.NotPaired
org.bluez.Error.NotSupported
void WriteValue(array{byte} value)
Possible Errors: org.bluez.Error.Failed
org.bluez.Error.InProgress
- org.bluez.Error.WriteNotPermitted
+ org.bluez.Error.NotPermitted
org.bluez.Error.InvalidValueLength
org.bluez.Error.NotAuthorized
- org.bluez.Error.NotPaired
org.bluez.Error.NotSupported
void StartNotify()
Possible Errors: org.bluez.Error.Failed
org.bluez.Error.InProgress
- org.bluez.Error.ReadNotPermitted
+ org.bluez.Error.NotPermitted
org.bluez.Error.NotAuthorized
- org.bluez.Error.NotPaired
org.bluez.Error.NotSupported
void WriteValue(array{byte} value)
Possible Errors: org.bluez.Error.Failed
org.bluez.Error.InProgress
- org.bluez.Error.WriteNotPermitted
+ org.bluez.Error.NotPermitted
org.bluez.Error.InvalidValueLength
org.bluez.Error.NotAuthorized
- org.bluez.Error.NotPaired
org.bluez.Error.NotSupported
Properties string UUID [read-only]
which a PropertiesChanged signal will be emitted.
-Service Manager hierarchy
-=============================
-
-Service Manager allows external applications to register GATT based
-services. Services must follow the API for Service and Characteristic
-described above.
-
-Local GATT services, characteristics and characteristic descriptors are
-discovered automatically using the D-Bus Object Manager interface.
+Profile hierarcy
+================
+
+Local profile (GATT client) instance. By registering this type of object
+an application effectively indicates support for a specific GATT profile
+and requests automatic connections to be established to devices
+supporting it.
+
+Service <application dependent>
+Interface org.bluez.GattProfile1 [Experimental]
+Object path <application dependent>
+
+Methods void Release()
+
+ This method gets called when the service daemon
+ unregisters the profile. The profile can use it to
+ do cleanup tasks. There is no need to unregister the
+ profile, because when this method gets called it has
+ already been unregistered.
+
+
+GATT Manager hierarchy
+======================
+
+GATT Manager allows external applications to register GATT services and
+profiles.
+
+Registering a profile allows applications to subscribe to *remote* services.
+These must implement the GattProfile1 interface defined above.
+
+Registering a service allows applications to publish a *local* GATT service,
+which then becomes available to remote devices. A GATT service is represented by
+a D-Bus object hierarchy where the root node corresponds to a service and the
+child nodes represent characteristics and descriptors that belong to that
+service. Each node must implement one of GattService1, GattCharacteristic1,
+or GattDescriptor1 interfaces described above, based on the attribute it
+represents. Each node must also implement the standard D-Bus Properties
+interface to expose their properties. These objects collectively represent a
+GATT service definition.
+
+To make service registration simple, BlueZ requires that all objects that belong
+to a GATT service be grouped under a D-Bus Object Manager that solely manages
+the objects of that service. Hence, the standard DBus.ObjectManager interface
+must be available on the root service path. An example application hierarchy
+containing two separate GATT services may look like this:
+
+-> /com/example
+ |
+ -> /com/example/service0
+ | | - org.freedesktop.DBus.ObjectManager
+ | | - org.freedesktop.DBus.Properties
+ | | - org.bluez.GattService1
+ | |
+ | -> /com/example/service0/char0
+ | | - org.freedesktop.DBus.Properties
+ | | - org.bluez.GattCharacteristic1
+ | |
+ | -> /com/example/service0/char1
+ | | - org.freedesktop.DBus.Properties
+ | | - org.bluez.GattCharacteristic1
+ | |
+ | -> /com/example/service0/char1/desc0
+ | - org.freedesktop.DBus.Properties
+ | - org.bluez.GattDescriptor1
+ |
+ -> /com/example/service1
+ | - org.freedesktop.DBus.ObjectManager
+ | - org.freedesktop.DBus.Properties
+ | - org.bluez.GattService1
+ |
+ -> /com/example/service1/char0
+ - org.freedesktop.DBus.Properties
+ - org.bluez.GattCharacteristic1
+
+When a service is registered, BlueZ will automatically obtain information about
+all objects using the service's Object Manager. Once a service has been
+registered, the objects of a service should not be removed. If BlueZ receives an
+InterfacesRemoved signal from a service's Object Manager, it will immediately
+unregister the service. Similarly, if the application disconnects from the bus,
+all of its registered services will be automatically unregistered.
+InterfacesAdded signals will be ignored.
Service org.bluez
Interface org.bluez.GattManager1 [Experimental]
-Object path /org/bluez
+Object path [variable prefix]/{hci0,hci1,...}
-Methods RegisterService(object service, dict options)
+Methods void RegisterService(object service, dict options)
- Registers remote application service exported under
- interface GattService1. Characteristic objects must
- be hierarchical to their service and must use the
- interface GattCharacteristic1. D-Bus Object Manager
- is used to fetch the exported objects.
+ Registers a local GATT service hierarchy as described
+ above.
"service" object path together with the D-Bus system
bus connection ID define the identification of the
Possible errors: org.bluez.Error.InvalidArguments
org.bluez.Error.AlreadyExists
- UnregisterService(object service)
+ void UnregisterService(object service)
This unregisters the service that has been
previously registered. The object path parameter
Possible errors: org.bluez.Error.InvalidArguments
org.bluez.Error.DoesNotExist
+ void RegisterProfile(object profile, array{string} UUIDs,
+ dict options)
+
+ Registers a GATT (client role) profile exported
+ under interface GattProfile1. The array of UUIDs
+ specifies the mandatory set of remote service
+ UUIDs that should all be available for the
+ remote device to match this profile. Matching
+ devices will be added to the auto-connection
+ list and connected whenever available.
+
+ Possible errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.AlreadyExists
+
+ void UnregisterProfile(object profile)
+
+ This unregisters the profile that has been
+ previously registered. The object path parameter
+ must match the same value that has been used
+ on registration.
+
+ Possible errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.DoesNotExist
#ifdef __TIZEN_PATCH__
GetService(string uuid)
--- /dev/null
+Maintainer guidelines
+*********************
+
+This document is intended for the maintainers of the BlueZ project. It
+serves as basic guidelines for handling patch review and commit access.
+
+
+Rule 1: Keep the GIT tree clean and linear
+==========================================
+
+The bluetooth.git, bluetooth-next.git and bluez.git trees are not your
+private playground. The history is meant to be clean and linear.
+
+ - NO merges
+ - NO branches
+ - NO tags
+
+If anyone needs testing or work on a feature, clone the tree and do
+it in your own copy. The master trees are off limits.
+
+One advise to avoid any accidental errors in this area to set proper
+options in global ~/.gitconfig or local .git/config files.
+
+ [merge]
+ ff = only
+
+Violations of this rule are not acceptable. This rule is enforced. If
+in doubt ask one of the seasoned maintainers.
+
+
+Rule 2: Enforce clean commit messages
+=====================================
+
+The commit messages are required to be clean and follow style guidelines
+to be consistent.
+
+Commit messages should adhere to a 72 characters by line limit. That
+makes it easy to read them via git log in a terminal window. Exceptions
+to this rule are logs, trace or other verbatim copied information.
+
+Every commit requires full names and email addresses. No synonyms or
+nicknames are allowed. It is also important that the Outlook style
+names with lastname, firstname are not allowed. It is the maintainers
+job to ensure we get proper firstname lastname <email> authorship.
+
+It is also important that the committer itself uses a valid name and
+email address when committing patches. So ensure that either the
+global ~/.gitconfig or local .git/config provides proper values.
+
+ [user]
+ name = Peter Mustermann
+ email = peter@mustermann.de
+
+Commit messages for bluez.git shall not contain Signed-off-by
+signatures. They are not used in userspace and with that it is the
+maintainers job to ensure they do not get committed to the repository.
+
+For bluetooth.git and bluetooth-next.git The Signed-off-by process is
+used and the signatures are required.
+
+Tags like Change-Id generated from Gerrit are never acceptable. It is
+the maintainers job to ensure that these are not committed into the
+repositories.
+
+Violations of this rule create a mess in the tree that can not be
+reversed. If in doubt ask one of the seasoned maintainers.
+
+
+Rule 3: Enforce correct coding style
+====================================
+
+The coding style follows roughly the kernel coding style with any
+exceptions documented in doc/coding-style.txt.
+
+To ensure trivial white-space errors don't get committed, have the
+following in your .gitconfig:
+
+ [apply]
+ whitespace = error
+
+It can also be helpful to use the checkpatch.pl script coming with the
+Linux kernel to do some automated checking. Adding the following to your
+.git/hooks/pre-commit and .git/hooks/pre-applypatch is a simple way to
+do this:
+
+ exec git diff --cached | ~/src/linux/scripts/checkpatch.pl -q \
+ --no-tree --no-signoff --show-types \
+ --ignore CAMELCASE,NEW_TYPEDEFS,INITIALISED_STATIC -
+
+The above assumes that a kernel tree resides in ~/src/linux/.
+
+
+Rule 4: Pay extra attention to adding new files to the tree
+===========================================================
+
+New files that are added to the tree require several things to be
+verified first:
+
+ - Check that the names are acceptible with other maintainers
+ - Ensure that the file modes are correct
+ - Verify that the license & copyright headers are correct
+ - If the file is supposed to be part of the release taraball,
+ make sure that it gets picked up by 'make dist' (particularly
+ important for documentation or other files that are not code)
+
+
+Rule 5: Keep the mailing list in sync with the commit process
+=============================================================
+
+When applying patches, be sure to send a response to the mailing list as
+soon as the code has been pushed to the upstream tree. Usually this
+means one email per patch, however patch-sets may only have one response
+covering the entire set. If applying a subset of a patch-set clearly
+state what was applied in your response.
Linux kernel v3.15 Version 1.5
Linux kernel v3.16 Version 1.6
Linux kernel v3.17 Version 1.7
-Linux kernel v3.19 Version 1.8 (not yet released)
+Linux kernel v3.19 Version 1.8
Version 1.1 introduces Set Device ID command.
then the configured static address will be used as identity
address.
+ In the case of a dual-mode controller with public address that
+ is configured as Low Energy only device (BR/EDR switched off),
+ the static address is used when set and public address otherwise.
+
If no short name is set the Short_Name parameter will be empty
(begin with a nul byte).
12 Debug Keys
13 Privacy
14 Controller Configuration
+ 15 Static Address
This command generates a Command Complete event on success or
a Command Status event on failure.
This command generates a Command Complete event on success
or failure.
- Possible errors: Connect Failed
+ Reject status is used when requested transport is not enabled.
+
+ Not Supported status is used if controller is not capable with
+ requested transport.
+
+ Possible errors: Rejected
+ Not Supported
+ Connect Failed
Busy
Invalid Parameters
Not Powered
will return P-192 versions of hash and randomizer as well as
P-256 versions of both.
+ Values returned by this command become invalid when the controller
+ is powered down. After each power-cycle it is required to call
+ this command again to get updated values.
+
This command generates a Command Complete event on success or
a Command Status event on failure.
Secure Connections is disabled, then of course this is the
same as not providing any data at all.
+ When providing data for remote LE devices, then the Hash_192 field
+ is used to provide the Security Manager TK Value. The Randomizer_192
+ field is not used and shall be set to zero. The Hash_192 value can
+ also be set to zero and that means that no Out Of Band data for
+ LE legacy pairing is provided.
+
+ The Hash_256 and Randomizer_256 fields can be used for LE secure
+ connections Out Of Band data. If only LE secure connections data
+ is provided the Hash_P192 and Randomizer_P192 fields can be set
+ to zero.
+
+ If Secure Connections Only mode has been enabled, then providing
+ Hash_P192 and Randomizer_P192 is not allowed. They are required
+ to be set to zero values.
+
This command can be used when the controller is not powered and
all settings will be programmed once powered.
0x0000 Disable Device ID
0x0001 Bluetooth SIG
- 0x0002 USB Implementerâ\80\99s Forum
+ 0x0002 USB Implementer?\99s Forum
The information are put into the EIR data. If the controller does
not support EIR or if SSP is disabled, this command will still
Command Code: 0x002B
Controller Index: <controller id>
Command Parameters: Address (6 Octets)
- Return Parameters:
+ Return Parameters: Current_Settings (4 Octets)
This command allows for setting the static random address. It is
only supported on controllers with LE support. The static random
to disable the static address.
When a controller has a public address (which is required for
- all dual-mode controllers), this address is not used. Only when
+ all dual-mode controllers), this address is not used. If a dual-mode
+ controller is configured as Low Energy only devices (BR/EDR has
+ been switched off), then the static address is used. Only when
the controller information reports BDADDR_ANY (00:00:00:00:00:00),
it is required to configure a static address first.
LE only without a public address, the static random address is
used as identity address.
+ The Static Address flag from the current settings can also be used
+ to determine if the configured static address is in use or not.
+
This command generates a Command Complete event on success or a
Command Status event on failure.
Invalid Index
+Read Local Out Of Band Extended Data Command
+============================================
+
+ Command Code: 0x003b
+ Controller Index: <controller id>
+ Command Parameters: Address_Type (1 Octet)
+ Return Parameters: Address_Type (1 Octet)
+ EIR_Data_Length (2 Octets)
+ EIR_Data (0-65535 Octets)
+
+ This command is used to read the local Out of Band data
+ information and provide them encoded as extended inquiry
+ response information or advertising data.
+
+ Possible values for the Address_Type parameter are a bit-wise or
+ of the following bits:
+
+ 0 BR/EDR
+ 1 LE Public
+ 2 LE Random
+
+ By combining these e.g. the following values are possible:
+
+ 1 BR/EDR
+ 6 LE (public & random)
+ 7 Reserved (not in use)
+
+ For BR/EDR controller (Address_Type 1) the returned information
+ will contain the following information:
+
+ Class of Device
+ Simple Pairing Hash C-192 (optional)
+ Simple Pairing Randomizer R-192 (optional)
+ Simple Pairing Hash C-256 (optional)
+ Simple Pairing Randomizer R-256 (optional)
+ Service Class UUID (optional)
+ Bluetooth Local Name (optional)
+
+ The Simple Pairing Hash C-256 and Simple Pairing Randomizer R-256
+ fields are only included when secure connections has been enabled.
+
+ The Device Address (BD_ADDR) is not included in the EIR_Data and
+ needs to be taken from controller information.
+
+ For LE controller (Address_Type 6) the returned information
+ will contain the following information:
+
+ LE Bluetooth Device Address
+ LE Role
+ Security Manager TK Value (optional)
+ LE Secure Connections Confirmation Value (optional)
+ LE Secure Connections Random Value (optional)
+ Appearance (optional)
+ Local Name (optional)
+ Flags
+
+ The LE Secure Connections Confirmation Value and LE Secure Connections
+ Random Value fields are only included when secure connections has been
+ enabled.
+
+ The returned information from BR/EDR controller and LE controller
+ types are not related to each other. Once they have been used
+ over an Out Of Band link, a new set of information shall be
+ requested.
+
+ When Secure Connections Only mode has been enabled, then the fields
+ for Simple Pairing Hash C-192, Simple Pairing Randomizer R-192 and
+ Security Manager TK Value are not returned. Only the fields for
+ the strong secure connections pairing are included.
+
+ This command can only be used when the controller is powered.
+
+ Values returned by this command become invalid when the controller
+ is powered down. After each power-cycle it is required to call
+ this command again to get updated information.
+
+ This command generates a Command Complete event on success or
+ a Command Status event on failure.
+
+ Possible errors: Not Supported
+ Busy
+ Invalid Parameters
+ Not Powered
+ Invalid Index
+
+
+Read Extended Controller Index List Command
+===========================================
+
+ Command Code: 0x003c
+ Controller Index: <non-controller>
+ Command Parameters:
+ Return Parameters: Num_Controllers (2 Octets)
+ Controller_Index[i] (2 Octets)
+ Controller_Type[i] (1 Octet)
+
+ This command returns the list of currently known controllers. It
+ includes configured, unconfigured and alternate controllers.
+
+ Controllers added or removed after calling this command can be
+ be monitored using the Extended Index Added and Extended Index
+ Removed events.
+
+ The existing Index Added, Index Removed, Unconfigured Index Added
+ and Unconfigured Index Removed are no longer sent after this command
+ has been used at least once.
+
+ Instead of calling Read Controller Index List and Read Unconfigured
+ Controller Index List, this command combines all the information
+ and can be used to retrieve the controller list.
+
+ The Controller_Type parameter has these values:
+
+ 0x00 Primary Controller (BR/EDR and/or LE)
+ 0x01 Unconfigured Controller (BR/EDR and/or LE)
+ 0x02 Alternate MAC/PHY Controller (AMP)
+
+ The 0x00 and 0x01 types indiciate a primary BR/EDR and/or LE
+ controller. The difference is just if they need extra configuration
+ or if they are fully configured.
+
+ Controllers in configured state will be listed as 0x00 and controllers
+ in unconfigured state will be listed as 0x01. A controller that is
+ fully configured and supports configuration changes will be listed
+ as 0x00.
+
+ Alternate MAC/PHY controllers will be listed as 0x02. They do not
+ support the difference between configured and unconfigured state.
+
+ Controllers marked as RAW only operation are currently not listed
+ by this command.
+
+ This command generates a Command Complete event on success or
+ a Command Status event on failure.
+
+
Command Complete Event
======================
system. It is usually followed by a Read Controller Information
command.
+ Once the Read Extended Controller Index List command has been
+ used at least once, the Extended Index Added event will be
+ send instead of this one.
+
Index Removed Event
===================
This event indicates that a controller has been removed from the
system.
+ Once the Read Extended Controller Index List command has been
+ used at least once, the Extended Index Removed event will be
+ send instead of this one.
+
New Settings Event
==================
Key {
Address (6 Octets)
Address_Type (1 Octet)
- Master (1 Octet)
+ Type (1 Octet)
Value (16 Octets)
}
The Store_Hint parameter indicates whether the host is expected
to store the key persistently or not.
- When the Master parameter is set to 0x01, then the signature
- resolving key from the remote peer device is provided. It is
- the key that is used for signature verification.
+ The Type parameter has the following possible values:
+
+ 0x00 Unauthenticated local CSRK
+ 0x01 Unauthenticated remote CSRK
+ 0x02 Authenticated local CSRK
+ 0x03 Authenticated remote CSRK
- When the Master parameter is set to 0x00, then it is the local
- signature resolving key that is used to sign data. The remote
- peer device will be using it for signature verification.
+ The local keys are used for signing data to be sent to the
+ remote device, whereas the remote keys are used to verify
+ signatures received from the remote device.
The local signature resolving key will be generated with each
- pairing request. Only after receiving this event with Master
- parameter set to 0x00 it is possible to use ATT Signed Write
+ pairing request. Only after receiving this event with the Type
+ indicating a local key is it possible to use ATT Signed Write
procedures.
Possible values for the Address_Type parameter:
be announced with this event. If it supports configuration, but
does not require it, then an Index Added event will be used.
+ Once the Read Extended Controller Index List command has been
+ used at least once, the Extended Index Added event will be
+ send instead of this one.
+
Unconfigured Index Removed Event
================================
This event indicates that an unconfigured controller has been
removed from the system.
+ Once the Read Extended Controller Index List command has been
+ used at least once, the Extended Index Removed event will be
+ send instead of this one.
+
New Configuration Options Event
===============================
This event indicates that one or more of the options for the
controller configuration has changed.
+
+
+Extended Index Added Event
+==========================
+
+ Event Code: 0x0020
+ Controller Index: <controller id>
+ Event Parameters: Controller_Type (1 Octet)
+
+ This event indicates that a new controller index has been
+ added to the system.
+
+ This event will only be used after Read Extended Controller Index
+ List has been used at least once. If it has not been used, then
+ Index Added and Unconfigured Index Added are send instead.
+
+
+Extended Index Removed Event
+============================
+
+ Event Code: 0x0021
+ Controller Index: <controller id>
+ Event Parameters: Controller_Type (1 Octet)
+
+ This event indicates that an existing controller index has been
+ removed from the system.
+
+ This event will only be used after Read Extended Controller Index
+ List has been used at least once. If it has not been used, then
+ Index Removed and Unconfigured Index Removed are send instead.
Possible errors: org.bluez.Error.InvalidArguments
org.bluez.Error.AlreadyExists
+#ifdef __TIZEN_PATCH__
+ void RegisterProfile2(object profile, string uuid, string destination,
+ string path,dict options)
+ This registers a profile implementation.
+
+ If an application disconects/exits, its registered profile
+ will not be removed and bluez launches an application through
+ dbus activation when profile is connected.
+
+ HFP HS UUID: 0000111e-0000-1000-8000-00805f9b34fb
+
+ Default RFCOMM channel is 6. And this requires
+ authentication.
+
+ string Destination
+
+ Application bus name
+
+ string Path
+
+ Applicatoin path name
+
+ Available options:
+
+ string Name
+
+ Human readable name for the profile
+
+ string Service
+
+ The primary service class UUID
+ (if different from the actual
+ profile UUID)
+
+ string Role
+
+ For asymmetric profiles that do not
+ have UUIDs available to uniquely
+ identify each side this
+ parameter allows specifying the
+ precise local role.
+
+ Possible values: "client", "server"
+
+ uint16 Channel
+
+ RFCOMM channel number that is used
+ for client and server UUIDs.
+
+ If applicable it will be used in the
+ SDP record as well.
+
+ uint16 PSM
+
+ PSM number that is used for client
+ and server UUIDs.
+
+ If applicable it will be used in the
+ SDP record as well.
+
+ boolean RequireAuthentication
+
+ Pairing is required before connections
+ will be established. No devices will
+ be connected if not paired.
+
+ boolean RequireAuthorization
+
+ Request authorization before any
+ connection will be established.
+
+ boolean AutoConnect
+
+ In case of a client UUID this will
+ force connection of the RFCOMM or
+ L2CAP channels when a remote device
+ is connected.
+
+ string ServiceRecord
+
+ Provide a manual SDP record.
+
+ uint16 Version
+
+ Profile version (for SDP record)
+
+ uint16 Features
+
+ Profile features (for SDP record)
+
+ Possible errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.AlreadyExists
+#endif
void UnregisterProfile(object profile)
This unregisters the profile that has been previously
Same as the [LongTermKey] group, except for slave keys.
+
[ConnectionParameters] group contains:
MinInterval Integer Minimum Connection Interval
Timeout Integer Supervision Timeout
+
+[LocalSignatureKey] and [RemoteSignatureKey] groups contain:
+
+ Key String Key in hexadecimal format
+
+ Counter Integer Signing counter
+
+ Authenticated Boolean True if the key is authenticated
+
#ifdef __TIZEN_PATCH__
[IdentityResolvingKey] group contains:
+
BlueZ test coverage
*******************
test-gobex-apparam 18 OBEX apparam handling
test-gobex-transfer 36 OBEX transfer handling
test-gdbus-client 13 D-Bus client handling
-test-gatt 11 GATT qualification test cases
+test-gatt 131 GATT qualification test cases
-----
- 586
+ 706
Automated end-to-end testing
Application Count Description
-------------------------------------------
-mgmt-tester 234 Kernel management interface testing
-l2cap-tester 26 Kernel L2CAP implementation testing
+mgmt-tester 243 Kernel management interface testing
+l2cap-tester 27 Kernel L2CAP implementation testing
rfcomm-tester 9 Kernel RFCOMM implementation testing
smp-tester 5 Kernel SMP implementation testing
sco-tester 8 Kernel SCO implementation testing
gap-tester 1 Daemon D-Bus API testing
hci-tester 14 Controller hardware testing
-----
- 297
+ 307
Android end-to-end testing
Application Count Description
-------------------------------------------
-android-tester 187 Android HAL interface testing
+android-tester 194 Android HAL interface testing
ipc-tester 132 Android IPC resistance testing
-----
- 319
+ 326
Android automated unit testing
#include <sys/socket.h>
#include <sys/un.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
#include "src/shared/util.h"
-#include "monitor/mainloop.h"
+#include "src/shared/mainloop.h"
#include "monitor/bt.h"
#include "amp.h"
#include <netdb.h>
#include <arpa/inet.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
-#include "monitor/mainloop.h"
+#include "src/shared/mainloop.h"
#define DEFAULT_SERVER "b1ee.com"
#define DEFAULT_HOST_PORT "45550" /* 0xb1ee */
#include <endian.h>
#include <stdbool.h>
-#include "bluetooth/bluetooth.h"
+#include "lib/bluetooth.h"
#include "src/shared/util.h"
#include "monitor/bt.h"
uint8_t addr_type;
uint8_t encr_mode;
uint16_t next_cid;
+ uint64_t fixed_chan;
struct l2conn *l2conns;
struct rcconn *rcconns;
struct cid_hook *cid_hooks;
if (bthost->new_conn_cb)
bthost->new_conn_cb(conn->handle, bthost->new_conn_data);
+
+ if (addr_type == BDADDR_BREDR) {
+ struct bt_l2cap_pdu_info_req req;
+ req.type = L2CAP_IT_FIXED_CHAN;
+ l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_INFO_REQ, 1,
+ &req, sizeof(req));
+ }
}
static void evt_conn_complete(struct bthost *bthost, const void *data,
uint8_t ident, const void *data, uint16_t len)
{
const struct bt_l2cap_pdu_conn_rsp *rsp = data;
+ struct bt_l2cap_pdu_config_req req;
struct l2conn *l2conn;
if (len < sizeof(*rsp))
else
return false;
- if (le16_to_cpu(rsp->result) == 0x0001) {
- struct bt_l2cap_pdu_config_req req;
+ if (rsp->result)
+ return true;
- memset(&req, 0, sizeof(req));
- req.dcid = rsp->dcid;
+ memset(&req, 0, sizeof(req));
+ req.dcid = rsp->dcid;
- l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_CONFIG_REQ, 0,
+ l2cap_sig_send(bthost, conn, BT_L2CAP_PDU_CONFIG_REQ, 0,
&req, sizeof(req));
- } else if (l2conn->psm == 0x0003 && !rsp->result && !rsp->status &&
- bthost->rfcomm_conn_data) {
- rfcomm_sabm_send(bthost, conn, l2conn, 1, 0);
- }
return true;
}
uint8_t ident, const void *data, uint16_t len)
{
const struct bt_l2cap_pdu_config_rsp *rsp = data;
+ struct l2conn *l2conn;
if (len < sizeof(*rsp))
return false;
+ l2conn = btconn_find_l2cap_conn_by_scid(conn, rsp->scid);
+ if (!l2conn)
+ return false;
+
+ if (l2conn->psm == 0x0003 && !rsp->result && bthost->rfcomm_conn_data)
+ rfcomm_sabm_send(bthost, conn, l2conn, 1, 0);
+
return true;
}
return true;
}
+static bool l2cap_info_rsp(struct bthost *bthost, struct btconn *conn,
+ uint8_t ident, const void *data, uint16_t len)
+{
+ const struct bt_l2cap_pdu_info_rsp *rsp = data;
+ uint16_t type;
+
+ if (len < sizeof(*rsp))
+ return false;
+
+ if (rsp->result)
+ return true;
+
+ type = le16_to_cpu(rsp->type);
+
+ switch (type) {
+ case L2CAP_IT_FIXED_CHAN:
+ if (len < sizeof(*rsp) + 8)
+ return false;
+ conn->fixed_chan = get_le64(rsp->data);
+ if (conn->smp_data && conn->encr_mode)
+ smp_conn_encrypted(conn->smp_data, conn->encr_mode);
+ break;
+ default:
+ break;
+ }
+
+ return true;
+}
+
static void handle_pending_l2reqs(struct bthost *bthost, struct btconn *conn,
uint8_t ident, uint8_t code,
const void *data, uint16_t len)
data + sizeof(*hdr), hdr_len);
break;
+ case BT_L2CAP_PDU_INFO_RSP:
+ ret = l2cap_info_rsp(bthost, conn, hdr->ident,
+ data + sizeof(*hdr), hdr_len);
+ break;
+
default:
printf("Unknown L2CAP code 0x%02x\n", hdr->code);
ret = false;
}
}
+#define GET_LEN8(length) ((length & 0xfe) >> 1)
+#define GET_LEN16(length) ((length & 0xfffe) >> 1)
+
static void rfcomm_uih_recv(struct bthost *bthost, struct btconn *conn,
struct l2conn *l2conn, const void *data,
uint16_t len)
{
const struct rfcomm_hdr *hdr = data;
- uint16_t hdr_len;
+ uint16_t hdr_len, data_len;
const void *p;
if (len < sizeof(*hdr))
return;
- if (RFCOMM_TEST_EA(hdr->length))
+ if (RFCOMM_TEST_EA(hdr->length)) {
+ data_len = (uint16_t) GET_LEN8(hdr->length);
hdr_len = sizeof(*hdr);
- else
+ } else {
+ uint8_t ex_len = *((uint8_t *)(data + sizeof(*hdr)));
+ data_len = ((uint16_t) hdr->length << 8) | ex_len;
hdr_len = sizeof(*hdr) + sizeof(uint8_t);
+ }
- if (len < hdr_len)
+ if (len < hdr_len + data_len)
return;
p = data + hdr_len;
hook = find_rfcomm_chan_hook(conn,
RFCOMM_GET_CHANNEL(hdr->address));
- if (!hook)
- return;
-
- hook->func(p, len - hdr_len - sizeof(uint8_t),
- hook->user_data);
+ if (hook && data_len)
+ hook->func(p, data_len, hook->user_data);
} else {
- rfcomm_mcc_recv(bthost, conn, l2conn, p, len - hdr_len);
+ rfcomm_mcc_recv(bthost, conn, l2conn, p, data_len);
}
}
send_command(bthost, BT_HCI_CMD_LE_START_ENCRYPT, &cmd, sizeof(cmd));
}
+uint64_t bthost_conn_get_fixed_chan(struct bthost *bthost, uint16_t handle)
+{
+ struct btconn *conn;
+
+ conn = bthost_find_conn(bthost, handle);
+ if (!conn)
+ return 0;
+
+ return conn->fixed_chan;
+}
+
void bthost_add_l2cap_server(struct bthost *bthost, uint16_t psm,
bthost_l2cap_connect_cb func, void *user_data)
{
void bthost_set_reject_user_confirm(struct bthost *bthost, bool reject);
bool bthost_get_reject_user_confirm(struct bthost *bthost);
+uint64_t bthost_conn_get_fixed_chan(struct bthost *bthost, uint16_t handle);
+
typedef void (*bthost_rfcomm_connect_cb) (uint16_t handle, uint16_t cid,
void *user_data, bool status);
#include <glib.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
#include "monitor/bt.h"
#include "emulator/btdev.h"
#include <sys/socket.h>
#include <sys/un.h>
-#include "monitor/mainloop.h"
+#include "src/shared/mainloop.h"
#include "src/shared/hfp.h"
static void hfp_debug(const char *str, void *user_data)
#include <sys/socket.h>
#include <sys/un.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
#include "src/shared/util.h"
#include "src/shared/crypto.h"
#include "src/shared/ecc.h"
-#include "monitor/mainloop.h"
+#include "src/shared/mainloop.h"
#include "monitor/bt.h"
#include "phy.h"
#include <stdbool.h>
#include <getopt.h>
-#include "monitor/mainloop.h"
+#include "src/shared/mainloop.h"
#include "serial.h"
#include "server.h"
#include "vhci.h"
#include <time.h>
#include "src/shared/util.h"
-#include "monitor/mainloop.h"
+#include "src/shared/mainloop.h"
#include "phy.h"
#include <sys/epoll.h>
#include <sys/uio.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
-#include "monitor/mainloop.h"
+#include "src/shared/mainloop.h"
#include "btdev.h"
#include "serial.h"
#include <netinet/in.h>
#include <arpa/inet.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
-#include "monitor/mainloop.h"
+#include "src/shared/mainloop.h"
#include "btdev.h"
#include "server.h"
#include <stdbool.h>
#include <sys/socket.h>
-#include "bluetooth/bluetooth.h"
-#include "bluetooth/hci.h"
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
#include "src/shared/util.h"
#include "src/shared/crypto.h"
#define SMP_CID 0x0006
#define SMP_BREDR_CID 0x0007
+#define L2CAP_FC_SMP_BREDR 0x80
+
#define SMP_PASSKEY_ENTRY_FAILED 0x01
#define SMP_OOB_NOT_AVAIL 0x02
#define SMP_AUTH_REQUIREMENTS 0x03
if (conn->local_key_dist & DIST_ID_KEY) {
memset(buf, 0, sizeof(buf));
+ smp_send(conn, BT_L2CAP_SMP_IDENT_INFO, buf, sizeof(buf));
+
+ memset(buf, 0, sizeof(buf));
if (conn->out) {
buf[0] = conn->ia_type;
buf[0] = conn->ra_type;
memcpy(&buf[1], conn->ra, 6);
}
- smp_send(conn, BT_L2CAP_SMP_IDENT_ADDR_INFO, buf, 7);
- memset(buf, 0, sizeof(buf));
- smp_send(conn, BT_L2CAP_SMP_IDENT_INFO, buf, sizeof(buf));
+ smp_send(conn, BT_L2CAP_SMP_IDENT_ADDR_INFO, buf, 7);
}
if (conn->local_key_dist & DIST_SIGN) {
conn->local_key_dist = conn->prsp[5];
conn->remote_key_dist = conn->prsp[6];
+ if (conn->addr_type == BDADDR_BREDR) {
+ conn->local_key_dist &= ~SC_NO_DIST;
+ conn->remote_key_dist &= ~SC_NO_DIST;
+ distribute_keys(conn);
+ return;
+ }
+
if (((conn->prsp[3] & 0x08) && (conn->preq[3] & 0x08)) ||
conn->addr_type == BDADDR_BREDR) {
conn->sc = true;
return 0;
}
+static void smp_conn_bredr(struct smp_conn *conn, uint8_t encrypt)
+{
+ struct smp *smp = conn->smp;
+ struct bt_l2cap_smp_pairing_request req;
+ uint64_t fixed_chan;
+
+ if (encrypt != 0x02)
+ return;
+
+ conn->sc = true;
+
+ if (!conn->out)
+ return;
+
+ fixed_chan = bthost_conn_get_fixed_chan(smp->bthost, conn->handle);
+ if (!(fixed_chan & L2CAP_FC_SMP_BREDR))
+ return;
+
+ memset(&req, 0, sizeof(req));
+ req.max_key_size = 0x10;
+ req.init_key_dist = KEY_DIST;
+ req.resp_key_dist = KEY_DIST;
+
+ smp_send(conn, BT_L2CAP_SMP_PAIRING_REQUEST, &req, sizeof(req));
+}
+
void smp_conn_encrypted(void *conn_data, uint8_t encrypt)
{
struct smp_conn *conn = conn_data;
if (!encrypt)
return;
+ if (conn->addr_type == BDADDR_BREDR) {
+ smp_conn_bredr(conn, encrypt);
+ return;
+ }
+
if (conn->out && conn->remote_key_dist)
return;
#include <stdlib.h>
#include <string.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
-#include "monitor/mainloop.h"
+#include "src/shared/mainloop.h"
#include "monitor/bt.h"
#include "btdev.h"
#include "vhci.h"
DBusConnection *dbus_conn;
char *service_name;
char *base_path;
+ char *root_path;
guint watch;
guint added_watch;
guint removed_watch;
{
DBusMessage *msg;
+ if (!client->connected)
+ return;
+
if (!client->proxy_added && !client->proxy_removed) {
refresh_properties(client);
return;
if (client->get_objects_call != NULL)
return;
- msg = dbus_message_new_method_call(client->service_name, "/",
- DBUS_INTERFACE_DBUS ".ObjectManager",
- "GetManagedObjects");
+ msg = dbus_message_new_method_call(client->service_name,
+ client->root_path,
+ DBUS_INTERFACE_OBJECT_MANAGER,
+ "GetManagedObjects");
if (msg == NULL)
return;
g_dbus_client_ref(client);
+ client->connected = TRUE;
+
if (client->connect_func)
client->connect_func(conn, client->connect_data);
get_managed_objects(client);
- client->connected = TRUE;
-
g_dbus_client_unref(client);
}
{
GDBusClient *client = user_data;
+ client->connected = FALSE;
+
g_list_free_full(client->proxy_list, proxy_free);
client->proxy_list = NULL;
- if (client->disconn_func) {
+ if (client->disconn_func)
client->disconn_func(conn, client->disconn_data);
- client->connected = FALSE;
- }
}
static DBusHandlerResult message_filter(DBusConnection *connection,
GDBusClient *g_dbus_client_new(DBusConnection *connection,
const char *service, const char *path)
{
+ return g_dbus_client_new_full(connection, service, path, "/");
+}
+
+GDBusClient *g_dbus_client_new_full(DBusConnection *connection,
+ const char *service,
+ const char *path,
+ const char *root_path)
+{
GDBusClient *client;
unsigned int i;
- if (connection == NULL)
+ if (!connection || !service || !root_path)
return NULL;
client = g_try_new0(GDBusClient, 1);
client->dbus_conn = dbus_connection_ref(connection);
client->service_name = g_strdup(service);
client->base_path = g_strdup(path);
+ client->root_path = g_strdup(root_path);
client->connected = FALSE;
client->match_rules = g_ptr_array_sized_new(1);
service_disconnect,
client, NULL);
client->added_watch = g_dbus_add_signal_watch(connection, service,
- "/",
+ client->root_path,
DBUS_INTERFACE_OBJECT_MANAGER,
"InterfacesAdded",
interfaces_added,
client, NULL);
client->removed_watch = g_dbus_add_signal_watch(connection, service,
- "/",
+ client->root_path,
DBUS_INTERFACE_OBJECT_MANAGER,
"InterfacesRemoved",
interfaces_removed,
g_free(client->service_name);
g_free(client->base_path);
+ g_free(client->root_path);
g_free(client);
}
client->property_changed = property_changed;
client->user_data = user_data;
- get_managed_objects(client);
+ if (proxy_added || proxy_removed || property_changed)
+ get_managed_objects(client);
return TRUE;
}
GDBusClient *g_dbus_client_new(DBusConnection *connection,
const char *service, const char *path);
+GDBusClient *g_dbus_client_new_full(DBusConnection *connection,
+ const char *service,
+ const char *path,
+ const char *root_path);
GDBusClient *g_dbus_client_ref(GDBusClient *client);
void g_dbus_client_unref(GDBusClient *client);
#include "gdbus.h"
#ifdef __TIZEN_PATCH__
+#if 0
#include <syslog.h>
static void gdbus_dbg(const char *format, ...)
{
va_end(ap);
}
+#endif
#else
#define info(fmt...)
#endif
iface->user_data) == TRUE)
return DBUS_HANDLER_RESULT_HANDLED;
#ifdef __TIZEN_PATCH__
+#if 0
gdbus_dbg("%s: %s.%s()", dbus_message_get_path(message),
iface->name, method->name);
#endif
+#endif
return process_message(connection, message, method,
iface->user_data);
}
member = dbus_message_get_member(message);
dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &arg, DBUS_TYPE_INVALID);
- /* Sender is always the owner */
- if (sender == NULL)
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ /* If sender != NULL it is always the owner */
for (current = listeners; current != NULL; current = current->next) {
data = current->data;
if (connection != data->connection)
continue;
+ if (!sender && data->owner)
+ continue;
+
if (data->owner && g_str_equal(sender, data->owner) == FALSE)
continue;
GHashTableIter iter;
gpointer key, value;
-#ifdef __TIZEN_PATCH__
- if(g_hash_table_size(apparam->tags) > 0) {
-#endif
g_hash_table_iter_init(&iter, apparam->tags);
while (g_hash_table_iter_next(&iter, &key, &value)) {
struct apparam_tag *tag = value;
return ret;
count += ret;
-#ifdef __TIZEN_PATCH__
- count += ret;
- /* Now remove this key as we have processed it
- * If we don't remove this key then obexd process will get into
- * infinite loop because this has been called from
- * vcard_list_get_next_header which in-turn called
- * from driver_get_header */
- g_hash_table_remove(apparam->tags, key);
-#endif
}
-#ifdef __TIZEN_PATCH__
- }
-#endif
return count;
}
#include <glib.h>
-#include <gobex/gobex-defs.h>
-#include <gobex/gobex-apparam.h>
+#include "gobex/gobex-defs.h"
+#include "gobex/gobex-apparam.h"
/* Header ID's */
#define G_OBEX_HDR_INVALID 0x00
#include <stdarg.h>
#include <glib.h>
-#include <gobex/gobex-defs.h>
-#include <gobex/gobex-header.h>
+#include "gobex/gobex-defs.h"
+#include "gobex/gobex-header.h"
/* Request opcodes */
#define G_OBEX_OP_CONNECT 0x00
#include <string.h>
#include <errno.h>
-#include "gobex.h"
-#include "gobex-debug.h"
+#include "gobex/gobex.h"
+#include "gobex/gobex-debug.h"
#define FIRST_PACKET_TIMEOUT 60
#include <stdarg.h>
#include <glib.h>
-#include <gobex/gobex-defs.h>
-#include <gobex/gobex-packet.h>
+#include "gobex/gobex-defs.h"
+#include "gobex/gobex-packet.h"
typedef enum {
G_OBEX_TRANSPORT_STREAM,
return "LM Technologies Ltd";
case 439:
return "General Electric Company";
+ case 440:
+ return "i+D3 S.L.";
+ case 441:
+ return "HANA Micron";
+ case 442:
+ return "Stages Cycling LLC";
+ case 443:
+ return "Cochlear Bone Anchored Solutions AB";
+ case 444:
+ return "SenionLab AB";
+ case 445:
+ return "Syszone Co., Ltd";
+ case 446:
+ return "Pulsate Mobile Ltd.";
+ case 447:
+ return "Hong Kong HunterSun Electronic Limited";
+ case 448:
+ return "pironex GmbH";
+ case 449:
+ return "BRADATECH Corp.";
+ case 450:
+ return "Transenergooil AG";
+ case 451:
+ return "Bunch";
+ case 452:
+ return "DME Microelectronics";
+ case 453:
+ return "Bitcraze AB";
+ case 454:
+ return "HASWARE Inc.";
+ case 455:
+ return "Abiogenix Inc.";
+ case 456:
+ return "Poly-Control ApS";
+ case 457:
+ return "Avi-on";
+ case 458:
+ return "Laerdal Medical AS";
+ case 459:
+ return "Fetch My Pet";
+ case 460:
+ return "Sam Labs Ltd.";
+ case 461:
+ return "Chengdu Synwing Technology Ltd";
+ case 462:
+ return "HOUWA SYSTEM DESIGN, k.k.";
+ case 463:
+ return "BSH";
+ case 464:
+ return "Primus Inter Pares Ltd";
+ case 465:
+ return "August";
+ case 466:
+ return "Gill Electronics";
+ case 467:
+ return "Sky Wave Design";
+ case 468:
+ return "Newlab S.r.l.";
+ case 469:
+ return "ELAD srl";
+ case 470:
+ return "G-wearables inc.";
+ case 471:
+ return "Squadrone Systems Inc.";
+ case 472:
+ return "Code Corporation";
+ case 473:
+ return "Savant Systems LLC";
+ case 474:
+ return "Logitech International SA";
+ case 475:
+ return "Innblue Consulting";
+ case 476:
+ return "iParking Ltd.";
+ case 477:
+ return "Koninklijke Philips Electronics N.V.";
+ case 478:
+ return "Minelab Electronics Pty Limited";
+ case 479:
+ return "Bison Group Ltd.";
+ case 480:
+ return "Widex A/S";
+ case 481:
+ return "Jolla Ltd";
+ case 482:
+ return "Lectronix, Inc.";
+ case 483:
+ return "Caterpillar Inc";
+ case 484:
+ return "Freedom Innovations";
+ case 485:
+ return "Dynamic Devices Ltd";
+ case 486:
+ return "Technology Solutions (UK) Ltd";
+ case 487:
+ return "IPS Group Inc.";
+ case 488:
+ return "STIR";
case 65535:
return "internal use";
default:
uint8_t data[16];
} uint128_t;
+static inline void bswap_128(const void *src, void *dst)
+{
+ const uint8_t *s = src;
+ uint8_t *d = dst;
+ int i;
+
+ for (i = 0; i < 16; i++)
+ d[15 - i] = s[i];
+}
+
#if __BYTE_ORDER == __BIG_ENDIAN
#define ntoh64(x) (x)
static inline void btoh128(const uint128_t *src, uint128_t *dst)
{
- int i;
-
- for (i = 0; i < 16; i++)
- dst->data[15 - i] = src->data[i];
+ bswap_128(src, dst);
}
#else
static inline void ntoh128(const uint128_t *src, uint128_t *dst)
{
- int i;
-
- for (i = 0; i < 16; i++)
- dst->data[15 - i] = src->data[i];
+ bswap_128(src, dst);
}
static inline void btoh128(const uint128_t *src, uint128_t *dst)
return "PCI";
case HCI_SDIO:
return "SDIO";
+#ifdef __TIZEN_PATCH__
+ case HCI_SMD:
+ return "QC_SMD";
+#endif
default:
return "UNKNOWN";
}
#define HCI_PCI 5
#define HCI_SDIO 6
+#ifdef __TIZEN_PATCH__
+#define HCI_SMD 7
+#endif
+
/* HCI controller types */
#define HCI_BREDR 0x00
#define HCI_AMP 0x01
#define MGMT_SETTING_DEBUG_KEYS 0x00001000
#define MGMT_SETTING_PRIVACY 0x00002000
#define MGMT_SETTING_CONFIGURATION 0x00004000
+#define MGMT_SETTING_STATIC_ADDRESS 0x00008000
#define MGMT_OP_READ_INFO 0x0004
struct mgmt_rp_read_info {
struct mgmt_csrk_info {
struct mgmt_addr_info addr;
- uint8_t master;
+ uint8_t type;
uint8_t val[16];
} __packed;
#define MGMT_OP_SET_ADVERTISING_DATA (TIZEN_OP_CODE_BASE + 0x02)
struct mgmt_cp_set_advertising_data {
- uint8_t data[MGMT_MAX_ADVERTISING_LENGTH - 3]; /* Except flag */
+ uint8_t data[MGMT_MAX_ADVERTISING_LENGTH];
} __packed;
#define MGMT_OP_SET_SCAN_RSP_DATA (TIZEN_OP_CODE_BASE + 0x03)
uint16_t voice_setting;
} __packed;
+#define MGMT_OP_GET_ADV_TX_POWER (TIZEN_OP_CODE_BASE + 0x11)
+struct mgmt_rp_get_adv_tx_power {
+ int8_t adv_tx_power;
+} __packed;
+
+#define MGMT_OP_ENABLE_6LOWPAN (TIZEN_OP_CODE_BASE + 0x12)
+struct mgmt_cp_enable_6lowpan {
+ uint8_t enable_6lowpan;
+} __packed;
+
+#define MGMT_OP_CONNECT_6LOWPAN (TIZEN_OP_CODE_BASE + 0x13)
+struct mgmt_cp_connect_6lowpan {
+ struct mgmt_addr_info addr;
+} __packed;
+
+#define MGMT_OP_DISCONNECT_6LOWPAN (TIZEN_OP_CODE_BASE + 0x14)
+struct mgmt_cp_disconnect_6lowpan {
+ struct mgmt_addr_info addr;
+} __packed;
+
/* Currently there is no support in kernel for below MGMT cmd opcodes. */
+#if 0 // Not defined in kernel
#define MGMT_OP_READ_RSSI (TIZEN_OP_CODE_BASE + 0x11)
struct mgmt_cp_read_rssi {
bdaddr_t bdaddr;
uint8_t status;
uint16_t auth_payload_timeout;
} __packed;
+#endif
+
/* BEGIN TIZEN_Bluetooth :: name update changes */
#define MGMT_EV_DEVICE_NAME_UPDATE (TIZEN_EV_BASE + 0x01)
uint8_t eir[0];
} __packed;
+#define MGMT_EV_MULTI_ADV_STATE_CHANGED (TIZEN_EV_BASE + 0x0b)
+struct mgmt_ev_vendor_specific_multi_adv_state_changed {
+ uint8_t adv_instance;
+ uint8_t state_change_reason;
+ int16_t connection_handle;
+} __packed;
+
+#define MGMT_EV_6LOWPAN_CONN_STATE_CHANGED (TIZEN_EV_BASE + 0x0c)
+struct mgmt_ev_6lowpan_conn_state_changed {
+ struct mgmt_addr_info addr;
+ uint8_t connected;
+} __packed;
+
+/* Currently there is no support in kernel for below MGMT events. */
+#if 0 // Not defined in kernel
#define MGMT_EV_NEW_LOCAL_IRK (TIZEN_EV_BASE + 0x0b)
struct mgmt_ev_new_local_irk {
uint8_t irk[16];
} __packed;
+#endif
static const char *mgmt_tizen_op[] = {
"<0x0000>",
"LE Connection Update",
"Set Manufacturer Data",
"LE Set Scan Parameters",
- "Set Voice Setting"
+ "Set Voice Setting",
+ "Get Adv Tx Power",
+ "Connect BT 6LOWPAN",
+ "Disconnect BT 6LOWPAN"
};
static const char *mgmt_tizen_ev[] = {
"LE Connection Updated",
"LE Connection Update Failed",
"LE Device Found",
- "New Local IRK Generated",
+ "Multi Adv State Change",
+ "BT 6LOWPAN state Change"
};
#endif /* End of __TIZEN_PATCH__ */
#include <stdlib.h>
#include <errno.h>
+#include "lib/bluetooth.h"
#include "uuid.h"
static uint128_t bluetooth_base_uuid = {
{
return strcasecmp(a, b);
}
+
+int bt_uuid_to_le(const bt_uuid_t *src, void *dst)
+{
+ bt_uuid_t uuid;
+
+ switch (src->type) {
+ case BT_UUID16:
+ bt_put_le16(src->value.u16, dst);
+ return 0;
+ case BT_UUID32:
+ bt_uuid_to_uuid128(src, &uuid);
+ /* Fallthrough */
+ case BT_UUID128:
+ /* Convert from 128-bit BE to LE */
+ bswap_128(&src->value.u128, dst);
+ return 0;
+ case BT_UUID_UNSPEC:
+ default:
+ return -EINVAL;
+ }
+}
#endif
#include <stdint.h>
+#ifdef __TIZEN_PATCH__
#include <bluetooth/bluetooth.h>
+#endif
#define GENERIC_AUDIO_UUID "00001203-0000-1000-8000-00805f9b34fb"
#define GATT_EXTERNAL_REPORT_REFERENCE 0x2907
#define GATT_REPORT_REFERENCE 0x2908
+#ifdef __TIZEN_PATCH__
+/* GATT Service UUIDs : Defined by SIG */
+#define GATT_IPSP_UUID 0x1820
+#endif
+
typedef struct {
enum {
BT_UUID_UNSPEC = 0,
int bt_uuid_to_string(const bt_uuid_t *uuid, char *str, size_t n);
int bt_string_to_uuid(bt_uuid_t *uuid, const char *string);
+int bt_uuid_to_le(const bt_uuid_t *uuid, void *dst);
+
static inline int bt_uuid_len(const bt_uuid_t *uuid)
{
return uuid->type / 8;
#include <ctype.h>
#include <inttypes.h>
-#include <bluetooth/bluetooth.h>
+#include "lib/bluetooth.h"
#include "src/shared/util.h"
#include "bt.h"
}
}
+static bool avrcp_set_browsed_player(struct avctp_frame *avctp_frame)
+{
+ struct l2cap_frame *frame = &avctp_frame->l2cap_frame;
+ uint32_t items;
+ uint16_t id, uids, charset;
+ uint8_t status, folders, indent = 2;
+
+ if (avctp_frame->hdr & 0x02)
+ goto response;
+
+ if (!l2cap_frame_get_be16(frame, &id))
+ return false;
+
+ print_field("%*cPlayerID: 0x%04x (%u)", indent, ' ', id, id);
+ return true;
+
+response:
+ if (!l2cap_frame_get_u8(frame, &status))
+ return false;
+
+ print_field("%*cStatus: 0x%02x (%s)", indent, ' ', status,
+ error2str(status));
+
+ if (!l2cap_frame_get_be16(frame, &uids))
+ return false;
+
+ print_field("%*cUIDCounter: 0x%04x (%u)", indent, ' ', uids, uids);
+
+ if (!l2cap_frame_get_be32(frame, &items))
+ return false;
+
+ print_field("%*cNumber of Items: 0x%08x (%u)", indent, ' ',
+ items, items);
+
+ if (!l2cap_frame_get_be16(frame, &charset))
+ return false;
+
+ print_field("%*cCharsetID: 0x%04x (%s)", indent, ' ', charset,
+ charset2str(charset));
+
+ if (!l2cap_frame_get_u8(frame, &folders))
+ return false;
+
+ print_field("%*cFolder Depth: 0x%02x (%u)", indent, ' ', folders,
+ folders);
+
+ for (; folders > 0; folders--) {
+ uint8_t len;
+
+ if (!l2cap_frame_get_u8(frame, &len))
+ return false;
+
+ printf("Folder: ");
+ for (; len > 0; len--) {
+ uint8_t c;
+
+ if (!l2cap_frame_get_u8(frame, &c))
+ return false;
+
+ printf("%1c", isprint(c) ? c : '.');
+ }
+ printf("\n");
+ }
+
+ return true;
+}
+
static bool avrcp_browsing_packet(struct avctp_frame *avctp_frame)
{
struct l2cap_frame *frame = &avctp_frame->l2cap_frame;
+ uint16_t len;
+ uint8_t pduid;
+
+ if (!l2cap_frame_get_u8(frame, &pduid))
+ return false;
+
+ if (!l2cap_frame_get_be16(frame, &len))
+ return false;
+
+ print_field("AVRCP: %s: len 0x%04x", pdu2str(pduid), len);
+
+ switch (pduid) {
+ case AVRCP_SET_BROWSED_PLAYER:
+ avrcp_set_browsed_player(avctp_frame);
+ break;
+ default:
+ packet_hexdump(frame->data, frame->size);
+ }
- packet_hexdump(frame->data, frame->size);
return true;
}
uint8_t rx_flow_spec[16];
} __attribute__ ((packed));
+#define BT_HCI_CMD_ENHANCED_SETUP_SYNC_CONN 0x043d
+struct bt_hci_cmd_enhanced_setup_sync_conn {
+ uint16_t handle;
+ uint32_t tx_bandwidth;
+ uint32_t rx_bandwidth;
+ uint8_t tx_coding_format[5];
+ uint8_t rx_coding_format[5];
+ uint16_t tx_codec_frame_size;
+ uint16_t rx_codec_frame_size;
+ uint32_t input_bandwidth;
+ uint32_t output_bandwidth;
+ uint8_t input_coding_format[5];
+ uint8_t output_coding_format[5];
+ uint16_t input_coded_data_size;
+ uint16_t output_coded_data_size;
+ uint8_t input_pcm_data_format;
+ uint8_t output_pcm_data_format;
+ uint8_t input_pcm_msb_position;
+ uint8_t output_pcm_msb_position;
+ uint8_t input_data_path;
+ uint8_t output_data_path;
+ uint8_t input_unit_size;
+ uint8_t output_unit_size;
+ uint16_t max_latency;
+ uint16_t pkt_type;
+ uint8_t retrans_effort;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_ENHANCED_ACCEPT_SYNC_CONN_REQUEST 0x043e
+struct bt_hci_cmd_enhanced_accept_sync_conn_request {
+ uint8_t bdaddr[6];
+ uint32_t tx_bandwidth;
+ uint32_t rx_bandwidth;
+ uint8_t tx_coding_format[5];
+ uint8_t rx_coding_format[5];
+ uint16_t tx_codec_frame_size;
+ uint16_t rx_codec_frame_size;
+ uint32_t input_bandwidth;
+ uint32_t output_bandwidth;
+ uint8_t input_coding_format[5];
+ uint8_t output_coding_format[5];
+ uint16_t input_coded_data_size;
+ uint16_t output_coded_data_size;
+ uint8_t input_pcm_data_format;
+ uint8_t output_pcm_data_format;
+ uint8_t input_pcm_msb_position;
+ uint8_t output_pcm_msb_position;
+ uint8_t input_data_path;
+ uint8_t output_data_path;
+ uint8_t input_unit_size;
+ uint8_t output_unit_size;
+ uint16_t max_latency;
+ uint16_t pkt_type;
+ uint8_t retrans_effort;
+} __attribute__ ((packed));
+
#define BT_HCI_CMD_TRUNCATED_PAGE 0x043f
struct bt_hci_cmd_truncated_page {
uint8_t bdaddr[6];
uint16_t sco_max_pkt;
} __attribute__ ((packed));
+#define BT_HCI_CMD_HOST_NUM_COMPLETED_PACKETS 0x0c35
+struct bt_hci_cmd_host_num_completed_packets {
+ uint8_t num_handles;
+ uint16_t handle;
+ uint16_t count;
+} __attribute__ ((packed));
+
#define BT_HCI_CMD_READ_LINK_SUPV_TIMEOUT 0x0c36
struct bt_hci_cmd_read_link_supv_timeout {
uint16_t handle;
int8_t level;
} __attribute__ ((packed));
+#define BT_HCI_CMD_READ_ERRONEOUS_REPORTING 0x0c5a
+struct bt_hci_rsp_read_erroneous_reporting {
+ uint8_t status;
+ uint8_t mode;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_ERRONEOUS_REPORTING 0x0c5b
+struct bt_hci_cmd_write_erroneous_reporting {
+ uint8_t mode;
+} __attribute__ ((packed));
+
#define BT_HCI_CMD_ENHANCED_FLUSH 0x0c5f
struct bt_hci_cmd_enhanced_flush {
uint16_t handle;
uint8_t mode;
} __attribute__ ((packed));
+#define BT_HCI_CMD_READ_ENHANCED_TX_POWER 0x0c68
+struct bt_hci_cmd_read_enhanced_tx_power {
+ uint16_t handle;
+ uint8_t type;
+} __attribute__ ((packed));
+struct bt_hci_rsp_read_enhanced_tx_power {
+ uint8_t status;
+ uint16_t handle;
+ int8_t level_gfsk;
+ int8_t level_dqpsk;
+ int8_t level_8dpsk;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_SHORT_RANGE_MODE 0x0c6b
+struct bt_hci_cmd_short_range_mode {
+ uint8_t phy_handle;
+ uint8_t mode;
+} __attribute__ ((packed));
+
#define BT_HCI_CMD_READ_LE_HOST_SUPPORTED 0x0c6c
struct bt_hci_rsp_read_le_host_supported {
uint8_t status;
uint8_t randomizer256[16];
} __attribute__ ((packed));
+#define BT_HCI_CMD_READ_EXT_PAGE_TIMEOUT 0x0c7e
+struct bt_hci_rsp_read_ext_page_timeout {
+ uint8_t status;
+ uint16_t timeout;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_EXT_PAGE_TIMEOUT 0x0c7f
+struct bt_hci_cmd_write_ext_page_timeout {
+ uint16_t timeout;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_READ_EXT_INQUIRY_LENGTH 0x0c80
+struct bt_hci_rsp_read_ext_inquiry_length {
+ uint8_t status;
+ uint16_t interval;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_EXT_INQUIRY_LENGTH 0x0c81
+struct bt_hci_cmd_write_ext_inquiry_length {
+ uint16_t interval;
+} __attribute__ ((packed));
+
#define BT_HCI_CMD_READ_LOCAL_VERSION 0x1001
struct bt_hci_rsp_read_local_version {
uint8_t status;
uint8_t num_filter;
} __attribute__ ((packed));
+#define BT_HCI_CMD_READ_LOOPBACK_MODE 0x1801
+struct bt_hci_rsp_read_loopback_mode {
+ uint8_t status;
+ uint8_t mode;
+} __attribute__ ((packed));
+
+#define BT_HCI_CMD_WRITE_LOOPBACK_MODE 0x1802
+struct bt_hci_cmd_write_loopback_mode {
+ uint8_t mode;
+} __attribute__ ((packed));
+
#define BT_HCI_CMD_ENABLE_DUT_MODE 0x1803
#define BT_HCI_CMD_WRITE_SSP_DEBUG_MODE 0x1804
uint8_t amp_status;
} __attribute__ ((packed));
+#define BT_HCI_EVT_TRIGGERED_CLOCK_CAPTURE 0x4e
+struct bt_hci_evt_triggered_clock_capture {
+ uint16_t handle;
+ uint8_t type;
+ uint32_t clock;
+ uint16_t clock_offset;
+} __attribute__ ((packed));
+
#define BT_HCI_EVT_SYNC_TRAIN_COMPLETE 0x4f
struct bt_hci_evt_sync_train_complete {
uint8_t status;
#include <sys/stat.h>
#include <arpa/inet.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
#include "btsnoop.h"
#include "src/shared/util.h"
#include "src/shared/btsnoop.h"
-#include "mainloop.h"
+#include "src/shared/mainloop.h"
+
#include "display.h"
#include "packet.h"
#include "hcidump.h"
"powered", "connectable", "fast-connectable", "discoverable",
"bondable", "link-security", "ssp", "br/edr", "hs", "le",
"advertising", "secure-conn", "debug-keys", "privacy",
+ "configuration", "static-addr",
};
static void mgmt_new_settings(uint16_t len, const void *buf)
static void mgmt_new_csrk(uint16_t len, const void *buf)
{
const struct mgmt_ev_new_csrk *ev = buf;
+ const char *type;
char addr[18];
if (len < sizeof(*ev)) {
ba2str(&ev->key.addr.bdaddr, addr);
- printf("@ New CSRK: %s (%d) %s\n", addr, ev->key.addr.type,
- ev->key.master ? "Master" : "Slave");
+ switch (ev->key.type) {
+ case 0x00:
+ type = "Local Unauthenticated";
+ break;
+ case 0x01:
+ type = "Remote Unauthenticated";
+ break;
+ case 0x02:
+ type = "Local Authenticated";
+ break;
+ case 0x03:
+ type = "Remote Authenticated";
+ break;
+ default:
+ type = "<unknown>";
+ break;
+ }
+
+ printf("@ New CSRK: %s (%d) %s (%u)\n", addr, ev->key.addr.type,
+ type, ev->key.type);
buf += sizeof(*ev);
len -= sizeof(*ev);
#include <sys/ioctl.h>
#include <sys/socket.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/hci_lib.h>
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+
+#include "src/shared/mainloop.h"
-#include "mainloop.h"
#include "packet.h"
#include "hcidump.h"
#include <string.h>
#include <inttypes.h>
-#include <bluetooth/bluetooth.h>
+#include "lib/bluetooth.h"
#include "src/shared/util.h"
#include "bt.h"
#include "avctp.h"
#include "rfcomm.h"
+/* L2CAP Control Field bit masks */
+#define L2CAP_CTRL_SAR_MASK 0xC000
+#define L2CAP_CTRL_REQSEQ_MASK 0x3F00
+#define L2CAP_CTRL_TXSEQ_MASK 0x007E
+#define L2CAP_CTRL_SUPERVISE_MASK 0x000C
+
+#define L2CAP_CTRL_RETRANS 0x0080
+#define L2CAP_CTRL_FINAL 0x0080
+#define L2CAP_CTRL_POLL 0x0010
+#define L2CAP_CTRL_FRAME_TYPE 0x0001 /* I- or S-Frame */
+
+#define L2CAP_CTRL_TXSEQ_SHIFT 1
+#define L2CAP_CTRL_SUPER_SHIFT 2
+#define L2CAP_CTRL_REQSEQ_SHIFT 8
+#define L2CAP_CTRL_SAR_SHIFT 14
+
+#define L2CAP_EXT_CTRL_TXSEQ_MASK 0xFFFC0000
+#define L2CAP_EXT_CTRL_SAR_MASK 0x00030000
+#define L2CAP_EXT_CTRL_SUPERVISE_MASK 0x00030000
+#define L2CAP_EXT_CTRL_REQSEQ_MASK 0x0000FFFC
+
+#define L2CAP_EXT_CTRL_POLL 0x00040000
+#define L2CAP_EXT_CTRL_FINAL 0x00000002
+#define L2CAP_EXT_CTRL_FRAME_TYPE 0x00000001 /* I- or S-Frame */
+
+#define L2CAP_EXT_CTRL_REQSEQ_SHIFT 2
+#define L2CAP_EXT_CTRL_SAR_SHIFT 16
+#define L2CAP_EXT_CTRL_SUPER_SHIFT 16
+#define L2CAP_EXT_CTRL_TXSEQ_SHIFT 18
+
+/* L2CAP Supervisory Function */
+#define L2CAP_SUPER_RR 0x00
+#define L2CAP_SUPER_REJ 0x01
+#define L2CAP_SUPER_RNR 0x02
+#define L2CAP_SUPER_SREJ 0x03
+
+/* L2CAP Segmentation and Reassembly */
+#define L2CAP_SAR_UNSEGMENTED 0x00
+#define L2CAP_SAR_START 0x01
+#define L2CAP_SAR_END 0x02
+#define L2CAP_SAR_CONTINUE 0x03
+
#define MAX_CHAN 64
struct chan_data {
uint16_t index;
uint16_t handle;
+ uint8_t ident;
uint16_t scid;
uint16_t dcid;
uint16_t psm;
uint8_t ctrlid;
uint8_t mode;
+ uint8_t ext_ctrl;
};
static struct chan_data chan_list[MAX_CHAN];
memset(&chan_list[n], 0, sizeof(chan_list[n]));
chan_list[n].index = frame->index;
chan_list[n].handle = frame->handle;
+ chan_list[n].ident = frame->ident;
if (frame->in)
chan_list[n].dcid = scid;
}
}
-static void assign_dcid(const struct l2cap_frame *frame,
- uint16_t dcid, uint16_t scid)
+static void assign_dcid(const struct l2cap_frame *frame, uint16_t dcid,
+ uint16_t scid)
{
int i;
if (chan_list[i].handle != frame->handle)
continue;
+ if (frame->ident != 0 && chan_list[i].ident != frame->ident)
+ continue;
+
if (frame->in) {
- if (chan_list[i].scid == scid) {
- chan_list[i].dcid = dcid;
- break;
+ if (scid) {
+ if (chan_list[i].scid == scid) {
+ chan_list[i].dcid = dcid;
+ break;
+ }
+ } else {
+ if (chan_list[i].scid && !chan_list[i].dcid) {
+ chan_list[i].dcid = dcid;
+ break;
+ }
}
} else {
- if (chan_list[i].dcid == scid) {
- chan_list[i].scid = dcid;
- break;
+ if (scid) {
+ if (chan_list[i].dcid == scid) {
+ chan_list[i].scid = dcid;
+ break;
+ }
+ } else {
+ if (chan_list[i].dcid && !chan_list[i].scid) {
+ chan_list[i].scid = dcid;
+ break;
+ }
}
}
}
}
}
-static uint16_t get_psm(const struct l2cap_frame *frame)
+static int get_chan_data_index(const struct l2cap_frame *frame)
{
int i;
chan_list[i].ctrlid == 0)
continue;
- if (chan_list[i].handle != frame->handle &&
+ if (chan_list[i].ctrlid != 0 &&
chan_list[i].ctrlid != frame->index)
continue;
+ if (chan_list[i].handle != frame->handle)
+ continue;
+
if (frame->in) {
if (chan_list[i].scid == frame->cid)
- return chan_list[i].psm;
+ return i;
} else {
if (chan_list[i].dcid == frame->cid)
- return chan_list[i].psm;
+ return i;
}
}
- return 0;
+ return -1;
+}
+
+static uint16_t get_psm(const struct l2cap_frame *frame)
+{
+ int i = get_chan_data_index(frame);
+
+ if (i < 0)
+ return 0;
+
+ return chan_list[i].psm;
}
static uint8_t get_mode(const struct l2cap_frame *frame)
{
+ int i = get_chan_data_index(frame);
+
+ if (i < 0)
+ return 0;
+
+ return chan_list[i].mode;
+}
+
+static uint16_t get_chan(const struct l2cap_frame *frame)
+{
+ int i = get_chan_data_index(frame);
+
+ if (i < 0)
+ return 0;
+
+ return i;
+}
+
+static void assign_ext_ctrl(const struct l2cap_frame *frame,
+ uint8_t ext_ctrl, uint16_t dcid)
+{
int i;
for (i = 0; i < MAX_CHAN; i++) {
- if (chan_list[i].index != frame->index &&
- chan_list[i].ctrlid == 0)
+ if (chan_list[i].index != frame->index)
continue;
- if (chan_list[i].handle != frame->handle &&
- chan_list[i].ctrlid != frame->index)
+ if (chan_list[i].handle != frame->handle)
continue;
if (frame->in) {
- if (chan_list[i].scid == frame->cid)
- return chan_list[i].mode;
+ if (chan_list[i].scid == dcid) {
+ chan_list[i].ext_ctrl = ext_ctrl;
+ break;
+ }
} else {
- if (chan_list[i].dcid == frame->cid)
- return chan_list[i].mode;
+ if (chan_list[i].dcid == dcid) {
+ chan_list[i].ext_ctrl = ext_ctrl;
+ break;
+ }
}
}
+}
+
+static uint8_t get_ext_ctrl(const struct l2cap_frame *frame)
+{
+ int i = get_chan_data_index(frame);
- return 0;
+ if (i < 0)
+ return 0;
+
+ return chan_list[i].ext_ctrl;
}
-static uint16_t get_chan(const struct l2cap_frame *frame)
+static char *sar2str(uint8_t sar)
{
- int i;
+ switch (sar) {
+ case L2CAP_SAR_UNSEGMENTED:
+ return "Unsegmented";
+ case L2CAP_SAR_START:
+ return "Start";
+ case L2CAP_SAR_END:
+ return "End";
+ case L2CAP_SAR_CONTINUE:
+ return "Continuation";
+ default:
+ return "Bad SAR";
+ }
+}
- for (i = 0; i < MAX_CHAN; i++) {
- if (chan_list[i].index != frame->index &&
- chan_list[i].ctrlid == 0)
- continue;
+static char *supervisory2str(uint8_t supervisory)
+{
+ switch (supervisory) {
+ case L2CAP_SUPER_RR:
+ return "Receiver Ready (RR)";
+ case L2CAP_SUPER_REJ:
+ return "Reject (REJ)";
+ case L2CAP_SUPER_RNR:
+ return "Receiver Not Ready (RNR)";
+ case L2CAP_SUPER_SREJ:
+ return "Select Reject (SREJ)";
+ default:
+ return "Bad Supervisory";
+ }
+}
- if (chan_list[i].handle != frame->handle &&
- chan_list[i].ctrlid != frame->index)
- continue;
+static void l2cap_ctrl_ext_parse(struct l2cap_frame *frame, uint32_t ctrl)
+{
+ printf(" %s:",
+ ctrl & L2CAP_EXT_CTRL_FRAME_TYPE ? "S-frame" : "I-frame");
- if (frame->in) {
- if (chan_list[i].scid == frame->cid)
- return i;
- } else {
- if (chan_list[i].dcid == frame->cid)
- return i;
+ if (ctrl & L2CAP_EXT_CTRL_FRAME_TYPE) {
+ printf(" %s",
+ supervisory2str((ctrl & L2CAP_EXT_CTRL_SUPERVISE_MASK) >>
+ L2CAP_EXT_CTRL_SUPER_SHIFT));
+
+ if (ctrl & L2CAP_EXT_CTRL_POLL)
+ printf(" P-bit");
+ } else {
+ uint8_t sar = (ctrl & L2CAP_EXT_CTRL_SAR_MASK) >>
+ L2CAP_EXT_CTRL_SAR_SHIFT;
+ printf(" %s", sar2str(sar));
+ if (sar == L2CAP_SAR_START) {
+ uint16_t len;
+
+ if (!l2cap_frame_get_le16(frame, &len))
+ return;
+
+ printf(" (len %d)", len);
}
+ printf(" TxSeq %d", (ctrl & L2CAP_EXT_CTRL_TXSEQ_MASK) >>
+ L2CAP_EXT_CTRL_TXSEQ_SHIFT);
}
- return 0;
+ printf(" ReqSeq %d", (ctrl & L2CAP_EXT_CTRL_REQSEQ_MASK) >>
+ L2CAP_EXT_CTRL_REQSEQ_SHIFT);
+
+ if (ctrl & L2CAP_EXT_CTRL_FINAL)
+ printf(" F-bit");
+}
+
+static void l2cap_ctrl_parse(struct l2cap_frame *frame, uint32_t ctrl)
+{
+ printf(" %s:",
+ ctrl & L2CAP_CTRL_FRAME_TYPE ? "S-frame" : "I-frame");
+
+ if (ctrl & 0x01) {
+ printf(" %s",
+ supervisory2str((ctrl & L2CAP_CTRL_SUPERVISE_MASK) >>
+ L2CAP_CTRL_SUPER_SHIFT));
+
+ if (ctrl & L2CAP_CTRL_POLL)
+ printf(" P-bit");
+ } else {
+ uint8_t sar;
+
+ sar = (ctrl & L2CAP_CTRL_SAR_MASK) >> L2CAP_CTRL_SAR_SHIFT;
+ printf(" %s", sar2str(sar));
+ if (sar == L2CAP_SAR_START) {
+ uint16_t len;
+
+ if (!l2cap_frame_get_le16(frame, &len))
+ return;
+
+ printf(" (len %d)", len);
+ }
+ printf(" TxSeq %d", (ctrl & L2CAP_CTRL_TXSEQ_MASK) >>
+ L2CAP_CTRL_TXSEQ_SHIFT);
+ }
+
+ printf(" ReqSeq %d", (ctrl & L2CAP_CTRL_REQSEQ_MASK) >>
+ L2CAP_CTRL_REQSEQ_SHIFT);
+
+ if (ctrl & L2CAP_CTRL_FINAL)
+ printf(" F-bit");
}
#define MAX_INDEX 16
};
static void print_config_options(const struct l2cap_frame *frame,
- uint8_t offset, uint16_t dcid, bool response)
+ uint8_t offset, uint16_t cid, bool response)
{
const uint8_t *data = frame->data + offset;
uint16_t size = frame->size - offset;
break;
case 0x04:
if (response)
- assign_mode(frame, data[consumed + 2], dcid);
+ assign_mode(frame, data[consumed + 2], cid);
switch (data[consumed + 2]) {
case 0x00:
get_le32(data + consumed + 14));
break;
case 0x07:
- print_field(" Max window size: %d",
+ print_field(" Extended window size: %d",
get_le16(data + consumed + 2));
+ assign_ext_ctrl(frame, 1, cid);
break;
default:
packet_hexdump(data + consumed + 2, len);
print_field("Credits: %u", le16_to_cpu(pdu->credits));
print_conn_result(pdu->result);
- /*assign_dcid(frame, le16_to_cpu(pdu->dcid), le16_to_cpu(pdu->scid));*/
+ assign_dcid(frame, le16_to_cpu(pdu->dcid), 0);
}
static void sig_le_flowctl_creds(const struct l2cap_frame *frame)
{ },
};
-static void l2cap_frame_init(struct l2cap_frame *frame,
- uint16_t index, bool in, uint16_t handle,
+static void l2cap_frame_init(struct l2cap_frame *frame, uint16_t index, bool in,
+ uint16_t handle, uint8_t ident,
uint16_t cid, const void *data, uint16_t size)
{
frame->index = index;
frame->in = in;
frame->handle = handle;
+ frame->ident = ident;
frame->cid = cid;
frame->data = data;
frame->size = size;
}
}
- l2cap_frame_init(&frame, index, in, handle, cid, data, len);
+ l2cap_frame_init(&frame, index, in, handle, hdr->ident, cid,
+ data, len);
opcode_data->func(&frame);
data += len;
}
}
- l2cap_frame_init(&frame, index, in, handle, cid, data, len);
+ l2cap_frame_init(&frame, index, in, handle, hdr->ident, cid, data, len);
opcode_data->func(&frame);
}
break;
}
- l2cap_frame_init(&frame, index, in, handle, cid, data, size);
+ l2cap_frame_init(&frame, index, in, handle, 0, cid, data, size);
}
static void print_controller_list(const uint8_t *data, uint16_t size)
}
}
- l2cap_frame_init(&frame, index, in, handle, cid, data + 6, len);
+ l2cap_frame_init(&frame, index, in, handle, 0, cid, data + 6, len);
opcode_data->func(&frame);
}
}
}
- l2cap_frame_init(&frame, index, in, handle, cid, data + 1, size - 1);
+ l2cap_frame_init(&frame, index, in, handle, 0, cid, data + 1, size - 1);
opcode_data->func(&frame);
}
}
}
- l2cap_frame_init(&frame, index, in, handle, cid, data + 1, size - 1);
+ l2cap_frame_init(&frame, index, in, handle, 0, cid, data + 1, size - 1);
opcode_data->func(&frame);
}
uint16_t cid, const void *data, uint16_t size)
{
struct l2cap_frame frame;
+ uint32_t ctrl32 = 0;
+ uint16_t ctrl16 = 0;
+ uint8_t ext_ctrl;
switch (cid) {
case 0x0001:
smp_packet(index, in, handle, cid, data, size);
break;
default:
- l2cap_frame_init(&frame, index, in, handle, cid, data, size);
+ l2cap_frame_init(&frame, index, in, handle, 0, cid, data, size);
+
+ if (frame.mode > 0) {
+ ext_ctrl = get_ext_ctrl(&frame);
+
+ if (ext_ctrl) {
+ if (!l2cap_frame_get_le32(&frame, &ctrl32))
+ return;
+
+ print_indent(6, COLOR_CYAN, "Channel:", "",
+ COLOR_OFF, " %d len %d"
+ " ext_ctrl 0x%8.8x"
+ " [PSM %d mode %d] {chan %d}",
+ cid, size, ctrl32, frame.psm,
+ frame.mode, frame.chan);
+
+ l2cap_ctrl_ext_parse(&frame, ctrl32);
+ } else {
+ if (!l2cap_frame_get_le16(&frame, &ctrl16))
+ return;
- print_indent(6, COLOR_CYAN, "Channel:", "", COLOR_OFF,
- " %d len %d [PSM %d mode %d] {chan %d}",
+ print_indent(6, COLOR_CYAN, "Channel:", "",
+ COLOR_OFF, " %d len %d"
+ " ctrl 0x%4.4x"
+ " [PSM %d mode %d] {chan %d}",
+ cid, size, ctrl16, frame.psm,
+ frame.mode, frame.chan);
+
+ l2cap_ctrl_parse(&frame, ctrl16);
+ }
+
+ printf("\n");
+ } else {
+ print_indent(6, COLOR_CYAN, "Channel:", "", COLOR_OFF,
+ " %d len %d [PSM %d mode %d] {chan %d}",
cid, size, frame.psm,
frame.mode, frame.chan);
+ }
switch (frame.psm) {
case 0x0001:
uint16_t index;
bool in;
uint16_t handle;
+ uint8_t ident;
uint16_t cid;
uint16_t psm;
uint16_t chan;
frame->index = source->index;
frame->in = source->in;
frame->handle = source->handle;
+ frame->ident = source->ident;
frame->cid = source->cid;
frame->psm = source->psm;
frame->chan = source->chan;
#include <string.h>
#include <getopt.h>
-#include "mainloop.h"
+#include "src/shared/mainloop.h"
+
#include "packet.h"
#include "lmp.h"
#include "keys.h"
#include <time.h>
#include <sys/time.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/hci_lib.h>
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
#include "src/shared/util.h"
#include "src/shared/btsnoop.h"
{ 0x1b, "SCO Offset Rejected" },
{ 0x1c, "SCO Interval Rejected" },
{ 0x1d, "SCO Air Mode Rejected" },
- { 0x1e, "Invalid LMP Parameters" },
+ { 0x1e, "Invalid LMP Parameters / Invalid LL Parameters" },
{ 0x1f, "Unspecified Error" },
- { 0x20, "Unsupported LMP Parameter Value" },
+ { 0x20, "Unsupported LMP Parameter Value / "
+ "Unsupported LL Parameter Value" },
{ 0x21, "Role Change Not Allowed" },
{ 0x22, "LMP Response Timeout / LL Response Timeout" },
{ 0x23, "LMP Error Transaction Collision" },
{ 0x38, "Host Busy - Pairing" },
{ 0x39, "Connection Rejected due to No Suitable Channel Found" },
{ 0x3a, "Controller Busy" },
- { 0x3b, "Unacceptable Connection Interval" },
+ { 0x3b, "Unacceptable Connection Parameters" },
{ 0x3c, "Directed Advertising Timeout" },
{ 0x3d, "Connection Terminated due to MIC Failure" },
{ 0x3e, "Connection Failed to be Established" },
print_field("Type: %s (0x%2.2x)", str, type);
}
-static void print_power_level(int8_t level)
+static void print_power_level(int8_t level, const char *type)
{
- print_field("TX power: %d dBm", level);
+ print_field("TX power%s%s%s: %d dBm",
+ type ? " (" : "", type ? type : "", type ? ")" : "", level);
}
static void print_sync_flow_control(uint8_t enable)
str = "Linear";
break;
case 0x01:
- str ="u-law";
+ str = "u-law";
break;
case 0x02:
str = "A-law";
str = "CVSD";
break;
case 0x01:
- str ="u-law";
+ str = "u-law";
break;
case 0x02:
str = "A-law";
print_field("Mode: %s (0x%2.2x)", str, mode);
}
+static void print_erroneous_reporting(uint8_t mode)
+{
+ const char *str;
+
+ switch (mode) {
+ case 0x00:
+ str = "Disabled";
+ break;
+ case 0x01:
+ str = "Enabled";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("Mode: %s (0x%2.2x)", str, mode);
+}
+
+static void print_loopback_mode(uint8_t mode)
+{
+ const char *str;
+
+ switch (mode) {
+ case 0x00:
+ str = "No Loopback";
+ break;
+ case 0x01:
+ str = "Local Loopback";
+ break;
+ case 0x02:
+ str = "Remote Loopback";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("Mode: %s (0x%2.2x)", str, mode);
+}
+
static void print_simple_pairing_mode(uint8_t mode)
{
const char *str;
print_field("OOB data: %s (0x%2.2x)", str, oob_data);
}
+static void print_oob_data_response(uint8_t oob_data)
+{
+ const char *str;
+
+ switch (oob_data) {
+ case 0x00:
+ str = "Authentication data not present";
+ break;
+ case 0x01:
+ str = "Authentication data present";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("OOB data: %s (0x%2.2x)", str, oob_data);
+}
+
static void print_authentication(uint8_t authentication)
{
const char *str;
if (count > 1) {
print_field(" Channel %u-%u",
- start, start + count - 1 );
+ start, start + count - 1);
count = 0;
} else if (count > 0) {
print_field(" Channel %u", start);
packet_print_company("Manufacturer", le16_to_cpu(manufacturer));
}
+static const struct {
+ uint16_t ver;
+ const char *str;
+} broadcom_uart_subversion_table[] = {
+ { 0x410e, "BCM43341B0" }, /* 002.001.014 */
+ { }
+};
+
+static const struct {
+ uint16_t ver;
+ const char *str;
+} broadcom_usb_subversion_table[] = {
+ { 0x210b, "BCM43142A0" }, /* 001.001.011 */
+ { 0x2112, "BCM4314A0" }, /* 001.001.018 */
+ { 0x2118, "BCM20702A0" }, /* 001.001.024 */
+ { 0x2126, "BCM4335A0" }, /* 001.001.038 */
+ { 0x220e, "BCM20702A1" }, /* 001.002.014 */
+ { 0x230f, "BCM4354A2" }, /* 001.003.015 */
+ { 0x4106, "BCM4335B0" }, /* 002.001.006 */
+ { 0x410e, "BCM20702B0" }, /* 002.001.014 */
+ { 0x6109, "BCM4335C0" }, /* 003.001.009 */
+ { 0x610c, "BCM4354" }, /* 003.001.012 */
+ { }
+};
+
+static void print_manufacturer_broadcom(uint16_t subversion, uint16_t revision)
+{
+ uint16_t ver = le16_to_cpu(subversion);
+ uint16_t rev = le16_to_cpu(revision);
+ const char *str = NULL;
+ int i;
+
+ switch ((rev & 0xf000) >> 12) {
+ case 0:
+ for (i = 0; broadcom_uart_subversion_table[i].str; i++) {
+ if (broadcom_uart_subversion_table[i].ver == ver) {
+ str = broadcom_uart_subversion_table[i].str;
+ break;
+ }
+ }
+ break;
+ case 1:
+ case 2:
+ for (i = 0; broadcom_usb_subversion_table[i].str; i++) {
+ if (broadcom_usb_subversion_table[i].ver == ver) {
+ str = broadcom_usb_subversion_table[i].str;
+ break;
+ }
+ }
+ break;
+ }
+
+ if (str)
+ print_field(" Firmware: %3.3u.%3.3u.%3.3u (%s)",
+ (ver & 0x7000) >> 13,
+ (ver & 0x1f00) >> 8, ver & 0x00ff, str);
+ else
+ print_field(" Firmware: %3.3u.%3.3u.%3.3u",
+ (ver & 0x7000) >> 13,
+ (ver & 0x1f00) >> 8, ver & 0x00ff);
+
+ if (rev != 0xffff)
+ print_field(" Build: %4.4u", rev & 0x0fff);
+}
+
static const char *get_supported_command(int bit);
static void print_commands(const uint8_t *commands)
LE_STATE_MASTER_SLAVE },
{ 39, LE_STATE_CONN_SLAVE | LE_STATE_HIGH_DIRECT_ADV |
LE_STATE_SLAVE_SLAVE },
- { 40, LE_STATE_CONN_SLAVE| LE_STATE_LOW_DIRECT_ADV |
+ { 40, LE_STATE_CONN_SLAVE | LE_STATE_LOW_DIRECT_ADV |
LE_STATE_SLAVE_SLAVE },
{ 41, LE_STATE_INITIATING | LE_STATE_CONN_SLAVE |
LE_STATE_MASTER_SLAVE },
if (count > 1) {
print_field(" Channel %u-%u",
- start, start + count - 1 );
+ start, start + count - 1);
count = 0;
} else if (count > 0) {
print_field(" Channel %u", start);
static void accept_logic_link_cmd(const void *data, uint8_t size)
{
- const struct bt_hci_cmd_accept_logic_link *cmd = data;
+ const struct bt_hci_cmd_accept_logic_link *cmd = data;
print_phy_handle(cmd->phy_handle);
print_flow_spec("TX", cmd->tx_flow_spec);
static void logic_link_cancel_rsp(const void *data, uint8_t size)
{
- const struct bt_hci_rsp_logic_link_cancel *rsp = data;
+ const struct bt_hci_rsp_logic_link_cancel *rsp = data;
print_status(rsp->status);
print_phy_handle(rsp->phy_handle);
print_flow_spec("RX", cmd->rx_flow_spec);
}
+static void enhanced_setup_sync_conn_cmd(const void *data, uint8_t size)
+{
+ const struct bt_hci_cmd_enhanced_setup_sync_conn *cmd = data;
+
+ print_handle(cmd->handle);
+ print_field("Transmit bandwidth: %d", le32_to_cpu(cmd->tx_bandwidth));
+ print_field("Receive bandwidth: %d", le32_to_cpu(cmd->rx_bandwidth));
+
+ /* TODO */
+
+ print_field("Max latency: %d", le16_to_cpu(cmd->max_latency));
+ print_pkt_type_sco(cmd->pkt_type);
+ print_retransmission_effort(cmd->retrans_effort);
+}
+
+static void enhanced_accept_sync_conn_request_cmd(const void *data, uint8_t size)
+{
+ const struct bt_hci_cmd_enhanced_accept_sync_conn_request *cmd = data;
+
+ print_bdaddr(cmd->bdaddr);
+ print_field("Transmit bandwidth: %d", le32_to_cpu(cmd->tx_bandwidth));
+ print_field("Receive bandwidth: %d", le32_to_cpu(cmd->rx_bandwidth));
+
+ /* TODO */
+
+ print_field("Max latency: %d", le16_to_cpu(cmd->max_latency));
+ print_pkt_type_sco(cmd->pkt_type);
+ print_retransmission_effort(cmd->retrans_effort);
+}
+
static void truncated_page_cmd(const void *data, uint8_t size)
{
const struct bt_hci_cmd_truncated_page *cmd = data;
print_status(rsp->status);
print_handle(rsp->handle);
- print_power_level(rsp->level);
+ print_power_level(rsp->level, NULL);
}
static void read_sync_flow_control_rsp(const void *data, uint8_t size)
le16_to_cpu(cmd->sco_max_pkt));
}
+static void host_num_completed_packets_cmd(const void *data, uint8_t size)
+{
+ const struct bt_hci_cmd_host_num_completed_packets *cmd = data;
+
+ print_field("Num handles: %d", cmd->num_handles);
+ print_handle(cmd->handle);
+ print_field("Count: %d", le16_to_cpu(cmd->count));
+
+ if (size > sizeof(*cmd))
+ packet_hexdump(data + sizeof(*cmd), size - sizeof(*cmd));
+}
+
static void read_link_supv_timeout_cmd(const void *data, uint8_t size)
{
const struct bt_hci_cmd_read_link_supv_timeout *cmd = data;
const struct bt_hci_rsp_read_inquiry_resp_tx_power *rsp = data;
print_status(rsp->status);
- print_power_level(rsp->level);
+ print_power_level(rsp->level, NULL);
}
static void write_inquiry_tx_power_cmd(const void *data, uint8_t size)
{
const struct bt_hci_cmd_write_inquiry_tx_power *cmd = data;
- print_power_level(cmd->level);
+ print_power_level(cmd->level, NULL);
+}
+
+static void read_erroneous_reporting_rsp(const void *data, uint8_t size)
+{
+ const struct bt_hci_rsp_read_erroneous_reporting *rsp = data;
+
+ print_status(rsp->status);
+ print_erroneous_reporting(rsp->mode);
+}
+
+static void write_erroneous_reporting_cmd(const void *data, uint8_t size)
+{
+ const struct bt_hci_cmd_write_erroneous_reporting *cmd = data;
+
+ print_erroneous_reporting(cmd->mode);
}
static void enhanced_flush_cmd(const void *data, uint8_t size)
print_flow_control_mode(cmd->mode);
}
+static void read_enhanced_tx_power_cmd(const void *data, uint8_t size)
+{
+ const struct bt_hci_cmd_read_enhanced_tx_power *cmd = data;
+
+ print_handle(cmd->handle);
+ print_power_type(cmd->type);
+}
+
+static void read_enhanced_tx_power_rsp(const void *data, uint8_t size)
+{
+ const struct bt_hci_rsp_read_enhanced_tx_power *rsp = data;
+
+ print_status(rsp->status);
+ print_handle(rsp->handle);
+ print_power_level(rsp->level_gfsk, "GFSK");
+ print_power_level(rsp->level_dqpsk, "DQPSK");
+ print_power_level(rsp->level_8dpsk, "8DPSK");
+}
+
+static void short_range_mode_cmd(const void *data, uint8_t size)
+{
+ const struct bt_hci_cmd_short_range_mode *cmd = data;
+
+ print_phy_handle(cmd->phy_handle);
+ print_short_range_mode(cmd->mode);
+}
+
static void read_le_host_supported_rsp(const void *data, uint8_t size)
{
const struct bt_hci_rsp_read_le_host_supported *rsp = data;
print_randomizer_p256(rsp->randomizer256);
}
+static void read_ext_page_timeout_rsp(const void *data, uint8_t size)
+{
+ const struct bt_hci_rsp_read_ext_page_timeout *rsp = data;
+
+ print_status(rsp->status);
+ print_timeout(rsp->timeout);
+}
+
+static void write_ext_page_timeout_cmd(const void *data, uint8_t size)
+{
+ const struct bt_hci_cmd_write_ext_page_timeout *cmd = data;
+
+ print_timeout(cmd->timeout);
+}
+
+static void read_ext_inquiry_length_rsp(const void *data, uint8_t size)
+{
+ const struct bt_hci_rsp_read_ext_inquiry_length *rsp = data;
+
+ print_status(rsp->status);
+ print_interval(rsp->interval);
+}
+
+static void write_ext_inquiry_length_cmd(const void *data, uint8_t size)
+{
+ const struct bt_hci_cmd_write_ext_inquiry_length *cmd = data;
+
+ print_interval(cmd->interval);
+}
+
static void read_local_version_rsp(const void *data, uint8_t size)
{
const struct bt_hci_rsp_read_local_version *rsp = data;
}
print_manufacturer(rsp->manufacturer);
+
+ switch (le16_to_cpu(rsp->manufacturer)) {
+ case 15:
+ print_manufacturer_broadcom(rsp->lmp_subver, rsp->hci_rev);
+ break;
+ }
}
static void read_local_commands_rsp(const void *data, uint8_t size)
print_field("Clock captures to filter: %u", cmd->num_filter);
}
+static void read_loopback_mode_rsp(const void *data, uint8_t size)
+{
+ const struct bt_hci_rsp_read_loopback_mode *rsp = data;
+
+ print_status(rsp->status);
+ print_loopback_mode(rsp->mode);
+}
+
+static void write_loopback_mode_cmd(const void *data, uint8_t size)
+{
+ const struct bt_hci_cmd_write_loopback_mode *cmd = data;
+
+ print_loopback_mode(cmd->mode);
+}
+
static void write_ssp_debug_mode_cmd(const void *data, uint8_t size)
{
const struct bt_hci_cmd_write_ssp_debug_mode *cmd = data;
const struct bt_hci_rsp_le_read_adv_tx_power *rsp = data;
print_status(rsp->status);
- print_power_level(rsp->level);
+ print_power_level(rsp->level, NULL);
}
static void le_set_adv_data_cmd(const void *data, uint8_t size)
logic_link_cancel_rsp, 3, true },
{ 0x043c, 175, "Flow Specifcation Modify",
flow_spec_modify_cmd, 34, true },
- { 0x043d, 235, "Enhanced Setup Synchronous Connection" },
- { 0x043e, 236, "Enhanced Accept Synchronous Connection Request" },
+ { 0x043d, 235, "Enhanced Setup Synchronous Connection",
+ enhanced_setup_sync_conn_cmd, 59, true },
+ { 0x043e, 236, "Enhanced Accept Synchronous Connection Request",
+ enhanced_accept_sync_conn_request_cmd, 63, true },
{ 0x043f, 246, "Truncated Page",
truncated_page_cmd, 9, true },
{ 0x0440, 247, "Truncated Page Cancel",
status_bdaddr_rsp, 7, true },
/* OGF 2 - Link Policy */
- { 0x0801, 33, "Holde Mode",
+ { 0x0801, 33, "Hold Mode",
hold_mode_cmd, 6, true },
{ 0x0803, 34, "Sniff Mode",
sniff_mode_cmd, 10, true },
{ 0x0c33, 86, "Host Buffer Size",
host_buffer_size_cmd, 7, true,
status_rsp, 1, true },
- { 0x0c35, 87, "Host Number of Completed Packets" },
+ { 0x0c35, 87, "Host Number of Completed Packets",
+ host_num_completed_packets_cmd, 1, true },
{ 0x0c36, 88, "Read Link Supervision Timeout",
read_link_supv_timeout_cmd, 2, true,
read_link_supv_timeout_rsp, 5, true },
{ 0x0c59, 145, "Write Inquiry Transmit Power Level",
write_inquiry_tx_power_cmd, 1, true,
status_rsp, 1, true },
- { 0x0c5a, 146, "Read Default Erroneous Reporting" },
- { 0x0c5b, 147, "Write Default Erroneous Reporting" },
+ { 0x0c5a, 146, "Read Default Erroneous Data Reporting",
+ null_cmd, 0, true,
+ read_erroneous_reporting_rsp, 2, true },
+ { 0x0c5b, 147, "Write Default Erroneous Data Reporting",
+ write_erroneous_reporting_cmd, 1, true,
+ status_rsp, 1, true },
{ 0x0c5f, 158, "Enhanced Flush",
enhanced_flush_cmd, 3, true },
{ 0x0c60, 162, "Send Keypress Notification",
{ 0x0c67, 185, "Write Flow Control Mode",
write_flow_control_mode_cmd, 1, true,
status_rsp, 1, true },
- { 0x0c68, 192, "Read Enhanced Transmit Power Level" },
+ { 0x0c68, 192, "Read Enhanced Transmit Power Level",
+ read_enhanced_tx_power_cmd, 3, true,
+ read_enhanced_tx_power_rsp, 6, true },
{ 0x0c69, 194, "Read Best Effort Flush Timeout" },
{ 0x0c6a, 195, "Write Best Effort Flush Timeout" },
- { 0x0c6b, 196, "Short Range Mode" },
+ { 0x0c6b, 196, "Short Range Mode",
+ short_range_mode_cmd, 2, true },
{ 0x0c6c, 197, "Read LE Host Supported",
null_cmd, 0, true,
read_le_host_supported_rsp, 3, true },
{ 0x0c7d, 262, "Read Local OOB Extended Data",
null_cmd, 0, true,
read_local_oob_ext_data_rsp, 65, true },
- { 0x0c7e, 264, "Read Extended Page Timeout" },
- { 0x0c7f, 265, "Write Extended Page Timeout" },
- { 0x0c80, 266, "Read Extended Inquiry Length" },
- { 0x0c81, 267, "Write Extended Inquiry Length" },
+ { 0x0c7e, 264, "Read Extended Page Timeout",
+ null_cmd, 0, true,
+ read_ext_page_timeout_rsp, 3, true },
+ { 0x0c7f, 265, "Write Extended Page Timeout",
+ write_ext_page_timeout_cmd, 2, true,
+ status_rsp, 1, true },
+ { 0x0c80, 266, "Read Extended Inquiry Length",
+ null_cmd, 0, true,
+ read_ext_inquiry_length_rsp, 3, true },
+ { 0x0c81, 267, "Write Extended Inquiry Length",
+ write_ext_inquiry_length_cmd, 2, true,
+ status_rsp, 1, true },
/* OGF 4 - Information Parameter */
{ 0x1001, 115, "Read Local Version Information",
status_rsp, 1, true },
/* OGF 6 - Testing */
- { 0x1801, 128, "Read Loopback Mode" },
- { 0x1802, 129, "Write Loopback Mode" },
+ { 0x1801, 128, "Read Loopback Mode",
+ null_cmd, 0, true,
+ read_loopback_mode_rsp, 2, true },
+ { 0x1802, 129, "Write Loopback Mode",
+ write_loopback_mode_cmd, 1, true,
+ status_rsp, 1, true },
{ 0x1803, 130, "Enable Device Under Test Mode",
null_cmd, 0, true,
status_rsp, 1, true },
print_handle(evt->handle);
print_lmp_version(evt->lmp_ver, evt->lmp_subver);
print_manufacturer(evt->manufacturer);
+
+ switch (le16_to_cpu(evt->manufacturer)) {
+ case 15:
+ print_manufacturer_broadcom(evt->lmp_subver, 0xffff);
+ break;
+ }
}
static void qos_setup_complete_evt(const void *data, uint8_t size)
print_bdaddr(evt->bdaddr);
print_io_capability(evt->capability);
- print_oob_data(evt->oob_data);
+ print_oob_data_response(evt->oob_data);
print_authentication(evt->authentication);
}
print_amp_status(evt->amp_status);
}
+static void triggered_clock_capture_evt(const void *data, uint8_t size)
+{
+ const struct bt_hci_evt_triggered_clock_capture *evt = data;
+
+ print_handle(evt->handle);
+ print_clock_type(evt->type);
+ print_clock(evt->clock);
+ print_clock_offset(evt->clock_offset);
+}
+
static void sync_train_complete_evt(const void *data, uint8_t size)
{
const struct bt_hci_evt_sync_train_complete *evt = data;
short_range_mode_change_evt, 3, true },
{ 0x4d, "AMP Status Change",
amp_status_change_evt, 2, true },
- { 0x4e, "Triggered Clock Capture" },
+ { 0x4e, "Triggered Clock Capture",
+ triggered_clock_capture_evt, 9, true },
{ 0x4f, "Synchronization Train Complete",
sync_train_complete_evt, 1, true },
{ 0x50, "Synchronization Train Received",
sprintf(extra_str, "(0x%2.2x) plen %d", hdr->evt, hdr->plen);
print_packet(tv, index, '>', event_color, "HCI Event",
- event_str, extra_str);
+ event_str, extra_str);
if (!event_data || !event_data->func) {
packet_hexdump(data, size);
#include <ctype.h>
#include <inttypes.h>
-#include <bluetooth/bluetooth.h>
+#include "lib/bluetooth.h"
#include "src/shared/util.h"
#include "bt.h"
#include <string.h>
#include <inttypes.h>
-#include <bluetooth/bluetooth.h>
+#include "lib/bluetooth.h"
#include "src/shared/util.h"
#include <inttypes.h>
#include <glib.h>
-#include <gdbus/gdbus.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/rfcomm.h>
-#include <bluetooth/sdp.h>
-#include <bluetooth/sdp_lib.h>
+#include "lib/bluetooth.h"
+#include "lib/rfcomm.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "gdbus/gdbus.h"
#include "btio/btio.h"
-#include "log.h"
+#include "obexd/src/log.h"
#include "transport.h"
#include "bluetooth.h"
#endif
#include <glib.h>
-#include <gdbus/gdbus.h>
-#include "log.h"
+#include "gdbus/gdbus.h"
+
+#include "obexd/src/log.h"
#include "dbus.h"
static void append_variant(DBusMessageIter *iter,
#include <string.h>
#include <errno.h>
#include <glib.h>
-#include <gdbus/gdbus.h>
+#include "gdbus/gdbus.h"
+
+#include "obexd/src/log.h"
#include "transfer.h"
#include "session.h"
#include "driver.h"
-#include "log.h"
static GSList *drivers = NULL;
#include <errno.h>
#include <string.h>
-#include <gdbus/gdbus.h>
+#include "gdbus/gdbus.h"
+#include "obexd/src/log.h"
#include "dbus.h"
-#include "log.h"
-
#include "transfer.h"
#include "session.h"
#include "driver.h"
#include <syslog.h>
#include <glib.h>
-#include <gdbus/gdbus.h>
-#include "log.h"
+#include "gdbus/gdbus.h"
+
+#include "obexd/src/log.h"
+#include "obexd/src/manager.h"
#include "transfer.h"
#include "session.h"
-#include "manager.h"
#include "bluetooth.h"
#include "opp.h"
#include "ftp.h"
#include "pbap.h"
#include "sync.h"
#include "map.h"
-#include "obexd/src/manager.h"
+#include "manager.h"
#ifdef __TIZEN_PATCH__
#include "mns-tizen.h"
#endif
int client_manager_init(void);
void client_manager_exit(void);
-#ifdef __TIZEN_PATCH__
-void release_session(struct obc_session *session);
-#endif
#include <stdbool.h>
#include <inttypes.h>
-#include "log.h"
+#include "gdbus/gdbus.h"
+
+#include "obexd/src/log.h"
#include "map-event.h"
-#include <gdbus/gdbus.h>
#include "transfer.h"
#include "session.h"
#include <stdbool.h>
#include <inttypes.h>
#include <stdlib.h>
+
#include <glib.h>
-#include <gdbus/gdbus.h>
-#include <gobex/gobex-apparam.h>
-#include <bluetooth/sdp.h>
+#include "lib/sdp.h"
+
+#include "gobex/gobex-apparam.h"
+#include "gdbus/gdbus.h"
+
+#include "obexd/src/log.h"
+#include "obexd/src/map_ap.h"
#include "dbus.h"
-#include "log.h"
-#include "map_ap.h"
#include "map-event.h"
#include "map.h"
#include <stdbool.h>
#include <stdlib.h>
-#include <gobex/gobex.h>
-#include <gobex/gobex-apparam.h>
-
-#include "obexd.h"
-#include "plugin.h"
-#include "log.h"
-#include "obex.h"
-#include "service.h"
-#include "mimetype.h"
-#include "map_ap.h"
+#include "gobex/gobex.h"
+#include "gobex/gobex-apparam.h"
+
+#include "obexd/src/obexd.h"
+#include "obexd/src/plugin.h"
+#include "obexd/src/log.h"
+#include "obexd/src/obex.h"
+#include "obexd/src/service.h"
+#include "obexd/src/mimetype.h"
+#include "obexd/src/map_ap.h"
#include "map-event.h"
#include "obexd/src/manager.h"
#endif
#include <errno.h>
-#include <gdbus/gdbus.h>
-#include "log.h"
+#include "gdbus/gdbus.h"
+
+#include "obexd/src/log.h"
#include "transfer.h"
#include "session.h"
#include <errno.h>
#include <string.h>
#include <stdio.h>
+
#include <glib.h>
-#include <gdbus/gdbus.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/sdp.h>
-#include <gobex/gobex-apparam.h>
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+
+#include "gobex/gobex-apparam.h"
+#include "gdbus/gdbus.h"
-#include "log.h"
+#include "obexd/src/log.h"
#include "transfer.h"
#include "session.h"
} else
return NULL;
+#ifdef __TIZEN_PATCH__
+ if (!g_ascii_strcasecmp(item, "nil"))
+ return path;
+#endif
+
if (!g_ascii_strcasecmp(item, "pb") ||
!g_ascii_strcasecmp(item, "ich") ||
!g_ascii_strcasecmp(item, "och") ||
}
request = pending_request_new(pbap, message);
-
- obc_session_setpath(pbap->session, path, pbap_setpath_cb, request,
- &err);
+#ifdef __TIZEN_PATCH__
+ if (pbap->path == NULL || strlen(pbap->path) == 0)
+ obc_session_setpath(pbap->session, path + 1, pbap_setpath_cb, request,
+ &err);
+ else
+ obc_session_setpath(pbap->session, path, pbap_setpath_cb, request,
+ &err);
+#else
+ obc_session_setpath(pbap->session, path, pbap_setpath_cb, request, &err);
+#endif
if (err != NULL) {
DBusMessage *reply;
reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed",
struct pbap_data *pbap = user_data;
GObexApparam *apparam;
DBusMessageIter args;
+#ifdef __TIZEN_PATCH__
+ const char *pb_folder;
+#endif
if (!pbap->path)
return g_dbus_create_error(message,
dbus_message_iter_init(message, &args);
+#ifdef __TIZEN_PATCH__
+ if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING)
+ return g_dbus_create_error(message,
+ ERROR_INTERFACE ".InvalidArguments", NULL);
+
+ dbus_message_iter_get_basic(&args, &pb_folder);
+ dbus_message_iter_next(&args);
+#endif
+
apparam = g_obex_apparam_set_uint16(NULL, MAXLISTCOUNT_TAG,
DEFAULT_COUNT);
apparam = g_obex_apparam_set_uint16(apparam, LISTSTARTOFFSET_TAG,
ERROR_INTERFACE ".InvalidArguments", NULL);
}
+#ifdef __TIZEN_PATCH__
+ return pull_vcard_listing(pbap, message, pb_folder, apparam);
+#else
return pull_vcard_listing(pbap, message, "", apparam);
+#endif
}
static GObexApparam *parse_attribute(GObexApparam *apparam, const char *field)
{ "properties", "a{sv}" }),
pbap_pull_vcard) },
{ GDBUS_ASYNC_METHOD("List",
- GDBUS_ARGS({ "filters", "a{sv}" }),
+#ifdef __TIZEN_PATCH__
+ GDBUS_ARGS({ "folder", "s" }, {"filters", "a{sv}" }),
+#else
+ GDBUS_ARGS({"filters", "a{sv}" }),
+#endif
GDBUS_ARGS({ "vcard_listing", "a(ss)" }),
pbap_list) },
{ GDBUS_ASYNC_METHOD("Search",
#include <sys/stat.h>
#include <glib.h>
-#include <gdbus/gdbus.h>
-#include <gobex/gobex.h>
+#include "gdbus/gdbus.h"
+#include "gobex/gobex.h"
+
+#include "obexd/src/log.h"
#include "dbus.h"
-#include "log.h"
#include "transfer.h"
#include "session.h"
#ifdef __TIZEN_PATCH__
data->user_data = user_data;
data->remaining = g_strsplit(strlen(path) ? path : "/", "/", 0);
+ if (!data->remaining || !data->remaining[0]) {
+ error("obc_session_setpath: invalid path %s", path);
+ g_set_error(err, OBEX_IO_ERROR, -EINVAL, "Invalid argument");
+ return 0;
+ }
+
p = pending_request_new(session, session_process_setpath, NULL,
setpath_op_complete, data, setpath_data_free);
session_queue(p);
#include <stdint.h>
#include <glib.h>
-#include <gdbus/gdbus.h>
struct obc_session;
GError **err);
void obc_session_cancel(struct obc_session *session, guint id,
gboolean remove);
+#ifdef __TIZEN_PATCH__
+void release_session(struct obc_session *session);
+#endif
#include <string.h>
#include <glib.h>
-#include <gdbus/gdbus.h>
-#include "log.h"
+#include "gdbus/gdbus.h"
+
+#include "obexd/src/log.h"
#include "transfer.h"
#include "session.h"
#include <inttypes.h>
#include <glib.h>
-#include <gdbus/gdbus.h>
-#include <gobex/gobex.h>
+#include "gdbus/gdbus.h"
+#include "gobex/gobex.h"
+
+#include "obexd/src/log.h"
#include "dbus.h"
-#include "log.h"
#include "transfer.h"
#define TRANSFER_INTERFACE "org.bluez.obex.Transfer1"
#include <glib.h>
#include <inttypes.h>
+#include "obexd/src/log.h"
#include "transport.h"
-#include "log.h"
static GSList *transports = NULL;
#include <sys/socket.h>
#include <glib.h>
-#include <gdbus/gdbus.h>
-#include "btio/btio.h"
+#include "lib/bluetooth.h"
#include "lib/uuid.h"
-#include "obexd.h"
-#include "plugin.h"
-#include "server.h"
-#include "obex.h"
-#include "transport.h"
-#include "service.h"
-#include "log.h"
+#include "gdbus/gdbus.h"
+
+#include "btio/btio.h"
+#include "obexd/src/obexd.h"
+#include "obexd/src/plugin.h"
+#include "obexd/src/server.h"
+#include "obexd/src/obex.h"
+#include "obexd/src/transport.h"
+#include "obexd/src/service.h"
+#include "obexd/src/log.h"
#define BT_RX_MTU 32767
#define BT_TX_MTU 32767
#include <glib.h>
-#include "obexd.h"
-#include "plugin.h"
-#include "log.h"
-#include "mimetype.h"
+#include "obexd/src/obexd.h"
+#include "obexd/src/plugin.h"
+#include "obexd/src/log.h"
+#include "obexd/src/mimetype.h"
#include "filesystem.h"
#define EOL_CHARS "\n"
#include <glib.h>
-#include "obexd.h"
-#include "plugin.h"
-#include "log.h"
-#include "obex.h"
-#include "manager.h"
-#include "mimetype.h"
-#include "service.h"
+#include "obexd/src/obexd.h"
+#include "obexd/src/plugin.h"
+#include "obexd/src/log.h"
+#include "obexd/src/obex.h"
+#include "obexd/src/manager.h"
+#include "obexd/src/mimetype.h"
+#include "obexd/src/service.h"
#include "ftp.h"
#include "filesystem.h"
#include <fcntl.h>
#include <inttypes.h>
-#include "obexd.h"
-#include "plugin.h"
-#include "log.h"
-#include "obex.h"
-#include "service.h"
+#include "obexd/src/obexd.h"
+#include "obexd/src/plugin.h"
+#include "obexd/src/log.h"
+#include "obexd/src/obex.h"
+#include "obexd/src/service.h"
+#include "obexd/src/manager.h"
+#include "obexd/src/mimetype.h"
#include "phonebook.h"
-#include "mimetype.h"
#include "filesystem.h"
-#include "manager.h"
struct aparam_header {
uint8_t tag;
#include <inttypes.h>
#include <sys/time.h>
-#include <gobex/gobex.h>
-#include <gobex/gobex-apparam.h>
-
-#include "obexd.h"
-#include "plugin.h"
-#include "log.h"
-#include "obex.h"
-#include "service.h"
-#include "mimetype.h"
+#include "gobex/gobex.h"
+#include "gobex/gobex-apparam.h"
+
+#include "obexd/src/obexd.h"
+#include "obexd/src/plugin.h"
+#include "obexd/src/log.h"
+#include "obexd/src/obex.h"
+#include "obexd/src/service.h"
+#include "obexd/src/mimetype.h"
+#include "obexd/src/manager.h"
+#include "obexd/src/map_ap.h"
#include "filesystem.h"
-#include "manager.h"
-#include "map_ap.h"
-
#include "messages.h"
#define READ_STATUS_REQ 0
#include <stdlib.h>
#include <string.h>
-#include "log.h"
+#include "obexd/src/log.h"
#include "messages.h"
#include <glib.h>
-#include "obexd.h"
-#include "plugin.h"
-#include "obex.h"
-#include "service.h"
-#include "log.h"
-#include "manager.h"
+#include "obexd/src/obexd.h"
+#include "obexd/src/plugin.h"
+#include "obexd/src/obex.h"
+#include "obexd/src/service.h"
+#include "obexd/src/log.h"
+#include "obexd/src/manager.h"
#include "filesystem.h"
#define VCARD_TYPE "text/x-vcard"
#include <fcntl.h>
#include <inttypes.h>
-#include <gobex/gobex.h>
-#include <gobex-apparam.h>
-
-#include "obexd.h"
-#include "plugin.h"
-#include "log.h"
-#include "obex.h"
-#include "service.h"
+#include "gobex/gobex.h"
+#include "gobex/gobex-apparam.h"
+
+#include "obexd/src/obexd.h"
+#include "obexd/src/plugin.h"
+#include "obexd/src/log.h"
+#include "obexd/src/obex.h"
+#include "obexd/src/service.h"
+#include "obexd/src/manager.h"
+#include "obexd/src/mimetype.h"
#include "phonebook.h"
-#include "mimetype.h"
#include "filesystem.h"
-#include "manager.h"
#define PHONEBOOK_TYPE "x-bt/phonebook"
#define VCARDLISTING_TYPE "x-bt/vcard-listing"
{
struct pbap_session *pbap = user_data;
const char *id;
- int ret = 0;
+ int ret;
DBG("");
#include <inttypes.h>
#include <glib.h>
-#include <gdbus/gdbus.h>
-
-#include "obexd.h"
-#include "plugin.h"
-#include "log.h"
-#include "obex.h"
-#include "mimetype.h"
-#include "service.h"
+
+#include "gdbus/gdbus.h"
+
+#include "obexd/src/obexd.h"
+#include "obexd/src/plugin.h"
+#include "obexd/src/log.h"
+#include "obexd/src/obex.h"
+#include "obexd/src/mimetype.h"
+#include "obexd/src/service.h"
#include "ftp.h"
#define PCSUITE_CHANNEL 24
#include <libical/vobject.h>
#include <libical/vcc.h>
-#include "log.h"
+#include "obexd/src/log.h"
#include "phonebook.h"
typedef void (*vcard_func_t) (const char *file, VObject *vo, void *user_data);
#include <string.h>
#include <errno.h>
-#include <glib.h>
-#include <bluetooth/bluetooth.h>
+#include <glib.h>
#include <libebook/e-book.h>
-#include "log.h"
-#include "obex.h"
-#include "service.h"
+#include "lib/bluetooth.h"
+
+#include "obexd/src/log.h"
+#include "obexd/src/obex.h"
+#include "obexd/src/service.h"
#include "phonebook.h"
#define QUERY_FN "(contains \"family_name\" \"%s\")"
#include <dbus/dbus.h>
#include <libtracker-sparql/tracker-sparql.h>
-#include "log.h"
-#include "obex.h"
-#include "service.h"
-#include "mimetype.h"
+#include "obexd/src/log.h"
+#include "obexd/src/obex.h"
+#include "obexd/src/service.h"
+#include "obexd/src/mimetype.h"
#include "phonebook.h"
#include "vcard.h"
#include <glib.h>
#include <dbus/dbus.h>
-#include <bluetooth/bluetooth.h>
+#include "lib/bluetooth.h"
-#include <gdbus/gdbus.h>
+#include "gdbus/gdbus.h"
#include "btio/btio.h"
-#include "plugin.h"
-#include "obex.h"
-#include "service.h"
-#include "mimetype.h"
-#include "log.h"
-#include "manager.h"
-#include "obexd.h"
+#include "obexd/src/plugin.h"
+#include "obexd/src/obex.h"
+#include "obexd/src/service.h"
+#include "obexd/src/mimetype.h"
+#include "obexd/src/log.h"
+#include "obexd/src/manager.h"
+#include "obexd/src/obexd.h"
#include "filesystem.h"
#define SYNCML_TARGET_SIZE 11
#include <errno.h>
#include <glib.h>
-#include <gdbus/gdbus.h>
+
+#include "gdbus/gdbus.h"
#include "vcard.h"
#include <syslog.h>
#include <glib.h>
-#include <gdbus/gdbus.h>
-
-#include "../client/manager.h"
+#include "gdbus/gdbus.h"
#include "log.h"
#include "obexd.h"
#include "server.h"
+#ifdef __TIZEN_PATCH__
+#include "../client/manager.h"
+#endif
+
#define DEFAULT_CAP_FILE CONFIGDIR "/capability.xml"
static GMainLoop *main_loop = NULL;
#include <unistd.h>
#include <string.h>
#include <errno.h>
-#include <gdbus/gdbus.h>
#include <sys/socket.h>
#include <inttypes.h>
-#include <gobex/gobex.h>
+#include "gdbus/gdbus.h"
+#include "gobex/gobex.h"
#include "btio/btio.h"
#include "obexd.h"
#include <inttypes.h>
#include <glib.h>
-#include <gobex/gobex.h>
+
+#include "gobex/gobex.h"
#include "btio/btio.h"
#include "obexd.h"
#include <inttypes.h>
#include <glib.h>
-#include <gobex/gobex.h>
+
+#include "gobex/gobex.h"
#include "log.h"
#include "obex.h"
#%define with_libcapng --enable-capng
Name: bluez
Summary: Bluetooth Stack for Linux
-Version: 5.27
+Version: 5.28
Release: 0
Group: Network & Connectivity/Bluetooth
License: GPL-2.0+
BuildRequires: libical-devel
BuildRequires: pkgconfig(libtzplatform-config)
-%define cups_lib_dir %{_prefix}/lib/cups
-
%description
The Bluetooth stack for Linux.
%description -n libbluetooth
Bluetooth protocol stack libraries.
-%package -n libbluetooth-plugins-service
-Summary: Bluetooth Plugins
-License: GPL-2.0+
-Group: Network & Connectivity/Bluetooth
-
-%description -n libbluetooth-plugins-service
-Bluetooth protocol stack plugins.
-
-%package cups
-Summary: CUPS Driver for Bluetooth Printers
-License: GPL-2.0+
-Group: Network & Connectivity/Bluetooth
-Requires: libbluetooth = %{version}
-
-%description cups
-Contains the files required by CUPS for printing to Bluetooth-connected
-printers.
-
%package -n obexd
Summary: OBEX Server A basic OBEX server implementation
Group: Network & Connectivity/Bluetooth
%build
autoreconf -fiv
-export CFLAGS="${CFLAGS} -D__TIZEN_PATCH__ -D__BROADCOM_PATCH__"
+export CFLAGS="${CFLAGS} -D__TIZEN_PATCH__ -D__BROADCOM_PATCH__ -DBLUEZ5_27_GATT_CLIENT"
export LDFLAGS=" -lncurses -Wl,--as-needed "
export CFLAGS+=" -DPBAP_SIM_ENABLE"
-#%reconfigure --with-pic \
-%reconfigure --with-pic \
- --libexecdir=/lib \
- --disable-usb \
- --enable-test \
- --enable-library \
- --enable-experimental \
- --enable-readline \
- --enable-service \
- --with-systemdunitdir=%{_unitdir} \
-# --disable-static \
-# --sysconfdir=%{_sysconfdir} \
-# --localstatedir=%{_localstatedir} \
-# --disable-systemd \
-# --enable-debug \
-# --enable-pie \
-#%if "%{?tizen_profile_name}" == "mobile"
-# --enable-network \
-#%endif
-# --enable-serial \
-# --enable-input \
-# --disable-usb \
-# --enable-tools \
-# --disable-bccmd \
-# --disable-pcmcia \
-# --disable-hid2hci \
-# --disable-alsa \
-# --enable-gstreamer=no \
-# --disable-dfutool \
-# --disable-cups \
-# --enable-health \
-# --enable-dbusoob \
-# --enable-test \
-# --with-telephony=tizen \
-# --enable-obex \
-# --enable-library \
-#%if "%{?tizen_profile_name}" == "wearable"
-# --enable-gatt \
-# --enable-wearable \
-#%endif
-#%if "%{?tizen_profile_name}" == "common"
-# --enable-usbbt \
-#%endif
-# --enable-experimental \
-# --enable-readline \
-#3 --enable-service \
-# --with-systemdunitdir=%{_unitdir} \
-# %{?with_libcapng}
-# --disable-autopair \
-# --disable-tizenunusedplugin
+%reconfigure --disable-static \
+ --sysconfdir=%{_sysconfdir} \
+ --localstatedir=%{_localstatedir} \
+ --with-systemdsystemunitdir=%{_libdir}/systemd/system \
+ --with-systemduserunitdir=%{_libdir}/systemd/user \
+ --libexecdir=%{_libdir} \
+ --enable-debug \
+ --enable-pie \
+ --enable-serial \
+ --enable-input \
+ --enable-usb=no \
+ --enable-tools \
+ --disable-bccmd \
+ --enable-pcmcia=no \
+ --enable-hid2hci=no \
+ --enable-alsa=no \
+ --enable-gstreamer=no \
+ --disable-dfutool \
+ --disable-cups \
+ --enable-health=yes \
+ --enable-dbusoob \
+ --enable-test \
+ --with-telephony=tizen \
+ --enable-obex \
+ --enable-library \
+ --enable-gatt \
+ --enable-experimental \
+ --enable-autopair=no \
+ --enable-network \
+ --enable-hid=yes \
+ --enable-tizenunusedplugin=no
+
+
make %{?_smp_mflags} all V=1
%check
rm -rvf $RPM_BUILD_ROOT/%{_libdir}/gstreamer-*
install --mode=0755 -D %{S:4} $RPM_BUILD_ROOT/usr/lib/udev/bluetooth.sh
install --mode=0644 -D %{S:7} $RPM_BUILD_ROOT/%{_sysconfdir}/modprobe.d/50-bluetooth.conf
-if ! test -e %{buildroot}%{cups_lib_dir}/backend/bluetooth
-then if test -e %{buildroot}%{_libdir}/cups/backend/bluetooth
- then mkdir -p %{buildroot}%{cups_lib_dir}/backend
- mv %{buildroot}%{_libdir}/cups/backend/bluetooth %{buildroot}%{cups_lib_dir}/backend/bluetooth
- fi
-fi
+
# no idea why this is suddenly necessary...
install --mode 0755 -d $RPM_BUILD_ROOT/var/lib/bluetooth
install -D -m 0755 %SOURCE103 %{buildroot}%{_bindir}/obex.sh
install -D -m 0755 tools/btiotest $RPM_BUILD_ROOT/%{_bindir}/
install -D -m 0755 tools/bluetooth-player $RPM_BUILD_ROOT/%{_bindir}/
-install -D -m 0755 tools/mpris-player $RPM_BUILD_ROOT/%{_bindir}/
+#install -D -m 0755 tools/mpris-player $RPM_BUILD_ROOT/%{_bindir}/
install -D -m 0755 tools/btmgmt $RPM_BUILD_ROOT/%{_bindir}/
install -D -m 0755 tools/scotest $RPM_BUILD_ROOT/%{_bindir}/
install -D -m 0755 tools/bluemoon $RPM_BUILD_ROOT/%{_bindir}/
%postun -n libbluetooth -p /sbin/ldconfig
-%post -n libbluetooth-plugins-service -p /sbin/ldconfig
-
-%postun -n libbluetooth-plugins-service -p /sbin/ldconfig
-
%files
%manifest %{name}.manifest
%defattr(-, root, root)
%{_bindir}/l2ping
%{_bindir}/obexctl
%{_bindir}/rfcomm
+%{_bindir}/mpris-proxy
%{_bindir}/sdptool
%{_bindir}/ciptool
#%{_bindir}/dfutool
%{_bindir}/hciattach
%{_bindir}/hciconfig
-/lib/bluetooth/bluetoothd
+%{_libdir}/bluetooth/bluetoothd
%{_bindir}/bccmd
#%{_sbindir}/hid2hci
%dir /usr/lib/udev
%dir /var/lib/bluetooth
%dir %{_sysconfdir}/modprobe.d
%config(noreplace) %{_sysconfdir}/modprobe.d/50-bluetooth.conf
-%{_unitdir}/bluetooth.service
%manifest %{name}.manifest
%defattr(-, root, root)
%{_libdir}/libbluetooth.so.*
-%{_libdir}/bluetooth/plugins/*.so
-%license COPYING
-
-%files -n libbluetooth-plugins-service
-%manifest %{name}.manifest
-%defattr(-, root, root)
-%{_libdir}/bluetooth/plugins/*.so
%license COPYING
-%files cups
-%manifest %{name}.manifest
-%defattr(-,root,root)
-%dir %{cups_lib_dir}
-%dir %{cups_lib_dir}/backend
-%{cups_lib_dir}/backend/bluetooth
-
%files -n obexd
%defattr(-,root,root,-)
-/lib/bluetooth/obexd
+%{_libdir}/bluetooth/obexd
%{_unitdir_user}/obex.service
%{_datadir}/dbus-1/services/org.bluez.obex.service
%{_sysconfdir}/obex/root-setup.d/000_create-symlinks
%{_bindir}/rctest
%{_bindir}/bluetoothctl
%{_bindir}/btiotest
-%{_bindir}/mpris-player
+#%{_bindir}/mpris-player
%{_bindir}/bluetooth-player
%{_bindir}/btmon
%{_bindir}/hcidump
%{_bindir}/scotest
%{_bindir}/bluemoon
%{_bindir}/gatttool
-
+%{_bindir}/hex2hcd
+%exclude /usr/lib/debug/*
%docs_package
#include <unistd.h>
#include <errno.h>
-#include <bluetooth/bluetooth.h>
#include <glib.h>
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+
#include "src/plugin.h"
#include "src/adapter.h"
#include "src/device.h"
#endif
#include <errno.h>
-#include <gdbus.h>
+#include "gdbus/gdbus.h"
#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>
#include <glib.h>
#include <errno.h>
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
#include "lib/uuid.h"
+
#include "src/plugin.h"
#include "src/adapter.h"
#include "src/shared/util.h"
#include <errno.h>
#include <stdint.h>
#include <stdlib.h>
-#include <gdbus/gdbus.h>
+#include <string.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+
+#include "gdbus/gdbus.h"
#include "src/dbus-common.h"
#include "src/plugin.h"
#include <stdlib.h>
#include <errno.h>
-#include <gdbus/gdbus.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/sdp.h>
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/sdp.h"
+
+#include "gdbus/gdbus.h"
#include "src/plugin.h"
#include "src/log.h"
#include <glib.h>
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
#include "lib/uuid.h"
#include "lib/mgmt.h"
+
#include "src/log.h"
#include "src/plugin.h"
#include "src/adapter.h"
#ifdef __TIZEN_PATCH__
#define CONTROL_CONNECT_TIMEOUT 4
+#define TARGET_CONNECT_TIMEOUT 1
#else
#define CONTROL_CONNECT_TIMEOUT 2
#endif
switch (new_state) {
case BTD_SERVICE_STATE_UNAVAILABLE:
+ if (data->sink_timer > 0) {
+ g_source_remove(data->sink_timer);
+ data->sink_timer = 0;
+ }
+ break;
case BTD_SERVICE_STATE_DISCONNECTED:
if (old_state == BTD_SERVICE_STATE_CONNECTING) {
int err = btd_service_get_error(service);
if (data->tg_timer > 0)
g_source_remove(data->tg_timer);
+#ifdef __TIZEN_PATCH__
+ data->tg_timer = g_timeout_add_seconds(TARGET_CONNECT_TIMEOUT,
+ policy_connect_tg,
+ data);
+#else
data->tg_timer = g_timeout_add_seconds(CONTROL_CONNECT_TIMEOUT,
policy_connect_tg,
data);
+#endif
}
static gboolean policy_connect_source(gpointer user_data)
switch (new_state) {
case BTD_SERVICE_STATE_UNAVAILABLE:
+ if (data->source_timer > 0) {
+ g_source_remove(data->source_timer);
+ data->source_timer = 0;
+ }
+ break;
case BTD_SERVICE_STATE_DISCONNECTED:
if (old_state == BTD_SERVICE_STATE_CONNECTING) {
int err = btd_service_get_error(service);
switch (new_state) {
case BTD_SERVICE_STATE_UNAVAILABLE:
+ if (data->ct_timer > 0) {
+ g_source_remove(data->ct_timer);
+ data->ct_timer = 0;
+ }
+ break;
case BTD_SERVICE_STATE_DISCONNECTED:
break;
case BTD_SERVICE_STATE_CONNECTING:
switch (new_state) {
case BTD_SERVICE_STATE_UNAVAILABLE:
+ if (data->tg_timer > 0) {
+ g_source_remove(data->tg_timer);
+ data->tg_timer = 0;
+ }
+ break;
case BTD_SERVICE_STATE_DISCONNECTED:
break;
case BTD_SERVICE_STATE_CONNECTING:
+++ /dev/null
-/*
- *
- * BlueZ - Bluetooth protocol stack for Linux
- *
- * Copyright (C) 2014 Intel Corporation.
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <errno.h>
-#include <unistd.h>
-
-#include <glib.h>
-#include <gdbus/gdbus.h>
-
-#include "lib/uuid.h"
-#include "src/plugin.h"
-#include "src/log.h"
-#include "src/dbus-common.h"
-#include "src/error.h"
-#include "src/adapter.h"
-#include "src/device.h"
-#include "src/service.h"
-#include "src/profile.h"
-
-#define SERVICE_INTERFACE "org.bluez.Service1"
-
-static unsigned int service_id = 0;
-static GSList *services = NULL;
-
-struct service_data {
- struct btd_service *service;
- char *path;
- DBusMessage *connect;
- DBusMessage *disconnect;
-};
-
-static struct service_data *find_data(struct btd_service *service)
-{
- GSList *l;
-
- for (l = services; l; l = l->next) {
- struct service_data *data = l->data;
-
- if (data->service == service)
- return data;
- }
-
- return NULL;
-}
-
-static void data_free(void *user_data)
-{
- struct service_data *data = user_data;
-
- if (data->connect)
- dbus_message_unref(data->connect);
-
- if (data->disconnect)
- dbus_message_unref(data->disconnect);
-
- g_free(data->path);
- g_free(data);
-}
-
-static void data_remove(struct service_data *data)
-{
- services = g_slist_remove(services, data);
- g_dbus_unregister_interface(btd_get_dbus_connection(), data->path,
- SERVICE_INTERFACE);
-}
-
-static DBusMessage *service_disconnect(DBusConnection *conn, DBusMessage *msg,
- void *user_data)
-{
- struct service_data *data = user_data;
- int err;
-
- if (data->disconnect)
- return btd_error_in_progress(msg);
-
- data->disconnect = dbus_message_ref(msg);
-
- err = btd_service_disconnect(data->service);
- if (err == 0)
- return NULL;
-
- dbus_message_unref(data->disconnect);
- data->disconnect = NULL;
-
- return btd_error_failed(msg, strerror(-err));
-}
-
-static DBusMessage *service_connect(DBusConnection *conn, DBusMessage *msg,
- void *user_data)
-{
- struct service_data *data = user_data;
- int err;
-
- if (data->connect)
- return btd_error_in_progress(msg);
-
- err = btd_service_connect(data->service);
- if (err < 0)
- return btd_error_failed(msg, strerror(-err));
-
- data->connect = dbus_message_ref(msg);
-
- return NULL;
-}
-
-static const char *data_get_state(struct service_data *data)
-{
- btd_service_state_t state = btd_service_get_state(data->service);
- int err;
-
- switch (state) {
- case BTD_SERVICE_STATE_UNAVAILABLE:
- return "unavailable";
- case BTD_SERVICE_STATE_DISCONNECTED:
- err = btd_service_get_error(data->service);
- return err < 0 ? "error" : "disconnected";
- case BTD_SERVICE_STATE_CONNECTING:
- return "connecting";
- case BTD_SERVICE_STATE_CONNECTED:
- return "connected";
- case BTD_SERVICE_STATE_DISCONNECTING:
- return "disconnecting";
- }
-
- return "unknown";
-}
-
-static gboolean get_device(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *user_data)
-{
- struct service_data *data = user_data;
- struct btd_device *dev = btd_service_get_device(data->service);
- const char *path = btd_device_get_path(dev);
-
- dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
-
- return TRUE;
-}
-
-static gboolean get_state(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *user_data)
-{
- struct service_data *data = user_data;
- const char *state;
-
- state = data_get_state(data);
-
- dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &state);
-
- return TRUE;
-}
-
-static gboolean remote_uuid_exists(const GDBusPropertyTable *property,
- void *user_data)
-{
- struct service_data *data = user_data;
- struct btd_profile *p = btd_service_get_profile(data->service);
-
- return p->remote_uuid != NULL;
-}
-
-static gboolean get_remote_uuid(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *user_data)
-{
- struct service_data *data = user_data;
- struct btd_profile *p = btd_service_get_profile(data->service);
-
- dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &p->remote_uuid);
-
- return TRUE;
-}
-
-
-static gboolean local_uuid_exists(const GDBusPropertyTable *property,
- void *user_data)
-{
- struct service_data *data = user_data;
- struct btd_profile *p = btd_service_get_profile(data->service);
-
- return p->local_uuid != NULL;
-}
-
-static gboolean get_local_uuid(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *user_data)
-{
- struct service_data *data = user_data;
- struct btd_profile *p = btd_service_get_profile(data->service);
-
- dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &p->local_uuid);
-
- return TRUE;
-}
-
-static gboolean version_exists(const GDBusPropertyTable *property,
- void *user_data)
-{
- struct service_data *data = user_data;
- uint16_t version = btd_service_get_version(data->service);
-
- return version != 0x0000;
-}
-
-static gboolean get_version(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *user_data)
-{
- struct service_data *data = user_data;
- uint16_t version = btd_service_get_version(data->service);
-
- dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &version);
-
- return TRUE;
-}
-
-static gboolean get_auto_connect(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *user_data)
-{
- struct service_data *data = user_data;
- dbus_bool_t value = btd_service_get_auto_connect(data->service);
-
- dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
-
- return TRUE;
-}
-
-static void set_auto_connect(const GDBusPropertyTable *property,
- DBusMessageIter *value,
- GDBusPendingPropertySet id,
- void *user_data)
-{
- struct service_data *data = user_data;
- dbus_bool_t b;
-
- if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_BOOLEAN) {
- g_dbus_pending_property_error(id,
- ERROR_INTERFACE ".InvalidArguments",
- "Invalid arguments in method call");
- return;
- }
-
- dbus_message_iter_get_basic(value, &b);
-
- btd_service_set_auto_connect(data->service, b);
-
- g_dbus_pending_property_success(id);
-}
-
-static gboolean get_blocked(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *user_data)
-{
- struct service_data *data = user_data;
- dbus_bool_t value = btd_service_is_blocked(data->service);
-
- dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
-
- return TRUE;
-}
-
-static void set_blocked(const GDBusPropertyTable *property,
- DBusMessageIter *value,
- GDBusPendingPropertySet id,
- void *user_data)
-{
- struct service_data *data = user_data;
- dbus_bool_t b;
-
- if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_BOOLEAN) {
- g_dbus_pending_property_error(id,
- ERROR_INTERFACE ".InvalidArguments",
- "Invalid arguments in method call");
- return;
- }
-
- dbus_message_iter_get_basic(value, &b);
-
- btd_service_set_blocked(data->service, b);
-
- g_dbus_pending_property_success(id);
-}
-
-static const GDBusPropertyTable service_properties[] = {
- { "Device", "o", get_device, NULL, NULL },
- { "State", "s", get_state, NULL, NULL },
- { "RemoteUUID", "s", get_remote_uuid, NULL, remote_uuid_exists },
- { "LocalUUID", "s", get_local_uuid, NULL, local_uuid_exists },
- { "Version", "q", get_version, NULL, version_exists },
- { "AutoConnect", "b", get_auto_connect, set_auto_connect, NULL },
- { "Blocked", "b", get_blocked, set_blocked, NULL },
- { }
-};
-
-static const GDBusMethodTable service_methods[] = {
- { GDBUS_ASYNC_METHOD("Disconnect", NULL, NULL, service_disconnect) },
- { GDBUS_ASYNC_METHOD("Connect", NULL, NULL, service_connect) },
- {}
-};
-
-static struct service_data *service_get_data(struct btd_service *service)
-{
- struct btd_device *dev = btd_service_get_device(service);
- struct btd_profile *p = btd_service_get_profile(service);
- struct service_data *data;
-
- data = find_data(service);
- if (data != NULL)
- return data;
-
- data = g_new0(struct service_data, 1);
- data->path = g_strdup_printf("%s/%s", btd_device_get_path(dev),
- p->remote_uuid);
- data->path = g_strdelimit(data->path, "-", '_');
- data->service = service;
- if (g_dbus_register_interface(btd_get_dbus_connection(),
- data->path, SERVICE_INTERFACE,
- service_methods, NULL,
- service_properties, data,
- data_free) == FALSE) {
- error("Unable to register service interface for %s",
- data->path);
- data_free(data);
- return NULL;
- }
-
- services = g_slist_prepend(services, data);
-
- DBG("%s", data->path);
-
- return data;
-}
-
-static void service_connected(struct service_data *data)
-{
- DBusMessage *reply;
-
- if (!data->connect)
- return;
-
- reply = dbus_message_new_method_return(data->connect);
- g_dbus_send_message(btd_get_dbus_connection(), reply);
- dbus_message_unref(data->connect);
- data->connect = NULL;
-}
-
-static void service_disconnected(struct service_data *data)
-{
- DBusMessage *reply;
- int err;
-
- if (data->disconnect) {
- reply = dbus_message_new_method_return(data->disconnect);
- g_dbus_send_message(btd_get_dbus_connection(), reply);
- dbus_message_unref(data->disconnect);
- data->connect = NULL;
- }
-
- if (!data->connect)
- return;
-
- err = btd_service_get_error(data->service);
-
- reply = btd_error_failed(data->connect, strerror(-err));
- g_dbus_send_message(btd_get_dbus_connection(), reply);
- dbus_message_unref(data->connect);
- data->connect = NULL;
-}
-
-static void service_cb(struct btd_service *service,
- btd_service_state_t old_state,
- btd_service_state_t new_state,
- void *user_data)
-{
- struct service_data *data;
-
- data = service_get_data(service);
- if (!data)
- return;
-
- switch (new_state) {
- case BTD_SERVICE_STATE_UNAVAILABLE:
- data_remove(data);
- return;
- case BTD_SERVICE_STATE_CONNECTED:
- service_connected(data);
- break;
- case BTD_SERVICE_STATE_DISCONNECTED:
- service_disconnected(data);
- break;
- default:
- break;
- }
-
- g_dbus_emit_property_changed(btd_get_dbus_connection(), data->path,
- SERVICE_INTERFACE, "State");
-}
-
-static int service_init(void)
-{
- DBG("");
-
- service_id = btd_service_add_state_cb(service_cb, NULL);
-
- return 0;
-}
-
-static void service_exit(void)
-{
- DBG("");
-
- btd_service_remove_state_cb(service_id);
-
- g_slist_free_full(services, data_free);
-}
-
-BLUETOOTH_PLUGIN_DEFINE(service, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
- service_init, service_exit)
#include <libudev.h>
#include "lib/bluetooth.h"
+#include "lib/sdp.h"
#include "lib/uuid.h"
+
#include "src/adapter.h"
#include "src/device.h"
#include "src/plugin.h"
#include <stdbool.h>
-#include <bluetooth/bluetooth.h>
#include <glib.h>
+#include "bluetooth/bluetooth.h"
+#include "bluetooth/sdp.h"
+
#include "src/plugin.h"
#include "src/adapter.h"
#include "src/device.h"
#include <stdbool.h>
#include <errno.h>
-#include <gdbus/gdbus.h>
-#include <glib.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+#include "lib/sdp.h"
#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
#include "src/plugin.h"
#include "src/dbus-common.h"
#include "attrib/att.h"
#include <dbus/dbus.h>
#include <glib.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/sdp.h>
-#include <bluetooth/sdp_lib.h>
-
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
#include "lib/uuid.h"
+
#include "src/plugin.h"
#include "src/adapter.h"
#include "src/device.h"
#include "src/service.h"
#include "src/log.h"
#include "src/sdpd.h"
+#include "src/shared/queue.h"
+
+#include "btio/btio.h"
#include "avdtp.h"
#include "sink.h"
#define SUSPEND_TIMEOUT 5
#define RECONFIGURE_TIMEOUT 500
+#define AVDTP_PSM 25
+
struct a2dp_sep {
struct a2dp_server *server;
struct a2dp_endpoint *endpoint;
uint32_t sink_record_id;
gboolean sink_enabled;
gboolean source_enabled;
+ GIOChannel *io;
+ struct queue *seps;
+ struct queue *channels;
+};
+
+struct a2dp_channel {
+ struct a2dp_server *server;
+ struct btd_device *device;
+ GIOChannel *io;
+ guint io_id;
+ unsigned int state_id;
+ unsigned int auth_id;
+ struct avdtp *session;
};
static GSList *servers = NULL;
auto_config(setup);
}
+static gboolean endpoint_match_codec_ind(struct avdtp *session,
+ struct avdtp_media_codec_capability *codec,
+ void *user_data)
+{
+ struct a2dp_sep *sep = user_data;
+ a2dp_vendor_codec_t *remote_codec;
+ a2dp_vendor_codec_t *local_codec;
+ uint8_t *capabilities;
+ size_t length;
+
+ if (codec->media_codec_type != A2DP_CODEC_VENDOR)
+ return TRUE;
+
+ if (sep->endpoint == NULL)
+ return FALSE;
+
+ length = sep->endpoint->get_capabilities(sep, &capabilities,
+ sep->user_data);
+ if (length < sizeof(a2dp_vendor_codec_t))
+ return FALSE;
+
+ local_codec = (a2dp_vendor_codec_t *) capabilities;
+ remote_codec = (a2dp_vendor_codec_t *) codec->data;
+
+ if (remote_codec->vendor_id != local_codec->vendor_id)
+ return FALSE;
+
+ if (remote_codec->codec_id != local_codec->codec_id)
+ return FALSE;
+
+ DBG("vendor 0x%08x codec 0x%04x", btohl(remote_codec->vendor_id),
+ btohs(remote_codec->codec_id));
+ return TRUE;
+}
+
static gboolean endpoint_setconf_ind(struct avdtp *session,
struct avdtp_local_sep *sep,
struct avdtp_stream *stream,
else
DBG("Source %p: Open_Ind", sep);
- setup = find_setup_by_session(session);
+ setup = a2dp_setup_get(session);
if (!setup)
- return TRUE;
+ return FALSE;
+
+ setup->stream = stream;
if (setup->reconfigure)
setup->reconfigure = FALSE;
};
static struct avdtp_sep_ind endpoint_ind = {
+ .match_codec = endpoint_match_codec_ind,
.get_capability = endpoint_getcap_ind,
.set_configuration = endpoint_setconf_ind,
.get_configuration = getconf_ind,
return NULL;
}
+static void channel_free(void *data)
+{
+ struct a2dp_channel *chan = data;
+
+ if (chan->auth_id > 0)
+ btd_cancel_authorization(chan->auth_id);
+
+ if (chan->io_id > 0)
+ g_source_remove(chan->io_id);
+
+ if (chan->io) {
+ g_io_channel_shutdown(chan->io, TRUE, NULL);
+ g_io_channel_unref(chan->io);
+ }
+
+ avdtp_remove_state_cb(chan->state_id);
+
+ g_free(chan);
+}
+
+static void channel_remove(struct a2dp_channel *chan)
+{
+ struct a2dp_server *server = chan->server;
+
+ DBG("chan %p", chan);
+
+ queue_remove(server->channels, chan);
+
+ channel_free(chan);
+}
+
+static gboolean disconnect_cb(GIOChannel *io, GIOCondition cond, gpointer data)
+{
+ struct a2dp_channel *chan = data;
+
+ DBG("chan %p", chan);
+
+ chan->io_id = 0;
+
+ channel_remove(chan);
+
+ return FALSE;
+}
+
+static void avdtp_state_cb(struct btd_device *dev, struct avdtp *session,
+ avdtp_session_state_t old_state,
+ avdtp_session_state_t new_state,
+ void *user_data)
+{
+ struct a2dp_channel *chan = user_data;
+
+ switch (new_state) {
+ case AVDTP_SESSION_STATE_DISCONNECTED:
+ if (chan->session == session)
+ channel_remove(chan);
+ break;
+ case AVDTP_SESSION_STATE_CONNECTING:
+ break;
+ case AVDTP_SESSION_STATE_CONNECTED:
+ break;
+ }
+}
+
+static struct a2dp_channel *channel_new(struct a2dp_server *server,
+ struct btd_device *device,
+ GIOChannel *io)
+{
+ struct a2dp_channel *chan;
+
+ chan = g_new0(struct a2dp_channel, 1);
+ chan->server = server;
+ chan->device = device;
+ chan->state_id = avdtp_add_state_cb(device, avdtp_state_cb, chan);
+
+ if (!queue_push_tail(server->channels, chan)) {
+ g_free(chan);
+ return NULL;
+ }
+
+ if (!io)
+ return chan;
+
+ chan->io = g_io_channel_ref(io);
+ chan->io_id = g_io_add_watch(io, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
+ (GIOFunc) disconnect_cb, chan);
+
+ return chan;
+}
+
+static bool match_by_device(const void *data, const void *user_data)
+{
+ const struct a2dp_channel *chan = data;
+ const struct btd_device *device = user_data;
+
+ return chan->device == device;
+}
+
+struct avdtp *a2dp_avdtp_get(struct btd_device *device)
+{
+ struct a2dp_server *server;
+ struct a2dp_channel *chan;
+
+ server = find_server(servers, device_get_adapter(device));
+ if (server == NULL)
+ return NULL;
+
+ chan = queue_find(server->channels, match_by_device, device);
+ if (!chan) {
+ chan = channel_new(server, device, NULL);
+ if (!chan)
+ return NULL;
+ }
+
+ if (chan->session)
+ return avdtp_ref(chan->session);
+
+ chan->session = avdtp_new(NULL, device, server->seps);
+ if (!chan->session) {
+ channel_remove(chan);
+ return NULL;
+ }
+
+ return avdtp_ref(chan->session);
+}
+
+static void connect_cb(GIOChannel *io, GError *err, gpointer user_data)
+{
+ struct a2dp_channel *chan = user_data;
+
+ if (err) {
+ error("%s", err->message);
+ goto fail;
+ }
+
+ chan->session = avdtp_new(chan->io, chan->device, chan->server->seps);
+ if (!chan->session) {
+ error("Unable to create AVDTP session");
+ goto fail;
+ }
+
+ g_io_channel_unref(chan->io);
+ chan->io = NULL;
+
+ g_source_remove(chan->io_id);
+ chan->io_id = 0;
+
+ return;
+
+fail:
+ channel_remove(chan);
+}
+
+static void auth_cb(DBusError *derr, void *user_data)
+{
+ struct a2dp_channel *chan = user_data;
+ GError *err = NULL;
+
+ chan->auth_id = 0;
+
+ if (derr && dbus_error_is_set(derr)) {
+ error("Access denied: %s", derr->message);
+ goto fail;
+ }
+
+ if (!bt_io_accept(chan->io, connect_cb, chan, NULL, &err)) {
+ error("bt_io_accept: %s", err->message);
+ g_error_free(err);
+ goto fail;
+ }
+
+ return;
+
+fail:
+ channel_remove(chan);
+}
+
+static void transport_cb(GIOChannel *io, GError *err, gpointer user_data)
+{
+ struct a2dp_setup *setup = user_data;
+ uint16_t omtu, imtu;
+
+ if (err) {
+ error("%s", err->message);
+ goto drop;
+ }
+
+ bt_io_get(io, &err, BT_IO_OPT_OMTU, &omtu, BT_IO_OPT_IMTU, &imtu,
+ BT_IO_OPT_INVALID);
+ if (err) {
+ error("%s", err->message);
+ g_error_free(err);
+ goto drop;
+ }
+
+ if (!avdtp_stream_set_transport(setup->stream,
+ g_io_channel_unix_get_fd(io),
+ omtu, imtu))
+ goto drop;
+
+ g_io_channel_set_close_on_unref(io, FALSE);
+
+ setup_unref(setup);
+
+ return;
+
+drop:
+ setup_unref(setup);
+ g_io_channel_shutdown(io, TRUE, NULL);
+
+}
+static void confirm_cb(GIOChannel *io, gpointer data)
+{
+ struct a2dp_server *server = data;
+ struct a2dp_channel *chan;
+ char address[18];
+ bdaddr_t src, dst;
+ GError *err = NULL;
+ struct btd_device *device;
+
+ bt_io_get(io, &err,
+ BT_IO_OPT_SOURCE_BDADDR, &src,
+ BT_IO_OPT_DEST_BDADDR, &dst,
+ BT_IO_OPT_DEST, address,
+ BT_IO_OPT_INVALID);
+ if (err) {
+ error("%s", err->message);
+ g_error_free(err);
+ goto drop;
+ }
+
+ DBG("AVDTP: incoming connect from %s", address);
+
+ device = btd_adapter_find_device(adapter_find(&src), &dst,
+ BDADDR_BREDR);
+ if (!device)
+ goto drop;
+
+ chan = queue_find(server->channels, match_by_device, device);
+ if (chan) {
+ struct a2dp_setup *setup;
+
+ setup = find_setup_by_session(chan->session);
+ if (!setup || !setup->stream)
+ goto drop;
+
+ if (!bt_io_accept(io, transport_cb, setup, NULL, &err)) {
+ error("bt_io_accept: %s", err->message);
+ g_error_free(err);
+ goto drop;
+ }
+
+ return;
+ }
+
+ chan = channel_new(server, device, io);
+ if (!chan)
+ goto drop;
+
+ chan->auth_id = btd_request_authorization(&src, &dst,
+ ADVANCED_AUDIO_UUID,
+ auth_cb, chan);
+ if (chan->auth_id == 0 && !chan->session)
+ channel_remove(chan);
+
+ return;
+
+drop:
+ g_io_channel_shutdown(io, TRUE, NULL);
+}
+
+static bool a2dp_server_listen(struct a2dp_server *server)
+{
+ GError *err = NULL;
+
+ if (server->io)
+ return true;
+
+ server->io = bt_io_listen(NULL, confirm_cb, server, NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR,
+ btd_adapter_get_address(server->adapter),
+ BT_IO_OPT_PSM, AVDTP_PSM,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+ BT_IO_OPT_MASTER, true,
+ BT_IO_OPT_INVALID);
+ if (server->io)
+ return true;
+
+ error("%s", err->message);
+ g_error_free(err);
+
+ return false;
+}
+
static struct a2dp_server *a2dp_server_register(struct btd_adapter *adapter)
{
struct a2dp_server *server;
server = g_new0(struct a2dp_server, 1);
+
server->adapter = btd_adapter_ref(adapter);
+ server->seps = queue_new();
+ server->channels = queue_new();
+
servers = g_slist_append(servers, server);
return server;
static void a2dp_unregister_sep(struct a2dp_sep *sep)
{
+ struct a2dp_server *server = sep->server;
+
if (sep->destroy) {
sep->destroy(sep->user_data);
sep->endpoint = NULL;
}
- avdtp_unregister_sep(sep->lsep);
+ avdtp_unregister_sep(server->seps, sep->lsep);
+
g_free(sep);
+
+ if (!queue_isempty(server->seps))
+ return;
+
+ if (server->io) {
+ g_io_channel_shutdown(server->io, TRUE, NULL);
+ g_io_channel_unref(server->io);
+ server->io = NULL;
+ }
}
static void a2dp_server_unregister(struct a2dp_server *server)
{
servers = g_slist_remove(servers, server);
+ queue_destroy(server->channels, channel_free);
+ queue_destroy(server->seps, NULL);
+
+ if (server->io) {
+ g_io_channel_shutdown(server->io, TRUE, NULL);
+ g_io_channel_unref(server->io);
+ }
+
btd_adapter_unref(server->adapter);
g_free(server);
}
sep = g_new0(struct a2dp_sep, 1);
- sep->lsep = avdtp_register_sep(adapter, type,
+ sep->lsep = avdtp_register_sep(server->seps, type,
AVDTP_MEDIA_TYPE_AUDIO, codec,
delay_reporting, &endpoint_ind,
&cfm, sep);
record = a2dp_record(type);
if (!record) {
error("Unable to allocate new service record");
- avdtp_unregister_sep(sep->lsep);
- g_free(sep);
+ a2dp_unregister_sep(sep);
if (err)
*err = -EINVAL;
return NULL;
if (adapter_service_add(server->adapter, record) < 0) {
error("Unable to register A2DP service record");
sdp_record_free(record);
- avdtp_unregister_sep(sep->lsep);
- g_free(sep);
+ a2dp_unregister_sep(sep);
+ if (err)
+ *err = -EINVAL;
+ return NULL;
+ }
+
+ if (!a2dp_server_listen(server)) {
+ sdp_record_free(record);
+ a2dp_unregister_sep(sep);
if (err)
*err = -EINVAL;
return NULL;
}
+
*record_id = record->handle;
add:
finalize_select(setup);
}
-static gboolean check_vendor_codec(struct a2dp_sep *sep, uint8_t *cap,
- size_t len)
-{
- uint8_t *capabilities;
- size_t length;
- a2dp_vendor_codec_t *local_codec;
- a2dp_vendor_codec_t *remote_codec;
-
- if (len < sizeof(a2dp_vendor_codec_t))
- return FALSE;
-
- remote_codec = (a2dp_vendor_codec_t *) cap;
-
- if (sep->endpoint == NULL)
- return FALSE;
-
- length = sep->endpoint->get_capabilities(sep,
- &capabilities, sep->user_data);
-
- if (length < sizeof(a2dp_vendor_codec_t))
- return FALSE;
-
- local_codec = (a2dp_vendor_codec_t *) capabilities;
-
- if (btohl(remote_codec->vendor_id) != btohl(local_codec->vendor_id))
- return FALSE;
-
- if (btohs(remote_codec->codec_id) != btohs(local_codec->codec_id))
- return FALSE;
-
- DBG("vendor 0x%08x codec 0x%04x", btohl(remote_codec->vendor_id),
- btohs(remote_codec->codec_id));
-
- return TRUE;
-}
-
static struct a2dp_sep *a2dp_find_sep(struct avdtp *session, GSList *list,
const char *sender)
{
for (; list; list = list->next) {
struct a2dp_sep *sep = list->data;
+#ifdef __TIZEN_PATCH__
struct avdtp_remote_sep *rsep;
struct avdtp_media_codec_capability *cap;
struct avdtp_service_capability *service;
+#endif
/* Use sender's endpoint if available */
if (sender) {
if (g_strcmp0(sender, name) != 0)
continue;
}
-
+#ifdef __TIZEN_PATCH__
rsep = avdtp_find_remote_sep(session, sep->lsep);
if (rsep == NULL)
continue;
service = avdtp_get_codec(rsep);
cap = (struct avdtp_media_codec_capability *) service->data;
-#ifdef __TIZEN_PATCH__
if (cap->media_codec_type != A2DP_CODEC_VENDOR) {
selected_sep = sep;
continue;
}
#else
- if (cap->media_codec_type != A2DP_CODEC_VENDOR)
- return sep;
+ if (avdtp_find_remote_sep(session, sep->lsep) == NULL)
+ continue;
+
#endif
+ return sep;
- if (check_vendor_codec(sep, cap->data,
- service->length - sizeof(*cap)))
- return sep;
}
#ifdef __TIZEN_PATCH__
static struct btd_profile a2dp_source_profile = {
.name = "a2dp-source",
.priority = BTD_PROFILE_PRIORITY_MEDIUM,
- .version = 0x0103,
.remote_uuid = A2DP_SOURCE_UUID,
- .local_uuid = A2DP_SINK_UUID,
- .auth_uuid = ADVANCED_AUDIO_UUID,
-
.device_probe = a2dp_source_probe,
.device_remove = a2dp_source_remove,
static struct btd_profile a2dp_sink_profile = {
.name = "a2dp-sink",
.priority = BTD_PROFILE_PRIORITY_MEDIUM,
- .version = 0x0103,
.remote_uuid = A2DP_SINK_UUID,
- .local_uuid = A2DP_SOURCE_UUID,
- .auth_uuid = ADVANCED_AUDIO_UUID,
-
.device_probe = a2dp_sink_probe,
.device_remove = a2dp_sink_remove,
gboolean a2dp_sep_unlock(struct a2dp_sep *sep, struct avdtp *session);
struct avdtp_stream *a2dp_sep_get_stream(struct a2dp_sep *sep);
struct btd_device *a2dp_setup_get_device(struct a2dp_setup *setup);
+struct avdtp *a2dp_avdtp_get(struct btd_device *device);
#include <fcntl.h>
#include <netinet/in.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/sdp.h>
-#include <bluetooth/l2cap.h>
-
#include <glib.h>
-#include "btio/btio.h"
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/l2cap.h"
#include "lib/uuid.h"
+
+#include "btio/btio.h"
#include "src/adapter.h"
#include "src/device.h"
-
#include "src/log.h"
#include "src/error.h"
#include "src/uinput.h"
};
struct key_pressed {
- uint8_t op;
+ uint16_t op;
guint timer;
};
return FALSE;
}
+static void handle_press(struct avctp *session, uint16_t op)
+{
+ if (session->key.timer > 0) {
+ g_source_remove(session->key.timer);
+
+ /* Only auto release if keys are different */
+ if (session->key.op == op)
+ goto done;
+
+ send_key(session->uinput, session->key.op, 0);
+ }
+
+ session->key.op = op;
+
+ send_key(session->uinput, op, 1);
+
+done:
+ session->key.timer = g_timeout_add_seconds(AVC_PRESS_TIMEOUT,
+ auto_release, session);
+}
+
+static void handle_release(struct avctp *session, uint16_t op)
+{
+ if (session->key.timer > 0) {
+ g_source_remove(session->key.timer);
+ session->key.timer = 0;
+ }
+
+ send_key(session->uinput, op, 0);
+}
#ifdef __TIZEN_PATCH__
extern void avrcp_stop_position_timer(void);
#endif
}
if (pressed) {
- if (session->key.timer > 0) {
- g_source_remove(session->key.timer);
-#ifndef __TIZEN_PATCH__
- send_key(session->uinput, session->key.op, 0);
-#endif
- }
-
- session->key.op = key_map[i].uinput;
- session->key.timer = g_timeout_add_seconds(
- AVC_PRESS_TIMEOUT,
- auto_release,
- session);
+ handle_press(session, key_map[i].uinput);
#ifdef __TIZEN_PATCH__
if (key_map[i].avc == AVC_REWIND)
avrcp_stop_position_timer();
#endif
- } else if (session->key.timer > 0) {
- g_source_remove(session->key.timer);
- session->key.timer = 0;
- }
+ } else
+ handle_release(session, key_map[i].uinput);
- send_key(session->uinput, key_map[i].uinput, pressed);
break;
}
return err;
}
+ send_event(fd, EV_REP, REP_DELAY, 300);
+
return fd;
}
#ifdef __TIZEN_PATCH__
session->auth_id = btd_request_authorization(src, dst,
AVRCP_TARGET_UUID,
- auth_cb, session, 0);
+ auth_cb, session);
#else
session->auth_id = btd_request_authorization(src, dst,
AVRCP_REMOTE_UUID,
- auth_cb, session, 0);
+ auth_cb, session);
#endif
if (session->auth_id == 0)
goto drop;
#include <bluetooth/bluetooth.h>
#include <bluetooth/sdp.h>
#include <bluetooth/sdp_lib.h>
+#include "lib/uuid.h"
#ifdef __TIZEN_PATCH__
#ifdef __BROADCOM_QOS_PATCH__
#include <glib.h>
-#include "src/log.h"
#include "btio/btio.h"
-#include "lib/uuid.h"
+#include "src/log.h"
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
#include "src/adapter.h"
#include "src/device.h"
+#ifdef __TIZEN_PATCH__
+#include "src/service.h"
+#endif
+
#include "avdtp.h"
#include "sink.h"
#include "source.h"
#define AVDTP_PSM 25
#define MAX_SEID 0x3E
+static unsigned int seids;
#ifndef MAX
# define MAX(x, y) ((x) > (y) ? (x) : (y))
struct avdtp_stream *stream;
};
-struct avdtp_server {
- struct btd_adapter *adapter;
- GIOChannel *io;
- GSList *seps;
- GSList *sessions;
-};
-
struct avdtp_local_sep {
avdtp_state_t state;
struct avdtp_stream *stream;
struct avdtp_sep_ind *ind;
struct avdtp_sep_cfm *cfm;
void *user_data;
- struct avdtp_server *server;
};
struct stream_callback {
uint16_t version;
- struct avdtp_server *server;
+ struct queue *lseps;
struct btd_device *device;
avdtp_session_state_t state;
- guint auth_id;
-
GIOChannel *io;
guint io_id;
gboolean stream_setup;
};
-static GSList *servers = NULL;
-
static GSList *state_callbacks = NULL;
static int send_request(struct avdtp *session, gboolean priority,
struct avdtp_local_sep *sep,
avdtp_state_t state);
-static struct avdtp_server *find_server(GSList *list, struct btd_adapter *a)
-{
- for (; list; list = list->next) {
- struct avdtp_server *server = list->data;
-
- if (server->adapter == a)
- return server;
- }
-
- return NULL;
-}
-
static const char *avdtp_statestr(avdtp_state_t state)
{
switch (state) {
session->state = new_state;
- for (l = state_callbacks; l != NULL; l = l->next) {
+ for (l = state_callbacks; l != NULL;) {
struct avdtp_state_callback *cb = l->data;
+ l = g_slist_next(l);
+
if (session->device != cb->dev)
continue;
avdtp_sep_set_state(session, sep, AVDTP_STATE_IDLE);
}
-static int avdtp_cancel_authorization(struct avdtp *session)
-{
- int err;
-
- if (session->state != AVDTP_SESSION_STATE_CONNECTING)
- return 0;
-
- if (session->auth_id == 0)
- return 0;
-
- err = btd_cancel_authorization(session->auth_id);
- if (err < 0)
- return err;
-
- session->auth_id = 0;
-
- return 0;
-}
-
static void sep_free(gpointer data)
{
struct avdtp_remote_sep *sep = data;
static void connection_lost(struct avdtp *session, int err)
{
- struct avdtp_server *server = session->server;
char address[18];
#ifdef __TIZEN_PATCH__
struct btd_service *service;
ba2str(device_get_address(session->device), address);
DBG("Disconnected from %s", address);
- if (err != EACCES)
- avdtp_cancel_authorization(session);
-
#ifdef __TIZEN_PATCH__
service = btd_device_get_service(session->device, A2DP_SINK_UUID);
if (service)
btd_service_connecting_complete(service, -err);
#endif
-
g_slist_foreach(session->streams, (GFunc) release_stream, session);
session->streams = NULL;
return;
#endif
- server->sessions = g_slist_remove(server->sessions, session);
avdtp_free(session);
}
#ifdef __TIZEN_PATCH__
struct btd_device *device = NULL;
struct btd_adapter *adapter = NULL;
- struct bdaddr_t *bdaddr = NULL;
+ const bdaddr_t *bdaddr = NULL;
#endif
DBG("");
+/* Fix : REVERSE_INULL */
+#ifdef __TIZEN_PATCH__
+ if (session->device == NULL) {
+ error("session device NOT found");
+ return FALSE;
+ }
+#endif
session->dc_timer = 0;
stream_setup = session->stream_setup;
}
#ifdef __TIZEN_PATCH__
- if (session->device) {
- adapter = device_get_adapter(session->device);
- bdaddr = device_get_address(session->device);
- if (adapter && bdaddr)
- device = btd_adapter_find_device(adapter, bdaddr, BDADDR_BREDR);
- if (!device)
- error("device is NOT found");
- }
+ adapter = device_get_adapter(session->device);
+ bdaddr = device_get_address(session->device);
+ if (adapter && bdaddr)
+ device = btd_adapter_find_device(adapter, bdaddr, BDADDR_BREDR);
+ if (!device)
+ error("device is NOT found");
#endif
connection_lost(session, ETIMEDOUT);
return session;
}
-static struct avdtp_local_sep *find_local_sep_by_seid(struct avdtp_server *server,
- uint8_t seid)
+static bool match_by_seid(const void *data, const void *user_data)
{
- GSList *l;
-
- for (l = server->seps; l != NULL; l = g_slist_next(l)) {
- struct avdtp_local_sep *sep = l->data;
+ const struct avdtp_local_sep *sep = data;
+ uint8_t seid = PTR_TO_UINT(user_data);
- if (sep->info.seid == seid)
- return sep;
- }
+ return sep->info.seid == seid;
+}
- return NULL;
+static struct avdtp_local_sep *find_local_sep_by_seid(struct avdtp *session,
+ uint8_t seid)
+{
+ return queue_find(session->lseps, match_by_seid, INT_TO_PTR(seid));
}
struct avdtp_remote_sep *avdtp_find_remote_sep(struct avdtp *session,
if (codec_data->media_codec_type != lsep->codec)
continue;
+ if (lsep->ind && lsep->ind->match_codec)
+ if (!lsep->ind->match_codec(session, codec_data,
+ lsep->user_data))
+ continue;
+
if (sep->stream == NULL)
return sep;
}
signal_id, NULL, 0);
}
+static void copy_seps(void *data, void *user_data)
+{
+ struct avdtp_local_sep *sep = data;
+ struct seid_info **p = user_data;
+
+ memcpy(*p, &sep->info, sizeof(struct seid_info));
+ *p = *p + 1;
+}
+
static gboolean avdtp_discover_cmd(struct avdtp *session, uint8_t transaction,
void *buf, int size)
{
- GSList *l;
- unsigned int rsp_size, sep_count, i;
- struct seid_info *seps;
+ unsigned int rsp_size, sep_count;
+ struct seid_info *seps, *p;
gboolean ret;
- sep_count = g_slist_length(session->server->seps);
+ sep_count = queue_length(session->lseps);
if (sep_count == 0) {
uint8_t err = AVDTP_NOT_SUPPORTED_COMMAND;
rsp_size = sep_count * sizeof(struct seid_info);
seps = g_new0(struct seid_info, sep_count);
+ p = seps;
- for (l = session->server->seps, i = 0; l != NULL; l = l->next, i++) {
- struct avdtp_local_sep *sep = l->data;
-
- memcpy(&seps[i], &sep->info, sizeof(struct seid_info));
- }
+ queue_foreach(session->lseps, copy_seps, &p);
ret = avdtp_send(session, transaction, AVDTP_MSG_TYPE_ACCEPT,
AVDTP_DISCOVER, seps, rsp_size);
goto failed;
}
- sep = find_local_sep_by_seid(session->server, req->acp_seid);
+ sep = find_local_sep_by_seid(session, req->acp_seid);
if (!sep) {
err = AVDTP_BAD_ACP_SEID;
goto failed;
return FALSE;
}
- sep = find_local_sep_by_seid(session->server, req->acp_seid);
+ sep = find_local_sep_by_seid(session, req->acp_seid);
if (!sep) {
err = AVDTP_BAD_ACP_SEID;
goto failed;
memset(buf, 0, sizeof(buf));
- sep = find_local_sep_by_seid(session->server, req->acp_seid);
+ sep = find_local_sep_by_seid(session, req->acp_seid);
if (!sep) {
err = AVDTP_BAD_ACP_SEID;
goto failed;
return FALSE;
}
- sep = find_local_sep_by_seid(session->server, req->acp_seid);
+ sep = find_local_sep_by_seid(session, req->acp_seid);
if (!sep) {
err = AVDTP_BAD_ACP_SEID;
goto failed;
for (i = 0; i < seid_count; i++, seid++) {
failed_seid = seid->seid;
- sep = find_local_sep_by_seid(session->server,
+ sep = find_local_sep_by_seid(session,
req->first_seid.seid);
if (!sep || !sep->stream) {
err = AVDTP_BAD_ACP_SEID;
return FALSE;
}
- sep = find_local_sep_by_seid(session->server, req->acp_seid);
+ sep = find_local_sep_by_seid(session, req->acp_seid);
if (!sep || !sep->stream) {
err = AVDTP_BAD_ACP_SEID;
goto failed;
for (i = 0; i < seid_count; i++, seid++) {
failed_seid = seid->seid;
- sep = find_local_sep_by_seid(session->server,
+ sep = find_local_sep_by_seid(session,
req->first_seid.seid);
if (!sep || !sep->stream) {
err = AVDTP_BAD_ACP_SEID;
return FALSE;
}
- sep = find_local_sep_by_seid(session->server, req->acp_seid);
+ sep = find_local_sep_by_seid(session, req->acp_seid);
if (!sep || !sep->stream)
return TRUE;
return FALSE;
}
- sep = find_local_sep_by_seid(session->server, req->acp_seid);
+ sep = find_local_sep_by_seid(session, req->acp_seid);
if (!sep || !sep->stream) {
err = AVDTP_BAD_ACP_SEID;
goto failed;
return FALSE;
}
-static struct avdtp *find_session(GSList *list, struct btd_device *device)
-{
- for (; list != NULL; list = g_slist_next(list)) {
- struct avdtp *s = list->data;
-
- if (s->device == device)
- return s;
- }
-
- return NULL;
-}
-
static uint16_t get_version(struct avdtp *session)
{
const sdp_record_t *rec;
return ver;
}
-static struct avdtp *avdtp_get_internal(struct btd_device *device)
-{
- struct avdtp_server *server;
- struct avdtp *session;
-
- server = find_server(servers, device_get_adapter(device));
- if (server == NULL)
- return NULL;
-
- session = find_session(server->sessions, device);
- if (session)
- return session;
-
- session = g_new0(struct avdtp, 1);
-
- session->server = server;
- session->device = btd_device_ref(device);
- /* We don't use avdtp_set_state() here since this isn't a state change
- * but just setting of the initial state */
- session->state = AVDTP_SESSION_STATE_DISCONNECTED;
-
- session->version = get_version(session);
-
- server->sessions = g_slist_append(server->sessions, session);
-
- return session;
-}
-
-struct avdtp *avdtp_get(struct btd_device *device)
-{
- struct avdtp *session;
-
- session = avdtp_get_internal(device);
-
- if (!session)
- return NULL;
-
- return avdtp_ref(session);
-}
-
static void avdtp_connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
{
struct avdtp *session = user_data;
connection_lost(session, err_no);
}
-static void auth_cb(DBusError *derr, void *user_data)
-{
- struct avdtp *session = user_data;
- GError *err = NULL;
-
- if (derr && dbus_error_is_set(derr)) {
- error("Access denied: %s", derr->message);
- connection_lost(session, EACCES);
- return;
- }
-
- if (!bt_io_accept(session->io, avdtp_connect_cb, session, NULL,
- &err)) {
- error("bt_io_accept: %s", err->message);
- connection_lost(session, EACCES);
- g_error_free(err);
- return;
- }
-
- /* This is so that avdtp_connect_cb will know to do the right thing
- * with respect to the disconnect timer */
- session->stream_setup = TRUE;
-}
-
-static void avdtp_confirm_cb(GIOChannel *chan, gpointer data)
+struct avdtp *avdtp_new(GIOChannel *chan, struct btd_device *device,
+ struct queue *lseps)
{
struct avdtp *session;
- char address[18];
- bdaddr_t src, dst;
- GError *err = NULL;
- struct btd_device *device;
-
- bt_io_get(chan, &err,
- BT_IO_OPT_SOURCE_BDADDR, &src,
- BT_IO_OPT_DEST_BDADDR, &dst,
- BT_IO_OPT_DEST, address,
- BT_IO_OPT_INVALID);
- if (err) {
- error("%s", err->message);
- g_error_free(err);
- goto drop;
- }
- DBG("AVDTP: incoming connect from %s", address);
-
- device = btd_adapter_find_device(adapter_find(&src), &dst,
- BDADDR_BREDR);
- if (!device)
- goto drop;
+ session = g_new0(struct avdtp, 1);
- session = avdtp_get_internal(device);
- if (!session)
- goto drop;
+ session->device = btd_device_ref(device);
+ /* We don't use avdtp_set_state() here since this isn't a state change
+ * but just setting of the initial state */
+ session->state = AVDTP_SESSION_STATE_DISCONNECTED;
+ session->lseps = lseps;
- /* This state (ie, session is already *connecting*) happens when the
- * device initiates a connect (really a config'd L2CAP channel) even
- * though there is a connect we initiated in progress. In sink.c &
- * source.c, this state is referred to as XCASE connect:connect.
- * Abort the device's channel in favor of our own.
- */
- if (session->state == AVDTP_SESSION_STATE_CONNECTING) {
- DBG("connect already in progress (XCASE connect:connect)");
- goto drop;
- }
+ session->version = get_version(session);
- if (session->pending_open && session->pending_open->open_acp) {
- if (!bt_io_accept(chan, avdtp_connect_cb, session, NULL, NULL))
- goto drop;
- return;
- }
+ if (!chan)
+ return session;
- if (session->io) {
- error("Refusing unexpected connect from %s", address);
- goto drop;
- }
+ avdtp_set_state(session, AVDTP_SESSION_STATE_CONNECTING);
btd_device_add_uuid(device, ADVANCED_AUDIO_UUID);
session->io = g_io_channel_ref(chan);
- avdtp_set_state(session, AVDTP_SESSION_STATE_CONNECTING);
-
session->io_id = g_io_add_watch(chan, G_IO_ERR | G_IO_HUP | G_IO_NVAL,
(GIOFunc) session_cb, session);
- session->auth_id = btd_request_authorization(&src, &dst,
- ADVANCED_AUDIO_UUID,
- auth_cb, session, 0);
- if (session->auth_id == 0) {
- avdtp_set_state(session, AVDTP_SESSION_STATE_DISCONNECTED);
- goto drop;
- }
+ /* This is so that avdtp_connect_cb will know to do the right thing
+ * with respect to the disconnect timer */
+ session->stream_setup = TRUE;
- return;
+ avdtp_connect_cb(chan, NULL, session);
-drop:
- g_io_channel_shutdown(chan, TRUE, NULL);
+ return session;
}
static GIOChannel *l2cap_connect(struct avdtp *session)
GIOChannel *io;
const bdaddr_t *src;
- src = btd_adapter_get_address(session->server->adapter);
+ src = btd_adapter_get_address(device_get_adapter(session->device));
io = bt_io_connect(avdtp_connect_cb, session,
NULL, &err,
return NULL;
}
+gboolean avdtp_stream_set_transport(struct avdtp_stream *stream, int fd,
+ size_t imtu, size_t omtu)
+{
+ GIOChannel *io;
+
+ if (stream != stream->session->pending_open)
+ return FALSE;
+
+ io = g_io_channel_unix_new(fd);
+
+ handle_transport_connect(stream->session, io, imtu, omtu);
+
+ g_io_channel_unref(io);
+
+ return TRUE;
+}
+
gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock,
uint16_t *imtu, uint16_t *omtu,
GSList **caps)
&req, sizeof(req));
}
-static GIOChannel *avdtp_server_socket(const bdaddr_t *src, gboolean master)
-{
- GError *err = NULL;
- GIOChannel *io;
-
- io = bt_io_listen(NULL, avdtp_confirm_cb,
- NULL, NULL, &err,
- BT_IO_OPT_SOURCE_BDADDR, src,
- BT_IO_OPT_PSM, AVDTP_PSM,
- BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
- BT_IO_OPT_MASTER, master,
- BT_IO_OPT_INVALID);
- if (!io) {
- error("%s", err->message);
- g_error_free(err);
- }
-
- return io;
-}
-
-static struct avdtp_server *avdtp_server_init(struct btd_adapter *adapter)
-{
- struct avdtp_server *server;
-
- server = g_new0(struct avdtp_server, 1);
-
- server->io = avdtp_server_socket(btd_adapter_get_address(adapter),
- TRUE);
- if (!server->io) {
- g_free(server);
- return NULL;
- }
-
- server->adapter = btd_adapter_ref(adapter);
-
- servers = g_slist_append(servers, server);
-
- return server;
-}
-
-struct avdtp_local_sep *avdtp_register_sep(struct btd_adapter *adapter,
- uint8_t type,
+struct avdtp_local_sep *avdtp_register_sep(struct queue *lseps, uint8_t type,
uint8_t media_type,
uint8_t codec_type,
gboolean delay_reporting,
struct avdtp_sep_cfm *cfm,
void *user_data)
{
- struct avdtp_server *server;
struct avdtp_local_sep *sep;
+ uint8_t seid = util_get_uid(&seids, MAX_SEID);
- server = find_server(servers, adapter);
- if (!server) {
- server = avdtp_server_init(adapter);
- if (!server)
- return NULL;
- }
-
- if (g_slist_length(server->seps) > MAX_SEID)
+ if (!seid)
return NULL;
sep = g_new0(struct avdtp_local_sep, 1);
sep->state = AVDTP_STATE_IDLE;
- sep->info.seid = g_slist_length(server->seps) + 1;
+ sep->info.seid = seid;
sep->info.type = type;
sep->info.media_type = media_type;
sep->codec = codec_type;
sep->ind = ind;
sep->cfm = cfm;
sep->user_data = user_data;
- sep->server = server;
- sep->delay_reporting = TRUE;
+ sep->delay_reporting = delay_reporting;
DBG("SEP %p registered: type:%d codec:%d seid:%d", sep,
sep->info.type, sep->codec, sep->info.seid);
- server->seps = g_slist_append(server->seps, sep);
- return sep;
-}
-
-static void avdtp_server_destroy(struct avdtp_server *server)
-{
- g_slist_free_full(server->sessions, avdtp_free);
-
- servers = g_slist_remove(servers, server);
+ if (!queue_push_tail(lseps, sep)) {
+ g_free(sep);
+ return NULL;
+ }
- g_io_channel_shutdown(server->io, TRUE, NULL);
- g_io_channel_unref(server->io);
- btd_adapter_unref(server->adapter);
- g_free(server);
+ return sep;
}
-int avdtp_unregister_sep(struct avdtp_local_sep *sep)
+int avdtp_unregister_sep(struct queue *lseps, struct avdtp_local_sep *sep)
{
- struct avdtp_server *server;
-
if (!sep)
return -EINVAL;
- server = sep->server;
- server->seps = g_slist_remove(server->seps, sep);
-
if (sep->stream)
release_stream(sep->stream, sep->stream->session);
DBG("SEP %p unregistered: type:%d codec:%d seid:%d", sep,
sep->info.type, sep->codec, sep->info.seid);
+ util_clear_uid(&seids, sep->info.seid);
+ queue_remove(lseps, sep);
g_free(sep);
- if (server->seps)
- return 0;
-
- avdtp_server_destroy(server);
-
return 0;
}
struct btd_adapter *avdtp_get_adapter(struct avdtp *session)
{
- return session->server->adapter;
+ return device_get_adapter(session->device);
}
struct btd_device *avdtp_get_device(struct avdtp *session)
} avdtp_session_state_t;
struct avdtp;
+struct avdtp_server;
struct avdtp_stream;
struct avdtp_local_sep;
struct avdtp_remote_sep;
/* Callbacks for indicating when we received a new command. The return value
* indicates whether the command should be rejected or accepted */
struct avdtp_sep_ind {
+ gboolean (*match_codec) (struct avdtp *session,
+ struct avdtp_media_codec_capability *codec,
+ void *user_data);
gboolean (*get_capability) (struct avdtp *session,
struct avdtp_local_sep *sep,
gboolean get_all,
typedef void (*avdtp_discover_cb_t) (struct avdtp *session, GSList *seps,
struct avdtp_error *err, void *user_data);
-struct avdtp *avdtp_get(struct btd_device *device);
-
void avdtp_unref(struct avdtp *session);
struct avdtp *avdtp_ref(struct avdtp *session);
struct avdtp_stream *stream,
unsigned int id);
+gboolean avdtp_stream_set_transport(struct avdtp_stream *stream, int fd,
+ size_t imtu, size_t omtu);
gboolean avdtp_stream_get_transport(struct avdtp_stream *stream, int *sock,
uint16_t *imtu, uint16_t *omtu,
GSList **caps);
int avdtp_delay_report(struct avdtp *session, struct avdtp_stream *stream,
uint16_t delay);
-struct avdtp_local_sep *avdtp_register_sep(struct btd_adapter *adapter,
- uint8_t type,
+struct avdtp_local_sep *avdtp_register_sep(struct queue *lseps, uint8_t type,
uint8_t media_type,
uint8_t codec_type,
gboolean delay_reporting,
struct avdtp_remote_sep *avdtp_find_remote_sep(struct avdtp *session,
struct avdtp_local_sep *lsep);
-int avdtp_unregister_sep(struct avdtp_local_sep *sep);
+int avdtp_unregister_sep(struct queue *lseps, struct avdtp_local_sep *sep);
avdtp_state_t avdtp_sep_get_state(struct avdtp_local_sep *sep);
struct btd_adapter *avdtp_get_adapter(struct avdtp *session);
struct btd_device *avdtp_get_device(struct avdtp *session);
+struct avdtp_server *avdtp_get_server(struct avdtp_local_sep *lsep);
+
+struct avdtp *avdtp_new(GIOChannel *chan, struct btd_device *device,
+ struct queue *lseps);
#include <sys/stat.h>
#include <fcntl.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/sdp.h>
-#include <bluetooth/sdp_lib.h>
-
#include <glib.h>
#include <dbus/dbus.h>
-#include <gdbus/gdbus.h>
+#include "bluetooth/bluetooth.h"
+#include "bluetooth/sdp.h"
+#include "bluetooth/sdp_lib.h"
#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
#include "src/plugin.h"
#include "src/adapter.h"
#include "src/device.h"
#include "src/profile.h"
#include "src/service.h"
-
#include "src/log.h"
#include "src/error.h"
#include "src/sdpd.h"
static GSList *servers = NULL;
static unsigned int avctp_id = 0;
#ifdef __TIZEN_PATCH__
-static uint16_t adapter_avrcp_ver = 0;
+static uint16_t adapter_avrcp_tg_ver = 0;
+static uint16_t adapter_avrcp_ct_ver = 0;
#endif
/* Company IDs supported by this device */
sdp_record_t *record;
sdp_data_t *psm[2], *version, *features;
uint16_t lp = AVCTP_CONTROL_PSM, ap = AVCTP_BROWSING_PSM;
+#ifdef __TIZEN_PATCH__
+ uint16_t avrcp_ver = 0x0103, avctp_ver = 0x0104;
+ uint16_t feat = 0;
+#ifdef ENABLE_AVRCP_CATEGORY1
+ feat = AVRCP_FEATURE_CATEGORY_1;
+#endif
+#ifdef ENABLE_AVRCP_CATEGORY2
+ feat = feat | AVRCP_FEATURE_CATEGORY_2;
+#endif
+#else
uint16_t avrcp_ver = 0x0105, avctp_ver = 0x0103;
uint16_t feat = ( AVRCP_FEATURE_CATEGORY_1 |
AVRCP_FEATURE_CATEGORY_2 |
AVRCP_FEATURE_CATEGORY_3 |
AVRCP_FEATURE_CATEGORY_4 |
AVRCP_FEATURE_BROWSING);
+#endif
record = sdp_record_alloc();
if (!record)
/* Bluetooth Profile Descriptor List */
sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
profile[0].version = avrcp_ver;
+#ifdef __TIZEN_PATCH__
+ adapter_avrcp_ct_ver = avrcp_ver;
+#endif
pfseq = sdp_list_append(NULL, &profile[0]);
sdp_set_profile_descs(record, pfseq);
sdp_list_t *aproto_control, *proto_control[2];
sdp_record_t *record;
sdp_data_t *psm_control, *version, *features, *psm_browsing;
- sdp_list_t *aproto_browsing, *proto_browsing[2] = {0};
+#ifndef __TIZEN_PATCH__
+ sdp_list_t *aproto_browsing;
+#endif
+ sdp_list_t *proto_browsing[2] = {0};
uint16_t lp = AVCTP_CONTROL_PSM;
uint16_t lp_browsing = AVCTP_BROWSING_PSM;
#ifdef __TIZEN_PATCH__
uint16_t avrcp_ver = 0x0103, avctp_ver = 0x0104;
- uint16_t feat = AVRCP_FEATURE_CATEGORY_1 |
- AVRCP_FEATURE_PLAYER_SETTINGS;
+ uint16_t feat = 0;
+#ifdef ENABLE_AVRCP_CATEGORY1
+ feat = AVRCP_FEATURE_CATEGORY_1 |
+ AVRCP_FEATURE_PLAYER_SETTINGS;
+#endif
+#ifdef ENABLE_AVRCP_CATEGORY2
+ feat = feat | AVRCP_FEATURE_CATEGORY_2;
+#endif
#else
uint16_t avrcp_ver = 0x0104, avctp_ver = 0x0103;
uint16_t feat = ( AVRCP_FEATURE_CATEGORY_1 |
sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
profile[0].version = avrcp_ver;
#ifdef __TIZEN_PATCH__
- adapter_avrcp_ver = avrcp_ver;
+ adapter_avrcp_tg_ver = avrcp_ver;
#endif
pfseq = sdp_list_append(NULL, &profile[0]);
sdp_set_profile_descs(record, pfseq);
sdp_list_free(proto_browsing[0], NULL);
sdp_list_free(proto_browsing[1], NULL);
sdp_list_free(apseq_browsing, NULL);
+#ifndef __TIZEN_PATCH__
sdp_list_free(aproto_browsing, NULL);
+#endif
free(psm_control);
free(version);
case AVRCP_EVENT_STATUS_CHANGED:
case AVRCP_EVENT_TRACK_CHANGED:
case AVRCP_EVENT_SETTINGS_CHANGED:
+#ifndef __TIZEN_PATCH__
case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED:
case AVRCP_EVENT_UIDS_CHANGED:
case AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED:
case AVRCP_EVENT_VOLUME_CHANGED:
+#endif
avrcp_register_notification(session, event);
break;
}
return;
#ifdef __TIZEN_PATCH__
- if (adapter_avrcp_ver < 0x0104)
+ if (adapter_avrcp_tg_ver < 0x0104)
return;
#endif
return;
controller = data_init(session, AVRCP_TARGET_UUID);
+#ifdef __TIZEN_PATCH__
+ /* Fix : NULL_RETURNS */
+ if (controller == NULL) {
+ error("controller is NULL");
+ return;
+ }
+#endif
session->controller = controller;
DBG("%p version 0x%04x", controller, controller->version);
+#ifdef __TIZEN_PATCH__
+ if ((controller->version >= 0x0104) && (adapter_avrcp_ct_ver >= 0x0104))
+ session->supported_events |= (1 << AVRCP_EVENT_VOLUME_CHANGED);
+#else
if (controller->version >= 0x0104)
session->supported_events |= (1 << AVRCP_EVENT_VOLUME_CHANGED);
+#endif
service = btd_device_get_service(session->dev, AVRCP_TARGET_UUID);
if (service != NULL)
if (controller->version < 0x0104)
return;
+#ifdef __TIZEN_PATCH__
+ if (adapter_avrcp_ct_ver < 0x0104)
+ return;
+#endif
+
if (!(controller->features & AVRCP_FEATURE_BROWSING))
return;
static struct btd_profile avrcp_target_profile = {
.name = "audio-avrcp-target",
- .version = 0x0105,
.remote_uuid = AVRCP_TARGET_UUID,
- .local_uuid = AVRCP_REMOTE_UUID,
- .auth_uuid = AVRCP_REMOTE_UUID,
-
.device_probe = avrcp_target_probe,
.device_remove = avrcp_target_remove,
static struct btd_profile avrcp_controller_profile = {
.name = "avrcp-controller",
- .version = 0x0104,
.remote_uuid = AVRCP_REMOTE_UUID,
- .local_uuid = AVRCP_TARGET_UUID,
- .auth_uuid = AVRCP_REMOTE_UUID,
-
.device_probe = avrcp_controller_probe,
.device_remove = avrcp_controller_remove,
static int avrcp_init(void)
{
+#ifdef __TIZEN_PATCH__
+#ifdef SUPPORT_AVRCP_CONTROL
btd_profile_register(&avrcp_controller_profile);
+#else
btd_profile_register(&avrcp_target_profile);
-
+#endif
+#else
+ btd_profile_register(&avrcp_controller_profile);
+ btd_profile_register(&avrcp_target_profile);
+#endif
return 0;
}
static void avrcp_exit(void)
{
+#ifdef __TIZEN_PATCH__
+#ifdef SUPPORT_AVRCP_CONTROL
+ btd_profile_unregister(&avrcp_controller_profile);
+#else
+ btd_profile_unregister(&avrcp_target_profile);
+#endif
+#else
btd_profile_unregister(&avrcp_controller_profile);
btd_profile_unregister(&avrcp_target_profile);
+#endif
}
BLUETOOTH_PLUGIN_DEFINE(avrcp, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
#include <sys/stat.h>
#include <fcntl.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/sdp.h>
-#include <bluetooth/sdp_lib.h>
-
#include <glib.h>
#include <dbus/dbus.h>
-#include <gdbus/gdbus.h>
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
#include "src/adapter.h"
#include "src/device.h"
#include "src/profile.h"
#include "src/service.h"
-
#include "src/log.h"
#include "src/error.h"
#include "src/sdpd.h"
#include <inttypes.h>
#include <glib.h>
-#include <gdbus/gdbus.h>
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
#include "src/plugin.h"
#include "src/adapter.h"
#include "src/device.h"
#include "src/dbus-common.h"
#include "src/profile.h"
+#ifdef __TIZEN_PATCH__
+#include "src/service.h"
+#endif
+
#include "src/uuid-helper.h"
#include "src/log.h"
#include "src/error.h"
+#include "src/shared/queue.h"
#include "avdtp.h"
#include "media.h"
done:
endpoint->transports = g_slist_remove(endpoint->transports, transport);
#ifdef __TIZEN_PATCH__
- if (mp = media_adapter_get_player(endpoint->adapter))
+ if ((mp = media_adapter_get_player(endpoint->adapter)))
if (mp->sink_watch) {
sink_remove_state_cb(mp->sink_watch);
mp->sink_watch = 0;
/* Clear endpoint configuration in case of NO_REPLY error */
if (dbus_error_has_name(&err, DBUS_ERROR_NO_REPLY)) {
- if (request->cb)
- request->cb(endpoint, NULL, size,
- request->user_data);
clear_endpoint(endpoint);
dbus_message_unref(reply);
dbus_error_free(&err);
#ifdef __TIZEN_PATCH__
set_avrcp_status = FALSE;
adapter = find_adapter(device);
- if (mp = media_adapter_get_player(adapter))
+ if ((mp = media_adapter_get_player(adapter)))
media_set_sink_callback(device, mp);
#endif
if (mp->track == NULL)
return UINT64_MAX;
+#ifdef __TIZEN_PATCH__
+ if (!g_hash_table_lookup(mp->track, "Title"))
+ return UINT64_MAX;
+#endif
+
return 0;
}
#include <glib.h>
#include <dbus/dbus.h>
-#include <gdbus/gdbus.h>
+
+#include "gdbus/gdbus.h"
#include "src/log.h"
#include "src/dbus-common.h"
#include <stdbool.h>
#include <errno.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/sdp.h>
-
#include <glib.h>
#include <dbus/dbus.h>
-#include <gdbus/gdbus.h>
-#include "src/log.h"
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "gdbus/gdbus.h"
+
+#include "src/log.h"
#include "src/adapter.h"
#include "src/device.h"
#include "src/service.h"
#include "src/error.h"
#include "src/dbus-common.h"
+#include "src/shared/queue.h"
#include "avdtp.h"
#include "media.h"
#ifndef __TIZEN_PATCH__
if (!sink->session)
- sink->session = avdtp_get(btd_service_get_device(service));
+ sink->session = a2dp_avdtp_get(btd_service_get_device(service));
if (!sink->session) {
DBG("Unable to get a session");
#ifdef __TIZEN_PATCH__
if (!sink->session)
- sink->session = avdtp_get(btd_service_get_device(service));
+ sink->session = a2dp_avdtp_get(btd_service_get_device(service));
if (!sink->session) {
DBG("Unable to get a session");
#include <stdbool.h>
#include <errno.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/sdp.h>
-
#include <glib.h>
#include <dbus/dbus.h>
-#include <gdbus/gdbus.h>
-#include "src/log.h"
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "gdbus/gdbus.h"
+
+#include "src/log.h"
#include "src/adapter.h"
#include "src/device.h"
#include "src/service.h"
#include "src/error.h"
#include "src/dbus-common.h"
+#include "src/shared/queue.h"
#include "avdtp.h"
#include "media.h"
struct source *source = btd_service_get_user_data(service);
if (!source->session)
- source->session = avdtp_get(btd_service_get_device(service));
+ source->session = a2dp_avdtp_get(btd_service_get_device(service));
if (!source->session) {
DBG("Unable to get a session");
#include <errno.h>
#include <glib.h>
-#include <gdbus/gdbus.h>
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
#include "src/adapter.h"
#include "src/device.h"
#include "src/dbus-common.h"
#include "src/log.h"
#include "src/error.h"
+#include "src/shared/queue.h"
#include "avdtp.h"
#include "media.h"
guint id;
if (a2dp->session == NULL) {
- a2dp->session = avdtp_get(transport->device);
+ a2dp->session = a2dp_avdtp_get(transport->device);
if (a2dp->session == NULL)
return 0;
}
#include <string.h>
#include <signal.h>
#include <sys/socket.h>
-
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/l2cap.h>
-#include <bluetooth/sdp.h>
-#include <bluetooth/sdp_lib.h>
-
#include <netinet/in.h>
+#include "lib/bluetooth.h"
+#include "lib/l2cap.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+
#include "cups.h"
#define HCRP_PDU_CREDIT_GRANT 0x0001
#include <assert.h>
#include <signal.h>
#include <sys/socket.h>
+
#include <glib.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/sdp.h>
-#include <bluetooth/sdp_lib.h>
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
-#include <gdbus/gdbus.h>
+#include "gdbus/gdbus.h"
#include "cups.h"
#include <signal.h>
#include <sys/socket.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/sdp.h>
-#include <bluetooth/sdp_lib.h>
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
#include "cups.h"
#include <signal.h>
#include <sys/socket.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/rfcomm.h>
-#include <bluetooth/sdp.h>
-#include <bluetooth/sdp_lib.h>
+#include "lib/bluetooth.h"
+#include "lib/rfcomm.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
#include "cups.h"
#include <errno.h>
#include <stdbool.h>
+
#include <glib.h>
-#include <gdbus/gdbus.h>
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
#include "src/plugin.h"
#include "src/adapter.h"
#include "src/device.h"
#include <glib.h>
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
#include "lib/uuid.h"
+
#include "src/plugin.h"
#include "src/adapter.h"
#include "src/device.h"
#include <glib.h>
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/sdp.h"
#include "lib/uuid.h"
+
#include "src/shared/util.h"
#include "src/shared/att.h"
#include "src/shared/queue.h"
#include "src/service.h"
#include "src/log.h"
+#define GAP_UUID16 0x1800
+
/* Generic Attribute/Access Service */
struct gas {
struct btd_device *device;
struct gatt_db *db;
+ unsigned int db_id;
struct bt_gatt_client *client;
- uint16_t start_handle, end_handle;
+ struct gatt_db_attribute *attr;
};
static GSList *devices;
static void gas_free(struct gas *gas)
{
- btd_device_unref(gas->device);
+ gatt_db_unregister(gas->db, gas->db_id);
gatt_db_unref(gas->db);
bt_gatt_client_unref(gas->client);
+ btd_device_unref(gas->device);
g_free(gas);
}
return gas->device == device ? 0 : -1;
}
-static char *name2utf8(const uint8_t *name, uint8_t len)
+static char *name2utf8(const uint8_t *name, uint16_t len)
{
char utf8_name[HCI_MAX_NAME_LENGTH + 2];
int i;
if (g_utf8_validate((const char *) name, len, NULL))
return g_strndup((char *) name, len);
+ len = MIN(len, sizeof(utf8_name) - 1);
+
memset(utf8_name, 0, sizeof(utf8_name));
strncpy(utf8_name, (char *) name, len);
void *user_data)
{
struct gas *gas = user_data;
- char *name = name2utf8(value, length);
+ char *name;
+
+ if (!success) {
+ DBG("Reading device name failed with ATT errror: %u",
+ att_ecode);
+ return;
+ }
+
+ if (!length)
+ return;
+
+ name = name2utf8(value, length);
DBG("GAP Device Name: %s", name);
static void handle_gap_service(struct gas *gas)
{
- struct gatt_db_attribute *attr;
-
- attr = gatt_db_get_attribute(gas->db, gas->start_handle);
- if (!attr) {
- error("Service with handle 0x%04x not found in db",
- gas->start_handle);
- return;
- }
-
- gatt_db_service_foreach_char(attr, handle_characteristic, gas);
+ gatt_db_service_foreach_char(gas->attr, handle_characteristic, gas);
}
static int gap_driver_probe(struct btd_service *service)
{
struct btd_device *device = btd_service_get_device(service);
struct gas *gas;
- uint16_t start_handle, end_handle;
GSList *l;
char addr[18];
- if (!btd_service_get_gatt_handles(service, &start_handle, &end_handle))
- return -1;
-
ba2str(device_get_address(device), addr);
- DBG("GAP profile probe (%s): start: 0x%04x, end 0x%04x", addr,
- start_handle, end_handle);
+ DBG("GAP profile probe (%s)", addr);
- /*
- * There can't be more than one instance of the GAP service on the same
- * device.
- */
+ /* Ignore, if we were probed for this device already */
l = g_slist_find_custom(devices, device, cmp_device);
if (l) {
- error("More than one GAP service exists on device");
+ error("Profile probed twice for the same device!");
return -1;
}
gas = g_new0(struct gas, 1);
+ if (!gas)
+ return -1;
gas->device = btd_device_ref(device);
- gas->start_handle = start_handle;
- gas->end_handle = end_handle;
devices = g_slist_append(devices, gas);
return 0;
{
struct btd_device *device = btd_service_get_device(service);
struct gas *gas;
- uint16_t start_handle, end_handle;
GSList *l;
char addr[18];
- if (!btd_service_get_gatt_handles(service, &start_handle,
- &end_handle)) {
- error("Removed service is not a GATT service");
- return;
- }
-
ba2str(device_get_address(device), addr);
- DBG("GAP profile remove (%s): start: 0x%04x, end 0x%04x", addr,
- start_handle, end_handle);
+ DBG("GAP profile remove (%s)", addr);
l = g_slist_find_custom(devices, device, cmp_device);
if (!l) {
gas = l->data;
- if (gas->start_handle != start_handle ||
- gas->end_handle != end_handle) {
- error("Removed unknown GAP service");
+ devices = g_slist_remove(devices, gas);
+ gas_free(gas);
+}
+
+static void foreach_gap_service(struct gatt_db_attribute *attr, void *user_data)
+{
+ struct gas *gas = user_data;
+
+ if (gas->attr) {
+ error("More than one GAP service exists for this device");
return;
}
- devices = g_slist_remove(devices, gas);
- gas_free(gas);
+ gas->attr = attr;
+ handle_gap_service(gas);
+}
+
+static void service_added(struct gatt_db_attribute *attr, void *user_data)
+{
+ struct gas *gas = user_data;
+ bt_uuid_t uuid, gap_uuid;
+
+ if (!bt_gatt_client_is_ready(gas->client))
+ return;
+
+ gatt_db_attribute_get_service_uuid(attr, &uuid);
+ bt_uuid16_create(&gap_uuid, GAP_UUID16);
+
+ if (bt_uuid_cmp(&uuid, &gap_uuid))
+ return;
+
+ if (gas->attr) {
+ error("More than one GAP service added to device");
+ return;
+ }
+
+ DBG("GAP service added");
+
+ gas->attr = attr;
+ handle_gap_service(gas);
+}
+
+static void service_removed(struct gatt_db_attribute *attr, void *user_data)
+{
+ struct gas *gas = user_data;
+
+ if (gas->attr != attr)
+ return;
+
+ DBG("GAP service removed");
+
+ gas->attr = NULL;
}
static int gap_driver_accept(struct btd_service *service)
struct gatt_db *db = btd_device_get_gatt_db(device);
struct bt_gatt_client *client = btd_device_get_gatt_client(device);
struct gas *gas;
- uint16_t start_handle, end_handle;
GSList *l;
char addr[18];
-
- if (!btd_service_get_gatt_handles(service, &start_handle,
- &end_handle)) {
- error("Service is not a GATT service");
- return -1;
- }
+ bt_uuid_t gap_uuid;
ba2str(device_get_address(device), addr);
- DBG("GAP profile accept (%s): start: 0x%04x, end 0x%04x", addr,
- start_handle, end_handle);
+ DBG("GAP profile accept (%s)", addr);
l = g_slist_find_custom(devices, device, cmp_device);
if (!l) {
gas = l->data;
- if (gas->start_handle != start_handle ||
- gas->end_handle != end_handle) {
- error("Accepting unknown GAP service");
- return -1;
- }
-
/* Clean-up any old client/db and acquire the new ones */
+ gas->attr = NULL;
+ gatt_db_unregister(gas->db, gas->db_id);
gatt_db_unref(gas->db);
bt_gatt_client_unref(gas->client);
gas->db = gatt_db_ref(db);
gas->client = bt_gatt_client_ref(client);
+ gas->db_id = gatt_db_register(db, service_added, service_removed, gas,
+ NULL);
- /* Handle the service */
- handle_gap_service(gas);
+ /* Handle the GAP services */
+ bt_uuid16_create(&gap_uuid, GAP_UUID16);
+ gatt_db_foreach_service(db, &gap_uuid, foreach_gap_service, gas);
return 0;
}
#include <glib.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/l2cap.h>
-#include <gdbus/gdbus.h>
+#include "lib/bluetooth.h"
+#include "lib/l2cap.h"
+#include "lib/sdp.h"
+
+#include "gdbus/gdbus.h"
+
#include "src/dbus-common.h"
#include "src/log.h"
#include "src/error.h"
#endif
#include <errno.h>
-#include <gdbus/gdbus.h>
-#include "src/plugin.h"
+#include "gdbus/gdbus.h"
+#include "src/plugin.h"
#include "hdp_manager.h"
static int hdp_init(void)
#include <stdbool.h>
-#include <bluetooth/sdp.h>
-#include <bluetooth/sdp_lib.h>
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "lib/uuid.h"
#include "btio/btio.h"
-#include "lib/uuid.h"
#include "src/adapter.h"
#include "src/device.h"
#include "src/profile.h"
static struct btd_profile hdp_source_profile = {
.name = "hdp-source",
- .version = 0x0100,
-
.remote_uuid = HDP_SOURCE_UUID,
- .local_uuid = HDP_SINK_UUID,
.device_probe = hdp_driver_probe,
.device_remove = hdp_driver_remove,
static struct btd_profile hdp_sink_profile = {
.name = "hdp-sink",
- .version = 0x0100,
-
.remote_uuid = HDP_SINK_UUID,
- .local_uuid = HDP_SOURCE_UUID,
.device_probe = hdp_driver_probe,
.device_remove = hdp_driver_remove,
#include <glib.h>
-#include <gdbus/gdbus.h>
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "lib/uuid.h"
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/sdp.h>
-#include <bluetooth/sdp_lib.h>
+#include "gdbus/gdbus.h"
+#include "btio/btio.h"
#include "src/adapter.h"
#include "src/device.h"
-
#include "src/sdpd.h"
#include "src/sdp-client.h"
#include "src/uuid-helper.h"
-
-#include "lib/uuid.h"
-#include "btio/btio.h"
-
#include "src/log.h"
#include "src/dbus-common.h"
#include <errno.h>
#include <stdbool.h>
#include <glib.h>
-#include <gdbus/gdbus.h>
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
#include "src/plugin.h"
#include "src/adapter.h"
#include "src/dbus-common.h"
#include <sys/signalfd.h>
#include <glib.h>
-#include <gdbus.h>
+
+#include "gdbus/gdbus.h"
#define IAP_PATH "/org/bluez/iap"
#include <unistd.h>
#include <sys/ioctl.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hidp.h>
-#include <bluetooth/sdp.h>
-#include <bluetooth/sdp_lib.h>
-
-#include <gdbus/gdbus.h>
+#include "lib/bluetooth.h"
+#include "lib/hidp.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "lib/uuid.h"
-#include "src/log.h"
+#include "gdbus/gdbus.h"
#include "btio/btio.h"
-#include "lib/uuid.h"
+#include "src/log.h"
#include "src/adapter.h"
#include "src/device.h"
#include "src/profile.h"
return TRUE;
}
-static gboolean property_get_connected(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
-{
- struct input_device *idev = data;
- dbus_bool_t connected;
-
- if (idev->service == NULL)
- return FALSE;
-
- if (btd_service_get_state(idev->service)
- == BTD_SERVICE_STATE_CONNECTED) {
- connected = true;
- } else
- connected = false;
-
- dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &connected);
-
- return TRUE;
-}
-
static const GDBusPropertyTable input_properties[] = {
{ "ReconnectMode", "s", property_get_reconnect_mode },
- { "Connected", "b", property_get_connected },
{ }
};
#include <sys/stat.h>
#include <fcntl.h>
-#include <bluetooth/bluetooth.h>
-
#include <glib.h>
-#include "src/log.h"
-
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
#include "lib/uuid.h"
+
+#include "src/log.h"
#include "src/adapter.h"
#include "src/device.h"
#include "src/profile.h"
#include "src/service.h"
#include "src/shared/util.h"
#include "src/shared/uhid.h"
-
#include "src/plugin.h"
#include "suspend.h"
switch (desc->uuid16) {
case GATT_CLIENT_CHARAC_CFG_UUID:
report = user_data;
- attrib = report->hogdev->attrib;
write_ccc(desc->handle, report);
break;
case GATT_REPORT_REFERENCE:
#include <errno.h>
#include <stdbool.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/sdp.h>
-#include <bluetooth/sdp_lib.h>
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "lib/uuid.h"
#include "src/log.h"
#include "src/plugin.h"
-
-#include "lib/uuid.h"
#include "src/adapter.h"
#include "src/device.h"
#include "src/profile.h"
static struct btd_profile input_profile = {
.name = "input-hid",
- .version = 0x0101,
-
.local_uuid = HID_UUID,
.remote_uuid = HID_UUID,
#include <stdbool.h>
#include <errno.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/sdp.h>
-
#include <glib.h>
#include <dbus/dbus.h>
-#include "src/log.h"
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+#include "src/log.h"
#include "src/uuid-helper.h"
#include "btio/btio.h"
-#include "lib/uuid.h"
#include "src/adapter.h"
#include "src/device.h"
#include "src/profile.h"
bacpy(&server->confirm->dst, &dst);
ret = btd_request_authorization(&src, &dst, HID_UUID,
- auth_callback, server, 0);
+ auth_callback, server);
if (ret != 0)
return;
#include <net/if.h>
#include <linux/sockios.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/l2cap.h>
-#include <bluetooth/bnep.h>
-
#include <glib.h>
+#include "lib/bluetooth.h"
+#include "lib/l2cap.h"
+#include "lib/bnep.h"
+#include "lib/uuid.h"
+
#include "src/log.h"
#include "src/shared/util.h"
-#include "lib/uuid.h"
#include "btio/btio.h"
#include "bnep.h"
void *disconn_data;
};
-uint16_t bnep_service_id(const char *svc)
-{
- int i;
- uint16_t id;
-
- /* Friendly service name */
- for (i = 0; __svc[i].name; i++) {
- if (!strcasecmp(svc, __svc[i].name))
- return __svc[i].id;
- }
-
- /* UUID 128 string */
- for (i = 0; __svc[i].uuid128; i++) {
- if (!strcasecmp(svc, __svc[i].uuid128))
- return __svc[i].id;
- }
-
- /* Try convert to HEX */
- id = strtol(svc, NULL, 16);
- if ((id < BNEP_SVC_PANU) || (id > BNEP_SVC_GN))
- return 0;
-
- return id;
-}
-
const char *bnep_uuid(uint16_t id)
{
int i;
err = -errno;
error("bnep: Can't add %s to the bridge %s: %s(%d)",
devname, bridge, strerror(-err), -err);
- } else
- info("bridge %s: interface %s added", bridge, devname);
+ } else {
+ info("bnep: bridge %s: interface %s added", bridge, devname);
+ }
close(sk);
err = -errno;
error("bnep: Can't delete %s from the bridge %s: %s(%d)",
devname, bridge, strerror(-err), -err);
- } else
- info("bridge %s: interface %s removed", bridge, devname);
+ } else {
+ info("bnep: bridge %s: interface %s removed", bridge, devname);
+ }
close(sk);
return send(sk, &rsp, sizeof(rsp), 0);
}
-uint16_t bnep_setup_chk(uint16_t dst, uint16_t src)
-{
- /* Allowed PAN Profile scenarios */
- switch (dst) {
- case BNEP_SVC_NAP:
- case BNEP_SVC_GN:
- if (src == BNEP_SVC_PANU)
- return 0;
- return BNEP_CONN_INVALID_SRC;
- case BNEP_SVC_PANU:
- if (src == BNEP_SVC_PANU || src == BNEP_SVC_GN ||
- src == BNEP_SVC_NAP)
- return 0;
-
- return BNEP_CONN_INVALID_SRC;
- }
-
- return BNEP_CONN_INVALID_DST;
-}
-
uint16_t bnep_setup_decode(struct bnep_setup_conn_req *req, uint16_t *dst,
uint16_t *src)
{
return BNEP_CONN_INVALID_SVC;
}
- return BNEP_SUCCESS;
+ /* Allowed PAN Profile scenarios */
+ switch (*dst) {
+ case BNEP_SVC_NAP:
+ case BNEP_SVC_GN:
+ if (*src == BNEP_SVC_PANU)
+ return BNEP_SUCCESS;
+ return BNEP_CONN_INVALID_SRC;
+ case BNEP_SVC_PANU:
+ if (*src == BNEP_SVC_PANU || *src == BNEP_SVC_GN ||
+ *src == BNEP_SVC_NAP)
+ return BNEP_SUCCESS;
+
+ return BNEP_CONN_INVALID_SRC;
+ }
+
+ return BNEP_CONN_INVALID_DST;
}
#ifdef __TIZEN_PATCH__
int bnep_init(void);
int bnep_cleanup(void);
-uint16_t bnep_service_id(const char *svc);
const char *bnep_uuid(uint16_t id);
const char *bnep_name(uint16_t id);
void bnep_server_delete(char *bridge, char *iface, const bdaddr_t *addr);
ssize_t bnep_send_ctrl_rsp(int sk, uint8_t type, uint8_t ctrl, uint16_t resp);
-uint16_t bnep_setup_chk(uint16_t dst_role, uint16_t src_role);
uint16_t bnep_setup_decode(struct bnep_setup_conn_req *req, uint16_t *dst,
uint16_t *src);
#ifdef __TIZEN_PATCH__
#include <unistd.h>
#include <netinet/in.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/bnep.h>
-#include <bluetooth/sdp.h>
-
#include <glib.h>
-#include <gdbus/gdbus.h>
+
+#include "lib/bluetooth.h"
+#include "lib/bnep.h"
+#include "lib/sdp.h"
+
+#include "gdbus/gdbus.h"
#include "btio/btio.h"
#include "src/log.h"
#include "src/profile.h"
#include "src/service.h"
#include "src/error.h"
+#include "lib/uuid.h"
#include "bnep.h"
#include "connection.h"
static GSList *peers = NULL;
-static uint16_t get_service_id(struct btd_service *service)
+static uint16_t get_pan_srv_id(const char *svc)
{
- return bnep_service_id(btd_service_get_profile(service)->remote_uuid);
+ if (!strcasecmp(svc, "panu") || !strcasecmp(svc, PANU_UUID))
+ return BNEP_SVC_PANU;
+ if (!strcasecmp(svc, "nap") || !strcasecmp(svc, NAP_UUID))
+ return BNEP_SVC_NAP;
+ if (!strcasecmp(svc, "gn") || !strcasecmp(svc, GN_UUID))
+ return BNEP_SVC_GN;
+
+ return 0;
}
static struct network_peer *find_peer(GSList *list, struct btd_device *device)
DBUS_TYPE_INVALID) == FALSE)
return btd_error_invalid_args(msg);
- id = bnep_service_id(svc);
+ id = get_pan_srv_id(svc);
uuid = bnep_uuid(id);
if (uuid == NULL)
}
/* Connect and initiate BNEP session */
-int connection_connect(struct btd_service *service)
+int connection_connect(struct btd_service *svc)
{
- struct network_conn *nc = btd_service_get_user_data(service);
+ struct network_conn *nc = btd_service_get_user_data(svc);
struct network_peer *peer = nc->peer;
- uint16_t id = get_service_id(service);
+ uint16_t id = get_pan_srv_id(btd_service_get_profile(svc)->remote_uuid);
GError *err = NULL;
const bdaddr_t *src;
const bdaddr_t *dst;
return 0;
}
-int connection_disconnect(struct btd_service *service)
+int connection_disconnect(struct btd_service *svc)
{
- struct network_conn *nc = btd_service_get_user_data(service);
+ struct network_conn *nc = btd_service_get_user_data(svc);
if (nc->state == DISCONNECTED)
return 0;
{ }
};
-void connection_unregister(struct btd_service *service)
+void connection_unregister(struct btd_service *svc)
{
- struct btd_device *device = btd_service_get_device(service);
- struct network_conn *conn = btd_service_get_user_data(service);
+ struct btd_device *device = btd_service_get_device(svc);
+ struct network_conn *conn = btd_service_get_user_data(svc);
struct network_peer *peer = conn->peer;
- uint16_t id = get_service_id(service);
+ uint16_t id = get_pan_srv_id(btd_service_get_profile(svc)->remote_uuid);
DBG("%s id %u", device_get_path(device), id);
return peer;
}
-int connection_register(struct btd_service *service)
+int connection_register(struct btd_service *svc)
{
- struct btd_device *device = btd_service_get_device(service);
+ struct btd_device *device = btd_service_get_device(svc);
struct network_peer *peer;
struct network_conn *nc;
- uint16_t id = get_service_id(service);
+ uint16_t id = get_pan_srv_id(btd_service_get_profile(svc)->remote_uuid);
DBG("%s id %u", device_get_path(device), id);
nc = g_new0(struct network_conn, 1);
nc->id = id;
- nc->service = btd_service_ref(service);
+ nc->service = btd_service_ref(svc);
nc->state = DISCONNECTED;
nc->peer = peer;
- btd_service_set_user_data(service, nc);
+ btd_service_set_user_data(svc, nc);
DBG("id %u registered", id);
#include <errno.h>
#include <stdbool.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/bnep.h>
-#include <bluetooth/sdp.h>
-
#include <glib.h>
-#include <gdbus/gdbus.h>
+
+#include "lib/bluetooth.h"
+#include "lib/bnep.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
#include "src/log.h"
#include "src/plugin.h"
-
-#include "lib/uuid.h"
#include "src/adapter.h"
#include "src/device.h"
#include "src/profile.h"
static struct btd_profile panu_profile = {
.name = "network-panu",
- .version = 0x0100,
.local_uuid = NAP_UUID,
.remote_uuid = PANU_UUID,
- .auth_uuid = BNEP_SVC_UUID,
.device_probe = connection_register,
.device_remove = connection_unregister,
.connect = connection_connect,
static struct btd_profile gn_profile = {
.name = "network-gn",
- .version = 0x0100,
.local_uuid = PANU_UUID,
.remote_uuid = GN_UUID,
- .auth_uuid = BNEP_SVC_UUID,
.device_probe = connection_register,
.device_remove = connection_unregister,
.connect = connection_connect,
static struct btd_profile nap_profile = {
.name = "network-nap",
- .version = 0x0100,
.local_uuid = PANU_UUID,
.remote_uuid = NAP_UUID,
- .auth_uuid = BNEP_SVC_UUID,
.device_probe = connection_register,
.device_remove = connection_unregister,
.connect = connection_connect,
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
-
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/bnep.h>
-#include <bluetooth/sdp.h>
-#include <bluetooth/sdp_lib.h>
#include <netinet/in.h>
#include <glib.h>
-#include <gdbus/gdbus.h>
-#include "btio/btio.h"
+#include "lib/bluetooth.h"
+#include "lib/bnep.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
+#include "btio/btio.h"
#include "src/dbus-common.h"
#include "src/adapter.h"
#include "src/log.h"
{
struct network_server *ns = NULL;
struct network_session *session = NULL;
- char address[24] = {0};
+ char address[20] = {0};
const char* paddr = address;
char *name_str = NULL;
GIOCondition cond, gpointer user_data)
{
struct network_adapter *na = user_data;
- struct network_server *ns = NULL;
+ struct network_server *ns;
uint8_t packet[BNEP_MTU];
struct bnep_setup_conn_req *req = (void *) packet;
uint16_t src_role, dst_role, rsp = BNEP_CONN_NOT_ALLOWED;
return FALSE;
rsp = bnep_setup_decode(req, &dst_role, &src_role);
- if (rsp)
- goto reply;
-
- rsp = bnep_setup_chk(dst_role, src_role);
- if (rsp)
+ if (rsp != BNEP_SUCCESS)
goto reply;
rsp = BNEP_CONN_NOT_ALLOWED;
}
ns = find_server(na->servers, BNEP_SVC_NAP);
- if (!ns)
- goto drop;
-
- if (!ns->record_id)
- goto drop;
-
- if (!ns->bridge)
+ if (!ns || !ns->record_id || !ns->bridge)
goto drop;
na->setup = g_new0(struct network_session, 1);
na->setup->io = g_io_channel_ref(chan);
ret = btd_request_authorization(&src, &dst, BNEP_SVC_UUID,
- auth_cb, na, 0);
+ auth_cb, na);
if (ret == 0) {
error("Refusing connect from %s", address);
setup_destroy(na);
for (list = ns->sessions; list; list = list->next) {
struct network_session *session = list->data;
- char address[24] = {0};
- char *paddr = address;
- const char *name_str = NULL;
if (*session->dev == '\0')
continue;
bnep_server_delete(ns->bridge, session->dev, &session->dst);
-
- name_str = session->dev;
- ba2str(&session->dst, paddr);
-
- DBG("send peerdisconnected signal");
-
- g_dbus_emit_signal(btd_get_dbus_connection(),
- adapter_get_path(ns->na->adapter),
- NETWORK_SERVER_INTERFACE, "PeerDisconnected",
- DBUS_TYPE_STRING, &name_str,
- DBUS_TYPE_STRING, &paddr,
- DBUS_TYPE_INVALID);
}
#ifndef __TIZEN_PATCH__
na = g_new0(struct network_adapter, 1);
na->adapter = btd_adapter_ref(adapter);
- na->io = bt_io_listen(NULL, confirm_event, na,
- NULL, &err,
+ na->io = bt_io_listen(NULL, confirm_event, na, NULL, &err,
BT_IO_OPT_SOURCE_BDADDR,
btd_adapter_get_address(adapter),
BT_IO_OPT_PSM, BNEP_PSM,
#include <glib.h>
#include <dbus/dbus.h>
-#include <gdbus/gdbus.h>
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
#include "src/log.h"
#include "src/adapter.h"
#include "attrib/gattrib.h"
#include <glib.h>
#include <dbus/dbus.h>
-#include <gdbus/gdbus.h>
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
#include "src/log.h"
#include "src/adapter.h"
#include "src/device.h"
#include <errno.h>
#include <stdint.h>
#include <glib.h>
-#include <gdbus/gdbus.h>
+
+#include "gdbus/gdbus.h"
#include "src/log.h"
#include "src/plugin.h"
#include <stdbool.h>
#include <glib.h>
-#include <gdbus/gdbus.h>
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
#include "src/adapter.h"
#include "src/device.h"
#include "src/profile.h"
#include <errno.h>
#include <fcntl.h>
-#include <gdbus/gdbus.h>
#include <stdint.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
-#include <glib.h>
-#include <bluetooth/bluetooth.h>
+#include <glib.h>
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
#include "src/dbus-common.h"
#include "src/adapter.h"
#include "src/device.h"
#include <glib.h>
#include <dbus/dbus.h>
-#include <gdbus/gdbus.h>
-
-#include "src/log.h"
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
+#include "src/log.h"
#include "src/dbus-common.h"
#include "src/adapter.h"
#include "src/device.h"
#endif
#include <errno.h>
-#include <gdbus/gdbus.h>
+
+#include "gdbus/gdbus.h"
+
#include "src/plugin.h"
#include "manager.h"
#include <stdbool.h>
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+
#include "src/log.h"
#include "src/adapter.h"
#include "src/device.h"
#endif
#include <glib.h>
-#include <gdbus/gdbus.h>
#include <stdint.h>
+#include "gdbus/gdbus.h"
+
#include "src/dbus-common.h"
#include "src/error.h"
#include "src/log.h"
+
#include "sap.h"
#define SAP_DUMMY_IFACE "org.bluez.SimAccessTest1"
#include <errno.h>
#include <glib.h>
-#include <bluetooth/sdp.h>
-#include <bluetooth/sdp_lib.h>
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
#include "btio/btio.h"
#include "src/adapter.h"
#include "src/sdpd.h"
#include "src/error.h"
#include "src/dbus-common.h"
#include "src/shared/util.h"
+
#include "sap.h"
#include "server.h"
ba2str(&dst, dstaddr);
ret = btd_request_authorization(&src, &dst, SAP_UUID, connect_auth_cb,
- server, 0);
+ server);
if (ret == 0) {
error("Authorization failure");
sap_server_remove_conn(server);
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
-#include <gdbus/gdbus.h>
-
int sap_server_register(struct btd_adapter *adapter);
void sap_server_unregister(const char *path);
#include <stdbool.h>
#include <errno.h>
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
#include "lib/uuid.h"
+
#include "src/log.h"
#include "src/plugin.h"
#include "src/adapter.h"
#include <stdbool.h>
#include <errno.h>
-#include <gdbus/gdbus.h>
-
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
#include "src/plugin.h"
#include "src/dbus-common.h"
#include "src/adapter.h"
#include <config.h>
#endif
-#include <glib.h>
#include <time.h>
#include <errno.h>
+#include <string.h>
#include <stdbool.h>
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
#include "src/adapter.h"
#include "src/device.h"
#include "src/profile.h"
#include "src/plugin.h"
-
-#include "lib/uuid.h"
#include "attrib/gattrib.h"
#include "attrib/att.h"
#include "attrib/gatt.h"
#include <sys/stat.h>
#include <dirent.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/sdp.h>
-#include <bluetooth/sdp_lib.h>
-
#include <glib.h>
#include <dbus/dbus.h>
-#include <gdbus/gdbus.h>
+
+#include "bluetooth/bluetooth.h"
+#include "bluetooth/hci.h"
+#include "bluetooth/hci_lib.h"
+#include "bluetooth/sdp.h"
+#include "bluetooth/sdp_lib.h"
+#include "lib/uuid.h"
+#include "lib/mgmt.h"
+
+#include "gdbus/gdbus.h"
#include "log.h"
#include "textfile.h"
-#include "lib/uuid.h"
-#include "lib/mgmt.h"
#include "src/shared/mgmt.h"
#include "src/shared/util.h"
+#include "src/shared/queue.h"
+#include "src/shared/att.h"
+#include "src/shared/gatt-db.h"
#include "hcid.h"
#include "sdpd.h"
#include "attrib/att.h"
#include "attrib/gatt.h"
#include "attrib-server.h"
+#include "gatt-database.h"
#include "eir.h"
-#ifdef BLUEZ5_GATT_CLIENT
-#include "src/shared/gatt-client.h"
-#include "src/shared/att-types.h"
-#include <bluetooth/l2cap.h>
+
+#ifdef __TIZEN_PATCH__
+#include "adapter_le_vsc_features.h"
#endif
#define ADAPTER_INTERFACE "org.bluez.Adapter1"
#define BONDING_TIMEOUT (2 * 60)
#ifdef __TIZEN_PATCH__
#define check_address(address) bachk(address)
-#define ADV_DATA_LENGTH_MAX 28
+#define ADV_DATA_MAX_LENGTH 31
#define SCAN_RESPONSE_DATA_LENGTH_MAX 31
#define MANUFACTURER_DATA_LENGTH_MAX 28
#define PRIVATE_ADDR_TIMEOUT (15 * 60)
static uint8_t mgmt_version = 0;
static uint8_t mgmt_revision = 0;
+#if 0 // Not used
#ifdef __TIZEN_PATCH__
static DBusMessage *write_sec_conn_host_support(DBusConnection *conn,
DBusMessage *msg, void *user_data);
#endif /* __TIZEN_PATCH__ */
+#endif
+
static GSList *adapter_drivers = NULL;
static GSList *disconnect_list = NULL;
struct btd_device *device;
struct btd_adapter *adapter;
struct agent *agent; /* NULL for queued auths */
- int fd;
};
struct btd_adapter_pin_cb_iter {
/* When the iterator reaches the end, it is NULL and attempt is 0 */
};
+#ifdef __TIZEN_PATCH__
+struct adv_info {
+ int slot_id; /* Reservied slot id is 0 (Single adv) */
+ bool status; /* Advertising status */
+};
+#endif
+
struct btd_adapter {
int ref_count;
bdaddr_t bdaddr; /* controller Bluetooth address */
#ifdef __TIZEN_PATCH__
bdaddr_t rpaddr; /* controller RPA */
-#ifdef BLUEZ5_GATT_CLIENT
- struct client *gatt_client;
-#endif
#endif
uint32_t dev_class; /* controller class of device */
char *name; /* controller device name */
bool le_privacy_enabled; /* whether LE Privacy feature enabled */
char local_irk[MGMT_IRK_SIZE]; /* adapter local IRK */
uint8_t disc_type;
+ bool ipsp_intialized; /* Ipsp Initialization state */
#endif
bool discovering; /* discovering property state */
struct btd_device *connect_le; /* LE device waiting to be connected */
sdp_list_t *services; /* Services associated to adapter */
+ struct btd_gatt_database *database;
+
gboolean initialized;
#ifdef __TIZEN_PATCH__
+ GSList *adv_list; /* List of advertising instance */
bool advertising; /* Advertising active */
gchar *version; /* Bluetooth Version */
+#if 0 // Not used
bool secure_connection; /* Secure Connection active*/
uint16_t auth_payload_timeout; /* Authenticated payload timeout value*/
+
bool set_new_rpa; /* RPA to be set */
bool rpa_is_set; /* RPA is set */
+#endif
+
+ uint8_t adv_tx_power;
gboolean le_discovering; /* LE Discovery active */
GSList *le_discovery_list; /* list of LE discovery clients */
unsigned int pair_device_id;
guint pair_device_timeout;
+ unsigned int db_id; /* Service event handler for GATT db */
#ifdef __TIZEN_PATCH__
guint private_addr_timeout;
#endif
bool is_default; /* true if adapter is default one */
};
-#ifdef BLUEZ5_GATT_CLIENT
-struct client {
- int fd;
- struct bt_gatt_client *gatt;
-};
-#endif
#ifdef __TIZEN_PATCH__
enum {
ENABLE_PRIVACY,
GEN_IRK_THEN_ENABLE_PRIVACY
};
+
+enum {
+ DEINIT_6LOWPAN,
+ INIT_6LOWPAN
+};
#endif
static struct btd_adapter *btd_adapter_lookup(uint16_t index)
{
struct btd_adapter *adapter = user_data;
const struct mgmt_cod *rp = param;
- uint8_t appearance[3];
uint32_t dev_class;
if (length < sizeof(*rp)) {
g_dbus_emit_property_changed(dbus_conn, adapter->path,
ADAPTER_INTERFACE, "Class");
-
- appearance[0] = rp->val[0];
- appearance[1] = rp->val[1] & 0x1f; /* removes service class */
- appearance[2] = rp->val[2];
-
- attrib_gap_set(adapter, GATT_CHARAC_APPEARANCE, appearance, 2);
}
static void set_dev_class_complete(uint8_t status, uint16_t length,
static bool set_privacy(struct btd_adapter *adapter, bool privacy);
#endif
+#ifdef __TIZEN_PATCH__
+static int compare_slot(gconstpointer a, gconstpointer b)
+{
+ const struct adv_info *adv = a;
+ const int id = *(int*)b;
+
+ return (adv->slot_id == id ? 0 : -1);
+}
+
+static struct adv_info *find_advertiser(struct btd_adapter *adapter,
+ int slot_id)
+{
+ GSList *list;
+
+ list = g_slist_find_custom(adapter->adv_list, &slot_id,
+ compare_slot);
+ if (list)
+ return list->data;
+
+ return NULL;
+}
+
+static void create_advertiser(struct btd_adapter *adapter,
+ int slot_id)
+{
+ struct adv_info *adv;
+
+ if (!adapter)
+ return;
+
+ if (find_advertiser(adapter, slot_id) != NULL) {
+ error("Aleady existed [%d]", slot_id);
+ return;
+ }
+
+ DBG("Create adv slot id : %d", slot_id);
+
+ adv = g_new0(struct adv_info, 1);
+ if (adv == NULL)
+ return;
+
+ adv->slot_id = slot_id;
+
+ adapter->adv_list= g_slist_append(adapter->adv_list, adv);
+ return;
+}
+
+#ifndef __TIZEN_PATCH__
+/* There is no caller of this function */
+static void destroy_advertiser(struct btd_adapter *adapter,
+ int slot_id)
+{
+ struct adv_info *adv;
+
+ if (!adapter)
+ return;
+
+ adv = find_advertiser(adapter, slot_id);
+ if (!adv) {
+ DBG("Unable to find advertiser [%d]", slot_id);
+ return;
+ }
+
+ adapter->adv_list = g_slist_remove(adapter->adv_list,
+ adv);
+}
+#endif
+
+static void advertising_state_changed(struct btd_adapter *adapter,
+ int slot_id, bool enabled)
+{
+ struct adv_info *adv;
+ int id = slot_id;
+ int state = enabled;
+
+ if (!adapter)
+ return;
+
+ adv = find_advertiser(adapter, slot_id);
+ if (!adv) {
+ DBG("Unable to find advertiser [%d]", slot_id);
+ return;
+ }
+
+ adv->status = enabled;
+ DBG("slot_id %d, status %d", adv->slot_id, adv->status);
+
+ g_dbus_emit_signal(dbus_conn, adapter->path,
+ ADAPTER_INTERFACE, "AdvertisingEnabled",
+ DBUS_TYPE_INT32, &id,
+ DBUS_TYPE_BOOLEAN, &state,
+ DBUS_TYPE_INVALID);
+}
+
+static void clear_advertiser_cb(gpointer data, gpointer user_data)
+{
+ struct adv_info *adv = data;
+ struct btd_adapter *adapter = user_data;
+
+ if (adv->status)
+ advertising_state_changed(adapter, adv->slot_id, 0);
+}
+
+static void advertiser_cleanup(struct btd_adapter *adapter)
+{
+ if (!adapter->adv_list)
+ return;
+
+ g_slist_foreach(adapter->adv_list, clear_advertiser_cb, adapter);
+ g_slist_free(adapter->adv_list);
+ adapter->adv_list = NULL;
+}
+#endif
+
static void settings_changed(struct btd_adapter *adapter, uint32_t settings)
{
uint32_t changed_mask;
(adapter->advertising)) {
return;
}
+
adapter->advertising = adapter->current_settings & MGMT_SETTING_ADVERTISING;
- g_dbus_emit_property_changed(dbus_conn, adapter->path,
- ADAPTER_INTERFACE, "Advertising");
+ advertising_state_changed(adapter, 0, adapter->advertising);
}
#endif
}
if (mgmt_send(adapter->mgmt, MGMT_OP_CLEAR_DEV_WHITE_LIST,
adapter->dev_id, 0, NULL,
NULL, NULL, NULL) > 0)
-
- return TRUE;
+ return TRUE;
+ else
+ return FALSE;
}
gboolean adapter_add_le_white_list(struct btd_adapter *adapter, struct btd_device *device)
{
struct mgmt_cp_add_dev_white_list cp;
- uint8_t bdaddr_type;
const bdaddr_t *dst;
char device_addr[18];
adapter->discovery_found = NULL;
}
+#ifndef __TIZEN_PATCH__
static gboolean remove_temp_devices(gpointer user_data)
{
struct btd_adapter *adapter = user_data;
return FALSE;
}
+#endif
static void discovery_destroy(void *user_data)
{
}
#ifdef __TIZEN_PATCH__
+static int set_adv_data_flag(uint8_t *adv_data, uint8_t *data, int data_len)
+{
+ adv_data[0] = 2;
+ adv_data[1] = EIR_FLAGS;
+ adv_data[2] = EIR_GEN_DISC;
+
+ memcpy(adv_data + 3, data, data_len);
+ return data_len + 3;
+}
+
+static int set_adv_data_device_name(uint8_t *adv_data, int adv_len, char *name)
+{
+ int ad_type;
+ int ad_len;
+ int i, j;
+ int name_len;
+ uint8_t *data = NULL;
+
+ data = g_memdup(adv_data, adv_len);
+
+ if (!data || !name)
+ return adv_len;
+
+ name_len = strlen(name);
+
+ for (i = 0; i <adv_len ; i++) {
+ ad_len = data[i];
+ ad_type = data[i + 1];
+
+ if (ad_type == EIR_NAME_COMPLETE) {
+ /* Move to last position and update local name */
+ for (j = i; j < adv_len - 2; j++)
+ adv_data[j] = data[j + 2];
+
+ adv_data[j] = name_len + 1;
+ if (name_len > ADV_DATA_MAX_LENGTH - adv_len) {
+ adv_data[j + 1] = EIR_NAME_SHORT;
+ memcpy(adv_data + j + 2, name, ADV_DATA_MAX_LENGTH - adv_len);
+ } else {
+ adv_data[j + 1] = EIR_NAME_COMPLETE;
+ memcpy(adv_data + j + 2, name, name_len);
+ }
+
+ g_free(data);
+ return adv_len + name_len;
+ } else {
+ memcpy(adv_data + i, &data[i], ad_len + 1);
+ i = i + data[i];
+ }
+ }
+
+ g_free(data);
+ return adv_len;
+}
+
+static int set_adv_data_tx_power(uint8_t *adv_data, int adv_len, int8_t tx_power)
+{
+ int ad_type;
+ int ad_len;
+ int i, j;
+ uint8_t *data = NULL;
+
+ data = g_memdup(adv_data, adv_len);
+ if (!data)
+ return adv_len;
+
+ for (i = 0; i <adv_len ; i++) {
+ ad_len = data[i];
+ ad_type = data[i + 1];
+
+ if (ad_type == EIR_TX_POWER) {
+ adv_data[i] = 2;
+ adv_data[i + 1] = EIR_TX_POWER;
+ adv_data[i + 2] = tx_power;
+
+ for(j = i + 2; j < adv_len; j++)
+ adv_data[j + 1] = data[j];
+
+ g_free(data);
+ return adv_len + 1;
+ } else {
+ memcpy(adv_data + i, &data[i], ad_len + 1);
+ i = i + data[i];
+ }
+ }
+
+ g_free(data);
+ return adv_len;
+}
+
+static gboolean adapter_le_set_missed_adv_data(uint8_t *p_data, uint8_t data_len,
+ gboolean is_scan_rsp, char *adapter_name, int8_t tx_power, uint8_t **adv_data, int *adv_len)
+{
+ uint8_t *data;
+ int len;
+
+ data = g_malloc0(ADV_DATA_MAX_LENGTH);
+ /* Fix : NULL_RETURNS */
+ if (data == NULL) {
+ return FALSE;
+ }
+ memcpy(data, p_data, data_len);
+ len = data_len;
+
+ /* In case multi advertising, need to update the below AD type
+ since it handled into kernel */
+ if (!is_scan_rsp) {
+ len = set_adv_data_flag(data, p_data, data_len);
+ }
+
+ len = set_adv_data_tx_power(data, len, tx_power);
+
+ len = set_adv_data_device_name(data, len, adapter_name);
+
+ *adv_data = data;
+ *adv_len = len;
+ return TRUE;
+}
+
static DBusMessage *adapter_start_custom_discovery(DBusConnection *conn,
DBusMessage *msg, void *user_data)
{
struct btd_adapter *adapter = data;
dbus_bool_t err;
dbus_bool_t enable = FALSE;
+ dbus_int32_t slot_id;
DBG("adapter_set_advertising");
return btd_error_not_ready(msg);
if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_BOOLEAN, &enable,
+ DBUS_TYPE_INT32, &slot_id,
DBUS_TYPE_INVALID))
return btd_error_invalid_args(msg);
- err = set_mode(adapter, MGMT_OP_SET_ADVERTISING, enable);
+ if (adapter_le_is_supported_multi_advertising() && slot_id > 0)
+ err = adapter_le_enable_multi_adv(enable, slot_id);
+ else
+ err = set_mode(adapter, MGMT_OP_SET_ADVERTISING, enable);
+
if (!err)
return btd_error_failed(msg, "Set Advertising failed");
+ if (enable)
+ create_advertiser(adapter, slot_id);
+
+ if (err && slot_id > 0)
+ advertising_state_changed(adapter, slot_id, enable);
+
return dbus_message_new_method_return(msg);
}
dbus_uint32_t interval_max;
dbus_uint32_t filter_policy;
dbus_uint32_t type;
+ dbus_int32_t slot_id;
+ gboolean ret;
DBG("Set customised advertising parameters");
DBUS_TYPE_UINT32, &interval_max,
DBUS_TYPE_UINT32, &filter_policy,
DBUS_TYPE_UINT32, &type,
+ DBUS_TYPE_INT32, &slot_id,
DBUS_TYPE_INVALID))
return btd_error_invalid_args(msg);
if (type > 0x04)
return btd_error_invalid_args(msg);
- cp.interval_max = interval_max;
- cp.interval_min = interval_min;
- cp.filter_policy = filter_policy;
- cp.type = type;
+ if (adapter_le_is_supported_multi_advertising() && slot_id > 0) {
+ adapter_le_adv_inst_info_t *p_inst;
+ adapter_le_adv_param_t *p_params;
+
+ p_inst = malloc(sizeof(adapter_le_adv_inst_info_t));
+ p_params = malloc(sizeof(adapter_le_adv_param_t));
+ memset(p_inst, 0, sizeof(adapter_le_adv_inst_info_t));
+ memset(p_params, 0, sizeof(adapter_le_adv_param_t));
+ p_inst->inst_id = slot_id;
+ p_params->adv_int_min = interval_min;
+ p_params->adv_int_max = interval_max;
+ p_params->adv_type = type;
+ p_params->channel_map = 0x07; /* fixed channel :: will be used all */
+ p_params->adv_filter_policy = filter_policy;
+ p_params->tx_power = BLE_ADV_TX_POWER_MID; /* TODO:need to optimize */
+ p_inst->bdaddr_type = 0x00;
+ bacpy(&p_inst->bdaddr, &adapter->bdaddr);
+
+ ret = adapter_le_set_multi_adv_params(p_inst, p_params);
+
+ free(p_inst);
+ free(p_params);
+
+ if (ret)
+ return dbus_message_new_method_return(msg);
+ else
+ return btd_error_failed(msg, "set advertising param failed");
+ } else {
+ cp.interval_max = interval_max;
+ cp.interval_min = interval_min;
+ cp.filter_policy = filter_policy;
+ cp.type = type;
- if (mgmt_send(adapter->mgmt, MGMT_OP_SET_ADVERTISING_PARAMS,
- adapter->dev_id, sizeof(cp), &cp,
- NULL, NULL, NULL) > 0)
- return dbus_message_new_method_return(msg);
+ if (mgmt_send(adapter->mgmt, MGMT_OP_SET_ADVERTISING_PARAMS,
+ adapter->dev_id, sizeof(cp), &cp,
+ NULL, NULL, NULL) > 0)
+ return dbus_message_new_method_return(msg);
- return btd_error_failed(msg, "set advertising param failed");
+ return btd_error_failed(msg, "set advertising param failed");
+ }
}
static DBusMessage *adapter_set_advertising_data(DBusConnection *conn,
struct mgmt_cp_set_advertising_data cp;
uint8_t *value;
int32_t len = 0;
+ dbus_int32_t slot_id;
+ uint8_t *adv_data = NULL;
+ int adv_len = 0;
DBG("Set advertising data");
if (!dbus_message_get_args(msg, NULL,
DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &value, &len,
+ DBUS_TYPE_INT32, &slot_id,
DBUS_TYPE_INVALID))
return btd_error_invalid_args(msg);
- if (len > ADV_DATA_LENGTH_MAX)
+ if (len > ADV_DATA_MAX_LENGTH - 3)
return btd_error_invalid_args(msg);
- memcpy(&cp, value, len);
+ if (adapter_le_set_missed_adv_data(value, len, FALSE,
+ adapter->name, adapter->adv_tx_power, &adv_data, &adv_len) != TRUE)
+ return btd_error_failed(msg, "set advertising data failed");
- if (mgmt_send(adapter->mgmt, MGMT_OP_SET_ADVERTISING_DATA,
- adapter->dev_id, len,
- &cp, NULL, NULL, NULL) > 0)
- return dbus_message_new_method_return(msg);
+ if (adapter_le_is_supported_multi_advertising() && slot_id > 0) {
+ if (adapter_le_set_multi_adv_data(slot_id, FALSE, adv_len, adv_data)) {
+ g_free(adv_data);
+ return dbus_message_new_method_return(msg);
+ } else {
+ g_free(adv_data);
+ return btd_error_failed(msg, "set advertising data failed");
+ }
+ } else {
+ memcpy(&cp, adv_data, adv_len);
+
+ if (mgmt_send(adapter->mgmt, MGMT_OP_SET_ADVERTISING_DATA,
+ adapter->dev_id, adv_len,
+ &cp, NULL, NULL, NULL) > 0) {
+ g_free(adv_data);
+ return dbus_message_new_method_return(msg);
+ }
+
+ g_free(adv_data);
+ return btd_error_failed(msg, "set advertising data failed");
+ }
+}
+
+static DBusMessage *adapter_le_scan_filter_param_setup(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct btd_adapter *adapter = data;
+ dbus_bool_t err;
+ dbus_int32_t client_if, action, filt_index;
+ dbus_int32_t feat_seln, list_logic_type, filt_logic_type;
+ dbus_int32_t rssi_high_thres, rssi_low_thres, dely_mode;
+ dbus_int32_t found_timeout, lost_timeout, found_timeout_cnt;
+ adapter_le_scan_filter_param_t params;
+
+ DBG("adapter_le_scan_filter_param_setup");
+
+ if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+ return btd_error_not_ready(msg);
+
+ if (adapter_le_get_scan_filter_size() == 0)
+ return btd_error_not_supported(msg);
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_INT32, &client_if,
+ DBUS_TYPE_INT32, &action,
+ DBUS_TYPE_INT32, &filt_index,
+ DBUS_TYPE_INT32, &feat_seln,
+ DBUS_TYPE_INT32, &list_logic_type,
+ DBUS_TYPE_INT32, &filt_logic_type,
+ DBUS_TYPE_INT32, &rssi_high_thres,
+ DBUS_TYPE_INT32, &rssi_low_thres,
+ DBUS_TYPE_INT32, &dely_mode,
+ DBUS_TYPE_INT32, &found_timeout,
+ DBUS_TYPE_INT32, &lost_timeout,
+ DBUS_TYPE_INT32, &found_timeout_cnt,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ memset(¶ms, 0, sizeof(params));
+
+ params.action = action;
+ params.index = filt_index;
+ params.feature = feat_seln;
+ params.filter_logic_type = filt_logic_type;
+ params.list_logic_type = list_logic_type;
+ params.delivery_mode = dely_mode;
+ params.rssi_high_threshold = rssi_high_thres;
+
+ if (params.delivery_mode == ON_FOUND) {
+ params.rssi_low_threshold = rssi_low_thres;
+ params.onfound_timeout = found_timeout;
+ params.onfound_timeout_cnt = found_timeout_cnt;
+ params.onlost_timeout = lost_timeout;
+ }
+
+ err = adapter_le_set_scan_filter_params(¶ms);
+
+ if (!err)
+ return btd_error_failed(msg, "Failed to scan filter param setup");
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *adapter_le_scan_filter_add_remove(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct btd_adapter *adapter = data;
+ dbus_bool_t err;
+ dbus_int32_t client_if, action, filt_type, filt_index;
+ dbus_int32_t company_id, company_id_mask;
+ gchar *string;
+ dbus_uint32_t address_type;
+ uint8_t *p_uuid, *p_uuid_mask, *p_data, *p_mask;
+ int32_t uuid_len = 0, uuid_mask_len = 0, data_len = 0, mask_len = 0;
+
+ DBG("adapter_le_scan_filter_add_remove");
- return btd_error_failed(msg, "set advertising data failed");
+ if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+ return btd_error_not_ready(msg);
+
+ if (adapter_le_get_scan_filter_size() == 0)
+ return btd_error_not_supported(msg);
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_INT32, &client_if,
+ DBUS_TYPE_INT32, &action,
+ DBUS_TYPE_INT32, &filt_type,
+ DBUS_TYPE_INT32, &filt_index,
+ DBUS_TYPE_INT32, &company_id,
+ DBUS_TYPE_INT32, &company_id_mask,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &p_uuid, &uuid_len,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &p_uuid_mask, &uuid_mask_len,
+ DBUS_TYPE_STRING, &string,
+ DBUS_TYPE_UINT32, &address_type,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &p_data, &data_len,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &p_mask, &mask_len,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ err = adapter_le_set_scan_filter_data(client_if, action, filt_type,
+ filt_index, company_id, company_id_mask,
+ uuid_len, p_uuid, uuid_mask_len, p_uuid_mask,
+ string, address_type, data_len, p_data, mask_len, p_mask);
+
+ if (!err)
+ return btd_error_failed(msg, "Failed to add/remove filter");
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *adapter_le_scan_filter_clear(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct btd_adapter *adapter = data;
+ dbus_bool_t err;
+ dbus_int32_t client_if;
+ dbus_int32_t filt_index;
+
+ DBG("adapter_le_scan_filter_clear");
+
+ if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+ return btd_error_not_ready(msg);
+
+ if (adapter_le_get_scan_filter_size() == 0)
+ return btd_error_not_supported(msg);
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_INT32, &client_if,
+ DBUS_TYPE_INT32, &filt_index,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ err = adapter_le_clear_scan_filter_data(client_if, filt_index);
+
+ if (!err)
+ return btd_error_failed(msg, "Failed to clear filter");
+
+ return dbus_message_new_method_return(msg);
+}
+
+
+static DBusMessage *adapter_le_scan_filter_enable(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct btd_adapter *adapter = data;
+ dbus_bool_t err;
+ dbus_bool_t enable = FALSE;
+ dbus_int32_t client_if;
+
+ DBG("adapter_le_scan_filter_enable");
+
+ if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+ return btd_error_not_ready(msg);
+
+ if (adapter_le_get_scan_filter_size() == 0)
+ return btd_error_not_supported(msg);
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_INT32, &client_if,
+ DBUS_TYPE_BOOLEAN, &enable,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ err = adapter_le_enable_scan_filtering(enable);
+
+ if (!err)
+ return btd_error_failed(msg, "Failed to enable scan filtering");
+
+ return dbus_message_new_method_return(msg);
}
static DBusMessage *adapter_le_set_scan_params(DBusConnection *conn,
struct mgmt_cp_set_scan_rsp_data cp;
uint8_t *value;
int32_t len = 0;
+ dbus_int32_t slot_id;
+ uint8_t *adv_data = NULL;
+ int adv_len = 0;
DBG("Set scan response data");
if (!dbus_message_get_args(msg, NULL,
DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &value, &len,
+ DBUS_TYPE_INT32, &slot_id,
DBUS_TYPE_INVALID))
return btd_error_invalid_args(msg);
if (len > SCAN_RESPONSE_DATA_LENGTH_MAX)
return btd_error_invalid_args(msg);
- memcpy(&cp, value, len);
+ if (adapter_le_set_missed_adv_data(value, len, TRUE,
+ adapter->name, adapter->adv_tx_power, &adv_data, &adv_len) != TRUE)
+ return btd_error_failed(msg, "set advertising data failed");
- if (mgmt_send(adapter->mgmt, MGMT_OP_SET_SCAN_RSP_DATA,
- adapter->dev_id, len, &cp,
- NULL, NULL, NULL) > 0)
- return dbus_message_new_method_return(msg);
+ if (adapter_le_is_supported_multi_advertising() && slot_id > 0) {
+ if (adapter_le_set_multi_adv_data(slot_id, TRUE, adv_len, (uint8_t *)adv_data)) {
+ g_free(adv_data);
+ return dbus_message_new_method_return(msg);
+ } else {
+ g_free(adv_data);
+ return btd_error_failed(msg, "set advertising data failed");
+ }
+ } else {
+ memcpy(&cp, adv_data, adv_len);
+
+ if (mgmt_send(adapter->mgmt, MGMT_OP_SET_SCAN_RSP_DATA,
+ adapter->dev_id, adv_len, &cp,
+ NULL, NULL, NULL) > 0) {
+ g_free(adv_data);
+ return dbus_message_new_method_return(msg);
+ }
- return btd_error_failed(msg, "set scan reponse data failed");
+ g_free(adv_data);
+ return btd_error_failed(msg, "set scan reponse data failed");
+ }
}
static DBusMessage *adapter_add_device_white_list(DBusConnection *conn,
return dbus_message_new_method_return(msg);
}
+#if 0 // Not used
static void read_sec_conn_host_support_complete(uint8_t status, uint16_t length,
const void *param, void *user_data)
{
NULL, NULL);
return dbus_message_new_method_return(msg);
}
+#endif
static DBusMessage *adapter_enable_rssi(DBusConnection *conn,
DBusMessage *msg, void *data)
return btd_error_failed(msg, "Get Raw RSSI Failed");
}
+static void get_adv_tx_power_complete(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ const struct mgmt_rp_get_adv_tx_power *rp = param;
+
+ if (status != MGMT_STATUS_SUCCESS) {
+ error("Failed to get adv tx power: %s (0x%02x)",
+ mgmt_errstr(status), status);
+ return;
+ }
+
+ if (length < sizeof(*rp)) {
+ error("Wrong size of get adv tx power");
+ return;
+ }
+
+ adapter->adv_tx_power = rp->adv_tx_power;
+ return;
+}
+
+static void adapter_get_adv_tx_power(void *data)
+{
+ struct btd_adapter *adapter = data;
+
+ mgmt_send(adapter->mgmt, MGMT_OP_GET_ADV_TX_POWER,
+ adapter->dev_id, 0, NULL,
+ get_adv_tx_power_complete, adapter, NULL);
+ return;
+}
+
#ifdef __BROADCOM_PATCH__
static DBusMessage *set_wbs_parameters(DBusConnection *conn,
DBusMessage *msg, void *data)
return;
}
+#ifdef __TIZEN_PATCH__
+ if (adapter_le_read_ble_feature_info())
+ g_dbus_emit_property_changed(dbus_conn, adapter->path,
+ ADAPTER_INTERFACE, "SupportedLEFeatures");
+
+ adapter_get_adv_tx_power(adapter);
+#endif
+
property_set_mode(adapter, MGMT_SETTING_POWERED, iter, id);
}
return TRUE;
}
-#ifdef __TIZEN_PATCH__
-#ifdef BLUEZ5_GATT_CLIENT
-static void print_services(struct client *cli)
+static void add_gatt_uuid(struct gatt_db_attribute *attrib, void *user_data)
{
- DBG("+");
- struct bt_gatt_service_iter iter_srv;
- const bt_gatt_service_t *service;
- struct bt_gatt_characteristic_iter iter_chr;
- const bt_gatt_characteristic_t *chrc;
+ GHashTable *uuids = user_data;
+ bt_uuid_t uuid, u128;
+ char uuidstr[MAX_LEN_UUID_STR + 1];
- if (!bt_gatt_service_iter_init(&iter_srv, cli->gatt)) {
- DBG("Failed to initialize service iterator");
+ if (!gatt_db_service_get_active(attrib))
return;
- }
- while (bt_gatt_service_iter_next(&iter_srv, &service)) {
- if (!bt_gatt_characteristic_iter_init(&iter_chr, service)) {
- DBG("Failed to initialize characteristic iterator");
- return;
- }
- DBG("service - start: 0x%04x, end: 0x%04x",
- service->start_handle, service->end_handle);
-
- while (bt_gatt_characteristic_iter_next(&iter_chr, &chrc)) {
- DBG("characterustuc - start: 0x%04x, end: 0x%04x, value: 0x%04x, props: 0x%02x",
- chrc->start_handle, chrc->end_handle, chrc->value_handle, chrc->properties);
- }
- }
-}
-
-static void gatt_discover_services_cb(bool success, uint8_t att_ecode, void *user_data)
-{
- struct btd_adapter *adapter = user_data;
- struct client *cli = adapter->gatt_client;
-
- DBG("LE Discover services\n");
-
- print_services(cli);
-}
-
-static void gatt_connected_cb(bool success, uint8_t att_ecode, void *user_data)
-{
- struct btd_device *device = user_data;
- struct client *cli;
- struct btd_adapter *adapter;
-
- if (!device) {
- DBG("Unable to get device object");
- return;
- }
- adapter = device_get_adapter(device);
- cli = adapter->gatt_client;
-
- if (!success) {
- DBG("GATT discovery procedures failed - error code: 0x%02x\n",
- att_ecode);
+ if (!gatt_db_attribute_get_service_uuid(attrib, &uuid))
return;
- }
-
- device_set_gatt_connected(device, TRUE);
- DBG("LE Device connected\n");
- print_services(cli);
+ bt_uuid_to_uuid128(&uuid, &u128);
+ bt_uuid_to_string(&u128, uuidstr, sizeof(uuidstr));
- fflush(stdout);
+ g_hash_table_add(uuids, strdup(uuidstr));
}
-static void gatt_disconnected_cb(void *user_data)
+static void iter_append_uuid(gpointer key, gpointer value, gpointer user_data)
{
- struct btd_device *device = user_data;
-
- if (!device) {
- DBG("Unable to get device object");
- return;
- }
-
- device_set_gatt_connected(device, FALSE);
- DBG("LE Device disconnected\n");
-}
-
-static int le_att_connect(bdaddr_t *src, bdaddr_t *dst, uint8_t dst_type,
- int sec)
-{
- DBG("+");
- int sock;
- struct sockaddr_l2 srcaddr, dstaddr;
- struct bt_security btsec;
-
- sock = socket(PF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
- if (sock < 0) {
- error("Failed to create L2CAP socket");
- return -1;
- }
-
- /* Set up source address */
- memset(&srcaddr, 0, sizeof(srcaddr));
- srcaddr.l2_family = AF_BLUETOOTH;
- srcaddr.l2_cid = htobs(ATT_CID);
- srcaddr.l2_bdaddr_type = 0;
- bacpy(&srcaddr.l2_bdaddr, src);
-
- if (bind(sock, (struct sockaddr *)&srcaddr, sizeof(srcaddr)) < 0) {
- error("Failed to bind L2CAP socket");
- close(sock);
- return -1;
- }
-
- /* Set the security level */
- memset(&btsec, 0, sizeof(btsec));
- btsec.level = sec;
- if (setsockopt(sock, SOL_BLUETOOTH, BT_SECURITY, &btsec,
- sizeof(btsec)) != 0) {
- error("Failed to set L2CAP security level\n");
- close(sock);
- return -1;
- }
-
- /* Set up destination address */
- memset(&dstaddr, 0, sizeof(dstaddr));
- dstaddr.l2_family = AF_BLUETOOTH;
- dstaddr.l2_cid = htobs(ATT_CID);
- dstaddr.l2_bdaddr_type = dst_type;
- bacpy(&dstaddr.l2_bdaddr, dst);
+ DBusMessageIter *iter = user_data;
+ const char *uuid = key;
- DBG("Connecting to device...");
- fflush(stdout);
-
- if (connect(sock, (struct sockaddr *) &dstaddr, sizeof(dstaddr)) < 0) {
- error("Failed to connect");
- close(sock);
- return -1;
- }
-
- DBG(" Done\n");
-
- return sock;
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid);
}
-static struct client *create_client(int fd, struct btd_device *device)
-{
- DBG("+");
- struct client *cli;
- struct bt_att *att;
- struct btd_adapter *adapter;
-
- cli = g_new0(struct client, 1);
- if (!cli) {
- error("Failed to allocate memory for client\n");
- return NULL;
- }
-
- att = bt_att_new(fd);
- if (!att) {
- error("Failed to initialze ATT transport layer\n");
- bt_att_unref(att);
- free(cli);
- return NULL;
- }
- if (!bt_att_set_close_on_unref(att, true)) {
- error("Failed to set up ATT transport layer\n");
- bt_att_unref(att);
- free(cli);
- return NULL;
- }
- if (!bt_att_register_disconnect(att, gatt_disconnected_cb, device, NULL)) {
- error("Failed to set ATT disconnect handler\n");
- bt_att_unref(att);
- free(cli);
- return NULL;
- }
-
- cli->fd = fd;
- cli->gatt = bt_gatt_client_new(att, BT_ATT_DEFAULT_LE_MTU); /* mtu value can be adjustable */
-
- if (!cli->gatt) {
- error("Failed to create GATT client\n");
- bt_att_unref(att);
- free(cli);
- return NULL;
- }
-
- adapter = device_get_adapter(device);
- adapter->gatt_client = cli;
-
- bt_gatt_client_set_ready_handler(cli->gatt, gatt_connected_cb, device, NULL);
-
- /* bt_gatt_client already holds a reference */
- bt_att_unref(att);
- return cli;
-}
-#endif /* BLUEZ5_GATT_CLIENT */
+#ifdef __TIZEN_PATCH__
static gboolean property_get_le_discovering(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *user_data)
return TRUE;
}
-static gboolean property_get_advertising(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *user_data)
-{
- struct btd_adapter *adapter = user_data;
- dbus_bool_t advertising = (dbus_bool_t)adapter->advertising;
-
- dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &advertising);
-
- return TRUE;
-}
-
+#if 0 // Not used
static gboolean property_get_secure_connection(const GDBusPropertyTable
*property, DBusMessageIter *iter, void *user_data)
{
return TRUE;
}
+#endif
static gboolean property_get_connectable(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *user_data)
property_set_mode(adapter, MGMT_SETTING_CONNECTABLE, iter, id);
}
-static gboolean property_get_version(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *user_data)
+static gboolean property_get_version(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ const char *str = adapter->version ? : "";
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str);
+
+ return TRUE;
+}
+
+static gboolean property_get_supported_le_features(
+ const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *user_data)
+{
+ const char *str, *val;
+ int value;
+ DBusMessageIter entry;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_STRING_AS_STRING, &entry);
+
+ value = adapter_le_get_max_adv_instance();
+ if (value > 0) {
+ str = g_strdup("adv_inst_max");
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &str);
+
+ val = g_strdup_printf("%d", value);
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &val);
+
+ free((void *)str);
+ free((void *)val);
+ }
+
+ value = adapter_le_is_supported_offloading();
+ if (value > 0) {
+ str = g_strdup("rpa_offloading");
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &str);
+
+ val = g_strdup_printf("%d", value);
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &val);
+
+ free((void *)str);
+ free((void *)val);
+ }
+
+ value = adapter_le_get_scan_filter_size();
+ if (value > 0) {
+ str = g_strdup("max_filter");
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &str);
+
+ val = g_strdup_printf("%d", value);
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &val);
+
+ free((void *)str);
+ free((void *)val);
+ }
+
+ dbus_message_iter_close_container(iter, &entry);
+
+ return TRUE;
+}
+
+static gboolean property_get_ipsp_init_state(
+ const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
{
- struct btd_adapter *adapter = user_data;
- const char *str = adapter->version ? : "";
+ struct btd_adapter *adapter = data;
+ dbus_bool_t ipsp_initialized;
- dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str);
+ DBG("property_get_ipsp_init_state called");
+ if (adapter->ipsp_intialized)
+ ipsp_initialized = TRUE;
+ else
+ ipsp_initialized = FALSE;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN,
+ &ipsp_initialized);
return TRUE;
}
-
#endif
static gboolean property_get_uuids(const GDBusPropertyTable *property,
struct btd_adapter *adapter = user_data;
DBusMessageIter entry;
sdp_list_t *l;
+ struct gatt_db *db;
+ GHashTable *uuids;
- dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
- DBUS_TYPE_STRING_AS_STRING, &entry);
+ uuids = g_hash_table_new_full(g_str_hash, g_str_equal, free, NULL);
+ if (!uuids)
+ return FALSE;
+ /* SDP records */
for (l = adapter->services; l != NULL; l = l->next) {
sdp_record_t *rec = l->data;
char *uuid;
if (uuid == NULL)
continue;
- dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING,
- &uuid);
- free(uuid);
+ g_hash_table_add(uuids, uuid);
}
+ /* GATT services */
+ db = btd_gatt_database_get_db(adapter->database);
+ if (db)
+ gatt_db_foreach_service(db, NULL, add_gatt_uuid, uuids);
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_STRING_AS_STRING, &entry);
+ g_hash_table_foreach(uuids, iter_append_uuid, &entry);
dbus_message_iter_close_container(iter, &entry);
+ g_hash_table_destroy(uuids);
+
return TRUE;
}
return reply;
}
-#ifdef BLUEZ5_GATT_CLIENT
-static DBusMessage *connect_le(DBusConnection *conn, DBusMessage *msg,
- void *user_data)
+static void adapter_set_ipsp_init_state(struct btd_adapter *adapter, gboolean initialized)
{
- struct btd_adapter *adapter = user_data;
- struct btd_device *device;
- struct client *cli;
- int fd;
- int sec_level = BT_SECURITY_LOW;
- const gchar *address;
- bdaddr_t addr;
+ if (adapter->ipsp_intialized == initialized)
+ return;
- if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &address,
- DBUS_TYPE_INVALID) == FALSE)
- return btd_error_invalid_args(msg);
+ adapter->ipsp_intialized = initialized;
- if (check_address(address) < 0)
- return btd_error_invalid_args(msg);
+ DBG("Set Ipsp init state for adapter %s", adapter->path);
- DBG("%s", address);
- str2ba(address, &addr);
+ g_dbus_emit_property_changed(dbus_conn, adapter->path,
+ ADAPTER_INTERFACE, "IpspInitStateChanged");
+}
- cli = g_new0(struct client, 1);
+static void deinitialize_6lowpan_complete(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ bool initialized = FALSE;
- device = btd_adapter_get_device(adapter, &addr,
- BDADDR_LE_PUBLIC);
+ if (status != MGMT_STATUS_SUCCESS)
+ error("De-Initialize BT 6lowpan failed for hci%u: %s (0x%02x)",
+ adapter->dev_id, mgmt_errstr(status), status);
+ else {
+ adapter_set_ipsp_init_state(adapter, initialized);
+ DBG("De-Initialize BT 6lowpan successfully for hci%u",
+ adapter->dev_id);
+ }
+}
- if (!device) {
- DBG("Unable to get device object");
- return btd_error_not_available(msg);
- } else {
- if (device_get_gatt_connected(device))
- return btd_error_already_connected(msg);
+static bool deinitialize_6lowpan(struct btd_adapter *adapter)
+{
+ struct mgmt_cp_enable_6lowpan cp;
- if (device_is_paired(device, BDADDR_LE_PUBLIC))
- sec_level = BT_SECURITY_MEDIUM;
- }
+ memset(&cp, 0, sizeof(cp));
- fd = le_att_connect(btd_adapter_get_address(adapter),
- &addr, BDADDR_LE_PUBLIC, sec_level);
+ cp.enable_6lowpan = DEINIT_6LOWPAN;
+ if (mgmt_send(adapter->mgmt, MGMT_OP_ENABLE_6LOWPAN,
+ adapter->dev_id, sizeof(cp), &cp,
+ deinitialize_6lowpan_complete, adapter, NULL) > 0)
+ return true;
- if (fd < 0)
- return btd_error_not_available(msg);
+ error("Failed to de-initialize BT 6Lowpan for index %u",
+ adapter->dev_id);
+ return false;
+}
- cli = create_client(fd, device);
+static void initialize_6lowpan_complete(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ bool initialized = TRUE;
- if (!cli) {
- close(fd);
- return btd_error_not_available(msg);
+ if (status != MGMT_STATUS_SUCCESS)
+ error("Initialize BT 6lowpan failed for hci%u: %s (0x%02x)",
+ adapter->dev_id, mgmt_errstr(status), status);
+ else {
+ adapter_set_ipsp_init_state(adapter, initialized);
+ DBG("Initialize BT 6lowpan successfully for hci%u",
+ adapter->dev_id);
}
- adapter->gatt_client = cli;
+}
- return dbus_message_new_method_return(msg);
+static bool initialize_6lowpan(struct btd_adapter *adapter)
+{
+ struct mgmt_cp_enable_6lowpan cp;
+
+ memset(&cp, 0, sizeof(cp));
+
+ cp.enable_6lowpan = INIT_6LOWPAN;
+ if (mgmt_send(adapter->mgmt, MGMT_OP_ENABLE_6LOWPAN,
+ adapter->dev_id, sizeof(cp), &cp,
+ initialize_6lowpan_complete, adapter, NULL) > 0)
+ return true;
+
+ error("Failed to initialize BT 6Lowpan for index %u",
+ adapter->dev_id);
+ return false;
}
-static DBusMessage *disconnect_le(DBusConnection *conn,
- DBusMessage *msg, void *user_data)
+static DBusMessage *adapter_initialize_ipsp(DBusConnection *conn,
+ DBusMessage *msg, void *data)
{
- struct btd_adapter *adapter = user_data;
- struct btd_device *device;
- const gchar *address;
- bdaddr_t addr;
+ struct btd_adapter *adapter = data;
+ dbus_bool_t err;
- if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &address,
- DBUS_TYPE_INVALID) == FALSE)
- return btd_error_invalid_args(msg);
+ DBG("Initialize IPSP");
- if (check_address(address) < 0)
- return btd_error_invalid_args(msg);
+ if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+ return btd_error_not_ready(msg);
- DBG("%s", address);
- str2ba(address, &addr);
+ if (adapter->ipsp_intialized)
+ return btd_error_already_exists(msg);
- device = btd_adapter_find_device(adapter, &addr, BDADDR_LE_PUBLIC);
+ /* Register IPSP service as GATT primary service */
+ err = gatt_register_internet_protocol_service(adapter);
- if (!device) {
- DBG("Unable to get device object");
- return btd_error_not_available(msg);
- } else {
- if (!device_get_gatt_connected(device))
- return btd_error_not_connected(msg);
+ if (!err)
+ return btd_error_failed(msg, "Failed to register IPSP service");
- if (btd_device_get_bdaddr_type(device) == BDADDR_BREDR)
- return btd_error_not_supported(msg);
- }
+ /* Enable BT 6lowpan in kernel */
+ err = initialize_6lowpan(adapter);
- DBG("Disconnect LE device");
- if (!disconnect_le_device(device))
- return btd_error_failed(msg, "to disconnect LE device");
+ if (!err)
+ return btd_error_failed(msg, "Failed to initialize BT 6lowpan");
return dbus_message_new_method_return(msg);
}
-static DBusMessage *discover_le_services(DBusConnection *conn,
- DBusMessage *msg, void *user_data)
+static DBusMessage *adapter_deinitialize_ipsp(DBusConnection *conn,
+ DBusMessage *msg, void *data)
{
- DBusMessage *reply;
- dbus_bool_t val;
- struct btd_adapter *adapter = user_data;
- struct btd_device *device;
- const gchar *address;
- bdaddr_t addr;
-
- if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &address,
- DBUS_TYPE_INVALID) == FALSE)
- return btd_error_invalid_args(msg);
-
- if (check_address(address) < 0)
- return btd_error_invalid_args(msg);
-
- DBG("%s", address);
- str2ba(address, &addr);
+ struct btd_adapter *adapter = data;
+ dbus_bool_t err;
- device = btd_adapter_find_device(adapter, &addr, BDADDR_LE_PUBLIC);
+ DBG("De-initialize IPSP");
- reply = dbus_message_new_method_return(msg);
- if (!reply)
- return btd_error_invalid_args(msg);
+ if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+ return btd_error_not_ready(msg);
- if (!device) {
- DBG("Unable to get device object");
- return btd_error_not_available(msg);
- } else {
- if (!device_get_gatt_connected(device))
- return btd_error_not_connected(msg);
+ if (!adapter->ipsp_intialized)
+ return btd_error_not_permitted(msg, "IPSP not initialized");
- if (btd_device_get_bdaddr_type(device) == BDADDR_BREDR)
- return btd_error_not_supported(msg);
- }
+ /* Un-register IPSP service as GATT primary service */
+ err = gatt_unregister_internet_protocol_service(adapter);
- val = bt_gatt_discover_services(adapter->gatt_client->gatt);
+ if (!err)
+ return btd_error_failed(msg, "Failed to un-register IPSP service");
- if(val == TRUE)
- bt_gatt_client_set_ready_handler(adapter->gatt_client->gatt,
- gatt_discover_services_cb, adapter, NULL);
+ /* Disable BT 6lowpan in kernel */
+ err = deinitialize_6lowpan(adapter);
- dbus_message_append_args(reply, DBUS_TYPE_BOOLEAN, &val,
- DBUS_TYPE_INVALID);
+ if (!err)
+ return btd_error_failed(msg, "Failed to deinitialize BT 6lowpan");
- return reply;
+ return dbus_message_new_method_return(msg);
}
-#endif /* BLUEZ5_GATT_CLIENT */
#endif
static DBusMessage *remove_device(DBusConnection *conn,
adapter_start_le_discovery) },
{ GDBUS_ASYNC_METHOD("StopLEDiscovery", NULL, NULL,
adapter_stop_le_discovery) },
+#if 0 // Not used
{ GDBUS_METHOD("EnableSecureConnection",
GDBUS_ARGS({"enable", "b"}), NULL,
write_sec_conn_host_support)},
+
{ GDBUS_METHOD("ReadSecureConnection", NULL,
NULL, read_sec_conn_host_support)},
+#endif
+
{ GDBUS_METHOD("SetAdvertising",
- GDBUS_ARGS({ "enable", "b" }), NULL,
+ GDBUS_ARGS({ "enable", "b" },
+ { "slot_id", "i" }), NULL,
adapter_set_advertising) },
{ GDBUS_METHOD("SetAdvertisingParameters",
GDBUS_ARGS({ "interval_min", "u" },
{ "interval_max", "u" },
{ "filter_policy", "u" },
- { "type", "u" }), NULL,
+ { "type", "u" },
+ { "slot_id", "i" }), NULL,
adapter_set_advertising_params) },
{ GDBUS_METHOD("SetAdvertisingData",
GDBUS_ARGS({ "value", "ay" },
- { "length", "i" }), NULL,
+ { "slot_id", "i" }), NULL,
adapter_set_advertising_data) },
{ GDBUS_METHOD("SetScanParameters",
GDBUS_ARGS({ "type", "u" },
{ "interval", "u" },
{ "window", "u" }), NULL,
adapter_le_set_scan_params) },
+ { GDBUS_ASYNC_METHOD("scan_filter_param_setup",
+ GDBUS_ARGS({ "client_if", "i" }, { "action", "i" },
+ { "filt_index", "i" }, { "feat_seln", "i"},
+ { "list_logic_type", "i" }, { "filt_logic_type", "i"},
+ { "rssi_high_thres", "i" }, { "rssi_low_thres", "i"},
+ { "dely_mode", "i" }, { "found_timeout", "i"},
+ { "lost_timeout", "i" }, { "found_timeout_cnt", "i"}), NULL,
+ adapter_le_scan_filter_param_setup) },
+ { GDBUS_ASYNC_METHOD("scan_filter_add_remove",
+ GDBUS_ARGS({ "client_if", "i" }, { "action", "i" },
+ { "filt_type", "i" }, { "filt_index", "i"},
+ { "company_id", "i" }, { "company_id_mask", "i"},
+ { "p_uuid", "ay" }, { "p_uuid_mask", "ay" },
+ { "string", "s" }, { "address_type", "u" },
+ /*{ "data_len", "i" },*/ { "p_data", "ay" },
+ /*{ "mask_len", "i" },*/ { "p_mask", "ay" }), NULL,
+ adapter_le_scan_filter_add_remove) },
+ { GDBUS_ASYNC_METHOD("scan_filter_clear",
+ GDBUS_ARGS({ "client_if", "i" }, { "filt_index", "i" }), NULL,
+ adapter_le_scan_filter_clear) },
+ { GDBUS_ASYNC_METHOD("scan_filter_enable",
+ GDBUS_ARGS({ "client_if", "i" }, { "enable", "b" }), NULL,
+ adapter_le_scan_filter_enable) },
+ { GDBUS_ASYNC_METHOD("InitializeIpsp",
+ NULL, NULL,
+ adapter_initialize_ipsp) },
+ { GDBUS_ASYNC_METHOD("DeinitializeIpsp",
+ NULL, NULL,
+ adapter_deinitialize_ipsp) },
{ GDBUS_METHOD("SetScanRespData",
GDBUS_ARGS({ "value", "ay" },
- { "length", "i" }), NULL,
+ { "slot_id", "i" }), NULL,
adapter_set_scan_rsp_data) },
{ GDBUS_METHOD("AddDeviceWhiteList",
GDBUS_ARGS({ "address", "s" },
{ GDBUS_ASYNC_METHOD("CreateDevice",
GDBUS_ARGS({ "address", "s" }), NULL,
create_device) },
-#ifdef BLUEZ5_GATT_CLIENT
- { GDBUS_METHOD("ConnectLE",
- GDBUS_ARGS({ "address", "s"}),
- NULL, connect_le) },
- { GDBUS_METHOD("DisconnectLE",
- GDBUS_ARGS({ "address", "s"}),
- NULL, disconnect_le) },
- { GDBUS_METHOD("DiscoverLEServices",
- GDBUS_ARGS({ "address", "s"}),
- GDBUS_ARGS({ "result", "b"}),
- discover_le_services) },
-#endif
#endif
{ GDBUS_ASYNC_METHOD("RemoveDevice",
GDBUS_ARGS({ "device", "o" }), NULL, remove_device) },
#ifdef __TIZEN_PATCH__
static const GDBusSignalTable adapter_signals[] = {
+ { GDBUS_SIGNAL("AdvertisingEnabled",
+ GDBUS_ARGS({ "slot_id", "i" },
+ { "enabled", "b"})) },
{ GDBUS_SIGNAL("RssiEnabled",
GDBUS_ARGS({"address","s"},
{ "link_type", "i" },
{ "Modalias", "s", property_get_modalias, NULL,
property_exists_modalias },
#ifdef __TIZEN_PATCH__
- { "Advertising", "b", property_get_advertising },
+#if 0 // Not used
{ "SecureConnection", "b", property_get_secure_connection },
+#endif
{ "Connectable", "b", property_get_connectable,
property_set_connectable },
{ "Version", "s", property_get_version },
+ { "SupportedLEFeatures", "as", property_get_supported_le_features},
+ { "IpspInitStateChanged", "b", property_get_ipsp_init_state},
#endif
{ }
return false;
}
+struct btd_gatt_database *btd_adapter_get_database(struct btd_adapter *adapter)
+{
+ if (!adapter)
+ return NULL;
+
+ return adapter->database;
+}
+
uint32_t btd_adapter_get_class(struct btd_adapter *adapter)
{
return adapter->dev_class;
if (record_has_uuid(rec, att_uuid))
goto failed;
+ /* TODO: Do this through btd_gatt_database */
if (!gatt_parse_record(rec, &uuid, &psm, &start, &end))
goto failed;
static void adapter_remove(struct btd_adapter *adapter)
{
GSList *l;
+#ifndef __TIZEN_PATCH__
+ struct gatt_db *db;
+#endif
DBG("Removing adapter %s", adapter->path);
adapter->devices = NULL;
unload_drivers(adapter);
+
+#ifndef __TIZEN_PATCH__
+ db = btd_gatt_database_get_db(adapter->database);
+ gatt_db_unregister(db, adapter->db_id);
+ adapter->db_id = 0;
+
+ btd_gatt_database_destroy(adapter->database);
+ adapter->database = NULL;
+#else
btd_adapter_gatt_server_stop(adapter);
+#endif
g_slist_free(adapter->pin_callbacks);
adapter->pin_callbacks = NULL;
}
device_set_last_addr_type(dev, bdaddr_type);
+ device_set_ipsp_connected(dev, FALSE);
#else
if (device_is_temporary(dev) && !adapter->discovery_list) {
eir_data_free(&eir_data);
}
#ifdef __TIZEN_PATCH__
- if (adapter->advertising) {
- adapter->advertising = FALSE;
- g_dbus_emit_property_changed(dbus_conn, adapter->path,
- ADAPTER_INTERFACE, "Advertising");
- }
+ advertiser_cleanup(adapter);
#endif
g_dbus_emit_property_changed(dbus_conn, adapter->path,
ADAPTER_INTERFACE, "Powered");
if (auth->svc_id > 0)
return FALSE;
- if (device_is_service_blocked(device, auth->uuid)) {
- auth->cb(&err, auth->user_data);
- goto next;
- }
-
/* The below patch should be removed after we provide the API
* to control autorization for the specific UUID.
*/
dev_path = device_get_path(device);
if (agent_authorize_service(auth->agent, dev_path, auth->uuid,
- agent_auth_cb, adapter, NULL, auth->fd) < 0) {
+ agent_auth_cb, adapter, NULL) < 0) {
auth->cb(&err, auth->user_data);
goto next;
}
static int adapter_authorize(struct btd_adapter *adapter, const bdaddr_t *dst,
const char *uuid, service_auth_cb cb,
- void *user_data, int fd)
+ void *user_data)
{
struct service_auth *auth;
struct btd_device *device;
auth->device = device;
auth->adapter = adapter;
auth->id = ++id;
- auth->fd = fd;
auth->svc_id = device_wait_for_svc_complete(device, svc_complete, auth);
g_queue_push_tail(adapter->auths, auth);
guint btd_request_authorization(const bdaddr_t *src, const bdaddr_t *dst,
const char *uuid, service_auth_cb cb,
- void *user_data, int fd)
+ void *user_data)
{
struct btd_adapter *adapter;
GSList *l;
if (!adapter)
return 0;
- return adapter_authorize(adapter, dst, uuid, cb, user_data, fd);
+ return adapter_authorize(adapter, dst, uuid, cb, user_data);
}
for (l = adapters; l != NULL; l = g_slist_next(l)) {
adapter = l->data;
- id = adapter_authorize(adapter, dst, uuid, cb, user_data, fd);
+ id = adapter_authorize(adapter, dst, uuid, cb, user_data);
if (id != 0)
return id;
}
adapter->version = g_strdup(ver);
}
+#if 0 // Not used
static void new_local_irk_callback(uint16_t index, uint16_t length,
const void *param, void *user_data)
{
memcpy(adapter->local_irk, (char *)ev->irk, sizeof(adapter->local_irk));
store_adapter_info(adapter);
}
+#endif
static void hardware_error_callback(uint16_t index, uint16_t length,
const void *param, void *user_data)
struct btd_adapter *adapter = user_data;
struct btd_device *device;
char addr[18];
- const char *eir_name;
+ const uint8_t *eir_name;
struct eir_data eir_data;
if (length < sizeof(*ev)) {
eir_data_free(&eir_data);
}
+
+static void multi_adv_state_change_callback(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_vendor_specific_multi_adv_state_changed *ev = param;
+
+ if (length < sizeof(*ev)) {
+ error("Too small adv state change event");
+ return;
+ }
+
+ DBG("adv id %d, state change reason %d, connection_handle %x",
+ ev->adv_instance, ev->state_change_reason, ev->connection_handle);
+
+ if ((ev->adv_instance > 0 && ev->adv_instance < adapter_le_get_max_adv_instance()) &&
+ ev->state_change_reason == 0)
+ adapter_le_enable_multi_adv(TRUE, ev->adv_instance);
+}
+
+static void bt_6lowpan_conn_state_change_callback(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_6lowpan_conn_state_changed *ev = param;
+ struct btd_adapter *adapter = user_data;
+ struct btd_device *device;
+ char addr[18];
+ gboolean connected = 0;
+
+ if (length < sizeof(*ev)) {
+ error("Too small device connected event");
+ return;
+ }
+
+ ba2str(&ev->addr.bdaddr, addr);
+
+ DBG("hci%u device %s", index, addr);
+
+ device = btd_adapter_find_device(adapter, &ev->addr.bdaddr,
+ ev->addr.type);
+ if (!device) {
+ error("Unable to get device object for %s", addr);
+ return;
+ }
+
+ if (ev->connected)
+ connected = TRUE;
+ else
+ connected = FALSE;
+
+ device_set_ipsp_connected(device, connected);
+}
#endif
struct btd_adapter_pin_cb_iter *btd_adapter_pin_cb_iter_new(
static void store_csrk(const bdaddr_t *local, const bdaddr_t *peer,
uint8_t bdaddr_type, const unsigned char *key,
- uint8_t master)
+ uint32_t counter, uint8_t type)
{
const char *group;
char adapter_addr[18];
GKeyFile *key_file;
char key_str[33];
gsize length = 0;
+ gboolean auth;
char *str;
int i;
- if (master == 0x00)
+ switch (type) {
+ case 0x00:
group = "LocalSignatureKey";
- else if (master == 0x01)
+ auth = FALSE;
+ break;
+ case 0x01:
group = "RemoteSignatureKey";
- else {
- warn("Unsupported CSRK type %u", master);
+ auth = FALSE;
+ break;
+ case 0x02:
+ group = "LocalSignatureKey";
+ auth = TRUE;
+ break;
+ case 0x03:
+ group = "RemoteSignatureKey";
+ auth = TRUE;
+ break;
+ default:
+ warn("Unsupported CSRK type %u", type);
return;
}
sprintf(key_str + (i * 2), "%2.2X", key[i]);
g_key_file_set_string(key_file, group, "Key", key_str);
+ g_key_file_set_integer(key_file, group, "Counter", counter);
+ g_key_file_set_boolean(key_file, group, "Authenticated", auth);
create_file(filename, S_IRUSR | S_IWUSR);
ba2str(&addr->bdaddr, dst);
- DBG("hci%u new CSRK for %s master %u", adapter->dev_id, dst,
- ev->key.master);
+ DBG("hci%u new CSRK for %s type %u", adapter->dev_id, dst,
+ ev->key.type);
device = btd_adapter_get_device(adapter, &addr->bdaddr, addr->type);
if (!device) {
if (!ev->store_hint)
return;
- store_csrk(bdaddr, &key->addr.bdaddr, key->addr.type, key->val,
- key->master);
+ store_csrk(bdaddr, &key->addr.bdaddr, key->addr.type, key->val, 0,
+ key->type);
if (device_is_temporary(device))
btd_device_set_temporary(device, FALSE);
return -EIO;
}
+#ifndef __TIZEN_PATCH__
+static void services_modified(struct gatt_db_attribute *attrib, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+
+ g_dbus_emit_property_changed(dbus_conn, adapter->path,
+ ADAPTER_INTERFACE, "UUIDs");
+}
+#endif
+
static int adapter_register(struct btd_adapter *adapter)
{
struct agent *agent;
+#ifndef __TIZEN_PATCH__
+ struct gatt_db *db;
+#endif
if (powering_down)
return -EBUSY;
agent_unref(agent);
}
+#ifndef __TIZEN_PATCH__
+ adapter->database = btd_gatt_database_new(adapter);
+ if (!adapter->database) {
+ error("Failed to create GATT database for adapter");
+ adapters = g_slist_remove(adapters, adapter);
+ return -EINVAL;
+ }
+
+ db = btd_gatt_database_get_db(adapter->database);
+ adapter->db_id = gatt_db_register(db, services_modified,
+ services_modified,
+ adapter, NULL);
+#else
btd_adapter_gatt_server_start(adapter);
+#endif
load_config(adapter);
fix_storage(adapter);
struct eir_data eir_data;
uint16_t eir_len;
char addr[18];
+ bool name_known;
if (length < sizeof(*ev)) {
error("Too small device connected event");
adapter_add_connection(adapter, device, ev->addr.type);
- if (eir_data.name != NULL) {
+ name_known = device_name_known(device);
+
+ if (eir_data.name && (eir_data.name_complete || !name_known)) {
device_store_cached_name(device, eir_data.name);
btd_device_device_set_name(device, eir_data.name);
}
return false;
}
+
+int btd_adapter_connect_ipsp(struct btd_adapter *adapter,
+ const bdaddr_t *bdaddr,
+ uint8_t bdaddr_type)
+
+{
+ struct mgmt_cp_connect_6lowpan cp;
+
+ memset(&cp, 0, sizeof(cp));
+ bacpy(&cp.addr.bdaddr, bdaddr);
+ cp.addr.type = bdaddr_type;
+
+ if (mgmt_send(adapter->mgmt, MGMT_OP_CONNECT_6LOWPAN,
+ adapter->dev_id, sizeof(cp), &cp,
+ NULL, NULL, NULL) > 0)
+ return 0;
+
+ return -EIO;
+}
+
+int btd_adapter_disconnect_ipsp(struct btd_adapter *adapter,
+ const bdaddr_t *bdaddr,
+ uint8_t bdaddr_type)
+
+{
+ struct mgmt_cp_disconnect_6lowpan cp;
+
+ memset(&cp, 0, sizeof(cp));
+ bacpy(&cp.addr.bdaddr, bdaddr);
+ cp.addr.type = bdaddr_type;
+
+ if (mgmt_send(adapter->mgmt, MGMT_OP_DISCONNECT_6LOWPAN,
+ adapter->dev_id, sizeof(cp), &cp,
+ NULL, NULL, NULL) > 0)
+ return 0;
+
+ return -EIO;
+}
#endif
static void clear_devices_complete(uint8_t status, uint16_t length,
break;
}
+#if 0
if (missing_settings & MGMT_SETTING_SECURE_CONN)
set_mode(adapter, MGMT_OP_SET_SECURE_CONN, 0x01);
+#endif
err = adapter_register(adapter);
if (err < 0) {
rssi_disabled_callback,
adapter, NULL);
+#if 0 // Not used
mgmt_register(adapter->mgmt, MGMT_EV_NEW_LOCAL_IRK,
adapter->dev_id,
new_local_irk_callback,
adapter, NULL);
+#endif
mgmt_register(adapter->mgmt, MGMT_EV_HARDWARE_ERROR,
adapter->dev_id,
adapter->dev_id,
device_name_update_callback,
adapter, NULL);
+
+ mgmt_register(adapter->mgmt, MGMT_EV_MULTI_ADV_STATE_CHANGED,
+ adapter->dev_id,
+ multi_adv_state_change_callback,
+ adapter, NULL);
+
+ mgmt_register(adapter->mgmt, MGMT_EV_6LOWPAN_CONN_STATE_CHANGED,
+ adapter->dev_id,
+ bt_6lowpan_conn_state_change_callback,
+ adapter, NULL);
#endif
set_dev_class(adapter);
}
#ifdef __TIZEN_PATCH__
+#if 0 // Not used
static void read_rssi_complete(uint8_t status, uint16_t length,
const void *param, void *user_data)
{
return 0;
return -EIO;
}
+#endif
int btd_adapter_le_conn_update(struct btd_adapter *adapter, bdaddr_t *bdaddr,
uint16_t interval_min, uint16_t interval_max,
*
*/
+#include <stdbool.h>
+#include <dbus/dbus.h>
+#include <glib.h>
+#ifdef __TIZEN_PATCH__
#include <bluetooth/bluetooth.h>
#include <bluetooth/sdp.h>
#include <bluetooth/hci.h>
#include <bluetooth/hci_lib.h>
-#include <dbus/dbus.h>
-#include <glib.h>
-#include <stdbool.h>
+#endif
#define MAX_NAME_LENGTH 248
bool btd_adapter_get_powered(struct btd_adapter *adapter);
bool btd_adapter_get_connectable(struct btd_adapter *adapter);
+struct btd_gatt_database *btd_adapter_get_database(struct btd_adapter *adapter);
+
uint32_t btd_adapter_get_class(struct btd_adapter *adapter);
const char *btd_adapter_get_name(struct btd_adapter *adapter);
void btd_adapter_remove_device(struct btd_adapter *adapter,
int btd_register_adapter_driver(struct btd_adapter_driver *driver);
void btd_unregister_adapter_driver(struct btd_adapter_driver *driver);
guint btd_request_authorization(const bdaddr_t *src, const bdaddr_t *dst,
- const char *uuid, service_auth_cb cb, void *user_data, int fd);
+ const char *uuid, service_auth_cb cb, void *user_data);
int btd_cancel_authorization(guint id);
int btd_adapter_restore_powered(struct btd_adapter *adapter);
bool btd_le_connect_before_pairing(void);
#ifdef __TIZEN_PATCH__
+#if 0 // Not used
int btd_adapter_read_rssi(struct btd_adapter *adapter, bdaddr_t *bdaddr,
struct btd_device *device);
+
int btd_adapter_l2cap_conn_param_update(struct btd_adapter *adapter,
bdaddr_t *bdaddr, uint16_t interval_min,
uint16_t interval_max, uint16_t latency,
uint16_t supervision_time_out);
+
int btd_adapter_write_auth_payload_timeout(struct btd_adapter *adapter,
bdaddr_t *bdaddr, uint32_t payload_timeout,
struct btd_device *device);
int btd_adapter_read_auth_payload_timeout(struct btd_adapter *adapter,
bdaddr_t *bdaddr, struct btd_device *device);
+#endif
+
int btd_adapter_le_conn_update(struct btd_adapter *adapter, bdaddr_t *bdaddr,
uint16_t interval_min, uint16_t interval_max,
uint16_t latency, uint16_t supervision_time_out);
gboolean btd_adapter_disable_le_auto_connect(struct btd_adapter *adapter);
void adapter_check_version(struct btd_adapter *adapter, uint8_t hci_ver);
GSList *btd_adapter_get_connections(struct btd_adapter *adapter);
-#endif
\ No newline at end of file
+int btd_adapter_connect_ipsp(struct btd_adapter *adapter,
+ const bdaddr_t *bdaddr,
+ uint8_t bdaddr_type);
+int btd_adapter_disconnect_ipsp(struct btd_adapter *adapter,
+ const bdaddr_t *bdaddr,
+ uint8_t bdaddr_type);
+
+#endif
--- /dev/null
+#ifdef __TIZEN_PATCH__
+#ifdef __BROADCOM_PATCH__
+
+#include <errno.h>
+
+#include "log.h"
+#include "adapter.h"
+#include "eir.h"
+
+#include "adapter_le_vsc_features.h"
+
+
+static apater_le_vsc_rp_get_vendor_cap ble_vsc_cb = { -1, };
+
+void add_data_to_stream(uint8_t *stream, uint8_t *data, uint8_t len)
+{
+ int i;
+
+ for (i=0; i<len; i++) {
+ *(stream)++ = (uint8_t) data[len-1-i];
+ DBG("[%d]>> 0x%X", i, data[len-1-i] );
+ }
+}
+
+static int send_vsc_command(uint16_t ocf, uint8_t *cp, uint8_t cp_len,
+ uint8_t *rp, uint8_t rp_len)
+{
+ int dd;
+ struct hci_request rq;
+
+ dd = hci_open_dev(0);
+ if (dd < 0) {
+ error("hci_open_dev is failed");
+ return -1;
+ }
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_VENDOR_CMD;
+ rq.ocf = ocf;
+ rq.cparam = cp;
+ rq.clen = cp_len;
+ rq.rparam = rp;
+ rq.rlen = rp_len;
+
+ if (hci_send_req(dd, &rq, 5000) < 0) {
+ error("Fail to send VSC");
+ hci_close_dev(dd);
+ return -1;
+ }
+
+ hci_close_dev(dd);
+ return 0;
+}
+
+gboolean adapter_le_read_ble_feature_info(void)
+{
+ int ret;
+
+ DBG("");
+
+ ret = send_vsc_command(OCF_BCM_LE_GET_VENDOR_CAP, (uint8_t *) NULL, 0,
+ (uint8_t *) &ble_vsc_cb, sizeof(ble_vsc_cb));
+ if (ret < 0)
+ return FALSE;
+
+ if (HCI_SUCCESS != ble_vsc_cb.status) {
+ error("Fail to read ble feature info");
+ return FALSE;
+ }
+
+ DBG("======== BLE support info ========");
+ DBG("adv_inst_max [%d]", ble_vsc_cb.adv_inst_max);
+ DBG("rpa_offloading [%d]", ble_vsc_cb.rpa_offloading);
+ DBG("tot_scan_results_strg [%d]", ble_vsc_cb.tot_scan_results_strg);
+ DBG("max_irk_list_sz [%d]", ble_vsc_cb.max_irk_list_sz);
+ DBG("filter_support [%d]", ble_vsc_cb.filter_support);
+ DBG("max_filter [%d]", ble_vsc_cb.max_filter);
+ DBG("energy_support [%d]", ble_vsc_cb.energy_support);
+ DBG("onlost_follow [%d]", ble_vsc_cb.onlost_follow);
+ DBG("=================================");
+
+ return TRUE;
+}
+
+int adapter_le_get_max_adv_instance(void)
+{
+ if (HCI_SUCCESS != ble_vsc_cb.status) {
+ error("not yet acquired chipset info");
+ return 0;
+ }
+
+ /* GearS does not support multi advertising.
+ but its official firmware returns adv_inst_max vaule to 5.
+ So here check rpa_offloading support and filter_support */
+ if (!ble_vsc_cb.rpa_offloading || !ble_vsc_cb.max_filter)
+ return 0;
+
+ return ble_vsc_cb.adv_inst_max;
+}
+
+gboolean adapter_le_is_supported_multi_advertising(void)
+{
+ if (HCI_SUCCESS != ble_vsc_cb.status) {
+ error("not yet acquired chipset info");
+ return FALSE;
+ }
+
+ /* GearS does not support multi advertising.
+ but its official firmware returns adv_inst_max vaule to 5.
+ So here check rpa_offloading support and filter_support */
+ if (!ble_vsc_cb.rpa_offloading || !ble_vsc_cb.max_filter)
+ return FALSE;
+
+ if (ble_vsc_cb.adv_inst_max >= 5)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+gboolean adapter_le_is_supported_offloading(void)
+{
+ if (HCI_SUCCESS != ble_vsc_cb.status) {
+ error("not yet acquired chipset info");
+ return FALSE;
+ }
+
+ return ble_vsc_cb.rpa_offloading ? TRUE : FALSE;
+}
+
+int adapter_le_get_scan_filter_size(void)
+{
+ if (HCI_SUCCESS != ble_vsc_cb.status) {
+ error("not yet acquired chipset info");
+ return 0;
+ }
+#if 0
+ if (!ble_vsc_cb.filter_support) {
+ error("filter_support is not supported");
+ return 0;
+ }
+#endif
+ return ble_vsc_cb.max_filter;
+}
+
+gboolean adapter_le_set_multi_adv_params (adapter_le_adv_inst_info_t *p_inst,
+ adapter_le_adv_param_t *p_params)
+{
+ int ret;
+ adapter_le_vsc_cp_set_multi_adv_params cp;
+ apater_le_vsc_rp_multi_adv rp;
+
+ DBG("");
+
+ memset(&cp, 0, sizeof(cp));
+ cp.subcode = SUB_CMD_LE_MULTI_ADV_SET_PARAM;
+ cp.adv_int_min = p_params->adv_int_min;
+ cp.adv_int_max = p_params->adv_int_max;
+ cp.adv_type = p_params->adv_type;
+ cp.bdaddr_type = p_inst->bdaddr_type;
+ bacpy(&cp.bdaddr, &p_inst->bdaddr);
+ cp.direct_bdaddr_type = 0;
+ bacpy(&cp.direct_bdaddr, BDADDR_ANY);
+
+ cp.channel_map = p_params->channel_map;
+ cp.adv_filter_policy = p_params->adv_filter_policy;
+ cp.inst_id = p_inst->inst_id;
+ cp.tx_power = p_params->tx_power;
+
+ ret = send_vsc_command(OCF_BCM_LE_MULTI_ADV, (uint8_t *) &cp, sizeof(cp),
+ (uint8_t *) &rp, sizeof(rp));
+
+ if (ret < 0)
+ return FALSE;
+
+ if (HCI_SUCCESS != rp.status) {
+ DBG("Fail to send VSC :: sub[%x] - status [0x%02x]", rp.subcode, rp.status);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean adapter_le_set_multi_adv_data(uint8_t inst_id, gboolean is_scan_rsp,
+ uint8_t data_len, uint8_t *p_data)
+{
+ int ret;
+ adapter_le_vsc_cp_set_multi_adv_data cp;
+ apater_le_vsc_rp_multi_adv rp;
+
+ DBG("");
+
+ memset(&cp, 0, sizeof(cp));
+ cp.subcode = (is_scan_rsp) ?
+ SUB_CMD_LE_MULTI_ADV_WRITE_SCAN_RSP_DATA :
+ SUB_CMD_LE_MULTI_ADV_WRITE_ADV_DATA;
+ cp.data_len = data_len;
+ memcpy(&cp.data, p_data, data_len);
+ cp.inst_id = inst_id;
+
+ ret = send_vsc_command(OCF_BCM_LE_MULTI_ADV, (uint8_t *) &cp, sizeof(cp),
+ (uint8_t *) &rp, sizeof(rp));
+
+ if (ret < 0)
+ return FALSE;
+
+ if (HCI_SUCCESS != rp.status) {
+ DBG("Fail to send VSC :: sub[%x] - status [0x%02x]", rp.subcode, rp.status);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean adapter_le_enable_multi_adv (gboolean enable, uint8_t inst_id)
+{
+ int ret;
+ adapter_le_vsc_cp_enable_multi_adv cp;
+ apater_le_vsc_rp_multi_adv rp;
+
+ DBG("");
+
+ memset(&cp, 0, sizeof(cp));
+ cp.subcode = SUB_CMD_LE_MULTI_ADV_ENB;
+ cp.enable = enable;
+ cp.inst_id = inst_id;
+
+ ret = send_vsc_command(OCF_BCM_LE_MULTI_ADV, (uint8_t *) &cp, sizeof(cp),
+ (uint8_t *) &rp, sizeof(rp));
+
+ if (ret < 0)
+ return FALSE;
+
+ if (HCI_SUCCESS != rp.status) {
+ DBG("Fail to send VSC :: sub[%x] - status [0x%02x]", rp.subcode, rp.status);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean adapter_le_enable_scan_filtering (gboolean enable)
+{
+ int ret;
+ adapter_le_vsc_cp_enable_scan_filter cp;
+ apater_le_vsc_rp_enable_scan_filter rp;
+
+ DBG(" enable[%d]", enable);
+
+ memset(&cp, 0, sizeof(cp));
+ cp.subcode = SUB_CMD_LE_META_PF_ENABLE;
+ cp.enable = enable;
+
+ ret = send_vsc_command(OCF_BCM_LE_SCAN_FILTER, (uint8_t *) &cp, sizeof(cp),
+ (uint8_t *) &rp, sizeof(rp));
+
+ if (ret < 0)
+ return FALSE;
+
+ if (HCI_SUCCESS != rp.status) {
+ DBG("Fail to send VSC :: sub[%x] - status [%x]", rp.subcode, rp.status);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean adapter_le_set_scan_filter_params(adapter_le_scan_filter_param_t *params)
+{
+ int ret;
+ adapter_le_vsc_cp_apcf_set_filter_params cp;
+ adapter_le_vsc_rp_apcf_set_scan_filter rp;
+
+ DBG("filter_index [%d]", params->index);
+
+ memset(&cp, 0, sizeof(cp));
+ cp.subcode = SUB_CMD_LE_META_PF_FEAT_SEL;
+ cp.action = params->action;
+ cp.filter_index= params->index;
+ cp.feature= params->feature;
+ cp.feature_list_logic = params->filter_logic_type;
+ cp.filter_logic = params->filter_logic_type;
+ cp.rssi_high_threshold = params->rssi_high_threshold;
+ cp.rssi_low_thresh = params->rssi_low_threshold;
+ cp.delivery_mode = params->delivery_mode;
+ cp.onfound_timeout = params->onfound_timeout;
+ cp.onfound_timeout_cnt = params->onfound_timeout_cnt;
+ cp.onlost_timeout = params->onlost_timeout;
+
+ ret = send_vsc_command(OCF_BCM_LE_SCAN_FILTER, (uint8_t *) &cp, sizeof(cp),
+ (uint8_t *) &rp, sizeof(rp));
+
+ if (ret < 0)
+ return FALSE;
+
+ if (HCI_SUCCESS != rp.status) {
+ DBG("Fail to send VSC :: sub[%x] - status [%x] Available space [%x]",
+ rp.subcode, rp.status, rp.available_space);
+ return FALSE;
+ }
+
+ DBG("Scan Filter VSC :: sub[%x] - status [%x] Action [%x] Available space [%x]",
+ rp.subcode, rp.status, rp.action, rp.available_space);
+ return TRUE;
+}
+
+gboolean adapter_le_service_address_scan_filtering(adapter_le_address_filter_params_t *params)
+{
+ int ret;
+ adapter_le_vsc_cp_address_scan_filter cp;
+ adapter_le_vsc_rp_apcf_set_scan_filter rp;
+
+ DBG("");
+
+ memset(&cp, 0, sizeof(cp));
+ cp.subcode = SUB_CMD_LE_META_PF_ADDR;
+ cp.action= params ->action;
+ cp.filter_index = params->filter_index;
+
+ bacpy(&cp.bdaddr, ¶ms->broadcaster_addr);
+ cp.bdaddr_type = params->bdaddr_type;
+
+ ret = send_vsc_command(OCF_BCM_LE_SCAN_FILTER, (uint8_t *) &cp, sizeof(cp),
+ (uint8_t *) &rp, sizeof(rp));
+
+ if (ret < 0)
+ return FALSE;
+
+ if (HCI_SUCCESS != rp.status) {
+ DBG("Fail to send VSC :: sub[%x] - status [%x]", rp.subcode, rp.status);
+ return FALSE;
+ }
+
+ DBG("Scan Filter VSC :: sub[%x] - status [%x] Action [%x] Available space [%x]",
+ rp.subcode, rp.status, rp.action, rp.available_space);
+
+ return TRUE;
+}
+
+gboolean adapter_le_service_uuid_scan_filtering(gboolean is_solicited,
+ adapter_le_uuid_params_t *params)
+{
+ int ret;
+ adapter_le_vsc_cp_service_uuid_scan_filter cp;
+ adapter_le_vsc_rp_apcf_set_scan_filter rp;
+ uint8_t *p = cp.data;
+ int cp_len = UUID_SCAN_FILTER_HEADER_SIZE;
+
+ DBG("");
+
+ memset(&cp, 0, sizeof(cp));
+ cp.subcode = (is_solicited) ? SUB_CMD_LE_META_PF_SOL_UUID :
+ SUB_CMD_LE_META_PF_UUID;
+
+ cp.action= params ->action;
+ cp.filter_index = params->filter_index;
+
+ add_data_to_stream((uint8_t *)&cp.data, params->uuid, params->uuid_len);
+ cp_len += params->uuid_len;
+
+ add_data_to_stream(p+params->uuid_len, params->uuid_mask, params->uuid_len);
+ cp_len += params->uuid_len;
+
+ ret = send_vsc_command(OCF_BCM_LE_SCAN_FILTER, (uint8_t *) &cp, cp_len,
+ (uint8_t *) &rp, sizeof(rp));
+
+ if (ret < 0)
+ return FALSE;
+
+ if (HCI_SUCCESS != rp.status) {
+ DBG("Fail to send VSC :: sub[%x] - status [%x]", rp.subcode, rp.status);
+ return FALSE;
+ }
+
+ DBG("Scan Filter VSC :: sub[%x] - status [%x] Action [%x] Available space [%x]",
+ rp.subcode, rp.status, rp.action, rp.available_space);
+
+ return TRUE;
+}
+
+gboolean adapter_le_local_name_scan_filtering(adapter_le_local_name_params_t *params)
+{
+ int ret;
+ adapter_le_vsc_cp_local_name_scan_filter cp;
+ adapter_le_vsc_rp_apcf_set_scan_filter rp;
+ int cp_len = NAME_SCAN_FILTER_HEADER_SIZE;
+ int name_len;
+
+ DBG("");
+
+ memset(&cp, 0, sizeof(cp));
+ cp.subcode = SUB_CMD_LE_META_PF_LOCAL_NAME;
+ cp.action= params->action;
+ cp.filter_index = params->filter_index;
+
+ name_len = params->name_len;
+ DBG("name [%s], len [%d]",params->local_name, name_len);
+
+ if (name_len > SCAN_FILTER_DATA_MAX_LEN)
+ name_len = SCAN_FILTER_DATA_MAX_LEN;
+
+ if (name_len > 0) {
+ memcpy(&cp.name, params->local_name, name_len);
+ cp_len += name_len;
+ }
+
+ ret = send_vsc_command(OCF_BCM_LE_SCAN_FILTER, (uint8_t *) &cp, cp_len,
+ (uint8_t *) &rp, sizeof(rp));
+
+ if (ret < 0)
+ return FALSE;
+
+ if (HCI_SUCCESS != rp.status) {
+ DBG("Fail to send VSC :: sub[%x] - status [%x]", rp.subcode, rp.status);
+ return FALSE;
+ }
+
+ DBG("Scan Filter VSC :: sub[%x] - status [%x] Action [%x] Available space [%x]",
+ rp.subcode, rp.status, rp.action, rp.available_space);
+
+ return TRUE;
+}
+
+gboolean adapter_le_manf_data_scan_filtering (adapter_le_manf_data_params_t *params)
+{
+ int ret;
+ adapter_le_vsc_cp_manf_data_scan_filter cp;
+ adapter_le_vsc_rp_apcf_set_scan_filter rp;
+ uint8_t *p = cp.data;
+ int data_len = 0;
+ DBG("");
+
+ memset(&cp, 0, sizeof(cp));
+ cp.subcode = SUB_CMD_LE_META_PF_MANU_DATA;
+ cp.action= params->action;
+ cp.filter_index = params->filter_index;
+
+ /* add company_id and data */
+ cp.data[data_len++] = (uint8_t) (params->company_id >> 8);
+ cp.data[data_len++] = (uint8_t) params->company_id;
+ DBG("");
+ memcpy(p + data_len, params->man_data, params->man_data_len);
+ data_len += params->man_data_len;
+
+ /* add company_id mask and data mask */
+ cp.data[data_len++] = (uint8_t) (params->company_id_mask >> 8);
+ cp.data[data_len++] = (uint8_t) params->company_id_mask;
+ memcpy(p + data_len, params->man_data_mask, params->man_data_len);
+ data_len += params->man_data_len;
+
+ ret = send_vsc_command(OCF_BCM_LE_SCAN_FILTER, (uint8_t *) &cp,
+ MANF_DATA_SCAN_FILTER_HEADER_SIZE + data_len,
+ (uint8_t *) &rp, sizeof(rp));
+
+ if (ret < 0)
+ return FALSE;
+
+ if (HCI_SUCCESS != rp.status) {
+ DBG("Fail to send VSC :: sub[%x] - status [%x]", rp.subcode, rp.status);
+ return FALSE;
+ }
+
+ DBG("Scan Filter VSC :: sub[%x] - status [%x] Action [%x] Available space [%x]",
+ rp.subcode, rp.status, rp.action, rp.available_space);
+
+ return TRUE;
+}
+
+gboolean adapter_le_service_data_scan_filtering (adapter_le_service_data_params_t *params)
+{
+ int ret;
+ adapter_le_vsc_cp_service_data_scan_filter cp;
+ adapter_le_vsc_rp_apcf_set_scan_filter rp;
+ uint8_t *p = cp.data;
+ int cp_len = SERVICE_DATA_SCAN_FILTER_HEADER_SIZE;
+
+ DBG("");
+
+ memset(&cp, 0, sizeof(cp));
+ cp.subcode = SUB_CMD_LE_META_PF_SRVC_DATA;
+ cp.action= params->action;
+ cp.filter_index = params->filter_index;
+
+ memcpy(&cp.data, params->service_data, params->service_data_len);
+ cp_len += params->service_data_len;
+
+ memcpy(p+params->service_data_len, params->service_data_mask,
+ params->service_data_len);
+ cp_len += params->service_data_len;
+
+ ret = send_vsc_command(OCF_BCM_LE_SCAN_FILTER, (uint8_t *) &cp, cp_len,
+ (uint8_t *) &rp, sizeof(rp));
+
+ if (ret < 0)
+ return FALSE;
+
+ if (HCI_SUCCESS != rp.status) {
+ DBG("Fail to send VSC :: sub[%x] - status [%x]", rp.subcode, rp.status);
+ return FALSE;
+ }
+
+ DBG("Scan Filter VSC :: sub[%x] - status [%x] Action [%x] Available space [%x]",
+ rp.subcode, rp.status, rp.action, rp.available_space);
+
+ return TRUE;
+}
+
+gboolean adapter_le_set_scan_filter_data(int client_if, int action,
+ int filt_type, int filter_index,
+ int company_id,
+ int company_id_mask,
+ int uuid_len, uint8_t *p_uuid,
+ int uuid_mask_len, uint8_t *p_uuid_mask,
+ gchar *string, int addr_type,
+ int data_len, uint8_t *p_data,
+ int mask_len, uint8_t *p_mask)
+{
+ gboolean ret;
+
+ DBG("");
+
+ switch (filt_type) {
+ case TYPE_DEVICE_ADDRESS: {
+ /* TYPE_DEVICE_ADDRESS */
+ adapter_le_address_filter_params_t params;
+ bdaddr_t bd_addr;
+
+ str2ba(string, &bd_addr);
+
+ params.action = action;
+ params.filter_index = filter_index;
+ bacpy(¶ms.broadcaster_addr, &bd_addr);
+ params.bdaddr_type = addr_type;
+
+ ret = adapter_le_service_address_scan_filtering(¶ms);
+ break;
+ }
+
+ case TYPE_SERVICE_UUID:
+ case TYPE_SOLICIT_UUID: {
+ adapter_le_uuid_params_t params;
+ gboolean is_solicited = (filt_type == TYPE_SOLICIT_UUID) ? TRUE : FALSE;
+
+ if (uuid_len != UUID_16_LEN && uuid_len != UUID_32_LEN
+ && uuid_len != UUID_128_LEN) {
+ DBG("UUID length error");
+ return FALSE;
+ }
+
+ if (uuid_len != uuid_mask_len) {
+ DBG("Both UUID and UUID_MASK length shoule be samed");
+ return FALSE;
+ }
+
+ params.action = action;
+ params.filter_index = filter_index;
+ params.uuid = p_uuid;
+ params.uuid_mask = p_uuid_mask;
+ params.uuid_len = uuid_len;
+
+ ret = adapter_le_service_uuid_scan_filtering(is_solicited, ¶ms);
+ break;
+ }
+
+ case TYPE_LOCAL_NAME: {
+ adapter_le_local_name_params_t params;
+
+ params.action = action;
+ params.filter_index = filter_index;
+ params.local_name = string;
+ params.name_len = strlen(string);
+ ret = adapter_le_local_name_scan_filtering(¶ms);
+ break;
+ }
+
+ case TYPE_MANUFACTURER_DATA: {
+ adapter_le_manf_data_params_t params;
+
+ if (data_len == 0 || (data_len != mask_len)) {
+ DBG("parameter length error");
+ return FALSE;
+ }
+
+ params.action = action;
+ params.filter_index = filter_index;
+ params.company_id = company_id;
+ params.company_id_mask = company_id_mask;
+ params.man_data = p_data;
+ params.man_data_mask = p_mask;
+ params.man_data_len = data_len;
+
+ ret = adapter_le_manf_data_scan_filtering(¶ms);
+ break;
+ }
+
+ case TYPE_SERVICE_DATA: {
+ adapter_le_service_data_params_t params;
+
+ if (data_len == 0 || (data_len != mask_len)) {
+ DBG("parameter length error");
+ return FALSE;
+ }
+
+ params.action = action;
+ params.filter_index = filter_index;
+ params.service_data = p_data;
+ params.service_data_mask = p_mask;
+ params.service_data_len = data_len;
+
+ ret = adapter_le_service_data_scan_filtering(¶ms);
+ break;
+ }
+
+ default:
+ DBG("filter_type error");
+ ret = FALSE;
+ }
+
+ return ret;
+}
+
+gboolean adapter_le_clear_scan_filter_data(int client_if, int filter_index)
+{
+ int ret;
+ adapter_le_vsc_cp_service_data_scan_filter cp;
+ adapter_le_vsc_rp_apcf_set_scan_filter rp;
+
+ DBG("");
+
+ memset(&cp, 0, sizeof(cp));
+ cp.subcode = SUB_CMD_LE_META_PF_FEAT_SEL;
+ cp.action= 0x02; // (Add - 0x00, Delete - 0x01, Clear - 0x02)
+ cp.filter_index = filter_index;
+
+ ret = send_vsc_command(OCF_BCM_LE_SCAN_FILTER, (uint8_t *) &cp, sizeof(cp),
+ (uint8_t *) &rp, sizeof(rp));
+
+ if (ret < 0)
+ return FALSE;
+
+ if (HCI_SUCCESS != rp.status) {
+ DBG("Fail to send VSC :: sub[%x] - status [%x] Available space [%x]",
+ rp.subcode, rp.status, rp.available_space);
+ return FALSE;
+ }
+
+ DBG("Scan Filter VSC :: sub[%x] - status [%x] Action [%x] Available space [%x]",
+ rp.subcode, rp.status, rp.action, rp.available_space);
+ return TRUE;
+}
+
+#endif /* __BROADCOM_PATCH__ */
+#endif /* __TIZEN_PATCH__ */
--- /dev/null
+#ifdef __TIZEN_PATCH__
+
+typedef enum {
+ BLE_ADV_TX_POWER_MIN = 0x00,
+ BLE_ADV_TX_POWER_LOW = 0x01,
+ BLE_ADV_TX_POWER_MID = 0x02,
+ BLE_ADV_TX_POWER_UPPER = 0x03,
+ BLE_ADV_TX_POWER_MAX = 0x04,
+} adapter_le_tx_power_t;
+
+typedef struct {
+ uint8_t inst_id;
+ uint8_t bdaddr_type;
+ bdaddr_t bdaddr;
+} adapter_le_adv_inst_info_t;
+
+typedef struct {
+ uint16_t adv_int_min; /* minimum adv interval */
+ uint16_t adv_int_max; /* maximum adv interval */
+ uint8_t adv_type; /* adv event type (0x00 ~ 0x04) */
+ uint8_t channel_map; /* adv channel map (all channel = 0x07) */
+ uint8_t adv_filter_policy; /* advertising filter policy (0x00 ~ 0x04) */
+ adapter_le_tx_power_t tx_power; /* adv tx power */
+} adapter_le_adv_param_t;
+
+typedef enum {
+ ADD,
+ DELETE,
+ CLEAR,
+} adapter_le_scan_filter_action_type;
+
+typedef enum {
+ ADDR_LE_PUBLIC,
+ ADDR_LE_RANDOM,
+} adapter_vsc_le_addr_type;
+
+typedef enum {
+ TYPE_DEVICE_ADDRESS = 0x01,
+ TYPE_SERVICE_DATA_CHANGED = 0x02,
+ TYPE_SERVICE_UUID = 0x04,
+ TYPE_SOLICIT_UUID = 0x08,
+ TYPE_LOCAL_NAME = 0x10,
+ TYPE_MANUFACTURER_DATA = 0x20,
+ TYPE_SERVICE_DATA = 0x40,
+} adapter_le_scan_filter_type;
+
+#define BDADDR_BREDR 0x00
+#define BDADDR_LE_PUBLIC 0x01
+
+#define BROADCAST_ADDR_FILTER 0x01
+#define SERVICE_DATA_CHANGE_FILTER 0x02
+#define SERVICE_UUID_CHECK 0x04
+#define SERVICE_SOLICITATION_UUID_CHECK 0x08
+#define LOCAL_NAME_CHECK 0x10
+#define MANUFACTURE_DATA_CHECK 0x20
+#define SERVICE_DATA_CHECK 0x40
+
+typedef uint16_t adapter_le_scan_filter_feature_t;
+
+typedef enum {
+ OR,
+ AND,
+} adapter_le_scan_filter_logic_type;
+
+typedef enum {
+ IMMEDIATE,
+ ON_FOUND,
+ BATCHED,
+} adapter_le_scan_filter_delivery_mode;
+
+typedef enum {
+ UUID_16_LEN=2,
+ UUID_32_LEN=4,
+ UUID_128_LEN =16,
+} adapter_le_uuid_len;
+
+typedef struct {
+ adapter_le_scan_filter_action_type action;
+ uint8_t index;
+ adapter_le_scan_filter_feature_t feature;
+ adapter_le_scan_filter_logic_type list_logic_type;
+ adapter_le_scan_filter_logic_type filter_logic_type;
+ uint8_t rssi_high_threshold;
+ adapter_le_scan_filter_delivery_mode delivery_mode;
+ uint16_t onfound_timeout;
+ uint8_t onfound_timeout_cnt;
+ uint8_t rssi_low_threshold;
+ uint16_t onlost_timeout;
+}adapter_le_scan_filter_param_t;
+
+typedef struct {
+ adapter_le_scan_filter_action_type action;
+ uint8_t filter_index;
+ bdaddr_t broadcaster_addr;
+ adapter_vsc_le_addr_type bdaddr_type;
+} adapter_le_address_filter_params_t;
+
+typedef struct {
+ adapter_le_scan_filter_action_type action;
+ uint8_t filter_index;
+ uint8_t *uuid;
+ uint8_t *uuid_mask;
+ adapter_le_uuid_len uuid_len;
+}adapter_le_uuid_params_t;
+
+typedef struct {
+ adapter_le_scan_filter_action_type action;
+ uint8_t filter_index;
+ const char *local_name;
+ uint8_t name_len;
+}adapter_le_local_name_params_t;
+
+typedef struct {
+ adapter_le_scan_filter_action_type action;
+ uint8_t filter_index;
+ uint16_t company_id;
+ uint16_t company_id_mask;
+ uint8_t *man_data;
+ uint8_t *man_data_mask;
+ uint8_t man_data_len;
+}adapter_le_manf_data_params_t;
+
+typedef struct {
+ adapter_le_scan_filter_action_type action;
+ uint8_t filter_index;
+ uint8_t *service_data;
+ uint8_t *service_data_mask;
+ uint8_t service_data_len;
+}adapter_le_service_data_params_t;
+
+#ifdef __BROADCOM_PATCH__
+
+/*****************************************************************************
+** Defentions for HCI Error Codes that are past in the events
+*/
+#define HCI_SUCCESS 0x00
+
+
+
+/*****************************************************************************
+** Vendor Specific Commands
+**
+*/
+
+#define OCF_BCM_LE_GET_VENDOR_CAP 0x0153 /* LE Get Vendor Capabilities */
+
+#define OCF_BCM_LE_MULTI_ADV 0x0154 /* Multi adv OCF */
+
+/* subcode for multi adv feature */
+#define SUB_CMD_LE_MULTI_ADV_SET_PARAM 0x01
+#define SUB_CMD_LE_MULTI_ADV_WRITE_ADV_DATA 0x02
+#define SUB_CMD_LE_MULTI_ADV_WRITE_SCAN_RSP_DATA 0x03
+#define SUB_CMD_LE_MULTI_ADV_SET_RANDOM_ADDR 0x04
+#define SUB_CMD_LE_MULTI_ADV_ENB 0x05
+
+/* APCF : Advertising Packet Content Filter feature */
+#define OCF_BCM_LE_SCAN_FILTER 0x0157 /* Advertising filter OCF */
+
+/* Sub codes for APCF */
+#define SUB_CMD_LE_META_PF_ENABLE 0x00
+#define SUB_CMD_LE_META_PF_FEAT_SEL 0x01
+#define SUB_CMD_LE_META_PF_ADDR 0x02
+#define SUB_CMD_LE_META_PF_UUID 0x03
+#define SUB_CMD_LE_META_PF_SOL_UUID 0x04
+#define SUB_CMD_LE_META_PF_LOCAL_NAME 0x05
+#define SUB_CMD_LE_META_PF_MANU_DATA 0x06
+#define SUB_CMD_LE_META_PF_SRVC_DATA 0x07
+#define SUB_CMD_LE_META_PF_ALL 0x08
+
+
+
+/*****************************************************************************
+** CP & RP for OCF_BCM_LE_GET_VENDOR_CAP
+**
+*/
+
+/**
+*
+* RP
+*
+* (1 octet) status : Command complete status
+* (1 octet) adv_inst_max : Num of advertisement instances supported
+* (1 octet) rpa_offloading: BT chip capability of RPA
+* (value 0 not capable, value 1 capable)
+* If supported by chip, it needs enablement by host
+* (2 octet) tot_scan_results_strg : Storage for scan results in bytes
+* (1 octet) max_irk_list_sz : Num of IRK entries supported in f/w
+* (1 octet) filter_support : Support Filtering in controller.
+* 0 = Not supported / 1 = supported
+* (1 octet) max_filter : Number of filters supported
+* (1 octet) energy_support : Supports reporting of activity and energy info
+* 0 = not capable, 1 = capable
+* (1 octet) onlost_follow : Number of advertisers that can be analysed
+* for onlost per filter
+*/
+typedef struct {
+ uint8_t status;
+ uint8_t adv_inst_max;
+ uint8_t rpa_offloading;
+ uint16_t tot_scan_results_strg;
+ uint8_t max_irk_list_sz;
+ uint8_t filter_support;
+ uint8_t max_filter;
+ uint8_t energy_support;
+ uint8_t onlost_follow;
+} __attribute__ ((packed)) apater_le_vsc_rp_get_vendor_cap;
+
+
+
+/*****************************************************************************
+** CP & RP for OCF_BCM_LE_MULTI_ADV
+**
+*/
+
+/**
+*
+* CP for OCF_BCM_LE_MULTI_ADV & SUB_CMD_LE_MULTI_ADV_SET_PARAM
+*
+* (1 octet) subcode : SUB_CMD_LE_MULTI_ADV_SET_PARAM
+* (2 octet) adv_int_min : per spec
+* (2 octet) adv_int_max : per spec
+* (1 octet) adv_type : per spec
+* (1 octet) bdaddr_type : per spec
+* (6 octet) bdaddr : per spec
+* (1 octet) direct_bdaddr_type : per spec
+* (6 octet) direct_bdaddr : per spec
+* (1 octet) channel_map : per spec
+* (1 octet) adv_filter_policy : per spec
+* (1 octet) inst_id : Specifies the applicability of the above parameters to an instance
+* (1 octet) tx_power : Transmit_Power Unit - in dBm (signed integer) Range (70 to +20)
+*/
+typedef struct {
+ uint8_t subcode;
+ uint16_t adv_int_min;
+ uint16_t adv_int_max;
+ uint8_t adv_type;
+ uint8_t bdaddr_type;
+ bdaddr_t bdaddr;
+ uint8_t direct_bdaddr_type;
+ bdaddr_t direct_bdaddr;
+ uint8_t channel_map;
+ uint8_t adv_filter_policy;
+ uint8_t inst_id;
+ uint8_t tx_power;
+} __attribute__ ((packed)) adapter_le_vsc_cp_set_multi_adv_params;
+
+/**
+*
+* CP for SUB_CMD_LE_MULTI_ADV_WRITE_ADV_DATA
+* CP for SUB_CMD_LE_MULTI_ADV_WRITE_SCAN_RSP_DATA
+*
+* ( 1 octet) subcode : SUB_CMD_LE_MULTI_ADV_WRITE_ADV_DATA
+* or SUB_CMD_LE_MULTI_ADV_WRITE_SCAN_RSP_DATA
+* ( 1 octet) data_len : per spec
+* (31 octet) data : per spec
+* ( 1 octet) inst_id : Specifies the applicability of the above parameters to an instance.
+*/
+typedef struct {
+ uint8_t subcode;
+ uint8_t data_len;
+ uint8_t data[31];
+ uint8_t inst_id;
+} __attribute__ ((packed)) adapter_le_vsc_cp_set_multi_adv_data;
+
+/**
+*
+* CP for SUB_CMD_LE_MULTI_ADV_ENB
+*
+* (1 octet) subcode : SUB_CMD_LE_MULTI_ADV_ENB
+* (1 octet) enable : When set to 1, it means enable, otherwise disable.
+* (1 octet) inst_id : Specifies the applicability of the above parameters
+* to an instance. Instance 0 has special meaning this
+* refers to std HCI instance.
+*/
+typedef struct {
+ uint8_t subcode;
+ uint8_t enable;
+ uint8_t inst_id;
+} __attribute__ ((packed)) adapter_le_vsc_cp_enable_multi_adv;
+
+/**
+*
+* RP
+*
+* (1 octet) status : Command complete status
+* (1 octet) subcode : subcode of OCF_BCM_LE_MULTI_ADV
+*/
+typedef struct {
+ uint8_t status;
+ uint8_t subcode;
+} __attribute__ ((packed)) apater_le_vsc_rp_multi_adv;
+
+
+
+/*****************************************************************************
+** CP & RP for OCF_BCM_LE_SCAN_FILTER
+**
+*/
+
+
+/* CP for SUB_CMD_LE_META_PF_ENABLE */
+typedef struct {
+ uint8_t subcode;
+ uint8_t enable;
+} __attribute__ ((packed)) adapter_le_vsc_cp_enable_scan_filter;
+
+/* RP for SUB_CMD_LE_META_PF_ENABLE */
+typedef struct {
+ uint8_t status;
+ uint8_t subcode;
+ uint8_t enable;
+} __attribute__ ((packed)) apater_le_vsc_rp_enable_scan_filter;
+
+/* CP for SUB_CMD_LE_META_PF_FEAT_SEL */
+typedef struct {
+ uint8_t subcode;
+ uint8_t action;
+ uint8_t filter_index;
+ uint16_t feature;
+ uint16_t feature_list_logic;
+ uint8_t filter_logic;
+ int8_t rssi_high_threshold;
+ uint8_t delivery_mode;
+ uint16_t onfound_timeout;
+ uint8_t onfound_timeout_cnt;
+ uint8_t rssi_low_thresh;
+ uint16_t onlost_timeout;
+} __attribute__ ((packed)) adapter_le_vsc_cp_apcf_set_filter_params;
+
+/* CP for SUB_CMD_LE_META_PF_ADDR */
+typedef struct {
+ uint8_t subcode;
+ uint8_t action;
+ uint8_t filter_index;
+ bdaddr_t bdaddr;
+ uint8_t bdaddr_type;
+} __attribute__ ((packed))adapter_le_vsc_cp_address_scan_filter;
+
+/* CP for SUB_CMD_LE_META_PF_UUID & SUB_CMD_LE_META_PF_SOL_UUID */
+typedef struct {
+ uint8_t subcode;
+ uint8_t action;
+ uint8_t filter_index;
+ uint8_t data[40]; /* UUID + UUID_MASK */
+} __attribute__ ((packed))adapter_le_vsc_cp_service_uuid_scan_filter;
+#define UUID_SCAN_FILTER_HEADER_SIZE 3
+
+#define SCAN_FILTER_DATA_MAX_LEN 29
+
+/* CP for SUB_CMD_LE_META_PF_LOCAL_NAME*/
+typedef struct {
+ uint8_t subcode;
+ uint8_t action;
+ uint8_t filter_index;
+ uint8_t name[SCAN_FILTER_DATA_MAX_LEN];
+} __attribute__ ((packed)) adapter_le_vsc_cp_local_name_scan_filter;
+#define NAME_SCAN_FILTER_HEADER_SIZE 3
+
+/* CP for SUB_CMD_LE_META_PF_MANU_DATA*/
+typedef struct {
+ uint8_t subcode;
+ uint8_t action;
+ uint8_t filter_index;
+ uint8_t data[SCAN_FILTER_DATA_MAX_LEN * 2]; /* data + mask filed */
+} __attribute__ ((packed)) adapter_le_vsc_cp_manf_data_scan_filter;
+#define MANF_DATA_SCAN_FILTER_HEADER_SIZE 3
+
+/* CP for SUB_CMD_LE_META_PF_SRVC_DATA*/
+typedef struct {
+ uint8_t subcode;
+ uint8_t action;
+ uint8_t filter_index;
+ uint8_t data[SCAN_FILTER_DATA_MAX_LEN * 2]; /* data + mask filed */
+} __attribute__ ((packed)) adapter_le_vsc_cp_service_data_scan_filter;
+#define SERVICE_DATA_SCAN_FILTER_HEADER_SIZE 3
+
+/* RP for SUB_CMD_LE_META_PF_ADDR & SUB_CMD_LE_META_PF_FEAT_SEL &
+ SUB_CMD_LE_META_PF_UUID & SUB_CMD_LE_META_PF_SOL_UUID &
+ SUB_CMD_LE_META_PF_LOCAL_NAME & SUB_CMD_LE_META_PF_MANU_DATA &
+ SUB_CMD_LE_META_PF_SRVC_DATA */
+typedef struct {
+ uint8_t status;
+ uint8_t subcode;
+ uint8_t action;
+ uint8_t available_space;
+} __attribute__ ((packed)) adapter_le_vsc_rp_apcf_set_scan_filter;
+
+
+
+
+/*****************************************************************************
+** Functions
+**
+*/
+
+/* Read supported BLE feature info from chipset */
+gboolean adapter_le_read_ble_feature_info(void);
+
+gboolean adapter_le_is_supported_multi_advertising(void);
+
+gboolean adapter_le_is_supported_offloading(void);
+
+int adapter_le_get_max_adv_instance(void);
+
+int adapter_le_get_scan_filter_size(void);
+
+gboolean adapter_le_set_multi_adv_params (adapter_le_adv_inst_info_t *p_inst,
+ adapter_le_adv_param_t *p_params);
+
+gboolean adapter_le_set_multi_adv_data(uint8_t inst_id, gboolean is_scan_rsp,
+ uint8_t data_len, uint8_t *p_data);
+
+gboolean adapter_le_enable_multi_adv (gboolean enable, uint8_t inst_id);
+
+
+gboolean adapter_le_enable_scan_filtering (gboolean enable);
+
+gboolean adapter_le_set_scan_filter_params(adapter_le_scan_filter_param_t *params);
+
+gboolean adapter_le_set_scan_filter_data(int client_if, int action,
+ int filt_type, int filter_index,
+ int company_id,
+ int company_id_mask,
+ int uuid_len, uint8_t *p_uuid,
+ int uuid_mask_len, uint8_t *p_uuid_mask,
+ gchar *string, int addr_type,
+ int data_len, uint8_t *p_data,
+ int mask_len, uint8_t *p_mask);
+gboolean adapter_le_clear_scan_filter_data(int client_if, int filter_index);
+#else /* __BROADCOM_PATCH__ */
+
+gboolean adapter_le_read_ble_feature_info(void) { return FALSE; }
+gboolean adapter_le_is_supported_multi_advertising(void) { return FALSE; }
+gboolean adapter_le_is_supported_offloading(void) { return FALSE; }
+int adapter_le_get_max_adv_instance(void) { return 0; }
+int adapter_le_get_scan_filter_size(void) { return 0; }
+
+gboolean adapter_le_set_multi_adv_params (adapter_le_adv_inst_info_t *p_inst,
+ adapter_le_adv_param_t *p_params) { return FALSE; }
+gboolean adapter_le_set_multi_adv_data(uint8_t inst_id, gboolean is_scan_rsp,
+ uint8_t data_len, uint8_t *p_data) { return FALSE; }
+gboolean adapter_le_enable_multi_adv (gboolean enable, uint8_t inst_id)
+ { return FALSE; }
+
+gboolean adapter_le_enable_scan_filtering (gboolean enable) { return FALSE; }
+gboolean adapter_le_set_scan_filter_params(adapter_le_scan_filter_param_t *params)
+ { return FALSE; }
+gboolean adapter_le_set_scan_filter_data(int client_if, int action,
+ int filt_type, int filter_index,
+ int company_id,
+ int company_id_mask,
+ int uuid_len, uint8_t *p_uuid,
+ int uuid_mask_len, uint8_t *p_uuid_mask,
+ gchar *string, int addr_type,
+ int data_len, uint8_t *p_data,
+ int mask_len, uint8_t *p_mask)
+ { return FALSE; }
+gboolean adapter_le_clear_scan_filter_data(int client_if, int filter_index)
+ { return FALSE; }
+#endif /* __BROADCOM_PATCH__ */
+#endif /* __TIZEN_PATCH__ */
#include <sys/socket.h>
#include <sys/ioctl.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/sdp.h>
-
#include <glib.h>
#include <dbus/dbus.h>
-#include <gdbus/gdbus.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+
+#include "gdbus/gdbus.h"
#include "log.h"
#include "error.h"
static int agent_call_authorize_service(struct agent_request *req,
const char *device_path,
- const char *uuid, const int fd)
+ const char *uuid)
{
- DBusMessageIter iter, dict;
struct agent *agent = req->agent;
req->msg = dbus_message_new_method_call(agent->owner, agent->path,
return -ENOMEM;
}
- dbus_message_iter_init_append(req->msg, &iter);
-
- dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
- &device_path);
-
- dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &uuid);
-
- dbus_message_iter_append_basic(&iter, DBUS_TYPE_UNIX_FD, &fd);
+ dbus_message_append_args(req->msg,
+ DBUS_TYPE_OBJECT_PATH, &device_path,
+ DBUS_TYPE_STRING, &uuid,
+ DBUS_TYPE_INVALID);
if (g_dbus_send_message_with_reply(btd_get_dbus_connection(),
req->msg, &req->call,
int agent_authorize_service(struct agent *agent, const char *path,
const char *uuid, agent_cb cb,
- void *user_data, GDestroyNotify destroy, int fd)
+ void *user_data, GDestroyNotify destroy)
{
struct agent_request *req;
int err;
req = agent_request_new(agent, AGENT_REQUEST_AUTHORIZE_SERVICE, cb,
user_data, destroy);
- err = agent_call_authorize_service(req, path, uuid, fd);
+ err = agent_call_authorize_service(req, path, uuid);
if (err < 0) {
agent_request_free(req, FALSE);
return -ENOMEM;
int agent_authorize_service(struct agent *agent, const char *path,
const char *uuid, agent_cb cb,
- void *user_data, GDestroyNotify destroy, int fd);
+ void *user_data, GDestroyNotify destroy);
int agent_request_pincode(struct agent *agent, struct btd_device *device,
agent_pincode_cb cb, gboolean secure,
#include <glib.h>
#include <sys/stat.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/sdp.h>
-#include <bluetooth/sdp_lib.h>
-
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
#include "lib/uuid.h"
+
#include "btio/btio.h"
#include "log.h"
#include "adapter.h"
}
#ifdef __TIZEN_PATCH__
-static int attribute_uuid_cmp(gconstpointer a, gconstpointer b)
+ static int attribute_uuid_cmp(gconstpointer a, gconstpointer b)
{
const struct attribute *attrib1 = a;
const bt_uuid_t *uuid = b;
- return bt_uuid_cmp(&attrib1->uuid, uuid);
+ if (attrib1->uuid.value.u16 != GATT_PRIM_SVC_UUID) {
+ return bt_uuid_cmp(&attrib1->uuid, uuid);
+ } else {
+ bt_uuid_t prim_uuid;
+ prim_uuid = att_get_uuid(attrib1->data, attrib1->len);
+
+ return bt_uuid_cmp(&prim_uuid, uuid);
+ }
}
-struct attribute *attribute_find(struct btd_adapter *adapter, bt_uuid_t *uuid)
+struct attribute *attribute_find(struct btd_adapter *adapter, const bt_uuid_t *uuid)
{
- GList *l;
+ GSList *l;
+ GList *ldata;
struct gatt_server *server;
/* Find the attrib server database for the given adapter */
server = l->data;
- l = g_list_find_custom(server->database, GUINT_TO_POINTER(uuid),
+ ldata = g_list_find_custom(server->database, GUINT_TO_POINTER(uuid),
attribute_uuid_cmp);
- if (!l)
+ if (!ldata)
return NULL;
- return l->data;
+ return ldata->data;
}
#endif
a = l->data;
- if (a->len <= offset)
+ if (a->len < offset)
return enc_error_resp(ATT_OP_READ_BLOB_REQ, handle,
ATT_ECODE_INVALID_OFFSET, pdu, len);
return 0;
}
-#ifdef __TIZEN_PATCH__
- device_set_gatt_connected(device, TRUE);
-#endif
-
if (!device_is_bonded(device, bdaddr_type)) {
char *filename;
return channel->id;
}
-static int channel_id_cmp(gconstpointer data, gconstpointer user_data)
-{
- const struct gatt_channel *channel = data;
- guint id = GPOINTER_TO_UINT(user_data);
-
- return channel->id - id;
-}
-
-gboolean attrib_channel_detach(GAttrib *attrib, guint id)
+static struct gatt_channel *find_channel(guint id)
{
- struct gatt_server *server;
- struct gatt_channel *channel;
- GError *gerr = NULL;
- GIOChannel *io;
- bdaddr_t src;
GSList *l;
- io = g_attrib_get_channel(attrib);
+ for (l = servers; l; l = g_slist_next(l)) {
+ struct gatt_server *server = l->data;
+ GSList *c;
- bt_io_get(io, &gerr, BT_IO_OPT_SOURCE_BDADDR, &src, BT_IO_OPT_INVALID);
+ for (c = server->clients; c; c = g_slist_next(c)) {
+ struct gatt_channel *channel = c->data;
- if (gerr != NULL) {
- error("bt_io_get: %s", gerr->message);
- g_error_free(gerr);
- return FALSE;
+ if (channel->id == id)
+ return channel;
+ }
}
- server = find_gatt_server(&src);
- if (server == NULL)
- return FALSE;
+ return NULL;
+}
- l = g_slist_find_custom(server->clients, GUINT_TO_POINTER(id),
- channel_id_cmp);
- if (!l)
- return FALSE;
+gboolean attrib_channel_detach(GAttrib *attrib, guint id)
+{
+ struct gatt_channel *channel;
- channel = l->data;
+ channel = find_channel(id);
+ if (channel == NULL)
+ return FALSE;
g_attrib_unregister(channel->attrib, channel->id);
channel_remove(channel);
atval, 2);
/* GAP service: device name characteristic */
- server->name_handle = 0x0006;
+ server->name_handle = 0x0003;
bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
atval[0] = GATT_CHR_PROP_READ;
put_le16(server->name_handle, &atval[1]);
put_le16(GATT_CHARAC_DEVICE_NAME, &atval[3]);
- attrib_db_add_new(server, 0x0004, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+ attrib_db_add_new(server, 0x0002, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
atval, 5);
/* GAP service: device name attribute */
ATT_NOT_PERMITTED, NULL, 0);
/* GAP service: device appearance characteristic */
- server->appearance_handle = 0x0008;
+ server->appearance_handle = 0x0005;
bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
atval[0] = GATT_CHR_PROP_READ;
put_le16(server->appearance_handle, &atval[1]);
put_le16(GATT_CHARAC_APPEARANCE, &atval[3]);
- attrib_db_add_new(server, 0x0007, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+ attrib_db_add_new(server, 0x0004, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
atval, 5);
/* GAP service: device appearance attribute */
put_le16(appearance, &atval[0]);
attrib_db_add_new(server, server->appearance_handle, &uuid, ATT_NONE,
ATT_NOT_PERMITTED, atval, 2);
+
server->gap_sdp_handle = attrib_create_sdp_new(server, 0x0001,
"Generic Access Profile");
if (server->gap_sdp_handle == 0) {
/* GATT service: primary service definition */
bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
put_le16(GENERIC_ATTRIB_PROFILE_ID, &atval[0]);
- attrib_db_add_new(server, 0x0010, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+ attrib_db_add_new(server, 0x0006, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
atval, 2);
#ifdef __TIZEN_PATCH__
/* GATT service: service changed characteristic */
- service_changed_handle = 0x0012;
- bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+ service_changed_handle = 0x0008;
+ bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
- atval[0] = GATT_CHR_PROP_READ;
+ atval[0] = GATT_CHR_PROP_INDICATE;
put_le16(service_changed_handle, &atval[1]);
put_le16(GATT_CHARAC_SERVICE_CHANGED, &atval[3]);
- put_le16(GATT_CLIENT_CHARAC_CFG_IND_BIT, atval);
- attrib_db_add_new(server, 0x0011, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
- atval, 4);
+
+ attrib_db_add_new(server, 0x0007, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+ atval, 5);
/* GATT service: service changed attribute */
bt_uuid16_create(&uuid, GATT_CHARAC_SERVICE_CHANGED);
- attrib_db_add_new(server, service_changed_handle, &uuid, ATT_NONE,
+ attrib_db_add_new(server, service_changed_handle, &uuid, ATT_NOT_PERMITTED,
ATT_NOT_PERMITTED, NULL, 0);
+
+ bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+ atval[0] = GATT_CHR_PROP_READ | GATT_CHR_PROP_WRITE;
+ atval[1] = 0;
+ attrib_db_add_new(server, 0x0009, &uuid, ATT_NONE, ATT_NONE, atval, 2);
#endif
- server->gatt_sdp_handle = attrib_create_sdp_new(server, 0x0010,
+ server->gatt_sdp_handle = attrib_create_sdp_new(server, 0x0006,
"Generic Attribute Profile");
if (server->gatt_sdp_handle == 0) {
error("Failed to register GATT service record");
/* Get the CCC value */
value = g_key_file_get_string(key_file, group, "Value", NULL);
- if (!value)
+ if (!value) {
+ /* Fix : RESOURCE_LEAK */
+ g_free(filename);
+ g_key_file_free(key_file);
return 0;
+ }
sscanf(value, "%hX", &cccval);
guint attrib_channel_attach(GAttrib *attrib);
gboolean attrib_channel_detach(GAttrib *attrib, guint id);
#ifdef __TIZEN_PATCH__
-struct attribute *attribute_find(struct btd_adapter *adapter, bt_uuid_t *uuid);
+struct attribute *attribute_find(struct btd_adapter *adapter, const bt_uuid_t *uuid);
void attrib_send_noty_ind(struct btd_device *device, GAttrib *attrib,
uint16_t handle, uint16_t desc_handle,
uint8_t *value, size_t vlen);
uint16_t send_sc_indication(uint16_t handle, uint16_t end_handle, size_t vlen,
uint8_t *pdu, size_t len);
+void attrib_send_sc_ind(struct btd_device *device, GAttrib *attrib,
+ uint16_t start_handle, uint16_t end_handle,
+ size_t vlen);
#endif
<allow own="org.bluez.Profile1"/>
<allow send_interface="org.bluez.Profile1"/>
<allow send_destination="org.bluez.Profile1"/>
+ <allow send_interface="org.bluez.HeartRateWatcher1"/>
+ <allow send_interface="org.bluez.CyclingSpeedWatcher1"/>
+ <allow send_interface="org.bluez.GattCharacteristic1"/>
+ <allow send_interface="org.bluez.GattDescriptor1"/>
<allow send_interface="org.freedesktop.DBus.ObjectManager"/>
+ <allow send_interface="org.freedesktop.DBus.Properties"/>
</policy>
<policy at_console="true">
[Service]
Type=dbus
BusName=org.bluez
-ExecStart=@libexecdir@/bluetoothd -E
+ExecStart=@libexecdir@/bluetoothd
NotifyAccess=main
#WatchdogSec=10
#Restart=on-failure
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
LimitNPROC=1
-SmackProcessLabel=User
[Install]
WantedBy=bluetooth.target
#include <glib.h>
#include <dbus/dbus.h>
-#include <gdbus/gdbus.h>
+
+#include "gdbus/gdbus.h"
#include "log.h"
#include <dirent.h>
#include <time.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/sdp.h>
-#include <bluetooth/sdp_lib.h>
-
#include <glib.h>
#include <dbus/dbus.h>
-#include <gdbus/gdbus.h>
-
-#include "log.h"
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
+#include "log.h"
#include "src/shared/util.h"
#include "src/shared/att.h"
#include "src/shared/queue.h"
#include "src/shared/gatt-db.h"
#include "src/shared/gatt-client.h"
+#include "src/shared/gatt-server.h"
#include "btio/btio.h"
#include "lib/mgmt.h"
#include "attrib/att.h"
#include "hcid.h"
#include "adapter.h"
+#include "gatt-database.h"
#include "attrib/gattrib.h"
#include "attio.h"
#ifdef __TIZEN_PATCH__
#include "eir.h"
-#include "attrib/client.h"
#endif
#include "device.h"
#include "gatt-client.h"
#include "attrib-server.h"
#ifdef __TIZEN_PATCH__
#include "sdp-xml.h"
-#include "gatt.h"
#endif
#define IO_CAPABILITY_NOINPUTNOOUTPUT 0x03
#endif
static DBusConnection *dbus_conn = NULL;
-unsigned service_state_cb_id;
+static unsigned service_state_cb_id;
struct btd_disconnect_data {
guint id;
bool svc_resolved;
};
+struct csrk_info {
+ uint8_t key[16];
+ uint32_t counter;
+};
+
#ifdef __TIZEN_PATCH__
typedef enum {
DEV_PAIRED_NONE = 0,
GSList *uuids;
GSList *primaries; /* List of primary services */
GSList *services; /* List of btd_service */
-#ifdef __TIZEN_PATCH__
- GSList *gatt_services; /* List of GATT Services */
-#endif
GSList *pending; /* Pending services */
GSList *watches; /* List of disconnect_data */
gboolean temporary;
GAttrib *attrib;
GSList *attios;
GSList *attios_offline;
- guint attachid; /* Attrib server attach */
struct bt_att *att; /* The new ATT transport */
uint16_t att_mtu; /* The ATT MTU */
*/
struct gatt_db *db; /* GATT db cache */
struct bt_gatt_client *client; /* GATT client instance */
+ struct bt_gatt_server *server; /* GATT server instance */
struct btd_gatt_client *client_dbus;
struct bearer_state bredr_state;
struct bearer_state le_state;
+ struct csrk_info *local_csrk;
+ struct csrk_info *remote_csrk;
+
sdp_list_t *tmp_records;
time_t bredr_seen;
GIOChannel *att_io;
guint store_id;
#ifdef __TIZEN_PATCH__
+ guint attachid; /* Attrib server attach */
bool legacy_pairing;
char *manufacturer_data;
int manufacturer_data_len;
uint8_t last_bdaddr_type;
gboolean le_auto_connect;
guint auto_id;
+ gboolean ipsp_connected; /* IPSP Connection state */
#endif
};
return NULL;
}
-static GSList *find_service_with_gatt_handles(GSList *list,
- uint16_t start_handle,
- uint16_t end_handle)
+static GSList *find_service_with_uuid(GSList *list, char *uuid)
{
GSList *l;
- uint16_t svc_start, svc_end;
for (l = list; l != NULL; l = g_slist_next(l)) {
struct btd_service *service = l->data;
+ struct btd_profile *profile = btd_service_get_profile(service);
- if (!btd_service_get_gatt_handles(service, &svc_start,
- &svc_end))
- continue;
-
- if (svc_start == start_handle && svc_end == end_handle)
+ if (bt_uuid_strcmp(profile->remote_uuid, uuid) == 0)
return l;
}
static void update_technologies(GKeyFile *file, struct btd_device *dev)
{
- const char *list[2] = {NULL, NULL};
+ const char *list[2];
size_t len = 0;
if (dev->bredr)
list, len);
}
+static void store_csrk(struct csrk_info *csrk, GKeyFile *key_file,
+ const char *group)
+{
+ char key[33];
+ int i;
+
+ for (i = 0; i < 16; i++)
+ sprintf(key + (i * 2), "%2.2X", csrk->key[i]);
+
+ g_key_file_set_string(key_file, group, "Key", key);
+ g_key_file_set_integer(key_file, group, "Counter", csrk->counter);
+}
+
#ifdef __TIZEN_PATCH__
static char *manufacturer_data2str(char *data, int size)
{
g_key_file_remove_group(key_file, "DeviceID", NULL);
}
+ if (device->local_csrk)
+ store_csrk(device->local_csrk, key_file, "LocalSignatureKey");
+
+ if (device->remote_csrk)
+ store_csrk(device->remote_csrk, key_file, "RemoteSignatureKey");
+
create_file(filename, S_IRUSR | S_IWUSR);
str = g_key_file_to_data(key_file, &length, NULL);
bt_gatt_client_unref(device->client);
device->client = NULL;
+ /*
+ * TODO: Once GATT over BR/EDR is properly supported, we should check
+ * the bonding state for the correct bearer based on the transport over
+ * which GATT is being done.
+ */
if (!device->le_state.bonded)
gatt_db_clear(device->db);
}
+#ifndef __TIZEN_PATCH__
+static void gatt_server_cleanup(struct btd_device *device)
+{
+ if (!device->server)
+ return;
+ bt_gatt_server_unref(device->server);
+ device->server = NULL;
+}
+#endif
static void attio_cleanup(struct btd_device *device)
{
+#ifdef __TIZEN_PATCH__
if (device->attachid) {
attrib_channel_detach(device->attrib, device->attachid);
device->attachid = 0;
}
+#endif
if (device->att_disconn_id)
bt_att_unregister_disconnect(device->att,
}
gatt_client_cleanup(device);
+#ifndef __TIZEN_PATCH__
+ gatt_server_cleanup(device);
+#endif
if (device->att) {
bt_att_unref(device->att);
if (device->attrib) {
GAttrib *attrib = device->attrib;
+
device->attrib = NULL;
g_attrib_cancel_all(attrib);
g_attrib_unref(attrib);
struct btd_device *device = req->device;
struct btd_adapter *adapter = device->adapter;
+ DBG("");
+
bt_cancel_discovery(btd_adapter_get_address(adapter), &device->bdaddr);
attio_cleanup(device);
g_slist_free_full(device->attios, g_free);
g_slist_free_full(device->attios_offline, g_free);
g_slist_free_full(device->svc_callbacks, svc_dev_remove);
-#ifdef __TIZEN_PATCH__
- g_slist_free_full(device->gatt_services, g_free);
-#endif
attio_cleanup(device);
if (device->eir_uuids)
g_slist_free_full(device->eir_uuids, g_free);
+ g_free(device->local_csrk);
+ g_free(device->remote_csrk);
g_free(device->path);
g_free(device->alias);
free(device->modalias);
return device->trusted;
}
-bool device_is_service_blocked(struct btd_device *device, const char *uuid)
-{
- GSList *l;
- bool block = false;
-
- for (l = device->services; l; l = g_slist_next(l)) {
- struct btd_service *service = l->data;
- struct btd_profile *p = btd_service_get_profile(service);
- const char *p_uuid;
-
- p_uuid = p->auth_uuid ? p->auth_uuid : p->local_uuid;
- if (!p_uuid)
- continue;
-
- if (strcasecmp(uuid, p_uuid))
- continue;
-
- if (!btd_service_is_blocked(service))
- return false;
-
- block = true;
- }
-
- /* Every service matching is blocked
- */
- return block;
-}
-
static gboolean dev_property_get_address(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *data)
{
}
#ifdef __TIZEN_PATCH__
-#if 0 /* caller of below function exists but currently commented */
static uint8_t device_get_connected_state(struct btd_device *device)
{
if (device->bredr_state.connected && device->le_state.connected)
else
return DEV_CONNECTED_NONE;
}
-#endif
static gboolean dev_property_get_payload(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *data)
return TRUE;
}
+static gboolean dev_property_get_ipsp_conn_state(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct btd_device *dev = data;
+ dbus_bool_t ipsp_connected;
+
+ if (dev->ipsp_connected)
+ ipsp_connected = TRUE;
+ else
+ ipsp_connected = FALSE;
+
+ dbus_message_iter_append_basic(iter,
+ DBUS_TYPE_BOOLEAN, &ipsp_connected);
+
+ return TRUE;
+}
#endif
static gboolean dev_property_get_connected(const GDBusPropertyTable *property,
{
struct btd_device *dev = data;
-#if 0 /* Need to discuss with SLP team */
-/* #ifdef __TIZEN_PATCH__ */
+#ifdef __TIZEN_PATCH__
uint8_t connected = device_get_connected_state(dev);
- dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE,
- &connected);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE, &connected);
#else
dbus_bool_t connected;
}
#ifdef __TIZEN_PATCH__
-static gboolean dev_property_get_prim_services(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
-{
- struct btd_device *device = data;
- gchar *attrib_data;
- DBusMessageIter array;
- GSList *l;
- dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
- DBUS_TYPE_STRING_AS_STRING,
- &array);
-
- for (l = device->primaries; l != NULL; l = l->next) {
- struct gatt_primary *primary = l->data;
-
- DBG("start handle = 0x%04x, end grp handle = 0x%04x "
- "uuid: %s\n", primary->range.start, primary->range.end,
- primary->uuid);
-
- /*
- * attrib_data contains the start_handle followed by '#', end
- * handle followed by '#' and 128 bit UUID as string. Attribute
- * data represents one service.
- */
-
- attrib_data = g_strdup_printf("%04x""#""%04x""#""%s",
- primary->range.start,primary->range.end,primary->uuid);
-
- DBG("attrib_data string: %s",attrib_data);
-
- dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING,
- &attrib_data);
-
- }
-
- dbus_message_iter_close_container(iter, &array);
-
- return TRUE;
-}
static gboolean property_get_flag(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *user_data)
return FALSE;
}
-#ifdef BLUEZ5_GATT_CLIENT
-gboolean disconnect_le_device(gpointer user_data)
-{
- struct btd_device *device = user_data;
- device->disconn_timer = 0;
-
- if (device->le_state.connected) {
- if (!btd_adapter_disconnect_device(device->adapter,
- &device->bdaddr, BDADDR_LE_PUBLIC))
- return TRUE;
- }
-
- return FALSE;
-}
-#endif
-
int device_block(struct btd_device *device, gboolean update_only)
{
int err = 0;
return 0;
}
-static void discover_services_req_exit(DBusConnection *conn, void *user_data)
+static void browse_request_exit(DBusConnection *conn, void *user_data)
{
struct browse_req *req = user_data;
- DBG("DiscoverServices requestor exited");
+ DBG("Requestor exited");
browse_request_cancel(req);
}
for (l = dev->services; l != NULL; l = g_slist_next(l)) {
service = l->data;
-
- if (!btd_service_get_auto_connect(service))
- continue;
-
p = btd_service_get_profile(service);
#ifdef __TIZEN_PATCH__
le_last = NVAL_TIME;
}
+ if (le_last == NVAL_TIME && bredr_last == NVAL_TIME)
+ return dev->bdaddr_type;
+
if (dev->bredr && (!dev->le || le_last == NVAL_TIME))
return BDADDR_BREDR;
return btd_error_failed(msg, strerror(-err));
}
+static void store_services(struct btd_device *device)
+{
+ struct btd_adapter *adapter = device->adapter;
+ char filename[PATH_MAX];
+ char src_addr[18], dst_addr[18];
+ uuid_t uuid;
+ char *prim_uuid;
+ GKeyFile *key_file;
+ GSList *l;
+ char *data;
+ gsize length = 0;
+
+ if (device_address_is_private(device)) {
+ warn("Can't store services for private addressed device %s",
+ device->path);
+ return;
+ }
+
+ sdp_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
+ prim_uuid = bt_uuid2string(&uuid);
+ if (prim_uuid == NULL)
+ return;
+
+ ba2str(btd_adapter_get_address(adapter), src_addr);
+ ba2str(&device->bdaddr, dst_addr);
+
+ snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/attributes", src_addr,
+ dst_addr);
+ key_file = g_key_file_new();
+
+ for (l = device->primaries; l; l = l->next) {
+ struct gatt_primary *primary = l->data;
+ char handle[6], uuid_str[33];
+ int i;
+
+ sprintf(handle, "%hu", primary->range.start);
+
+ bt_string2uuid(&uuid, primary->uuid);
+ sdp_uuid128_to_uuid(&uuid);
+
+ switch (uuid.type) {
+ case SDP_UUID16:
+ sprintf(uuid_str, "%4.4X", uuid.value.uuid16);
+ break;
+ case SDP_UUID32:
+ sprintf(uuid_str, "%8.8X", uuid.value.uuid32);
+ break;
+ case SDP_UUID128:
+ for (i = 0; i < 16; i++)
+ sprintf(uuid_str + (i * 2), "%2.2X",
+ uuid.value.uuid128.data[i]);
+ break;
+ default:
+ uuid_str[0] = '\0';
+ }
+
+ g_key_file_set_string(key_file, handle, "UUID", prim_uuid);
+ g_key_file_set_string(key_file, handle, "Value", uuid_str);
+ g_key_file_set_integer(key_file, handle, "EndGroupHandle",
+ primary->range.end);
+ }
+
+ data = g_key_file_to_data(key_file, &length, NULL);
+ if (length > 0) {
+ create_file(filename, S_IRUSR | S_IWUSR);
+ g_file_set_contents(filename, data, length, NULL);
+ }
+
+ free(prim_uuid);
+ g_free(data);
+ g_key_file_free(key_file);
+}
+
+static void browse_request_complete(struct browse_req *req, uint8_t bdaddr_type,
+ int err)
+{
+ struct btd_device *dev = req->device;
+ DBusMessage *reply = NULL;
+
+ if (!req->msg)
+ goto done;
+
+ if (dbus_message_is_method_call(req->msg, DEVICE_INTERFACE, "Pair")) {
+ if (!device_is_paired(dev, bdaddr_type)) {
+ reply = btd_error_failed(req->msg, "Not paired");
+ goto done;
+ }
+
+ if (dev->pending_paired) {
+ g_dbus_emit_property_changed(dbus_conn, dev->path,
+ DEVICE_INTERFACE, "Paired");
+ dev->pending_paired = false;
+ }
+
+ /* Disregard browse errors in case of Pair */
+ reply = g_dbus_create_reply(req->msg, DBUS_TYPE_INVALID);
+ goto done;
+ }
+
+ if (err) {
+ reply = btd_error_failed(req->msg, strerror(-err));
+ goto done;
+ }
+
+ if (dbus_message_is_method_call(req->msg, DEVICE_INTERFACE, "Connect"))
+ reply = dev_connect(dbus_conn, req->msg, dev);
+ else if (dbus_message_is_method_call(req->msg, DEVICE_INTERFACE,
+ "ConnectProfile"))
+ reply = connect_profile(dbus_conn, req->msg, dev);
+ else
+ reply = g_dbus_create_reply(req->msg, DBUS_TYPE_INVALID);
+
+done:
+ if (reply)
+ g_dbus_send_message(dbus_conn, reply);
+
+ browse_request_free(req);
+}
+
static void device_svc_resolved(struct btd_device *dev, uint8_t bdaddr_type,
int err)
{
struct bearer_state *state = get_state(dev, bdaddr_type);
- DBusMessage *reply;
struct browse_req *req = dev->browse;
DBG("%s err %d", dev->path, err);
state->svc_resolved = true;
- dev->browse = NULL;
/* Disconnection notification can happen before this function
* gets called, so don't set svc_refreshed for a disconnected
if (!dev->temporary)
store_device_info(dev);
- if (!req || !req->msg)
- return;
-
- if (dbus_message_is_method_call(req->msg, DEVICE_INTERFACE,
- "Pair")) {
- g_dbus_send_reply(dbus_conn, req->msg, DBUS_TYPE_INVALID);
- return;
- }
-
- if (err) {
- reply = btd_error_failed(req->msg, strerror(-err));
- g_dbus_send_message(dbus_conn, reply);
- return;
- }
+ if (bdaddr_type != BDADDR_BREDR && err == 0)
+ store_services(dev);
- if (dbus_message_is_method_call(req->msg, DEVICE_INTERFACE, "Connect"))
- reply = dev_connect(dbus_conn, req->msg, dev);
- else if (dbus_message_is_method_call(req->msg, DEVICE_INTERFACE,
- "ConnectProfile"))
- reply = connect_profile(dbus_conn, req->msg, dev);
- else
+ if (!req)
return;
- dbus_message_unref(req->msg);
- req->msg = NULL;
-
- if (reply)
- g_dbus_send_message(dbus_conn, reply);
+ dev->browse = NULL;
+ browse_request_complete(req, bdaddr_type, err);
}
static struct bonding_req *bonding_request_new(DBusMessage *msg,
#ifdef __TIZEN_PATCH__
if (((conn_type == DEV_CONN_DEFAULT && bdaddr_type != BDADDR_BREDR) ||
- (connect_le)) && !device->le_state.connected)
+ (connect_le)))
bonding = bonding_request_new(msg, device, bdaddr_type, agent);
else
bonding = bonding_request_new(msg, device, BDADDR_BREDR, agent);
#ifdef __TIZEN_PATCH__
if (((conn_type == DEV_CONN_DEFAULT && bdaddr_type != BDADDR_BREDR) ||
(connect_le)) && !device->le_state.connected)
- err = device_connect_le(device);
- else
- err = adapter_create_bonding(adapter, &device->bdaddr,
+ err = device_connect_le(device);
+ else if (connect_le) /* Send bonding request if LE is already connected*/
+ err = adapter_create_bonding(adapter, &device->bdaddr,
+ bdaddr_type,
+ IO_CAPABILITY_NOINPUTNOOUTPUT);
+ else
+ err = adapter_create_bonding(adapter, &device->bdaddr,
BDADDR_BREDR, io_cap);
#else
if (bdaddr_type != BDADDR_BREDR) {
}
#ifdef __TIZEN_PATCH__
+#if 0 // Not used
static DBusMessage *read_rssi(DBusConnection *conn, DBusMessage *msg,
void *user_data)
{
else
return dbus_message_new_method_return(msg);
}
-
-static DBusMessage *get_prim_services(DBusConnection *conn,
- DBusMessage *msg, void *user_data)
-{
- struct btd_device *device = user_data;
- DBusMessage *reply;
- const char *device_path;
- 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);
- device_path = device_get_path(device);
-
- for (l = device->primaries; l != NULL; l = l->next) {
- struct gatt_primary *primary = l->data;
- gchar* attrib_data;
-
- /*
- * attrib_data contains the start_handle followed by '#', end
- * handle followed by '#' and 128 bit UUID as string. Attribute
- * data represents one service.
- */
-
- attrib_data = g_strdup_printf("%s/service%04x", device_path,
- primary->range.start);
-
- DBG("attrib_data string: %s", attrib_data);
-
- dbus_message_iter_append_basic(&array_iter,
- DBUS_TYPE_OBJECT_PATH, &attrib_data);
-
- }
-
- dbus_message_iter_close_container(&iter, &array_iter);
-
- return reply;
-}
+#endif
static DBusMessage *discover_services(DBusConnection *conn,
DBusMessage *msg, void *user_data)
return dbus_message_new_method_return(msg);
}
-
+#ifndef __TIZEN_PATCH__
+/* Its not used */
static DBusMessage *write_auth_payload_timeout(DBusConnection *conn,
DBusMessage *msg, void *user_data)
{
else
return dbus_message_new_method_return(msg);
}
-
+#endif
void device_set_attrib(struct btd_device *device, guint attachid, GAttrib *attrib)
{
if (device == NULL) {
* simultaneously. And bdaddr_type is not supporting both BREDR and LE
* type. So, device for LE is created when connect_le() called.
*/
- if (device->bdaddr_type == BDADDR_BREDR)
- device = btd_adapter_get_device(device->adapter, &device->bdaddr, BDADDR_LE_PUBLIC);
+ if (device->bdaddr_type == BDADDR_BREDR) {
+ if(device->le)
+ device->bdaddr_type = BDADDR_LE_PUBLIC;
+ else
+ device = btd_adapter_get_device(device->adapter,
+ &device->bdaddr, BDADDR_LE_PUBLIC);
+ }
if (device->gatt_connected)
return btd_error_already_connected(msg);
if (device->bdaddr_type == BDADDR_BREDR)
return btd_error_not_supported(msg);
- if (device->le_auto_connect && !device->gatt_connected) {
- DBG("le_auto_connect : %d, gatt_connected : %d, attrib : %p",
- device->le_auto_connect, device->gatt_connected, device->attrib);
+ if (device->le_auto_connect && !device->le_state.connected) {
+ DBG("le_auto_connect : %d, le_connected : %d, attrib : %p",
+ device->le_auto_connect,
+ device->le_state.connected,
+ device->attrib);
DBG("Cancel LE auto connection");
btd_adapter_disable_le_auto_connect(device->adapter);
device->le_auto_connect = FALSE;
+
return dbus_message_new_method_return(msg);
}
- if(!device->gatt_connected || !device->attrib)
+ if (!device->le_state.connected)
return btd_error_not_connected(msg);
disconnect_all(device);
+ /*
+ * Current bt_adapter_start_device_discovery() cannot scan BREDR and LE
+ * simultaneously. And bdaddr_type is not supporting both BREDR and LE
+ * type. So, bdaddr_type is returned to bredr after disconnect le.
+ */
+ if(device->bredr)
+ device->bdaddr_type = BDADDR_BREDR;
+
return dbus_message_new_method_return(msg);
}
+static DBusMessage *connect_ipsp(DBusConnection *conn, DBusMessage *msg,
+ void *user_data)
+{
+ struct btd_device *device = user_data;
+
+ DBG("bdaddr_type %d", device->bdaddr_type);
+
+ if (device->bdaddr_type == BDADDR_BREDR) {
+ if(device->le)
+ device->bdaddr_type = BDADDR_LE_PUBLIC;
+ else
+ device = btd_adapter_get_device(device->adapter,
+ &device->bdaddr, BDADDR_LE_PUBLIC);
+ }
+
+ if (device->ipsp_connected)
+ return btd_error_already_connected(msg);
+
+ /* Initiate Connection for 6Lowan*/
+ if (btd_adapter_connect_ipsp(device->adapter, &device->bdaddr,
+ device->bdaddr_type) != 0)
+ return btd_error_failed(msg, "ConnectFailed");
+
+ return dbus_message_new_method_return(msg);;
+}
+
+static DBusMessage *disconnect_ipsp(DBusConnection *conn, DBusMessage *msg,
+ void *user_data)
+{
+ struct btd_device *device = user_data;
+ DBG("bdaddr_type %d", device->bdaddr_type);
+
+ if (device->bdaddr_type == BDADDR_BREDR)
+ return btd_error_not_supported(msg);
+
+ if (!device->ipsp_connected)
+ return btd_error_not_connected(msg);
+
+ /* Disconnect the 6Lowpan connection */
+ if (btd_adapter_disconnect_ipsp(device->adapter, &device->bdaddr,
+ device->bdaddr_type) != 0)
+ return btd_error_failed(msg, "DisconnectFailed");
+
+ /* TODO: Handle disconnection of GATT connection, If the connection
+ * is established as part of IPSP connection. */
+
+ return dbus_message_new_method_return(msg);;
+}
+
static DBusMessage *is_connected_profile(DBusConnection *conn, DBusMessage *msg,
void *user_data)
{
#endif
{ GDBUS_METHOD("CancelPairing", NULL, NULL, cancel_pairing) },
#ifdef __TIZEN_PATCH__
+#if 0 // Not used
{ GDBUS_METHOD("ReadRSSI", NULL, NULL, read_rssi) },
{ GDBUS_METHOD("L2capConnParamUpdate",
GDBUS_ARGS({ "interval_min", "u" },
{ "interval_max", "u" }, { "latency", "u" },
{ "time_out", "u" }), NULL,
l2cap_conn_param_update) },
- { GDBUS_METHOD("ConnectLE",
- GDBUS_ARGS({ "auto_connect", "b"}),
- NULL, connect_le) },
- { GDBUS_METHOD("DisconnectLE", NULL, NULL, disconnect_le) },
{ GDBUS_METHOD("WritePayloadTimeout",
GDBUS_ARGS({"auth_payload_timeout", "u"}),
NULL, write_auth_payload_timeout)},
{ GDBUS_METHOD("ReadPayloadTimeout", NULL,
NULL, read_auth_payload_timeout)},
+#endif
+ { GDBUS_METHOD("ConnectLE",
+ GDBUS_ARGS({ "auto_connect", "b"}),
+ NULL, connect_le) },
+ { GDBUS_METHOD("DisconnectLE", NULL, NULL, disconnect_le) },
{ GDBUS_METHOD("IsConnectedProfile", GDBUS_ARGS({ "UUID", "s" }),
GDBUS_ARGS({ "IsConnected", "b" }), is_connected_profile)},
{ GDBUS_METHOD("LeConnUpdate",
{ "interval_max", "u" }, { "latency", "u" },
{ "time_out", "u" }), NULL,
le_conn_update) },
- { GDBUS_METHOD("GetPrimServices",
- NULL, GDBUS_ARGS({ "Services", "ao" }),
- get_prim_services) },
{ GDBUS_ASYNC_METHOD("DiscoverServices",
GDBUS_ARGS({ "pattern", "s" }), NULL,
discover_services) },
{ GDBUS_METHOD("CancelDiscovery", NULL, NULL, cancel_discover) },
+ { GDBUS_ASYNC_METHOD("ConnectIpsp", NULL, NULL, connect_ipsp) },
+ { GDBUS_ASYNC_METHOD("DisconnectIpsp", NULL, NULL, disconnect_ipsp) },
#endif
{ }
};
{ "Blocked", "b", dev_property_get_blocked, dev_property_set_blocked },
{ "LegacyPairing", "b", dev_property_get_legacy },
{ "RSSI", "n", dev_property_get_rssi, NULL, dev_property_exists_rssi },
-#if 0 /* Need to discuss with SLP team */
-/* #ifdef __TIZEN_PATCH__ */
+#ifdef __TIZEN_PATCH__
{ "Connected", "y", dev_property_get_connected },
#else
{ "Connected", "b", dev_property_get_connected },
#ifdef __TIZEN_PATCH__
/* To handle Failed Legacy Pairing when initiated from Remote device*/
{ "LegacyPaired", "b", dev_property_get_paired },
- { "Services", "as", dev_property_get_prim_services },
{ "Flag", "q", property_get_flag },
{ "ManufacturerDataLen", "q", property_get_manufacturer_data_len },
{ "ManufacturerData", "ay", property_get_manufacturer_data },
{ "GattConnected", "b", dev_property_get_gatt_connected },
{ "PayloadTimeout", "q", dev_property_get_payload},
{ "LastAddrType", "y", dev_property_get_last_addr_type},
-
+ { "IpspConnected", "b", dev_property_get_ipsp_conn_state },
#endif
{ }
};
state->connected = true;
+#ifndef __TIZEN_PATCH__
if (dev->le_state.connected && dev->bredr_state.connected)
return;
-#ifndef __TIZEN_PATCH__
g_dbus_emit_property_changed(dbus_conn, dev->path, DEVICE_INTERFACE,
"Connected");
#else
btd_adapter_remove_bonding(device->adapter, &device->bdaddr,
bdaddr_type);
+#ifndef __TIZEN_PATCH__
if (device->bredr_state.connected || device->le_state.connected)
return;
-#ifndef __TIZEN_PATCH__
g_dbus_emit_property_changed(dbus_conn, device->path,
DEVICE_INTERFACE, "Connected");
#else
return str;
}
+static struct csrk_info *load_csrk(GKeyFile *key_file, const char *group)
+{
+ struct csrk_info *csrk;
+ char *str;
+ int i;
+
+ str = g_key_file_get_string(key_file, group, "Key", NULL);
+ if (!str)
+ return NULL;
+
+ csrk = g_new0(struct csrk_info, 1);
+
+ for (i = 0; i < 16; i++) {
+ if (sscanf(str + (i * 2), "%2hhx", &csrk->key[i]) != 1)
+ goto fail;
+ }
+
+ /*
+ * In case of older storage this will return 0 which is fine since it
+ * didn't support signing at that point the counter should never have
+ * been used.
+ */
+ csrk->counter = g_key_file_get_integer(key_file, group, "Counter",
+ NULL);
+ g_free(str);
+
+ return csrk;
+
+fail:
+ g_free(str);
+ g_free(csrk);
+ return NULL;
+}
+
static void load_info(struct btd_device *device, const char *local,
const char *peer, GKeyFile *key_file)
{
error("Unknown LE device technology");
g_free(str);
+
+ device->local_csrk = load_csrk(key_file, "LocalSignatureKey");
+ device->remote_csrk = load_csrk(key_file, "RemoteSignatureKey");
}
g_strfreev(techno);
GSList *prim_list, int psm)
{
device->primaries = g_slist_concat(device->primaries, prim_list);
-#ifdef __TIZEN_PATCH__
- device->gatt_services = attrib_client_register(dbus_conn, device, psm, NULL,
- prim_list);
-#endif
}
static void add_primary(struct gatt_db_attribute *attr, void *user_data)
static void device_add_uuids(struct btd_device *device, GSList *uuids)
{
GSList *l;
+ bool changed = false;
for (l = uuids; l != NULL; l = g_slist_next(l)) {
GSList *match = g_slist_find_custom(device->uuids, l->data,
if (match)
continue;
+ changed = true;
device->uuids = g_slist_insert_sorted(device->uuids,
g_strdup(l->data),
bt_uuid_strcmp);
}
- g_dbus_emit_property_changed(dbus_conn, device->path,
+ if (changed)
+ g_dbus_emit_property_changed(dbus_conn, device->path,
DEVICE_INTERFACE, "UUIDs");
}
-static void store_services(struct btd_device *device);
-
struct gatt_probe_data {
struct btd_device *dev;
bool all_services;
GSList *uuids;
struct gatt_db_attribute *cur_attr;
char cur_uuid[MAX_LEN_UUID_STR];
- uint16_t start_handle, end_handle;
- GSList *valid_services;
};
static bool device_match_profile(struct btd_device *device,
if (!p->remote_uuid || bt_uuid_strcmp(p->remote_uuid, data->cur_uuid))
return;
- service = service_create_gatt(data->dev, p, data->start_handle,
- data->end_handle);
+ service = service_create(data->dev, p);
if (!service)
return;
return;
}
- data->dev->services = g_slist_append(data->dev->services, service);
-
- if (data->all_services)
- data->valid_services = g_slist_append(data->valid_services,
- service);
-}
-
-static bool match_existing_service(struct gatt_probe_data *data)
-{
- struct btd_device *dev = data->dev;
- struct btd_service *service;
- struct btd_profile *profile;
- uint16_t start, end;
- GSList *l, *tmp;
-
- /*
- * Check if the profiles should be probed for the service in the
- * database.
- */
- for (l = dev->services; l != NULL;) {
- service = l->data;
- profile = btd_service_get_profile(service);
-
- /* If this is local or non-GATT based service, then skip. */
- if (!profile->remote_uuid ||
- !btd_service_get_gatt_handles(service, &start, &end)) {
- l = g_slist_next(l);
- continue;
- }
-
- /* Skip this service if the handle ranges don't overlap. */
- if (start > data->end_handle || end < data->start_handle) {
- l = g_slist_next(l);
- continue;
- }
-
- /*
- * If there is an exact match, then there's no need to probe the
- * profiles. An exact match is when the service handles AND the
- * service UUID match.
- */
- if (start == data->start_handle && end == data->end_handle &&
- !bt_uuid_strcmp(profile->remote_uuid, data->cur_uuid)) {
- if (data->all_services)
- data->valid_services = g_slist_append(
- data->valid_services, service);
- return true;
- }
-
- /*
- * The handles overlap but there is no exact match. This means
- * that the service is no longer valid. Remove it.
- *
- * TODO: The following code is fairly inefficient, especially
- * when we consider all the extra searches that we're already
- * doing. Consider changing the services list to a GList.
- */
- tmp = l->next;
- dev->services = g_slist_delete_link(dev->services, l);
- dev->pending = g_slist_remove(dev->pending, service);
- service_remove(service);
-
- l = tmp;
- }
+ /* Mark service as claimed */
+ gatt_db_service_set_claimed(data->cur_attr, true);
- /* No match found */
- return false;
+ data->dev->services = g_slist_append(data->dev->services, service);
}
static void dev_probe_gatt_profile(struct gatt_db_attribute *attr,
bt_uuid_t uuid;
GSList *l = NULL;
- gatt_db_attribute_get_service_data(attr, &data->start_handle,
- &data->end_handle, NULL,
- &uuid);
+ gatt_db_attribute_get_service_uuid(attr, &uuid);
bt_uuid_to_string(&uuid, data->cur_uuid, sizeof(data->cur_uuid));
data->cur_attr = attr;
- /* Don't probe the profiles if a matching service already exists. */
- if (!match_existing_service(data))
- btd_profile_foreach(dev_probe_gatt, data);
-
- if (data->all_services) {
+ /*
+ * If we're probing for all services, store the UUID since device->uuids
+ * was cleared.
+ */
+ if (data->all_services)
data->uuids = g_slist_append(data->uuids,
g_strdup(data->cur_uuid));
+
+ /* Don't probe the profiles if a matching service already exists. */
+ if (find_service_with_uuid(data->dev->services, data->cur_uuid)) {
+ /* Mark the service as claimed by the existing profile. */
+ gatt_db_service_set_claimed(data->cur_attr, true);
return;
}
+ btd_profile_foreach(dev_probe_gatt, data);
+
+ if (data->all_services)
+ return;
+
l = g_slist_append(l, g_strdup(data->cur_uuid));
device_add_uuids(data->dev, l);
}
g_slist_free_full(data.uuids, g_free);
}
-static void remove_invalid_services(struct gatt_probe_data *data)
-{
- struct btd_device *dev = data->dev;
- struct btd_service *service;
- GSList *l, *tmp;
-
- for (l = dev->services; l != NULL;) {
- service = l->data;
-
- if (g_slist_find(data->valid_services, service)) {
- l = g_slist_next(l);
- continue;
- }
-
- /* Service no longer valid, so remove it */
- tmp = l->next;
- dev->services = g_slist_delete_link(dev->services, l);
- dev->pending = g_slist_remove(dev->pending, service);
- service_remove(service);
-
- l = tmp;
- }
-}
-
static void device_probe_gatt_profiles(struct btd_device *device)
{
struct gatt_probe_data data;
gatt_db_foreach_service(device->db, NULL, dev_probe_gatt_profile,
&data);
+
device_add_uuids(device, data.uuids);
g_slist_free_full(data.uuids, g_free);
-
- remove_invalid_services(&data);
- g_slist_free(data.valid_services);
}
static void device_accept_gatt_profiles(struct btd_device *device)
static void device_remove_gatt_profile(struct btd_device *device,
struct gatt_db_attribute *attr)
{
- uint16_t start, end;
struct btd_service *service;
+ bt_uuid_t uuid;
+ char uuid_str[MAX_LEN_UUID_STR];
GSList *l;
- gatt_db_attribute_get_service_handles(attr, &start, &end);
+ gatt_db_attribute_get_service_uuid(attr, &uuid);
+ bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
- l = find_service_with_gatt_handles(device->services, start, end);
+ l = find_service_with_uuid(device->services, uuid_str);
if (!l)
return;
{
struct btd_device *device = user_data;
GSList *new_service = NULL;
+ bt_uuid_t uuid;
+ char uuid_str[MAX_LEN_UUID_STR];
uint16_t start, end;
GSList *l;
if (!bt_gatt_client_is_ready(device->client))
return;
- gatt_db_attribute_get_service_handles(attr, &start, &end);
+ gatt_db_attribute_get_service_data(attr, &start, &end, NULL, &uuid);
+ bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
DBG("start: 0x%04x, end: 0x%04x", start, end);
if (!new_service)
return;
+ l = find_service_with_uuid(device->services, uuid_str);
+
device_register_primaries(device, new_service, -1);
- device_probe_gatt_profile(device, attr);
+ /*
+ * If the profile was probed for the first time then call accept on
+ * the service.
+ */
+ if (!l) {
+ l = find_service_with_uuid(device->services, uuid_str);
+ if (l)
+ service_accept(l->data);
+ }
- l = find_service_with_gatt_handles(device->services, start, end);
- if (l)
- service_accept(l->data);
+ device_probe_gatt_profile(device, attr);
- store_services(device);
+ store_device_info(device);
btd_gatt_client_service_added(device->client_dbus, attr);
}
struct gatt_primary *prim;
uint16_t start, end;
- if (!bt_gatt_client_is_ready(device->client))
- return;
+ /*
+ * NOTE: shared/gatt-client clears the database in case of failure. This
+ * triggers the service_removed callback for all affected services.
+ * Hence, this function will be called in the following cases:
+ *
+ * 1. When a GATT service gets removed due to "Service Changed".
+ *
+ * 2. When a GATT service gets removed when the database get cleared
+ * upon disconnection with a non-bonded device.
+ *
+ * 3. When a GATT service gets removed when the database get cleared
+ * by shared/gatt-client when its initialization procedure fails,
+ * e.g. due to an ATT protocol error or an unexpected disconnect.
+ * In this case the gatt-client will not be ready.
+ */
gatt_db_attribute_get_service_handles(attr, &start, &end);
if (!l)
return;
- prim = l->data;
- device->primaries = g_slist_delete_link(device->primaries, l);
-
- /*
- * If this happend since the db was cleared for a non-bonded device,
- * then don't remove the btd_service just yet. We do this so that we can
- * avoid re-probing the profile if the same GATT service is found on the
- * device on re-connection. However, if the device is marked as
- * temporary, then we remove it anyway.
- */
- if (device->client || device->temporary == TRUE)
- device_remove_gatt_profile(device, attr);
+ prim = l->data;
+ device->primaries = g_slist_delete_link(device->primaries, l);
/*
- * Remove the corresponding UUIDs entry, only if this is the last
- * service with this UUID.
+ * Remove the corresponding UUIDs entry and profile, only if this is
+ * the last service with this UUID.
*/
l = g_slist_find_custom(device->uuids, prim->uuid, bt_uuid_strcmp);
- if (!g_slist_find_custom(device->primaries, prim->uuid,
+ if (l && !g_slist_find_custom(device->primaries, prim->uuid,
prim_uuid_cmp)) {
+ /*
+ * If this happend since the db was cleared for a non-bonded
+ * device, then don't remove the btd_service just yet. We do
+ * this so that we can avoid re-probing the profile if the same
+ * GATT service is found on the device on re-connection.
+ * However, if the device is marked as temporary, then we
+ * remove it anyway.
+ */
+ if (device->client || device->temporary == TRUE)
+ device_remove_gatt_profile(device, attr);
+
+ g_free(l->data);
device->uuids = g_slist_delete_link(device->uuids, l);
g_dbus_emit_property_changed(dbus_conn, device->path,
DEVICE_INTERFACE, "UUIDs");
g_free(prim);
- store_services(device);
+ store_device_info(device);
btd_gatt_client_service_removed(device->client_dbus, attr);
}
if (device->browse)
browse_request_cancel(device->browse);
- attrib_client_unregister(device->gatt_services);
// while (device->services != NULL) {
// struct btd_service *service = device->services->data;
if (device->browse)
browse_request_cancel(device->browse);
-#ifdef __TIZEN_PATCH__
- attrib_client_unregister(device->gatt_services);
-#endif
while (device->services != NULL) {
struct btd_service *service = device->services->data;
#endif
device_svc_resolved(device, BDADDR_BREDR, err);
- browse_request_free(req);
}
static void browse_cb(sdp_list_t *recs, int err, gpointer user_data)
search_cb(recs, err, user_data);
}
-static void store_services(struct btd_device *device)
-{
- struct btd_adapter *adapter = device->adapter;
- char filename[PATH_MAX];
- char src_addr[18], dst_addr[18];
- uuid_t uuid;
- char *prim_uuid;
- GKeyFile *key_file;
- GSList *l;
- char *data;
- gsize length = 0;
-
- if (device_address_is_private(device)) {
- warn("Can't store services for private addressed device %s",
- device->path);
- return;
- }
-
- sdp_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
- prim_uuid = bt_uuid2string(&uuid);
- if (prim_uuid == NULL)
- return;
-
- ba2str(btd_adapter_get_address(adapter), src_addr);
- ba2str(&device->bdaddr, dst_addr);
-
- snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/attributes", src_addr,
- dst_addr);
- key_file = g_key_file_new();
-
- for (l = device->primaries; l; l = l->next) {
- struct gatt_primary *primary = l->data;
- char handle[6], uuid_str[33];
- int i;
-
- sprintf(handle, "%hu", primary->range.start);
-
- bt_string2uuid(&uuid, primary->uuid);
- sdp_uuid128_to_uuid(&uuid);
-
- switch (uuid.type) {
- case SDP_UUID16:
- sprintf(uuid_str, "%4.4X", uuid.value.uuid16);
- break;
- case SDP_UUID32:
- sprintf(uuid_str, "%8.8X", uuid.value.uuid32);
- break;
- case SDP_UUID128:
- for (i = 0; i < 16; i++)
- sprintf(uuid_str + (i * 2), "%2.2X",
- uuid.value.uuid128.data[i]);
- break;
- default:
- uuid_str[0] = '\0';
- }
-
- g_key_file_set_string(key_file, handle, "UUID", prim_uuid);
- g_key_file_set_string(key_file, handle, "Value", uuid_str);
- g_key_file_set_integer(key_file, handle, "EndGroupHandle",
- primary->range.end);
- }
-
- data = g_key_file_to_data(key_file, &length, NULL);
- if (length > 0) {
- create_file(filename, S_IRUSR | S_IWUSR);
- g_file_set_contents(filename, data, length, NULL);
- }
-
- free(prim_uuid);
- g_free(data);
- g_key_file_free(key_file);
-}
-
static bool device_get_auto_connect(struct btd_device *device)
{
if (device->disable_auto_connect)
attio_cleanup(device);
}
-static void send_le_browse_response(struct browse_req *req)
-{
- struct btd_device *dev = req->device;
- struct bearer_state *state = &dev->le_state;
- DBusMessage *reply, *msg = req->msg;
-
- if (!msg)
- return;
-
- if (!dbus_message_is_method_call(msg, DEVICE_INTERFACE, "Pair")) {
- reply = btd_error_failed(msg, "Service discovery failed");
- g_dbus_send_message(dbus_conn, reply);
- return;
- }
-
- if (!state->paired) {
- reply = btd_error_failed(msg, "Not paired");
- g_dbus_send_message(dbus_conn, reply);
- return;
- }
-
- if (!dev->bredr_state.paired && dev->pending_paired) {
- g_dbus_emit_property_changed(dbus_conn, dev->path,
- DEVICE_INTERFACE, "Paired");
- dev->pending_paired = false;
- }
-
- g_dbus_send_reply(dbus_conn, msg, DBUS_TYPE_INVALID);
-}
-
-static void register_gatt_services(struct browse_req *req)
+static void register_gatt_services(struct btd_device *device)
{
- struct btd_device *device = req->device;
+ struct browse_req *req = device->browse;
GSList *services = NULL;
if (!bt_gatt_client_is_ready(device->client))
btd_device_set_temporary(device, FALSE);
- update_gatt_uuids(req, device->primaries, services);
+ if (req)
+ update_gatt_uuids(req, device->primaries, services);
+
g_slist_free_full(device->primaries, g_free);
device->primaries = NULL;
device_probe_gatt_profiles(device);
device_svc_resolved(device, device->bdaddr_type, 0);
+}
- store_services(device);
-
- browse_request_free(req);
+#ifdef __TIZEN_PATCH__
+static void gatt_client_debug_func(const char *str, void *user_data)
+{
+ DBG("%s", str);
}
+#endif
static void gatt_client_ready_cb(bool success, uint8_t att_ecode,
void *user_data)
if (device->browse) {
struct browse_req *req = device->browse;
- send_le_browse_response(req);
device->browse = NULL;
- browse_request_free(req);
+ browse_request_complete(req, device->bdaddr_type, -EIO);
}
+#ifdef __TIZEN_PATCH__
+ if (device->attachid == 0) {
+ error("GATT client / server both are not connected");
+ return;
+ }
+
+ error("Even though GATT client's connection is failed, "
+ "because GATT server's connection is made, "
+ "update GATT connection state.");
+ device_set_gatt_connected(device, TRUE);
+#endif
+
return;
}
DBG("MTU: %u", device->att_mtu);
- if (device->browse)
- register_gatt_services(device->browse);
+ register_gatt_services(device);
device_accept_gatt_profiles(device);
return;
}
+#ifdef __TIZEN_PATCH__
+ if (!bt_gatt_client_set_debug(device->client, gatt_client_debug_func,
+ NULL, NULL)) {
+ error("Failed to set debug function");
+ }
+#endif
+
if (!bt_gatt_client_set_ready_handler(device->client,
gatt_client_ready_cb,
device, NULL)) {
}
}
+#ifndef __TIZEN_PATCH__
+static void gatt_server_init(struct btd_device *device, struct gatt_db *db)
+{
+ if (!db) {
+ error("No local GATT database exists for this adapter");
+ return;
+ }
+
+ gatt_server_cleanup(device);
+
+ device->server = bt_gatt_server_new(db, device->att, device->att_mtu);
+ if (!device->server)
+ error("Failed to initialize bt_gatt_server");
+}
+#endif
+
+static bool local_counter(uint32_t *sign_cnt, void *user_data)
+{
+ struct btd_device *dev = user_data;
+
+ if (!dev->local_csrk)
+ return false;
+
+ *sign_cnt = dev->local_csrk->counter++;
+
+ store_device_info(dev);
+
+ return true;
+}
+
+static bool remote_counter(uint32_t *sign_cnt, void *user_data)
+{
+ struct btd_device *dev = user_data;
+
+ if (!dev->remote_csrk || *sign_cnt < dev->remote_csrk->counter)
+ return false;
+
+ dev->remote_csrk->counter = *sign_cnt;
+
+ store_device_info(dev);
+
+ return true;
+}
+
bool device_attach_att(struct btd_device *dev, GIOChannel *io)
{
GError *gerr = NULL;
BtIOSecLevel sec_level;
uint16_t mtu;
uint16_t cid;
+#ifndef __TIZEN_PATCH__
+ struct btd_gatt_database *database;
+#endif
bt_io_get(io, &gerr, BT_IO_OPT_SEC_LEVEL, &sec_level,
BT_IO_OPT_IMTU, &mtu,
return false;
}
+#if 0
if (sec_level == BT_IO_SEC_LOW && dev->le_state.paired) {
DBG("Elevating security level since LTK is available");
return false;
}
}
+#endif
dev->att_mtu = MIN(mtu, BT_ATT_MAX_LE_MTU);
attrib = g_attrib_new(io, dev->att_mtu);
return false;
}
+#ifdef __TIZEN_PATCH__
dev->attachid = attrib_channel_attach(attrib);
if (dev->attachid == 0) {
g_attrib_unref(attrib);
error("Attribute server attach failure!");
return false;
}
+#endif
dev->attrib = attrib;
dev->att = g_attrib_get_att(attrib);
att_disconnected_cb, dev, NULL);
bt_att_set_close_on_unref(dev->att, true);
+ if (dev->local_csrk)
+ bt_att_set_local_key(dev->att, dev->local_csrk->key,
+ local_counter, dev);
+
+ if (dev->remote_csrk)
+ bt_att_set_remote_key(dev->att, dev->remote_csrk->key,
+ remote_counter, dev);
+
+#ifndef __TIZEN_PATCH__
+ database = btd_adapter_get_database(dev->adapter);
+#endif
+
gatt_client_init(dev);
+#ifndef __TIZEN_PATCH__
+ gatt_server_init(dev, btd_gatt_database_get_db(database));
+#endif
/*
* Remove the device from the connect_list and give the passive
struct btd_device *device = attcb->user_data;
struct browse_req *req = device->browse;
- send_le_browse_response(req);
device->browse = NULL;
- browse_request_free(req);
+ browse_request_complete(req, device->bdaddr_type, -ECONNABORTED);
}
-static void browse_gatt_client(struct browse_req *req)
+static void att_browse_cb(gpointer user_data)
{
- struct btd_device *device = req->device;
+ DBG("ATT connection successful");
+}
- if (!device->client) {
- DBG("No instance currently attached");
- return;
- }
+static struct browse_req *browse_request_new(struct btd_device *device,
+ DBusMessage *msg)
+{
+ struct browse_req *req;
+
+ if (device->browse)
+ return NULL;
+
+ req = g_new0(struct browse_req, 1);
+ req->device = device;
+
+ device->browse = req;
+
+ if (!msg)
+ return req;
+
+ req->msg = dbus_message_ref(msg);
/*
- * If gatt-client is ready, then register all services. Otherwise, this
- * will be deferred until client becomes ready.
+ * Track the request owner to cancel it automatically if the owner
+ * exits
*/
- if (bt_gatt_client_is_ready(device->client))
- register_gatt_services(req);
-}
-
-static void att_browse_cb(gpointer user_data)
-{
- struct att_callbacks *attcb = user_data;
- struct btd_device *device = attcb->user_data;
+ req->listener_id = g_dbus_add_disconnect_watch(dbus_conn,
+ dbus_message_get_sender(msg),
+ browse_request_exit,
+ req, NULL);
- browse_gatt_client(device->browse);
+ return req;
}
static int device_browse_gatt(struct btd_device *device, DBusMessage *msg)
struct att_callbacks *attcb;
struct browse_req *req;
- if (device->browse)
+ req = browse_request_new(device, msg);
+ if (!req)
return -EBUSY;
- req = g_new0(struct browse_req, 1);
- req->device = device;
-
- device->browse = req;
-
if (device->attrib) {
- browse_gatt_client(device->browse);
- goto done;
+ /*
+ * If discovery has not yet completed, then wait for gatt-client
+ * to become ready.
+ */
+ if (!device->le_state.svc_resolved)
+ return 0;
+
+ /*
+ * Services have already been discovered, so signal this browse
+ * request as resolved.
+ */
+ device_svc_resolved(device, device->bdaddr_type, 0);
+ return 0;
}
attcb = g_new0(struct att_callbacks, 1);
return -EIO;
}
-done:
-
- if (msg) {
- const char *sender = dbus_message_get_sender(msg);
-
- req->msg = dbus_message_ref(msg);
- /* Track the request owner to cancel it
- * automatically if the owner exits */
- req->listener_id = g_dbus_add_disconnect_watch(dbus_conn,
- sender,
- discover_services_req_exit,
- req, NULL);
- }
-
return 0;
}
uuid_t uuid;
int err;
- if (device->browse)
+ req = browse_request_new(device, msg);
+ if (!req)
return -EBUSY;
- req = g_new0(struct browse_req, 1);
- req->device = device;
sdp_uuid16_create(&uuid, uuid_list[req->search_uuid++]);
req->sdp_flags = get_sdp_flags(device);
return err;
}
- device->browse = req;
-
- if (msg) {
- const char *sender = dbus_message_get_sender(msg);
-
- req->msg = dbus_message_ref(msg);
- /* Track the request owner to cancel it
- * automatically if the owner exits */
- req->listener_id = g_dbus_add_disconnect_watch(dbus_conn,
- sender,
- discover_services_req_exit,
- req, NULL);
- }
-
return err;
}
* automatically if the owner exits */
req->listener_id = g_dbus_add_disconnect_watch(dbus_conn,
sender,
- discover_services_req_exit,
+ browse_request_exit,
req, NULL);
}
return err;
device->last_bdaddr_type = type;
}
+
+void device_set_ipsp_connected(struct btd_device *device, gboolean connected)
+{
+ if (device == NULL) {
+ error("device is NULL");
+ return;
+ }
+
+ if (device->ipsp_connected == connected)
+ return;
+
+ device->ipsp_connected = connected;
+
+ DBG("ipsp_connected %d", connected);
+
+ g_dbus_emit_property_changed(dbus_conn, device->path,
+ DEVICE_INTERFACE, "IpspConnected");
+}
#endif
int device_discover_services(struct btd_device *device)
return device->client;
}
+struct bt_gatt_server *btd_device_get_gatt_server(struct btd_device *device)
+{
+ if (!device)
+ return NULL;
+
+ return device->server;
+}
+
void btd_device_gatt_set_service_changed(struct btd_device *device,
uint16_t start, uint16_t end)
{
GSList *btd_device_get_primaries(struct btd_device *device);
struct gatt_db *btd_device_get_gatt_db(struct btd_device *device);
struct bt_gatt_client *btd_device_get_gatt_client(struct btd_device *device);
+struct bt_gatt_server *btd_device_get_gatt_server(struct btd_device *device);
void btd_device_gatt_set_service_changed(struct btd_device *device,
uint16_t start, uint16_t end);
bool device_attach_att(struct btd_device *dev, GIOChannel *io);
gboolean device_is_trusted(struct btd_device *device);
void device_set_paired(struct btd_device *dev, uint8_t bdaddr_type);
void device_set_unpaired(struct btd_device *dev, uint8_t bdaddr_type);
-bool device_is_service_blocked(struct btd_device *device, const char *uuid);
void btd_device_set_temporary(struct btd_device *device, gboolean temporary);
void btd_device_set_trusted(struct btd_device *device, gboolean trusted);
void device_set_bonded(struct btd_device *device, uint8_t bdaddr_type);
void device_set_payload_timeout(struct btd_device *device,
uint16_t payload_timeout);
void device_set_last_addr_type(struct btd_device *device, uint8_t type);
+void device_set_ipsp_connected(struct btd_device *device, gboolean connected);
#endif
struct btd_device *btd_device_ref(struct btd_device *device);
void btd_device_init(void);
void btd_device_cleanup(void);
-#ifdef BLUEZ5_GATT_CLIENT
-gboolean disconnect_le_device(gpointer user_data);
-#endif
#include <stdbool.h>
#include <glib.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/sdp.h>
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/sdp.h"
#include "src/shared/util.h"
#include "uuid-helper.h"
char *ptr;
in_name = g_malloc0(sizeof(char) * (len + 1));
+ /* Fix : NULL_RETURNS */
+ if (in_name == NULL)
+ return NULL;
memcpy(in_name, name, sizeof(char) * len);
in_name[len] = '\0';
#include <config.h>
#endif
-#include <gdbus/gdbus.h>
+#include "gdbus/gdbus.h"
#include "error.h"
DBusMessage *btd_error_invalid_args_str(DBusMessage *msg, const char *str)
{
return g_dbus_create_error(msg, ERROR_INTERFACE ".InvalidArguments",
- str);
+ "%s", str);
}
DBusMessage *btd_error_busy(DBusMessage *msg)
DBusMessage *btd_error_not_permitted(DBusMessage *msg, const char *str)
{
- return g_dbus_create_error(msg, ERROR_INTERFACE ".NotPermitted", str);
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".NotPermitted",
+ "%s", str);
}
DBusMessage *btd_error_no_such_adapter(DBusMessage *msg)
#include <stdint.h>
#include <dbus/dbus.h>
-#include <gdbus/gdbus.h>
-#include <bluetooth/bluetooth.h>
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
#include "log.h"
#include "error.h"
#include "adapter.h"
#include "device.h"
-#include "lib/uuid.h"
#include "src/shared/queue.h"
#include "src/shared/att.h"
#include "src/shared/gatt-db.h"
#include "gatt-client.h"
#include "dbus-common.h"
+#ifndef NELEM
+#define NELEM(x) (sizeof(x) / sizeof((x)[0]))
+#endif
+
#define GATT_SERVICE_IFACE "org.bluez.GattService1"
#define GATT_CHARACTERISTIC_IFACE "org.bluez.GattCharacteristic1"
#define GATT_DESCRIPTOR_IFACE "org.bluez.GattDescriptor1"
struct btd_gatt_client {
struct btd_device *device;
+ bool ready;
char devaddr[18];
struct gatt_db *db;
struct bt_gatt_client *gatt;
struct queue *services;
+ struct queue *all_notify_clients;
};
struct service {
char *path;
struct queue *chrcs;
bool chrcs_ready;
+ struct queue *pending_ext_props;
+ guint idle_id;
};
struct characteristic {
uint16_t handle;
uint16_t value_handle;
uint8_t props;
+ uint16_t ext_props;
+ uint16_t ext_props_handle;
bt_uuid_t uuid;
char *path;
- bool in_read;
- bool in_write;
+ unsigned int read_id;
+ unsigned int write_id;
struct queue *descs;
+
+ bool notifying;
+ struct queue *notify_clients;
};
struct descriptor {
bt_uuid_t uuid;
char *path;
- bool in_read;
- bool in_write;
+ unsigned int read_id;
+ unsigned int write_id;
};
+static bool uuid_cmp(const bt_uuid_t *uuid, uint16_t u16)
+{
+ bt_uuid_t uuid16;
+
+ bt_uuid16_create(&uuid16, u16);
+
+ return bt_uuid_cmp(uuid, &uuid16) == 0;
+}
+
static gboolean descriptor_get_uuid(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *data)
{
return true;
}
+#ifdef __TIZEN_PATCH__
+static bool parse_type_value_arg(DBusMessage *msg, uint8_t *type, uint8_t **value,
+ size_t *value_len)
+{
+ DBusMessageIter iter;
+ DBusMessageIter array;
+ uint8_t *val;
+ int len;
+
+ if (!dbus_message_iter_init(msg, &iter))
+ return false;
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BYTE)
+ return false;
+ dbus_message_iter_get_basic(&iter, type);
+ dbus_message_iter_next(&iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
+ return false;
+
+ dbus_message_iter_recurse(&iter, &array);
+ dbus_message_iter_get_fixed_array(&array, &val, &len);
+ dbus_message_iter_next(&iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_INVALID)
+ return false;
+
+ if (len < 0)
+ return false;
+
+ *value = val;
+ *value_len = len;
+
+ return true;
+}
+#endif
+
typedef bool (*async_dbus_op_complete_t)(void *data);
struct async_dbus_op {
{
struct async_dbus_op *op = data;
- dbus_message_unref(op->msg);
+ if (op->msg)
+ dbus_message_unref(op->msg);
+
free(op);
}
case 0:
return btd_error_failed(msg, "Operation failed");
default:
- return g_dbus_create_error(msg, ERROR_INTERFACE,
+ return g_dbus_create_error(msg, ERROR_INTERFACE ".Failed",
"Operation failed with ATT error: 0x%02x",
att_ecode);
}
if (!success) {
DBusMessage *reply = create_gatt_dbus_error(op->msg, att_ecode);
- desc->in_read = false;
+ desc->read_id = 0;
g_dbus_send_message(btd_get_dbus_connection(), reply);
return;
}
- gatt_db_attribute_write(desc->attr, 0, value, length, 0, NULL,
+ if (!op->offset)
+ gatt_db_attribute_reset(desc->attr);
+
+ gatt_db_attribute_write(desc->attr, op->offset, value, length, 0, NULL,
write_descriptor_cb, desc);
/*
*/
if (length == bt_gatt_client_get_mtu(service->client->gatt) - 1) {
op->offset += length;
- if (bt_gatt_client_read_long_value(service->client->gatt,
+ desc->read_id = bt_gatt_client_read_long_value(
+ service->client->gatt,
desc->handle,
op->offset,
desc_read_cb,
async_dbus_op_ref(op),
- async_dbus_op_unref))
+ async_dbus_op_unref);
+ if (desc->read_id)
return;
}
- desc->in_read = false;
+ desc->read_id = 0;
/* Read the stored data from db */
gatt_db_attribute_read(desc->attr, 0, 0, NULL, read_op_cb, op);
struct bt_gatt_client *gatt = desc->chrc->service->client->gatt;
struct async_dbus_op *op;
- if (desc->in_read)
+ if (!gatt)
+ return btd_error_failed(msg, "Not connected");
+
+ if (desc->read_id)
return btd_error_in_progress(msg);
op = new0(struct async_dbus_op, 1);
op->msg = dbus_message_ref(msg);
op->data = desc;
- if (bt_gatt_client_read_value(gatt, desc->handle, desc_read_cb,
+ desc->read_id = bt_gatt_client_read_value(gatt, desc->handle,
+ desc_read_cb,
async_dbus_op_ref(op),
- async_dbus_op_unref)) {
- desc->in_read = true;
+ async_dbus_op_unref);
+ if (desc->read_id)
return NULL;
- }
async_dbus_op_free(op);
write_result_cb(success, false, att_ecode, user_data);
}
-static bool start_long_write(DBusMessage *msg, uint16_t handle,
+static unsigned int start_long_write(DBusMessage *msg, uint16_t handle,
struct bt_gatt_client *gatt,
bool reliable, const uint8_t *value,
size_t value_len, void *data,
async_dbus_op_complete_t complete)
{
struct async_dbus_op *op;
+ unsigned int id;
op = new0(struct async_dbus_op, 1);
if (!op)
op->data = data;
op->complete = complete;
- if (bt_gatt_client_write_long_value(gatt, reliable, handle,
+ id = bt_gatt_client_write_long_value(gatt, reliable, handle,
0, value, value_len,
write_result_cb, op,
- async_dbus_op_free))
- return true;
+ async_dbus_op_free);
- async_dbus_op_free(op);
+ if (!id)
+ async_dbus_op_free(op);
- return false;
+ return id;
}
-static bool start_write_request(DBusMessage *msg, uint16_t handle,
+static unsigned int start_write_request(DBusMessage *msg, uint16_t handle,
struct bt_gatt_client *gatt,
const uint8_t *value, size_t value_len,
void *data,
async_dbus_op_complete_t complete)
{
struct async_dbus_op *op;
+ unsigned int id;
op = new0(struct async_dbus_op, 1);
if (!op)
op->data = data;
op->complete = complete;
- if (bt_gatt_client_write_value(gatt, handle, value, value_len,
+ id = bt_gatt_client_write_value(gatt, handle, value, value_len,
write_cb, op,
- async_dbus_op_free))
- return true;
-
- async_dbus_op_free(op);
+ async_dbus_op_free);
+ if (!id)
+ async_dbus_op_free(op);
- return false;
+ return id;
}
static bool desc_write_complete(void *data)
{
struct descriptor *desc = data;
- desc->in_write = false;
+ desc->write_id = false;
/*
* The descriptor might have been unregistered during the read. Return
struct bt_gatt_client *gatt = desc->chrc->service->client->gatt;
uint8_t *value = NULL;
size_t value_len = 0;
- bool result;
- bt_uuid_t uuid;
- if (desc->in_write)
+ if (!gatt)
+ return btd_error_failed(msg, "Not connected");
+
+ if (desc->write_id)
return btd_error_in_progress(msg);
if (!parse_value_arg(msg, &value, &value_len))
* descriptors. We achieve this through the StartNotify and StopNotify
* methods on GattCharacteristic1.
*/
- bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
- if (bt_uuid_cmp(&desc->uuid, &uuid))
+ if (uuid_cmp(&desc->uuid, GATT_CLIENT_CHARAC_CFG_UUID))
return btd_error_not_permitted(msg, "Write not permitted");
/*
* write.
*/
if (value_len <= (unsigned) bt_gatt_client_get_mtu(gatt) - 3)
- result = start_write_request(msg, desc->handle, gatt, value,
+ desc->write_id = start_write_request(msg, desc->handle,
+ gatt, value,
value_len, desc,
desc_write_complete);
else
- result = start_long_write(msg, desc->handle, gatt, false, value,
+ desc->write_id = start_long_write(msg, desc->handle,
+ gatt, false, value,
value_len, desc,
desc_write_complete);
- if (!result)
+ if (!desc->write_id)
return btd_error_failed(msg, "Failed to initiate write");
- desc->in_write = true;
-
return NULL;
}
static const GDBusPropertyTable descriptor_properties[] = {
+#ifdef __TIZEN_PATCH__
+ { "UUID", "s", descriptor_get_uuid },
+ { "Characteristic", "o", descriptor_get_characteristic },
+ { "Value", "ay", descriptor_get_value, NULL,
+ descriptor_value_exists },
+ { }
+#else
{ "UUID", "s", descriptor_get_uuid, NULL, NULL,
G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
{ "Characteristic", "o", descriptor_get_characteristic, NULL, NULL,
{ "Value", "ay", descriptor_get_value, NULL, descriptor_value_exists,
G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
{ }
+#endif
};
static const GDBusMethodTable descriptor_methods[] = {
+#ifdef __TIZEN_PATCH__
+ { GDBUS_ASYNC_METHOD("ReadValue", NULL, GDBUS_ARGS({ "value", "ay" }),
+ descriptor_read_value) },
+ { GDBUS_ASYNC_METHOD("WriteValue", GDBUS_ARGS({ "value", "ay" }),
+ NULL, descriptor_write_value) },
+ { }
+#else
{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("ReadValue", NULL,
GDBUS_ARGS({ "value", "ay" }),
descriptor_read_value) },
NULL,
descriptor_write_value) },
{ }
+#endif
};
static void descriptor_free(void *data)
DBG("Exported GATT characteristic descriptor: %s", desc->path);
+ if (uuid_cmp(&desc->uuid, GATT_CHARAC_EXT_PROPER_UUID))
+ chrc->ext_props_handle = desc->handle;
+
return desc;
}
static void unregister_descriptor(void *data)
{
struct descriptor *desc = data;
+ struct bt_gatt_client *gatt = desc->chrc->service->client->gatt;
DBG("Removing GATT descriptor: %s", desc->path);
+ if (desc->read_id)
+ bt_gatt_client_cancel(gatt, desc->read_id);
+
+ if (desc->write_id)
+ bt_gatt_client_cancel(gatt, desc->write_id);
+
+ desc->chrc = NULL;
+
g_dbus_unregister_interface(btd_get_dbus_connection(), desc->path,
GATT_DESCRIPTOR_IFACE);
}
static gboolean characteristic_get_notifying(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *data)
{
- dbus_bool_t notifying = FALSE;
-
- /*
- * TODO: Return the correct value here once StartNotify and StopNotify
- * methods are implemented.
- */
+ struct characteristic *chrc = data;
+ dbus_bool_t notifying = chrc->notifying ? TRUE : FALSE;
dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, ¬ifying);
return TRUE;
}
-static struct {
+struct chrc_prop_data {
uint8_t prop;
char *str;
-} properties[] = {
+};
+
+static struct chrc_prop_data chrc_props[] = {
/* Default Properties */
{ BT_GATT_CHRC_PROP_BROADCAST, "broadcast" },
{ BT_GATT_CHRC_PROP_READ, "read" },
{ BT_GATT_CHRC_PROP_NOTIFY, "notify" },
{ BT_GATT_CHRC_PROP_INDICATE, "indicate" },
{ BT_GATT_CHRC_PROP_AUTH, "authenticated-signed-writes" },
- { },
+ { BT_GATT_CHRC_PROP_EXT_PROP, "extended-properties" }
+};
+
+static struct chrc_prop_data chrc_ext_props[] = {
+ /* Extended Properties */
+ { BT_GATT_CHRC_EXT_PROP_RELIABLE_WRITE, "reliable-write" },
+ { BT_GATT_CHRC_EXT_PROP_WRITABLE_AUX, "writable-auxiliaries" }
};
static gboolean characteristic_get_flags(const GDBusPropertyTable *property,
{
struct characteristic *chrc = data;
DBusMessageIter array;
- int i;
+ unsigned i;
dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "s", &array);
- for (i = 0; properties[i].str; i++) {
- if (chrc->props & properties[i].prop)
+ for (i = 0; i < NELEM(chrc_props); i++) {
+ if (chrc->props & chrc_props[i].prop)
dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING,
- &properties[i].str);
+ &chrc_props[i].str);
}
- /*
- * TODO: Handle extended properties if the descriptor is
- * present.
- */
+ for (i = 0; i < NELEM(chrc_ext_props); i++) {
+ if (chrc->ext_props & chrc_ext_props[i].prop)
+ dbus_message_iter_append_basic(&array, DBUS_TYPE_STRING,
+ &chrc_ext_props[i].str);
+ }
dbus_message_iter_close_container(iter, &array);
GATT_CHARACTERISTIC_IFACE, "Value");
}
+#ifdef __TIZEN_PATCH__
+static void notify_characteristic_cb(struct gatt_db_attribute *attr, int err,
+ void *user_data)
+{
+ struct characteristic *chrc = user_data;
+
+ if (err)
+ return;
+
+ g_dbus_emit_property_changed(btd_get_dbus_connection(), chrc->path,
+ GATT_CHARACTERISTIC_IFACE, "ChangedValue");
+}
+#endif
+
static void chrc_read_cb(bool success, uint8_t att_ecode, const uint8_t *value,
uint16_t length, void *user_data)
{
if (!success) {
DBusMessage *reply = create_gatt_dbus_error(op->msg, att_ecode);
- chrc->in_read = false;
+ chrc->read_id = 0;
g_dbus_send_message(btd_get_dbus_connection(), reply);
return ;
}
- gatt_db_attribute_write(chrc->attr, 0, value, length, op->offset, NULL,
+ if (!op->offset)
+ gatt_db_attribute_reset(chrc->attr);
+
+ gatt_db_attribute_write(chrc->attr, op->offset, value, length, 0, NULL,
write_characteristic_cb, chrc);
/*
*/
if (length == bt_gatt_client_get_mtu(service->client->gatt) - 1) {
op->offset += length;
- if (bt_gatt_client_read_long_value(service->client->gatt,
+ chrc->read_id = bt_gatt_client_read_long_value(
+ service->client->gatt,
chrc->value_handle,
op->offset,
chrc_read_cb,
async_dbus_op_ref(op),
- async_dbus_op_unref))
+ async_dbus_op_unref);
+ if (chrc->read_id)
return;
}
- chrc->in_read = false;
+ chrc->read_id = 0;
/* Read the stored data from db */
gatt_db_attribute_read(chrc->attr, 0, 0, NULL, read_op_cb, op);
struct bt_gatt_client *gatt = chrc->service->client->gatt;
struct async_dbus_op *op;
- if (chrc->in_read)
+ if (!gatt)
+ return btd_error_failed(msg, "Not connected");
+
+ if (chrc->read_id)
return btd_error_in_progress(msg);
op = new0(struct async_dbus_op, 1);
op->msg = dbus_message_ref(msg);
op->data = chrc;
- if (bt_gatt_client_read_value(gatt, chrc->value_handle, chrc_read_cb,
- async_dbus_op_ref(op),
- async_dbus_op_unref)) {
- chrc->in_read = true;
+ chrc->read_id = bt_gatt_client_read_value(gatt, chrc->value_handle,
+ chrc_read_cb,
+ async_dbus_op_ref(op),
+ async_dbus_op_unref);
+ if (chrc->read_id)
return NULL;
- }
async_dbus_op_free(op);
{
struct characteristic *chrc = data;
- chrc->in_write = false;
+ chrc->write_id = false;
/*
* The characteristic might have been unregistered during the read.
struct bt_gatt_client *gatt = chrc->service->client->gatt;
uint8_t *value = NULL;
size_t value_len = 0;
+ bool supported = false;
- if (chrc->in_write)
+ if (!gatt)
+ return btd_error_failed(msg, "Not connected");
+
+ if (chrc->write_id)
return btd_error_in_progress(msg);
if (!parse_value_arg(msg, &value, &value_len))
return btd_error_invalid_args(msg);
- if (!(chrc->props & (BT_GATT_CHRC_PROP_WRITE |
- BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP)))
- return btd_error_not_supported(msg);
-
/*
* Decide which write to use based on characteristic properties. For now
* we don't perform signed writes since gatt-client doesn't support them
* - If value is larger than MTU - 3: long-write
* * "write-without-response" property set -> write command.
*/
+#ifndef __TIZEN_PATCH__
+ if ((chrc->ext_props & BT_GATT_CHRC_EXT_PROP_RELIABLE_WRITE)) {
+ supported = true;
+ chrc->write_id = start_long_write(msg, chrc->value_handle, gatt,
+ true, value, value_len,
+ chrc, chrc_write_complete);
+ if (chrc->write_id)
+ return NULL;
+ }
+#endif
+
if (chrc->props & BT_GATT_CHRC_PROP_WRITE) {
uint16_t mtu;
- bool result;
+ supported = true;
mtu = bt_gatt_client_get_mtu(gatt);
if (!mtu)
return btd_error_failed(msg, "No ATT transport");
if (value_len <= (unsigned) mtu - 3)
- result = start_write_request(msg, chrc->value_handle,
- gatt, value,
- value_len, chrc,
- chrc_write_complete);
+ chrc->write_id = start_write_request(msg,
+ chrc->value_handle,
+ gatt, value, value_len,
+ chrc, chrc_write_complete);
else
- result = start_long_write(msg, chrc->value_handle, gatt,
- false, value, value_len, chrc,
- chrc_write_complete);
+ chrc->write_id = start_long_write(msg,
+ chrc->value_handle, gatt,
+ false, value, value_len,
+ chrc, chrc_write_complete);
- if (result)
- goto done_async;
+ if (chrc->write_id)
+ return NULL;
}
- if ((chrc->props & BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP) &&
- bt_gatt_client_write_without_response(gatt,
- chrc->value_handle,
- false, value,
- value_len))
+ if (!(chrc->props & BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP))
+ goto fail;
+
+ supported = true;
+ chrc->write_id = bt_gatt_client_write_without_response(gatt,
+ chrc->value_handle,
+ chrc->props & BT_GATT_CHRC_PROP_AUTH,
+ value, value_len);
+ if (chrc->write_id) {
+#ifdef __TIZEN_PATCH__
+ chrc->write_id = 0;
+#endif
return dbus_message_new_method_return(msg);
+ }
+
+fail:
+ if (supported)
+ return btd_error_failed(msg, "Failed to initiate write");
+
+ return btd_error_not_supported(msg);
+}
+
+#ifdef __TIZEN_PATCH__
+static DBusMessage *characteristic_write_value_by_type(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ struct characteristic *chrc = user_data;
+ struct bt_gatt_client *gatt = chrc->service->client->gatt;
+ uint8_t *value = NULL;
+ size_t value_len = 0;
+ bool supported = false;
+ uint8_t write_type = 0;
- return btd_error_failed(msg, "Failed to initiate write");
+ if (!gatt)
+ return btd_error_failed(msg, "Not connected");
-done_async:
- chrc->in_write = true;
+ if (chrc->write_id)
+ return btd_error_in_progress(msg);
- return NULL;
+ if (!parse_type_value_arg(msg, &write_type, &value, &value_len))
+ return btd_error_invalid_args(msg);
+
+
+ if ((write_type & chrc->props) == BT_GATT_CHRC_PROP_WRITE) {
+ DBG("BT_GATT_CHRC_PROP_WRITE");
+ uint16_t mtu;
+ supported = true;
+ mtu = bt_gatt_client_get_mtu(gatt);
+ if (!mtu)
+ return btd_error_failed(msg, "No ATT transport");
+
+ if (value_len <= (unsigned) mtu - 3)
+ chrc->write_id = start_write_request(msg,
+ chrc->value_handle,
+ gatt, value, value_len,
+ chrc, chrc_write_complete);
+ else
+ chrc->write_id = start_long_write(msg,
+ chrc->value_handle, gatt,
+ false, value, value_len,
+ chrc, chrc_write_complete);
+
+ if (chrc->write_id)
+ return NULL;
+ } else if ((write_type & chrc->props) ==
+ BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP) {
+ DBG("BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP");
+ supported = true;
+ chrc->write_id = bt_gatt_client_write_without_response(gatt,
+ chrc->value_handle,
+ chrc->props & BT_GATT_CHRC_PROP_AUTH,
+ value, value_len);
+ if (chrc->write_id) {
+ chrc->write_id = 0;
+ return dbus_message_new_method_return(msg);
+ }
+ }
+
+ if (supported)
+ return btd_error_failed(msg, "Failed to initiate write");
+
+ return btd_error_not_supported(msg);
+}
+#endif
+
+struct notify_client {
+ struct characteristic *chrc;
+ int ref_count;
+ char *owner;
+ guint watch;
+ unsigned int notify_id;
+};
+
+static void notify_client_free(struct notify_client *client)
+{
+ DBG("owner %s", client->owner);
+
+ g_dbus_remove_watch(btd_get_dbus_connection(), client->watch);
+ bt_gatt_client_unregister_notify(client->chrc->service->client->gatt,
+ client->notify_id);
+ free(client->owner);
+ free(client);
+}
+
+static void notify_client_unref(void *data)
+{
+ struct notify_client *client = data;
+
+ DBG("owner %s", client->owner);
+
+ if (__sync_sub_and_fetch(&client->ref_count, 1))
+ return;
+
+ notify_client_free(client);
+}
+
+static struct notify_client *notify_client_ref(struct notify_client *client)
+{
+ DBG("owner %s", client->owner);
+
+ __sync_fetch_and_add(&client->ref_count, 1);
+
+ return client;
+}
+
+static bool match_notifying(const void *a, const void *b)
+{
+ const struct notify_client *client = a;
+
+ return !!client->notify_id;
+}
+
+static void update_notifying(struct characteristic *chrc)
+{
+ if (!chrc->notifying)
+ return;
+
+ if (queue_find(chrc->notify_clients, match_notifying, NULL))
+ return;
+
+ chrc->notifying = false;
+
+ g_dbus_emit_property_changed(btd_get_dbus_connection(), chrc->path,
+ GATT_CHARACTERISTIC_IFACE,
+ "Notifying");
+}
+
+static void notify_client_disconnect(DBusConnection *conn, void *user_data)
+{
+ struct notify_client *client = user_data;
+ struct characteristic *chrc = client->chrc;
+
+ DBG("owner %s", client->owner);
+
+ queue_remove(chrc->notify_clients, client);
+ queue_remove(chrc->service->client->all_notify_clients, client);
+
+ update_notifying(chrc);
+
+ notify_client_unref(client);
+}
+
+static struct notify_client *notify_client_create(struct characteristic *chrc,
+ const char *owner)
+{
+ struct notify_client *client;
+
+ client = new0(struct notify_client, 1);
+ if (!client)
+ return NULL;
+
+ client->chrc = chrc;
+ client->owner = strdup(owner);
+ if (!client->owner) {
+ free(client);
+ return NULL;
+ }
+
+ client->watch = g_dbus_add_disconnect_watch(btd_get_dbus_connection(),
+ owner, notify_client_disconnect,
+ client, NULL);
+ if (!client->watch) {
+ free(client->owner);
+ free(client);
+ return NULL;
+ }
+
+ return notify_client_ref(client);
+}
+
+static bool match_notify_sender(const void *a, const void *b)
+{
+ const struct notify_client *client = a;
+ const char *sender = b;
+
+ return strcmp(client->owner, sender) == 0;
+}
+
+static void notify_cb(uint16_t value_handle, const uint8_t *value,
+ uint16_t length, void *user_data)
+{
+ struct async_dbus_op *op = user_data;
+ struct notify_client *client = op->data;
+ struct characteristic *chrc = client->chrc;
+
+ /*
+ * Even if the value didn't change, we want to send a PropertiesChanged
+ * signal so that we propagate the notification/indication to
+ * applications.
+ */
+ gatt_db_attribute_reset(chrc->attr);
+#ifdef __TIZEN_PATCH__
+ gatt_db_attribute_write(chrc->attr, 0, value, length, 0, NULL,
+ notify_characteristic_cb, chrc);
+#else
+ gatt_db_attribute_write(chrc->attr, 0, value, length, 0, NULL,
+ write_characteristic_cb, chrc);
+#endif
+}
+
+static DBusMessage *create_notify_reply(struct async_dbus_op *op,
+ bool success, uint8_t att_ecode)
+{
+ DBusMessage *reply = NULL;
+
+ if (!op->msg)
+ return NULL;
+
+ if (success)
+ reply = g_dbus_create_reply(op->msg, DBUS_TYPE_INVALID);
+ else if (att_ecode)
+ reply = create_gatt_dbus_error(op->msg, att_ecode);
+ else
+ reply = btd_error_failed(op->msg,
+ "Characteristic not available");
+
+ if (!reply)
+ error("Failed to construct D-Bus message reply");
+
+ return reply;
+}
+
+static void register_notify_cb(uint16_t att_ecode, void *user_data)
+{
+ struct async_dbus_op *op = user_data;
+ struct notify_client *client = op->data;
+ struct characteristic *chrc = client->chrc;
+ DBusMessage *reply;
+
+ if (att_ecode) {
+ queue_remove(chrc->notify_clients, client);
+ queue_remove(chrc->service->client->all_notify_clients, client);
+ notify_client_free(client);
+
+ reply = create_notify_reply(op, false, att_ecode);
+
+ goto done;
+ }
+
+ if (!chrc->notifying) {
+ chrc->notifying = true;
+ g_dbus_emit_property_changed(btd_get_dbus_connection(),
+ chrc->path, GATT_CHARACTERISTIC_IFACE,
+ "Notifying");
+ }
+
+ reply = create_notify_reply(op, true, 0);
+
+done:
+ if (reply)
+ g_dbus_send_message(btd_get_dbus_connection(), reply);
}
static DBusMessage *characteristic_start_notify(DBusConnection *conn,
DBusMessage *msg, void *user_data)
{
- /* TODO: Implement */
- return btd_error_failed(msg, "Not implemented");
+ struct characteristic *chrc = user_data;
+ struct bt_gatt_client *gatt = chrc->service->client->gatt;
+ const char *sender = dbus_message_get_sender(msg);
+ struct async_dbus_op *op;
+ struct notify_client *client;
+
+ if (!(chrc->props & BT_GATT_CHRC_PROP_NOTIFY ||
+ chrc->props & BT_GATT_CHRC_PROP_INDICATE))
+ return btd_error_not_supported(msg);
+
+ /* Each client can only have one active notify session. */
+ client = queue_find(chrc->notify_clients, match_notify_sender, sender);
+ if (client)
+ return client->notify_id ?
+ btd_error_failed(msg, "Already notifying") :
+ btd_error_in_progress(msg);
+
+ client = notify_client_create(chrc, sender);
+ if (!client)
+ return btd_error_failed(msg, "Failed allocate notify session");
+
+ queue_push_tail(chrc->notify_clients, client);
+ queue_push_tail(chrc->service->client->all_notify_clients, client);
+
+ /*
+ * If the device is currently not connected, return success. We will
+ * automatically try and register all clients when a GATT client becomes
+ * ready.
+ */
+ if (!gatt) {
+ DBusMessage *reply;
+
+ reply = g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+ if (reply)
+ return reply;
+
+ /*
+ * Clean up and respond with an error instead of timing out to
+ * avoid any ambiguities.
+ */
+ error("Failed to construct D-Bus message reply");
+ goto fail;
+ }
+
+ op = new0(struct async_dbus_op, 1);
+ if (!op)
+ goto fail;
+
+ op->data = client;
+ op->msg = dbus_message_ref(msg);
+
+ client->notify_id = bt_gatt_client_register_notify(gatt,
+ chrc->value_handle,
+ register_notify_cb, notify_cb,
+ op, async_dbus_op_free);
+ if (client->notify_id)
+ return NULL;
+
+ async_dbus_op_free(op);
+
+fail:
+ queue_remove(chrc->notify_clients, client);
+ queue_remove(chrc->service->client->all_notify_clients, client);
+
+ /* Directly free the client */
+ notify_client_free(client);
+
+ return btd_error_failed(msg, "Failed to register notify session");
}
static DBusMessage *characteristic_stop_notify(DBusConnection *conn,
DBusMessage *msg, void *user_data)
{
- /* TODO: Implement */
- return btd_error_failed(msg, "Not implemented");
+ struct characteristic *chrc = user_data;
+ struct bt_gatt_client *gatt = chrc->service->client->gatt;
+ const char *sender = dbus_message_get_sender(msg);
+ struct notify_client *client;
+
+ if (!chrc->notifying)
+ return btd_error_failed(msg, "Not notifying");
+
+ client = queue_remove_if(chrc->notify_clients, match_notify_sender,
+ (void *) sender);
+ if (!client)
+ return btd_error_failed(msg, "No notify session started");
+
+ queue_remove(chrc->service->client->all_notify_clients, client);
+ bt_gatt_client_unregister_notify(gatt, client->notify_id);
+ update_notifying(chrc);
+
+ notify_client_unref(client);
+
+ return dbus_message_new_method_return(msg);
}
static void append_desc_path(void *data, void *user_data)
}
static const GDBusPropertyTable characteristic_properties[] = {
+#ifdef __TIZEN_PATCH__
+ { "UUID", "s", characteristic_get_uuid },
+ { "Service", "o", characteristic_get_service },
+ { "Value", "ay", characteristic_get_value, NULL,
+ characteristic_value_exists },
+ { "ChangedValue", "ay", characteristic_get_value, NULL,
+ characteristic_value_exists },
+ { "Notifying", "b", characteristic_get_notifying },
+ { "Flags", "as", characteristic_get_flags },
+ { "Descriptors", "ao", characteristic_get_descriptors },
+ { }
+#else
{ "UUID", "s", characteristic_get_uuid, NULL, NULL,
G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
{ "Service", "o", characteristic_get_service, NULL, NULL,
{ "Descriptors", "ao", characteristic_get_descriptors, NULL, NULL,
G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
{ }
+#endif
+
};
static const GDBusMethodTable characteristic_methods[] = {
+#ifdef __TIZEN_PATCH__
+ { GDBUS_ASYNC_METHOD("ReadValue", NULL, GDBUS_ARGS({ "value", "ay" }),
+ characteristic_read_value) },
+ { GDBUS_ASYNC_METHOD("WriteValue", GDBUS_ARGS({ "value", "ay" }),
+ NULL, characteristic_write_value) },
+ { GDBUS_ASYNC_METHOD("WriteValuebyType",
+ GDBUS_ARGS({ "type", "y" }, { "value", "ay" }),
+ NULL, characteristic_write_value_by_type) },
+ { GDBUS_ASYNC_METHOD("StartNotify", NULL, NULL,
+ characteristic_start_notify) },
+ { GDBUS_METHOD("StopNotify", NULL, NULL, characteristic_stop_notify) },
+ { }
+#else
{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("ReadValue", NULL,
GDBUS_ARGS({ "value", "ay" }),
characteristic_read_value) },
{ GDBUS_EXPERIMENTAL_METHOD("StopNotify", NULL, NULL,
characteristic_stop_notify) },
{ }
+#endif
};
static void characteristic_free(void *data)
{
struct characteristic *chrc = data;
- queue_destroy(chrc->descs, NULL); /* List should be empty here */
+ /* List should be empty here */
+ queue_destroy(chrc->descs, NULL);
+ queue_destroy(chrc->notify_clients, NULL);
+
g_free(chrc->path);
free(chrc);
}
return NULL;
}
+ chrc->notify_clients = queue_new();
+ if (!chrc->notify_clients) {
+ queue_destroy(chrc->descs, NULL);
+ free(chrc);
+ return NULL;
+ }
+
chrc->service = service;
gatt_db_attribute_get_char_data(attr, &chrc->handle,
return chrc;
}
+static void remove_client(void *data)
+{
+ struct notify_client *ntfy_client = data;
+ struct btd_gatt_client *client = ntfy_client->chrc->service->client;
+
+ queue_remove(client->all_notify_clients, ntfy_client);
+
+ notify_client_unref(ntfy_client);
+}
+
static void unregister_characteristic(void *data)
{
struct characteristic *chrc = data;
+ struct bt_gatt_client *gatt = chrc->service->client->gatt;
DBG("Removing GATT characteristic: %s", chrc->path);
+ if (chrc->read_id)
+ bt_gatt_client_cancel(gatt, chrc->read_id);
+
+ if (chrc->write_id)
+ bt_gatt_client_cancel(gatt, chrc->write_id);
+
+ queue_remove_all(chrc->notify_clients, NULL, NULL, remove_client);
queue_remove_all(chrc->descs, NULL, NULL, unregister_descriptor);
g_dbus_unregister_interface(btd_get_dbus_connection(), chrc->path,
}
static const GDBusPropertyTable service_properties[] = {
+#ifdef __TIZEN_PATCH__
+ { "UUID", "s", service_get_uuid },
+ { "Device", "o", service_get_device },
+ { "Primary", "b", service_get_primary },
+ { "Characteristics", "ao", service_get_characteristics },
+ { }
+#else
{ "UUID", "s", service_get_uuid, NULL, NULL,
G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
{ "Device", "o", service_get_device, NULL, NULL,
{ "Characteristics", "ao", service_get_characteristics, NULL, NULL,
G_DBUS_PROPERTY_FLAG_EXPERIMENTAL },
{ }
+#endif
};
static void service_free(void *data)
struct service *service = data;
queue_destroy(service->chrcs, NULL); /* List should be empty here */
+ queue_destroy(service->pending_ext_props, NULL);
g_free(service->path);
free(service);
}
return NULL;
}
+ service->pending_ext_props = queue_new();
+ if (!service->pending_ext_props) {
+ queue_destroy(service->chrcs, NULL);
+ free(service);
+ return NULL;
+ }
+
service->client = client;
gatt_db_attribute_get_service_data(attr, &service->start_handle,
DBG("Removing GATT service: %s", service->path);
+ if (service->idle_id)
+ g_source_remove(service->idle_id);
+
queue_remove_all(service->chrcs, NULL, NULL, unregister_characteristic);
g_dbus_unregister_interface(btd_get_dbus_connection(), service->path,
GATT_SERVICE_IFACE);
}
+static void notify_chrcs(struct service *service)
+{
+
+ if (service->chrcs_ready ||
+ !queue_isempty(service->pending_ext_props))
+ return;
+
+ service->chrcs_ready = true;
+
+ g_dbus_emit_property_changed(btd_get_dbus_connection(), service->path,
+ GATT_SERVICE_IFACE,
+ "Characteristics");
+}
+
struct export_data {
void *root;
bool failed;
queue_push_tail(charac->descs, desc);
}
+static void read_ext_props_cb(bool success, uint8_t att_ecode,
+ const uint8_t *value, uint16_t length,
+ void *user_data)
+{
+ struct characteristic *chrc = user_data;
+ struct service *service = chrc->service;
+
+ if (!success) {
+ error("Failed to obtain extended properties - error: 0x%02x",
+ att_ecode);
+ return;
+ }
+
+ if (!value || length != 2) {
+ error("Malformed extended properties value");
+ return;
+ }
+
+ chrc->ext_props = get_le16(value);
+ if (chrc->ext_props)
+ g_dbus_emit_property_changed(btd_get_dbus_connection(),
+ chrc->path,
+ GATT_CHARACTERISTIC_IFACE,
+ "Flags");
+
+ queue_remove(service->pending_ext_props, chrc);
+
+ notify_chrcs(service);
+}
+
+static void read_ext_props(void *data, void *user_data)
+{
+ struct characteristic *chrc = data;
+
+ bt_gatt_client_read_value(chrc->service->client->gatt,
+ chrc->ext_props_handle,
+ read_ext_props_cb,
+ chrc, NULL);
+}
+
static bool create_descriptors(struct gatt_db_attribute *attr,
struct characteristic *charac)
{
queue_push_tail(service->chrcs, charac);
+ if (charac->ext_props_handle)
+ queue_push_tail(service->pending_ext_props, charac);
+
return;
fail:
gatt_db_service_foreach_char(attr, export_char, &data);
- return !data.failed;
-}
+ if (data.failed)
+ return false;
-static void notify_chrcs(void *data, void *user_data)
-{
- struct service *service = data;
+ /* Obtain extended properties */
+ queue_foreach(service->pending_ext_props, read_ext_props, NULL);
- service->chrcs_ready = true;
-
- g_dbus_emit_property_changed(btd_get_dbus_connection(), service->path,
- GATT_SERVICE_IFACE,
- "Characteristics");
+ return true;
}
static gboolean set_chrcs_ready(gpointer user_data)
{
- struct btd_gatt_client *client = user_data;
+ struct service *service = user_data;
- if (!client->gatt)
- return FALSE;
+ notify_chrcs(service);
- queue_foreach(client->services, notify_chrcs, NULL);
+#ifdef __TIZEN_PATCH__
+ device_set_gatt_connected(service->client->device, TRUE);
+#endif
return FALSE;
}
struct btd_gatt_client *client = user_data;
struct service *service;
+ if (gatt_db_service_get_claimed(attr))
+ return;
+
service = service_create(attr, client);
if (!service)
return;
}
queue_push_tail(client->services, service);
+
+ /*
+ * Asynchronously update the "Characteristics" property of the service.
+ * If there are any pending reads to obtain the value of the "Extended
+ * Properties" descriptor then wait until they are complete.
+ */
+ if (!service->chrcs_ready && queue_isempty(service->pending_ext_props))
+ service->idle_id = g_idle_add(set_chrcs_ready, service);
}
static void create_services(struct btd_gatt_client *client)
DBG("Exporting objects for GATT services: %s", client->devaddr);
gatt_db_foreach_service(client->db, NULL, export_service, client);
-
- /*
- * Asynchronously update the "Characteristics" property of each service.
- * We do this so that users have a way to know that all characteristics
- * of a service have been exported.
- */
- g_idle_add(set_chrcs_ready, client);
}
struct btd_gatt_client *btd_gatt_client_new(struct btd_device *device)
return NULL;
}
+ client->all_notify_clients = queue_new();
+ if (!client->all_notify_clients) {
+ queue_destroy(client->services, NULL);
+ free(client);
+ return NULL;
+ }
+
client->device = device;
ba2str(device_get_address(device), client->devaddr);
return;
queue_destroy(client->services, unregister_service);
+ queue_destroy(client->all_notify_clients, NULL);
bt_gatt_client_unref(client->gatt);
gatt_db_unref(client->db);
free(client);
}
+static void register_notify(void *data, void *user_data)
+{
+ struct notify_client *notify_client = data;
+ struct btd_gatt_client *client = user_data;
+ struct async_dbus_op *op;
+
+ DBG("Re-register subscribed notification client");
+
+ op = new0(struct async_dbus_op, 1);
+ if (!op)
+ goto fail;
+
+ op->data = notify_client;
+
+ notify_client->notify_id = bt_gatt_client_register_notify(client->gatt,
+ notify_client->chrc->value_handle,
+ register_notify_cb, notify_cb,
+ op, async_dbus_op_free);
+ if (notify_client->notify_id)
+ return;
+
+ async_dbus_op_free(op);
+
+fail:
+ DBG("Failed to re-register notification client");
+
+ queue_remove(notify_client->chrc->notify_clients, client);
+ queue_remove(client->all_notify_clients, client);
+
+ notify_client_free(notify_client);
+}
+
void btd_gatt_client_ready(struct btd_gatt_client *client)
{
struct bt_gatt_client *gatt;
bt_gatt_client_unref(client->gatt);
client->gatt = bt_gatt_client_ref(gatt);
- create_services(client);
+ client->ready = true;
+
+ DBG("GATT client ready");
+
+ if (queue_isempty(client->services)) {
+ DBG("Exporting services");
+ create_services(client);
+ return;
+ }
+
+ /*
+ * Services have already been created before. Re-enable notifications
+ * for any pre-registered notification sessions.
+ */
+ queue_foreach(client->all_notify_clients, register_notify, client);
+
+#ifdef __TIZEN_PATCH__
+ device_set_gatt_connected(client->device, TRUE);
+#endif
}
void btd_gatt_client_service_added(struct btd_gatt_client *client,
struct gatt_db_attribute *attrib)
{
- /* TODO */
+ if (!client || !attrib || !client->ready)
+ return;
+
+ export_service(attrib, client);
+}
+
+static bool match_service_handle(const void *a, const void *b)
+{
+ const struct service *service = a;
+ uint16_t start_handle = PTR_TO_UINT(b);
+
+ return service->start_handle == start_handle;
}
void btd_gatt_client_service_removed(struct btd_gatt_client *client,
struct gatt_db_attribute *attrib)
{
- /* TODO */
+ uint16_t start_handle, end_handle;
+
+ if (!client || !attrib || !client->ready)
+ return;
+
+ gatt_db_attribute_get_service_handles(attrib, &start_handle,
+ &end_handle);
+
+ DBG("GATT Services Removed - start: 0x%04x, end: 0x%04x", start_handle,
+ end_handle);
+ queue_remove_all(client->services, match_service_handle,
+ UINT_TO_PTR(start_handle),
+ unregister_service);
+}
+
+static void clear_notify_id(void *data, void *user_data)
+{
+ struct notify_client *client = data;
+
+ client->notify_id = 0;
+}
+
+static void cancel_desc_ops(void *data, void *user_data)
+{
+ struct descriptor *desc = data;
+ struct bt_gatt_client *gatt = user_data;
+
+ if (desc->read_id) {
+ bt_gatt_client_cancel(gatt, desc->read_id);
+ desc->read_id = 0;
+ }
+
+ if (desc->write_id) {
+ bt_gatt_client_cancel(gatt, desc->write_id);
+ desc->write_id = 0;
+ }
+}
+
+static void cancel_chrc_ops(void *data, void *user_data)
+{
+ struct characteristic *chrc = data;
+ struct bt_gatt_client *gatt = user_data;
+
+ if (chrc->read_id) {
+ bt_gatt_client_cancel(gatt, chrc->read_id);
+ chrc->read_id = 0;
+ }
+
+ if (chrc->write_id) {
+ bt_gatt_client_cancel(gatt, chrc->write_id);
+ chrc->write_id = 0;
+ }
+
+ queue_foreach(chrc->descs, cancel_desc_ops, user_data);
+}
+
+static void cancel_ops(void *data, void *user_data)
+{
+ struct service *service = data;
+
+ queue_foreach(service->chrcs, cancel_chrc_ops, user_data);
}
void btd_gatt_client_disconnected(struct btd_gatt_client *client)
{
- if (!client)
+ if (!client || !client->gatt)
return;
- DBG("Device disconnected. Cleaning up");
+ DBG("Device disconnected. Cleaning up.");
/*
* Remove all services. We'll recreate them when a new bt_gatt_client
- * becomes ready.
+ * becomes ready. Leave the services around if the device is bonded.
+ * TODO: Once GATT over BR/EDR is properly supported, we should pass the
+ * correct bdaddr_type based on the transport over which GATT is being
+ * done.
*/
- queue_remove_all(client->services, NULL, NULL, unregister_service);
+ if (!device_is_bonded(client->device, BDADDR_LE_PUBLIC)) {
+ DBG("Device not bonded. Removing exported services.");
+ queue_remove_all(client->services, NULL, NULL,
+ unregister_service);
+ } else {
+ DBG("Device is bonded. Keeping exported services up.");
+ queue_foreach(client->all_notify_clients, clear_notify_id,
+ NULL);
+ queue_foreach(client->services, cancel_ops, client->gatt);
+ }
bt_gatt_client_unref(client->gatt);
client->gatt = NULL;
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2015 Google 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.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdint.h>
+#include <stdlib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "lib/uuid.h"
+#include "btio/btio.h"
+#include "gdbus/gdbus.h"
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+#include "src/shared/att.h"
+#include "src/shared/gatt-db.h"
+#include "src/shared/gatt-server.h"
+#include "log.h"
+#include "error.h"
+#include "adapter.h"
+#include "device.h"
+#include "gatt-database.h"
+#include "dbus-common.h"
+
+#ifndef ATT_CID
+#define ATT_CID 4
+#endif
+
+#ifndef ATT_PSM
+#define ATT_PSM 31
+#endif
+
+#define GATT_MANAGER_IFACE "org.bluez.GattManager1"
+#define GATT_SERVICE_IFACE "org.bluez.GattService1"
+#define GATT_CHRC_IFACE "org.bluez.GattCharacteristic1"
+#define GATT_DESC_IFACE "org.bluez.GattDescriptor1"
+
+#define UUID_GAP 0x1800
+#define UUID_GATT 0x1801
+
+#ifndef MIN
+#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#endif
+
+struct btd_gatt_database {
+ struct btd_adapter *adapter;
+ struct gatt_db *db;
+ unsigned int db_id;
+ GIOChannel *le_io;
+ GIOChannel *l2cap_io;
+ uint32_t gap_handle;
+ uint32_t gatt_handle;
+ struct queue *device_states;
+ struct queue *ccc_callbacks;
+ struct gatt_db_attribute *svc_chngd;
+ struct gatt_db_attribute *svc_chngd_ccc;
+ struct queue *services;
+};
+
+struct external_service {
+ struct btd_gatt_database *database;
+ bool failed;
+ char *owner;
+ char *path; /* Path to GattService1 */
+ DBusMessage *reg;
+ GDBusClient *client;
+ GDBusProxy *proxy;
+ struct gatt_db_attribute *attrib;
+ uint16_t attr_cnt;
+ struct queue *chrcs;
+ struct queue *descs;
+};
+
+struct external_chrc {
+ struct external_service *service;
+ char *path;
+ GDBusProxy *proxy;
+ uint8_t props;
+ uint8_t ext_props;
+ struct gatt_db_attribute *attrib;
+ struct gatt_db_attribute *ccc;
+ struct queue *pending_reads;
+ struct queue *pending_writes;
+ unsigned int ntfy_cnt;
+};
+
+struct external_desc {
+ struct external_service *service;
+ char *chrc_path;
+ GDBusProxy *proxy;
+ struct gatt_db_attribute *attrib;
+ bool handled;
+ struct queue *pending_reads;
+ struct queue *pending_writes;
+};
+
+struct pending_op {
+ unsigned int id;
+ struct gatt_db_attribute *attrib;
+ struct queue *owner_queue;
+ void *setup_data;
+};
+
+struct device_state {
+ bdaddr_t bdaddr;
+ uint8_t bdaddr_type;
+ struct queue *ccc_states;
+};
+
+struct ccc_state {
+ uint16_t handle;
+ uint8_t value[2];
+};
+
+struct ccc_cb_data {
+ uint16_t handle;
+ btd_gatt_database_ccc_write_t callback;
+ btd_gatt_database_destroy_t destroy;
+ void *user_data;
+};
+
+struct device_info {
+ bdaddr_t bdaddr;
+ uint8_t bdaddr_type;
+};
+
+static void ccc_cb_free(void *data)
+{
+ struct ccc_cb_data *ccc_cb = data;
+
+ if (ccc_cb->destroy)
+ ccc_cb->destroy(ccc_cb->user_data);
+
+ free(ccc_cb);
+}
+
+static bool ccc_cb_match_service(const void *data, const void *match_data)
+{
+ const struct ccc_cb_data *ccc_cb = data;
+ const struct gatt_db_attribute *attrib = match_data;
+ uint16_t start, end;
+
+ if (!gatt_db_attribute_get_service_handles(attrib, &start, &end))
+ return false;
+
+ return ccc_cb->handle >= start && ccc_cb->handle <= end;
+}
+
+static bool ccc_cb_match_handle(const void *data, const void *match_data)
+{
+ const struct ccc_cb_data *ccc_cb = data;
+ uint16_t handle = PTR_TO_UINT(match_data);
+
+ return ccc_cb->handle == handle;
+}
+
+static bool dev_state_match(const void *a, const void *b)
+{
+ const struct device_state *dev_state = a;
+ const struct device_info *dev_info = b;
+
+ return bacmp(&dev_state->bdaddr, &dev_info->bdaddr) == 0 &&
+ dev_state->bdaddr_type == dev_info->bdaddr_type;
+}
+
+static struct device_state *
+find_device_state(struct btd_gatt_database *database, bdaddr_t *bdaddr,
+ uint8_t bdaddr_type)
+{
+ struct device_info dev_info;
+
+ memset(&dev_info, 0, sizeof(dev_info));
+
+ bacpy(&dev_info.bdaddr, bdaddr);
+ dev_info.bdaddr_type = bdaddr_type;
+
+ return queue_find(database->device_states, dev_state_match, &dev_info);
+}
+
+static bool ccc_state_match(const void *a, const void *b)
+{
+ const struct ccc_state *ccc = a;
+ uint16_t handle = PTR_TO_UINT(b);
+
+ return ccc->handle == handle;
+}
+
+static struct ccc_state *find_ccc_state(struct device_state *dev_state,
+ uint16_t handle)
+{
+ return queue_find(dev_state->ccc_states, ccc_state_match,
+ UINT_TO_PTR(handle));
+}
+
+static struct device_state *device_state_create(bdaddr_t *bdaddr,
+ uint8_t bdaddr_type)
+{
+ struct device_state *dev_state;
+
+ dev_state = new0(struct device_state, 1);
+ if (!dev_state)
+ return NULL;
+
+ dev_state->ccc_states = queue_new();
+ if (!dev_state->ccc_states) {
+ free(dev_state);
+ return NULL;
+ }
+
+ bacpy(&dev_state->bdaddr, bdaddr);
+ dev_state->bdaddr_type = bdaddr_type;
+
+ return dev_state;
+}
+
+static struct device_state *get_device_state(struct btd_gatt_database *database,
+ bdaddr_t *bdaddr,
+ uint8_t bdaddr_type)
+{
+ struct device_state *dev_state;
+
+ /*
+ * Find and return a device state. If a matching state doesn't exist,
+ * then create a new one.
+ */
+ dev_state = find_device_state(database, bdaddr, bdaddr_type);
+ if (dev_state)
+ return dev_state;
+
+ dev_state = device_state_create(bdaddr, bdaddr_type);
+ if (!dev_state)
+ return NULL;
+
+ queue_push_tail(database->device_states, dev_state);
+
+ return dev_state;
+}
+
+static struct ccc_state *get_ccc_state(struct btd_gatt_database *database,
+ bdaddr_t *bdaddr,
+ uint8_t bdaddr_type,
+ uint16_t handle)
+{
+ struct device_state *dev_state;
+ struct ccc_state *ccc;
+
+ dev_state = get_device_state(database, bdaddr, bdaddr_type);
+ if (!dev_state)
+ return NULL;
+
+ ccc = find_ccc_state(dev_state, handle);
+ if (ccc)
+ return ccc;
+
+ ccc = new0(struct ccc_state, 1);
+ if (!ccc)
+ return NULL;
+
+ ccc->handle = handle;
+ queue_push_tail(dev_state->ccc_states, ccc);
+
+ return ccc;
+}
+
+static void device_state_free(void *data)
+{
+ struct device_state *state = data;
+
+ queue_destroy(state->ccc_states, free);
+ free(state);
+}
+
+static void cancel_pending_read(void *data)
+{
+ struct pending_op *op = data;
+
+ gatt_db_attribute_read_result(op->attrib, op->id,
+ BT_ATT_ERROR_REQUEST_NOT_SUPPORTED,
+ NULL, 0);
+ op->owner_queue = NULL;
+}
+
+static void cancel_pending_write(void *data)
+{
+ struct pending_op *op = data;
+
+ gatt_db_attribute_write_result(op->attrib, op->id,
+ BT_ATT_ERROR_REQUEST_NOT_SUPPORTED);
+ op->owner_queue = NULL;
+}
+
+static void chrc_free(void *data)
+{
+ struct external_chrc *chrc = data;
+
+ queue_destroy(chrc->pending_reads, cancel_pending_read);
+ queue_destroy(chrc->pending_writes, cancel_pending_write);
+
+ g_free(chrc->path);
+
+ g_dbus_proxy_set_property_watch(chrc->proxy, NULL, NULL);
+ g_dbus_proxy_unref(chrc->proxy);
+
+ free(chrc);
+}
+
+static void desc_free(void *data)
+{
+ struct external_desc *desc = data;
+
+ queue_destroy(desc->pending_reads, cancel_pending_read);
+ queue_destroy(desc->pending_writes, cancel_pending_write);
+
+ g_dbus_proxy_unref(desc->proxy);
+ g_free(desc->chrc_path);
+
+ free(desc);
+}
+
+static void service_free(void *data)
+{
+ struct external_service *service = data;
+
+ queue_destroy(service->chrcs, chrc_free);
+ queue_destroy(service->descs, desc_free);
+
+ gatt_db_remove_service(service->database->db, service->attrib);
+
+ if (service->client) {
+ g_dbus_client_set_disconnect_watch(service->client, NULL, NULL);
+ g_dbus_client_set_proxy_handlers(service->client, NULL, NULL,
+ NULL, NULL);
+ g_dbus_client_set_ready_watch(service->client, NULL, NULL);
+ g_dbus_proxy_unref(service->proxy);
+ g_dbus_client_unref(service->client);
+ }
+
+ if (service->reg)
+ dbus_message_unref(service->reg);
+
+ g_free(service->owner);
+ g_free(service->path);
+
+ free(service);
+}
+
+static void gatt_database_free(void *data)
+{
+ struct btd_gatt_database *database = data;
+
+ if (database->le_io) {
+ g_io_channel_shutdown(database->le_io, FALSE, NULL);
+ g_io_channel_unref(database->le_io);
+ }
+
+ if (database->l2cap_io) {
+ g_io_channel_shutdown(database->l2cap_io, FALSE, NULL);
+ g_io_channel_unref(database->l2cap_io);
+ }
+
+ if (database->gatt_handle)
+ adapter_service_remove(database->adapter,
+ database->gatt_handle);
+
+ if (database->gap_handle)
+ adapter_service_remove(database->adapter, database->gap_handle);
+
+ /* TODO: Persistently store CCC states before freeing them */
+ gatt_db_unregister(database->db, database->db_id);
+
+ queue_destroy(database->device_states, device_state_free);
+ queue_destroy(database->services, service_free);
+ queue_destroy(database->ccc_callbacks, ccc_cb_free);
+ database->device_states = NULL;
+ database->ccc_callbacks = NULL;
+
+ gatt_db_unref(database->db);
+
+ btd_adapter_unref(database->adapter);
+ free(database);
+}
+
+static void connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
+{
+ struct btd_adapter *adapter;
+ struct btd_device *device;
+ uint8_t dst_type;
+ bdaddr_t src, dst;
+
+ DBG("New incoming LE ATT connection");
+
+ if (gerr) {
+ error("%s", gerr->message);
+ return;
+ }
+
+ bt_io_get(io, &gerr, BT_IO_OPT_SOURCE_BDADDR, &src,
+ BT_IO_OPT_DEST_BDADDR, &dst,
+ BT_IO_OPT_DEST_TYPE, &dst_type,
+ BT_IO_OPT_INVALID);
+ if (gerr) {
+ error("bt_io_get: %s", gerr->message);
+ g_error_free(gerr);
+ return;
+ }
+
+ adapter = adapter_find(&src);
+ if (!adapter)
+ return;
+
+ device = btd_adapter_get_device(adapter, &dst, dst_type);
+ if (!device)
+ return;
+
+ device_attach_att(device, io);
+}
+
+static void gap_device_name_read_cb(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ struct btd_gatt_database *database = user_data;
+ uint8_t error = 0;
+ size_t len = 0;
+ const uint8_t *value = NULL;
+ const char *device_name;
+
+ DBG("GAP Device Name read request\n");
+
+ device_name = btd_adapter_get_name(database->adapter);
+ len = strlen(device_name);
+
+ if (offset > len) {
+ error = BT_ATT_ERROR_INVALID_OFFSET;
+ goto done;
+ }
+
+ len -= offset;
+ value = len ? (const uint8_t *) &device_name[offset] : NULL;
+
+done:
+ gatt_db_attribute_read_result(attrib, id, error, value, len);
+}
+
+static void gap_appearance_read_cb(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ struct btd_gatt_database *database = user_data;
+ uint8_t error = 0;
+ size_t len = 2;
+ const uint8_t *value = NULL;
+ uint8_t appearance[2];
+ uint32_t dev_class;
+
+ DBG("GAP Appearance read request\n");
+
+ dev_class = btd_adapter_get_class(database->adapter);
+
+ if (offset > 2) {
+ error = BT_ATT_ERROR_INVALID_OFFSET;
+ goto done;
+ }
+
+ appearance[0] = dev_class & 0x00ff;
+ appearance[1] = (dev_class >> 8) & 0x001f;
+
+ len -= offset;
+ value = len ? &appearance[offset] : NULL;
+
+done:
+ gatt_db_attribute_read_result(attrib, id, error, value, len);
+}
+
+static sdp_record_t *record_new(uuid_t *uuid, uint16_t start, uint16_t end)
+{
+ sdp_list_t *svclass_id, *apseq, *proto[2], *root, *aproto;
+ uuid_t root_uuid, proto_uuid, l2cap;
+ sdp_record_t *record;
+ sdp_data_t *psm, *sh, *eh;
+ uint16_t lp = ATT_PSM;
+
+ if (uuid == NULL)
+ return NULL;
+
+ if (start > end)
+ return NULL;
+
+ record = sdp_record_alloc();
+ if (record == NULL)
+ return NULL;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(record, root);
+ sdp_list_free(root, NULL);
+
+ svclass_id = sdp_list_append(NULL, uuid);
+ sdp_set_service_classes(record, svclass_id);
+ sdp_list_free(svclass_id, NULL);
+
+ sdp_uuid16_create(&l2cap, L2CAP_UUID);
+ proto[0] = sdp_list_append(NULL, &l2cap);
+ psm = sdp_data_alloc(SDP_UINT16, &lp);
+ proto[0] = sdp_list_append(proto[0], psm);
+ apseq = sdp_list_append(NULL, proto[0]);
+
+ sdp_uuid16_create(&proto_uuid, ATT_UUID);
+ proto[1] = sdp_list_append(NULL, &proto_uuid);
+ sh = sdp_data_alloc(SDP_UINT16, &start);
+ proto[1] = sdp_list_append(proto[1], sh);
+ eh = sdp_data_alloc(SDP_UINT16, &end);
+ proto[1] = sdp_list_append(proto[1], eh);
+ apseq = sdp_list_append(apseq, proto[1]);
+
+ aproto = sdp_list_append(NULL, apseq);
+ sdp_set_access_protos(record, aproto);
+
+ sdp_data_free(psm);
+ sdp_data_free(sh);
+ sdp_data_free(eh);
+ sdp_list_free(proto[0], NULL);
+ sdp_list_free(proto[1], NULL);
+ sdp_list_free(apseq, NULL);
+ sdp_list_free(aproto, NULL);
+
+ return record;
+}
+
+static uint32_t database_add_record(struct btd_gatt_database *database,
+ uint16_t uuid,
+ struct gatt_db_attribute *attr,
+ const char *name)
+{
+ sdp_record_t *record;
+ uint16_t start, end;
+ uuid_t svc, gap_uuid;
+
+ sdp_uuid16_create(&svc, uuid);
+ gatt_db_attribute_get_service_handles(attr, &start, &end);
+
+ record = record_new(&svc, start, end);
+ if (!record)
+ return 0;
+
+ if (name != NULL)
+ sdp_set_info_attr(record, name, "BlueZ", NULL);
+
+ sdp_uuid16_create(&gap_uuid, UUID_GAP);
+ if (sdp_uuid_cmp(&svc, &gap_uuid) == 0) {
+ sdp_set_url_attr(record, "http://www.bluez.org/",
+ "http://www.bluez.org/",
+ "http://www.bluez.org/");
+ }
+
+ if (adapter_service_add(database->adapter, record) == 0)
+ return record->handle;
+
+ sdp_record_free(record);
+ return 0;
+}
+
+static void populate_gap_service(struct btd_gatt_database *database)
+{
+ bt_uuid_t uuid;
+ struct gatt_db_attribute *service;
+
+ /* Add the GAP service */
+ bt_uuid16_create(&uuid, UUID_GAP);
+ service = gatt_db_add_service(database->db, &uuid, true, 5);
+ database->gap_handle = database_add_record(database, UUID_GAP, service,
+ "Generic Access Profile");
+
+ /*
+ * Device Name characteristic.
+ */
+ bt_uuid16_create(&uuid, GATT_CHARAC_DEVICE_NAME);
+ gatt_db_service_add_characteristic(service, &uuid, BT_ATT_PERM_READ,
+ BT_GATT_CHRC_PROP_READ,
+ gap_device_name_read_cb,
+ NULL, database);
+
+ /*
+ * Device Appearance characteristic.
+ */
+ bt_uuid16_create(&uuid, GATT_CHARAC_APPEARANCE);
+ gatt_db_service_add_characteristic(service, &uuid, BT_ATT_PERM_READ,
+ BT_GATT_CHRC_PROP_READ,
+ gap_appearance_read_cb,
+ NULL, database);
+
+ gatt_db_service_set_active(service, true);
+}
+
+static bool get_dst_info(struct bt_att *att, bdaddr_t *dst, uint8_t *dst_type)
+{
+ GIOChannel *io = NULL;
+ GError *gerr = NULL;
+
+ io = g_io_channel_unix_new(bt_att_get_fd(att));
+ if (!io)
+ return false;
+
+ bt_io_get(io, &gerr, BT_IO_OPT_DEST_BDADDR, dst,
+ BT_IO_OPT_DEST_TYPE, dst_type,
+ BT_IO_OPT_INVALID);
+ if (gerr) {
+ error("gatt: bt_io_get: %s", gerr->message);
+ g_error_free(gerr);
+ g_io_channel_unref(io);
+ return false;
+ }
+
+ g_io_channel_unref(io);
+ return true;
+}
+
+static void gatt_ccc_read_cb(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ struct btd_gatt_database *database = user_data;
+ struct ccc_state *ccc;
+ uint16_t handle;
+ uint8_t ecode = 0;
+ const uint8_t *value = NULL;
+ size_t len = 0;
+ bdaddr_t bdaddr;
+ uint8_t bdaddr_type;
+
+ handle = gatt_db_attribute_get_handle(attrib);
+
+ DBG("CCC read called for handle: 0x%04x", handle);
+
+ if (offset > 2) {
+ ecode = BT_ATT_ERROR_INVALID_OFFSET;
+ goto done;
+ }
+
+ if (!get_dst_info(att, &bdaddr, &bdaddr_type)) {
+ ecode = BT_ATT_ERROR_UNLIKELY;
+ goto done;
+ }
+
+ ccc = get_ccc_state(database, &bdaddr, bdaddr_type, handle);
+ if (!ccc) {
+ ecode = BT_ATT_ERROR_UNLIKELY;
+ goto done;
+ }
+
+ len = 2 - offset;
+ value = len ? &ccc->value[offset] : NULL;
+
+done:
+ gatt_db_attribute_read_result(attrib, id, ecode, value, len);
+}
+
+static void gatt_ccc_write_cb(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ const uint8_t *value, size_t len,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ struct btd_gatt_database *database = user_data;
+ struct ccc_state *ccc;
+ struct ccc_cb_data *ccc_cb;
+ uint16_t handle;
+ uint8_t ecode = 0;
+ bdaddr_t bdaddr;
+ uint8_t bdaddr_type;
+
+ handle = gatt_db_attribute_get_handle(attrib);
+
+ DBG("CCC write called for handle: 0x%04x", handle);
+
+ if (!value || len != 2) {
+ ecode = BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN;
+ goto done;
+ }
+
+ if (offset > 2) {
+ ecode = BT_ATT_ERROR_INVALID_OFFSET;
+ goto done;
+ }
+
+ if (!get_dst_info(att, &bdaddr, &bdaddr_type)) {
+ ecode = BT_ATT_ERROR_UNLIKELY;
+ goto done;
+ }
+
+ ccc = get_ccc_state(database, &bdaddr, bdaddr_type, handle);
+ if (!ccc) {
+ ecode = BT_ATT_ERROR_UNLIKELY;
+ goto done;
+ }
+
+ ccc_cb = queue_find(database->ccc_callbacks, ccc_cb_match_handle,
+ UINT_TO_PTR(gatt_db_attribute_get_handle(attrib)));
+ if (!ccc_cb) {
+ ecode = BT_ATT_ERROR_UNLIKELY;
+ goto done;
+ }
+
+ /* If value is identical, then just succeed */
+ if (ccc->value[0] == value[0] && ccc->value[1] == value[1])
+ goto done;
+
+ if (ccc_cb->callback)
+ ecode = ccc_cb->callback(get_le16(value), ccc_cb->user_data);
+
+ if (!ecode) {
+ ccc->value[0] = value[0];
+ ccc->value[1] = value[1];
+ }
+
+done:
+ gatt_db_attribute_write_result(attrib, id, ecode);
+}
+
+static struct gatt_db_attribute *
+service_add_ccc(struct gatt_db_attribute *service,
+ struct btd_gatt_database *database,
+ btd_gatt_database_ccc_write_t write_callback,
+ void *user_data,
+ btd_gatt_database_destroy_t destroy)
+{
+ struct gatt_db_attribute *ccc;
+ struct ccc_cb_data *ccc_cb;
+ bt_uuid_t uuid;
+
+ ccc_cb = new0(struct ccc_cb_data, 1);
+ if (!ccc_cb) {
+ error("Could not allocate memory for callback data");
+ return NULL;
+ }
+
+ bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+ ccc = gatt_db_service_add_descriptor(service, &uuid,
+ BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+ gatt_ccc_read_cb, gatt_ccc_write_cb, database);
+ if (!ccc) {
+ error("Failed to create CCC entry in database");
+ free(ccc_cb);
+ return NULL;
+ }
+
+ ccc_cb->handle = gatt_db_attribute_get_handle(ccc);
+ ccc_cb->callback = write_callback;
+ ccc_cb->destroy = destroy;
+ ccc_cb->user_data = user_data;
+
+ queue_push_tail(database->ccc_callbacks, ccc_cb);
+
+ return ccc;
+}
+
+struct gatt_db_attribute *
+btd_gatt_database_add_ccc(struct btd_gatt_database *database,
+ uint16_t service_handle,
+ btd_gatt_database_ccc_write_t write_callback,
+ void *user_data,
+ btd_gatt_database_destroy_t destroy)
+{
+ struct gatt_db_attribute *service;
+
+ if (!database || !service_handle)
+ return NULL;
+
+ service = gatt_db_get_attribute(database->db, service_handle);
+ if (!service) {
+ error("No service exists with handle: 0x%04x", service_handle);
+ return NULL;
+ }
+
+ return service_add_ccc(service, database, write_callback, user_data,
+ destroy);
+}
+
+static void populate_gatt_service(struct btd_gatt_database *database)
+{
+ bt_uuid_t uuid;
+ struct gatt_db_attribute *service;
+
+ /* Add the GATT service */
+ bt_uuid16_create(&uuid, UUID_GATT);
+ service = gatt_db_add_service(database->db, &uuid, true, 4);
+ database->gatt_handle = database_add_record(database, UUID_GATT,
+ service,
+ "Generic Attribute Profile");
+
+ bt_uuid16_create(&uuid, GATT_CHARAC_SERVICE_CHANGED);
+ database->svc_chngd = gatt_db_service_add_characteristic(service, &uuid,
+ BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_INDICATE,
+ NULL, NULL, database);
+
+ database->svc_chngd_ccc = service_add_ccc(service, database, NULL, NULL,
+ NULL);
+
+ gatt_db_service_set_active(service, true);
+}
+
+static void register_core_services(struct btd_gatt_database *database)
+{
+ populate_gap_service(database);
+ populate_gatt_service(database);
+}
+
+struct notify {
+ struct btd_gatt_database *database;
+ uint16_t handle, ccc_handle;
+ const uint8_t *value;
+ uint16_t len;
+ bool indicate;
+};
+
+static void conf_cb(void *user_data)
+{
+ DBG("GATT server received confirmation");
+}
+
+static void send_notification_to_device(void *data, void *user_data)
+{
+ struct device_state *device_state = data;
+ struct notify *notify = user_data;
+ struct ccc_state *ccc;
+ struct btd_device *device;
+
+ ccc = find_ccc_state(device_state, notify->ccc_handle);
+ if (!ccc)
+ return;
+
+ if (!ccc->value[0] || (notify->indicate && !(ccc->value[0] & 0x02)))
+ return;
+
+ device = btd_adapter_get_device(notify->database->adapter,
+ &device_state->bdaddr,
+ device_state->bdaddr_type);
+ if (!device)
+ return;
+
+ /*
+ * TODO: If the device is not connected but bonded, send the
+ * notification/indication when it becomes connected.
+ */
+ if (!notify->indicate) {
+ DBG("GATT server sending notification");
+ bt_gatt_server_send_notification(
+ btd_device_get_gatt_server(device),
+ notify->handle, notify->value,
+ notify->len);
+ return;
+ }
+
+ DBG("GATT server sending indication");
+ bt_gatt_server_send_indication(btd_device_get_gatt_server(device),
+ notify->handle,
+ notify->value,
+ notify->len, conf_cb,
+ NULL, NULL);
+}
+
+static void send_notification_to_devices(struct btd_gatt_database *database,
+ uint16_t handle, const uint8_t *value,
+ uint16_t len, uint16_t ccc_handle,
+ bool indicate)
+{
+ struct notify notify;
+
+ memset(¬ify, 0, sizeof(notify));
+
+ notify.database = database;
+ notify.handle = handle;
+ notify.ccc_handle = ccc_handle;
+ notify.value = value;
+ notify.len = len;
+ notify.indicate = indicate;
+
+ queue_foreach(database->device_states, send_notification_to_device,
+ ¬ify);
+}
+
+static void send_service_changed(struct btd_gatt_database *database,
+ struct gatt_db_attribute *attrib)
+{
+ uint16_t start, end;
+ uint8_t value[4];
+ uint16_t handle, ccc_handle;
+
+ if (!gatt_db_attribute_get_service_handles(attrib, &start, &end)) {
+ error("Failed to obtain changed service handles");
+ return;
+ }
+
+ handle = gatt_db_attribute_get_handle(database->svc_chngd);
+ ccc_handle = gatt_db_attribute_get_handle(database->svc_chngd_ccc);
+
+ if (!handle || !ccc_handle) {
+ error("Failed to obtain handles for \"Service Changed\""
+ " characteristic");
+ return;
+ }
+
+ put_le16(start, value);
+ put_le16(end, value + 2);
+
+ send_notification_to_devices(database, handle, value, sizeof(value),
+ ccc_handle, true);
+}
+
+static void gatt_db_service_added(struct gatt_db_attribute *attrib,
+ void *user_data)
+{
+ struct btd_gatt_database *database = user_data;
+
+ DBG("GATT Service added to local database");
+
+ send_service_changed(database, attrib);
+}
+
+static bool ccc_match_service(const void *data, const void *match_data)
+{
+ const struct ccc_state *ccc = data;
+ const struct gatt_db_attribute *attrib = match_data;
+ uint16_t start, end;
+
+ if (!gatt_db_attribute_get_service_handles(attrib, &start, &end))
+ return false;
+
+ return ccc->handle >= start && ccc->handle <= end;
+}
+
+static void remove_device_ccc(void *data, void *user_data)
+{
+ struct device_state *state = data;
+
+ queue_remove_all(state->ccc_states, ccc_match_service, user_data, free);
+}
+
+static void gatt_db_service_removed(struct gatt_db_attribute *attrib,
+ void *user_data)
+{
+ struct btd_gatt_database *database = user_data;
+
+ DBG("Local GATT service removed");
+
+ send_service_changed(database, attrib);
+
+ queue_foreach(database->device_states, remove_device_ccc, attrib);
+ queue_remove_all(database->ccc_callbacks, ccc_cb_match_service, attrib,
+ ccc_cb_free);
+}
+
+struct svc_match_data {
+ const char *path;
+ const char *sender;
+};
+
+static bool match_service(const void *a, const void *b)
+{
+ const struct external_service *service = a;
+ const struct svc_match_data *data = b;
+
+ return g_strcmp0(service->path, data->path) == 0 &&
+ g_strcmp0(service->owner, data->sender) == 0;
+}
+
+static gboolean service_free_idle_cb(void *data)
+{
+ service_free(data);
+
+ return FALSE;
+}
+
+static void service_remove_helper(void *data)
+{
+ struct external_service *service = data;
+
+ queue_remove(service->database->services, service);
+
+ /*
+ * Do not run in the same loop, this may be a disconnect
+ * watch call and GDBusClient should not be destroyed.
+ */
+ g_idle_add(service_free_idle_cb, service);
+}
+
+static void client_disconnect_cb(DBusConnection *conn, void *user_data)
+{
+ DBG("Client disconnected");
+
+ service_remove_helper(user_data);
+}
+
+static void service_remove(void *data)
+{
+ struct external_service *service = data;
+
+ /*
+ * Set callback to NULL to avoid potential race condition
+ * when calling remove_service and GDBusClient unref.
+ */
+ g_dbus_client_set_disconnect_watch(service->client, NULL, NULL);
+
+ /*
+ * Set proxy handlers to NULL, so that this gets called only once when
+ * the first proxy that belongs to this service gets removed.
+ */
+ g_dbus_client_set_proxy_handlers(service->client, NULL, NULL,
+ NULL, NULL);
+
+ service_remove_helper(service);
+}
+
+static struct external_chrc *chrc_create(struct external_service *service,
+ GDBusProxy *proxy,
+ const char *path)
+{
+ struct external_chrc *chrc;
+
+ chrc = new0(struct external_chrc, 1);
+ if (!chrc)
+ return NULL;
+
+ chrc->pending_reads = queue_new();
+ if (!chrc->pending_reads) {
+ free(chrc);
+ return NULL;
+ }
+
+ chrc->pending_writes = queue_new();
+ if (!chrc->pending_writes) {
+ queue_destroy(chrc->pending_reads, NULL);
+ free(chrc);
+ return NULL;
+ }
+
+ chrc->path = g_strdup(path);
+ if (!chrc->path) {
+ queue_destroy(chrc->pending_reads, NULL);
+ queue_destroy(chrc->pending_writes, NULL);
+ free(chrc);
+ return NULL;
+ }
+
+ chrc->service = service;
+ chrc->proxy = g_dbus_proxy_ref(proxy);
+
+ return chrc;
+}
+
+static struct external_desc *desc_create(struct external_service *service,
+ GDBusProxy *proxy,
+ const char *chrc_path)
+{
+ struct external_desc *desc;
+
+ desc = new0(struct external_desc, 1);
+ if (!desc)
+ return NULL;
+
+ desc->pending_reads = queue_new();
+ if (!desc->pending_reads) {
+ free(desc);
+ return NULL;
+ }
+
+ desc->pending_writes = queue_new();
+ if (!desc->pending_writes) {
+ queue_destroy(desc->pending_reads, NULL);
+ free(desc);
+ return NULL;
+ }
+
+ desc->chrc_path = g_strdup(chrc_path);
+ if (!desc->chrc_path) {
+ queue_destroy(desc->pending_reads, NULL);
+ queue_destroy(desc->pending_writes, NULL);
+ free(desc);
+ return NULL;
+ }
+
+ desc->service = service;
+ desc->proxy = g_dbus_proxy_ref(proxy);
+
+ return desc;
+}
+
+static bool incr_attr_count(struct external_service *service, uint16_t incr)
+{
+ if (service->attr_cnt > UINT16_MAX - incr)
+ return false;
+
+ service->attr_cnt += incr;
+
+ return true;
+}
+
+static bool parse_path(GDBusProxy *proxy, const char *name, const char **path)
+{
+ DBusMessageIter iter;
+
+ if (!g_dbus_proxy_get_property(proxy, name, &iter))
+ return false;
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_OBJECT_PATH)
+ return false;
+
+ dbus_message_iter_get_basic(&iter, path);
+
+ return true;
+}
+
+static bool check_service_path(GDBusProxy *proxy,
+ struct external_service *service)
+{
+ const char *service_path;
+
+ if (!parse_path(proxy, "Service", &service_path))
+ return false;
+
+ return g_strcmp0(service_path, service->path) == 0;
+}
+
+static bool parse_flags(GDBusProxy *proxy, uint8_t *props, uint8_t *ext_props)
+{
+ DBusMessageIter iter, array;
+ const char *flag;
+
+ *props = *ext_props = 0;
+
+ if (!g_dbus_proxy_get_property(proxy, "Flags", &iter))
+ return false;
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY)
+ return false;
+
+ dbus_message_iter_recurse(&iter, &array);
+
+ do {
+ if (dbus_message_iter_get_arg_type(&array) != DBUS_TYPE_STRING)
+ return false;
+
+ dbus_message_iter_get_basic(&array, &flag);
+
+ if (!strcmp("broadcast", flag))
+ *props |= BT_GATT_CHRC_PROP_BROADCAST;
+ else if (!strcmp("read", flag))
+ *props |= BT_GATT_CHRC_PROP_READ;
+ else if (!strcmp("write-without-response", flag))
+ *props |= BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP;
+ else if (!strcmp("write", flag))
+ *props |= BT_GATT_CHRC_PROP_WRITE;
+ else if (!strcmp("notify", flag))
+ *props |= BT_GATT_CHRC_PROP_NOTIFY;
+ else if (!strcmp("indicate", flag))
+ *props |= BT_GATT_CHRC_PROP_INDICATE;
+ else if (!strcmp("authenticated-signed-writes", flag))
+ *props |= BT_GATT_CHRC_PROP_AUTH;
+ else if (!strcmp("reliable-write", flag))
+ *ext_props |= BT_GATT_CHRC_EXT_PROP_RELIABLE_WRITE;
+ else if (!strcmp("writable-auxiliaries", flag))
+ *ext_props |= BT_GATT_CHRC_EXT_PROP_WRITABLE_AUX;
+ else {
+ error("Invalid characteristic flag: %s", flag);
+ return false;
+ }
+ } while (dbus_message_iter_next(&array));
+
+ if (*ext_props)
+ *props |= BT_GATT_CHRC_PROP_EXT_PROP;
+
+ return true;
+}
+
+static void proxy_added_cb(GDBusProxy *proxy, void *user_data)
+{
+ struct external_service *service = user_data;
+ const char *iface, *path;
+
+ if (service->failed || service->attrib)
+ return;
+
+ iface = g_dbus_proxy_get_interface(proxy);
+ path = g_dbus_proxy_get_path(proxy);
+
+ if (!g_str_has_prefix(path, service->path))
+ return;
+
+ if (g_strcmp0(iface, GATT_SERVICE_IFACE) == 0) {
+ if (service->proxy)
+ return;
+
+ /*
+ * TODO: We may want to support adding included services in a
+ * single hierarchy.
+ */
+ if (g_strcmp0(path, service->path) != 0) {
+ error("Multiple services added within hierarchy");
+ service->failed = true;
+ return;
+ }
+
+ /* Add 1 for the service declaration */
+ if (!incr_attr_count(service, 1)) {
+ error("Failed to increment attribute count");
+ service->failed = true;
+ return;
+ }
+
+ service->proxy = g_dbus_proxy_ref(proxy);
+ } else if (g_strcmp0(iface, GATT_CHRC_IFACE) == 0) {
+ struct external_chrc *chrc;
+
+ if (g_strcmp0(path, service->path) == 0) {
+ error("Characteristic path same as service path");
+ service->failed = true;
+ return;
+ }
+
+ chrc = chrc_create(service, proxy, path);
+ if (!chrc) {
+ service->failed = true;
+ return;
+ }
+
+ /*
+ * Add 2 for the characteristic declaration and the value
+ * attribute.
+ */
+ if (!incr_attr_count(service, 2)) {
+ error("Failed to increment attribute count");
+ service->failed = true;
+ return;
+ }
+
+ /*
+ * Parse characteristic flags (i.e. properties) here since they
+ * are used to determine if any special descriptors should be
+ * created.
+ */
+ if (!parse_flags(proxy, &chrc->props, &chrc->ext_props)) {
+ error("Failed to parse characteristic properties");
+ service->failed = true;
+ return;
+ }
+
+ if ((chrc->props & BT_GATT_CHRC_PROP_NOTIFY ||
+ chrc->props & BT_GATT_CHRC_PROP_INDICATE) &&
+ !incr_attr_count(service, 1)) {
+ error("Failed to increment attribute count for CCC");
+ service->failed = true;
+ return;
+ }
+
+ if (chrc->ext_props && !incr_attr_count(service, 1)) {
+ error("Failed to increment attribute count for CEP");
+ service->failed = true;
+ return;
+ }
+
+ queue_push_tail(service->chrcs, chrc);
+ } else if (g_strcmp0(iface, GATT_DESC_IFACE) == 0) {
+ struct external_desc *desc;
+ const char *chrc_path;
+
+ if (!parse_path(proxy, "Characteristic", &chrc_path)) {
+ error("Failed to obtain characteristic path for "
+ "descriptor");
+ service->failed = true;
+ return;
+ }
+
+ desc = desc_create(service, proxy, chrc_path);
+ if (!desc) {
+ service->failed = true;
+ return;
+ }
+
+ /* Add 1 for the descriptor attribute */
+ if (!incr_attr_count(service, 1)) {
+ error("Failed to increment attribute count");
+ service->failed = true;
+ return;
+ }
+
+ queue_push_tail(service->descs, desc);
+ } else {
+ DBG("Ignoring unrelated interface: %s", iface);
+ return;
+ }
+
+ DBG("Object added to service - path: %s, iface: %s", path, iface);
+}
+
+static void proxy_removed_cb(GDBusProxy *proxy, void *user_data)
+{
+ struct external_service *service = user_data;
+ const char *path;
+
+ path = g_dbus_proxy_get_path(proxy);
+
+ if (!g_str_has_prefix(path, service->path))
+ return;
+
+ DBG("Proxy removed - removing service: %s", service->path);
+
+ service_remove(service);
+}
+
+static bool parse_uuid(GDBusProxy *proxy, bt_uuid_t *uuid)
+{
+ DBusMessageIter iter;
+ bt_uuid_t tmp;
+ const char *uuidstr;
+
+ if (!g_dbus_proxy_get_property(proxy, "UUID", &iter))
+ return false;
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+ return false;
+
+ dbus_message_iter_get_basic(&iter, &uuidstr);
+
+ if (bt_string_to_uuid(uuid, uuidstr) < 0)
+ return false;
+
+ /* GAP & GATT services are created and managed by BlueZ */
+ bt_uuid16_create(&tmp, UUID_GAP);
+ if (!bt_uuid_cmp(&tmp, uuid)) {
+ error("GAP service must be handled by BlueZ");
+ return false;
+ }
+
+ bt_uuid16_create(&tmp, UUID_GATT);
+ if (!bt_uuid_cmp(&tmp, uuid)) {
+ error("GATT service must be handled by BlueZ");
+ return false;
+ }
+
+ return true;
+}
+
+static bool parse_primary(GDBusProxy *proxy, bool *primary)
+{
+ DBusMessageIter iter;
+
+ if (!g_dbus_proxy_get_property(proxy, "Primary", &iter))
+ return false;
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BOOLEAN)
+ return false;
+
+ dbus_message_iter_get_basic(&iter, primary);
+ return true;
+}
+
+static uint8_t dbus_error_to_att_ecode(const char *error_name)
+{
+ /* TODO: Parse error ATT ecode from error_message */
+
+ if (strcmp(error_name, "org.bluez.Error.Failed") == 0)
+ return 0x80; /* For now return this "application error" */
+
+ if (strcmp(error_name, "org.bluez.Error.NotSupported") == 0)
+ return BT_ATT_ERROR_REQUEST_NOT_SUPPORTED;
+
+ if (strcmp(error_name, "org.bluez.Error.NotAuthorized") == 0)
+ return BT_ATT_ERROR_AUTHORIZATION;
+
+ if (strcmp(error_name, "org.bluez.Error.InvalidValueLength") == 0)
+ return BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN;
+
+ return 0;
+}
+
+static void read_reply_cb(DBusMessage *message, void *user_data)
+{
+ struct pending_op *op = user_data;
+ DBusError err;
+ DBusMessageIter iter, array;
+ uint8_t ecode = 0;
+ uint8_t *value = NULL;
+ int len = 0;
+
+ if (!op->owner_queue) {
+ DBG("Pending read was canceled when object got removed");
+ return;
+ }
+
+ dbus_error_init(&err);
+
+ if (dbus_set_error_from_message(&err, message) == TRUE) {
+ DBG("Failed to read value: %s: %s", err.name, err.message);
+ ecode = dbus_error_to_att_ecode(err.name);
+ ecode = ecode ? ecode : BT_ATT_ERROR_READ_NOT_PERMITTED;
+ dbus_error_free(&err);
+ goto done;
+ }
+
+ dbus_message_iter_init(message, &iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+ /*
+ * Return not supported for this, as the external app basically
+ * doesn't properly support reading from this characteristic.
+ */
+ ecode = BT_ATT_ERROR_REQUEST_NOT_SUPPORTED;
+ error("Invalid return value received for \"ReadValue\"");
+ goto done;
+ }
+
+ dbus_message_iter_recurse(&iter, &array);
+ dbus_message_iter_get_fixed_array(&array, &value, &len);
+
+ if (len < 0) {
+ ecode = BT_ATT_ERROR_REQUEST_NOT_SUPPORTED;
+ value = NULL;
+ len = 0;
+ goto done;
+ }
+
+ /* Truncate the value if it's too large */
+ len = MIN(BT_ATT_MAX_VALUE_LEN, len);
+ value = len ? value : NULL;
+
+done:
+ gatt_db_attribute_read_result(op->attrib, op->id, ecode, value, len);
+}
+
+static void pending_op_free(void *data)
+{
+ struct pending_op *op = data;
+
+ if (op->owner_queue)
+ queue_remove(op->owner_queue, op);
+
+ free(op);
+}
+
+static struct pending_op *pending_read_new(struct queue *owner_queue,
+ struct gatt_db_attribute *attrib,
+ unsigned int id)
+{
+ struct pending_op *op;
+
+ op = new0(struct pending_op, 1);
+ if (!op)
+ return NULL;
+
+ op->owner_queue = owner_queue;
+ op->attrib = attrib;
+ op->id = id;
+ queue_push_tail(owner_queue, op);
+
+ return op;
+}
+
+static void send_read(struct gatt_db_attribute *attrib, GDBusProxy *proxy,
+ struct queue *owner_queue,
+ unsigned int id)
+{
+ struct pending_op *op;
+ uint8_t ecode = BT_ATT_ERROR_UNLIKELY;
+
+ op = pending_read_new(owner_queue, attrib, id);
+ if (!op) {
+ error("Failed to allocate memory for pending read call");
+ ecode = BT_ATT_ERROR_INSUFFICIENT_RESOURCES;
+ goto error;
+ }
+
+ if (g_dbus_proxy_method_call(proxy, "ReadValue", NULL, read_reply_cb,
+ op, pending_op_free) == TRUE)
+ return;
+
+ pending_op_free(op);
+
+error:
+ gatt_db_attribute_read_result(attrib, id, ecode, NULL, 0);
+}
+
+static void write_setup_cb(DBusMessageIter *iter, void *user_data)
+{
+ struct pending_op *op = user_data;
+ struct iovec *iov = op->setup_data;
+ DBusMessageIter array;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array);
+ dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
+ &iov->iov_base, iov->iov_len);
+ dbus_message_iter_close_container(iter, &array);
+}
+
+static void write_reply_cb(DBusMessage *message, void *user_data)
+{
+ struct pending_op *op = user_data;
+ DBusError err;
+ DBusMessageIter iter;
+ uint8_t ecode = 0;
+
+ if (!op->owner_queue) {
+ DBG("Pending write was canceled when object got removed");
+ return;
+ }
+
+ dbus_error_init(&err);
+
+ if (dbus_set_error_from_message(&err, message) == TRUE) {
+ DBG("Failed to write value: %s: %s", err.name, err.message);
+ ecode = dbus_error_to_att_ecode(err.name);
+ ecode = ecode ? ecode : BT_ATT_ERROR_WRITE_NOT_PERMITTED;
+ dbus_error_free(&err);
+ goto done;
+ }
+
+ dbus_message_iter_init(message, &iter);
+ if (dbus_message_iter_has_next(&iter)) {
+ /*
+ * Return not supported for this, as the external app basically
+ * doesn't properly support the "WriteValue" API.
+ */
+ ecode = BT_ATT_ERROR_REQUEST_NOT_SUPPORTED;
+ error("Invalid return value received for \"WriteValue\"");
+ }
+
+done:
+ gatt_db_attribute_write_result(op->attrib, op->id, ecode);
+}
+
+static struct pending_op *pending_write_new(struct queue *owner_queue,
+ struct gatt_db_attribute *attrib,
+ unsigned int id,
+ const uint8_t *value,
+ size_t len)
+{
+ struct pending_op *op;
+ struct iovec iov;
+
+ op = new0(struct pending_op, 1);
+ if (!op)
+ return NULL;
+
+ iov.iov_base = (uint8_t *) value;
+ iov.iov_len = len;
+
+ op->owner_queue = owner_queue;
+ op->attrib = attrib;
+ op->id = id;
+ op->setup_data = &iov;
+ queue_push_tail(owner_queue, op);
+
+ return op;
+}
+
+static void send_write(struct gatt_db_attribute *attrib, GDBusProxy *proxy,
+ struct queue *owner_queue,
+ unsigned int id,
+ const uint8_t *value, size_t len)
+{
+ struct pending_op *op;
+ uint8_t ecode = BT_ATT_ERROR_UNLIKELY;
+
+ op = pending_write_new(owner_queue, attrib, id, value, len);
+ if (!op) {
+ error("Failed to allocate memory for pending read call");
+ ecode = BT_ATT_ERROR_INSUFFICIENT_RESOURCES;
+ goto error;
+ }
+
+ if (g_dbus_proxy_method_call(proxy, "WriteValue", write_setup_cb,
+ write_reply_cb, op,
+ pending_op_free) == TRUE)
+ return;
+
+ pending_op_free(op);
+
+error:
+ gatt_db_attribute_write_result(attrib, id, ecode);
+}
+
+static uint32_t permissions_from_props(uint8_t props, uint8_t ext_props)
+{
+ uint32_t perm = 0;
+
+ if (props & BT_GATT_CHRC_PROP_WRITE ||
+ props & BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP ||
+ ext_props & BT_GATT_CHRC_EXT_PROP_RELIABLE_WRITE)
+ perm |= BT_ATT_PERM_WRITE;
+
+ if (props & BT_GATT_CHRC_PROP_READ)
+ perm |= BT_ATT_PERM_READ;
+
+ return perm;
+}
+
+static uint8_t ccc_write_cb(uint16_t value, void *user_data)
+{
+ struct external_chrc *chrc = user_data;
+
+ DBG("External CCC write received with value: 0x%04x", value);
+
+ /* Notifications/indications disabled */
+ if (!value) {
+ if (!chrc->ntfy_cnt)
+ return 0;
+
+ if (__sync_sub_and_fetch(&chrc->ntfy_cnt, 1))
+ return 0;
+
+ /*
+ * Send request to stop notifying. This is best-effort
+ * operation, so simply ignore the return the value.
+ */
+ g_dbus_proxy_method_call(chrc->proxy, "StopNotify", NULL,
+ NULL, NULL, NULL);
+ return 0;
+ }
+
+ /*
+ * TODO: All of the errors below should fall into the so called
+ * "Application Error" range. Since there is no well defined error for
+ * these, we return a generic ATT protocol error for now.
+ */
+
+ if (chrc->ntfy_cnt == UINT_MAX) {
+ /* Maximum number of per-device CCC descriptors configured */
+ return BT_ATT_ERROR_REQUEST_NOT_SUPPORTED;
+ }
+
+ /* Don't support undefined CCC values yet */
+ if (value > 2 ||
+ (value == 1 && !(chrc->props & BT_GATT_CHRC_PROP_NOTIFY)) ||
+ (value == 2 && !(chrc->props & BT_GATT_CHRC_PROP_INDICATE)))
+ return BT_ATT_ERROR_REQUEST_NOT_SUPPORTED;
+
+ /*
+ * Always call StartNotify for an incoming enable and ignore the return
+ * value for now.
+ */
+ if (g_dbus_proxy_method_call(chrc->proxy,
+ "StartNotify", NULL, NULL,
+ NULL, NULL) == FALSE)
+ return BT_ATT_ERROR_REQUEST_NOT_SUPPORTED;
+
+ __sync_fetch_and_add(&chrc->ntfy_cnt, 1);
+
+ return 0;
+}
+
+static void property_changed_cb(GDBusProxy *proxy, const char *name,
+ DBusMessageIter *iter, void *user_data)
+{
+ struct external_chrc *chrc = user_data;
+ DBusMessageIter array;
+ uint8_t *value = NULL;
+ int len = 0;
+
+ if (strcmp(name, "Value"))
+ return;
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) {
+ DBG("Malformed \"Value\" property received");
+ return;
+ }
+
+ dbus_message_iter_recurse(iter, &array);
+ dbus_message_iter_get_fixed_array(&array, &value, &len);
+
+ if (len < 0) {
+ DBG("Malformed \"Value\" property received");
+ return;
+ }
+
+ /* Truncate the value if it's too large */
+ len = MIN(BT_ATT_MAX_VALUE_LEN, len);
+ value = len ? value : NULL;
+
+ send_notification_to_devices(chrc->service->database,
+ gatt_db_attribute_get_handle(chrc->attrib),
+ value, len,
+ gatt_db_attribute_get_handle(chrc->ccc),
+ chrc->props & BT_GATT_CHRC_PROP_INDICATE);
+}
+
+static bool database_add_ccc(struct external_service *service,
+ struct external_chrc *chrc)
+{
+ if (!(chrc->props & BT_GATT_CHRC_PROP_NOTIFY) &&
+ !(chrc->props & BT_GATT_CHRC_PROP_INDICATE))
+ return true;
+
+ chrc->ccc = service_add_ccc(service->attrib, service->database,
+ ccc_write_cb, chrc, NULL);
+ if (!chrc->ccc) {
+ error("Failed to create CCC entry for characteristic");
+ return false;
+ }
+
+ if (g_dbus_proxy_set_property_watch(chrc->proxy, property_changed_cb,
+ chrc) == FALSE) {
+ error("Failed to set up property watch for characteristic");
+ return false;
+ }
+
+ DBG("Created CCC entry for characteristic");
+
+ return true;
+}
+
+static void cep_write_cb(struct gatt_db_attribute *attrib, int err,
+ void *user_data)
+{
+ if (err)
+ DBG("Failed to store CEP value in the database");
+ else
+ DBG("Stored CEP value in the database");
+}
+
+static bool database_add_cep(struct external_service *service,
+ struct external_chrc *chrc)
+{
+ struct gatt_db_attribute *cep;
+ bt_uuid_t uuid;
+ uint8_t value[2];
+
+ if (!chrc->ext_props)
+ return true;
+
+ bt_uuid16_create(&uuid, GATT_CHARAC_EXT_PROPER_UUID);
+ cep = gatt_db_service_add_descriptor(service->attrib, &uuid,
+ BT_ATT_PERM_READ,
+ NULL, NULL, NULL);
+ if (!cep) {
+ error("Failed to create CEP entry for characteristic");
+ return false;
+ }
+
+ memset(value, 0, sizeof(value));
+ value[0] = chrc->ext_props;
+
+ if (!gatt_db_attribute_write(cep, 0, value, sizeof(value), 0, NULL,
+ cep_write_cb, NULL)) {
+ DBG("Failed to store CEP value in the database");
+ return false;
+ }
+
+ DBG("Created CEP entry for characteristic");
+
+ return true;
+}
+
+static void desc_read_cb(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ struct external_desc *desc = user_data;
+
+ if (desc->attrib != attrib) {
+ error("Read callback called with incorrect attribute");
+ return;
+ }
+
+ send_read(attrib, desc->proxy, desc->pending_reads, id);
+}
+
+static void desc_write_cb(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ const uint8_t *value, size_t len,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ struct external_desc *desc = user_data;
+
+ if (desc->attrib != attrib) {
+ error("Read callback called with incorrect attribute");
+ return;
+ }
+
+ send_write(attrib, desc->proxy, desc->pending_writes, id, value, len);
+}
+
+static bool database_add_desc(struct external_service *service,
+ struct external_desc *desc)
+{
+ bt_uuid_t uuid;
+
+ if (!parse_uuid(desc->proxy, &uuid)) {
+ error("Failed to read \"UUID\" property of descriptor");
+ return false;
+ }
+
+ /*
+ * TODO: Set permissions based on a D-Bus property of the external
+ * descriptor.
+ */
+ desc->attrib = gatt_db_service_add_descriptor(service->attrib, &uuid,
+ BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+ desc_read_cb, desc_write_cb, desc);
+ if (!desc->attrib) {
+ error("Failed to create descriptor entry in database");
+ return false;
+ }
+
+ desc->handled = true;
+
+ return true;
+}
+
+static void chrc_read_cb(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ struct external_chrc *chrc = user_data;
+
+ if (chrc->attrib != attrib) {
+ error("Read callback called with incorrect attribute");
+ return;
+ }
+
+ send_read(attrib, chrc->proxy, chrc->pending_reads, id);
+}
+
+static void chrc_write_cb(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ const uint8_t *value, size_t len,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ struct external_chrc *chrc = user_data;
+
+ if (chrc->attrib != attrib) {
+ error("Write callback called with incorrect attribute");
+ return;
+ }
+
+ send_write(attrib, chrc->proxy, chrc->pending_writes, id, value, len);
+}
+
+static bool database_add_chrc(struct external_service *service,
+ struct external_chrc *chrc)
+{
+ bt_uuid_t uuid;
+ uint32_t perm;
+ const struct queue_entry *entry;
+
+ if (!parse_uuid(chrc->proxy, &uuid)) {
+ error("Failed to read \"UUID\" property of characteristic");
+ return false;
+ }
+
+ if (!check_service_path(chrc->proxy, service)) {
+ error("Invalid service path for characteristic");
+ return false;
+ }
+
+ /*
+ * TODO: Once shared/gatt-server properly supports permission checks,
+ * set the permissions based on a D-Bus property of the external
+ * characteristic.
+ */
+ perm = permissions_from_props(chrc->props, chrc->ext_props);
+ chrc->attrib = gatt_db_service_add_characteristic(service->attrib,
+ &uuid, perm,
+ chrc->props, chrc_read_cb,
+ chrc_write_cb, chrc);
+ if (!chrc->attrib) {
+ error("Failed to create characteristic entry in database");
+ return false;
+ }
+
+ if (!database_add_ccc(service, chrc))
+ return false;
+
+ if (!database_add_cep(service, chrc))
+ return false;
+
+ /* Handle the descriptors that belong to this characteristic. */
+ entry = queue_get_entries(service->descs);
+ while (entry) {
+ struct external_desc *desc = entry->data;
+
+ if (desc->handled || g_strcmp0(desc->chrc_path, chrc->path))
+ continue;
+
+ if (!database_add_desc(service, desc)) {
+ chrc->attrib = NULL;
+ error("Failed to create descriptor entry");
+ return false;
+ }
+
+ entry = entry->next;
+ }
+
+ return true;
+}
+
+static bool match_desc_unhandled(const void *a, const void *b)
+{
+ const struct external_desc *desc = a;
+
+ return !desc->handled;
+}
+
+static bool create_service_entry(struct external_service *service)
+{
+ bt_uuid_t uuid;
+ bool primary;
+ const struct queue_entry *entry;
+
+ if (!parse_uuid(service->proxy, &uuid)) {
+ error("Failed to read \"UUID\" property of service");
+ return false;
+ }
+
+ if (!parse_primary(service->proxy, &primary)) {
+ error("Failed to read \"Primary\" property of service");
+ return false;
+ }
+
+ service->attrib = gatt_db_add_service(service->database->db, &uuid,
+ primary, service->attr_cnt);
+ if (!service->attrib)
+ return false;
+
+ entry = queue_get_entries(service->chrcs);
+ while (entry) {
+ struct external_chrc *chrc = entry->data;
+
+ if (!database_add_chrc(service, chrc)) {
+ error("Failed to add characteristic");
+ goto fail;
+ }
+
+ entry = entry->next;
+ }
+
+ /* If there are any unhandled descriptors, return an error */
+ if (queue_find(service->descs, match_desc_unhandled, NULL)) {
+ error("Found descriptor with no matching characteristic!");
+ goto fail;
+ }
+
+ gatt_db_service_set_active(service->attrib, true);
+
+ return true;
+
+fail:
+ gatt_db_remove_service(service->database->db, service->attrib);
+ service->attrib = NULL;
+
+ return false;
+}
+
+static void client_ready_cb(GDBusClient *client, void *user_data)
+{
+ struct external_service *service = user_data;
+ DBusMessage *reply;
+ bool fail = false;
+
+ if (!service->proxy || service->failed) {
+ error("No valid external GATT objects found");
+ fail = true;
+ reply = btd_error_failed(service->reg,
+ "No valid service object found");
+ goto reply;
+ }
+
+ if (!create_service_entry(service)) {
+ error("Failed to create GATT service entry in local database");
+ fail = true;
+ reply = btd_error_failed(service->reg,
+ "Failed to create entry in database");
+ goto reply;
+ }
+
+ DBG("GATT service registered: %s", service->path);
+
+ reply = dbus_message_new_method_return(service->reg);
+
+reply:
+ g_dbus_send_message(btd_get_dbus_connection(), reply);
+ dbus_message_unref(service->reg);
+ service->reg = NULL;
+
+ if (fail)
+ service_remove(service);
+}
+
+static struct external_service *service_create(DBusConnection *conn,
+ DBusMessage *msg, const char *path)
+{
+ struct external_service *service;
+ const char *sender = dbus_message_get_sender(msg);
+
+ if (!path || !g_str_has_prefix(path, "/"))
+ return NULL;
+
+ service = new0(struct external_service, 1);
+ if (!service)
+ return NULL;
+
+ service->client = g_dbus_client_new_full(conn, sender, path, path);
+ if (!service->client)
+ goto fail;
+
+ service->owner = g_strdup(sender);
+ if (!service->owner)
+ goto fail;
+
+ service->path = g_strdup(path);
+ if (!service->path)
+ goto fail;
+
+ service->chrcs = queue_new();
+ if (!service->chrcs)
+ goto fail;
+
+ service->descs = queue_new();
+ if (!service->descs)
+ goto fail;
+
+ service->reg = dbus_message_ref(msg);
+
+ g_dbus_client_set_disconnect_watch(service->client,
+ client_disconnect_cb, service);
+ g_dbus_client_set_proxy_handlers(service->client, proxy_added_cb,
+ proxy_removed_cb, NULL,
+ service);
+ g_dbus_client_set_ready_watch(service->client, client_ready_cb,
+ service);
+
+ return service;
+
+fail:
+ service_free(service);
+ return NULL;
+}
+
+static DBusMessage *manager_register_service(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ struct btd_gatt_database *database = user_data;
+ const char *sender = dbus_message_get_sender(msg);
+ DBusMessageIter args;
+ const char *path;
+ struct external_service *service;
+ struct svc_match_data match_data;
+
+ if (!dbus_message_iter_init(msg, &args))
+ return btd_error_invalid_args(msg);
+
+ if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH)
+ return btd_error_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&args, &path);
+
+ match_data.path = path;
+ match_data.sender = sender;
+
+ if (queue_find(database->services, match_service, &match_data))
+ return btd_error_already_exists(msg);
+
+ dbus_message_iter_next(&args);
+ if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_ARRAY)
+ return btd_error_invalid_args(msg);
+
+ service = service_create(conn, msg, path);
+ if (!service)
+ return btd_error_failed(msg, "Failed to register service");
+
+ DBG("Registering service - path: %s", path);
+
+ service->database = database;
+ queue_push_tail(database->services, service);
+
+ return NULL;
+}
+
+static DBusMessage *manager_unregister_service(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ struct btd_gatt_database *database = user_data;
+ const char *sender = dbus_message_get_sender(msg);
+ const char *path;
+ DBusMessageIter args;
+ struct external_service *service;
+ struct svc_match_data match_data;
+
+ if (!dbus_message_iter_init(msg, &args))
+ return btd_error_invalid_args(msg);
+
+ if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_OBJECT_PATH)
+ return btd_error_invalid_args(msg);
+
+ dbus_message_iter_get_basic(&args, &path);
+
+ match_data.path = path;
+ match_data.sender = sender;
+
+ service = queue_remove_if(database->services, match_service,
+ &match_data);
+ if (!service)
+ return btd_error_does_not_exist(msg);
+
+ service_free(service);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable manager_methods[] = {
+ { GDBUS_EXPERIMENTAL_ASYNC_METHOD("RegisterService",
+ GDBUS_ARGS({ "service", "o" }, { "options", "a{sv}" }),
+ NULL, manager_register_service) },
+ { GDBUS_EXPERIMENTAL_ASYNC_METHOD("UnregisterService",
+ GDBUS_ARGS({ "service", "o" }),
+ NULL, manager_unregister_service) },
+ { }
+};
+
+struct btd_gatt_database *btd_gatt_database_new(struct btd_adapter *adapter)
+{
+ struct btd_gatt_database *database;
+ GError *gerr = NULL;
+ const bdaddr_t *addr;
+
+ if (!adapter)
+ return NULL;
+
+ database = new0(struct btd_gatt_database, 1);
+ if (!database)
+ return NULL;
+
+ database->adapter = btd_adapter_ref(adapter);
+ database->db = gatt_db_new();
+ if (!database->db)
+ goto fail;
+
+ database->device_states = queue_new();
+ if (!database->device_states)
+ goto fail;
+
+ database->services = queue_new();
+ if (!database->services)
+ goto fail;
+
+ database->ccc_callbacks = queue_new();
+ if (!database->ccc_callbacks)
+ goto fail;
+
+ database->db_id = gatt_db_register(database->db, gatt_db_service_added,
+ gatt_db_service_removed,
+ database, NULL);
+ if (!database->db_id)
+ goto fail;
+
+ addr = btd_adapter_get_address(adapter);
+ database->le_io = bt_io_listen(connect_cb, NULL, NULL, NULL, &gerr,
+ BT_IO_OPT_SOURCE_BDADDR, addr,
+ BT_IO_OPT_SOURCE_TYPE, BDADDR_LE_PUBLIC,
+ BT_IO_OPT_CID, ATT_CID,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+ BT_IO_OPT_INVALID);
+ if (!database->le_io) {
+ error("Failed to start listening: %s", gerr->message);
+ g_error_free(gerr);
+ goto fail;
+ }
+
+ /* BR/EDR socket */
+ database->l2cap_io = bt_io_listen(connect_cb, NULL, NULL, NULL, &gerr,
+ BT_IO_OPT_SOURCE_BDADDR, addr,
+ BT_IO_OPT_PSM, ATT_PSM,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+ BT_IO_OPT_INVALID);
+ if (database->l2cap_io == NULL) {
+ error("Failed to start listening: %s", gerr->message);
+ g_error_free(gerr);
+ goto fail;
+ }
+
+ if (g_dbus_register_interface(btd_get_dbus_connection(),
+ adapter_get_path(adapter),
+ GATT_MANAGER_IFACE,
+ manager_methods, NULL, NULL,
+ database, NULL))
+ DBG("GATT Manager registered for adapter: %s",
+ adapter_get_path(adapter));
+
+ register_core_services(database);
+
+ return database;
+
+fail:
+ gatt_database_free(database);
+
+ return NULL;
+}
+
+void btd_gatt_database_destroy(struct btd_gatt_database *database)
+{
+ if (!database)
+ return;
+
+ g_dbus_unregister_interface(btd_get_dbus_connection(),
+ adapter_get_path(database->adapter),
+ GATT_MANAGER_IFACE);
+
+ gatt_database_free(database);
+}
+
+struct gatt_db *btd_gatt_database_get_db(struct btd_gatt_database *database)
+{
+ if (!database)
+ return NULL;
+
+ return database->db;
+}
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2015 Google 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.
+ *
+ */
+
+struct btd_gatt_database;
+
+struct btd_gatt_database *btd_gatt_database_new(struct btd_adapter *adapter);
+void btd_gatt_database_destroy(struct btd_gatt_database *database);
+
+struct gatt_db *btd_gatt_database_get_db(struct btd_gatt_database *database);
+
+typedef uint8_t (*btd_gatt_database_ccc_write_t) (uint16_t value,
+ void *user_data);
+typedef void (*btd_gatt_database_destroy_t) (void *data);
+
+struct gatt_db_attribute *
+btd_gatt_database_add_ccc(struct btd_gatt_database *database,
+ uint16_t service_handle,
+ btd_gatt_database_ccc_write_t write_callback,
+ void *user_data,
+ btd_gatt_database_destroy_t destroy);
+#ifdef __TIZEN_PATCH__
/*
*
* BlueZ - Bluetooth protocol stack for Linux
*/
static GHashTable *proxy_hash;
-static DBusMessage *get_service(DBusConnection *conn,
- DBusMessage *msg, void *user_data);
-
static GSList *external_services;
static int external_service_path_cmp(gconstpointer a, gconstpointer b)
const char *str;
bt_uuid_t uuid;
uint8_t *value;
- size_t len;
+ int len;
if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
return;
* send notification or indication if CCC is set */
btd_gatt_update_char(&uuid, value, len);
}
-#endif
+static DBusMessage *__bt_gatt_dbus_method_send(const char *path, const char *svc_path,
+ const char *interface, const char *method, DBusError *err, int type, ...)
+{
+ DBusMessage *msg;
+ DBusMessage *reply;
+ va_list args;
+ int timeout = -1;
+ struct external_service *esvc;
+ GSList *list;
+
+ list = g_slist_find_custom(external_services, svc_path,
+ external_service_path_cmp);
+ if (!list) {
+ return NULL;
+ }
+
+ esvc = list->data;
+ msg = dbus_message_new_method_call(esvc->owner,
+ svc_path, interface, method);
+ if (!msg) {
+ DBG("Unable to allocate new D-Bus %s message \n", method);
+ return NULL;
+ }
+
+ va_start(args, type);
+
+ if (!dbus_message_append_args_valist(msg, type, args)) {
+ dbus_message_unref(msg);
+ va_end(args);
+ return NULL;
+ }
+
+ va_end(args);
+
+ dbus_error_init(err);
+ reply = dbus_connection_send_with_reply_and_block(btd_get_dbus_connection(),
+ msg, timeout, err);
+ dbus_message_unref(msg);
+
+ return reply;
+}
+#endif
#ifdef __TIZEN_PATCH__
-static uint8_t proxy_read_cb(struct btd_attribute *attr,
+static uint8_t proxy_read_cb(struct btd_attribute *attr, bdaddr_t *bdaddr,
btd_attr_read_result_t result, void *user_data)
#else
static void proxy_read_cb(struct btd_attribute *attr,
btd_attr_read_result_t result, void *user_data)
#endif
{
- DBusMessageIter iter, array;
GDBusProxy *proxy;
- uint8_t *value;
- int len;
-
+ uint8_t *value = NULL;
+ int len = 0;
+#ifdef __TIZEN_PATCH__
+ char dstaddr[18] = { 0 };
+ char *addr_value = NULL;
+ DBusMessage *reply;
+ DBusError err;
+ uint8_t request_id = 1;
+ uint16_t offset = 0;
+ const char *path = NULL;
+ const char *svc_path = NULL;
+#endif
/*
* Remote device is trying to read the informed attribute,
* "Value" should be read from the proxy. GDBusProxy tracks
return;
#endif
}
-
+#ifndef __TIZEN_PATCH__
if (!g_dbus_proxy_get_property(proxy, "Value", &iter)) {
/* Unusual situation, read property will checked earlier */
#ifdef __TIZEN_PATCH__
dbus_message_iter_recurse(&iter, &array);
dbus_message_iter_get_fixed_array(&array, &value, &len);
-
+#else
+ ba2str(bdaddr, dstaddr);
+ addr_value = g_strdup(dstaddr);
+ path = btd_get_attrib_path(attr);
+ if (!path) {
+ /* Fix : RESOURCE_LEAK */
+ if (addr_value)
+ g_free(addr_value);
+ return result(-EPERM, NULL, 0, user_data);
+ }
+ svc_path = btd_find_service_from_attr(attr);
+ if (!svc_path) {
+ /* Fix : RESOURCE_LEAK */
+ if (addr_value)
+ g_free(addr_value);
+ return result(-EPERM, NULL, 0, user_data);
+ }
+ reply = __bt_gatt_dbus_method_send(path, svc_path,
+ DBUS_INTERFACE_PROPERTIES,
+ "ReadValue", &err,
+ DBUS_TYPE_STRING, &addr_value,
+ DBUS_TYPE_STRING, &path,
+ DBUS_TYPE_BYTE, &request_id,
+ DBUS_TYPE_UINT16, &offset,
+ DBUS_TYPE_INVALID);
+ if (!reply) {
+ DBG("Error returned in method call\n");
+ if (dbus_error_is_set(&err))
+ DBG("Error = %s", err.message);
+ /* Fix : RESOURCE_LEAK */
+ if (addr_value)
+ g_free(addr_value);
+ return result(-EPERM, NULL, 0, user_data);
+ } else {
+ DBusMessageIter iter, array;
+ uint8_t *value = NULL;
+ int len;
+ dbus_message_iter_init(reply, &iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_ARRAY) {
+ dbus_message_iter_recurse(&iter, &array);
+ dbus_message_iter_get_fixed_array(&array, &value, &len);
+ if (len > 0)
+ return result(0, value, len, user_data);
+ else
+ return result(-ENOENT, NULL, 0, user_data);
+ }
+ }
+#endif
DBG("attribute: %p read %d bytes", attr, len);
#ifdef __TIZEN_PATCH__
g_dbus_unregister_interface(btd_get_dbus_connection(), "/org/bluez",
GATT_MGR_IFACE);
}
+
+#endif
+#ifdef __TIZEN_PATCH__
/*
*
* BlueZ - Bluetooth protocol stack for Linux
gboolean gatt_dbus_manager_register(void);
void gatt_dbus_manager_unregister(void);
+
+#endif
+#ifdef __TIZEN_PATCH__
/*
*
* BlueZ - Bluetooth protocol stack for Linux
#include "attrib/gatt.h"
#include "src/attrib-server.h"
#include "src/device.h"
+#include "dbus-common.h"
#endif
/* Common GATT UUIDs */
attrib->attr_start_handle = start_handle;
attrib->attr_end_handle = end_handle;
- g_list_insert_sorted (local_attribute_db, attrib, attribute_cmp);
+ local_attribute_db = g_list_insert_sorted (local_attribute_db, attrib, attribute_cmp);
return;
}
return status;
}
-static uint8_t write_result(int err, void *user_data)
-{
- DBG(" ");
- return err;
-}
-
static uint8_t read_desc_attr_db_value(struct attribute *a,
struct btd_device *device,
gpointer user_data)
local_attr = find_local_attr(&a->uuid);
if (local_attr && local_attr->read_cb) {
- status = local_attr->read_cb(local_attr, read_result, a);
+ bdaddr_t *bdaddr = (bdaddr_t *)device_get_address(device);
+ status = local_attr->read_cb(local_attr, bdaddr, read_result, a);
} else
return -ENOENT;
local_attr = find_local_attr(&a->uuid);
if (local_attr && local_attr->read_cb) {
- status = local_attr->read_cb(local_attr, read_result, a);
+ bdaddr_t *bdaddr = (bdaddr_t *)device_get_address(device);
+ status = local_attr->read_cb(local_attr, bdaddr, read_result, a);
} else
return -ENOENT;
GList *l;
uint16_t nex_hndl = 0x0001;
struct pending_hndl *temp_list;
- struct btd_attribute *local_attr;
unsigned int attr_size = 0;
/* Calculate the size of the char attributes to be added to attribute DB */
GUINT_TO_POINTER(nex_hndl),
handle_cmp);
if (l) {
- local_attr = l->data;
if (temp_list->type == GATT_TYPE_CHARAC_SVC) {
attr_size += 2;
}
do {
l = g_list_find_custom(connections, GUINT_TO_POINTER(NULL),
- is_connected);
+ (GCompareFunc)is_connected);
if (l) {
dev = l->data;
if (!svc_added) {
new_service_add = FALSE;
new_char_desc_add = FALSE;
+ /* Fix : RESOURCE_LEAK */
+ if (temp_att) {
+ free(temp_att);
+ temp_att = NULL;
+ }
break;
} else {
new_service_add = TRUE;
}
+ }
+ /* Fix : RESOURCE_LEAK */
+ if (temp_att) {
free(temp_att);
temp_att = NULL;
}
return attrib->path;
}
+const char *btd_get_attrib_path(struct btd_attribute *attrib)
+{
+ if(attrib)
+ return attrib->path;
+ else
+ return NULL;
+}
+
+const char *btd_find_service_from_attr(struct btd_attribute *attrib)
+{
+ struct btd_attribute *svc_attrib = NULL;
+ GList *list;
+
+ for (list = local_attribute_db; list; list = g_list_next(list)) {
+ struct btd_attribute *attr = list->data;
+ if (attr && (attr->type.value.u16 == GATT_PRIM_SVC_UUID ||
+ attr->type.value.u16 == GATT_SND_SVC_UUID)) {
+ svc_attrib = attr;
+ } else {
+ if (attr && !attribute_cmp(attrib, attr))
+ break;
+ else
+ continue;
+ }
+ }
+ if (svc_attrib)
+ return svc_attrib->path;
+ else
+ return NULL;
+}
DBusMessage *service_append_dict(bt_uuid_t uuid, DBusMessage *msg)
{
GList *list;
static void remove_attr_service(struct btd_attribute *service)
{
- uint16_t start_handle, end_handle;
+ uint16_t start_handle = 0, end_handle = 0;
bt_uuid_t prim_uuid;
struct btd_adapter *adapter;
bool notify_indicate)
{
attrib->notify_indicate = notify_indicate;
- g_list_insert_sorted (local_attribute_db, attrib,
+ local_attribute_db = g_list_insert_sorted (local_attribute_db, attrib,
attribute_cmp);
}
+
+gboolean gatt_register_internet_protocol_service(struct btd_adapter *adapter)
+{
+ bt_uuid_t uuid;
+
+ bt_uuid16_create(&uuid, GATT_IPSP_UUID);
+
+ return gatt_service_add(adapter, GATT_PRIM_SVC_UUID, &uuid,
+ GATT_OPT_INVALID);
+}
+
+gboolean gatt_unregister_internet_protocol_service(struct btd_adapter *adapter)
+{
+ bt_uuid_t uuid;
+ struct attribute *a;
+
+ bt_uuid16_create(&uuid, GATT_IPSP_UUID);
+
+ a = attribute_find(adapter, &uuid);
+ if (!a) {
+ error("Attribute not found for handle 0x%04x", a->handle);
+ return FALSE;
+ }
+
+ if (attrib_db_del(adapter, a->handle) < 0) {
+ error("Can't delete handle 0x%04x", a->handle);
+ return FALSE;
+ }
+ return TRUE;
+}
#endif
#ifdef __TIZEN_PATCH__
attrib = l->data;
notify_indicate = attrib->notify_indicate;
- g_list_insert_sorted (local_attribute_db, attrib, attribute_cmp);
+ local_attribute_db = g_list_insert_sorted (local_attribute_db, attrib, attribute_cmp);
l = g_list_find_custom(local_attribute_db,
GUINT_TO_POINTER(attrib->handle - 1),
fail:
#ifdef __TIZEN_PATCH__
+ /* Fix : RESOURCE_LEAK */
+ if (char_decl->path)
+ g_free(char_decl->path);
if (char_decl)
free(char_decl);
if (char_value)
gatt_dbus_manager_unregister();
}
+
+#endif
+#ifdef __TIZEN_PATCH__
/*
*
* BlueZ - Bluetooth protocol stack for Linux
* Service implementation callback passed to core (ATT layer). It manages read
* operations received from remote devices.
* @attr: reference of the attribute to be read.
+ * @bdaddr: Remote device address, which requets read value
* @result: callback called from the service implementation informing the
* value of attribute read.
* @user_data: user_data passed in btd_attr_read_result_t callback.
*/
#ifdef __TIZEN_PATCH__
-typedef uint8_t (*btd_attr_read_t) (struct btd_attribute *attr,
+typedef uint8_t (*btd_attr_read_t) (struct btd_attribute *attr, bdaddr_t *bdaddr,
btd_attr_read_result_t result,
void *user_data);
#else
char *btd_get_service_path(bt_uuid_t uuid);
/*
+ * btd_get_attrib_path - Gets the attribute path if registerd.
+ */
+const char *btd_get_attrib_path(struct btd_attribute *attrib);
+
+/*
+ * btd_find_service_from_attr - Gets the Service from attribute if registerd.
+ */
+const char *btd_find_service_from_attr(struct btd_attribute *attrib);
+
+/*
* service_append_dict - Prepare the dictionar entry for a Service
* along with characteristics and discriptors.
*/
*/
bool btd_gatt_update_char(const bt_uuid_t *uuid, uint8_t *value, size_t len);
#endif
+
+#endif
{
int option = LOG_NDELAY | LOG_PID;
- enabled = g_strsplit_set(g_strdup("*"), ":, ", 0);
+ /* Fix : RESOURCE_LEAK */
+ char *str = g_strdup("*");
+
+ enabled = g_strsplit_set(str, ":, ", 0);
__btd_enable_debug(__start___debug, __stop___debug);
openlog("hciattach", option, LOG_DAEMON);
syslog(LOG_INFO, "hciattach daemon for debugging");
+
+ g_free(str);
}
#endif
#include <stdlib.h>
#include <string.h>
#include <signal.h>
+#include <stdbool.h>
#include <sys/signalfd.h>
#include <sys/types.h>
#include <sys/stat.h>
-#include <bluetooth/bluetooth.h>
-
#include <glib.h>
#include <dbus/dbus.h>
-#include <gdbus/gdbus.h>
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+
+#include "gdbus/gdbus.h"
#include "log.h"
#include "dbus-common.h"
#include "agent.h"
#include "profile.h"
-#include "gatt.h"
#include "systemd.h"
+#ifdef __TIZEN_PATCH__
+#include "gatt.h"
+#endif
+
#define BLUEZ_NAME "org.bluez"
#define DEFAULT_PAIRABLE_TIMEOUT 0 /* disabled */
struct main_opts main_opts;
static GKeyFile *main_conf;
+static enum {
+ MPS_OFF,
+ MPS_SINGLE,
+ MPS_MULTIPLE,
+} mps = MPS_OFF;
+
static const char * const supported_options[] = {
"Name",
"Class",
"NameResolving",
"DebugKeys",
"ControllerMode",
+ "MultiProfile",
#ifdef __TIZEN_PATCH__
"EnableLEPrivacy",
#endif
g_free(str);
}
+ str = g_key_file_get_string(config, "General", "MultiProfile", &err);
+ if (err) {
+ g_clear_error(&err);
+ } else {
+ DBG("MultiProfile=%s", str);
+
+ if (!strcmp(str, "single"))
+ mps = MPS_SINGLE;
+ else if (!strcmp(str, "multiple"))
+ mps = MPS_MULTIPLE;
+
+ g_free(str);
+ }
#ifdef __TIZEN_PATCH__
boolean = g_key_file_get_boolean(config, "General",
"EnableLEPrivacy", &err);
main_opts.name_resolv = TRUE;
main_opts.debug_keys = FALSE;
#ifdef __TIZEN_PATCH__
- main_opts.le_privacy = TRUE;
+ main_opts.le_privacy = FALSE;
#endif
if (sscanf(VERSION, "%hhu.%hhu", &major, &minor) != 2)
g_dbus_set_flags(gdbus_flags);
+#ifdef __TIZEN_PATCH__
gatt_init();
+#endif
if (adapter_init() < 0) {
error("Adapter handling initialization failed");
register_device_id(main_opts.did_source, main_opts.did_vendor,
main_opts.did_product, main_opts.did_version);
+ if (mps != MPS_OFF)
+ register_mps(mps == MPS_MULTIPLE);
+
/* Loading plugins has to be done after D-Bus has been setup since
* the plugins might wanna expose some paths on the bus. However the
* best order of how to init various subsystems of the Bluetooth
adapter_cleanup();
+#ifdef __TIZEN_PATCH__
gatt_cleanup();
+#endif
rfkill_exit();
+++ /dev/null
-[General]
-
-# Default adaper name
-# %h - substituted for hostname
-# %d - substituted for adapter id
-# Defaults to 'BlueZ'
-#Name = %h-%d
-
-# Default device class. Only the major and minor device class bits are
-# considered. Defaults to '0x000000'.
-#Class = 0x000100
-
-# How long to stay in discoverable mode before going back to non-discoverable
-# The value is in seconds. Default is 180, i.e. 3 minutes.
-# 0 = disable timer, i.e. stay discoverable forever
-#DiscoverableTimeout = 0
-
-# How long to stay in pairable mode before going back to non-discoverable
-# The value is in seconds. Default is 0.
-# 0 = disable timer, i.e. stay pairable forever
-#PairableTimeout = 0
-
-# Automatic connection for bonded devices driven by platform/user events.
-# If a platform plugin uses this mechanism, automatic connections will be
-# enabled during the interval defined below. Initially, this feature
-# intends to be used to establish connections to ATT channels. Default is 60.
-#AutoConnectTimeout = 60
-
-# 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
-# doesn't like us doing reverse SDP for some test cases (though there could in
-# theory be other useful purposes for this too). Defaults to 'true'.
-#ReverseServiceDiscovery = true
-
-# Enable name resolving after inquiry. Set it to 'false' if you don't need
-# remote devices name and want shorter discovery cycle. Defaults to 'true'.
-#NameResolving = true
-
-# Enable runtime persistency of debug link keys. Default is false which
-# makes debug link keys valid only for the duration of the connection
-# that they were created for.
-#DebugKeys = false
-
-#[Policy]
-#
-# The ReconnectUUIDs defines the set of remote services that should try
-# to be reconnected to in case of a link loss (link supervision
-# timeout). The policy plugin should contain a sane set of values by
-# default, but this list can be overridden here. By setting the list to
-# empty the reconnection feature gets disabled.
-#ReconnectUUIDs=
# Possible values: "dual", "bredr", "le"
#ControllerMode = dual
+# Enables Multi Profile Specification support. This allows to specify if
+# system supports only Multiple Profiles Single Device (MPSD) configuration
+# or both Multiple Profiles Single Device (MPSD) and Multiple Profiles Multiple
+# Devices (MPMD) configurations.
+# Possible values: "off", "single", "multiple"
+#MultiProfile = off
+
+#ifdef __TIZEN_PATCH__
+# Enable the LE Privacy feature. If value is true, i.e. LE Privacy is enabled
+# otherwise the feature is disabled by default for the local device.
+EnableLEPrivacy = false
+#endif
+
#[Policy]
#
# The ReconnectUUIDs defines the set of remote services that should try
# timeout). The policy plugin should contain a sane set of values by
# default, but this list can be overridden here. By setting the list to
# empty the reconnection feature gets disabled.
-#ReconnectUUIDs=
-
-#ifdef __TIZEN_PATCH__
-# Enable the LE Privacy feature. If value is true, i.e. LE Privacy is enabled
-# otherwise the feature is disabled by default for the local device.
-EnableLEPrivacy = true
-#endif
\ No newline at end of file
+#ReconnectUUIDs=
\ No newline at end of file
# Possible values: "dual", "bredr", "le"
#ControllerMode = dual
-#[Policy]
+#ifdef __TIZEN_PATCH__
+# Enable the LE Privacy feature. If value is true, i.e. LE Privacy is enabled
+# otherwise the feature is disabled by default for the local device.
+EnableLEPrivacy = false
+#endif
+
+[Policy]
#
# The ReconnectUUIDs defines the set of remote services that should try
# to be reconnected to in case of a link loss (link supervision
# timeout). The policy plugin should contain a sane set of values by
# default, but this list can be overridden here. By setting the list to
# empty the reconnection feature gets disabled.
-#ReconnectUUIDs=
-
#ifdef __TIZEN_PATCH__
-# Enable the LE Privacy feature. If value is true, i.e. LE Privacy is enabled
-# otherwise the feature is disabled by default for the local device.
-EnableLEPrivacy = true
+ReconnectUUIDs=
+#else
+#ReconnectUUIDs=
#endif
+
Exec=/bin/false
User=root
SystemdService=dbus-org.bluez.service
-#test
-#SystemdService=bluetooth.service
-
#include <config.h>
#endif
+#include "lib/bluetooth.h"
#include "oui.h"
#ifdef HAVE_UDEV_HWDB_NEW
*
*/
-#include <bluetooth/bluetooth.h>
-
char *batocomp(const bdaddr_t *ba);
#include <string.h>
#include <sys/stat.h>
-#include <bluetooth/bluetooth.h>
-
#include <glib.h>
+#include "lib/bluetooth.h"
+
#include "btio/btio.h"
#include "src/plugin.h"
#include "src/log.h"
#include <glib.h>
#include <dbus/dbus.h>
-#include <gdbus/gdbus.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/sdp.h>
-#include <bluetooth/sdp_lib.h>
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
#include "btio/btio.h"
-#include "lib/uuid.h"
#include "sdpd.h"
#include "log.h"
#include "error.h"
#define BTD_PROFILE_PSM_AUTO -1
#define BTD_PROFILE_CHAN_AUTO -1
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+#define HID_DEVICE_INTR_PSM 17
+#define HID_DEVICE_CTRL_PSM 19
+#endif
+
#define HFP_HF_RECORD \
"<?xml version=\"1.0\" encoding=\"UTF-8\" ?> \
<record> \
</attribute> \
</record>"
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+#define HID_DEVICE_RECORD \
+ "<?xml version=\"1.0\" encoding=\"UTF-8\" ?> \
+ <record> \
+ <attribute id=\"0x0001\"> \
+ <sequence> \
+ <uuid value=\"0x1124\" /> \
+ </sequence> \
+ </attribute> \
+ <attribute id=\"0x0004\"> \
+ <sequence> \
+ <sequence> \
+ <uuid value=\"0x0100\" /> \
+ <uint16 value=\"0x0011\" /> \
+ </sequence> \
+ <sequence> \
+ <uuid value=\"0x0011\" /> \
+ </sequence> \
+ </sequence> \
+ </attribute> \
+ <attribute id=\"0x0005\"> \
+ <sequence> \
+ <uuid value=\"0x1002\" /> \
+ </sequence> \
+ </attribute> \
+ <attribute id=\"0x0006\"> \
+ <sequence> \
+ <uint16 value=\"0x656e\" /> \
+ <uint16 value=\"0x006a\" /> \
+ <uint16 value=\"0x0100\" /> \
+ </sequence> \
+ </attribute> \
+ <attribute id=\"0x0009\"> \
+ <sequence> \
+ <sequence> \
+ <uuid value=\"0x0011\" /> \
+ <uint16 value=\"0x0100\" /> \
+ </sequence> \
+ </sequence> \
+ </attribute> \
+ <attribute id=\"0x000d\"> \
+ <sequence> \
+ <sequence> \
+ <sequence> \
+ <uuid value=\"0x0100\" /> \
+ <uint16 value=\"0x0013\" /> \
+ </sequence> \
+ <sequence> \
+ <uuid value=\"0x0011\" /> \
+ </sequence> \
+ </sequence> \
+ </sequence> \
+ </attribute> \
+ <attribute id=\"0x0100\"> \
+ <text value=\"Bluez Mouse\" /> \
+ </attribute> \
+ <attribute id=\"0x0101\"> \
+ <text value=\"Mouse\" /> \
+ </attribute> \
+ <attribute id=\"0x0200\"> \
+ <uint16 value=\"0x0100\" /> \
+ </attribute> \
+ <attribute id=\"0x0201\"> \
+ <uint16 value=\"0x0111\" /> \
+ </attribute> \
+ <attribute id=\"0x0202\"> \
+ <uint8 value=\"0x40\" /> \
+ </attribute> \
+ <attribute id=\"0x0203\"> \
+ <uint8 value=\"0x00\" /> \
+ </attribute> \
+ <attribute id=\"0x0204\"> \
+ <boolean value=\"true\" /> \
+ </attribute> \
+ <attribute id=\"0x0205\"> \
+ <boolean value=\"true\" /> \
+ </attribute> \
+ <attribute id=\"0x0206\"> \
+ <sequence> \
+ <sequence> \
+ <uint8 value=\"0x22\" /> \
+ <text encoding=\"hex\" value=\"05010902a10185010901a1000509190129031500250175019503810275059501810105010930093109381581257f750895028106c0c005010906a1018502a100050719e029e71500250175019508810295087508150025650507190029658100c0c0\" /> \
+ </sequence> \
+ </sequence> \
+ </attribute> \
+ <attribute id=\"0x0207\"> \
+ <sequence> \
+ <sequence> \
+ <uint16 value=\"0x0409\" /> \
+ <uint16 value=\"0x0100\" /> \
+ </sequence> \
+ </sequence> \
+ </attribute> \
+ <attribute id=\"0x020b\"> \
+ <uint16 value=\"0x0100\" /> \
+ </attribute> \
+ <attribute id=\"0x020e\"> \
+ <boolean value=\"true\" /> \
+ </attribute> \
+ </record>"
+#endif
+
+
struct ext_io;
struct ext_profile {
char *uuid;
char *service;
char *role;
-
char *record;
char *(*get_record)(struct ext_profile *ext, struct ext_io *l2cap,
struct ext_io *rfcomm);
GSList *conns;
GSList *connects;
+#ifdef __TIZEN_PATCH__
+ char *destination;
+ char *app_path;
+#endif
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+ bool local_connect;
+#endif
};
struct ext_io {
static GSList *profiles = NULL;
static GSList *ext_profiles = NULL;
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+static int connect_io(struct ext_io *conn, const bdaddr_t *src,
+ const bdaddr_t *dst);
+#endif
+
void btd_profile_foreach(void (*func)(struct btd_profile *p, void *data),
void *data)
{
const sdp_record_t *rec;
const char *path;
int fd;
-
+ DBG("Sending New Connection owner %s path %s", ext->owner, ext->path);
msg = dbus_message_new_method_call(ext->owner, ext->path,
"org.bluez.Profile1",
"NewConnection");
return true;
}
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+static int check_connection_psm(gconstpointer a, gconstpointer b)
+{
+ const struct ext_io *conn = a;
+ const int *psm = b;
+ DBG("conn->psm %d, psm %d", conn->psm, *psm);
+ return (conn->psm == *psm ? 0 : -1);
+}
+#endif
+
static void ext_connect(GIOChannel *io, GError *err, gpointer user_data)
{
struct ext_io *conn = user_data;
conn->io_id = g_io_add_watch(io, cond, ext_io_disconnected,
conn);
}
-
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+ if (g_strcmp0(ext->uuid, HID_UUID) == 0 && ext->local_connect == TRUE) {
+ GSList *l = NULL;
+ int psm = HID_DEVICE_CTRL_PSM;
+ ext->local_connect = FALSE;
+ l = g_slist_find_custom(ext->conns, &psm, check_connection_psm);
+ if (l == NULL) {
+ struct ext_io *conn1 = g_new0(struct ext_io, 1);
+ int error = 0;
+ ext->remote_psm = psm;
+ conn1->ext = ext;
+ conn1->psm = ext->remote_psm;
+ conn1->chan = ext->remote_chan;
+ error = connect_io(conn1, btd_adapter_get_address(conn->adapter),
+ device_get_address(conn->device));
+ DBG("error from connect_io %d", error);
+ conn1->adapter = btd_adapter_ref(conn->adapter);
+ conn1->device = btd_device_ref(conn->device);
+ conn1->service = btd_service_ref(conn->service);
+ ext->conns = g_slist_append(ext->conns, conn1);
+ } else {
+ DBG("Connection Already there");
+ }
+ }
+#endif
if (send_new_connection(ext, conn))
return;
GError *gerr = NULL;
bdaddr_t src, dst;
char addr[18];
- int fd;
bt_io_get(io, &gerr,
BT_IO_OPT_SOURCE_BDADDR, &src,
if (conn == NULL)
return;
- fd = g_io_channel_unix_get_fd(conn->io);
conn->auth_id = btd_request_authorization(&src, &dst, uuid, ext_auth,
- conn, fd);
+ conn);
if (conn->auth_id == 0) {
error("%s authorization failure", ext->name);
ext_io_destroy(conn);
struct btd_adapter *adapter)
{
struct ext_io *l2cap = NULL;
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+ struct ext_io *l2cap1 = NULL;
+#endif
struct ext_io *rfcomm = NULL;
BtIOConfirm confirm;
BtIOConnect connect;
GError *err = NULL;
GIOChannel *io;
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+ GIOChannel *io1;
+#endif
if (ext->authorize) {
confirm = ext_confirm;
ext->servers = g_slist_append(ext->servers, l2cap);
DBG("%s listening on PSM %u", ext->name, psm);
}
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+ if (g_strcmp0(ext->uuid , HID_UUID) == 0) {
+ psm = HID_DEVICE_CTRL_PSM;
+ l2cap1 = g_new0(struct ext_io, 1);
+ l2cap1->ext = ext;
+ io1 = bt_io_listen(connect, confirm, l2cap, NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR,
+ btd_adapter_get_address(adapter),
+ BT_IO_OPT_MODE, ext->mode,
+ BT_IO_OPT_PSM, psm,
+ BT_IO_OPT_SEC_LEVEL, ext->sec_level,
+ BT_IO_OPT_INVALID);
+ l2cap1->io = io1;
+ l2cap1->proto = BTPROTO_L2CAP;
+ l2cap1->psm = psm;
+ l2cap1->adapter = btd_adapter_ref(adapter);
+ ext->servers = g_slist_append(ext->servers, l2cap1);
+ DBG("%s listening on PSM %u", ext->name, psm);
+ }
+#endif
}
if (ext->local_chan) {
DBG("profile uuid %s port uuid %s", profile_uuid, ext->remote_uuid);
- if (g_strncasecmp(profile_uuid, ext->remote_uuid,
+ if (g_ascii_strncasecmp(profile_uuid, ext->remote_uuid,
strlen(profile_uuid)) != 0) {
free(profile_uuid);
continue;
conn = g_new0(struct ext_io, 1);
conn->ext = ext;
-
+#ifndef TIZEN_BT_HID_DEVICE_ENABLE
if (ext->remote_psm || ext->remote_chan) {
conn->psm = ext->remote_psm;
conn->chan = ext->remote_chan;
if (err < 0)
goto failed;
-
+#else
+ if (g_strcmp0(ext->uuid, HID_UUID) == 0) {
+ ext->local_connect = TRUE;
+ ext->remote_psm = HID_DEVICE_INTR_PSM;
+ conn->psm = ext->remote_psm;
+ conn->chan = ext->remote_chan;
+ err = connect_io(conn, btd_adapter_get_address(adapter),
+ device_get_address(dev));
+ } else {
+ if (ext->remote_psm || ext->remote_chan) {
+ conn->psm = ext->remote_psm;
+ conn->chan = ext->remote_chan;
+ err = connect_io(conn, btd_adapter_get_address(adapter),
+ device_get_address(dev));
+ } else {
+ err = resolve_service(conn, btd_adapter_get_address(adapter),
+ device_get_address(dev));
+ }
+ }
+ if (err < 0)
+ goto failed;
+#endif
conn->adapter = btd_adapter_ref(adapter);
conn->device = btd_device_ref(dev);
conn->service = btd_service_ref(service);
if (!ext)
return -ENOENT;
+#ifndef TIZEN_BT_HID_DEVICE_ENABLE
conn = find_connection(ext, dev);
if (!conn || !conn->connected)
return -ENOTCONN;
err = send_disconn_req(ext, conn);
if (err < 0)
return err;
+#else
+ if (g_strcmp0(ext->uuid, HID_UUID) != 0) {
+ conn = find_connection(ext, dev);
+ if (!conn || !conn->connected)
+ return -ENOTCONN;
+
+ if (conn->pending)
+ return -EBUSY;
+ err = send_disconn_req(ext, conn);
+ if (err < 0)
+ return err;
+ } else {
+ GSList *l;
+ /* As HID will be using two psm we need to send disconnect
+ * request for both the psms */
+ for (l = ext->conns; l != NULL; l = g_slist_next(l)) {
+ struct ext_io *conn1 = l->data;
+ if (conn1->device == dev) {
+ err = send_disconn_req(ext, conn1);
+ }
+ }
+ }
+#endif
return 0;
}
return g_strdup_printf(SYNC_RECORD, rfcomm->chan, ext->version,
ext->name);
}
-
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+static char *get_hid_device_record(struct ext_profile *ext, struct ext_io *l2cap,
+ struct ext_io *rfcomm)
+{
+ return g_strdup(HID_DEVICE_RECORD);
+}
+#endif
static char *get_opp_record(struct ext_profile *ext, struct ext_io *l2cap,
struct ext_io *rfcomm)
{
+#ifndef __TIZEN_PATCH__
uint16_t psm = 0;
+#endif
uint8_t chan = 0;
+#ifndef __TIZEN_PATCH__
if (l2cap)
psm = l2cap->psm;
+#endif
if (rfcomm)
chan = rfcomm->chan;
.channel = SPP_DEFAULT_CHANNEL,
.authorize = true,
.get_record = get_spp_record,
-#ifdef __TIZEN_PATCH__
- .version = 0x0100,
-#else
.version = 0x0102,
-#endif
}, {
.uuid = DUN_GW_UUID,
.name = "Dial-Up Networking",
.authorize = true,
.get_record = get_mns_record,
.version = 0x0102
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+ }, {
+ .uuid = HID_UUID,
+ .name = "HID Device",
+ .psm = HID_DEVICE_INTR_PSM,
+ .authorize = TRUE,
+ .get_record = get_hid_device_record,
+ .version = 0x0100,
+ },
+#else
},
+#endif
};
static void ext_set_defaults(struct ext_profile *ext)
}
}
+#ifdef __TIZEN_PATCH__
+static struct ext_profile *create_ext2(const char *owner, const char *path,
+ const char *uuid, const char *destination, const char *app_path,
+ DBusMessageIter *opts)
+{
+ struct btd_profile *p;
+ struct ext_profile *ext;
+
+ ext = g_new0(struct ext_profile, 1);
+
+ ext->uuid = bt_name2string(uuid);
+ if (ext->uuid == NULL) {
+ g_free(ext);
+ return NULL;
+ }
+
+ ext->owner = g_strdup(destination);
+ ext->path = g_strdup(app_path);
+ ext->destination = g_strdup(destination);
+ ext->app_path = g_strdup(app_path);
+ DBG("VALUES Dest %s, path2 %s", destination, app_path);
+ ext_set_defaults(ext);
+
+ while (dbus_message_iter_get_arg_type(opts) == DBUS_TYPE_DICT_ENTRY) {
+ DBusMessageIter value, entry;
+ const char *key;
+
+ dbus_message_iter_recurse(opts, &entry);
+ dbus_message_iter_get_basic(&entry, &key);
+
+ dbus_message_iter_next(&entry);
+ dbus_message_iter_recurse(&entry, &value);
+
+ if (parse_ext_opt(ext, key, &value) < 0)
+ error("Invalid value for profile option %s", key);
+
+ dbus_message_iter_next(opts);
+ }
+
+ if (!ext->service)
+ set_service(ext);
+
+ if (ext->enable_server && !(ext->record || ext->get_record))
+ ext->get_record = get_generic_record;
+
+ if (!ext->name)
+ ext->name = g_strdup_printf("%s%s/%s", owner, path, uuid);
+
+ if (!ext->remote_uuid) {
+ if (ext->service)
+ ext->remote_uuid = g_strdup(ext->service);
+ else
+ ext->remote_uuid = g_strdup(ext->uuid);
+ }
+
+ p = &ext->p;
+
+ p->name = ext->name;
+ p->local_uuid = ext->service ? ext->service : ext->uuid;
+ p->remote_uuid = ext->remote_uuid;
+
+ if (ext->enable_server) {
+ p->adapter_probe = ext_adapter_probe;
+ p->adapter_remove = ext_adapter_remove;
+ }
+
+ if (ext->enable_client) {
+ p->device_probe = ext_device_probe;
+ p->device_remove = ext_device_remove;
+ p->connect = ext_connect_dev;
+ p->disconnect = ext_disconnect_dev;
+ }
+
+ DBG("Created \"%s\"", ext->name);
+
+ ext_profiles = g_slist_append(ext_profiles, ext);
+
+ adapter_foreach(adapter_add_profile, &ext->p);
+
+ return ext;
+}
+#endif
+
static struct ext_profile *create_ext(const char *owner, const char *path,
const char *uuid,
DBusMessageIter *opts)
return dbus_message_new_method_return(msg);
}
+#ifdef __TIZEN_PATCH__
+static DBusMessage *register_profile2(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ const char *path, *sender, *uuid;
+ DBusMessageIter args, opts;
+ struct ext_profile *ext;
+ const char *destination, *app_path;
+ sender = dbus_message_get_sender(msg);
+
+ DBG("sender %s", sender);
+
+ dbus_message_iter_init(msg, &args);
+
+ dbus_message_iter_get_basic(&args, &path);
+ dbus_message_iter_next(&args);
+ DBG("path %s", path);
+
+ DBG("path %s", path);
+ dbus_message_iter_get_basic(&args, &uuid);
+ dbus_message_iter_next(&args);
+ DBG("uuid %s", uuid);
+ dbus_message_iter_get_basic(&args, &destination);
+ dbus_message_iter_next(&args);
+ DBG("destination %s", destination);
+ dbus_message_iter_get_basic(&args, &app_path);
+ dbus_message_iter_next(&args);
+ DBG("path2 %s", app_path);
+ ext = find_ext_profile(destination, path);
+ if (ext)
+ return btd_error_already_exists(msg);
+ if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_ARRAY)
+ return btd_error_invalid_args(msg);
+ DBG("interator");
+ dbus_message_iter_recurse(&args, &opts);
+
+ ext = create_ext2(sender, path, uuid, destination, app_path, &opts);
+ if (!ext)
+ return btd_error_invalid_args(msg);
+#if 0
+ ext->id = g_dbus_add_disconnect_watch(conn, sender, ext_exited, ext,
+ NULL);
+#endif
+
+ return dbus_message_new_method_return(msg);
+}
+#endif
+
static const GDBusMethodTable methods[] = {
{ GDBUS_METHOD("RegisterProfile",
GDBUS_ARGS({ "profile", "o"}, { "UUID", "s" },
GDBUS_ARGS({ "profile", "o"}, { "UUID", "s" },
{ "options", "a{sv}" }),
NULL, register_profile) },
+ { GDBUS_METHOD("RegisterProfile2",
+ GDBUS_ARGS({"profile", "o"}, { "UUID", "s" },
+ {"destination", "s"}, {"path", "s"},
+ { "options", "a{sv}"}),
+ NULL, register_profile2) },
#endif
{ GDBUS_METHOD("UnregisterProfile", GDBUS_ARGS({ "profile", "o" }),
struct btd_profile {
const char *name;
int priority;
- uint16_t version;
const char *local_uuid;
const char *remote_uuid;
- const char *auth_uuid;
bool auto_connect;
void *user_data);
bool btd_profile_remove_custom_prop(const char *uuid, const char *name);
+#ifdef __TIZEN_PATCH__
+gboolean ext_profile_is_registered_as_client_role(struct btd_profile *p);
+#endif
+
void btd_profile_init(void);
void btd_profile_cleanup(void);
#include <glib.h>
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+
#include "log.h"
#include "adapter.h"
#include "hcid.h"
#include <errno.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/sdp.h>
-#include <bluetooth/sdp_lib.h>
-
#include <glib.h>
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+
#include "btio/btio.h"
#include "log.h"
#include "sdp-client.h"
#include <glib.h>
-#include <bluetooth/sdp.h>
-#include <bluetooth/sdp_lib.h>
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
#include "sdp-xml.h"
*
*/
-
-#ifndef __SDP_XML_H
-#define __SDP_XML_H
-
-#include <bluetooth/sdp.h>
-
void convert_sdp_record_to_xml(sdp_record_t *rec,
void *user_data, void (*append_func) (void *, const char *));
sdp_record_t *sdp_xml_parse_record(const char *data, int size);
-
-#endif /* __SDP_XML_H */
#endif
#include <stdlib.h>
+#include <stdbool.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/sdp.h>
-#include <bluetooth/sdp_lib.h>
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
#include "sdpd.h"
#include "log.h"
#include <errno.h>
#include <stdlib.h>
#include <limits.h>
+#include <stdbool.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/l2cap.h>
-#include <bluetooth/sdp.h>
-#include <bluetooth/sdp_lib.h>
+#include "lib/bluetooth.h"
+#include "lib/l2cap.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
#include "src/shared/util.h"
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
+#include <stdbool.h>
#include <sys/stat.h>
-
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/l2cap.h>
-#include <bluetooth/sdp.h>
-#include <bluetooth/sdp_lib.h>
-
#include <sys/un.h>
#include <glib.h>
+#include "lib/bluetooth.h"
+#include "lib/l2cap.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+
#include "log.h"
#include "sdpd.h"
#include <stdlib.h>
#include <assert.h>
#include <sys/time.h>
-
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/sdp.h>
-#include <bluetooth/sdp_lib.h>
+#include <stdbool.h>
#include <glib.h>
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+
#include "src/shared/util.h"
#include "sdpd.h"
#include "log.h"
+#define MPSD_HFP_AG_A2DP_SRC_ANSWER_CALL_DURING_AUDIO (1ULL << 0)
+#define MPSD_HFP_HF_A2DP_SNK_ANSWER_CALL_DURING_AUDIO (1ULL << 1)
+#define MPSD_HFP_AG_A2DP_SRC_OUTGOING_CALL_DURING_AUDIO (1ULL << 2)
+#define MPSD_HFP_HF_A2DP_SNK_OUTGOING_CALL_DURING_AUDIO (1ULL << 3)
+#define MPSD_HFP_AG_A2DP_SRC_REJECT_CALL_DURING_AUDIO (1ULL << 4)
+#define MPSD_HFP_HF_A2DP_SNK_SRC_REJECT_CALL_DURING_AUDIO (1ULL << 5)
+#define MPSD_HFP_AG_A2DP_SRC_TERMINATE_CALL_DURING_AVP (1ULL << 6)
+#define MPSD_HFP_HF_A2DP_SNK_TERMINATE_CALL_DURING_AVP (1ULL << 7)
+#define MPSD_HFP_AG_A2DP_SRC_PRESS_PLAY_DURING_ACTIVE_CALL (1ULL << 8)
+#define MPSD_HFP_HF_A2DP_SNK_PRESS_PLAY_DURING_ACTIVE_CALL (1ULL << 9)
+#define MPSD_HFP_AG_A2DP_SRC_START_AUDIO_STREAM_AFTER_PLAY (1ULL << 10)
+#define MPSD_HFP_HF_A2DP_SNK_START_AUDIO_STREAM_AFTER_PLAY (1ULL << 11)
+#define MPSD_HFP_AG_A2DP_SRC_SUSPEND_AUDIO_STREAM_ON_PAUSE (1ULL << 12)
+#define MPSD_HFP_HF_A2DP_SNK_SUSPEND_AUDIO_STREAM_ON_PAUSE (1ULL << 13)
+#define MPSD_HFP_AG_DUN_GW_DATA_COMM_DURING_VOICE_CALL (1ULL << 14)
+#define MPSD_HFP_HF_DUN_DT_DATA_COMM_DURING_VOICE_CALL (1ULL << 15)
+#define MPSD_HFP_AG_DUN_GW_OUTGOING_CALL_DURING_DATA_COMM (1ULL << 16)
+#define MPSD_HFP_HF_DUN_DT_OUTGOING_CALL_DURING_DATA_COMM (1ULL << 17)
+#define MPSD_HFP_AG_DUN_GW_INCOMING_CALL_DURING_DATA_COMM (1ULL << 18)
+#define MPSD_HFP_HF_DUN_DT_INCOMING_CALL_DURING_DATA_COMM (1ULL << 19)
+#define MPSD_A2DP_SRC_DUN_GW_START_AUDIO_DURING_DATA_COMM (1ULL << 20)
+#define MPSD_A2DP_SNK_DUN_DT_START_AUDIO_DURING_DATA_COMM (1ULL << 21)
+#define MPSD_A2DP_SRC_DUN_GW_DATA_COMM_DURING_AUDIO_STREAM (1ULL << 22)
+#define MPSD_A2DP_SNK_DUN_DT_DATA_COMM_DURING_AUDIO_STREAM (1ULL << 23)
+#define MPSD_HFP_AG_DUN_GW_TERMINATE_CALL_DURING_DATA_COMM (1ULL << 24)
+#define MPSD_HFP_HF_DUN_DT_TERMINATE_CALL_DURING_DATA_COMM (1ULL << 25)
+#define MPSD_HFP_AG_PAN_NAP_DATA_COMM_DURING_VOICE_CALL (1ULL << 26)
+#define MPSD_HFP_HF_PAN_PANU_DATA_COMM_DURING_VOICE_CALL (1ULL << 27)
+#define MPSD_HFP_AG_PAN_NAP_OUTGOING_CALL_DURING_DATA_COMM (1ULL << 28)
+#define MPSD_HFP_HF_PAN_PANU_OUTGOING_CALL_DURING_DATA_COMM (1ULL << 29)
+#define MPSD_HFP_AG_PAN_NAP_INCOMING_CALL_DURING_DATA_COMM (1ULL << 30)
+#define MPSD_HFP_HF_PAN_PANU_INCOMING_CALL_DURING_DATA_COMM (1ULL << 31)
+#define MPSD_A2DP_SRC_PAN_NAP_START_AUDIO_DURING_DATA_COMM (1ULL << 32)
+#define MPSD_A2DP_SNK_PAN_PANU_START_AUDIO_DURING_DATA_COMM (1ULL << 33)
+#define MPSD_A2DP_SRC_PAN_NAP_DATA_COMM_DURING_AUDIO_STREAM (1ULL << 34)
+#define MPSD_A2DP_SNK_PAN_PANU_DATA_COMM_DURING_AUDIO_STREAM (1ULL << 35)
+#define MPSD_A2DP_SRC_PBAP_SRV_PB_DL_DURING_AUDIO_STREAM (1ULL << 36)
+#define MPSD_A2DP_SNK_PBAP_CLI_PB_DL_DURING_AUDIO_STREAM (1ULL << 37)
+
+#define MPMD_HFP_HF_A2DP_SNK_AVRCP_CT_ANSWER_CALL_DURING_AUDIO (1ULL << 0)
+#define MPMD_A2DP_SRC_AVRCP_TG_ANSWER_CALL_DURING_AUDIO (1ULL << 1)
+#define MPMD_HFP_HF_A2DP_SNK_AVRCP_CT_OUTGOING_CALL_DURING_AUDIO (1ULL << 2)
+#define MPMD_A2DP_SRC_AVRCP_TG_OUTGOING_CALL_DURING_AUDIO (1ULL << 3)
+#define MPMD_HFP_HF_A2DP_SNK_AVRCP_CT_REJECT_CALL_DURING_AUDIO (1ULL << 4)
+#define MPMD_A2DP_SRC_AVRCP_TG_REJECT_CALL_DURING_AUDIO (1ULL << 5)
+#define MPMD_HFP_AG_CALL_TERMINATION_DURING_AVP (1ULL << 6)
+#define MPMD_HFP_HF_A2DP_SNK_AVRCP_CT_TERMINATION_DURING_AVP (1ULL << 7)
+#define MPMD_A2DP_SRC_AVRCP_TG_TERMINATION_DURING_AVP (1ULL << 8)
+#define MPMD_HFP_HF_A2DP_SNK_AVRCP_CT_PLAY_DURING_CALL (1ULL << 9)
+#define MPMD_A2DP_SRC_AVRCP_TG_PRESS_PLAY_DURING_CALL (1ULL << 10)
+#define MPMD_AVRCP_CT_NO_A2DP_SNK_START_AUDIO_AFTER_PLAY (1ULL << 11)
+#define MPMD_A2DP_SRC_AVRCP_TG_START_AUDIO_AFTER_PLAY (1ULL << 12)
+#define MPMD_AVRCP_CT_NO_A2DP_SNK_SUSPEND_AUDIO_AFTER_PAUSE (1ULL << 13)
+#define MPMD_A2DP_SRC_AVRCP_TG_SUSPEND_AUDIO_AFTER_PAUSE (1ULL << 14)
+#define MPMD_A2DP_SRC_AVRCP_TG_START_AUDIO_DURING_DATA_COMM (1ULL << 15)
+#define MPMD_A2DP_SNK_AVRCP_CT_DUN_DT_AUDIO_DURING_DATA_COMM (1ULL << 16)
+#define MPMD_A2DP_SRC_AVRCP_TG_START_DATA_DURING_AUDIO (1ULL << 17)
+#define MPMD_A2DP_SNK_AVRCP_CT_DUN_DT_START_DATA_DURING_AUDIO (1ULL << 18)
+
+/* Note: in spec dependency bit position starts from 1 (bit 0 unused?) */
+#define MPS_DEPS_SNIFF_MODE_DURRING_STREAMING (1ULL << 1)
+#define MPS_DEPS_GAVDP_REQUIREMENTS (1ULL << 2)
+#define MPS_DEPS_DIS_CONNECTION_ORDER_BEHAVIOR (1ULL << 3)
+
+/*
+ * default MPS features are all disabled, will be updated if relevant service
+ * is (un)registered
+ */
+#define MPS_MPSD_DEFAULT_FEATURES 0
+#define MPS_MPMD_DEFAULT_FEATURES 0
+
+/*
+ * Those defines bits for all features that depend on specific profile and role.
+ * If profile is not supported then all those bits should not be set in record
+ */
+#define MPS_MPSD_HFP_AG (MPSD_HFP_AG_A2DP_SRC_ANSWER_CALL_DURING_AUDIO | \
+ MPSD_HFP_AG_A2DP_SRC_OUTGOING_CALL_DURING_AUDIO | \
+ MPSD_HFP_AG_A2DP_SRC_REJECT_CALL_DURING_AUDIO | \
+ MPSD_HFP_AG_A2DP_SRC_TERMINATE_CALL_DURING_AVP | \
+ MPSD_HFP_AG_A2DP_SRC_PRESS_PLAY_DURING_ACTIVE_CALL | \
+ MPSD_HFP_AG_A2DP_SRC_START_AUDIO_STREAM_AFTER_PLAY | \
+ MPSD_HFP_AG_DUN_GW_DATA_COMM_DURING_VOICE_CALL | \
+ MPSD_HFP_AG_A2DP_SRC_SUSPEND_AUDIO_STREAM_ON_PAUSE | \
+ MPSD_HFP_AG_DUN_GW_OUTGOING_CALL_DURING_DATA_COMM | \
+ MPSD_HFP_AG_DUN_GW_INCOMING_CALL_DURING_DATA_COMM | \
+ MPSD_HFP_AG_DUN_GW_TERMINATE_CALL_DURING_DATA_COMM | \
+ MPSD_HFP_AG_PAN_NAP_DATA_COMM_DURING_VOICE_CALL | \
+ MPSD_HFP_AG_PAN_NAP_OUTGOING_CALL_DURING_DATA_COMM | \
+ MPSD_HFP_AG_PAN_NAP_INCOMING_CALL_DURING_DATA_COMM)
+
+#define MPS_MPSD_HFP_HF (MPSD_HFP_HF_A2DP_SNK_ANSWER_CALL_DURING_AUDIO | \
+ MPSD_HFP_HF_A2DP_SNK_OUTGOING_CALL_DURING_AUDIO | \
+ MPSD_HFP_HF_A2DP_SNK_SRC_REJECT_CALL_DURING_AUDIO | \
+ MPSD_HFP_HF_A2DP_SNK_TERMINATE_CALL_DURING_AVP | \
+ MPSD_HFP_HF_A2DP_SNK_PRESS_PLAY_DURING_ACTIVE_CALL | \
+ MPSD_HFP_HF_A2DP_SNK_START_AUDIO_STREAM_AFTER_PLAY | \
+ MPSD_HFP_HF_A2DP_SNK_SUSPEND_AUDIO_STREAM_ON_PAUSE | \
+ MPSD_HFP_HF_DUN_DT_DATA_COMM_DURING_VOICE_CALL | \
+ MPSD_HFP_HF_DUN_DT_OUTGOING_CALL_DURING_DATA_COMM | \
+ MPSD_HFP_HF_DUN_DT_INCOMING_CALL_DURING_DATA_COMM | \
+ MPSD_HFP_HF_DUN_DT_TERMINATE_CALL_DURING_DATA_COMM | \
+ MPSD_HFP_HF_PAN_PANU_DATA_COMM_DURING_VOICE_CALL | \
+ MPSD_HFP_HF_PAN_PANU_OUTGOING_CALL_DURING_DATA_COMM | \
+ MPSD_HFP_HF_PAN_PANU_INCOMING_CALL_DURING_DATA_COMM)
+
+#define MPS_MPSD_A2DP_SRC (MPSD_HFP_AG_A2DP_SRC_ANSWER_CALL_DURING_AUDIO | \
+ MPSD_HFP_AG_A2DP_SRC_OUTGOING_CALL_DURING_AUDIO | \
+ MPSD_HFP_AG_A2DP_SRC_REJECT_CALL_DURING_AUDIO | \
+ MPSD_HFP_AG_A2DP_SRC_TERMINATE_CALL_DURING_AVP | \
+ MPSD_HFP_AG_A2DP_SRC_PRESS_PLAY_DURING_ACTIVE_CALL | \
+ MPSD_HFP_AG_A2DP_SRC_START_AUDIO_STREAM_AFTER_PLAY | \
+ MPSD_HFP_AG_A2DP_SRC_SUSPEND_AUDIO_STREAM_ON_PAUSE | \
+ MPSD_A2DP_SRC_DUN_GW_START_AUDIO_DURING_DATA_COMM | \
+ MPSD_A2DP_SRC_DUN_GW_DATA_COMM_DURING_AUDIO_STREAM | \
+ MPSD_A2DP_SRC_PAN_NAP_START_AUDIO_DURING_DATA_COMM | \
+ MPSD_A2DP_SRC_PAN_NAP_DATA_COMM_DURING_AUDIO_STREAM | \
+ MPSD_A2DP_SRC_PBAP_SRV_PB_DL_DURING_AUDIO_STREAM)
+
+#define MPS_MPSD_A2DP_SNK (MPSD_HFP_HF_A2DP_SNK_ANSWER_CALL_DURING_AUDIO | \
+ MPSD_HFP_HF_A2DP_SNK_OUTGOING_CALL_DURING_AUDIO | \
+ MPSD_HFP_HF_A2DP_SNK_SRC_REJECT_CALL_DURING_AUDIO | \
+ MPSD_HFP_HF_A2DP_SNK_TERMINATE_CALL_DURING_AVP | \
+ MPSD_HFP_HF_A2DP_SNK_PRESS_PLAY_DURING_ACTIVE_CALL | \
+ MPSD_HFP_HF_A2DP_SNK_START_AUDIO_STREAM_AFTER_PLAY | \
+ MPSD_HFP_HF_A2DP_SNK_SUSPEND_AUDIO_STREAM_ON_PAUSE | \
+ MPSD_A2DP_SNK_DUN_DT_START_AUDIO_DURING_DATA_COMM | \
+ MPSD_A2DP_SNK_DUN_DT_DATA_COMM_DURING_AUDIO_STREAM | \
+ MPSD_A2DP_SNK_PAN_PANU_START_AUDIO_DURING_DATA_COMM | \
+ MPSD_A2DP_SNK_PAN_PANU_DATA_COMM_DURING_AUDIO_STREAM | \
+ MPSD_A2DP_SNK_PBAP_CLI_PB_DL_DURING_AUDIO_STREAM)
+
+#define MPS_MPSD_AVRCP_CT MPS_MPSD_A2DP_SNK
+
+#define MPS_MPSD_AVRCP_TG MPS_MPSD_A2DP_SRC
+
+#define MPS_MPSD_DUN_GW (MPSD_HFP_AG_DUN_GW_DATA_COMM_DURING_VOICE_CALL | \
+ MPSD_HFP_AG_DUN_GW_OUTGOING_CALL_DURING_DATA_COMM | \
+ MPSD_HFP_AG_DUN_GW_INCOMING_CALL_DURING_DATA_COMM | \
+ MPSD_A2DP_SRC_DUN_GW_START_AUDIO_DURING_DATA_COMM | \
+ MPSD_A2DP_SRC_DUN_GW_DATA_COMM_DURING_AUDIO_STREAM | \
+ MPSD_HFP_AG_DUN_GW_TERMINATE_CALL_DURING_DATA_COMM)
+
+#define MPS_MPSD_DUN_DT (MPSD_HFP_HF_DUN_DT_DATA_COMM_DURING_VOICE_CALL | \
+ MPSD_HFP_HF_DUN_DT_OUTGOING_CALL_DURING_DATA_COMM | \
+ MPSD_HFP_HF_DUN_DT_INCOMING_CALL_DURING_DATA_COMM | \
+ MPSD_A2DP_SNK_DUN_DT_START_AUDIO_DURING_DATA_COMM | \
+ MPSD_A2DP_SNK_DUN_DT_DATA_COMM_DURING_AUDIO_STREAM | \
+ MPSD_HFP_HF_DUN_DT_TERMINATE_CALL_DURING_DATA_COMM)
+
+#define MPS_MPSD_PAN_NAP (MPSD_HFP_AG_PAN_NAP_DATA_COMM_DURING_VOICE_CALL | \
+ MPSD_HFP_AG_PAN_NAP_OUTGOING_CALL_DURING_DATA_COMM | \
+ MPSD_HFP_AG_PAN_NAP_INCOMING_CALL_DURING_DATA_COMM | \
+ MPSD_A2DP_SRC_PAN_NAP_START_AUDIO_DURING_DATA_COMM | \
+ MPSD_A2DP_SRC_PAN_NAP_DATA_COMM_DURING_AUDIO_STREAM)
+
+#define MPS_MPSD_PAN_PANU (MPSD_HFP_HF_PAN_PANU_DATA_COMM_DURING_VOICE_CALL | \
+ MPSD_HFP_HF_PAN_PANU_OUTGOING_CALL_DURING_DATA_COMM | \
+ MPSD_HFP_HF_PAN_PANU_INCOMING_CALL_DURING_DATA_COMM | \
+ MPSD_A2DP_SNK_PAN_PANU_START_AUDIO_DURING_DATA_COMM | \
+ MPSD_A2DP_SNK_PAN_PANU_DATA_COMM_DURING_AUDIO_STREAM)
+
+#define MPS_MPSD_PBAP_SRC MPSD_A2DP_SRC_PBAP_SRV_PB_DL_DURING_AUDIO_STREAM
+
+#define MPS_MPSD_PBAP_CLI MPSD_A2DP_SNK_PBAP_CLI_PB_DL_DURING_AUDIO_STREAM
+
+#define MPS_MPSD_ALL (MPS_MPSD_HFP_AG | MPS_MPSD_HFP_HF | \
+ MPS_MPSD_A2DP_SRC | MPS_MPSD_A2DP_SNK | \
+ MPS_MPSD_AVRCP_CT | MPS_MPSD_AVRCP_TG | \
+ MPS_MPSD_DUN_GW | MPS_MPSD_DUN_DT | \
+ MPS_MPSD_PAN_NAP | MPS_MPSD_PAN_PANU | \
+ MPS_MPSD_PBAP_SRC | MPS_MPSD_PBAP_CLI)
+
+#define MPS_MPMD_HFP_AG MPMD_HFP_AG_CALL_TERMINATION_DURING_AVP
+
+#define MPS_MPMD_HFP_HF ( \
+ MPMD_HFP_HF_A2DP_SNK_AVRCP_CT_ANSWER_CALL_DURING_AUDIO | \
+ MPMD_HFP_HF_A2DP_SNK_AVRCP_CT_OUTGOING_CALL_DURING_AUDIO | \
+ MPMD_HFP_HF_A2DP_SNK_AVRCP_CT_REJECT_CALL_DURING_AUDIO | \
+ MPMD_HFP_HF_A2DP_SNK_AVRCP_CT_TERMINATION_DURING_AVP | \
+ MPMD_HFP_HF_A2DP_SNK_AVRCP_CT_PLAY_DURING_CALL)
+
+#define MPS_MPMD_A2DP_SRC (MPMD_A2DP_SRC_AVRCP_TG_ANSWER_CALL_DURING_AUDIO | \
+ MPMD_A2DP_SRC_AVRCP_TG_OUTGOING_CALL_DURING_AUDIO | \
+ MPMD_A2DP_SRC_AVRCP_TG_REJECT_CALL_DURING_AUDIO | \
+ MPMD_A2DP_SRC_AVRCP_TG_TERMINATION_DURING_AVP | \
+ MPMD_A2DP_SRC_AVRCP_TG_PRESS_PLAY_DURING_CALL | \
+ MPMD_A2DP_SRC_AVRCP_TG_START_AUDIO_AFTER_PLAY | \
+ MPMD_A2DP_SRC_AVRCP_TG_SUSPEND_AUDIO_AFTER_PAUSE | \
+ MPMD_A2DP_SRC_AVRCP_TG_START_AUDIO_DURING_DATA_COMM | \
+ MPMD_A2DP_SRC_AVRCP_TG_START_DATA_DURING_AUDIO)
+
+#define MPS_MPMD_A2DP_SNK ( \
+ MPMD_HFP_HF_A2DP_SNK_AVRCP_CT_ANSWER_CALL_DURING_AUDIO | \
+ MPMD_HFP_HF_A2DP_SNK_AVRCP_CT_OUTGOING_CALL_DURING_AUDIO | \
+ MPMD_HFP_HF_A2DP_SNK_AVRCP_CT_REJECT_CALL_DURING_AUDIO | \
+ MPMD_HFP_HF_A2DP_SNK_AVRCP_CT_TERMINATION_DURING_AVP | \
+ MPMD_HFP_HF_A2DP_SNK_AVRCP_CT_PLAY_DURING_CALL | \
+ MPMD_A2DP_SNK_AVRCP_CT_DUN_DT_AUDIO_DURING_DATA_COMM | \
+ MPMD_A2DP_SNK_AVRCP_CT_DUN_DT_START_DATA_DURING_AUDIO)
+
+#define MPS_MPMD_AVRCP_CT MPS_MPMD_A2DP_SNK
+
+/* should be set only if CT is supported but SNK is not supported */
+#define MPS_MPMD_AVRCP_CT_ONLY ( \
+ MPMD_AVRCP_CT_NO_A2DP_SNK_START_AUDIO_AFTER_PLAY | \
+ MPMD_AVRCP_CT_NO_A2DP_SNK_SUSPEND_AUDIO_AFTER_PAUSE)
+
+#define MPS_MPMD_AVRCP_TG MPS_MPMD_A2DP_SRC
+
+#define MPS_MPMD_DUN_DT ( \
+ MPMD_A2DP_SNK_AVRCP_CT_DUN_DT_AUDIO_DURING_DATA_COMM | \
+ MPMD_A2DP_SNK_AVRCP_CT_DUN_DT_START_DATA_DURING_AUDIO)
+
+#define MPS_MPMD_ALL (MPS_MPMD_HFP_AG | MPS_MPMD_HFP_HF | MPS_MPMD_A2DP_SRC | \
+ MPS_MPMD_A2DP_SNK | MPS_MPMD_AVRCP_CT | \
+ MPS_MPMD_AVRCP_CT_ONLY | MPS_MPMD_AVRCP_TG | \
+ MPS_MPMD_DUN_DT)
+
+/* Assume all dependencies are supported */
+#define MPS_DEFAULT_DEPS (MPS_DEPS_SNIFF_MODE_DURRING_STREAMING | \
+ MPS_DEPS_GAVDP_REQUIREMENTS | \
+ MPS_DEPS_DIS_CONNECTION_ORDER_BEHAVIOR)
+
static sdp_record_t *server = NULL;
static uint32_t fixed_dbts = 0;
};
static const int sdpServerVnumEntries = 1;
+static uint32_t mps_handle = 0;
+static bool mps_mpmd = false;
+
/*
* A simple function which returns the time of day in
* seconds. Used for updating the service db state
update_db_timestamp();
}
+static bool class_supported(uint16_t class)
+{
+ sdp_list_t *list;
+ uuid_t uuid;
+
+ sdp_uuid16_create(&uuid, class);
+
+ for (list = sdp_get_record_list(); list; list = list->next) {
+ sdp_record_t *rec = list->data;
+
+ if (sdp_uuid_cmp(&rec->svclass, &uuid) == 0)
+ return true;
+ }
+
+ return false;
+}
+
+static uint64_t mps_mpsd_features(void)
+{
+ uint64_t feat = MPS_MPSD_ALL;
+
+ if (!class_supported(HANDSFREE_AGW_SVCLASS_ID))
+ feat &= ~MPS_MPSD_HFP_AG;
+
+ if (!class_supported(HANDSFREE_SVCLASS_ID))
+ feat &= ~MPS_MPSD_HFP_HF;
+
+ if (!class_supported(AUDIO_SOURCE_SVCLASS_ID))
+ feat &= ~MPS_MPSD_A2DP_SRC;
+
+ if (!class_supported(AUDIO_SINK_SVCLASS_ID))
+ feat &= ~MPS_MPSD_A2DP_SNK;
+
+ if (!class_supported(AV_REMOTE_CONTROLLER_SVCLASS_ID))
+ feat &= ~MPS_MPSD_AVRCP_CT;
+
+ if (!class_supported(AV_REMOTE_TARGET_SVCLASS_ID))
+ feat &= ~MPS_MPSD_AVRCP_TG;
+
+ if (!class_supported(DIALUP_NET_SVCLASS_ID))
+ feat &= ~MPS_MPSD_DUN_GW;
+
+ /* TODO */
+ feat &= ~MPS_MPSD_DUN_DT;
+
+ if (!class_supported(NAP_SVCLASS_ID))
+ feat &= ~MPS_MPSD_PAN_NAP;
+
+ if (!class_supported(PANU_SVCLASS_ID))
+ feat &= ~MPS_MPSD_PAN_PANU;
+
+ if (!class_supported(PBAP_PSE_SVCLASS_ID))
+ feat &= ~MPS_MPSD_PBAP_SRC;
+
+ if (!class_supported(PBAP_PCE_SVCLASS_ID))
+ feat &= ~MPS_MPSD_PBAP_CLI;
+
+ return feat;
+}
+
+static uint64_t mps_mpmd_features(void)
+{
+ uint64_t feat = MPS_MPMD_ALL;
+
+ if (!class_supported(HANDSFREE_AGW_SVCLASS_ID))
+ feat &= ~MPS_MPMD_HFP_AG;
+
+ if (!class_supported(HANDSFREE_SVCLASS_ID))
+ feat &= ~MPS_MPMD_HFP_HF;
+
+ if (!class_supported(AUDIO_SOURCE_SVCLASS_ID))
+ feat &= ~MPS_MPMD_A2DP_SRC;
+
+ if (!class_supported(AUDIO_SINK_SVCLASS_ID))
+ feat &= ~MPS_MPMD_A2DP_SNK;
+ else
+ feat &= ~MPS_MPMD_AVRCP_CT_ONLY;
+
+ if (!class_supported(AV_REMOTE_CONTROLLER_SVCLASS_ID)) {
+ feat &= ~MPS_MPMD_AVRCP_CT;
+ feat &= ~MPS_MPMD_AVRCP_CT_ONLY;
+ }
+
+ if (!class_supported(AV_REMOTE_TARGET_SVCLASS_ID))
+ feat &= ~MPS_MPMD_AVRCP_TG;
+
+ /* TODO */
+ feat &= ~MPS_MPMD_DUN_DT;
+
+ return feat;
+}
+
+static sdp_record_t *mps_record(int mpmd)
+{
+ sdp_data_t *mpsd_features, *mpmd_features, *dependencies;
+ sdp_list_t *svclass_id, *pfseq, *root;
+ uuid_t root_uuid, svclass_uuid;
+ sdp_profile_desc_t profile;
+ sdp_record_t *record;
+ uint64_t mpsd_feat = MPS_MPSD_DEFAULT_FEATURES;
+ uint64_t mpmd_feat = MPS_MPMD_DEFAULT_FEATURES;
+ uint16_t deps = MPS_DEFAULT_DEPS;
+
+ record = sdp_record_alloc();
+ if (!record)
+ return NULL;
+
+ sdp_uuid16_create(&root_uuid, PUBLIC_BROWSE_GROUP);
+ root = sdp_list_append(NULL, &root_uuid);
+ sdp_set_browse_groups(record, root);
+ sdp_list_free(root, NULL);
+
+ sdp_uuid16_create(&svclass_uuid, MPS_SVCLASS_ID);
+ svclass_id = sdp_list_append(NULL, &svclass_uuid);
+ sdp_set_service_classes(record, svclass_id);
+ sdp_list_free(svclass_id, NULL);
+
+ sdp_uuid16_create(&profile.uuid, MPS_PROFILE_ID);
+ profile.version = 0x0100;
+ pfseq = sdp_list_append(NULL, &profile);
+ sdp_set_profile_descs(record, pfseq);
+ sdp_list_free(pfseq, NULL);
+
+ mpsd_features = sdp_data_alloc(SDP_UINT64, &mpsd_feat);
+ sdp_attr_add(record, SDP_ATTR_MPSD_SCENARIOS, mpsd_features);
+
+ if (mpmd) {
+ mpmd_features = sdp_data_alloc(SDP_UINT64, &mpmd_feat);
+ sdp_attr_add(record, SDP_ATTR_MPMD_SCENARIOS, mpmd_features);
+ }
+
+ dependencies = sdp_data_alloc(SDP_UINT16, &deps);
+ sdp_attr_add(record, SDP_ATTR_MPS_DEPENDENCIES, dependencies);
+
+ sdp_set_info_attr(record, "Multi Profile", 0, 0);
+
+ return record;
+}
+
+void register_mps(bool mpmd)
+{
+ sdp_record_t *record;
+
+ record = mps_record(mpmd);
+ if (!record)
+ return;
+
+ if (add_record_to_server(BDADDR_ANY, record) < 0) {
+ sdp_record_free(record);
+ return;
+ }
+
+ mps_handle = record->handle;
+ mps_mpmd = mpmd;
+}
+
+static void update_mps(void)
+{
+ sdp_record_t *rec;
+ sdp_data_t *data;
+ uint64_t mpsd_feat, mpmd_feat;
+
+ if (!mps_handle)
+ return;
+
+ rec = sdp_record_find(mps_handle);
+ if (!rec)
+ return;
+
+ mpsd_feat = mps_mpsd_features();
+ data = sdp_data_alloc(SDP_UINT64, &mpsd_feat);
+ sdp_attr_replace(rec, SDP_ATTR_MPSD_SCENARIOS, data);
+
+ if (mps_mpmd) {
+ mpmd_feat = mps_mpmd_features();
+ data = sdp_data_alloc(SDP_UINT64, &mpmd_feat);
+ sdp_attr_replace(rec, SDP_ATTR_MPMD_SCENARIOS, data);
+ }
+}
+
int add_record_to_server(const bdaddr_t *src, sdp_record_t *rec)
{
sdp_data_t *data;
DBG("Record pattern UUID %s", uuid);
}
+ update_mps();
update_db_timestamp();
return 0;
if (!rec)
return -ENOENT;
- if (sdp_record_remove(handle) == 0)
+ if (sdp_record_remove(handle) == 0) {
+ update_mps();
update_db_timestamp();
+ }
sdp_record_free(rec);
*
*/
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/sdp.h>
-
#ifdef SDP_DEBUG
#include <syslog.h>
#define SDPDBG(fmt, arg...) syslog(LOG_DEBUG, "%s: " fmt "\n", __func__ , ## arg)
void register_server_service(void);
void register_device_id(uint16_t source, uint16_t vendor,
uint16_t product, uint16_t version);
+void register_mps(bool mpmd);
int record_sort(const void *r1, const void *r2);
void sdp_svcdb_reset(void);
#include <sys/ioctl.h>
#include <errno.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/sdp.h>
-#include <bluetooth/sdp_lib.h>
-
#include <glib.h>
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+
#include "log.h"
#include "adapter.h"
void *user_data;
btd_service_state_t state;
int err;
- uint16_t start_handle;
- uint16_t end_handle;
- bool auto_connect;
- bool blocked;
};
struct service_state_callback {
service->ref = 1;
service->device = device; /* Weak ref */
service->profile = profile;
- service->auto_connect = profile->auto_connect;
service->state = BTD_SERVICE_STATE_UNAVAILABLE;
return service;
}
-struct btd_service *service_create_gatt(struct btd_device *device,
- struct btd_profile *profile,
- uint16_t start_handle,
- uint16_t end_handle)
-{
- struct btd_service *service;
-
- if (!start_handle || !end_handle || start_handle > end_handle)
- return NULL;
-
- service = service_create(device, profile);
- if (!service)
- return NULL;
-
- service->start_handle = start_handle;
- service->end_handle = end_handle;
-
- return service;
-}
-
int service_probe(struct btd_service *service)
{
char addr[18];
void service_remove(struct btd_service *service)
{
+ change_state(service, BTD_SERVICE_STATE_DISCONNECTED, -ECONNABORTED);
change_state(service, BTD_SERVICE_STATE_UNAVAILABLE, 0);
service->profile->device_remove(service);
service->device = NULL;
return service->err;
}
-uint16_t btd_service_get_version(const struct btd_service *service)
-{
- const sdp_record_t *rec;
- sdp_list_t *list;
- sdp_profile_desc_t *desc;
- uint16_t version;
-
- if (!service->profile->version)
- return 0;
-
- rec = btd_device_get_record(service->device,
- service->profile->remote_uuid);
- if (rec == NULL)
- return 0;
-
- if (sdp_get_profile_descs(rec, &list) < 0)
- return 0;
-
- desc = list->data;
- version = desc->version;
- sdp_list_free(list, free);
-
- return MIN(version, service->profile->version);
-}
-
-void btd_service_set_auto_connect(struct btd_service *service, bool value)
-{
- service->auto_connect = value;
-}
-
-bool btd_service_get_auto_connect(const struct btd_service *service)
-{
- return service->auto_connect;
-}
-
-void btd_service_set_blocked(struct btd_service *service, bool value)
-{
- service->blocked = value;
-}
-
-bool btd_service_is_blocked(const struct btd_service *service)
-{
- return service->blocked;
-}
-
-bool btd_service_get_gatt_handles(const struct btd_service *service,
- uint16_t *start_handle,
- uint16_t *end_handle)
-{
- if (!service || !service->start_handle || !service->end_handle)
- return false;
-
- if (start_handle)
- *start_handle = service->start_handle;
-
- if (end_handle)
- *end_handle = service->end_handle;
-
- return true;
-}
-
unsigned int btd_service_add_state_cb(btd_service_state_cb cb, void *user_data)
{
struct service_state_callback *state_cb;
/* Service management functions used by the core */
struct btd_service *service_create(struct btd_device *device,
struct btd_profile *profile);
-struct btd_service *service_create_gatt(struct btd_device *device,
- struct btd_profile *profile,
- uint16_t start_handle,
- uint16_t end_handle);
int service_probe(struct btd_service *service);
void service_remove(struct btd_service *service);
struct btd_profile *btd_service_get_profile(const struct btd_service *service);
btd_service_state_t btd_service_get_state(const struct btd_service *service);
int btd_service_get_error(const struct btd_service *service);
-uint16_t btd_service_get_version(const struct btd_service *service);
-void btd_service_set_auto_connect(struct btd_service *service, bool value);
-bool btd_service_get_auto_connect(const struct btd_service *service);
-void btd_service_set_blocked(struct btd_service *service, bool value);
-bool btd_service_is_blocked(const struct btd_service *service);
-bool btd_service_get_gatt_handles(const struct btd_service *service,
- uint16_t *start_handle,
- uint16_t *end_handle);
unsigned int btd_service_add_state_cb(btd_service_state_cb cb,
void *user_data);
#define BT_ATT_DEFAULT_LE_MTU 23
#define BT_ATT_MAX_LE_MTU 517
+#define BT_ATT_MAX_VALUE_LEN 512
/* ATT protocol opcodes */
-#define BT_ATT_OP_ERROR_RSP 0x01
+#define BT_ATT_OP_ERROR_RSP 0x01
#define BT_ATT_OP_MTU_REQ 0x02
#define BT_ATT_OP_MTU_RSP 0x03
#define BT_ATT_OP_FIND_INFO_REQ 0x04
#include "src/shared/queue.h"
#include "src/shared/util.h"
#include "src/shared/timeout.h"
+#include "lib/bluetooth.h"
#include "lib/uuid.h"
#include "src/shared/att.h"
+#include "src/shared/crypto.h"
#define ATT_MIN_PDU_LEN 1 /* At least 1 byte for the opcode. */
#define ATT_OP_CMD_MASK 0x40
#define BT_ERROR_ALREADY_IN_PROGRESS 0xfe
#define BT_ERROR_OUT_OF_RANGE 0xff
+/* Length of signature in write signed packet */
+#define BT_ATT_SIGNATURE_LEN 12
+
struct att_send_op;
struct bt_att {
int ref_count;
int fd;
struct io *io;
+ bool io_on_l2cap;
+ int io_sec_level; /* Only used for non-L2CAP */
struct queue *req_queue; /* Queued ATT protocol requests */
struct att_send_op *pending_req;
bt_att_debug_func_t debug_callback;
bt_att_destroy_func_t debug_destroy;
void *debug_data;
+
+ struct bt_crypto *crypto;
+
+ struct sign_info *local_sign;
+ struct sign_info *remote_sign;
+};
+
+struct sign_info {
+ uint8_t key[16];
+ bt_att_counter_func_t counter;
+ void *user_data;
};
enum att_op_type {
return disconn->id == id;
}
-static bool encode_pdu(struct att_send_op *op, const void *pdu,
- uint16_t length, uint16_t mtu)
+static bool encode_pdu(struct bt_att *att, struct att_send_op *op,
+ const void *pdu, uint16_t length)
{
uint16_t pdu_len = 1;
+ struct sign_info *sign = att->local_sign;
+ uint32_t sign_cnt;
+
+ if (sign && (op->opcode & ATT_OP_SIGNED_MASK))
+ pdu_len += BT_ATT_SIGNATURE_LEN;
if (length && pdu)
pdu_len += length;
- if (pdu_len > mtu)
+ if (pdu_len > att->mtu)
return false;
op->len = pdu_len;
if (pdu_len > 1)
memcpy(op->pdu + 1, pdu, length);
- return true;
+ if (!sign || !(op->opcode & ATT_OP_SIGNED_MASK))
+ return true;
+
+ if (!sign->counter(&sign_cnt, sign->user_data))
+ goto fail;
+
+ if ((bt_crypto_sign_att(att->crypto, sign->key, op->pdu, 1 + length,
+ sign_cnt, &((uint8_t *) op->pdu)[1 + length])))
+ return true;
+
+ util_debug(att->debug_callback, att->debug_data,
+ "ATT unable to generate signature");
+
+fail:
+ free(op->pdu);
+ return false;
}
-static struct att_send_op *create_att_send_op(uint8_t opcode, const void *pdu,
- uint16_t length, uint16_t mtu,
+static struct att_send_op *create_att_send_op(struct bt_att *att,
+ uint8_t opcode,
+ const void *pdu,
+ uint16_t length,
bt_att_response_func_t callback,
void *user_data,
bt_att_destroy_func_t destroy)
op->destroy = destroy;
op->user_data = user_data;
- if (!encode_pdu(op, pdu, length, mtu)) {
+ if (!encode_pdu(att, op, pdu, length)) {
free(op);
return NULL;
}
return opcode == test_opcode;
}
-static void notify_handler(void *data, void *user_data)
-{
- struct att_notify *notify = data;
- struct notify_data *not_data = user_data;
-
- if (!opcode_match(notify->opcode, not_data->opcode))
- return;
-
- not_data->handler_found = true;
-
- if (notify->callback)
- notify->callback(not_data->opcode, not_data->pdu,
- not_data->pdu_len, notify->user_data);
-}
-
static void respond_not_supported(struct bt_att *att, uint8_t opcode)
{
uint8_t pdu[4];
NULL);
}
+static bool handle_signed(struct bt_att *att, uint8_t opcode, uint8_t *pdu,
+ ssize_t pdu_len)
+{
+ uint8_t *signature;
+ uint32_t sign_cnt;
+ struct sign_info *sign;
+
+ /* Check if there is enough data for a signature */
+ if (pdu_len < 2 + BT_ATT_SIGNATURE_LEN)
+ goto fail;
+
+ sign = att->remote_sign;
+ if (!sign)
+ goto fail;
+
+ signature = pdu + (pdu_len - BT_ATT_SIGNATURE_LEN);
+ sign_cnt = get_le32(signature);
+
+ /* Validate counter */
+ if (!sign->counter(&sign_cnt, sign->user_data))
+ goto fail;
+
+ /* Generate signature and verify it */
+ if (!bt_crypto_sign_att(att->crypto, sign->key, pdu,
+ pdu_len - BT_ATT_SIGNATURE_LEN, sign_cnt,
+ signature))
+ goto fail;
+
+ return true;
+
+fail:
+ util_debug(att->debug_callback, att->debug_data,
+ "ATT failed to verify signature: 0x%02x", opcode);
+
+ return false;
+}
+
static void handle_notify(struct bt_att *att, uint8_t opcode, uint8_t *pdu,
ssize_t pdu_len)
{
- struct notify_data data;
+ const struct queue_entry *entry;
+ bool found;
+
+ if (opcode & ATT_OP_SIGNED_MASK) {
+ if (!handle_signed(att, opcode, pdu, pdu_len))
+ return;
+ pdu_len -= BT_ATT_SIGNATURE_LEN;
+ }
bt_att_ref(att);
- memset(&data, 0, sizeof(data));
- data.opcode = opcode;
+ found = false;
+ entry = queue_get_entries(att->notify_list);
- if (pdu_len > 0) {
- data.pdu = pdu;
- data.pdu_len = pdu_len;
- }
+ while (entry) {
+ struct att_notify *notify = entry->data;
- queue_foreach(att->notify_list, notify_handler, &data);
+ entry = entry->next;
- bt_att_unref(att);
+ if (!opcode_match(notify->opcode, opcode))
+ continue;
+
+ found = true;
+
+ if (notify->callback)
+ notify->callback(opcode, pdu, pdu_len,
+ notify->user_data);
+
+ /* callback could remove all entries from notify list */
+ if (queue_isempty(att->notify_list))
+ break;
+ }
/*
* If this was a request and no handler was registered for it, respond
* with "Not Supported"
*/
- if (!data.handler_found && get_op_type(opcode) == ATT_OP_TYPE_REQ)
+ if (!found && get_op_type(opcode) == ATT_OP_TYPE_REQ)
respond_not_supported(att, opcode);
+
+ bt_att_unref(att);
}
static bool can_read_data(struct io *io, void *user_data)
return true;
}
+static bool is_io_l2cap_based(int fd)
+{
+ int domain;
+ int proto;
+ int err;
+ socklen_t len;
+
+ domain = 0;
+ len = sizeof(domain);
+ err = getsockopt(fd, SOL_SOCKET, SO_DOMAIN, &domain, &len);
+ if (err < 0)
+ return false;
+
+ if (domain != AF_BLUETOOTH)
+ return false;
+
+ proto = 0;
+ len = sizeof(proto);
+ err = getsockopt(fd, SOL_SOCKET, SO_PROTOCOL, &proto, &len);
+ if (err < 0)
+ return false;
+
+ return proto == BTPROTO_L2CAP;
+}
+
static void bt_att_free(struct bt_att *att)
{
if (att->pending_req)
destroy_att_send_op(att->pending_ind);
io_destroy(att->io);
+ bt_crypto_unref(att->crypto);
queue_destroy(att->req_queue, NULL);
queue_destroy(att->ind_queue, NULL);
if (att->debug_destroy)
att->debug_destroy(att->debug_data);
+ free(att->local_sign);
+ free(att->remote_sign);
+
free(att->buf);
free(att);
if (!att->io)
goto fail;
+ /* crypto is optional, if not available leave it NULL */
+ att->crypto = bt_crypto_new();
+
att->req_queue = queue_new();
if (!att->req_queue)
goto fail;
if (!io_set_disconnect_handler(att->io, disconnect_cb, att, NULL))
goto fail;
+ att->io_on_l2cap = is_io_l2cap_based(att->fd);
+ if (!att->io_on_l2cap)
+ att->io_sec_level = BT_SECURITY_LOW;
+
return bt_att_ref(att);
fail:
return io_set_close_on_destroy(att->io, do_close);
}
+int bt_att_get_fd(struct bt_att *att)
+{
+ if (!att)
+ return -1;
+
+ return att->fd;
+}
+
bool bt_att_set_debug(struct bt_att *att, bt_att_debug_func_t callback,
void *user_data, bt_att_destroy_func_t destroy)
{
if (!att || !att->io)
return 0;
- op = create_att_send_op(opcode, pdu, length, att->mtu, callback,
- user_data, destroy);
+ op = create_att_send_op(att, opcode, pdu, length, callback, user_data,
+ destroy);
if (!op)
return 0;
return true;
}
+
+int bt_att_get_sec_level(struct bt_att *att)
+{
+ struct bt_security sec;
+ socklen_t len;
+
+ if (!att)
+ return -EINVAL;
+
+ if (!att->io_on_l2cap)
+ return att->io_sec_level;
+
+ memset(&sec, 0, sizeof(sec));
+ len = sizeof(sec);
+ if (getsockopt(att->fd, SOL_BLUETOOTH, BT_SECURITY, &sec, &len) < 0)
+ return -EIO;
+
+ return sec.level;
+}
+
+bool bt_att_set_sec_level(struct bt_att *att, int level)
+{
+ struct bt_security sec;
+
+ if (!att || level < BT_SECURITY_LOW || level > BT_SECURITY_HIGH)
+ return false;
+
+ if (!att->io_on_l2cap) {
+ att->io_sec_level = level;
+ return true;
+ }
+
+ memset(&sec, 0, sizeof(sec));
+ sec.level = level;
+
+ if (setsockopt(att->fd, SOL_BLUETOOTH, BT_SECURITY, &sec,
+ sizeof(sec)) < 0)
+ return false;
+
+ return true;
+}
+
+static bool sign_set_key(struct sign_info **sign, uint8_t key[16],
+ bt_att_counter_func_t func, void *user_data)
+{
+ if (!(*sign)) {
+ *sign = new0(struct sign_info, 1);
+ if (!(*sign))
+ return false;
+ }
+
+ (*sign)->counter = func;
+ (*sign)->user_data = user_data;
+ memcpy((*sign)->key, key, 16);
+
+ return true;
+}
+
+bool bt_att_set_local_key(struct bt_att *att, uint8_t sign_key[16],
+ bt_att_counter_func_t func, void *user_data)
+{
+ if (!att)
+ return false;
+
+ return sign_set_key(&att->local_sign, sign_key, func, user_data);
+}
+
+bool bt_att_set_remote_key(struct bt_att *att, uint8_t sign_key[16],
+ bt_att_counter_func_t func, void *user_data)
+{
+ if (!att)
+ return false;
+
+ return sign_set_key(&att->remote_sign, sign_key, func, user_data);
+}
bool bt_att_set_close_on_unref(struct bt_att *att, bool do_close);
+int bt_att_get_fd(struct bt_att *att);
+
typedef void (*bt_att_response_func_t)(uint8_t opcode, const void *pdu,
uint16_t length, void *user_data);
typedef void (*bt_att_notify_func_t)(uint8_t opcode, const void *pdu,
typedef void (*bt_att_timeout_func_t)(unsigned int id, uint8_t opcode,
void *user_data);
typedef void (*bt_att_disconnect_func_t)(int err, void *user_data);
+typedef bool (*bt_att_counter_func_t)(uint32_t *sign_cnt, void *user_data);
bool bt_att_set_debug(struct bt_att *att, bt_att_debug_func_t callback,
void *user_data, bt_att_destroy_func_t destroy);
bool bt_att_unregister_disconnect(struct bt_att *att, unsigned int id);
bool bt_att_unregister_all(struct bt_att *att);
+
+int bt_att_get_sec_level(struct bt_att *att);
+bool bt_att_set_sec_level(struct bt_att *att, int level);
+
+bool bt_att_set_local_key(struct bt_att *att, uint8_t sign_key[16],
+ bt_att_counter_func_t func, void *user_data);
+bool bt_att_set_remote_key(struct bt_att *att, uint8_t sign_key[16],
+ bt_att_counter_func_t func, void *user_data);
#include "src/shared/util.h"
#include "src/shared/crypto.h"
-#ifndef PF_ALG
+#ifndef HAVE_LINUX_IF_ALG_H
+#ifndef HAVE_LINUX_TYPES_H
+typedef uint8_t __u8;
+typedef uint16_t __u16;
+typedef uint32_t __u32;
+#else
#include <linux/types.h>
+#endif
struct sockaddr_alg {
__u16 salg_family;
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
memcpy(irk->addr, addr, 6);
memcpy(irk->key, key, 16);
+ if (!queue_push_tail(gap->irk_list, irk)) {
+ free(irk);
+ return false;
+ }
+
return true;
}
*/
#include "src/shared/att.h"
+#include "lib/bluetooth.h"
#include "lib/uuid.h"
#include "src/shared/gatt-helpers.h"
#include "src/shared/util.h"
#include "src/shared/gatt-db.h"
#include "src/shared/gatt-client.h"
-#ifdef BLUEZ5_GATT_CLIENT
+#ifdef BLUEZ5_27_GATT_CLIENT
#include "../log.h"
#endif
#include <assert.h>
#include <limits.h>
+#include <sys/uio.h>
#ifndef MAX
#define MAX(a, b) ((a) > (b) ? (a) : (b))
bool in_init;
bool ready;
- /* Queue of long write requests. An error during "prepare write"
+ /*
+ * Queue of long write requests. An error during "prepare write"
* requests can result in a cancel through "execute write". To prevent
* cancelation of prepared writes to the wrong attribute and multiple
* requests to the same attribute that may result in a corrupted final
int next_reg_id;
unsigned int disc_id, notify_id, ind_id;
- /* Handles of the GATT Service and the Service Changed characteristic
+ /*
+ * Handles of the GATT Service and the Service Changed characteristic
* value handle. These will have the value 0 if they are not present on
* the remote peripheral.
*/
unsigned int svc_chngd_ind_id;
+ bool svc_chngd_registered;
struct queue *svc_chngd_queue; /* Queued service changed events */
bool in_svc_chngd;
+
+ /*
+ * List of pending read/write operations. For operations that span
+ * across multiple PDUs, this list provides a mapping from an operation
+ * id to an ATT request id.
+ */
+ struct queue *pending_requests;
+ unsigned int next_request_id;
+
+ struct bt_gatt_request *discovery_req;
+ unsigned int mtu_req_id;
};
+struct request {
+ struct bt_gatt_client *client;
+ bool long_write;
+ bool removed;
+ int ref_count;
+ unsigned int id;
+ unsigned int att_id;
+ void *data;
+ void (*destroy)(void *);
+};
+
+static struct request *request_ref(struct request *req)
+{
+ __sync_fetch_and_add(&req->ref_count, 1);
+
+ return req;
+}
+
+static struct request *request_create(struct bt_gatt_client *client)
+{
+ struct request *req;
+
+ req = new0(struct request, 1);
+ if (!req)
+ return NULL;
+
+ if (client->next_request_id < 1)
+ client->next_request_id = 1;
+
+ queue_push_tail(client->pending_requests, req);
+ req->client = client;
+ req->id = client->next_request_id++;
+
+ return request_ref(req);
+}
+
+static void request_unref(void *data)
+{
+ struct request *req = data;
+
+ if (__sync_sub_and_fetch(&req->ref_count, 1))
+ return;
+
+ if (req->destroy)
+ req->destroy(req->data);
+
+ if (!req->removed)
+ queue_remove(req->client->pending_requests, req);
+
+ free(req);
+}
+
struct notify_chrc {
uint16_t value_handle;
uint16_t ccc_handle;
struct notify_data {
struct bt_gatt_client *client;
- bool invalid;
unsigned int id;
+ unsigned int att_id;
int ref_count;
struct notify_chrc *chrc;
- bt_gatt_client_notify_id_callback_t callback;
+ bt_gatt_client_register_callback_t callback;
bt_gatt_client_notify_callback_t notify;
void *user_data;
bt_gatt_client_destroy_func_t destroy;
&properties, NULL))
return NULL;
- /* Find the CCC characteristic */
- ccc = NULL;
- gatt_db_service_foreach_desc(attr, find_ccc, &ccc);
- if (!ccc)
- return NULL;
-
chrc = new0(struct notify_chrc, 1);
if (!chrc)
return NULL;
return NULL;
}
+ /*
+ * Find the CCC characteristic. Some characteristics that allow
+ * notifications may not have a CCC descriptor. We treat these as
+ * automatically successful.
+ */
+ ccc = NULL;
+ gatt_db_service_foreach_desc(attr, find_ccc, &ccc);
+ if (ccc)
+ chrc->ccc_handle = gatt_db_attribute_get_handle(ccc);
+
chrc->value_handle = value_handle;
- chrc->ccc_handle = gatt_db_attribute_get_handle(ccc);
chrc->properties = properties;
queue_push_tail(client->notify_chrcs, chrc);
discovery_op_free(op);
}
+static void discovery_req_clear(struct bt_gatt_client *client)
+{
+ if (!client->discovery_req)
+ return;
+
+ bt_gatt_request_unref(client->discovery_req);
+ client->discovery_req = NULL;
+}
+
static void discover_chrcs_cb(bool success, uint8_t att_ecode,
struct bt_gatt_result *result,
void *user_data);
char uuid_str[MAX_LEN_UUID_STR];
unsigned int includes_count, i;
+ discovery_req_clear(client);
+
if (!success) {
if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND)
goto next;
goto failed;
op->cur_svc = attr;
- if (bt_gatt_discover_characteristics(client->att,
+
+ client->discovery_req = bt_gatt_discover_characteristics(
+ client->att,
start, end,
discover_chrcs_cb,
discovery_op_ref(op),
- discovery_op_unref))
+ discovery_op_unref);
+ if (client->discovery_req)
return;
util_debug(client->debug_callback, client->debug_data,
if (!gatt_db_attribute_get_service_handles(attr, &start, &end))
goto failed;
- if (bt_gatt_discover_included_services(client->att, start, end,
+ if (start == end)
+ goto next;
+
+ client->discovery_req = bt_gatt_discover_included_services(client->att,
+ start, end,
discover_incl_cb,
discovery_op_ref(op),
- discovery_op_unref))
+ discovery_op_unref);
+ if (client->discovery_req)
return;
util_debug(client->debug_callback, client->debug_data,
*discovering = false;
while ((chrc_data = queue_pop_head(op->pending_chrcs))) {
- attr = gatt_db_service_add_characteristic(op->cur_svc,
+ attr = gatt_db_service_insert_characteristic(op->cur_svc,
+ chrc_data->value_handle,
&chrc_data->uuid, 0,
chrc_data->properties,
NULL, NULL, NULL);
chrc_data->value_handle)
goto failed;
+#ifdef __TIZEN_PATCH__
+ if (chrc_data->value_handle >= chrc_data->end_handle) {
+ free(chrc_data);
+ continue;
+ }
+ desc_start = chrc_data->value_handle + 1;
+#else
desc_start = chrc_data->value_handle + 1;
-
if (desc_start > chrc_data->end_handle) {
free(chrc_data);
continue;
}
+#endif
- if (bt_gatt_discover_descriptors(client->att, desc_start,
+ client->discovery_req = bt_gatt_discover_descriptors(
+ client->att, desc_start,
chrc_data->end_handle,
discover_descs_cb,
discovery_op_ref(op),
- discovery_op_unref)) {
+ discovery_op_unref);
+ if (client->discovery_req) {
*discovering = true;
goto done;
}
unsigned int desc_count;
bool discovering;
+ discovery_req_clear(client);
+
if (!success) {
if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND) {
success = true;
"handle: 0x%04x, uuid: %s",
handle, uuid_str);
- attr = gatt_db_service_add_descriptor(op->cur_svc, &uuid, 0,
- NULL, NULL, NULL);
+ attr = gatt_db_service_insert_descriptor(op->cur_svc, handle,
+ &uuid, 0, NULL, NULL,
+ NULL);
if (!attr)
goto failed;
goto failed;
}
+next:
if (!discover_descs(op, &discovering))
goto failed;
if (discovering)
return;
-next:
/* Done with the current service */
gatt_db_service_set_active(op->cur_svc, true);
if (!gatt_db_attribute_get_service_handles(attr, &start, &end))
goto failed;
+ if (start == end)
+ goto next;
+
/* Move on to the next service */
op->cur_svc = attr;
- if (bt_gatt_discover_characteristics(client->att, start, end,
+
+ client->discovery_req = bt_gatt_discover_characteristics(client->att,
+ start, end,
discover_chrcs_cb,
discovery_op_ref(op),
- discovery_op_unref))
+ discovery_op_unref);
+ if (client->discovery_req)
return;
util_debug(client->debug_callback, client->debug_data,
unsigned int chrc_count;
bool discovering;
+ discovery_req_clear(client);
+
if (!success) {
if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND) {
success = true;
if (!gatt_db_attribute_get_service_handles(attr, &start, &end))
goto failed;
+ if (start == end)
+ goto next;
+
/* Move on to the next service */
op->cur_svc = attr;
- if (bt_gatt_discover_characteristics(client->att, start, end,
+
+ client->discovery_req = bt_gatt_discover_characteristics(client->att,
+ start, end,
discover_chrcs_cb,
discovery_op_ref(op),
- discovery_op_unref))
+ discovery_op_unref);
+ if (client->discovery_req)
return;
util_debug(client->debug_callback, client->debug_data,
bt_uuid_t uuid;
char uuid_str[MAX_LEN_UUID_STR];
+ discovery_req_clear(client);
+
if (!success) {
util_debug(client->debug_callback, client->debug_data,
"Secondary service discovery failed."
goto done;
}
- if (bt_gatt_discover_included_services(client->att, start, end,
+ client->discovery_req = bt_gatt_discover_included_services(client->att,
+ start, end,
discover_incl_cb,
discovery_op_ref(op),
- discovery_op_unref))
+ discovery_op_unref);
+ if (client->discovery_req)
return;
util_debug(client->debug_callback, client->debug_data,
bt_uuid_t uuid;
char uuid_str[MAX_LEN_UUID_STR];
+ discovery_req_clear(client);
+
if (!success) {
util_debug(client->debug_callback, client->debug_data,
"Primary service discovery failed."
" ATT ECODE: 0x%02x", att_ecode);
- goto done;
+ goto secondary;
}
if (!result || !bt_gatt_iter_init(&iter, result)) {
queue_push_tail(op->pending_svcs, attr);
}
+secondary:
/* Discover secondary services */
- if (bt_gatt_discover_secondary_services(client->att, NULL,
- op->start, op->end,
- discover_secondary_cb,
- discovery_op_ref(op),
- discovery_op_unref))
+ client->discovery_req = bt_gatt_discover_secondary_services(client->att,
+ NULL, op->start, op->end,
+ discover_secondary_cb,
+ discovery_op_ref(op),
+ discovery_op_unref);
+ if (client->discovery_req)
return;
util_debug(client->debug_callback, client->debug_data,
struct bt_gatt_client *client = op->client;
op->success = success;
+ client->mtu_req_id = 0;
if (!success) {
util_debug(client->debug_callback, client->debug_data,
return;
}
- if (bt_gatt_discover_all_primary_services(client->att, NULL,
+ client->discovery_req = bt_gatt_discover_all_primary_services(
+ client->att, NULL,
discover_primary_cb,
discovery_op_ref(op),
- discovery_op_unref))
+ discovery_op_unref);
+ if (client->discovery_req)
return;
util_debug(client->debug_callback, client->debug_data,
uint16_t end_handle;
};
-#ifdef BLUEZ5_GATT_CLIENT
+#ifdef BLUEZ5_27_GATT_CLIENT
bool bt_gatt_discover_services(struct bt_gatt_client *client)
{
struct discovery_op *op;
return false;
op->client = client;
- gatt_client_clear_services(op->client);
+ gatt_db_unref(op->client->db);
if (bt_gatt_discover_all_primary_services(client->att, NULL,
discover_primary_cb,
}
#endif
-static void service_changed_reregister_cb(unsigned int id, uint16_t att_ecode,
- void *user_data)
+static void service_changed_reregister_cb(uint16_t att_ecode, void *user_data)
{
struct bt_gatt_client *client = user_data;
- if (!id || att_ecode) {
+ if (!att_ecode) {
util_debug(client->debug_callback, client->debug_data,
- "Failed to register handler for \"Service Changed\"");
+ "Re-registered handler for \"Service Changed\" after "
+ "change in GATT service");
+ client->svc_chngd_registered = true;
return;
}
- client->svc_chngd_ind_id = id;
-
util_debug(client->debug_callback, client->debug_data,
- "Re-registered handler for \"Service Changed\" after change in "
- "GATT service");
+ "Failed to register handler for \"Service Changed\"");
+ client->svc_chngd_ind_id = 0;
}
static void process_service_changed(struct bt_gatt_client *client,
static void service_changed_cb(uint16_t value_handle, const uint8_t *value,
uint16_t length, void *user_data);
+static void get_first_attribute(struct gatt_db_attribute *attrib,
+ void *user_data)
+{
+ struct gatt_db_attribute **stored = user_data;
+
+ if (*stored)
+ return;
+
+ *stored = attrib;
+}
+
static void service_changed_complete(struct discovery_op *op, bool success,
uint8_t att_ecode)
{
struct service_changed_op *next_sc_op;
uint16_t start_handle = op->start;
uint16_t end_handle = op->end;
- struct gatt_db_attribute *attr;
+ struct gatt_db_attribute *attr = NULL;
bt_uuid_t uuid;
- struct queue *q;
client->in_svc_chngd = false;
return;
}
- /* Check if the GATT service was among the changed services */
- q = queue_new();
- if (!q)
- return;
-
bt_uuid16_create(&uuid, SVC_CHNGD_UUID);
- gatt_db_find_by_type(client->db, start_handle, end_handle, &uuid, q);
- if (queue_isempty(q)) {
- queue_destroy(q, NULL);
+ gatt_db_find_by_type(client->db, start_handle, end_handle, &uuid,
+ get_first_attribute, &attr);
+ if (!attr)
return;
- }
-
- attr = queue_pop_head(q);
- queue_destroy(q, NULL);
/* The GATT service was modified. Re-register the handler for
* indications from the "Service Changed" characteristic.
*/
- if (bt_gatt_client_register_notify(client,
+ client->svc_chngd_registered = false;
+ client->svc_chngd_ind_id = bt_gatt_client_register_notify(client,
gatt_db_attribute_get_handle(attr),
service_changed_reregister_cb,
service_changed_cb,
- client, NULL))
+ client, NULL);
+ if (client->svc_chngd_ind_id)
return;
util_debug(client->debug_callback, client->debug_data,
static void service_changed_failure(struct discovery_op *op)
{
- gatt_db_clear_range(op->client->db, op->start, op->end);
+ struct bt_gatt_client *client = op->client;
+
+ gatt_db_clear_range(client->db, op->start, op->end);
}
static void process_service_changed(struct bt_gatt_client *client,
if (!op)
goto fail;
- if (bt_gatt_discover_primary_services(client->att, NULL,
- start_handle, end_handle,
+ client->discovery_req = bt_gatt_discover_primary_services(client->att,
+ NULL, start_handle, end_handle,
discover_primary_cb,
discovery_op_ref(op),
- discovery_op_unref)) {
+ discovery_op_unref);
+ if (client->discovery_req) {
client->in_svc_chngd = true;
return;
}
+ discovery_op_free(op);
+
fail:
util_debug(client->debug_callback, client->debug_data,
"Failed to initiate service discovery"
" after Service Changed");
- discovery_op_free(op);
}
static void service_changed_cb(uint16_t value_handle, const uint8_t *value,
queue_push_tail(client->svc_chngd_queue, op);
}
-static void service_changed_register_cb(unsigned int id, uint16_t att_ecode,
- void *user_data)
+static void service_changed_register_cb(uint16_t att_ecode, void *user_data)
{
bool success;
struct bt_gatt_client *client = user_data;
- if (!id || att_ecode) {
+ if (att_ecode) {
util_debug(client->debug_callback, client->debug_data,
"Failed to register handler for \"Service Changed\"");
success = false;
+ client->svc_chngd_ind_id = 0;
goto done;
}
- client->svc_chngd_ind_id = id;
+ client->svc_chngd_registered = true;
client->ready = true;
success = true;
util_debug(client->debug_callback, client->debug_data,
- "Registered handler for \"Service Changed\": %u", id);
+ "Registered handler for \"Service Changed\": %u",
+ client->svc_chngd_ind_id);
done:
notify_client_ready(client, success, att_ecode);
uint8_t att_ecode)
{
struct bt_gatt_client *client = op->client;
- bool registered;
- struct gatt_db_attribute *attr;
+ struct gatt_db_attribute *attr = NULL;
bt_uuid_t uuid;
- struct queue *q;
client->in_init = false;
if (!success)
goto fail;
- q = queue_new();
- if (!q)
- goto fail;
-
bt_uuid16_create(&uuid, SVC_CHNGD_UUID);
- gatt_db_find_by_type(client->db, 0x0001, 0xffff, &uuid, q);
- if (queue_isempty(q)) {
- queue_destroy(q, NULL);
+ gatt_db_find_by_type(client->db, 0x0001, 0xffff, &uuid,
+ get_first_attribute, &attr);
+ if (!attr) {
client->ready = true;
goto done;
}
- attr = queue_pop_head(q);
- queue_destroy(q, NULL);
-
/* Register an indication handler for the "Service Changed"
* characteristic and report ready only if the handler is registered
* successfully. Temporarily set "ready" to true so that we can register
* the handler using the existing framework.
*/
client->ready = true;
- registered = bt_gatt_client_register_notify(client,
+ client->svc_chngd_ind_id = bt_gatt_client_register_notify(client,
gatt_db_attribute_get_handle(attr),
service_changed_register_cb,
service_changed_cb,
client, NULL);
- client->ready = false;
- if (registered)
+ if (!client->svc_chngd_registered)
+ client->ready = false;
+
+ if (client->svc_chngd_ind_id)
return;
util_debug(client->debug_callback, client->debug_data,
"Failed to register handler for \"Service Changed\"");
+ success = false;
fail:
util_debug(client->debug_callback, client->debug_data,
static void init_fail(struct discovery_op *op)
{
- gatt_db_clear(op->client->db);
+ struct bt_gatt_client *client = op->client;
+
+ gatt_db_clear(client->db);
}
static bool gatt_client_init(struct bt_gatt_client *client, uint16_t mtu)
return false;
/* Configure the MTU */
- if (!bt_gatt_exchange_mtu(client->att, MAX(BT_ATT_DEFAULT_LE_MTU, mtu),
- exchange_mtu_cb,
- discovery_op_ref(op),
- discovery_op_unref)) {
+ client->mtu_req_id = bt_gatt_exchange_mtu(client->att,
+ MAX(BT_ATT_DEFAULT_LE_MTU, mtu),
+ exchange_mtu_cb,
+ discovery_op_ref(op),
+ discovery_op_unref);
+ if (!client->mtu_req_id) {
discovery_op_free(op);
return false;
}
/* Increment the per-characteristic ref count of notify handlers */
__sync_fetch_and_add(¬ify_data->chrc->notify_count, 1);
- /* Add the handler to the bt_gatt_client's general list */
- queue_push_tail(notify_data->client->notify_list,
- notify_data_ref(notify_data));
-
- /* Assign an ID to the handler and notify the caller that it was
- * successfully registered.
- */
- if (notify_data->client->next_reg_id < 1)
- notify_data->client->next_reg_id = 1;
-
- notify_data->id = notify_data->client->next_reg_id++;
- notify_data->callback(notify_data->id, 0, notify_data->user_data);
+ notify_data->att_id = 0;
+ notify_data->callback(0, notify_data->user_data);
}
static bool notify_data_write_ccc(struct notify_data *notify_data, bool enable,
bt_att_response_func_t callback)
{
uint8_t pdu[4];
+ unsigned int att_id;
assert(notify_data->chrc->ccc_handle);
memset(pdu, 0, sizeof(pdu));
return false;
}
- notify_data->chrc->ccc_write_id = bt_att_send(notify_data->client->att,
- BT_ATT_OP_WRITE_REQ,
- pdu, sizeof(pdu),
- callback,
- notify_data, notify_data_unref);
+ att_id = bt_att_send(notify_data->client->att, BT_ATT_OP_WRITE_REQ,
+ pdu, sizeof(pdu), callback,
+ notify_data_ref(notify_data),
+ notify_data_unref);
+ notify_data->chrc->ccc_write_id = notify_data->att_id = att_id;
- return !!notify_data->chrc->ccc_write_id;
+ return !!att_id;
}
static uint8_t process_error(const void *pdu, uint16_t length)
* the next one in the queue. If there was an error sending the
* write request, then just move on to the next queued entry.
*/
- notify_data->callback(0, att_ecode, notify_data->user_data);
+ queue_remove(notify_data->client->notify_list, notify_data);
+ notify_data->callback(att_ecode, notify_data->user_data);
while ((notify_data = queue_pop_head(
notify_data->chrc->reg_notify_queue))) {
{
struct notify_data *notify_data = data;
- if (__sync_sub_and_fetch(¬ify_data->chrc->notify_count, 1)) {
- notify_data_unref(notify_data);
- return;
+ /*
+ * If a procedure to enable the CCC is still pending, then cancel it and
+ * return.
+ */
+ if (notify_data->att_id) {
+ bt_att_cancel(notify_data->client->att, notify_data->att_id);
+ goto done;
}
- notify_data_write_ccc(notify_data, false, disable_ccc_callback);
+ if (__sync_sub_and_fetch(¬ify_data->chrc->notify_count, 1) ||
+ !notify_data->chrc->ccc_handle)
+ goto done;
+
+ if (notify_data_write_ccc(notify_data, false, disable_ccc_callback))
+ return;
+
+done:
+ notify_data_unref(notify_data);
}
static void notify_handler(void *data, void *user_data)
if (pdu_data->length > 2)
value = pdu_data->pdu + 2;
+ /*
+ * Even if the notify data has a pending ATT request to write to the
+ * CCC, there is really no reason not to notify the handlers.
+ */
if (notify_data->notify)
notify_data->notify(value_handle, value, pdu_data->length - 2,
notify_data->user_data);
bt_gatt_client_unref(client);
}
-static void long_write_op_unref(void *data);
+static void notify_data_cleanup(void *data)
+{
+ struct notify_data *notify_data = data;
+
+ if (notify_data->att_id)
+ bt_att_cancel(notify_data->client->att, notify_data->att_id);
+
+ notify_data_unref(notify_data);
+}
static void bt_gatt_client_free(struct bt_gatt_client *client)
{
+ bt_gatt_client_cancel_all(client);
+
+ queue_destroy(client->notify_list, notify_data_cleanup);
+
if (client->ready_destroy)
client->ready_destroy(client->ready_data);
gatt_db_unref(client->db);
queue_destroy(client->svc_chngd_queue, free);
- queue_destroy(client->long_write_queue, long_write_op_unref);
- queue_destroy(client->notify_list, notify_data_unref);
+ queue_destroy(client->long_write_queue, request_unref);
queue_destroy(client->notify_chrcs, notify_chrc_free);
+ queue_destroy(client->pending_requests, request_unref);
free(client);
}
if (!client->notify_chrcs)
goto fail;
+ client->pending_requests = queue_new();
+ if (!client->pending_requests)
+ goto fail;
+
client->notify_id = bt_att_register(att, BT_ATT_OP_HANDLE_VAL_NOT,
notify_cb, client, NULL);
if (!client->notify_id)
return bt_att_get_mtu(client->att);
}
+static bool match_req_id(const void *a, const void *b)
+{
+ const struct request *req = a;
+ unsigned int id = PTR_TO_UINT(b);
+
+ return req->id == id;
+}
+
+static void cancel_long_write_cb(uint8_t opcode, const void *pdu, uint16_t len,
+ void *user_data)
+{
+ /* Do nothing */
+}
+
+bool bt_gatt_client_cancel(struct bt_gatt_client *client, unsigned int id)
+{
+ struct request *req;
+ uint8_t pdu = 0x00;
+
+ if (!client || !id || !client->att)
+ return false;
+
+ req = queue_remove_if(client->pending_requests, match_req_id,
+ UINT_TO_PTR(id));
+ if (!req)
+ return false;
+
+ req->removed = true;
+
+ if (!bt_att_cancel(client->att, req->att_id) && !req->long_write)
+ return false;
+
+ /* If this was a long-write, we need to abort all prepared writes */
+ if (!req->long_write)
+ return true;
+
+ if (!req->att_id)
+ queue_remove(client->long_write_queue, req);
+ else
+ bt_att_send(client->att, BT_ATT_OP_EXEC_WRITE_REQ,
+ &pdu, sizeof(pdu),
+ cancel_long_write_cb,
+ NULL, NULL);
+
+ if (queue_isempty(client->long_write_queue))
+ client->in_long_write = false;
+
+ return true;
+}
+
+static void cancel_request(void *data)
+{
+ struct request *req = data;
+ uint8_t pdu = 0x00;
+
+ req->removed = true;
+
+ if (!req->long_write) {
+ bt_att_cancel(req->client->att, req->att_id);
+ return;
+ }
+
+ if (!req->att_id)
+ queue_remove(req->client->long_write_queue, req);
+
+ if (queue_isempty(req->client->long_write_queue))
+ req->client->in_long_write = false;
+
+ bt_att_send(req->client->att, BT_ATT_OP_EXEC_WRITE_REQ,
+ &pdu, sizeof(pdu),
+ cancel_long_write_cb,
+ NULL, NULL);
+
+ bt_att_cancel(req->client->att, req->att_id);
+}
+
+bool bt_gatt_client_cancel_all(struct bt_gatt_client *client)
+{
+ if (!client || !client->att)
+ return false;
+
+ queue_remove_all(client->pending_requests, NULL, NULL, cancel_request);
+
+ if (client->discovery_req) {
+ bt_gatt_request_cancel(client->discovery_req);
+ bt_gatt_request_unref(client->discovery_req);
+ client->discovery_req = NULL;
+ }
+
+ if (client->mtu_req_id)
+ bt_att_cancel(client->att, client->mtu_req_id);
+
+ return true;
+}
+
struct read_op {
bt_gatt_client_read_callback_t callback;
void *user_data;
static void read_cb(uint8_t opcode, const void *pdu, uint16_t length,
void *user_data)
{
- struct read_op *op = user_data;
+ struct request *req = user_data;
+ struct read_op *op = req->data;
bool success;
uint8_t att_ecode = 0;
const uint8_t *value = NULL;
op->callback(success, att_ecode, value, length, op->user_data);
}
-bool bt_gatt_client_read_value(struct bt_gatt_client *client,
+unsigned int bt_gatt_client_read_value(struct bt_gatt_client *client,
uint16_t value_handle,
bt_gatt_client_read_callback_t callback,
void *user_data,
bt_gatt_client_destroy_func_t destroy)
{
+ struct request *req;
struct read_op *op;
uint8_t pdu[2];
if (!client)
- return false;
+ return 0;
op = new0(struct read_op, 1);
if (!op)
- return false;
+ return 0;
+
+ req = request_create(client);
+ if (!req) {
+ free(op);
+ return 0;
+ }
op->callback = callback;
op->user_data = user_data;
op->destroy = destroy;
+ req->data = op;
+ req->destroy = destroy_read_op;
+
put_le16(value_handle, pdu);
- if (!bt_att_send(client->att, BT_ATT_OP_READ_REQ, pdu, sizeof(pdu),
- read_cb, op,
- destroy_read_op)) {
- free(op);
- return false;
+ req->att_id = bt_att_send(client->att, BT_ATT_OP_READ_REQ,
+ pdu, sizeof(pdu),
+ read_cb, req,
+ request_unref);
+ if (!req->att_id) {
+ op->destroy = NULL;
+ request_unref(req);
+ return 0;
}
- return true;
+ return req->id;
}
static void read_multiple_cb(uint8_t opcode, const void *pdu, uint16_t length,
void *user_data)
{
- struct read_op *op = user_data;
+ struct request *req = user_data;
+ struct read_op *op = req->data;
uint8_t att_ecode;
bool success;
op->callback(success, att_ecode, pdu, length, op->user_data);
}
-bool bt_gatt_client_read_multiple(struct bt_gatt_client *client,
+unsigned int bt_gatt_client_read_multiple(struct bt_gatt_client *client,
uint16_t *handles, uint8_t num_handles,
bt_gatt_client_read_callback_t callback,
void *user_data,
bt_gatt_client_destroy_func_t destroy)
{
uint8_t pdu[num_handles * 2];
+ struct request *req;
struct read_op *op;
int i;
if (!client)
- return false;
+ return 0;
if (num_handles < 2)
- return false;
+ return 0;
if (num_handles * 2 > bt_att_get_mtu(client->att) - 1)
- return false;
+ return 0;
op = new0(struct read_op, 1);
if (!op)
- return false;
+ return 0;
+
+ req = request_create(client);
+ if (!req) {
+ free(op);
+ return 0;
+ }
op->callback = callback;
op->user_data = user_data;
op->destroy = destroy;
+ req->data = op;
+ req->destroy = destroy_read_op;
+
for (i = 0; i < num_handles; i++)
put_le16(handles[i], pdu + (2 * i));
- if (!bt_att_send(client->att, BT_ATT_OP_READ_MULT_REQ, pdu, sizeof(pdu),
- read_multiple_cb, op, destroy_read_op)) {
- free(op);
- return false;
+ req->att_id = bt_att_send(client->att, BT_ATT_OP_READ_MULT_REQ,
+ pdu, sizeof(pdu),
+ read_multiple_cb, req,
+ request_unref);
+ if (!req->att_id) {
+ op->destroy = NULL;
+ request_unref(req);
+ return 0;
}
- return true;
+ return req->id;
}
struct read_long_op {
struct bt_gatt_client *client;
int ref_count;
uint16_t value_handle;
- size_t orig_offset;
- size_t offset;
- struct queue *blobs;
+ uint16_t offset;
+ struct iovec iov;
bt_gatt_client_read_callback_t callback;
void *user_data;
bt_gatt_client_destroy_func_t destroy;
};
-struct blob {
- uint8_t *data;
- uint16_t offset;
- uint16_t length;
-};
-
-static struct blob *create_blob(const uint8_t *data, uint16_t len,
- uint16_t offset)
-{
- struct blob *blob;
-
- blob = new0(struct blob, 1);
- if (!blob)
- return NULL;
-
- blob->data = malloc(len);
- if (!blob->data) {
- free(blob);
- return NULL;
- }
-
- memcpy(blob->data, data, len);
- blob->length = len;
- blob->offset = offset;
-
- return blob;
-}
-
-static void destroy_blob(void *data)
-{
- struct blob *blob = data;
-
- free(blob->data);
- free(blob);
-}
-
-static struct read_long_op *read_long_op_ref(struct read_long_op *op)
-{
- __sync_fetch_and_add(&op->ref_count, 1);
-
- return op;
-}
-
-static void read_long_op_unref(void *data)
+static void destroy_read_long_op(void *data)
{
struct read_long_op *op = data;
- if (__sync_sub_and_fetch(&op->ref_count, 1))
- return;
-
if (op->destroy)
op->destroy(op->user_data);
- queue_destroy(op->blobs, destroy_blob);
-
+ free(op->iov.iov_base);
free(op);
}
-static void append_blob(void *data, void *user_data)
-{
- struct blob *blob = data;
- uint8_t *value = user_data;
-
- memcpy(value + blob->offset, blob->data, blob->length);
-}
-
-static void complete_read_long_op(struct read_long_op *op, bool success,
- uint8_t att_ecode)
+static bool append_chunk(struct read_long_op *op, const uint8_t *data,
+ uint16_t len)
{
- uint8_t *value = NULL;
- uint16_t length = 0;
+ void *buf;
- if (!success)
- goto done;
-
- length = op->offset - op->orig_offset;
+ /* Truncate if the data would exceed maximum length */
+ if (op->offset + len > BT_ATT_MAX_VALUE_LEN)
+ len = BT_ATT_MAX_VALUE_LEN - op->offset;
- if (!length)
- goto done;
+ buf = realloc(op->iov.iov_base, op->iov.iov_len + len);
+ if (!buf)
+ return false;
- value = malloc(length);
- if (!value) {
- success = false;
- goto done;
- }
+ op->iov.iov_base = buf;
- queue_foreach(op->blobs, append_blob, value - op->orig_offset);
+ memcpy(op->iov.iov_base + op->iov.iov_len, data, len);
-done:
- if (op->callback)
- op->callback(success, att_ecode, value, length, op->user_data);
+ op->iov.iov_len += len;
+ op->offset += len;
- free(value);
+ return true;
}
static void read_long_cb(uint8_t opcode, const void *pdu,
uint16_t length, void *user_data)
{
- struct read_long_op *op = user_data;
- struct blob *blob;
+ struct request *req = user_data;
+ struct read_long_op *op = req->data;
bool success;
uint8_t att_ecode = 0;
if (!length)
goto success;
- blob = create_blob(pdu, length, op->offset);
- if (!blob) {
+ if (!append_chunk(op, pdu, length)) {
success = false;
goto done;
}
- queue_push_tail(op->blobs, blob);
- op->offset += length;
- if (op->offset > UINT16_MAX)
+ if (op->offset >= BT_ATT_MAX_VALUE_LEN)
goto success;
if (length >= bt_att_get_mtu(op->client->att) - 1) {
put_le16(op->value_handle, pdu);
put_le16(op->offset, pdu + 2);
- if (bt_att_send(op->client->att, BT_ATT_OP_READ_BLOB_REQ,
+ req->att_id = bt_att_send(op->client->att,
+ BT_ATT_OP_READ_BLOB_REQ,
pdu, sizeof(pdu),
read_long_cb,
- read_long_op_ref(op),
- read_long_op_unref))
+ request_ref(req),
+ request_unref);
+ if (req->att_id)
return;
- read_long_op_unref(op);
+ request_unref(req);
success = false;
goto done;
}
success = true;
done:
- complete_read_long_op(op, success, att_ecode);
+ if (op->callback)
+ op->callback(success, att_ecode, op->iov.iov_base,
+ op->iov.iov_len, op->user_data);
}
-bool bt_gatt_client_read_long_value(struct bt_gatt_client *client,
+unsigned int bt_gatt_client_read_long_value(struct bt_gatt_client *client,
uint16_t value_handle, uint16_t offset,
bt_gatt_client_read_callback_t callback,
void *user_data,
bt_gatt_client_destroy_func_t destroy)
{
+ struct request *req;
struct read_long_op *op;
uint8_t pdu[4];
if (!client)
- return false;
+ return 0;
op = new0(struct read_long_op, 1);
if (!op)
- return false;
+ return 0;
- op->blobs = queue_new();
- if (!op->blobs) {
+ req = request_create(client);
+ if (!req) {
free(op);
- return false;
+ return 0;
}
op->client = client;
op->value_handle = value_handle;
- op->orig_offset = offset;
op->offset = offset;
op->callback = callback;
op->user_data = user_data;
op->destroy = destroy;
+ req->data = op;
+ req->destroy = destroy_read_long_op;
+
put_le16(value_handle, pdu);
put_le16(offset, pdu + 2);
- if (!bt_att_send(client->att, BT_ATT_OP_READ_BLOB_REQ, pdu, sizeof(pdu),
- read_long_cb,
- read_long_op_ref(op),
- read_long_op_unref)) {
- queue_destroy(op->blobs, free);
- free(op);
- return false;
+ req->att_id = bt_att_send(client->att, BT_ATT_OP_READ_BLOB_REQ,
+ pdu, sizeof(pdu),
+ read_long_cb, req,
+ request_unref);
+ if (!req->att_id) {
+ op->destroy = NULL;
+ request_unref(req);
+ return 0;
}
- return true;
+ return req->id;
}
-bool bt_gatt_client_write_without_response(struct bt_gatt_client *client,
+unsigned int bt_gatt_client_write_without_response(
+ struct bt_gatt_client *client,
uint16_t value_handle,
bool signed_write,
const uint8_t *value, uint16_t length) {
uint8_t pdu[2 + length];
+ struct request *req;
+ int security;
+ uint8_t op;
if (!client)
return 0;
- /* TODO: Support this once bt_att_send supports signed writes. */
- if (signed_write)
+ req = request_create(client);
+ if (!req)
return 0;
+ /* Only use signed write if unencrypted */
+ if (signed_write) {
+ security = bt_att_get_sec_level(client->att);
+ op = security > BT_SECURITY_LOW ? BT_ATT_OP_WRITE_CMD :
+ BT_ATT_OP_SIGNED_WRITE_CMD;
+ } else
+ op = BT_ATT_OP_WRITE_CMD;
+
put_le16(value_handle, pdu);
memcpy(pdu + 2, value, length);
- return bt_att_send(client->att, BT_ATT_OP_WRITE_CMD, pdu, sizeof(pdu),
- NULL, NULL, NULL);
+ req->att_id = bt_att_send(client->att, op, pdu, sizeof(pdu), NULL, req,
+ request_unref);
+ if (!req->att_id) {
+ request_unref(req);
+ return 0;
+ }
+
+ return req->id;
}
struct write_op {
- bt_gatt_result_callback_t callback;
+ bt_gatt_client_callback_t callback;
void *user_data;
bt_gatt_destroy_func_t destroy;
};
static void write_cb(uint8_t opcode, const void *pdu, uint16_t length,
void *user_data)
{
- struct write_op *op = user_data;
+ struct request *req = user_data;
+ struct write_op *op = req->data;
bool success = true;
uint8_t att_ecode = 0;
op->callback(success, att_ecode, op->user_data);
}
-bool bt_gatt_client_write_value(struct bt_gatt_client *client,
+unsigned int bt_gatt_client_write_value(struct bt_gatt_client *client,
uint16_t value_handle,
const uint8_t *value, uint16_t length,
bt_gatt_client_callback_t callback,
void *user_data,
bt_gatt_client_destroy_func_t destroy)
{
+ struct request *req;
struct write_op *op;
uint8_t pdu[2 + length];
if (!client)
- return false;
+ return 0;
op = new0(struct write_op, 1);
if (!op)
- return false;
+ return 0;
+
+ req = request_create(client);
+ if (!req) {
+ free(op);
+ return 0;
+ }
op->callback = callback;
op->user_data = user_data;
op->destroy = destroy;
+ req->data = op;
+ req->destroy = destroy_write_op;
+
put_le16(value_handle, pdu);
memcpy(pdu + 2, value, length);
- if (!bt_att_send(client->att, BT_ATT_OP_WRITE_REQ, pdu, sizeof(pdu),
- write_cb, op,
- destroy_write_op)) {
- free(op);
- return false;
+ req->att_id = bt_att_send(client->att, BT_ATT_OP_WRITE_REQ,
+ pdu, sizeof(pdu),
+ write_cb, req,
+ request_unref);
+ if (!req->att_id) {
+ op->destroy = NULL;
+ request_unref(req);
+ return 0;
}
- return true;
+ return req->id;
}
struct long_write_op {
struct bt_gatt_client *client;
- int ref_count;
bool reliable;
bool success;
uint8_t att_ecode;
bt_gatt_client_destroy_func_t destroy;
};
-static struct long_write_op *long_write_op_ref(struct long_write_op *op)
-{
- __sync_fetch_and_add(&op->ref_count, 1);
-
- return op;
-}
-
-static void long_write_op_unref(void *data)
+static void long_write_op_free(void *data)
{
struct long_write_op *op = data;
- if (__sync_sub_and_fetch(&op->ref_count, 1))
- return;
-
if (op->destroy)
op->destroy(op->user_data);
static void prepare_write_cb(uint8_t opcode, const void *pdu, uint16_t length,
void *user_data);
-static void complete_write_long_op(struct long_write_op *op, bool success,
+static void complete_write_long_op(struct request *req, bool success,
uint8_t att_ecode, bool reliable_error);
-static void handle_next_prep_write(struct long_write_op *op)
+static void handle_next_prep_write(struct request *req)
{
+ struct long_write_op *op = req->data;
bool success = true;
uint8_t *pdu;
put_le16(op->offset + op->index, pdu + 2);
memcpy(pdu + 4, op->value + op->index, op->cur_length);
- if (!bt_att_send(op->client->att, BT_ATT_OP_PREP_WRITE_REQ,
+ req->att_id = bt_att_send(op->client->att, BT_ATT_OP_PREP_WRITE_REQ,
pdu, op->cur_length + 4,
prepare_write_cb,
- long_write_op_ref(op),
- long_write_op_unref)) {
- long_write_op_unref(op);
+ request_ref(req),
+ request_unref);
+ if (!req->att_id) {
+ request_unref(req);
success = false;
}
return;
done:
- complete_write_long_op(op, success, 0, false);
+ complete_write_long_op(req, success, 0, false);
}
static void start_next_long_write(struct bt_gatt_client *client)
{
- struct long_write_op *op;
+ struct request *req;
if (queue_isempty(client->long_write_queue)) {
client->in_long_write = false;
return;
}
- op = queue_pop_head(client->long_write_queue);
- if (!op)
+ req = queue_pop_head(client->long_write_queue);
+ if (!req)
return;
- handle_next_prep_write(op);
+ handle_next_prep_write(req);
- /* send_next_prep_write adds an extra ref. Unref here to clean up if
+ /*
+ * send_next_prep_write adds an extra ref. Unref here to clean up if
* necessary, since we also added a ref before pushing to the queue.
*/
- long_write_op_unref(op);
+ request_unref(req);
}
static void execute_write_cb(uint8_t opcode, const void *pdu, uint16_t length,
void *user_data)
{
- struct long_write_op *op = user_data;
+ struct request *req = user_data;
+ struct long_write_op *op = req->data;
bool success = op->success;
uint8_t att_ecode = op->att_ecode;
start_next_long_write(op->client);
}
-static void complete_write_long_op(struct long_write_op *op, bool success,
+static void complete_write_long_op(struct request *req, bool success,
uint8_t att_ecode, bool reliable_error)
{
+ struct long_write_op *op = req->data;
uint8_t pdu;
op->success = success;
else
pdu = 0x00; /* Cancel */
- if (bt_att_send(op->client->att, BT_ATT_OP_EXEC_WRITE_REQ,
+ req->att_id = bt_att_send(op->client->att, BT_ATT_OP_EXEC_WRITE_REQ,
&pdu, sizeof(pdu),
execute_write_cb,
- long_write_op_ref(op),
- long_write_op_unref))
+ request_ref(req),
+ request_unref);
+ if (req->att_id)
return;
- long_write_op_unref(op);
+
+ request_unref(req);
success = false;
if (op->callback)
static void prepare_write_cb(uint8_t opcode, const void *pdu, uint16_t length,
void *user_data)
{
- struct long_write_op *op = user_data;
+ struct request *req = user_data;
+ struct long_write_op *op = req->data;
bool success = true;
bool reliable_error = false;
uint8_t att_ecode = 0;
op->index = next_index;
op->cur_length = MIN(op->length - op->index,
bt_att_get_mtu(op->client->att) - 5);
- handle_next_prep_write(op);
+ handle_next_prep_write(req);
return;
}
done:
- complete_write_long_op(op, success, att_ecode, reliable_error);
+ complete_write_long_op(req, success, att_ecode, reliable_error);
}
-bool bt_gatt_client_write_long_value(struct bt_gatt_client *client,
+unsigned int bt_gatt_client_write_long_value(struct bt_gatt_client *client,
bool reliable,
uint16_t value_handle, uint16_t offset,
const uint8_t *value, uint16_t length,
void *user_data,
bt_gatt_client_destroy_func_t destroy)
{
+ struct request *req;
struct long_write_op *op;
uint8_t *pdu;
- bool status;
if (!client)
- return false;
+ return 0;
if ((size_t)(length + offset) > UINT16_MAX)
- return false;
+ return 0;
/* Don't allow writing a 0-length value using this procedure. The
* upper-layer should use bt_gatt_write_value for that instead.
*/
if (!length || !value)
- return false;
+ return 0;
op = new0(struct long_write_op, 1);
if (!op)
- return false;
+ return 0;
op->value = malloc(length);
if (!op->value) {
free(op);
- return false;
+ return 0;
+ }
+
+ req = request_create(client);
+ if (!req) {
+ free(op->value);
+ free(op);
+ return 0;
}
memcpy(op->value, value, length);
op->user_data = user_data;
op->destroy = destroy;
+ req->data = op;
+ req->destroy = long_write_op_free;
+ req->long_write = true;
+
if (client->in_long_write) {
- queue_push_tail(client->long_write_queue,
- long_write_op_ref(op));
- return true;
+ queue_push_tail(client->long_write_queue, req);
+ return req->id;
}
pdu = malloc(op->cur_length + 4);
if (!pdu) {
free(op->value);
free(op);
- return false;
+ return 0;
}
put_le16(value_handle, pdu);
put_le16(offset, pdu + 2);
memcpy(pdu + 4, op->value, op->cur_length);
- status = bt_att_send(client->att, BT_ATT_OP_PREP_WRITE_REQ,
+ req->att_id = bt_att_send(client->att, BT_ATT_OP_PREP_WRITE_REQ,
pdu, op->cur_length + 4,
- prepare_write_cb,
- long_write_op_ref(op),
- long_write_op_unref);
-
+ prepare_write_cb, req,
+ request_unref);
free(pdu);
- if (!status) {
- free(op->value);
- free(op);
- return false;
+ if (!req->att_id) {
+ op->destroy = NULL;
+ request_unref(req);
+ return 0;
}
client->in_long_write = true;
- return true;
+ return req->id;
}
static bool match_notify_chrc_value_handle(const void *a, const void *b)
return chrc->value_handle == value_handle;
}
-bool bt_gatt_client_register_notify(struct bt_gatt_client *client,
+unsigned int bt_gatt_client_register_notify(struct bt_gatt_client *client,
uint16_t chrc_value_handle,
- bt_gatt_client_notify_id_callback_t callback,
+ bt_gatt_client_register_callback_t callback,
bt_gatt_client_notify_callback_t notify,
void *user_data,
bt_gatt_client_destroy_func_t destroy)
struct notify_chrc *chrc = NULL;
if (!client || !client->db || !chrc_value_handle || !callback)
- return false;
+ return 0;
if (!bt_gatt_client_is_ready(client) || client->in_svc_chngd)
- return false;
+ return 0;
/* Check if a characteristic ref count has been started already */
chrc = queue_find(client->notify_chrcs, match_notify_chrc_value_handle,
*/
chrc = notify_chrc_create(client, chrc_value_handle);
if (!chrc)
- return false;
-
+ return 0;
}
/* Fail if we've hit the maximum allowed notify sessions */
if (chrc->notify_count == INT_MAX)
- return false;
+ return 0;
notify_data = new0(struct notify_data, 1);
if (!notify_data)
- return false;
+ return 0;
notify_data->client = client;
notify_data->ref_count = 1;
notify_data->user_data = user_data;
notify_data->destroy = destroy;
+ /* Add the handler to the bt_gatt_client's general list */
+ queue_push_tail(client->notify_list, notify_data);
+
+ /* Assign an ID to the handler. */
+ if (client->next_reg_id < 1)
+ client->next_reg_id = 1;
+
+ notify_data->id = client->next_reg_id++;
+
/*
* If a write to the CCC descriptor is in progress, then queue this
* request.
*/
if (chrc->ccc_write_id) {
queue_push_tail(chrc->reg_notify_queue, notify_data);
- return true;
+ return notify_data->id;
}
/*
* If the ref count is not zero, then notifications are already enabled.
*/
- if (chrc->notify_count > 0) {
+ if (chrc->notify_count > 0 || !chrc->ccc_handle) {
complete_notify_request(notify_data);
- return true;
+ return notify_data->id;
}
/* Write to the CCC descriptor */
if (!notify_data_write_ccc(notify_data, true, enable_ccc_callback)) {
+ queue_remove(client->notify_list, notify_data);
free(notify_data);
- return false;
+ return 0;
}
- return true;
+ return notify_data->id;
}
bool bt_gatt_client_unregister_notify(struct bt_gatt_client *client,
complete_unregister_notify(notify_data);
return true;
}
+
+bool bt_gatt_client_set_sec_level(struct bt_gatt_client *client,
+ int level)
+{
+ if (!client)
+ return false;
+
+ return bt_att_set_sec_level(client->att, level);
+}
+
+int bt_gatt_client_get_sec_level(struct bt_gatt_client *client)
+{
+ if (!client)
+ return -1;
+
+ return bt_att_get_sec_level(client->att);
+}
typedef void (*bt_gatt_client_notify_callback_t)(uint16_t value_handle,
const uint8_t *value, uint16_t length,
void *user_data);
-typedef void (*bt_gatt_client_notify_id_callback_t)(unsigned int id,
- uint16_t att_ecode,
+typedef void (*bt_gatt_client_register_callback_t)(uint16_t att_ecode,
void *user_data);
typedef void (*bt_gatt_client_service_changed_callback_t)(uint16_t start_handle,
uint16_t end_handle,
uint16_t bt_gatt_client_get_mtu(struct bt_gatt_client *client);
-bool bt_gatt_client_read_value(struct bt_gatt_client *client,
+bool bt_gatt_client_cancel(struct bt_gatt_client *client, unsigned int id);
+bool bt_gatt_client_cancel_all(struct bt_gatt_client *client);
+
+unsigned int bt_gatt_client_read_value(struct bt_gatt_client *client,
uint16_t value_handle,
bt_gatt_client_read_callback_t callback,
void *user_data,
bt_gatt_client_destroy_func_t destroy);
-bool bt_gatt_client_read_long_value(struct bt_gatt_client *client,
+unsigned int bt_gatt_client_read_long_value(struct bt_gatt_client *client,
uint16_t value_handle, uint16_t offset,
bt_gatt_client_read_callback_t callback,
void *user_data,
bt_gatt_client_destroy_func_t destroy);
-bool bt_gatt_client_read_multiple(struct bt_gatt_client *client,
+unsigned int bt_gatt_client_read_multiple(struct bt_gatt_client *client,
uint16_t *handles, uint8_t num_handles,
bt_gatt_client_read_callback_t callback,
void *user_data,
bt_gatt_client_destroy_func_t destroy);
-bool bt_gatt_client_write_without_response(struct bt_gatt_client *client,
+unsigned int bt_gatt_client_write_without_response(
+ struct bt_gatt_client *client,
uint16_t value_handle,
bool signed_write,
const uint8_t *value, uint16_t length);
-bool bt_gatt_client_write_value(struct bt_gatt_client *client,
+unsigned int bt_gatt_client_write_value(struct bt_gatt_client *client,
uint16_t value_handle,
const uint8_t *value, uint16_t length,
bt_gatt_client_callback_t callback,
void *user_data,
bt_gatt_client_destroy_func_t destroy);
-bool bt_gatt_client_write_long_value(struct bt_gatt_client *client,
+unsigned int bt_gatt_client_write_long_value(struct bt_gatt_client *client,
bool reliable,
uint16_t value_handle, uint16_t offset,
const uint8_t *value, uint16_t length,
void *user_data,
bt_gatt_client_destroy_func_t destroy);
-bool bt_gatt_client_register_notify(struct bt_gatt_client *client,
+unsigned int bt_gatt_client_register_notify(struct bt_gatt_client *client,
uint16_t chrc_value_handle,
- bt_gatt_client_notify_id_callback_t callback,
+ bt_gatt_client_register_callback_t callback,
bt_gatt_client_notify_callback_t notify,
void *user_data,
bt_gatt_client_destroy_func_t destroy);
bool bt_gatt_client_unregister_notify(struct bt_gatt_client *client,
unsigned int id);
-#ifdef BLUEZ5_GATT_CLIENT
+
+bool bt_gatt_client_set_sec_level(struct bt_gatt_client *client, int level);
+int bt_gatt_client_get_sec_level(struct bt_gatt_client *client);
+
+#ifdef BLUEZ5_27_GATT_CLIENT
bool bt_gatt_discover_services(struct bt_gatt_client *client);
#endif
#include <stdbool.h>
#include <errno.h>
+#include "lib/bluetooth.h"
#include "lib/uuid.h"
#include "src/shared/util.h"
#include "src/shared/queue.h"
#include "src/shared/timeout.h"
+#include "src/shared/att.h"
#include "src/shared/gatt-db.h"
#ifndef MAX
#define MAX_CHAR_DECL_VALUE_LEN 19
#define MAX_INCLUDED_VALUE_LEN 6
-#define ATTRIBUTE_TIMEOUT 1000
+#define ATTRIBUTE_TIMEOUT 5000
static const bt_uuid_t primary_service_uuid = { .type = BT_UUID16,
.value.u16 = GATT_PRIM_SVC_UUID };
struct gatt_db_service {
struct gatt_db *db;
bool active;
+ bool claimed;
uint16_t num_handles;
struct gatt_db_attribute **attributes;
};
+static void pending_read_result(struct pending_read *p, int err,
+ const uint8_t *data, size_t length)
+{
+ if (p->timeout_id > 0)
+ timeout_remove(p->timeout_id);
+
+ p->func(p->attrib, err, data, length, p->user_data);
+
+ free(p);
+}
+
+static void pending_read_free(void *data)
+{
+ struct pending_read *p = data;
+
+ pending_read_result(p, -ECANCELED, NULL, 0);
+}
+
+static void pending_write_result(struct pending_write *p, int err)
+{
+ if (p->timeout_id > 0)
+ timeout_remove(p->timeout_id);
+
+ p->func(p->attrib, err, p->user_data);
+
+ free(p);
+}
+
+static void pending_write_free(void *data)
+{
+ struct pending_write *p = data;
+
+ pending_write_result(p, -ECANCELED);
+}
+
static void attribute_destroy(struct gatt_db_attribute *attribute)
{
/* Attribute was not initialized by user */
if (!attribute)
return;
- queue_destroy(attribute->pending_reads, free);
- queue_destroy(attribute->pending_writes, free);
+ queue_destroy(attribute->pending_reads, pending_read_free);
+ queue_destroy(attribute->pending_writes, pending_write_free);
free(attribute->value);
free(attribute);
}
static struct gatt_db_attribute *new_attribute(struct gatt_db_service *service,
+ uint16_t handle,
const bt_uuid_t *type,
const uint8_t *val,
uint16_t len)
return NULL;
attribute->service = service;
+ attribute->handle = handle;
attribute->uuid = *type;
attribute->value_len = len;
if (len) {
}
static struct gatt_db_service *gatt_db_service_create(const bt_uuid_t *uuid,
+ uint16_t handle,
bool primary,
uint16_t num_handles)
{
len = uuid_to_le(uuid, value);
- service->attributes[0] = new_attribute(service, type, value, len);
+ service->attributes[0] = new_attribute(service, handle, type, value,
+ len);
if (!service->attributes[0]) {
gatt_db_service_destroy(service);
return NULL;
if (!find_insert_loc(db, handle, handle + num_handles - 1, &after))
return NULL;
- service = gatt_db_service_create(uuid, primary, num_handles);
+ service = gatt_db_service_create(uuid, handle, primary, num_handles);
if (!service)
return NULL;
attribute->user_data = user_data;
}
-struct gatt_db_attribute *
-gatt_db_service_add_characteristic(struct gatt_db_attribute *attrib,
+static struct gatt_db_attribute *
+service_insert_characteristic(struct gatt_db_service *service,
+ uint16_t handle,
const bt_uuid_t *uuid,
uint32_t permissions,
uint8_t properties,
gatt_db_write_t write_func,
void *user_data)
{
- struct gatt_db_service *service;
uint8_t value[MAX_CHAR_DECL_VALUE_LEN];
uint16_t len = 0;
int i;
- if (!attrib)
+ /* Check if handle is in within service range */
+ if (handle && handle <= service->attributes[0]->handle)
return NULL;
- service = attrib->service;
-
i = get_attribute_index(service, 1);
if (!i)
return NULL;
+ if (!handle)
+ handle = get_handle_at_index(service, i - 1) + 2;
+
value[0] = properties;
len += sizeof(properties);
+
/* We set handle of characteristic value, which will be added next */
- put_le16(get_handle_at_index(service, i - 1) + 2, &value[1]);
+ put_le16(handle, &value[1]);
len += sizeof(uint16_t);
len += uuid_to_le(uuid, &value[3]);
- service->attributes[i] = new_attribute(service, &characteristic_uuid,
- value, len);
+ service->attributes[i] = new_attribute(service, handle - 1,
+ &characteristic_uuid,
+ value, len);
if (!service->attributes[i])
return NULL;
- attribute_update(service, i++);
+ i++;
- service->attributes[i] = new_attribute(service, uuid, NULL, 0);
+ service->attributes[i] = new_attribute(service, handle, uuid, NULL, 0);
if (!service->attributes[i]) {
free(service->attributes[i - 1]);
return NULL;
set_attribute_data(service->attributes[i], read_func, write_func,
permissions, user_data);
- return attribute_update(service, i);
+ return service->attributes[i];
}
struct gatt_db_attribute *
-gatt_db_service_add_descriptor(struct gatt_db_attribute *attrib,
+gatt_db_service_insert_characteristic(struct gatt_db_attribute *attrib,
+ uint16_t handle,
const bt_uuid_t *uuid,
uint32_t permissions,
+ uint8_t properties,
gatt_db_read_t read_func,
gatt_db_write_t write_func,
void *user_data)
{
- struct gatt_db_service *service;
- int i;
+ if (!attrib || !handle)
+ return NULL;
+ return service_insert_characteristic(attrib->service, handle, uuid,
+ permissions, properties,
+ read_func, write_func,
+ user_data);
+}
+
+struct gatt_db_attribute *
+gatt_db_service_add_characteristic(struct gatt_db_attribute *attrib,
+ const bt_uuid_t *uuid,
+ uint32_t permissions,
+ uint8_t properties,
+ gatt_db_read_t read_func,
+ gatt_db_write_t write_func,
+ void *user_data)
+{
if (!attrib)
- return false;
+ return NULL;
- service = attrib->service;
+ return service_insert_characteristic(attrib->service, 0, uuid,
+ permissions, properties,
+ read_func, write_func,
+ user_data);
+}
+
+static struct gatt_db_attribute *
+service_insert_descriptor(struct gatt_db_service *service,
+ uint16_t handle,
+ const bt_uuid_t *uuid,
+ uint32_t permissions,
+ gatt_db_read_t read_func,
+ gatt_db_write_t write_func,
+ void *user_data)
+{
+ int i;
i = get_attribute_index(service, 0);
if (!i)
return NULL;
- service->attributes[i] = new_attribute(service, uuid, NULL, 0);
+ /* Check if handle is in within service range */
+ if (handle && handle <= service->attributes[0]->handle)
+ return NULL;
+
+ if (!handle)
+ handle = get_handle_at_index(service, i - 1) + 1;
+
+ service->attributes[i] = new_attribute(service, handle, uuid, NULL, 0);
if (!service->attributes[i])
return NULL;
set_attribute_data(service->attributes[i], read_func, write_func,
permissions, user_data);
- return attribute_update(service, i);
+ return service->attributes[i];
+}
+
+struct gatt_db_attribute *
+gatt_db_service_insert_descriptor(struct gatt_db_attribute *attrib,
+ uint16_t handle,
+ const bt_uuid_t *uuid,
+ uint32_t permissions,
+ gatt_db_read_t read_func,
+ gatt_db_write_t write_func,
+ void *user_data)
+{
+ if (!attrib || !handle)
+ return NULL;
+
+ return service_insert_descriptor(attrib->service, handle, uuid,
+ permissions, read_func, write_func,
+ user_data);
+}
+
+struct gatt_db_attribute *
+gatt_db_service_add_descriptor(struct gatt_db_attribute *attrib,
+ const bt_uuid_t *uuid,
+ uint32_t permissions,
+ gatt_db_read_t read_func,
+ gatt_db_write_t write_func,
+ void *user_data)
+{
+ if (!attrib)
+ return NULL;
+
+ return service_insert_descriptor(attrib->service, 0, uuid,
+ permissions, read_func, write_func,
+ user_data);
}
struct gatt_db_attribute *
if (!index)
return NULL;
- service->attributes[index] = new_attribute(service,
+ service->attributes[index] = new_attribute(service, 0,
&included_service_uuid,
value, len);
if (!service->attributes[index])
return false;
service = attrib->service;
+
+ if (service->active == active)
+ return true;
+
service->active = active;
notify_service_changed(service->db, service, active);
return true;
}
+bool gatt_db_service_get_active(struct gatt_db_attribute *attrib)
+{
+ if (!attrib)
+ return false;
+
+ return attrib->service->active;
+}
+
+bool gatt_db_service_set_claimed(struct gatt_db_attribute *attrib,
+ bool claimed)
+{
+ if (!attrib)
+ return false;
+
+ attrib->service->claimed = claimed;
+
+ return true;
+}
+
+bool gatt_db_service_get_claimed(struct gatt_db_attribute *attrib)
+{
+ if (!attrib)
+ return false;
+
+ return attrib->service->claimed;
+}
+
void gatt_db_read_by_group_type(struct gatt_db *db, uint16_t start_handle,
uint16_t end_handle,
const bt_uuid_t type,
}
struct find_by_type_value_data {
- struct queue *queue;
bt_uuid_t uuid;
uint16_t start_handle;
uint16_t end_handle;
+ gatt_db_attribute_cb_t func;
+ void *user_data;
+ const void *value;
+ size_t value_len;
+ unsigned int num_of_res;
};
static void find_by_type(void *data, void *user_data)
if (bt_uuid_cmp(&search_data->uuid, &attribute->uuid))
continue;
- queue_push_tail(search_data->queue, attribute);
+ /* TODO: fix for read-callback based attributes */
+ if (search_data->value && memcmp(attribute->value,
+ search_data->value,
+ search_data->value_len))
+ continue;
+
+ search_data->num_of_res++;
+ search_data->func(attribute, search_data->user_data);
}
}
-void gatt_db_find_by_type(struct gatt_db *db, uint16_t start_handle,
- uint16_t end_handle,
- const bt_uuid_t *type,
- struct queue *queue)
+unsigned int gatt_db_find_by_type(struct gatt_db *db, uint16_t start_handle,
+ uint16_t end_handle,
+ const bt_uuid_t *type,
+ gatt_db_attribute_cb_t func,
+ void *user_data)
{
struct find_by_type_value_data data;
+ memset(&data, 0, sizeof(data));
+
data.uuid = *type;
data.start_handle = start_handle;
data.end_handle = end_handle;
- data.queue = queue;
+ data.func = func;
+ data.user_data = user_data;
+
+ queue_foreach(db->services, find_by_type, &data);
+
+ return data.num_of_res;
+}
+
+unsigned int gatt_db_find_by_type_value(struct gatt_db *db,
+ uint16_t start_handle,
+ uint16_t end_handle,
+ const bt_uuid_t *type,
+ const void *value,
+ size_t value_len,
+ gatt_db_attribute_cb_t func,
+ void *user_data)
+{
+ struct find_by_type_value_data data;
+
+ data.uuid = *type;
+ data.start_handle = start_handle;
+ data.end_handle = end_handle;
+ data.func = func;
+ data.user_data = user_data;
+ data.value = value;
+ data.value_len = value_len;
queue_foreach(db->services, find_by_type, &data);
+
+ return data.num_of_res;
}
struct read_by_type_data {
service = attrib->service;
/* Start from the attribute following the value handle */
- i = attrib->handle - service->attributes[0]->handle + 2;
+ for (i = 0; i < service->num_handles; i++) {
+ if (service->attributes[i] == attrib) {
+ i += 2;
+ break;
+ }
+ }
+
for (; i < service->num_handles; i++) {
attr = service->attributes[i];
if (!attr)
uint16_t handle = PTR_TO_UINT(user_data);
uint16_t start, end;
- start = service->attributes[0]->handle;
- end = start + service->num_handles;
+ gatt_db_service_get_handles(service, &start, &end);
- return (start <= handle) && (handle < end);
+ return (start <= handle) && (handle <= end);
}
struct gatt_db_attribute *gatt_db_get_attribute(struct gatt_db *db,
uint16_t handle)
{
struct gatt_db_service *service;
- uint16_t service_handle;
+ int i;
if (!db || !handle)
return NULL;
if (!service)
return NULL;
- service_handle = service->attributes[0]->handle;
+ for (i = 0; i < service->num_handles; i++) {
+ if (!service->attributes[i])
+ continue;
+
+ if (service->attributes[i]->handle == handle)
+ return service->attributes[i];
+ }
- /*
- * We can safely get attribute from attributes array with offset,
- * because find_service_for_handle() check if given handle is
- * in service range.
- */
- return service->attributes[handle - service_handle];
+ return NULL;
+}
+
+static bool find_service_with_uuid(const void *data, const void *user_data)
+{
+ const struct gatt_db_service *service = data;
+ const bt_uuid_t *uuid = user_data;
+ bt_uuid_t svc_uuid;
+
+ gatt_db_attribute_get_service_uuid(service->attributes[0], &svc_uuid);
+
+ return bt_uuid_cmp(uuid, &svc_uuid) == 0;
+}
+
+struct gatt_db_attribute *gatt_db_get_service_with_uuid(struct gatt_db *db,
+ const bt_uuid_t *uuid)
+{
+ struct gatt_db_service *service;
+
+ if (!db || !uuid)
+ return NULL;
+
+ service = queue_find(db->services, find_service_with_uuid, uuid);
+ if (!service)
+ return NULL;
+
+ return service->attributes[0];
}
const bt_uuid_t *gatt_db_attribute_get_type(
return true;
}
-bool gatt_db_attribute_get_permissions(const struct gatt_db_attribute *attrib,
- uint32_t *permissions)
-{
- if (!attrib || !permissions)
- return false;
-
- *permissions = attrib->permissions;
-
- return true;
-}
-
-static void pending_read_result(struct pending_read *p, int err,
- const uint8_t *data, size_t length)
+uint32_t
+gatt_db_attribute_get_permissions(const struct gatt_db_attribute *attrib)
{
- if (p->timeout_id > 0)
- timeout_remove(p->timeout_id);
-
- p->func(p->attrib, err, data, length, p->user_data);
+ if (!attrib)
+ return 0;
- free(p);
+ return attrib->permissions;
}
static bool read_timeout(void *user_data)
}
bool gatt_db_attribute_read(struct gatt_db_attribute *attrib, uint16_t offset,
- uint8_t opcode, bdaddr_t *bdaddr,
+ uint8_t opcode, struct bt_att *att,
gatt_db_attribute_read_t func, void *user_data)
{
uint8_t *value;
queue_push_tail(attrib->pending_reads, p);
- attrib->read_func(attrib, p->id, offset, opcode, bdaddr,
+ attrib->read_func(attrib, p->id, offset, opcode, att,
attrib->user_data);
return true;
}
/* Check boundary if value is stored in the db */
- if (offset > attrib->value_len)
- return false;
+ if (offset > attrib->value_len) {
+ func(attrib, BT_ATT_ERROR_INVALID_OFFSET, NULL, 0, user_data);
+ return true;
+ }
/* Guard against invalid access if offset equals to value length */
value = offset == attrib->value_len ? NULL : &attrib->value[offset];
return true;
}
-static void pending_write_result(struct pending_write *p, int err)
-{
- if (p->timeout_id > 0)
- timeout_remove(p->timeout_id);
-
- p->func(p->attrib, err, p->user_data);
-
- free(p);
-}
-
static bool write_timeout(void *user_data)
{
struct pending_write *p = user_data;
bool gatt_db_attribute_write(struct gatt_db_attribute *attrib, uint16_t offset,
const uint8_t *value, size_t len,
- uint8_t opcode, bdaddr_t *bdaddr,
+ uint8_t opcode, struct bt_att *att,
gatt_db_attribute_write_t func,
void *user_data)
{
queue_push_tail(attrib->pending_writes, p);
attrib->write_func(attrib, p->id, offset, value, len, opcode,
- bdaddr, attrib->user_data);
+ att, attrib->user_data);
return true;
}
+ /* Nothing to write just skip */
+ if (len == 0)
+ goto done;
+
/* For values stored in db allocate on demand */
if (!attrib->value || offset >= attrib->value_len ||
len > (unsigned) (attrib->value_len - offset)) {
memcpy(&attrib->value[offset], value, len);
+done:
func(attrib, 0, user_data);
return true;
return true;
}
+
+bool gatt_db_attribute_reset(struct gatt_db_attribute *attrib)
+{
+ if (!attrib)
+ return false;
+
+ if (!attrib->value || !attrib->value_len)
+ return true;
+
+ free(attrib->value);
+ attrib->value = NULL;
+ attrib->value_len = 0;
+
+ return true;
+}
typedef void (*gatt_db_read_t) (struct gatt_db_attribute *attrib,
unsigned int id, uint16_t offset,
- uint8_t opcode, bdaddr_t *bdaddr,
+ uint8_t opcode, struct bt_att *att,
void *user_data);
typedef void (*gatt_db_write_t) (struct gatt_db_attribute *attrib,
unsigned int id, uint16_t offset,
const uint8_t *value, size_t len,
- uint8_t opcode, bdaddr_t *bdaddr,
+ uint8_t opcode, struct bt_att *att,
void *user_data);
struct gatt_db_attribute *
gatt_db_read_t read_func,
gatt_db_write_t write_func,
void *user_data);
+struct gatt_db_attribute *
+gatt_db_service_insert_characteristic(struct gatt_db_attribute *attrib,
+ uint16_t handle,
+ const bt_uuid_t *uuid,
+ uint32_t permissions,
+ uint8_t properties,
+ gatt_db_read_t read_func,
+ gatt_db_write_t write_func,
+ void *user_data);
struct gatt_db_attribute *
gatt_db_service_add_descriptor(struct gatt_db_attribute *attrib,
gatt_db_read_t read_func,
gatt_db_write_t write_func,
void *user_data);
+struct gatt_db_attribute *
+gatt_db_service_insert_descriptor(struct gatt_db_attribute *attrib,
+ uint16_t handle,
+ const bt_uuid_t *uuid,
+ uint32_t permissions,
+ gatt_db_read_t read_func,
+ gatt_db_write_t write_func,
+ void *user_data);
struct gatt_db_attribute *
gatt_db_service_add_included(struct gatt_db_attribute *attrib,
struct gatt_db_attribute *include);
bool gatt_db_service_set_active(struct gatt_db_attribute *attrib, bool active);
+bool gatt_db_service_get_active(struct gatt_db_attribute *attrib);
+
+bool gatt_db_service_set_claimed(struct gatt_db_attribute *attrib,
+ bool claimed);
+bool gatt_db_service_get_claimed(struct gatt_db_attribute *attrib);
+
+typedef void (*gatt_db_attribute_cb_t)(struct gatt_db_attribute *attrib,
+ void *user_data);
void gatt_db_read_by_group_type(struct gatt_db *db, uint16_t start_handle,
uint16_t end_handle,
const bt_uuid_t type,
struct queue *queue);
-void gatt_db_find_by_type(struct gatt_db *db, uint16_t start_handle,
- uint16_t end_handle,
- const bt_uuid_t *type,
- struct queue *queue);
+unsigned int gatt_db_find_by_type(struct gatt_db *db, uint16_t start_handle,
+ uint16_t end_handle,
+ const bt_uuid_t *type,
+ gatt_db_attribute_cb_t func,
+ void *user_data);
+
+unsigned int gatt_db_find_by_type_value(struct gatt_db *db,
+ uint16_t start_handle,
+ uint16_t end_handle,
+ const bt_uuid_t *type,
+ const void *value,
+ size_t value_len,
+ gatt_db_attribute_cb_t func,
+ void *user_data);
void gatt_db_read_by_type(struct gatt_db *db, uint16_t start_handle,
uint16_t end_handle,
struct queue *queue);
-typedef void (*gatt_db_attribute_cb_t)(struct gatt_db_attribute *attrib,
- void *user_data);
-
void gatt_db_foreach_service(struct gatt_db *db, const bt_uuid_t *uuid,
gatt_db_attribute_cb_t func,
void *user_data);
struct gatt_db_attribute *gatt_db_get_attribute(struct gatt_db *db,
uint16_t handle);
+struct gatt_db_attribute *gatt_db_get_service_with_uuid(struct gatt_db *db,
+ const bt_uuid_t *uuid);
+
const bt_uuid_t *gatt_db_attribute_get_type(
const struct gatt_db_attribute *attrib);
uint16_t *start_handle,
uint16_t *end_handle);
-bool gatt_db_attribute_get_permissions(const struct gatt_db_attribute *attrib,
- uint32_t *permissions);
+uint32_t
+gatt_db_attribute_get_permissions(const struct gatt_db_attribute *attrib);
typedef void (*gatt_db_attribute_read_t) (struct gatt_db_attribute *attrib,
int err, const uint8_t *value,
size_t length, void *user_data);
bool gatt_db_attribute_read(struct gatt_db_attribute *attrib, uint16_t offset,
- uint8_t opcode, bdaddr_t *bdaddr,
+ uint8_t opcode, struct bt_att *att,
gatt_db_attribute_read_t func, void *user_data);
bool gatt_db_attribute_read_result(struct gatt_db_attribute *attrib,
bool gatt_db_attribute_write(struct gatt_db_attribute *attrib, uint16_t offset,
const uint8_t *value, size_t len,
- uint8_t opcode, bdaddr_t *bdaddr,
+ uint8_t opcode, struct bt_att *att,
gatt_db_attribute_write_t func,
void *user_data);
bool gatt_db_attribute_write_result(struct gatt_db_attribute *attrib,
unsigned int id, int err);
+
+bool gatt_db_attribute_reset(struct gatt_db_attribute *attrib);
#include "src/shared/queue.h"
#include "src/shared/att.h"
+#include "lib/bluetooth.h"
#include "lib/uuid.h"
#include "src/shared/gatt-helpers.h"
#include "src/shared/util.h"
return true;
}
-struct discovery_op {
+struct bt_gatt_request {
struct bt_att *att;
+ unsigned int id;
uint16_t end_handle;
int ref_count;
bt_uuid_t uuid;
uint16_t service_type;
struct bt_gatt_result *result_head;
struct bt_gatt_result *result_tail;
- bt_gatt_discovery_callback_t callback;
+ bt_gatt_request_callback_t callback;
void *user_data;
bt_gatt_destroy_func_t destroy;
};
static struct bt_gatt_result *result_append(uint8_t opcode, const void *pdu,
uint16_t pdu_len,
uint16_t data_len,
- struct discovery_op *op)
+ struct bt_gatt_request *op)
{
struct bt_gatt_result *result;
uint16_t *end_handle, uint8_t uuid[16])
{
struct bt_gatt_result *read_result;
- struct discovery_op *op;
+ struct bt_gatt_request *op;
const void *pdu_ptr;
int i = 0;
uint16_t *start_handle, uint16_t *end_handle,
uint8_t uuid[16])
{
- struct discovery_op *op;
+ struct bt_gatt_request *op;
const void *pdu_ptr;
bt_uuid_t tmp;
uint16_t *value_handle, uint8_t *properties,
uint8_t uuid[16])
{
- struct discovery_op *op;
+ struct bt_gatt_request *op;
const void *pdu_ptr;
if (!iter || !iter->result || !start_handle || !end_handle ||
uint16_t *handle, uint16_t *length,
const uint8_t **value)
{
- struct discovery_op *op;
+ struct bt_gatt_request *op;
const void *pdu_ptr;
if (!iter || !iter->result || !handle || !length || !value)
op->callback(success, att_ecode, op->user_data);
}
-bool bt_gatt_exchange_mtu(struct bt_att *att, uint16_t client_rx_mtu,
+unsigned int bt_gatt_exchange_mtu(struct bt_att *att, uint16_t client_rx_mtu,
bt_gatt_result_callback_t callback,
void *user_data,
bt_gatt_destroy_func_t destroy)
{
struct mtu_op *op;
uint8_t pdu[2];
+ unsigned int id;
if (!att || !client_rx_mtu)
return false;
put_le16(client_rx_mtu, pdu);
- if (!bt_att_send(att, BT_ATT_OP_MTU_REQ, pdu, sizeof(pdu),
- mtu_cb, op,
- destroy_mtu_op)) {
+ id = bt_att_send(att, BT_ATT_OP_MTU_REQ, pdu, sizeof(pdu), mtu_cb, op,
+ destroy_mtu_op);
+ if (!id)
free(op);
- return false;
- }
-
- return true;
-}
-static void put_uuid_le(const bt_uuid_t *src, void *dst)
-{
- bt_uuid_t uuid;
-
- switch (src->type) {
- case BT_UUID16:
- put_le16(src->value.u16, dst);
- break;
- case BT_UUID128:
- bswap_128(&src->value.u128, dst);
- break;
- case BT_UUID32:
- bt_uuid_to_uuid128(src, &uuid);
- bswap_128(&uuid.value.u128, dst);
- break;
- case BT_UUID_UNSPEC:
- default:
- break;
- }
+ return id;
}
static inline int get_uuid_len(const bt_uuid_t *uuid)
return (uuid->type == BT_UUID16) ? 2 : 16;
}
-static struct discovery_op* discovery_op_ref(struct discovery_op *op)
+struct bt_gatt_request *bt_gatt_request_ref(struct bt_gatt_request *req)
{
- __sync_fetch_and_add(&op->ref_count, 1);
+ if (!req)
+ return NULL;
- return op;
+ __sync_fetch_and_add(&req->ref_count, 1);
+
+ return req;
}
-static void discovery_op_unref(void *data)
+void bt_gatt_request_unref(struct bt_gatt_request *req)
{
- struct discovery_op *op = data;
+ if (!req)
+ return;
- if (__sync_sub_and_fetch(&op->ref_count, 1))
+ if (__sync_sub_and_fetch(&req->ref_count, 1))
return;
- if (op->destroy)
- op->destroy(op->user_data);
+ bt_gatt_request_cancel(req);
- result_destroy(op->result_head);
+ if (req->destroy)
+ req->destroy(req->user_data);
+
+ result_destroy(req->result_head);
+
+ free(req);
+}
+
+void bt_gatt_request_cancel(struct bt_gatt_request *req)
+{
+ if (!req)
+ return;
+
+ if (!req->id)
+ return;
+
+ bt_att_cancel(req->att, req->id);
+ req->id = 0;
+}
+
+static void async_req_unref(void *data)
+{
+ struct bt_gatt_request *req = data;
+
+ bt_gatt_request_unref(req);
+}
+
+static void discovery_op_complete(struct bt_gatt_request *op, bool success,
+ uint8_t ecode)
+{
+ if (op->callback)
+ op->callback(success, ecode, success ? op->result_head : NULL,
+ op->user_data);
+
+ if (!op->id)
+ async_req_unref(op);
+ else
+ op->id = 0;
- free(op);
}
static void read_by_grp_type_cb(uint8_t opcode, const void *pdu,
uint16_t length, void *user_data)
{
- struct discovery_op *op = user_data;
+ struct bt_gatt_request *op = user_data;
bool success;
uint8_t att_ecode = 0;
- struct bt_gatt_result *final_result = NULL;
struct bt_gatt_result *cur_result;
size_t data_length;
size_t list_length;
data_length = ((uint8_t *) pdu)[0];
list_length = length - 1;
- if ((list_length % data_length) ||
- (data_length != 6 && data_length != 20)) {
+ if ((data_length != 6 && data_length != 20) ||
+ (list_length % data_length)) {
success = false;
goto done;
}
put_le16(op->end_handle, pdu + 2);
put_le16(op->service_type, pdu + 4);
- if (bt_att_send(op->att, BT_ATT_OP_READ_BY_GRP_TYPE_REQ,
- pdu, sizeof(pdu),
- read_by_grp_type_cb,
- discovery_op_ref(op),
- discovery_op_unref))
+ op->id = bt_att_send(op->att, BT_ATT_OP_READ_BY_GRP_TYPE_REQ,
+ pdu, sizeof(pdu),
+ read_by_grp_type_cb,
+ bt_gatt_request_ref(op),
+ async_req_unref);
+ if (op->id)
return;
- discovery_op_unref(op);
success = false;
goto done;
}
cur_result->pdu + length - data_length + 1);
success:
- /* End of procedure */
- final_result = op->result_head;
success = true;
done:
- if (op->callback)
- op->callback(success, att_ecode, final_result, op->user_data);
+ discovery_op_complete(op, success, att_ecode);
}
static void find_by_type_val_cb(uint8_t opcode, const void *pdu,
uint16_t length, void *user_data)
{
- struct discovery_op *op = user_data;
+ struct bt_gatt_request *op = user_data;
bool success;
uint8_t att_ecode = 0;
- struct bt_gatt_result *final_result = NULL;
uint16_t last_end;
if (opcode == BT_ATT_OP_ERROR_RSP) {
put_le16(last_end + 1, pdu);
put_le16(op->end_handle, pdu + 2);
put_le16(op->service_type, pdu + 4);
- put_uuid_le(&op->uuid, pdu + 6);
+ bt_uuid_to_le(&op->uuid, pdu + 6);
- if (bt_att_send(op->att, BT_ATT_OP_FIND_BY_TYPE_VAL_REQ,
- pdu, sizeof(pdu),
- find_by_type_val_cb,
- discovery_op_ref(op),
- discovery_op_unref))
+ op->id = bt_att_send(op->att, BT_ATT_OP_FIND_BY_TYPE_VAL_REQ,
+ pdu, sizeof(pdu),
+ find_by_type_val_cb,
+ bt_gatt_request_ref(op),
+ async_req_unref);
+ if (op->id)
return;
- discovery_op_unref(op);
success = false;
goto done;
}
success:
- /* End of procedure */
- final_result = op->result_head;
success = true;
done:
- if (op->callback)
- op->callback(success, att_ecode, final_result, op->user_data);
+ discovery_op_complete(op, success, att_ecode);
}
-static bool discover_services(struct bt_att *att, bt_uuid_t *uuid,
+static struct bt_gatt_request *discover_services(struct bt_att *att,
+ bt_uuid_t *uuid,
uint16_t start, uint16_t end,
- bt_gatt_discovery_callback_t callback,
+ bt_gatt_request_callback_t callback,
void *user_data,
bt_gatt_destroy_func_t destroy,
bool primary)
{
- struct discovery_op *op;
- bool result;
+ struct bt_gatt_request *op;
if (!att)
- return false;
+ return NULL;
- op = new0(struct discovery_op, 1);
+ op = new0(struct bt_gatt_request, 1);
if (!op)
- return false;
+ return NULL;
op->att = att;
op->end_handle = end;
put_le16(end, pdu + 2);
put_le16(op->service_type, pdu + 4);
- result = bt_att_send(att, BT_ATT_OP_READ_BY_GRP_TYPE_REQ,
- pdu, sizeof(pdu),
- read_by_grp_type_cb,
- discovery_op_ref(op),
- discovery_op_unref);
+ op->id = bt_att_send(att, BT_ATT_OP_READ_BY_GRP_TYPE_REQ,
+ pdu, sizeof(pdu),
+ read_by_grp_type_cb,
+ bt_gatt_request_ref(op),
+ async_req_unref);
} else {
uint8_t pdu[6 + get_uuid_len(uuid)];
if (uuid->type == BT_UUID_UNSPEC) {
free(op);
- return false;
+ return NULL;
}
/* Discover by UUID */
put_le16(start, pdu);
put_le16(end, pdu + 2);
put_le16(op->service_type, pdu + 4);
- put_uuid_le(&op->uuid, pdu + 6);
+ bt_uuid_to_le(&op->uuid, pdu + 6);
- result = bt_att_send(att, BT_ATT_OP_FIND_BY_TYPE_VAL_REQ,
- pdu, sizeof(pdu),
- find_by_type_val_cb,
- discovery_op_ref(op),
- discovery_op_unref);
+ op->id = bt_att_send(att, BT_ATT_OP_FIND_BY_TYPE_VAL_REQ,
+ pdu, sizeof(pdu),
+ find_by_type_val_cb,
+ bt_gatt_request_ref(op),
+ async_req_unref);
}
- if (!result)
+ if (!op->id) {
free(op);
+ return NULL;
+ }
- return result;
+ return bt_gatt_request_ref(op);
}
-bool bt_gatt_discover_all_primary_services(struct bt_att *att, bt_uuid_t *uuid,
- bt_gatt_discovery_callback_t callback,
+struct bt_gatt_request *bt_gatt_discover_all_primary_services(
+ struct bt_att *att, bt_uuid_t *uuid,
+ bt_gatt_request_callback_t callback,
void *user_data,
bt_gatt_destroy_func_t destroy)
{
destroy);
}
-bool bt_gatt_discover_primary_services(struct bt_att *att, bt_uuid_t *uuid,
+struct bt_gatt_request *bt_gatt_discover_primary_services(
+ struct bt_att *att, bt_uuid_t *uuid,
uint16_t start, uint16_t end,
- bt_gatt_discovery_callback_t callback,
+ bt_gatt_request_callback_t callback,
void *user_data,
bt_gatt_destroy_func_t destroy)
{
destroy, true);
}
-bool bt_gatt_discover_secondary_services(struct bt_att *att, bt_uuid_t *uuid,
+struct bt_gatt_request *bt_gatt_discover_secondary_services(
+ struct bt_att *att, bt_uuid_t *uuid,
uint16_t start, uint16_t end,
- bt_gatt_discovery_callback_t callback,
+ bt_gatt_request_callback_t callback,
void *user_data,
bt_gatt_destroy_func_t destroy)
{
}
struct read_incl_data {
- struct discovery_op *op;
+ struct bt_gatt_request *op;
struct bt_gatt_result *result;
int pos;
int ref_count;
if (!data)
return NULL;
- data->op = discovery_op_ref(res->op);
+ data->op = bt_gatt_request_ref(res->op);
data->result = res;
return data;
if (__sync_sub_and_fetch(&read_data->ref_count, 1))
return;
- discovery_op_unref(read_data->op);
+ async_req_unref(read_data->op);
free(read_data);
}
uint16_t length, void *user_data)
{
struct read_incl_data *data = user_data;
- struct bt_gatt_result *final_result = NULL;
- struct discovery_op *op = data->op;
+ struct bt_gatt_request *op = data->op;
uint8_t att_ecode = 0;
uint8_t read_pdu[2];
bool success;
last_handle = get_le16(data->result->pdu + data->pos -
data->result->data_len);
if (last_handle == op->end_handle) {
- final_result = op->result_head;
success = true;
goto done;
}
put_le16(op->end_handle, pdu + 2);
put_le16(GATT_INCLUDE_UUID, pdu + 4);
- if (bt_att_send(op->att, BT_ATT_OP_READ_BY_TYPE_REQ,
- pdu, sizeof(pdu), discover_included_cb,
- discovery_op_ref(op), discovery_op_unref))
+ op->id = bt_att_send(op->att, BT_ATT_OP_READ_BY_TYPE_REQ,
+ pdu, sizeof(pdu),
+ discover_included_cb,
+ bt_gatt_request_ref(op),
+ async_req_unref);
+ if (op->id)
return;
- discovery_op_unref(op);
success = false;
goto done;
}
success = false;
done:
- if (op->callback)
- op->callback(success, att_ecode, final_result, op->user_data);
+ discovery_op_complete(op, success, att_ecode);
}
static void read_included(struct read_incl_data *data)
{
- struct discovery_op *op = data->op;
+ struct bt_gatt_request *op = data->op;
uint8_t pdu[2];
memcpy(pdu, data->result->pdu + 2, sizeof(uint16_t));
read_included_unref))
return;
- read_included_unref(data);
-
if (op->callback)
op->callback(false, 0, NULL, data->op->user_data);
+
+ read_included_unref(data);
}
static void discover_included_cb(uint8_t opcode, const void *pdu,
uint16_t length, void *user_data)
{
- struct bt_gatt_result *final_result = NULL;
- struct discovery_op *op = user_data;
+ struct bt_gatt_request *op = user_data;
struct bt_gatt_result *cur_result;
uint8_t att_ecode = 0;
uint16_t last_handle;
put_le16(op->end_handle, pdu + 2);
put_le16(GATT_INCLUDE_UUID, pdu + 4);
- if (bt_att_send(op->att, BT_ATT_OP_READ_BY_TYPE_REQ,
- pdu, sizeof(pdu),
- discover_included_cb,
- discovery_op_ref(op),
- discovery_op_unref))
+ op->id = bt_att_send(op->att, BT_ATT_OP_READ_BY_TYPE_REQ,
+ pdu, sizeof(pdu),
+ discover_included_cb,
+ bt_gatt_request_ref(op),
+ async_req_unref);
+ if (op->id)
return;
- discovery_op_unref(op);
success = false;
goto failed;
}
done:
success = true;
- final_result = op->result_head;
failed:
- if (op->callback)
- op->callback(success, att_ecode, final_result, op->user_data);
+ discovery_op_complete(op, success, att_ecode);
}
-bool bt_gatt_discover_included_services(struct bt_att *att,
+struct bt_gatt_request *bt_gatt_discover_included_services(struct bt_att *att,
uint16_t start, uint16_t end,
- bt_gatt_discovery_callback_t callback,
+ bt_gatt_request_callback_t callback,
void *user_data,
bt_gatt_destroy_func_t destroy)
{
- struct discovery_op *op;
+ struct bt_gatt_request *op;
uint8_t pdu[6];
if (!att)
return false;
- op = new0(struct discovery_op, 1);
+ op = new0(struct bt_gatt_request, 1);
if (!op)
return false;
put_le16(end, pdu + 2);
put_le16(GATT_INCLUDE_UUID, pdu + 4);
- if (!bt_att_send(att, BT_ATT_OP_READ_BY_TYPE_REQ,
- pdu, sizeof(pdu),
- discover_included_cb,
- discovery_op_ref(op),
- discovery_op_unref)) {
+ op->id = bt_att_send(att, BT_ATT_OP_READ_BY_TYPE_REQ, pdu, sizeof(pdu),
+ discover_included_cb, bt_gatt_request_ref(op),
+ async_req_unref);
+ if (!op->id) {
free(op);
- return false;
+ return NULL;
}
- return true;
+ return bt_gatt_request_ref(op);
}
static void discover_chrcs_cb(uint8_t opcode, const void *pdu,
uint16_t length, void *user_data)
{
- struct discovery_op *op = user_data;
+ struct bt_gatt_request *op = user_data;
bool success;
uint8_t att_ecode = 0;
- struct bt_gatt_result *final_result = NULL;
size_t data_length;
uint16_t last_handle;
data_length = ((uint8_t *) pdu)[0];
- if (((length - 1) % data_length) ||
- (data_length != 7 && data_length != 21)) {
+ if ((data_length != 7 && data_length != 21) ||
+ ((length - 1) % data_length)) {
success = false;
goto done;
}
put_le16(op->end_handle, pdu + 2);
put_le16(GATT_CHARAC_UUID, pdu + 4);
- if (bt_att_send(op->att, BT_ATT_OP_READ_BY_TYPE_REQ,
+ op->id = bt_att_send(op->att, BT_ATT_OP_READ_BY_TYPE_REQ,
pdu, sizeof(pdu),
discover_chrcs_cb,
- discovery_op_ref(op),
- discovery_op_unref))
+ bt_gatt_request_ref(op),
+ async_req_unref);
+ if (op->id)
return;
- discovery_op_unref(op);
success = false;
goto done;
}
success:
- final_result = op->result_head;
success = true;
done:
- if (op->callback)
- op->callback(success, att_ecode, final_result,
- op->user_data);
+ discovery_op_complete(op, success, att_ecode);
}
-bool bt_gatt_discover_characteristics(struct bt_att *att,
+struct bt_gatt_request *bt_gatt_discover_characteristics(struct bt_att *att,
uint16_t start, uint16_t end,
- bt_gatt_discovery_callback_t callback,
+ bt_gatt_request_callback_t callback,
void *user_data,
bt_gatt_destroy_func_t destroy)
{
- struct discovery_op *op;
+ struct bt_gatt_request *op;
uint8_t pdu[6];
if (!att)
return false;
- op = new0(struct discovery_op, 1);
+ op = new0(struct bt_gatt_request, 1);
if (!op)
return false;
put_le16(end, pdu + 2);
put_le16(GATT_CHARAC_UUID, pdu + 4);
- if (!bt_att_send(att, BT_ATT_OP_READ_BY_TYPE_REQ,
- pdu, sizeof(pdu),
- discover_chrcs_cb,
- discovery_op_ref(op),
- discovery_op_unref)) {
+ op->id = bt_att_send(att, BT_ATT_OP_READ_BY_TYPE_REQ, pdu, sizeof(pdu),
+ discover_chrcs_cb, bt_gatt_request_ref(op),
+ async_req_unref);
+ if (!op->id) {
free(op);
- return false;
+ return NULL;
}
- return true;
+ return bt_gatt_request_ref(op);
}
static void read_by_type_cb(uint8_t opcode, const void *pdu,
uint16_t length, void *user_data)
{
- struct discovery_op *op = user_data;
+ struct bt_gatt_request *op = user_data;
bool success;
uint8_t att_ecode = 0;
size_t data_length;
put_le16(last_handle + 1, pdu);
put_le16(op->end_handle, pdu + 2);
- put_uuid_le(&op->uuid, pdu + 4);
+ bt_uuid_to_le(&op->uuid, pdu + 4);
- if (bt_att_send(op->att, BT_ATT_OP_READ_BY_TYPE_REQ,
+ op->id = bt_att_send(op->att, BT_ATT_OP_READ_BY_TYPE_REQ,
pdu, sizeof(pdu),
read_by_type_cb,
- discovery_op_ref(op),
- discovery_op_unref))
+ bt_gatt_request_ref(op),
+ async_req_unref);
+ if (op->id)
return;
- discovery_op_unref(op);
success = false;
goto done;
}
success = true;
done:
- if (op->callback)
- op->callback(success, att_ecode, success ? op->result_head :
- NULL, op->user_data);
+ discovery_op_complete(op, success, att_ecode);
}
bool bt_gatt_read_by_type(struct bt_att *att, uint16_t start, uint16_t end,
const bt_uuid_t *uuid,
- bt_gatt_discovery_callback_t callback,
+ bt_gatt_request_callback_t callback,
void *user_data,
bt_gatt_destroy_func_t destroy)
{
- struct discovery_op *op;
+ struct bt_gatt_request *op;
uint8_t pdu[4 + get_uuid_len(uuid)];
if (!att || !uuid || uuid->type == BT_UUID_UNSPEC)
return false;
- op = new0(struct discovery_op, 1);
+ op = new0(struct bt_gatt_request, 1);
if (!op)
return false;
put_le16(start, pdu);
put_le16(end, pdu + 2);
- put_uuid_le(uuid, pdu + 4);
+ bt_uuid_to_le(uuid, pdu + 4);
- if (!bt_att_send(att, BT_ATT_OP_READ_BY_TYPE_REQ, pdu, sizeof(pdu),
- read_by_type_cb, discovery_op_ref(op),
- discovery_op_unref)) {
- free(op);
- return false;
- }
+ op->id = bt_att_send(att, BT_ATT_OP_READ_BY_TYPE_REQ, pdu, sizeof(pdu),
+ read_by_type_cb,
+ bt_gatt_request_ref(op),
+ async_req_unref);
+ if (op->id)
+ return true;
- return true;
+ free(op);
+ return false;
}
static void discover_descs_cb(uint8_t opcode, const void *pdu,
uint16_t length, void *user_data)
{
- struct discovery_op *op = user_data;
+ struct bt_gatt_request *op = user_data;
bool success;
uint8_t att_ecode = 0;
- struct bt_gatt_result *final_result = NULL;
uint8_t format;
uint16_t last_handle;
size_t data_length;
put_le16(last_handle + 1, pdu);
put_le16(op->end_handle, pdu + 2);
- if (bt_att_send(op->att, BT_ATT_OP_FIND_INFO_REQ,
+ op->id = bt_att_send(op->att, BT_ATT_OP_FIND_INFO_REQ,
pdu, sizeof(pdu),
discover_descs_cb,
- discovery_op_ref(op),
- discovery_op_unref))
+ bt_gatt_request_ref(op),
+ async_req_unref);
+ if (op->id)
return;
- discovery_op_unref(op);
success = false;
goto done;
}
success:
- final_result = op->result_head;
success = true;
done:
- if (op->callback)
- op->callback(success, att_ecode, final_result, op->user_data);
+ discovery_op_complete(op, success, att_ecode);
}
-bool bt_gatt_discover_descriptors(struct bt_att *att,
+struct bt_gatt_request *bt_gatt_discover_descriptors(struct bt_att *att,
uint16_t start, uint16_t end,
- bt_gatt_discovery_callback_t callback,
+ bt_gatt_request_callback_t callback,
void *user_data,
bt_gatt_destroy_func_t destroy)
{
- struct discovery_op *op;
+ struct bt_gatt_request *op;
uint8_t pdu[4];
if (!att)
return false;
- op = new0(struct discovery_op, 1);
+ op = new0(struct bt_gatt_request, 1);
if (!op)
return false;
put_le16(start, pdu);
put_le16(end, pdu + 2);
- if (!bt_att_send(att, BT_ATT_OP_FIND_INFO_REQ, pdu, sizeof(pdu),
+ op->id = bt_att_send(att, BT_ATT_OP_FIND_INFO_REQ, pdu, sizeof(pdu),
discover_descs_cb,
- discovery_op_ref(op),
- discovery_op_unref)) {
+ bt_gatt_request_ref(op),
+ async_req_unref);
+ if (!op->id) {
free(op);
- return false;
+ return NULL;
}
- return true;
+ return bt_gatt_request_ref(op);
}
typedef void (*bt_gatt_result_callback_t)(bool success, uint8_t att_ecode,
void *user_data);
-typedef void (*bt_gatt_discovery_callback_t)(bool success, uint8_t att_ecode,
+typedef void (*bt_gatt_request_callback_t)(bool success, uint8_t att_ecode,
struct bt_gatt_result *result,
void *user_data);
-bool bt_gatt_exchange_mtu(struct bt_att *att, uint16_t client_rx_mtu,
+struct bt_gatt_request;
+
+struct bt_gatt_request *bt_gatt_request_ref(struct bt_gatt_request *req);
+void bt_gatt_request_unref(struct bt_gatt_request *req);
+void bt_gatt_request_cancel(struct bt_gatt_request *req);
+
+unsigned int bt_gatt_exchange_mtu(struct bt_att *att, uint16_t client_rx_mtu,
bt_gatt_result_callback_t callback,
void *user_data,
bt_gatt_destroy_func_t destroy);
-bool bt_gatt_discover_all_primary_services(struct bt_att *att, bt_uuid_t *uuid,
- bt_gatt_discovery_callback_t callback,
+struct bt_gatt_request *bt_gatt_discover_all_primary_services(
+ struct bt_att *att, bt_uuid_t *uuid,
+ bt_gatt_request_callback_t callback,
void *user_data,
bt_gatt_destroy_func_t destroy);
-bool bt_gatt_discover_primary_services(struct bt_att *att, bt_uuid_t *uuid,
+struct bt_gatt_request *bt_gatt_discover_primary_services(
+ struct bt_att *att, bt_uuid_t *uuid,
uint16_t start, uint16_t end,
- bt_gatt_discovery_callback_t callback,
+ bt_gatt_request_callback_t callback,
void *user_data,
bt_gatt_destroy_func_t destroy);
-bool bt_gatt_discover_secondary_services(struct bt_att *att, bt_uuid_t *uuid,
+struct bt_gatt_request *bt_gatt_discover_secondary_services(
+ struct bt_att *att, bt_uuid_t *uuid,
uint16_t start, uint16_t end,
- bt_gatt_discovery_callback_t callback,
+ bt_gatt_request_callback_t callback,
void *user_data,
bt_gatt_destroy_func_t destroy);
-bool bt_gatt_discover_included_services(struct bt_att *att,
+struct bt_gatt_request *bt_gatt_discover_included_services(struct bt_att *att,
uint16_t start, uint16_t end,
- bt_gatt_discovery_callback_t callback,
+ bt_gatt_request_callback_t callback,
void *user_data,
bt_gatt_destroy_func_t destroy);
-bool bt_gatt_discover_characteristics(struct bt_att *att,
+struct bt_gatt_request *bt_gatt_discover_characteristics(struct bt_att *att,
uint16_t start, uint16_t end,
- bt_gatt_discovery_callback_t callback,
+ bt_gatt_request_callback_t callback,
void *user_data,
bt_gatt_destroy_func_t destroy);
-bool bt_gatt_discover_descriptors(struct bt_att *att,
+struct bt_gatt_request *bt_gatt_discover_descriptors(struct bt_att *att,
uint16_t start, uint16_t end,
- bt_gatt_discovery_callback_t callback,
+ bt_gatt_request_callback_t callback,
void *user_data,
bt_gatt_destroy_func_t destroy);
bool bt_gatt_read_by_type(struct bt_att *att, uint16_t start, uint16_t end,
const bt_uuid_t *uuid,
- bt_gatt_discovery_callback_t callback,
+ bt_gatt_request_callback_t callback,
void *user_data,
bt_gatt_destroy_func_t destroy);
#include <errno.h>
#include "src/shared/att.h"
+#include "lib/bluetooth.h"
#include "lib/uuid.h"
#include "src/shared/queue.h"
#include "src/shared/gatt-db.h"
unsigned int read_by_grp_type_id;
unsigned int read_by_type_id;
unsigned int find_info_id;
+ unsigned int find_by_type_value_id;
unsigned int write_id;
unsigned int write_cmd_id;
unsigned int read_id;
unsigned int read_blob_id;
+ unsigned int read_multiple_id;
unsigned int prep_write_id;
unsigned int exec_write_id;
bt_att_unregister(server->att, server->read_by_grp_type_id);
bt_att_unregister(server->att, server->read_by_type_id);
bt_att_unregister(server->att, server->find_info_id);
+ bt_att_unregister(server->att, server->find_by_type_value_id);
bt_att_unregister(server->att, server->write_id);
bt_att_unregister(server->att, server->write_cmd_id);
bt_att_unregister(server->att, server->read_id);
bt_att_unregister(server->att, server->read_blob_id);
+ bt_att_unregister(server->att, server->read_multiple_id);
bt_att_unregister(server->att, server->prep_write_id);
bt_att_unregister(server->att, server->exec_write_id);
}
static bool encode_read_by_grp_type_rsp(struct gatt_db *db, struct queue *q,
- uint16_t mtu,
- uint8_t *pdu, uint16_t *len)
+ struct bt_att *att,
+ uint16_t mtu, uint8_t *pdu,
+ uint16_t *len)
{
int iter = 0;
uint16_t start_handle, end_handle;
struct iovec value;
- uint8_t data_val_len;
+ uint8_t data_val_len = 0;
*len = 0;
*/
if (!gatt_db_attribute_read(attrib, 0,
BT_ATT_OP_READ_BY_GRP_TYPE_REQ,
- NULL, attribute_read_cb,
+ att, attribute_read_cb,
&value) || !value.iov_len)
return false;
goto error;
}
- if (!encode_read_by_grp_type_rsp(server->db, q, mtu, rsp_pdu,
- &rsp_len)) {
+ if (!encode_read_by_grp_type_rsp(server->db, q, server->att, mtu,
+ rsp_pdu, &rsp_len)) {
ecode = BT_ATT_ERROR_UNLIKELY;
goto error;
}
return;
}
- if (!gatt_db_attribute_get_permissions(attr, &perm)) {
- ecode = BT_ATT_ERROR_UNLIKELY;
- goto error;
- }
+ perm = gatt_db_attribute_get_permissions(attr);
/*
* Check for the READ access permission. Encryption,
goto error;
}
- if (gatt_db_attribute_read(attr, 0, op->opcode, NULL,
- read_by_type_read_complete_cb, op))
+ if (gatt_db_attribute_read(attr, 0, op->opcode, server->att,
+ read_by_type_read_complete_cb, op))
return;
ecode = BT_ATT_ERROR_UNLIKELY;
queue_destroy(q, NULL);
}
-static void put_uuid_le(const bt_uuid_t *src, void *dst)
-{
- bt_uuid_t uuid;
-
- switch (src->type) {
- case BT_UUID16:
- put_le16(src->value.u16, dst);
- break;
- case BT_UUID128:
- bswap_128(&src->value.u128, dst);
- break;
- case BT_UUID32:
- bt_uuid_to_uuid128(src, &uuid);
- bswap_128(&uuid.value.u128, dst);
- break;
- case BT_UUID_UNSPEC:
- default:
- break;
- }
-}
-
static bool encode_find_info_rsp(struct gatt_db *db, struct queue *q,
uint16_t mtu,
uint8_t *pdu, uint16_t *len)
uint16_t handle;
struct gatt_db_attribute *attr;
const bt_uuid_t *type;
- int uuid_len, cur_uuid_len;
+ int uuid_len = 0, cur_uuid_len;
int iter = 0;
*len = 0;
break;
put_le16(handle, pdu + iter);
- put_uuid_le(type, pdu + iter + 2);
+ bt_uuid_to_le(type, pdu + iter + 2);
iter += uuid_len + 2;
}
}
+struct find_by_type_val_data {
+ uint8_t *pdu;
+ uint16_t len;
+ uint16_t mtu;
+ uint8_t ecode;
+};
+
+static void find_by_type_val_att_cb(struct gatt_db_attribute *attrib,
+ void *user_data)
+{
+ uint16_t handle, end_handle;
+ struct find_by_type_val_data *data = user_data;
+
+ if (data->ecode)
+ return;
+
+ if (data->len + 4 > data->mtu - 1)
+ return;
+
+ /*
+ * This OP is only valid for Primary Service per the spec
+ * page 562, so this should work.
+ */
+ gatt_db_attribute_get_service_data(attrib, &handle, &end_handle, NULL,
+ NULL);
+
+ if (!handle || !end_handle) {
+ data->ecode = BT_ATT_ERROR_UNLIKELY;
+ return;
+ }
+
+ put_le16(handle, data->pdu + data->len);
+ put_le16(end_handle, data->pdu + data->len + 2);
+
+ data->len += 4;
+}
+
+static void find_by_type_val_cb(uint8_t opcode, const void *pdu,
+ uint16_t length, void *user_data)
+{
+ struct bt_gatt_server *server = user_data;
+ uint16_t start, end, uuid16;
+ struct find_by_type_val_data data;
+ uint16_t mtu = bt_att_get_mtu(server->att);
+ uint8_t rsp_pdu[mtu];
+ uint16_t ehandle = 0;
+ bt_uuid_t uuid;
+
+ if (length < 6) {
+ data.ecode = BT_ATT_ERROR_INVALID_PDU;
+ goto error;
+ }
+
+ data.pdu = rsp_pdu;
+ data.len = 0;
+ data.mtu = mtu;
+ data.ecode = 0;
+
+ start = get_le16(pdu);
+ end = get_le16(pdu + 2);
+ uuid16 = get_le16(pdu + 4);
+
+ util_debug(server->debug_callback, server->debug_data,
+ "Find By Type Value - start: 0x%04x end: 0x%04x uuid: 0x%04x",
+ start, end, uuid16);
+ ehandle = start;
+ if (start > end) {
+ data.ecode = BT_ATT_ERROR_INVALID_HANDLE;
+ goto error;
+ }
+
+ bt_uuid16_create(&uuid, uuid16);
+ gatt_db_find_by_type_value(server->db, start, end, &uuid, pdu + 6,
+ length - 6,
+ find_by_type_val_att_cb,
+ &data);
+
+ if (!data.len)
+ data.ecode = BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND;
+
+ if (data.ecode)
+ goto error;
+
+ bt_att_send(server->att, BT_ATT_OP_FIND_BY_TYPE_VAL_RSP, data.pdu,
+ data.len, NULL, NULL, NULL);
+
+ return;
+
+error:
+ bt_att_send_error_rsp(server->att, opcode, ehandle, data.ecode);
+}
+
static void async_write_op_destroy(struct async_write_op *op)
{
if (op->server)
(opcode == BT_ATT_OP_WRITE_REQ) ? "Req" : "Cmd",
handle);
- if (!gatt_db_attribute_get_permissions(attr, &perm)) {
- ecode = BT_ATT_ERROR_INVALID_HANDLE;
- goto error;
- }
+ perm = gatt_db_attribute_get_permissions(attr);
if (!(perm & BT_ATT_PERM_WRITE)) {
ecode = BT_ATT_ERROR_WRITE_NOT_PERMITTED;
server->pending_write_op = op;
if (gatt_db_attribute_write(attr, 0, pdu + 2, length - 2, opcode,
- NULL, write_complete_cb, op))
+ server->att,
+ write_complete_cb, op))
return;
if (op)
opcode == BT_ATT_OP_READ_BLOB_REQ ? "Blob " : "",
handle);
- if (!gatt_db_attribute_get_permissions(attr, &perm)) {
- ecode = BT_ATT_ERROR_INVALID_HANDLE;
- goto error;
- }
+ perm = gatt_db_attribute_get_permissions(attr);
if (perm && !(perm & BT_ATT_PERM_READ)) {
ecode = BT_ATT_ERROR_READ_NOT_PERMITTED;
op->server = server;
server->pending_read_op = op;
- if (gatt_db_attribute_read(attr, offset, opcode, NULL,
+ if (gatt_db_attribute_read(attr, offset, opcode, server->att,
read_complete_cb, op))
return;
handle_read_req(server, opcode, handle, offset);
}
+struct read_multiple_resp_data {
+ struct bt_gatt_server *server;
+ uint16_t *handles;
+ size_t cur_handle;
+ size_t num_handles;
+ uint8_t *rsp_data;
+ size_t length;
+ size_t mtu;
+};
+
+static void read_multiple_resp_data_free(struct read_multiple_resp_data *data)
+{
+ free(data->handles);
+ data->handles = NULL;
+
+ free(data->rsp_data);
+ data->rsp_data = NULL;
+}
+
+static void read_multiple_complete_cb(struct gatt_db_attribute *attr, int err,
+ const uint8_t *value, size_t len,
+ void *user_data)
+{
+ struct read_multiple_resp_data *data = user_data;
+ struct gatt_db_attribute *next_attr;
+ uint32_t perm;
+ uint16_t handle = gatt_db_attribute_get_handle(attr);
+
+ if (err != 0) {
+ bt_att_send_error_rsp(data->server->att,
+ BT_ATT_OP_READ_MULT_REQ, handle, err);
+ read_multiple_resp_data_free(data);
+ return;
+ }
+
+ perm = gatt_db_attribute_get_permissions(attr);
+
+ if (perm && !(perm & BT_ATT_PERM_READ)) {
+ bt_att_send_error_rsp(data->server->att,
+ BT_ATT_OP_READ_MULT_REQ, handle,
+ BT_ATT_ERROR_READ_NOT_PERMITTED);
+ read_multiple_resp_data_free(data);
+ return;
+ }
+
+ len = MIN(len, data->mtu - data->length - 1);
+
+ memcpy(data->rsp_data + data->length, value, len);
+ data->length += len;
+
+ data->cur_handle++;
+
+ if ((data->length >= data->mtu - 1) ||
+ (data->cur_handle == data->num_handles)) {
+ bt_att_send(data->server->att, BT_ATT_OP_READ_MULT_RSP,
+ data->rsp_data, data->length, NULL, NULL, NULL);
+ read_multiple_resp_data_free(data);
+ return;
+ }
+
+ util_debug(data->server->debug_callback, data->server->debug_data,
+ "Read Multiple Req - #%zu of %zu: 0x%04x",
+ data->cur_handle + 1, data->num_handles,
+ data->handles[data->cur_handle]);
+
+ next_attr = gatt_db_get_attribute(data->server->db,
+ data->handles[data->cur_handle]);
+
+ if (!next_attr) {
+ bt_att_send_error_rsp(data->server->att,
+ BT_ATT_OP_READ_MULT_REQ,
+ data->handles[data->cur_handle],
+ BT_ATT_ERROR_INVALID_HANDLE);
+ read_multiple_resp_data_free(data);
+ return;
+ }
+
+ if (!gatt_db_attribute_read(next_attr, 0, BT_ATT_OP_READ_MULT_REQ,
+ data->server->att,
+ read_multiple_complete_cb, data)) {
+ bt_att_send_error_rsp(data->server->att,
+ BT_ATT_OP_READ_MULT_REQ,
+ data->handles[data->cur_handle],
+ BT_ATT_ERROR_UNLIKELY);
+ read_multiple_resp_data_free(data);
+ }
+}
+
+static void read_multiple_cb(uint8_t opcode, const void *pdu,
+ uint16_t length, void *user_data)
+{
+ struct bt_gatt_server *server = user_data;
+ struct gatt_db_attribute *attr;
+ struct read_multiple_resp_data data;
+ uint8_t ecode = BT_ATT_ERROR_UNLIKELY;
+ size_t i = 0;
+
+ data.handles = NULL;
+ data.rsp_data = NULL;
+
+ if (length < 4) {
+ ecode = BT_ATT_ERROR_INVALID_PDU;
+ goto error;
+ }
+
+ data.server = server;
+ data.num_handles = length / 2;
+ data.cur_handle = 0;
+ data.mtu = bt_att_get_mtu(server->att);
+ data.length = 0;
+ data.rsp_data = malloc(data.mtu - 1);
+
+ if (!data.rsp_data)
+ goto error;
+
+ data.handles = new0(uint16_t, data.num_handles);
+
+ if (!data.handles)
+ goto error;
+
+ for (i = 0; i < data.num_handles; i++)
+ data.handles[i] = get_le16(pdu + i * 2);
+
+ util_debug(server->debug_callback, server->debug_data,
+ "Read Multiple Req - %zu handles, 1st: 0x%04x",
+ data.num_handles, data.handles[0]);
+
+ attr = gatt_db_get_attribute(server->db, data.handles[0]);
+
+ if (!attr) {
+ ecode = BT_ATT_ERROR_INVALID_HANDLE;
+ goto error;
+ }
+
+ if (gatt_db_attribute_read(attr, 0, opcode, server->att,
+ read_multiple_complete_cb, &data))
+ return;
+
+error:
+ read_multiple_resp_data_free(&data);
+ bt_att_send_error_rsp(server->att, opcode, 0, ecode);
+}
+
static void prep_write_cb(uint8_t opcode, const void *pdu,
uint16_t length, void *user_data)
{
util_debug(server->debug_callback, server->debug_data,
"Prep Write Req - handle: 0x%04x", handle);
- if (!gatt_db_attribute_get_permissions(attr, &perm)) {
- ecode = BT_ATT_ERROR_INVALID_HANDLE;
- goto error;
- }
+ perm = gatt_db_attribute_get_permissions(attr);
/*
* TODO: The "Prepare Write" request requires security permission checks
status = gatt_db_attribute_write(attr, next->offset,
next->value, next->length,
- BT_ATT_OP_EXEC_WRITE_REQ, NULL,
+ BT_ATT_OP_EXEC_WRITE_REQ,
+ server->att,
exec_write_complete_cb, server);
prep_write_data_destroy(next);
if (!server->find_info_id)
return false;
+ /* Find By Type Value */
+ server->find_by_type_value_id = bt_att_register(server->att,
+ BT_ATT_OP_FIND_BY_TYPE_VAL_REQ,
+ find_by_type_val_cb,
+ server, NULL);
+
+ if (!server->find_by_type_value_id)
+ return false;
+
/* Write Request */
server->write_id = bt_att_register(server->att, BT_ATT_OP_WRITE_REQ,
write_cb,
if (!server->read_blob_id)
return false;
+ /* Read Multiple Request */
+ server->read_multiple_id = bt_att_register(server->att,
+ BT_ATT_OP_READ_MULT_REQ,
+ read_multiple_cb,
+ server, NULL);
+
+ if (!server->read_multiple_id)
+ return false;
+
/* Prepare Write Request */
server->prep_write_id = bt_att_register(server->att,
BT_ATT_OP_PREP_WRITE_REQ,
if (!server || (length && !value))
return false;
- pdu_len = MIN(bt_att_get_mtu(server->att), length + 2);
+ pdu_len = MIN(bt_att_get_mtu(server->att) - 1, length + 2);
pdu = malloc(pdu_len);
if (!pdu)
return false;
put_le16(handle, pdu);
- memcpy(pdu + 2, value, length);
+ memcpy(pdu + 2, value, pdu_len - 2);
result = !!bt_att_send(server->att, BT_ATT_OP_HANDLE_VAL_NOT, pdu,
pdu_len, NULL, NULL, NULL);
if (!server || (length && !value))
return false;
- pdu_len = MIN(bt_att_get_mtu(server->att), length + 2);
+ pdu_len = MIN(bt_att_get_mtu(server->att) - 1, length + 2);
pdu = malloc(pdu_len);
if (!pdu)
return false;
data->user_data = user_data;
put_le16(handle, pdu);
- memcpy(pdu + 2, value, length);
+ memcpy(pdu + 2, value, pdu_len - 2);
result = !!bt_att_send(server->att, BT_ATT_OP_HANDLE_VAL_IND, pdu,
pdu_len, conf_cb,
#include <sys/uio.h>
#include "monitor/bt.h"
-#include "monitor/mainloop.h"
+#include "src/shared/mainloop.h"
#include "src/shared/io.h"
#include "src/shared/util.h"
#include "src/shared/queue.h"
const char *data)
{
if (hfp->command_callback) {
- hfp->command_callback(data, hfp->command_data);
hfp->result_pending = true;
+ hfp->command_callback(data, hfp->command_data);
} else {
hfp_gw_send_result(hfp, HFP_RESULT_ERROR);
}
return true;
}
+ hfp->result_pending = true;
handler->callback(&context, type, handler->user_data);
return true;
return NULL;
}
- if (!io_set_read_handler(hfp->io, can_read_data,
- hfp, read_watch_destroy)) {
- queue_destroy(hfp->cmd_handlers,
- destroy_cmd_handler);
+ if (!io_set_read_handler(hfp->io, can_read_data, hfp,
+ read_watch_destroy)) {
+ queue_destroy(hfp->cmd_handlers, destroy_cmd_handler);
io_destroy(hfp->io);
ringbuf_free(hfp->write_buf);
ringbuf_free(hfp->read_buf);
*/
if (hfp->result_pending) {
hfp->result_pending = false;
- can_read_data(hfp->io, hfp);
+ process_input(hfp);
}
return true;
wakeup_writer(hfp);
- hfp->result_pending = false;
+ /*
+ * There might be already something to read in the ring buffer.
+ * If so, let's read it.
+ */
+ if (hfp->result_pending) {
+ hfp->result_pending = false;
+ process_input(hfp);
+ }
return true;
}
if (len == ringbuf_len(hfp->read_buf))
goto done;
- /* If we are here second time for some reason, that is wrong */
- if (free_tmp)
- goto done;
-
str2 = ringbuf_peek(hfp->read_buf, len, &len2);
if (!str2)
goto done;
#include <errno.h>
#include <sys/socket.h>
-#include "monitor/mainloop.h"
+#include "src/shared/mainloop.h"
#include "src/shared/util.h"
#include "src/shared/io.h"
static int epoll_fd;
static int epoll_terminate;
+static int exit_status;
struct mainloop_data {
int fd;
epoll_terminate = 1;
}
+void mainloop_exit_success(void)
+{
+ exit_status = EXIT_SUCCESS;
+ epoll_terminate = 1;
+}
+
+void mainloop_exit_failure(void)
+{
+ exit_status = EXIT_FAILURE;
+ epoll_terminate = 1;
+}
+
static void signal_callback(int fd, uint32_t events, void *user_data)
{
struct signal_data *data = user_data;
if (signal_data) {
if (sigprocmask(SIG_BLOCK, &signal_data->mask, NULL) < 0)
- return 1;
+ return EXIT_FAILURE;
signal_data->fd = signalfd(-1, &signal_data->mask,
SFD_NONBLOCK | SFD_CLOEXEC);
if (signal_data->fd < 0)
- return 1;
+ return EXIT_FAILURE;
if (mainloop_add_fd(signal_data->fd, EPOLLIN,
signal_callback, signal_data, NULL) < 0) {
close(signal_data->fd);
- return 1;
+ return EXIT_FAILURE;
}
}
+ exit_status = EXIT_SUCCESS;
+
while (!epoll_terminate) {
struct epoll_event events[MAX_EPOLL_EVENTS];
int n, nfds;
close(epoll_fd);
epoll_fd = 0;
- return 0;
+ return exit_status;
}
int mainloop_add_fd(int fd, uint32_t events, mainloop_event_func callback,
void mainloop_init(void);
void mainloop_quit(void);
+void mainloop_exit_success(void);
+void mainloop_exit_failure(void);
int mainloop_run(void);
int mainloop_add_fd(int fd, uint32_t events, mainloop_event_func callback,
if (function) {
while (entry) {
void *data;
+ unsigned int entries = queue->entries;
data = queue_remove_if(queue, function, user_data);
- if (!data)
+ if (entries == queue->entries)
break;
if (destroy)
count++;
}
-
- queue->entries -= count;
} else {
queue->head = NULL;
queue->tail = NULL;
return test->user_data;
}
-static void tester_summarize(void)
+static int tester_summarize(void)
{
unsigned int not_run = 0, passed = 0, failed = 0;
gdouble execution_time;
execution_time = g_timer_elapsed(test_timer, NULL);
printf("Overall execution time: %.3g seconds\n", execution_time);
+ return failed;
}
static gboolean teardown_callback(gpointer user_data)
int tester_run(void)
{
guint signal;
+ int ret;
if (!main_loop)
return EXIT_FAILURE;
g_main_loop_unref(main_loop);
- tester_summarize();
+ ret = tester_summarize();
g_list_free_full(test_list, test_destroy);
- return EXIT_SUCCESS;
+ return ret == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
}
#include <stdlib.h>
-#include "monitor/mainloop.h"
-
+#include "mainloop.h"
#include "util.h"
#include "timeout.h"
#include <unistd.h>
#include <dirent.h>
#include <limits.h>
+#include <string.h>
#include "src/shared/util.h"
return DT_UNKNOWN;
}
+
+/* Helpers for bitfield operations */
+
+/* Find unique id in range from 1 to max but no bigger then
+ * sizeof(int) * 8. ffs() is used since it is POSIX standard
+ */
+uint8_t util_get_uid(unsigned int *bitmap, uint8_t max)
+{
+ uint8_t id;
+
+ id = ffs(~*bitmap);
+
+ if (!id || id > max)
+ return 0;
+
+ *bitmap |= 1 << (id - 1);
+
+ return id;
+}
+
+/* Clear id bit in bitmap */
+void util_clear_uid(unsigned int *bitmap, uint8_t id)
+{
+ if (!id)
+ return;
+
+ *bitmap &= ~(1 << (id - 1));
+}
unsigned char util_get_dt(const char *parent, const char *name);
-static inline void bswap_128(const void *src, void *dst)
-{
- const uint8_t *s = src;
- uint8_t *d = dst;
- int i;
-
- for (i = 0; i < 16; i++)
- d[15 - i] = s[i];
-}
+uint8_t util_get_uid(unsigned int *bitmap, uint8_t max);
+void util_clear_uid(unsigned int *bitmap, uint8_t id);
static inline uint16_t get_le16(const void *ptr)
{
#include <glib.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/sdp.h>
-#include <bluetooth/sdp_lib.h>
-
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
#include "lib/uuid.h"
+
#include "textfile.h"
#include "uuid-helper.h"
#include "storage.h"
#include <errno.h>
#include <arpa/inet.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/sdp.h>
-#include <bluetooth/sdp_lib.h>
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
#include "uuid-helper.h"
pbap_client.flush_transfers(lambda: test_paths(paths[1:]))
- test_paths(["PB", "ICH", "OCH", "MCH", "CCH"])
+ test_paths(["PB", "ICH", "OCH", "MCH", "CCH", "SPD", "FAV"])
mainloop.run()
#include <string.h>
#include <getopt.h>
-#include "monitor/mainloop.h"
#include "monitor/bt.h"
+#include "src/shared/mainloop.h"
#include "src/shared/timeout.h"
#include "src/shared/util.h"
#include "src/shared/hci.h"
#define LT_ADDR 0x01
#define PKT_TYPE 0x0008 /* 0x0008 = EDR + DM1, 0xff1e = BR only */
-#define SERVICE_DATA LT_ADDR
+#define SERVICE_DATA 0x00
+
+struct broadcast_message {
+ uint32_t frame_sync_instant;
+ uint16_t bluetooth_clock_phase;
+ uint16_t left_open_offset;
+ uint16_t left_close_offset;
+ uint16_t right_open_offset;
+ uint16_t right_close_offset;
+ uint16_t frame_sync_period;
+ uint8_t frame_sync_period_fraction;
+} __attribute__ ((packed));
+
+struct brcm_evt_sync_train_received {
+ uint8_t status;
+ uint8_t bdaddr[6];
+ uint32_t offset;
+ uint8_t map[10];
+ uint8_t service_data;
+ uint8_t lt_addr;
+ uint32_t instant;
+ uint16_t interval;
+} __attribute__ ((packed));
static struct bt_hci *hci_dev;
mainloop_quit();
}
-static void slave_broadcast_receive(const void *data, uint8_t size,
+static void inquiry_started(const void *data, uint8_t size, void *user_data)
+{
+ uint8_t status = *((uint8_t *) data);
+
+ if (status) {
+ printf("Failed to search for 3D display\n");
+ shutdown_device();
+ return;
+ }
+
+ printf("Searching for 3D display\n");
+}
+
+static void start_inquiry(void)
+{
+ struct bt_hci_cmd_inquiry cmd;
+
+ cmd.lap[0] = 0x33;
+ cmd.lap[1] = 0x8b;
+ cmd.lap[2] = 0x9e;
+ cmd.length = 0x08;
+ cmd.num_resp = 0x00;
+
+ bt_hci_send(hci_dev, BT_HCI_CMD_INQUIRY, &cmd, sizeof(cmd),
+ inquiry_started, NULL, NULL);
+}
+
+static void set_slave_broadcast_receive(const void *data, uint8_t size,
void *user_data)
{
printf("Slave broadcast receiption enabled\n");
if (evt->status) {
printf("Failed to synchronize with 3D display\n");
- shutdown_device();
+ start_inquiry();
+ return;
+ }
+
+ if (evt->lt_addr != LT_ADDR) {
+ printf("Ignoring synchronization for non 3D display\n");
return;
}
memcpy(cmd.map, evt->map, 10);
bt_hci_send(hci_dev, BT_HCI_CMD_SET_SLAVE_BROADCAST_RECEIVE,
- &cmd, sizeof(cmd),
- slave_broadcast_receive, NULL, NULL);
+ &cmd, sizeof(cmd),
+ set_slave_broadcast_receive, NULL, NULL);
+}
+
+static void brcm_sync_train_received(const void *data, uint8_t size,
+ void *user_data)
+{
+ const struct brcm_evt_sync_train_received *evt = data;
+ struct bt_hci_cmd_set_slave_broadcast_receive cmd;
+
+ if (evt->status) {
+ printf("Failed to synchronize with 3D display\n");
+ start_inquiry();
+ return;
+ }
+
+ if (evt->lt_addr != LT_ADDR) {
+ printf("Ignoring synchronization for non 3D display\n");
+ return;
+ }
+
+ cmd.enable = 0x01;
+ memcpy(cmd.bdaddr, evt->bdaddr, 6);
+ cmd.lt_addr = evt->lt_addr;
+ cmd.interval = evt->interval;
+ cmd.offset = evt->offset;
+ cmd.instant = evt->instant;
+ cmd.timeout = cpu_to_le16(0xfffe);
+ cmd.accuracy = 250;
+ cmd.skip = 20;
+ cmd.pkt_type = cpu_to_le16(PKT_TYPE);
+ memcpy(cmd.map, evt->map, 10);
+
+ bt_hci_send(hci_dev, BT_HCI_CMD_SET_SLAVE_BROADCAST_RECEIVE,
+ &cmd, sizeof(cmd),
+ set_slave_broadcast_receive, NULL, NULL);
}
static void truncated_page_complete(const void *data, uint8_t size,
printf("Attempt to synchronize with 3D display\n");
- bt_hci_register(hci_dev, BT_HCI_EVT_SYNC_TRAIN_RECEIVED,
- sync_train_received, NULL, NULL);
+ memcpy(cmd.bdaddr, evt->bdaddr, 6);
+ cmd.timeout = cpu_to_le16(0x4000);
+ cmd.window = cpu_to_le16(0x0100);
+ cmd.interval = cpu_to_le16(0x0080);
+
+ bt_hci_send(hci_dev, BT_HCI_CMD_RECEIVE_SYNC_TRAIN, &cmd, sizeof(cmd),
+ NULL, NULL, NULL);
+}
+
+static void slave_broadcast_timeout(const void *data, uint8_t size,
+ void *user_data)
+{
+ const struct bt_hci_evt_slave_broadcast_timeout *evt = data;
+ struct bt_hci_cmd_receive_sync_train cmd;
+
+ printf("Re-synchronizing with 3D display\n");
memcpy(cmd.bdaddr, evt->bdaddr, 6);
cmd.timeout = cpu_to_le16(0x4000);
NULL, NULL, NULL);
}
+static void slave_broadcast_receive(const void *data, uint8_t size,
+ void *user_data)
+{
+ const struct bt_hci_evt_slave_broadcast_receive *evt = data;
+ struct bt_hci_cmd_read_clock cmd;
+
+ if (evt->status != 0x00)
+ return;
+
+ if (le32_to_cpu(evt->clock) != 0x00000000)
+ return;
+
+ cmd.handle = cpu_to_le16(0x0000);
+ cmd.type = 0x00;
+
+ bt_hci_send(hci_dev, BT_HCI_CMD_READ_CLOCK, &cmd, sizeof(cmd),
+ NULL, NULL, NULL);
+}
+
static void ext_inquiry_result(const void *data, uint8_t size, void *user_data)
{
const struct bt_hci_evt_ext_inquiry_result *evt = data;
bt_hci_send(hci_dev, BT_HCI_CMD_INQUIRY_CANCEL, NULL, 0,
NULL, NULL, NULL);
- bt_hci_register(hci_dev, BT_HCI_EVT_TRUNCATED_PAGE_COMPLETE,
- truncated_page_complete, NULL, NULL);
-
memcpy(cmd.bdaddr, evt->bdaddr, 6);
cmd.pscan_rep_mode = evt->pscan_rep_mode;
cmd.clock_offset = evt->clock_offset;
{
printf("No 3D display found\n");
- shutdown_device();
+ start_inquiry();
}
-static void inquiry_started(const void *data, uint8_t size, void *user_data)
+static void read_local_version(const void *data, uint8_t size, void *user_data)
{
- uint8_t status = *((uint8_t *) data);
+ const struct bt_hci_rsp_read_local_version *rsp = data;
- if (status) {
- printf("Failed to search for 3D display\n");
+ if (rsp->status) {
+ printf("Failed to read local version information\n");
shutdown_device();
return;
}
- printf("Searching for 3D display\n");
+ if (rsp->manufacturer == 15) {
+ printf("Enabling receiver workaround for Broadcom\n");
+
+ bt_hci_register(hci_dev, BT_HCI_EVT_SYNC_TRAIN_RECEIVED,
+ brcm_sync_train_received, NULL, NULL);
+ } else {
+ bt_hci_register(hci_dev, BT_HCI_EVT_SYNC_TRAIN_RECEIVED,
+ sync_train_received, NULL, NULL);
+ }
}
static void start_glasses(void)
{
- struct bt_hci_cmd_inquiry cmd;
uint8_t evtmask1[] = { 0x03, 0xe0, 0x00, 0x00, 0x02, 0x40, 0x00, 0x00 };
uint8_t evtmask2[] = { 0x00, 0x00, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00 };
uint8_t inqmode = 0x02;
NULL, NULL, NULL);
}
+ bt_hci_send(hci_dev, BT_HCI_CMD_READ_LOCAL_VERSION, NULL, 0,
+ read_local_version, NULL, NULL);
+
bt_hci_send(hci_dev, BT_HCI_CMD_SET_EVENT_MASK_PAGE2, evtmask2, 8,
NULL, NULL, NULL);
bt_hci_send(hci_dev, BT_HCI_CMD_WRITE_INQUIRY_MODE, &inqmode, 1,
bt_hci_register(hci_dev, BT_HCI_EVT_EXT_INQUIRY_RESULT,
ext_inquiry_result, NULL, NULL);
- cmd.lap[0] = 0x33;
- cmd.lap[1] = 0x8b;
- cmd.lap[2] = 0x9e;
- cmd.length = 0x08;
- cmd.num_resp = 0x00;
-
- bt_hci_send(hci_dev, BT_HCI_CMD_INQUIRY, &cmd, sizeof(cmd),
- inquiry_started, NULL, NULL);
-}
-
-static void conn_request(const void *data, uint8_t size, void *user_data)
-{
- const struct bt_hci_evt_conn_request *evt = data;
- struct bt_hci_cmd_accept_conn_request cmd;
-
- printf("Incoming connection from 3D glasses\n");
-
- memcpy(cmd.bdaddr, evt->bdaddr, 6);
- cmd.role = 0x00;
+ bt_hci_register(hci_dev, BT_HCI_EVT_TRUNCATED_PAGE_COMPLETE,
+ truncated_page_complete, NULL, NULL);
+ bt_hci_register(hci_dev, BT_HCI_EVT_SLAVE_BROADCAST_TIMEOUT,
+ slave_broadcast_timeout, NULL, NULL);
+ bt_hci_register(hci_dev, BT_HCI_EVT_SLAVE_BROADCAST_RECEIVE,
+ slave_broadcast_receive, NULL, NULL);
- bt_hci_send(hci_dev, BT_HCI_CMD_ACCEPT_CONN_REQUEST, &cmd, sizeof(cmd),
- NULL, NULL, NULL);
+ start_inquiry();
}
static bool sync_train_active = false;
sync_train_active = false;
}
-static void slave_page_response_timeout(const void *data, uint8_t size,
- void *user_data)
+static void start_sync_train(void)
{
struct bt_hci_cmd_write_sync_train_params cmd;
bt_hci_send(hci_dev, BT_HCI_CMD_WRITE_SYNC_TRAIN_PARAMS,
&cmd, sizeof(cmd), NULL, NULL, NULL);
- bt_hci_send(hci_dev, BT_HCI_CMD_READ_SYNC_TRAIN_PARAMS, NULL, 0,
- NULL, NULL, NULL);
-
bt_hci_send(hci_dev, BT_HCI_CMD_START_SYNC_TRAIN, NULL, 0,
NULL, NULL, NULL);
sync_train_active = true;
}
+static void conn_request(const void *data, uint8_t size, void *user_data)
+{
+ const struct bt_hci_evt_conn_request *evt = data;
+ struct bt_hci_cmd_accept_conn_request cmd;
+
+ printf("Incoming connection from 3D glasses\n");
+
+ memcpy(cmd.bdaddr, evt->bdaddr, 6);
+ cmd.role = 0x00;
+
+ bt_hci_send(hci_dev, BT_HCI_CMD_ACCEPT_CONN_REQUEST, &cmd, sizeof(cmd),
+ NULL, NULL, NULL);
+
+ start_sync_train();
+}
+
+static void slave_page_response_timeout(const void *data, uint8_t size,
+ void *user_data)
+{
+ printf("Incoming truncated page received\n");
+
+ start_sync_train();
+}
+
+static void slave_broadcast_channel_map_change(const void *data, uint8_t size,
+ void *user_data)
+{
+ printf("Broadcast channel map changed\n");
+
+ start_sync_train();
+}
+
static void inquiry_resp_tx_power(const void *data, uint8_t size,
void *user_data)
{
NULL, NULL, NULL);
}
+static void read_clock(const void *data, uint8_t size, void *user_data)
+{
+ const struct bt_hci_rsp_read_clock *rsp = data;
+ struct broadcast_message msg;
+ uint8_t bcastdata[sizeof(msg) + 3] = { LT_ADDR, 0x03, 0x11, };
+
+ if (rsp->status) {
+ printf("Failed to read local clock information\n");
+ shutdown_device();
+ return;
+ }
+
+ msg.frame_sync_instant = rsp->clock;
+ msg.bluetooth_clock_phase = rsp->accuracy;
+ msg.left_open_offset = cpu_to_le16(50);
+ msg.left_close_offset = cpu_to_le16(300);
+ msg.right_open_offset = cpu_to_le16(350);
+ msg.right_close_offset = cpu_to_le16(600);
+ msg.frame_sync_period = cpu_to_le16(650);
+ msg.frame_sync_period_fraction = 0;
+ memcpy(bcastdata + 3, &msg, sizeof(msg));
+
+ bt_hci_send(hci_dev, BT_HCI_CMD_SET_SLAVE_BROADCAST_DATA,
+ bcastdata, sizeof(bcastdata), NULL, NULL, NULL);
+}
+
+static void set_slave_broadcast(const void *data, uint8_t size, void *user_data)
+{
+ const struct bt_hci_rsp_set_slave_broadcast *rsp = data;
+ struct bt_hci_cmd_read_clock cmd;
+
+ if (rsp->status) {
+ printf("Failed to set slave broadcast transmission\n");
+ shutdown_device();
+ return;
+ }
+
+ cmd.handle = cpu_to_le16(0x0000);
+ cmd.type = 0x00;
+
+ bt_hci_send(hci_dev, BT_HCI_CMD_READ_CLOCK, &cmd, sizeof(cmd),
+ read_clock, NULL, NULL);
+}
+
static void start_display(void)
{
struct bt_hci_cmd_set_slave_broadcast cmd;
- uint8_t bcastdata[20] = { LT_ADDR, 0x03, 0x11, 0x23, 0x42, };
uint8_t evtmask1[] = { 0x1c, 0xe0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
uint8_t evtmask2[] = { 0x00, 0xc0, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00 };
uint8_t sspmode = 0x01;
bt_hci_register(hci_dev, BT_HCI_EVT_SLAVE_PAGE_RESPONSE_TIMEOUT,
slave_page_response_timeout, NULL, NULL);
+ bt_hci_register(hci_dev, BT_HCI_EVT_SLAVE_BROADCAST_CHANNEL_MAP_CHANGE,
+ slave_broadcast_channel_map_change, NULL, NULL);
bt_hci_register(hci_dev, BT_HCI_EVT_SYNC_TRAIN_COMPLETE,
sync_train_complete, NULL, NULL);
bt_hci_send(hci_dev, BT_HCI_CMD_READ_INQUIRY_RESP_TX_POWER, NULL, 0,
inquiry_resp_tx_power, NULL, NULL);
- bt_hci_send(hci_dev, BT_HCI_CMD_SET_SLAVE_BROADCAST_DATA,
- bcastdata, sizeof(bcastdata), NULL, NULL, NULL);
-
cmd.enable = 0x01;
cmd.lt_addr = LT_ADDR;
cmd.lpo_allowed = 0x01;
cmd.timeout = cpu_to_le16(0xfffe);
bt_hci_send(hci_dev, BT_HCI_CMD_SET_SLAVE_BROADCAST, &cmd, sizeof(cmd),
- NULL, NULL, NULL);
+ set_slave_broadcast, NULL, NULL);
}
static void signal_callback(int signum, void *user_data)
#include <sys/ioctl.h>
#include <sys/socket.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/hci_lib.h>
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
static int activate_amp_controller(int dev_id)
{
#include <sys/ioctl.h>
#include <sys/socket.h>
-#include "profiles/audio/a2dp-codecs.h"
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+#include "lib/l2cap.h"
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/hci_lib.h>
-#include <bluetooth/l2cap.h>
+#include "profiles/audio/a2dp-codecs.h"
#define AVDTP_PSM 25
#include <sys/socket.h>
#include <netinet/in.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/hci_lib.h>
-#include <bluetooth/l2cap.h>
-#include <bluetooth/sdp.h>
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+#include "lib/l2cap.h"
+#include "lib/sdp.h"
#define AVDTP_PKT_TYPE_SINGLE 0x00
#define AVDTP_PKT_TYPE_START 0x01
#include <getopt.h>
#include <sys/socket.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/hci_lib.h>
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
#include "csr.h"
if (err < 0)
return -1;
- handle = array[0] | (array[1] << 8);
keylen = array[2] | (array[3] << 8);
printf("Crypt key length: %d bit\n", keylen * 8);
#include <sys/ioctl.h>
#include <sys/socket.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/hci_lib.h>
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
#include "src/oui.h"
#include <sys/stat.h>
#include <sys/param.h>
-#include "monitor/mainloop.h"
#include "monitor/bt.h"
+#include "src/shared/mainloop.h"
#include "src/shared/util.h"
#include "src/shared/hci.h"
+#define CMD_RESET 0xfc01
+struct cmd_reset {
+ uint8_t reset_type;
+ uint8_t patch_enable;
+ uint8_t otp_ddc_reload;
+ uint8_t boot_option;
+ uint32_t boot_addr;
+} __attribute__ ((packed));
+
#define CMD_NO_OPERATION 0xfc02
#define CMD_READ_VERSION 0xfc05
uint8_t rx_trace;
} __attribute__ ((packed));
+#define CMD_TRIGGER_EXCEPTION 0xfc4d
+struct cmd_trigger_exception {
+ uint8_t type;
+} __attribute__ ((packed));
+
#define CMD_MEMORY_WRITE 0xfc8e
static struct bt_hci *hci_dev;
uint8_t manufacturer_mode_reset = 0x00;
static bool use_manufacturer_mode = false;
static bool set_traces = false;
+static bool set_exception = false;
static bool reset_on_exit = false;
+static bool cold_boot = false;
static void reset_complete(const void *data, uint8_t size, void *user_data)
{
mainloop_quit();
}
+static void cold_boot_complete(const void *data, uint8_t size, void *user_data)
+{
+ uint8_t status = *((uint8_t *) data);
+
+ if (status) {
+ fprintf(stderr, "Failed to cold boot (0x%02x)\n", status);
+ mainloop_quit();
+ return;
+ }
+
+ if (reset_on_exit) {
+ bt_hci_send(hci_dev, BT_HCI_CMD_RESET, NULL, 0,
+ reset_complete, NULL, NULL);
+ return;
+ }
+
+ mainloop_quit();
+}
+
static void leave_manufacturer_mode_complete(const void *data, uint8_t size,
void *user_data)
{
act_deact_traces_complete, NULL, NULL);
}
+static void trigger_exception(void)
+{
+ struct cmd_trigger_exception cmd;
+
+ cmd.type = 0x00;
+
+ bt_hci_send(hci_dev, CMD_TRIGGER_EXCEPTION, &cmd, sizeof(cmd),
+ NULL, NULL, NULL);
+
+ shutdown_device();
+}
+
static void write_bd_data_complete(const void *data, uint8_t size,
void *user_data)
{
return;
}
+ if (set_exception) {
+ trigger_exception();
+ return;
+ }
+
shutdown_device();
}
return;
}
+ if (cold_boot) {
+ struct cmd_reset cmd;
+
+ cmd.reset_type = 0x01;
+ cmd.patch_enable = 0x00;
+ cmd.otp_ddc_reload = 0x01;
+ cmd.boot_option = 0x00;
+ cmd.boot_addr = cpu_to_le32(0x00000000);
+
+ bt_hci_send(hci_dev, CMD_RESET, &cmd, sizeof(cmd),
+ cold_boot_complete, NULL, NULL);
+ return;
+ }
+
if (load_firmware) {
if (load_firmware_value) {
printf("Firmware: %s\n", load_firmware_value);
if (len != st.st_size) {
fprintf(stderr, "Failed to read complete firmware file\n");
goto done;
- return;
}
if ((size_t) len < sizeof(*css)) {
printf("\n");
- if (len != le32_to_cpu(css->size) * 4) {
+ if ((size_t) len != le32_to_cpu(css->size) * 4) {
fprintf(stderr, "CSS.size does not match file length\n");
goto done;
}
"\t-F, --firmware [file] Load firmware\n"
"\t-C, --check <file> Check firmware image\n"
"\t-R, --reset Reset controller\n"
+ "\t-B, --coldboot Cold boot controller\n"
+ "\t-E, --exception Trigger exception\n"
"\t-i, --index <num> Use specified controller\n"
"\t-h, --help Show help options\n");
}
{ "check", required_argument, NULL, 'C' },
{ "traces", no_argument, NULL, 'T' },
{ "reset", no_argument, NULL, 'R' },
+ { "coldboot", no_argument, NULL, 'B' },
+ { "exception",no_argument, NULL, 'E' },
{ "index", required_argument, NULL, 'i' },
+ { "raw", no_argument, NULL, 'r' },
{ "version", no_argument, NULL, 'v' },
{ "help", no_argument, NULL, 'h' },
{ }
int main(int argc, char *argv[])
{
const char *str;
+ bool use_raw = false;
sigset_t mask;
int exit_status;
for (;;) {
int opt;
- opt = getopt_long(argc, argv, "A::DF::C:TRi:vh",
+ opt = getopt_long(argc, argv, "A::DF::C:TRBEi:rvh",
main_options, NULL);
if (opt < 0)
break;
check_firmware_value = optarg;
check_firmware = true;
break;
+ case 'E':
+ use_manufacturer_mode = true;
+ set_exception = true;
+ break;
case 'T':
use_manufacturer_mode = true;
set_traces = true;
case 'R':
reset_on_exit = true;
break;
+ case 'B':
+ cold_boot = true;
+ break;
case 'i':
if (strlen(optarg) > 3 && !strncmp(optarg, "hci", 3))
str = optarg + 3;
}
hci_index = atoi(str);
break;
+ case 'r':
+ use_raw = true;
+ break;
case 'v':
printf("%s\n", VERSION);
return EXIT_SUCCESS;
return EXIT_SUCCESS;
}
- hci_dev = bt_hci_new_user_channel(hci_index);
- if (!hci_dev) {
- fprintf(stderr, "Failed to open HCI user channel\n");
- return EXIT_FAILURE;
+ if (use_raw) {
+ hci_dev = bt_hci_new_raw_device(hci_index);
+ if (!hci_dev) {
+ fprintf(stderr, "Failed to open HCI raw device\n");
+ return EXIT_FAILURE;
+ }
+ } else {
+ hci_dev = bt_hci_new_user_channel(hci_index);
+ if (!hci_dev) {
+ fprintf(stderr, "Failed to open HCI user channel\n");
+ return EXIT_FAILURE;
+ }
}
bt_hci_send(hci_dev, CMD_READ_VERSION, NULL, 0,
#include <readline/readline.h>
#include <readline/history.h>
#include <glib.h>
-#include <gdbus.h>
-#include <client/display.h>
+#include "gdbus/gdbus.h"
+#include "client/display.h"
/* String display constants */
#define COLORED_NEW COLOR_GREEN "NEW" COLOR_OFF
rl_printf("Player %s\n", g_dbus_proxy_get_path(proxy));
print_property(proxy, "Name");
- print_property(proxy, "Searchable");
print_property(proxy, "Repeat");
print_property(proxy, "Equalizer");
print_property(proxy, "Shuffle");
#include <sys/ioctl.h>
#include <poll.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/hci_lib.h>
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
#include "hciattach.h"
-#include "monitor/mainloop.h"
#include "monitor/bt.h"
+#include "src/shared/mainloop.h"
#include "src/shared/timeout.h"
#include "src/shared/util.h"
#include "src/shared/hci.h"
#include <limits.h>
#include <errno.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/hci_lib.h>
-#include <bluetooth/l2cap.h>
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+#include "lib/l2cap.h"
#include "lib/uuid.h"
-#include "monitor/mainloop.h"
+#include "src/shared/mainloop.h"
#include "src/shared/util.h"
#include "src/shared/att.h"
#include "src/shared/queue.h"
struct client {
int fd;
+ struct bt_att *att;
struct gatt_db *db;
struct bt_gatt_client *gatt;
};
fflush(stdout);
}
+static const char *ecode_to_string(uint8_t ecode)
+{
+ switch (ecode) {
+ case BT_ATT_ERROR_INVALID_HANDLE:
+ return "Invalid Handle";
+ case BT_ATT_ERROR_READ_NOT_PERMITTED:
+ return "Read Not Permitted";
+ case BT_ATT_ERROR_WRITE_NOT_PERMITTED:
+ return "Write Not Permitted";
+ case BT_ATT_ERROR_INVALID_PDU:
+ return "Invalid PDU";
+ case BT_ATT_ERROR_AUTHENTICATION:
+ return "Authentication Required";
+ case BT_ATT_ERROR_REQUEST_NOT_SUPPORTED:
+ return "Request Not Supported";
+ case BT_ATT_ERROR_INVALID_OFFSET:
+ return "Invalid Offset";
+ case BT_ATT_ERROR_AUTHORIZATION:
+ return "Authorization Required";
+ case BT_ATT_ERROR_PREPARE_QUEUE_FULL:
+ return "Prepare Write Queue Full";
+ case BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND:
+ return "Attribute Not Found";
+ case BT_ATT_ERROR_ATTRIBUTE_NOT_LONG:
+ return "Attribute Not Long";
+ case BT_ATT_ERROR_INSUFFICIENT_ENCRYPTION_KEY_SIZE:
+ return "Insuficient Encryption Key Size";
+ case BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN:
+ return "Invalid Attribute value len";
+ case BT_ATT_ERROR_UNLIKELY:
+ return "Unlikely Error";
+ case BT_ATT_ERROR_INSUFFICIENT_ENCRYPTION:
+ return "Insufficient Encryption";
+ case BT_ATT_ERROR_UNSUPPORTED_GROUP_TYPE:
+ return "Group type Not Supported";
+ case BT_ATT_ERROR_INSUFFICIENT_RESOURCES:
+ return "Insufficient Resources";
+ default:
+ return "Unknown error type";
+ }
+}
+
static void att_disconnect_cb(int err, void *user_data)
{
printf("Device disconnected: %s\n", strerror(err));
static struct client *client_create(int fd, uint16_t mtu)
{
struct client *cli;
- struct bt_att *att;
cli = new0(struct client, 1);
if (!cli) {
return NULL;
}
- att = bt_att_new(fd);
- if (!att) {
+ cli->att = bt_att_new(fd);
+ if (!cli->att) {
fprintf(stderr, "Failed to initialze ATT transport layer\n");
- bt_att_unref(att);
+ bt_att_unref(cli->att);
free(cli);
return NULL;
}
- if (!bt_att_set_close_on_unref(att, true)) {
+ if (!bt_att_set_close_on_unref(cli->att, true)) {
fprintf(stderr, "Failed to set up ATT transport layer\n");
- bt_att_unref(att);
+ bt_att_unref(cli->att);
free(cli);
return NULL;
}
- if (!bt_att_register_disconnect(att, att_disconnect_cb, NULL, NULL)) {
+ if (!bt_att_register_disconnect(cli->att, att_disconnect_cb, NULL,
+ NULL)) {
fprintf(stderr, "Failed to set ATT disconnect handler\n");
- bt_att_unref(att);
+ bt_att_unref(cli->att);
free(cli);
return NULL;
}
cli->db = gatt_db_new();
if (!cli->db) {
fprintf(stderr, "Failed to create GATT database\n");
- bt_att_unref(att);
+ bt_att_unref(cli->att);
free(cli);
return NULL;
}
- cli->gatt = bt_gatt_client_new(cli->db, att, mtu);
+ cli->gatt = bt_gatt_client_new(cli->db, cli->att, mtu);
if (!cli->gatt) {
fprintf(stderr, "Failed to create GATT client\n");
gatt_db_unref(cli->db);
- bt_att_unref(att);
+ bt_att_unref(cli->att);
free(cli);
return NULL;
}
NULL, NULL);
if (verbose) {
- bt_att_set_debug(att, att_debug_cb, "att: ", NULL);
+ bt_att_set_debug(cli->att, att_debug_cb, "att: ", NULL);
bt_gatt_client_set_debug(cli->gatt, gatt_debug_cb, "gatt: ",
NULL);
}
NULL);
/* bt_gatt_client already holds a reference */
- bt_att_unref(att);
gatt_db_unref(cli->db);
return cli;
static void client_destroy(struct client *cli)
{
bt_gatt_client_unref(cli->gatt);
+ bt_att_unref(cli->att);
+ free(cli);
}
static void print_uuid(const bt_uuid_t *uuid)
}
value = malloc(sizeof(uint16_t) * argc);
+ if (!value) {
+ printf("Failed to construct value\n");
+ return;
+ }
for (i = 0; i < argc; i++) {
value[i] = strtol(argv[i], &endptr, 0);
int i;
if (!success) {
- PRLOG("\nRead request failed: 0x%02x\n", att_ecode);
+ PRLOG("\nRead request failed: %s (0x%02x)\n",
+ ecode_to_string(att_ecode), att_ecode);
return;
}
printf("Usage: write-value [options] <value_handle> <value>\n"
"Options:\n"
"\t-w, --without-response\tWrite without response\n"
+ "\t-s, --signed-write\tSigned write command\n"
"e.g.:\n"
"\twrite-value 0x0001 00 01 00\n");
}
static struct option write_value_options[] = {
{ "without-response", 0, 0, 'w' },
+ { "signed-write", 0, 0, 's' },
{ }
};
if (success) {
PRLOG("\nWrite successful\n");
} else {
- PRLOG("\nWrite failed: 0x%02x\n", att_ecode);
+ PRLOG("\nWrite failed: %s (0x%02x)\n",
+ ecode_to_string(att_ecode), att_ecode);
}
}
int length;
uint8_t *value = NULL;
bool without_response = false;
+ bool signed_write = false;
if (!bt_gatt_client_is_ready(cli->gatt)) {
printf("GATT client not initialized\n");
optind = 0;
argv[0] = "write-value";
- while ((opt = getopt_long(argc, argv, "+w", write_value_options,
+ while ((opt = getopt_long(argc, argv, "+ws", write_value_options,
NULL)) != -1) {
switch (opt) {
case 'w':
without_response = true;
break;
+ case 's':
+ signed_write = true;
+ break;
default:
write_value_usage();
return;
if (without_response) {
if (!bt_gatt_client_write_without_response(cli->gatt, handle,
- false, value, length)) {
+ signed_write, value, length)) {
printf("Failed to initiate write without response "
"procedure\n");
goto done;
} else if (reliable_error) {
PRLOG("Reliable write not verified\n");
} else {
- PRLOG("Write failed: 0x%02x\n", att_ecode);
+ PRLOG("\nWrite failed: %s (0x%02x)\n",
+ ecode_to_string(att_ecode), att_ecode);
}
}
PRLOG("\n");
}
-static void register_notify_cb(unsigned int id, uint16_t att_ecode,
- void *user_data)
+static void register_notify_cb(uint16_t att_ecode, void *user_data)
{
- if (!id) {
+ if (att_ecode) {
PRLOG("Failed to register notify handler "
"- error code: 0x%02x\n", att_ecode);
return;
}
- PRLOG("Registered notify handler with id: %u\n", id);
+ PRLOG("Registered notify handler!");
}
static void cmd_register_notify(struct client *cli, char *cmd_str)
char *argv[2];
int argc = 0;
uint16_t value_handle;
+ unsigned int id;
char *endptr = NULL;
if (!bt_gatt_client_is_ready(cli->gatt)) {
return;
}
- if (!bt_gatt_client_register_notify(cli->gatt, value_handle,
+ id = bt_gatt_client_register_notify(cli->gatt, value_handle,
register_notify_cb,
- notify_cb, NULL, NULL))
+ notify_cb, NULL, NULL);
+ if (!id) {
printf("Failed to register notify handler\n");
+ return;
+ }
- printf("\n");
+ PRLOG("Registering notify handler with id: %u\n", id);
}
static void unregister_notify_usage(void)
printf("Unregistered notify handler with id: %u\n", id);
}
+static void set_sec_level_usage(void)
+{
+ printf("Usage: set_sec_level <level>\n"
+ "level: 1-3\n"
+ "e.g.:\n"
+ "\tset-sec-level 2\n");
+}
+
+static void cmd_set_sec_level(struct client *cli, char *cmd_str)
+{
+ char *argvbuf[1];
+ char **argv = argvbuf;
+ int argc = 0;
+ char *endptr = NULL;
+ int level;
+
+ if (!bt_gatt_client_is_ready(cli->gatt)) {
+ printf("GATT client not initialized\n");
+ return;
+ }
+
+ if (!parse_args(cmd_str, 1, argv, &argc)) {
+ printf("Too many arguments\n");
+ set_sec_level_usage();
+ return;
+ }
+
+ if (argc < 1) {
+ set_sec_level_usage();
+ return;
+ }
+
+ level = strtol(argv[0], &endptr, 0);
+ if (!endptr || *endptr != '\0' || level < 1 || level > 3) {
+ printf("Invalid level: %s\n", argv[0]);
+ return;
+ }
+
+ if (!bt_gatt_client_set_sec_level(cli->gatt, level))
+ printf("Could not set sec level\n");
+ else
+ printf("Setting security level %d success\n", level);
+}
+
+static void cmd_get_sec_level(struct client *cli, char *cmd_str)
+{
+ int level;
+
+ if (!bt_gatt_client_is_ready(cli->gatt)) {
+ printf("GATT client not initialized\n");
+ return;
+ }
+
+ level = bt_gatt_client_get_sec_level(cli->gatt);
+ if (level < 0)
+ printf("Could not set sec level\n");
+ else
+ printf("Security level: %u\n", level);
+}
+
+static bool convert_sign_key(char *optarg, uint8_t key[16])
+{
+ int i;
+
+ if (strlen(optarg) != 32) {
+ printf("sign-key length is invalid\n");
+ return false;
+ }
+
+ for (i = 0; i < 16; i++) {
+ if (sscanf(optarg + (i * 2), "%2hhx", &key[i]) != 1)
+ return false;
+ }
+
+ return true;
+}
+
+static void set_sign_key_usage(void)
+{
+ printf("Usage: set-sign-key [options]\nOptions:\n"
+ "\t -c, --sign-key <csrk>\tCSRK\n"
+ "e.g.:\n"
+ "\tset-sign-key -c D8515948451FEA320DC05A2E88308188\n");
+}
+
+static bool local_counter(uint32_t *sign_cnt, void *user_data)
+{
+ static uint32_t cnt = 0;
+
+ *sign_cnt = cnt++;
+
+ return true;
+}
+
+static void cmd_set_sign_key(struct client *cli, char *cmd_str)
+{
+ char *argv[3];
+ int argc = 0;
+ uint8_t key[16];
+
+ memset(key, 0, 16);
+
+ if (!parse_args(cmd_str, 2, argv, &argc)) {
+ set_sign_key_usage();
+ return;
+ }
+
+ if (argc != 2) {
+ set_sign_key_usage();
+ return;
+ }
+
+ if (!strcmp(argv[0], "-c") || !strcmp(argv[0], "--sign-key")) {
+ if (convert_sign_key(argv[1], key))
+ bt_att_set_local_key(cli->att, key, local_counter, cli);
+ } else
+ set_sign_key_usage();
+}
+
static void cmd_help(struct client *cli, char *cmd_str);
typedef void (*command_func_t)(struct client *cli, char *cmd_str);
"\tSubscribe to not/ind from a characteristic" },
{ "unregister-notify", cmd_unregister_notify,
"Unregister a not/ind session"},
+ { "set-sec-level", cmd_set_sec_level,
+ "Set security level on le connection"},
+ { "get-sec-level", cmd_get_sec_level,
+ "Get security level on le connection"},
+ { "set-sign-key", cmd_set_sign_key,
+ "\tSet signing key for signed write command"},
{ }
};
#include <unistd.h>
#include <errno.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/hci_lib.h>
-#include <bluetooth/l2cap.h>
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+#include "lib/l2cap.h"
#include "lib/uuid.h"
-#include "monitor/mainloop.h"
+#include "src/shared/mainloop.h"
#include "src/shared/util.h"
#include "src/shared/att.h"
#include "src/shared/queue.h"
struct server {
int fd;
+ struct bt_att *att;
struct gatt_db *db;
struct bt_gatt_server *gatt;
static void gap_device_name_read_cb(struct gatt_db_attribute *attrib,
unsigned int id, uint16_t offset,
- uint8_t opcode, bdaddr_t *bdaddr,
+ uint8_t opcode, struct bt_att *att,
void *user_data)
{
struct server *server = user_data;
static void gap_device_name_write_cb(struct gatt_db_attribute *attrib,
unsigned int id, uint16_t offset,
const uint8_t *value, size_t len,
- uint8_t opcode, bdaddr_t *bdaddr,
+ uint8_t opcode, struct bt_att *att,
void *user_data)
{
struct server *server = user_data;
static void gap_device_name_ext_prop_read_cb(struct gatt_db_attribute *attrib,
unsigned int id, uint16_t offset,
- uint8_t opcode, bdaddr_t *bdaddr,
+ uint8_t opcode, struct bt_att *att,
void *user_data)
{
uint8_t value[2];
static void gatt_service_changed_cb(struct gatt_db_attribute *attrib,
unsigned int id, uint16_t offset,
- uint8_t opcode, bdaddr_t *bdaddr,
+ uint8_t opcode, struct bt_att *att,
void *user_data)
{
PRLOG("Service Changed Read called\n");
static void gatt_svc_chngd_ccc_read_cb(struct gatt_db_attribute *attrib,
unsigned int id, uint16_t offset,
- uint8_t opcode, bdaddr_t *bdaddr,
+ uint8_t opcode, struct bt_att *att,
void *user_data)
{
struct server *server = user_data;
static void gatt_svc_chngd_ccc_write_cb(struct gatt_db_attribute *attrib,
unsigned int id, uint16_t offset,
const uint8_t *value, size_t len,
- uint8_t opcode, bdaddr_t *bdaddr,
+ uint8_t opcode, struct bt_att *att,
void *user_data)
{
struct server *server = user_data;
static void hr_msrmt_ccc_read_cb(struct gatt_db_attribute *attrib,
unsigned int id, uint16_t offset,
- uint8_t opcode, bdaddr_t *bdaddr,
+ uint8_t opcode, struct bt_att *att,
void *user_data)
{
struct server *server = user_data;
static void hr_msrmt_ccc_write_cb(struct gatt_db_attribute *attrib,
unsigned int id, uint16_t offset,
const uint8_t *value, size_t len,
- uint8_t opcode, bdaddr_t *bdaddr,
+ uint8_t opcode, struct bt_att *att,
void *user_data)
{
struct server *server = user_data;
static void hr_control_point_write_cb(struct gatt_db_attribute *attrib,
unsigned int id, uint16_t offset,
const uint8_t *value, size_t len,
- uint8_t opcode, bdaddr_t *bdaddr,
+ uint8_t opcode, struct bt_att *att,
void *user_data)
{
struct server *server = user_data;
static struct server *server_create(int fd, uint16_t mtu, bool hr_visible)
{
struct server *server;
- struct bt_att *att;
size_t name_len = strlen(test_device_name);
server = new0(struct server, 1);
return NULL;
}
- att = bt_att_new(fd);
- if (!att) {
+ server->att = bt_att_new(fd);
+ if (!server->att) {
fprintf(stderr, "Failed to initialze ATT transport layer\n");
goto fail;
}
- if (!bt_att_set_close_on_unref(att, true)) {
+ if (!bt_att_set_close_on_unref(server->att, true)) {
fprintf(stderr, "Failed to set up ATT transport layer\n");
goto fail;
}
- if (!bt_att_register_disconnect(att, att_disconnect_cb, NULL, NULL)) {
+ if (!bt_att_register_disconnect(server->att, att_disconnect_cb, NULL,
+ NULL)) {
fprintf(stderr, "Failed to set ATT disconnect handler\n");
goto fail;
}
goto fail;
}
- server->gatt = bt_gatt_server_new(server->db, att, mtu);
+ server->gatt = bt_gatt_server_new(server->db, server->att, mtu);
if (!server->gatt) {
fprintf(stderr, "Failed to create GATT server\n");
goto fail;
server->hr_visible = hr_visible;
if (verbose) {
- bt_att_set_debug(att, att_debug_cb, "att: ", NULL);
+ bt_att_set_debug(server->att, att_debug_cb, "att: ", NULL);
bt_gatt_server_set_debug(server->gatt, gatt_debug_cb,
"server: ", NULL);
}
srand(time(NULL));
/* bt_gatt_server already holds a reference */
- bt_att_unref(att);
populate_db(server);
return server;
fail:
gatt_db_unref(server->db);
free(server->device_name);
- bt_att_unref(att);
+ bt_att_unref(server->att);
free(server);
return NULL;
"\t-s, --security-level <sec>\tSet security level (low|"
"medium|high)\n"
"\t-v, --verbose\t\t\tEnable extra logging\n"
- "\t-r, --heart-rate\t\tEnable Heart Rate service"
+ "\t-r, --heart-rate\t\tEnable Heart Rate service\n"
"\t-h, --help\t\t\tDisplay help\n");
}
gatt_db_foreach_service(server->db, NULL, print_service, server);
}
+static bool convert_sign_key(char *optarg, uint8_t key[16])
+{
+ int i;
+
+ if (strlen(optarg) != 32) {
+ printf("sign-key length is invalid\n");
+ return false;
+ }
+
+ for (i = 0; i < 16; i++) {
+ if (sscanf(optarg + (i * 2), "%2hhx", &key[i]) != 1)
+ return false;
+ }
+
+ return true;
+}
+
+static void set_sign_key_usage(void)
+{
+ printf("Usage: set-sign-key [options]\nOptions:\n"
+ "\t -c, --sign-key <remote csrk>\tRemote CSRK\n"
+ "e.g.:\n"
+ "\tset-sign-key -c D8515948451FEA320DC05A2E88308188\n");
+}
+
+static bool remote_counter(uint32_t *sign_cnt, void *user_data)
+{
+ static uint32_t cnt = 0;
+
+ if (*sign_cnt < cnt)
+ return false;
+
+ cnt = *sign_cnt;
+
+ return true;
+}
+
+static void cmd_set_sign_key(struct server *server, char *cmd_str)
+{
+ char *argv[3];
+ int argc = 0;
+ uint8_t key[16];
+
+ memset(key, 0, 16);
+
+ if (!parse_args(cmd_str, 2, argv, &argc)) {
+ set_sign_key_usage();
+ return;
+ }
+
+ if (argc != 2) {
+ set_sign_key_usage();
+ return;
+ }
+
+ if (!strcmp(argv[0], "-c") || !strcmp(argv[0], "--sign-key")) {
+ if (convert_sign_key(argv[1], key))
+ bt_att_set_remote_key(server->att, key, remote_counter,
+ server);
+ } else
+ set_sign_key_usage();
+}
+
static void cmd_help(struct server *server, char *cmd_str);
typedef void (*command_func_t)(struct server *server, char *cmd_str);
{ "notify", cmd_notify, "\tSend handle-value notification" },
{ "heart-rate", cmd_heart_rate, "\tHide/Unhide Heart Rate Service" },
{ "services", cmd_services, "\tEnumerate all services" },
+ { "set-sign-key", cmd_set_sign_key,
+ "\tSet remote signing key for signed write command"},
{ }
};
#include <sys/ioctl.h>
#include <sys/socket.h>
-#include "monitor/mainloop.h"
#include "monitor/bt.h"
+#include "src/shared/mainloop.h"
#include "src/shared/timeout.h"
#include "src/shared/util.h"
#include "src/shared/hci.h"
#include <glib.h>
-#include <bluetooth/bluetooth.h>
+#include "lib/bluetooth.h"
#include "btio/btio.h"
#endif
#include <stdio.h>
+#include <stdarg.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <poll.h>
#include <getopt.h>
#include <stdbool.h>
+#include <wordexp.h>
+#include <ctype.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/hci_lib.h>
-#include <bluetooth/sdp.h>
-#include <bluetooth/sdp_lib.h>
+#include <readline/readline.h>
+#include <readline/history.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
#include "src/uuid-helper.h"
#include "lib/mgmt.h"
-#include "monitor/mainloop.h"
+#include "client/display.h"
+#include "src/shared/mainloop.h"
+#include "src/shared/io.h"
#include "src/shared/util.h"
#include "src/shared/mgmt.h"
-#include "src/shared/gap.h"
-static bool monitor = false;
+static struct mgmt *mgmt = NULL;
+static uint16_t mgmt_index = MGMT_INDEX_NONE;
+
static bool discovery = false;
static bool resolve_names = true;
+static bool interactive = false;
+
+static char *saved_prompt = NULL;
+static int saved_point = 0;
-static int pending = 0;
+static struct {
+ uint16_t index;
+ uint16_t req;
+ struct mgmt_addr_info addr;
+} prompt = {
+ .index = MGMT_INDEX_NONE,
+};
+
+static int pending_index = 0;
#ifndef MIN
#define MIN(x, y) ((x) < (y) ? (x) : (y))
#endif
-static size_t convert_hexstr(const char *hexstr, uint8_t *buf, size_t buflen)
+#define PROMPT_ON COLOR_BLUE "[mgmt]" COLOR_OFF "# "
+
+static void update_prompt(uint16_t index)
+{
+ char str[32];
+
+ if (index == MGMT_INDEX_NONE)
+ snprintf(str, sizeof(str), "%s# ",
+ COLOR_BLUE "[mgmt]" COLOR_OFF);
+ else
+ snprintf(str, sizeof(str),
+ COLOR_BLUE "[hci%u]" COLOR_OFF "# ", index);
+
+ if (saved_prompt) {
+ free(saved_prompt);
+ saved_prompt = strdup(str);
+ return;
+ }
+
+ rl_set_prompt(str);
+}
+
+static void noninteractive_quit(int status)
+{
+ if (interactive)
+ return;
+
+ if (status == EXIT_SUCCESS)
+ mainloop_exit_success();
+ else
+ mainloop_exit_failure();
+}
+
+#define print(fmt, arg...) do { \
+ if (interactive) \
+ rl_printf(fmt "\n", ## arg); \
+ else \
+ printf(fmt "\n", ## arg); \
+} while (0)
+
+#define error(fmt, arg...) do { \
+ if (interactive) \
+ rl_printf(COLOR_RED fmt "\n" COLOR_OFF, ## arg); \
+ else \
+ fprintf(stderr, fmt "\n", ## arg); \
+} while (0)
+
+static size_t hex2bin(const char *hexstr, uint8_t *buf, size_t buflen)
{
size_t i, len;
return len;
}
+static size_t bin2hex(const uint8_t *buf, size_t buflen, char *str,
+ size_t strlen)
+{
+ size_t i;
+
+ for (i = 0; i < buflen && i < (strlen / 2); i++)
+ sprintf(str + (i * 2), "%02x", buf[i]);
+
+ return i;
+}
+
static bool load_identity(uint16_t index, struct mgmt_irk_info *irk)
{
char identity_path[PATH_MAX];
fp = fopen(identity_path, "r");
if (!fp) {
- perror("Failed to open identity file");
+ error("Failed to open identity file: %s", strerror(errno));
return false;
}
return false;
str2ba(addr, &irk->addr.bdaddr);
- convert_hexstr(key, irk->val, sizeof(irk->val));
+ hex2bin(key, irk->val, sizeof(irk->val));
free(addr);
free(key);
irk->addr.type = BDADDR_LE_RANDOM;
break;
default:
- fprintf(stderr, "Invalid address type %u\n", type);
+ error("Invalid address type %u", type);
return false;
}
const struct mgmt_ev_controller_error *ev = param;
if (len < sizeof(*ev)) {
- fprintf(stderr,
- "Too short (%u bytes) controller error event\n", len);
+ error("Too short (%u bytes) controller error event", len);
return;
}
- if (monitor)
- printf("hci%u error 0x%02x\n", index, ev->error_code);
+ print("hci%u error 0x%02x", index, ev->error_code);
}
static void index_added(uint16_t index, uint16_t len,
const void *param, void *user_data)
{
- if (monitor)
- printf("hci%u added\n", index);
+ print("hci%u added", index);
}
static void index_removed(uint16_t index, uint16_t len,
const void *param, void *user_data)
{
- if (monitor)
- printf("hci%u removed\n", index);
+ print("hci%u removed", index);
}
static void unconf_index_added(uint16_t index, uint16_t len,
const void *param, void *user_data)
{
- if (monitor)
- printf("hci%u added (unconfigured)\n", index);
+ print("hci%u added (unconfigured)", index);
}
static void unconf_index_removed(uint16_t index, uint16_t len,
const void *param, void *user_data)
{
- if (monitor)
- printf("hci%u removed (unconfigured)\n", index);
+ print("hci%u removed (unconfigured)", index);
}
static const char *options_str[] = {
"public-address",
};
-static void print_options(uint32_t options)
+static const char *options2str(uint32_t options)
{
+ static char str[256];
unsigned i;
+ int off;
+
+ off = 0;
+ str[0] = '\0';
for (i = 0; i < NELEM(options_str); i++) {
if ((options & (1 << i)) != 0)
- printf("%s ", options_str[i]);
+ off += snprintf(str + off, sizeof(str) - off, "%s ",
+ options_str[i]);
}
+
+ return str;
}
static void new_config_options(uint16_t index, uint16_t len,
const uint32_t *ev = param;
if (len < sizeof(*ev)) {
- fprintf(stderr, "Too short new_config_options event (%u)\n", len);
+ error("Too short new_config_options event (%u)", len);
return;
}
- if (monitor) {
- printf("hci%u new_config_options: ", index);
- print_options(get_le32(ev));
- printf("\n");
- }
+ print("hci%u new_config_options: %s", index, options2str(get_le32(ev)));
}
static const char *settings_str[] = {
"debug-keys",
"privacy",
"configuration",
+ "static-addr",
};
-static void print_settings(uint32_t settings)
+static const char *settings2str(uint32_t settings)
{
+ static char str[256];
unsigned i;
+ int off;
+
+ off = 0;
+ str[0] = '\0';
for (i = 0; i < NELEM(settings_str); i++) {
if ((settings & (1 << i)) != 0)
- printf("%s ", settings_str[i]);
+ off += snprintf(str + off, sizeof(str) - off, "%s ",
+ settings_str[i]);
}
+
+ return str;
}
static void new_settings(uint16_t index, uint16_t len,
const uint32_t *ev = param;
if (len < sizeof(*ev)) {
- fprintf(stderr, "Too short new_settings event (%u)\n", len);
+ error("Too short new_settings event (%u)", len);
return;
}
- if (monitor) {
- printf("hci%u new_settings: ", index);
- print_settings(get_le32(ev));
- printf("\n");
- }
+ print("hci%u new_settings: %s", index, settings2str(get_le32(ev)));
}
static void discovering(uint16_t index, uint16_t len, const void *param,
const struct mgmt_ev_discovering *ev = param;
if (len < sizeof(*ev)) {
- fprintf(stderr, "Too short (%u bytes) discovering event\n",
- len);
+ error("Too short (%u bytes) discovering event", len);
return;
}
- if (ev->discovering == 0 && discovery) {
- mainloop_quit();
- return;
- }
+ print("hci%u type %u discovering %s", index, ev->type,
+ ev->discovering ? "on" : "off");
- if (monitor)
- printf("hci%u type %u discovering %s\n", index,
- ev->type, ev->discovering ? "on" : "off");
+ if (ev->discovering == 0 && discovery)
+ return noninteractive_quit(EXIT_SUCCESS);
}
static void new_link_key(uint16_t index, uint16_t len, const void *param,
void *user_data)
{
const struct mgmt_ev_new_link_key *ev = param;
+ char addr[18];
if (len != sizeof(*ev)) {
- fprintf(stderr, "Invalid new_link_key length (%u bytes)\n",
- len);
+ error("Invalid new_link_key length (%u bytes)", len);
return;
}
- if (monitor) {
- char addr[18];
- 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);
- }
+ ba2str(&ev->key.addr.bdaddr, addr);
+ print("hci%u new_link_key %s type 0x%02x pin_len %d store_hint %u",
+ index, addr, ev->key.type, ev->key.pin_len, ev->store_hint);
}
static const char *typestr(uint8_t type)
{
- const char *str[] = { "BR/EDR", "LE Public", "LE Random" };
+ static const char *str[] = { "BR/EDR", "LE Public", "LE Random" };
if (type <= BDADDR_LE_RANDOM)
return str[type];
{
const struct mgmt_ev_device_connected *ev = param;
uint16_t eir_len;
+ char addr[18];
if (len < sizeof(*ev)) {
- fprintf(stderr,
- "Invalid connected event length (%u bytes)\n", len);
+ error("Invalid connected event length (%u bytes)", len);
return;
}
eir_len = 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);
+ error("Invalid connected event length (%u != eir_len %u)",
+ len, eir_len);
return;
}
- if (monitor) {
- char addr[18];
- ba2str(&ev->addr.bdaddr, addr);
- printf("hci%u %s type %s connected eir_len %u\n", index, addr,
+ ba2str(&ev->addr.bdaddr, addr);
+ print("hci%u %s type %s connected eir_len %u", index, addr,
typestr(ev->addr.type), eir_len);
- }
+}
+
+static void release_prompt(void)
+{
+ if (!interactive)
+ return;
+
+ memset(&prompt, 0, sizeof(prompt));
+ prompt.index = MGMT_INDEX_NONE;
+
+ if (!saved_prompt)
+ return;
+
+ /* This will cause rl_expand_prompt to re-run over the last prompt,
+ * but our prompt doesn't expand anyway.
+ */
+ rl_set_prompt(saved_prompt);
+ rl_replace_line("", 0);
+ rl_point = saved_point;
+ rl_redisplay();
+
+ free(saved_prompt);
+ saved_prompt = NULL;
}
static void disconnected(uint16_t index, uint16_t len, const void *param,
void *user_data)
{
const struct mgmt_ev_device_disconnected *ev = param;
+ char addr[18];
+ uint8_t reason;
if (len < sizeof(struct mgmt_addr_info)) {
- fprintf(stderr,
- "Invalid disconnected event length (%u bytes)\n", len);
+ error("Invalid disconnected event length (%u bytes)", len);
return;
}
- if (monitor) {
- char addr[18];
- uint8_t reason;
+ if (!memcmp(&ev->addr, &prompt.addr, sizeof(ev->addr)))
+ release_prompt();
- if (len < sizeof(*ev))
- reason = MGMT_DEV_DISCONN_UNKNOWN;
- else
- reason = ev->reason;
+ if (len < sizeof(*ev))
+ reason = MGMT_DEV_DISCONN_UNKNOWN;
+ else
+ reason = ev->reason;
- ba2str(&ev->addr.bdaddr, addr);
- printf("hci%u %s type %s disconnected with reason %u\n",
- index, addr, typestr(ev->addr.type), reason);
- }
+ ba2str(&ev->addr.bdaddr, addr);
+ print("hci%u %s type %s disconnected with reason %u",
+ index, addr, typestr(ev->addr.type), reason);
}
static void conn_failed(uint16_t index, uint16_t len, const void *param,
void *user_data)
{
const struct mgmt_ev_connect_failed *ev = param;
+ char addr[18];
if (len != sizeof(*ev)) {
- fprintf(stderr,
- "Invalid connect_failed event length (%u bytes)\n", len);
+ error("Invalid connect_failed event length (%u bytes)", len);
return;
}
- if (monitor) {
- char addr[18];
- ba2str(&ev->addr.bdaddr, addr);
- printf("hci%u %s type %s connect failed (status 0x%02x, %s)\n",
- index, addr, typestr(ev->addr.type), ev->status,
- mgmt_errstr(ev->status));
- }
+ ba2str(&ev->addr.bdaddr, addr);
+ print("hci%u %s type %s connect failed (status 0x%02x, %s)",
+ index, addr, typestr(ev->addr.type), ev->status,
+ mgmt_errstr(ev->status));
}
static void auth_failed(uint16_t index, uint16_t len, const void *param,
void *user_data)
{
const struct mgmt_ev_auth_failed *ev = param;
+ char addr[18];
if (len != sizeof(*ev)) {
- fprintf(stderr,
- "Invalid auth_failed event length (%u bytes)\n", len);
+ error("Invalid auth_failed event length (%u bytes)", len);
return;
}
- if (monitor) {
- char addr[18];
- ba2str(&ev->addr.bdaddr, addr);
- printf("hci%u %s auth failed with status 0x%02x (%s)\n",
+ if (!memcmp(&ev->addr, &prompt.addr, sizeof(ev->addr)))
+ release_prompt();
+
+ ba2str(&ev->addr.bdaddr, addr);
+ print("hci%u %s auth failed with status 0x%02x (%s)",
index, addr, ev->status, mgmt_errstr(ev->status));
- }
}
static void local_name_changed(uint16_t index, uint16_t len, const void *param,
const struct mgmt_ev_local_name_changed *ev = param;
if (len != sizeof(*ev)) {
- fprintf(stderr,
- "Invalid local_name_changed length (%u bytes)\n", len);
+ error("Invalid local_name_changed length (%u bytes)", len);
return;
}
- if (monitor)
- printf("hci%u name changed: %s\n", index, ev->name);
+ print("hci%u name changed: %s", index, ev->name);
}
static void confirm_name_rsp(uint8_t status, uint16_t len,
char addr[18];
if (len == 0 && status != 0) {
- fprintf(stderr,
- "confirm_name failed with status 0x%02x (%s)\n",
- status, mgmt_errstr(status));
+ error("confirm_name failed with status 0x%02x (%s)", status,
+ mgmt_errstr(status));
return;
}
if (len != sizeof(*rp)) {
- fprintf(stderr, "confirm_name rsp length %u instead of %zu\n",
- len, sizeof(*rp));
+ error("confirm_name rsp length %u instead of %zu",
+ len, sizeof(*rp));
return;
}
ba2str(&rp->addr.bdaddr, addr);
if (status != 0)
- fprintf(stderr, "confirm_name for %s failed: 0x%02x (%s)\n",
+ error("confirm_name for %s failed: 0x%02x (%s)",
addr, status, mgmt_errstr(status));
else
- printf("confirm_name succeeded for %s\n", addr);
+ print("confirm_name succeeded for %s", addr);
}
static char *eir_get_name(const uint8_t *eir, uint16_t eir_len)
uint32_t flags;
if (len < sizeof(*ev)) {
- fprintf(stderr,
- "Too short device_found length (%u bytes)\n", len);
+ error("Too short device_found length (%u bytes)", len);
return;
}
eir_len = get_le16(&ev->eir_len);
if (len != sizeof(*ev) + eir_len) {
- fprintf(stderr, "dev_found: expected %zu bytes, got %u bytes\n",
+ error("dev_found: expected %zu bytes, got %u bytes",
sizeof(*ev) + eir_len, len);
return;
}
- if (monitor || discovery) {
+ if (discovery) {
char addr[18], *name;
ba2str(&ev->addr.bdaddr, addr);
- printf("hci%u dev_found: %s type %s rssi %d "
+ print("hci%u dev_found: %s type %s rssi %d "
"flags 0x%04x ", index, addr,
typestr(ev->addr.type), ev->rssi, flags);
if (ev->addr.type != BDADDR_BREDR)
- printf("AD flags 0x%02x ",
+ print("AD flags 0x%02x ",
eir_get_flags(ev->eir, eir_len));
name = eir_get_name(ev->eir, eir_len);
if (name)
- printf("name %s\n", name);
+ print("name %s", name);
else
- printf("eir_len %u\n", eir_len);
+ print("eir_len %u", eir_len);
free(name);
}
void *user_data)
{
if (status != 0) {
- fprintf(stderr,
- "PIN Code reply failed with status 0x%02x (%s)\n",
+ error("PIN Code reply failed with status 0x%02x (%s)",
status, mgmt_errstr(status));
- mainloop_quit();
- return;
+ return noninteractive_quit(EXIT_FAILURE);
}
- printf("PIN Reply successful\n");
+ print("PIN Reply successful");
}
static int mgmt_pin_reply(struct mgmt *mgmt, uint16_t index,
void *user_data)
{
if (status != 0) {
- fprintf(stderr,
- "PIN Neg reply failed with status 0x%02x (%s)\n",
+ error("PIN Neg reply failed with status 0x%02x (%s)",
status, mgmt_errstr(status));
- mainloop_quit();
- return;
+ return noninteractive_quit(EXIT_FAILURE);
}
- printf("PIN Negative Reply successful\n");
+ print("PIN Negative Reply successful");
}
static int mgmt_pin_neg_reply(struct mgmt *mgmt, uint16_t index,
sizeof(cp), &cp, pin_neg_rsp, NULL, NULL);
}
-static void request_pin(uint16_t index, uint16_t len, const void *param,
- void *user_data)
-{
- const struct mgmt_ev_pin_code_request *ev = param;
- struct mgmt *mgmt = user_data;
- char pin[18];
- size_t pin_len;
-
- if (len != sizeof(*ev)) {
- fprintf(stderr,
- "Invalid pin_code request length (%u bytes)\n", len);
- return;
- }
-
- if (monitor) {
- char addr[18];
- ba2str(&ev->addr.bdaddr, addr);
- printf("hci%u %s request PIN\n", index, addr);
- }
-
- printf("PIN Request (press enter to reject) >> ");
- fflush(stdout);
-
- memset(pin, 0, sizeof(pin));
-
- if (fgets(pin, sizeof(pin), stdin) == NULL || pin[0] == '\n') {
- mgmt_pin_neg_reply(mgmt, index, &ev->addr);
- return;
- }
-
- pin_len = strlen(pin);
- if (pin[pin_len - 1] == '\n') {
- pin[pin_len - 1] = '\0';
- pin_len--;
- }
-
- mgmt_pin_reply(mgmt, index, &ev->addr, pin, pin_len);
-}
-
static void confirm_rsp(uint8_t status, uint16_t len, const void *param,
void *user_data)
{
if (status != 0) {
- fprintf(stderr,
- "User Confirm reply failed. status 0x%02x (%s)\n",
+ error("User Confirm reply failed. status 0x%02x (%s)",
status, mgmt_errstr(status));
- mainloop_quit();
- return;
+ return noninteractive_quit(EXIT_FAILURE);
}
- printf("User Confirm Reply successful\n");
+ print("User Confirm Reply successful");
}
static int mgmt_confirm_reply(struct mgmt *mgmt, uint16_t index,
void *user_data)
{
if (status != 0) {
- fprintf(stderr,
- "Confirm Neg reply failed. status 0x%02x (%s)\n",
+ error("Confirm Neg reply failed. status 0x%02x (%s)",
status, mgmt_errstr(status));
- mainloop_quit();
- return;
+ return noninteractive_quit(EXIT_FAILURE);
}
- printf("User Confirm Negative Reply successful\n");
+ print("User Confirm Negative Reply successful");
}
static int mgmt_confirm_neg_reply(struct mgmt *mgmt, uint16_t index,
sizeof(cp), &cp, confirm_neg_rsp, NULL, NULL);
}
-
-static void user_confirm(uint16_t index, uint16_t len, const void *param,
- void *user_data)
-{
- const struct mgmt_ev_user_confirm_request *ev = param;
- struct mgmt *mgmt = user_data;
- char rsp[5];
- size_t rsp_len;
- uint32_t val;
- char addr[18];
-
- if (len != sizeof(*ev)) {
- fprintf(stderr,
- "Invalid user_confirm request length (%u)\n", len);
- return;
- }
-
- ba2str(&ev->addr.bdaddr, addr);
- val = get_le32(&ev->value);
-
- if (monitor)
- printf("hci%u %s User Confirm %06u hint %u\n", index, addr,
- val, ev->confirm_hint);
-
- if (ev->confirm_hint)
- printf("Accept pairing with %s (yes/no) >> ", addr);
- else
- printf("Confirm value %06u for %s (yes/no) >> ", val, addr);
-
- fflush(stdout);
-
- memset(rsp, 0, sizeof(rsp));
-
- if (fgets(rsp, sizeof(rsp), stdin) == NULL || rsp[0] == '\n') {
- mgmt_confirm_neg_reply(mgmt, index, &ev->addr);
- return;
- }
-
- rsp_len = strlen(rsp);
- if (rsp[rsp_len - 1] == '\n')
- rsp[rsp_len - 1] = '\0';
-
- if (rsp[0] == 'y' || rsp[0] == 'Y')
- mgmt_confirm_reply(mgmt, index, &ev->addr);
- else
- mgmt_confirm_neg_reply(mgmt, index, &ev->addr);
-}
-
static void passkey_rsp(uint8_t status, uint16_t len, const void *param,
void *user_data)
{
if (status != 0) {
- fprintf(stderr,
- "User Passkey reply failed. status 0x%02x (%s)\n",
+ error("User Passkey reply failed. status 0x%02x (%s)",
status, mgmt_errstr(status));
- mainloop_quit();
- return;
+ return noninteractive_quit(EXIT_FAILURE);
}
- printf("User Passkey Reply successful\n");
+ print("User Passkey Reply successful");
}
static int mgmt_passkey_reply(struct mgmt *mgmt, uint16_t index,
void *user_data)
{
if (status != 0) {
- fprintf(stderr,
- "Passkey Neg reply failed. status 0x%02x (%s)\n",
+ error("Passkey Neg reply failed. status 0x%02x (%s)",
status, mgmt_errstr(status));
- mainloop_quit();
- return;
+ return noninteractive_quit(EXIT_FAILURE);
}
- printf("User Passkey Negative Reply successful\n");
+ print("User Passkey Negative Reply successful");
}
static int mgmt_passkey_neg_reply(struct mgmt *mgmt, uint16_t index,
sizeof(cp), &cp, passkey_neg_rsp, NULL, NULL);
}
+static bool prompt_input(const char *input)
+{
+ size_t len;
-static void request_passkey(uint16_t index, uint16_t len, const void *param,
+ if (!prompt.req)
+ return false;
+
+ len = strlen(input);
+
+ switch (prompt.req) {
+ case MGMT_EV_PIN_CODE_REQUEST:
+ if (len)
+ mgmt_pin_reply(mgmt, prompt.index, &prompt.addr,
+ input, len);
+ else
+ mgmt_pin_neg_reply(mgmt, prompt.index, &prompt.addr);
+ break;
+ case MGMT_EV_USER_PASSKEY_REQUEST:
+ if (strlen(input) > 0)
+ mgmt_passkey_reply(mgmt, prompt.index, &prompt.addr,
+ atoi(input));
+ else
+ mgmt_passkey_neg_reply(mgmt, prompt.index,
+ &prompt.addr);
+ break;
+ case MGMT_EV_USER_CONFIRM_REQUEST:
+ if (input[0] == 'y' || input[0] == 'Y')
+ mgmt_confirm_reply(mgmt, prompt.index, &prompt.addr);
+ else
+ mgmt_confirm_neg_reply(mgmt, prompt.index,
+ &prompt.addr);
+ break;
+ }
+
+ release_prompt();
+
+ return true;
+}
+
+static void interactive_prompt(const char *msg)
+{
+ if (saved_prompt)
+ return;
+
+ saved_prompt = strdup(rl_prompt);
+ if (!saved_prompt)
+ return;
+
+ saved_point = rl_point;
+
+ rl_set_prompt("");
+ rl_redisplay();
+
+ rl_set_prompt(msg);
+
+ rl_replace_line("", 0);
+ rl_redisplay();
+}
+
+static size_t get_input(char *buf, size_t buf_len)
+{
+ size_t len;
+
+ if (!fgets(buf, buf_len, stdin))
+ return 0;
+
+ len = strlen(buf);
+
+ /* Remove trailing white-space */
+ while (len && isspace(buf[len - 1]))
+ buf[--len] = '\0';
+
+ return len;
+}
+
+static void ask(uint16_t index, uint16_t req, const struct mgmt_addr_info *addr,
+ const char *fmt, ...)
+{
+ char msg[256], buf[18];
+ va_list ap;
+ int off;
+
+ prompt.index = index;
+ prompt.req = req;
+ memcpy(&prompt.addr, addr, sizeof(*addr));
+
+ va_start(ap, fmt);
+ off = vsnprintf(msg, sizeof(msg), fmt, ap);
+ va_end(ap);
+
+ snprintf(msg + off, sizeof(msg) - off, " %s ",
+ COLOR_BOLDGRAY ">>" COLOR_OFF);
+
+ if (interactive) {
+ interactive_prompt(msg);
+ va_end(ap);
+ return;
+ }
+
+ printf("%s", msg);
+ fflush(stdout);
+
+ memset(buf, 0, sizeof(buf));
+ get_input(buf, sizeof(buf));
+ prompt_input(buf);
+}
+
+static void request_pin(uint16_t index, uint16_t len, const void *param,
void *user_data)
{
- const struct mgmt_ev_user_passkey_request *ev = param;
- struct mgmt *mgmt = user_data;
- char passkey[7];
+ const struct mgmt_ev_pin_code_request *ev = param;
+ char addr[18];
if (len != sizeof(*ev)) {
- fprintf(stderr,
- "Invalid passkey request length (%u bytes)\n", len);
+ error("Invalid pin_code request length (%u bytes)", len);
return;
}
- if (monitor) {
- char addr[18];
- ba2str(&ev->addr.bdaddr, addr);
- printf("hci%u %s request passkey\n", index, addr);
- }
+ ba2str(&ev->addr.bdaddr, addr);
+ print("hci%u %s request PIN", index, addr);
- printf("Passkey Request (press enter to reject) >> ");
- fflush(stdout);
+ ask(index, MGMT_EV_PIN_CODE_REQUEST, &ev->addr,
+ "PIN Request (press enter to reject)");
+}
- memset(passkey, 0, sizeof(passkey));
+static void user_confirm(uint16_t index, uint16_t len, const void *param,
+ void *user_data)
+{
+ const struct mgmt_ev_user_confirm_request *ev = param;
+ uint32_t val;
+ char addr[18];
- if (fgets(passkey, sizeof(passkey), stdin) == NULL ||
- passkey[0] == '\n') {
- mgmt_passkey_neg_reply(mgmt, index, &ev->addr);
+ if (len != sizeof(*ev)) {
+ error("Invalid user_confirm request length (%u)", len);
return;
}
- len = strlen(passkey);
- if (passkey[len - 1] == '\n') {
- passkey[len - 1] = '\0';
- len--;
+ ba2str(&ev->addr.bdaddr, addr);
+ val = get_le32(&ev->value);
+
+ print("hci%u %s User Confirm %06u hint %u", index, addr,
+ val, ev->confirm_hint);
+
+ if (ev->confirm_hint)
+ ask(index, MGMT_EV_USER_CONFIRM_REQUEST, &ev->addr,
+ "Accept pairing with %s (yes/no)", addr);
+ else
+ ask(index, MGMT_EV_USER_CONFIRM_REQUEST, &ev->addr,
+ "Confirm value %06u for %s (yes/no)", val, addr);
+}
+
+static void request_passkey(uint16_t index, uint16_t len, const void *param,
+ void *user_data)
+{
+ const struct mgmt_ev_user_passkey_request *ev = param;
+ char addr[18];
+
+ if (len != sizeof(*ev)) {
+ error("Invalid passkey request length (%u bytes)", len);
+ return;
}
- mgmt_passkey_reply(mgmt, index, &ev->addr, atoi(passkey));
+ ba2str(&ev->addr.bdaddr, addr);
+ print("hci%u %s request passkey", index, addr);
+
+ ask(index, MGMT_EV_USER_PASSKEY_REQUEST, &ev->addr,
+ "Passkey Request (press enter to reject)");
}
static void passkey_notify(uint16_t index, uint16_t len, const void *param,
void *user_data)
{
const struct mgmt_ev_passkey_notify *ev = param;
+ char addr[18];
if (len != sizeof(*ev)) {
- fprintf(stderr,
- "Invalid passkey request length (%u bytes)\n", len);
+ error("Invalid passkey request length (%u bytes)", len);
return;
}
- if (monitor) {
- char addr[18];
- ba2str(&ev->addr.bdaddr, addr);
- printf("hci%u %s request passkey\n", index, addr);
- }
+ ba2str(&ev->addr.bdaddr, addr);
+ print("hci%u %s request passkey", index, addr);
- printf("Passkey Notify: %06u (entered %u)\n", get_le32(&ev->passkey),
+ print("Passkey Notify: %06u (entered %u)", get_le32(&ev->passkey),
ev->entered);
}
-static void cmd_monitor(struct mgmt *mgmt, uint16_t index, int argc,
- char **argv)
-{
- printf("Monitoring mgmt events...\n");
- monitor = true;
-}
-
static void version_rsp(uint8_t status, uint16_t len, const void *param,
void *user_data)
{
const struct mgmt_rp_read_version *rp = param;
if (status != 0) {
- fprintf(stderr, "Reading mgmt version failed with status"
- " 0x%02x (%s)\n", status, mgmt_errstr(status));
+ error("Reading mgmt version failed with status 0x%02x (%s)",
+ status, mgmt_errstr(status));
goto done;
}
if (len < sizeof(*rp)) {
- fprintf(stderr, "Too small version reply (%u bytes)\n", len);
+ error("Too small version reply (%u bytes)", len);
goto done;
}
- printf("MGMT Version %u, revision %u\n", rp->version,
+ print("MGMT Version %u, revision %u", rp->version,
get_le16(&rp->revision));
done:
- mainloop_quit();
+ noninteractive_quit(EXIT_SUCCESS);
}
static void cmd_version(struct mgmt *mgmt, uint16_t index, int argc,
{
if (mgmt_send(mgmt, MGMT_OP_READ_VERSION, MGMT_INDEX_NONE,
0, NULL, version_rsp, NULL, NULL) == 0) {
- fprintf(stderr, "Unable to send read_version cmd\n");
- exit(EXIT_FAILURE);
+ error("Unable to send read_version cmd");
+ return noninteractive_quit(EXIT_FAILURE);
}
}
int i;
if (status != 0) {
- fprintf(stderr, "Reading supported commands failed with status"
- " 0x%02x (%s)\n", status, mgmt_errstr(status));
+ error("Read Supported Commands failed: status 0x%02x (%s)",
+ status, mgmt_errstr(status));
goto done;
}
if (len < sizeof(*rp)) {
- fprintf(stderr, "Too small commands reply (%u bytes)\n", len);
+ error("Too small commands reply (%u bytes)", len);
goto done;
}
num_events * sizeof(uint16_t);
if (len < expected_len) {
- fprintf(stderr, "Too small commands reply (%u != %zu)\n",
+ error("Too small commands reply (%u != %zu)",
len, expected_len);
goto done;
}
opcode = rp->opcodes;
- printf("%u commands:\n", num_commands);
+ print("%u commands:", num_commands);
for (i = 0; i < num_commands; i++) {
uint16_t op = get_le16(opcode++);
- printf("\t%s (0x%04x)\n", mgmt_opstr(op), op);
+ print("\t%s (0x%04x)", mgmt_opstr(op), op);
}
- printf("%u events:\n", num_events);
+ print("%u events:", num_events);
for (i = 0; i < num_events; i++) {
uint16_t ev = get_le16(opcode++);
- printf("\t%s (0x%04x)\n", mgmt_evstr(ev), ev);
+ print("\t%s (0x%04x)", mgmt_evstr(ev), ev);
}
done:
- mainloop_quit();
+ noninteractive_quit(EXIT_SUCCESS);
}
static void cmd_commands(struct mgmt *mgmt, uint16_t index, int argc,
{
if (mgmt_send(mgmt, MGMT_OP_READ_COMMANDS, MGMT_INDEX_NONE,
0, NULL, commands_rsp, NULL, NULL) == 0) {
- fprintf(stderr, "Unable to send read_commands cmd\n");
- exit(EXIT_FAILURE);
+ error("Unable to send read_commands cmd");
+ return noninteractive_quit(EXIT_FAILURE);
}
}
unsigned int i;
if (status != 0) {
- fprintf(stderr,
- "Reading index list failed with status 0x%02x (%s)\n",
+ error("Reading index list failed with status 0x%02x (%s)",
status, mgmt_errstr(status));
goto done;
}
if (len < sizeof(*rp)) {
- fprintf(stderr, "Too small index list reply (%u bytes)\n",
- len);
+ error("Too small index list reply (%u bytes)", len);
goto done;
}
count = get_le16(&rp->num_controllers);
if (len < sizeof(*rp) + count * sizeof(uint16_t)) {
- fprintf(stderr,
- "Index count (%u) doesn't match reply length (%u)\n",
+ error("Index count (%u) doesn't match reply length (%u)",
count, len);
goto done;
}
- printf("Unconfigured index list with %u item%s\n",
+ print("Unconfigured index list with %u item%s",
count, count != 1 ? "s" : "");
for (i = 0; i < count; i++) {
index = get_le16(&rp->index[i]);
- printf("\thci%u\n", index);
+ print("\thci%u", index);
}
done:
- mainloop_quit();
+ noninteractive_quit(EXIT_SUCCESS);
}
static void config_info_rsp(uint8_t status, uint16_t len, const void *param,
uint16_t index = PTR_TO_UINT(user_data);
if (status != 0) {
- fprintf(stderr,
- "Reading hci%u config failed with status 0x%02x (%s)\n",
+ error("Reading hci%u config failed with status 0x%02x (%s)",
index, status, mgmt_errstr(status));
goto done;
}
if (len < sizeof(*rp)) {
- fprintf(stderr, "Too small info reply (%u bytes)\n", len);
+ error("Too small info reply (%u bytes)", len);
goto done;
}
- printf("hci%u:\tmanufacturer %u\n", index, get_le16(&rp->manufacturer));
-
- printf("\tsupported options: ");
- print_options(get_le32(&rp->supported_options));
- printf("\n");
+ print("hci%u:\tmanufacturer %u", index, get_le16(&rp->manufacturer));
- printf("\tmissing options: ");
- print_options(get_le32(&rp->missing_options));
- printf("\n");
+ print("\tsupported options: %s",
+ options2str(get_le32(&rp->supported_options)));
+ print("\tmissing options: %s",
+ options2str(get_le32(&rp->missing_options)));
done:
- mainloop_quit();
+ noninteractive_quit(EXIT_SUCCESS);
}
static void cmd_config(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
if (mgmt_send(mgmt, MGMT_OP_READ_UNCONF_INDEX_LIST,
MGMT_INDEX_NONE, 0, NULL,
unconf_index_rsp, mgmt, NULL) == 0) {
- fprintf(stderr, "Unable to send unconf_index_list cmd\n");
- exit(EXIT_FAILURE);
+ error("Unable to send unconf_index_list cmd");
+ return noninteractive_quit(EXIT_FAILURE);
}
return;
if (mgmt_send(mgmt, MGMT_OP_READ_CONFIG_INFO, index, 0, NULL,
config_info_rsp, data, NULL) == 0) {
- fprintf(stderr, "Unable to send read_config_info cmd\n");
- exit(EXIT_FAILURE);
+ error("Unable to send read_config_info cmd");
+ return noninteractive_quit(EXIT_FAILURE);
}
}
uint16_t index = PTR_TO_UINT(user_data);
char addr[18];
- pending--;
+ pending_index--;
if (status != 0) {
- fprintf(stderr,
- "Reading hci%u info failed with status 0x%02x (%s)\n",
+ error("Reading hci%u info failed with status 0x%02x (%s)",
index, status, mgmt_errstr(status));
goto done;
}
if (len < sizeof(*rp)) {
- fprintf(stderr, "Too small info reply (%u bytes)\n", len);
+ error("Too small info reply (%u bytes)", len);
goto done;
}
ba2str(&rp->bdaddr, addr);
- printf("hci%u:\taddr %s version %u manufacturer %u"
- " class 0x%02x%02x%02x\n", index,
+ print("hci%u:\taddr %s version %u manufacturer %u"
+ " class 0x%02x%02x%02x", index,
addr, rp->version, get_le16(&rp->manufacturer),
rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]);
- printf("\tsupported settings: ");
- print_settings(get_le32(&rp->supported_settings));
+ print("\tsupported settings: %s",
+ settings2str(get_le32(&rp->supported_settings)));
- printf("\n\tcurrent settings: ");
- print_settings(get_le32(&rp->current_settings));
+ print("\tcurrent settings: %s",
+ settings2str(get_le32(&rp->current_settings)));
- printf("\n\tname %s\n", rp->name);
- printf("\tshort name %s\n", rp->short_name);
+ print("\tname %s", rp->name);
+ print("\tshort name %s", rp->short_name);
- if (pending > 0)
+ if (pending_index > 0)
return;
done:
- mainloop_quit();
+ noninteractive_quit(EXIT_SUCCESS);
}
static void index_rsp(uint8_t status, uint16_t len, const void *param,
unsigned int i;
if (status != 0) {
- fprintf(stderr,
- "Reading index list failed with status 0x%02x (%s)\n",
+ error("Reading index list failed with status 0x%02x (%s)",
status, mgmt_errstr(status));
- goto done;
+ return noninteractive_quit(EXIT_FAILURE);
}
if (len < sizeof(*rp)) {
- fprintf(stderr, "Too small index list reply (%u bytes)\n",
- len);
- goto done;
+ error("Too small index list reply (%u bytes)", len);
+ return noninteractive_quit(EXIT_FAILURE);
}
count = get_le16(&rp->num_controllers);
if (len < sizeof(*rp) + count * sizeof(uint16_t)) {
- fprintf(stderr,
- "Index count (%u) doesn't match reply length (%u)\n",
+ error("Index count (%u) doesn't match reply length (%u)",
count, len);
- goto done;
+ return noninteractive_quit(EXIT_FAILURE);
}
- if (monitor)
- printf("Index list with %u item%s\n",
- count, count != 1 ? "s" : "");
-
- if (count == 0)
- goto done;
-
- if (monitor && count > 0)
- printf("\t");
+ print("Index list with %u item%s", count, count != 1 ? "s" : "");
for (i = 0; i < count; i++) {
uint16_t index;
index = get_le16(&rp->index[i]);
- if (monitor)
- printf("hci%u ", index);
-
data = UINT_TO_PTR(index);
if (mgmt_send(mgmt, MGMT_OP_READ_INFO, index, 0, NULL,
info_rsp, data, NULL) == 0) {
- fprintf(stderr, "Unable to send read_info cmd\n");
- goto done;
+ error("Unable to send read_info cmd");
+ return noninteractive_quit(EXIT_FAILURE);
}
- pending++;
+ pending_index++;
}
- if (monitor && count > 0)
- printf("\n");
-
- return;
-
-done:
- mainloop_quit();
+ if (!count)
+ noninteractive_quit(EXIT_SUCCESS);
}
static void cmd_info(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
if (mgmt_send(mgmt, MGMT_OP_READ_INDEX_LIST,
MGMT_INDEX_NONE, 0, NULL,
index_rsp, mgmt, NULL) == 0) {
- fprintf(stderr, "Unable to send index_list cmd\n");
- exit(EXIT_FAILURE);
+ error("Unable to send index_list cmd");
+ return noninteractive_quit(EXIT_FAILURE);
}
return;
if (mgmt_send(mgmt, MGMT_OP_READ_INFO, index, 0, NULL, info_rsp,
data, NULL) == 0) {
- fprintf(stderr, "Unable to send read_info cmd\n");
- exit(EXIT_FAILURE);
+ error("Unable to send read_info cmd");
+ return noninteractive_quit(EXIT_FAILURE);
}
}
const uint32_t *rp = param;
if (status != 0) {
- fprintf(stderr,
- "%s for hci%u failed with status 0x%02x (%s)\n",
+ error("%s for hci%u failed with status 0x%02x (%s)",
mgmt_opstr(op), id, status, mgmt_errstr(status));
goto done;
}
if (len < sizeof(*rp)) {
- fprintf(stderr, "Too small %s response (%u bytes)\n",
+ error("Too small %s response (%u bytes)",
mgmt_opstr(op), len);
goto done;
}
- printf("hci%u %s complete, settings: ", id, mgmt_opstr(op));
- print_settings(get_le32(rp));
- printf("\n");
+ print("hci%u %s complete, settings: %s", id, mgmt_opstr(op),
+ settings2str(get_le32(rp)));
done:
- mainloop_quit();
+ noninteractive_quit(EXIT_SUCCESS);
}
static void cmd_setting(struct mgmt *mgmt, uint16_t index, uint16_t op,
uint8_t val;
if (argc < 2) {
- printf("Specify \"on\" or \"off\"\n");
- exit(EXIT_FAILURE);
+ print("Specify \"on\" or \"off\"");
+ return noninteractive_quit(EXIT_FAILURE);
}
if (strcasecmp(argv[1], "on") == 0 || strcasecmp(argv[1], "yes") == 0)
index = 0;
if (send_cmd(mgmt, op, index, sizeof(val), &val, setting_rsp) == 0) {
- fprintf(stderr, "Unable to send %s cmd\n", mgmt_opstr(op));
- exit(EXIT_FAILURE);
+ error("Unable to send %s cmd", mgmt_opstr(op));
+ return noninteractive_quit(EXIT_FAILURE);
}
}
struct mgmt_cp_set_discoverable cp;
if (argc < 2) {
- printf("Usage: btmgmt %s <yes/no/limited> [timeout]\n", argv[0]);
- exit(EXIT_FAILURE);
+ print("Usage: %s <yes/no/limited> [timeout]", argv[0]);
+ return noninteractive_quit(EXIT_FAILURE);
}
memset(&cp, 0, sizeof(cp));
if (send_cmd(mgmt, MGMT_OP_SET_DISCOVERABLE, index, sizeof(cp), &cp,
setting_rsp) == 0) {
- fprintf(stderr, "Unable to send set_discoverable cmd\n");
- exit(EXIT_FAILURE);
+ error("Unable to send set_discoverable cmd");
+ return noninteractive_quit(EXIT_FAILURE);
}
}
uint8_t val;
if (argc < 2) {
- printf("Specify \"on\" or \"off\" or \"only\"\n");
- exit(EXIT_FAILURE);
+ print("Specify \"on\" or \"off\" or \"only\"");
+ return noninteractive_quit(EXIT_FAILURE);
}
if (strcasecmp(argv[1], "on") == 0 || strcasecmp(argv[1], "yes") == 0)
if (send_cmd(mgmt, MGMT_OP_SET_SECURE_CONN, index,
sizeof(val), &val, setting_rsp) == 0) {
- fprintf(stderr, "Unable to send set_secure_conn cmd\n");
- exit(EXIT_FAILURE);
+ error("Unable to send set_secure_conn cmd");
+ return noninteractive_quit(EXIT_FAILURE);
}
}
struct mgmt_cp_set_privacy cp;
if (argc < 2) {
- printf("Specify \"on\" or \"off\"\n");
- exit(EXIT_FAILURE);
+ print("Specify \"on\" or \"off\"");
+ return noninteractive_quit(EXIT_FAILURE);
}
if (strcasecmp(argv[1], "on") == 0 || strcasecmp(argv[1], "yes") == 0)
index = 0;
if (argc > 2) {
- if (convert_hexstr(argv[2], cp.irk,
+ if (hex2bin(argv[2], cp.irk,
sizeof(cp.irk)) != sizeof(cp.irk)) {
- fprintf(stderr, "Invalid key format\n");
- exit(EXIT_FAILURE);
+ error("Invalid key format");
+ return noninteractive_quit(EXIT_FAILURE);
}
} else {
int fd;
fd = open("/dev/urandom", O_RDONLY);
if (fd < 0) {
- fprintf(stderr, "open(/dev/urandom): %s\n",
- strerror(errno));
- exit(EXIT_FAILURE);
+ error("open(/dev/urandom): %s", strerror(errno));
+ return noninteractive_quit(EXIT_FAILURE);
}
if (read(fd, cp.irk, sizeof(cp.irk)) != sizeof(cp.irk)) {
- fprintf(stderr, "Reading from urandom failed\n");
+ error("Reading from urandom failed");
close(fd);
- exit(EXIT_FAILURE);
+ return noninteractive_quit(EXIT_FAILURE);
}
close(fd);
if (send_cmd(mgmt, MGMT_OP_SET_PRIVACY, index, sizeof(cp), &cp,
setting_rsp) == 0) {
- fprintf(stderr, "Unable to send Set Privacy command\n");
- exit(EXIT_FAILURE);
+ error("Unable to send Set Privacy command");
+ return noninteractive_quit(EXIT_FAILURE);
}
}
const struct mgmt_ev_class_of_dev_changed *rp = param;
if (len == 0 && status != 0) {
- fprintf(stderr, "%s failed, status 0x%02x (%s)\n",
+ error("%s failed, status 0x%02x (%s)",
mgmt_opstr(op), status, mgmt_errstr(status));
- goto done;
+ return noninteractive_quit(EXIT_FAILURE);
}
if (len != sizeof(*rp)) {
- fprintf(stderr, "Unexpected %s len %u\n", mgmt_opstr(op), len);
- goto done;
+ error("Unexpected %s len %u", mgmt_opstr(op), len);
+ return noninteractive_quit(EXIT_FAILURE);
}
- printf("%s succeeded. Class 0x%02x%02x%02x\n", mgmt_opstr(op),
+ print("%s succeeded. Class 0x%02x%02x%02x", mgmt_opstr(op),
rp->class_of_dev[2], rp->class_of_dev[1], rp->class_of_dev[0]);
-done:
- mainloop_quit();
+ noninteractive_quit(EXIT_SUCCESS);
}
static void cmd_class(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
uint8_t class[2];
if (argc < 3) {
- printf("Usage: btmgmt %s <major> <minor>\n", argv[0]);
- exit(EXIT_FAILURE);
+ print("Usage: %s <major> <minor>", argv[0]);
+ return noninteractive_quit(EXIT_FAILURE);
}
class[0] = atoi(argv[1]);
if (send_cmd(mgmt, MGMT_OP_SET_DEV_CLASS, index, sizeof(class), class,
class_rsp) == 0) {
- fprintf(stderr, "Unable to send set_dev_class cmd\n");
- exit(EXIT_FAILURE);
+ error("Unable to send set_dev_class cmd");
+ return noninteractive_quit(EXIT_FAILURE);
}
}
char addr[18];
if (len == 0 && status != 0) {
- fprintf(stderr, "Disconnect failed with status 0x%02x (%s)\n",
+ error("Disconnect failed with status 0x%02x (%s)",
status, mgmt_errstr(status));
- goto done;
+ return noninteractive_quit(EXIT_FAILURE);
}
if (len != sizeof(*rp)) {
- fprintf(stderr, "Invalid disconnect response length (%u)\n",
- len);
- goto done;
+ error("Invalid disconnect response length (%u)", len);
+ return noninteractive_quit(EXIT_FAILURE);
}
ba2str(&rp->addr.bdaddr, addr);
if (status == 0)
- printf("%s disconnected\n", addr);
+ print("%s disconnected", addr);
else
- fprintf(stderr,
- "Disconnecting %s failed with status 0x%02x (%s)\n",
+ error("Disconnecting %s failed with status 0x%02x (%s)",
addr, status, mgmt_errstr(status));
-done:
- mainloop_quit();
+ noninteractive_quit(EXIT_SUCCESS);
}
static void disconnect_usage(void)
{
- printf("Usage: btmgmt disconnect [-t type] <remote address>\n");
+ print("Usage: disconnect [-t type] <remote address>");
}
static struct option disconnect_options[] = {
type = strtol(optarg, NULL, 0);
break;
case 'h':
+ disconnect_usage();
+ return noninteractive_quit(EXIT_SUCCESS);
default:
disconnect_usage();
- exit(EXIT_SUCCESS);
+ return noninteractive_quit(EXIT_FAILURE);
}
}
if (argc < 1) {
disconnect_usage();
- exit(EXIT_FAILURE);
+ return noninteractive_quit(EXIT_FAILURE);
}
if (index == MGMT_INDEX_NONE)
if (mgmt_send(mgmt, MGMT_OP_DISCONNECT, index, sizeof(cp), &cp,
disconnect_rsp, NULL, NULL) == 0) {
- fprintf(stderr, "Unable to send disconnect cmd\n");
- exit(EXIT_FAILURE);
+ error("Unable to send disconnect cmd");
+ return noninteractive_quit(EXIT_FAILURE);
}
}
uint16_t count, i;
if (len < sizeof(*rp)) {
- fprintf(stderr, "Too small (%u bytes) get_connections rsp\n",
- len);
- goto done;
+ error("Too small (%u bytes) get_connections rsp", len);
+ return noninteractive_quit(EXIT_FAILURE);
}
count = get_le16(&rp->conn_count);
if (len != sizeof(*rp) + count * sizeof(struct mgmt_addr_info)) {
- fprintf(stderr, "Invalid get_connections length "
- " (count=%u, len=%u)\n", count, len);
- goto done;
+ error("Invalid get_connections length (count=%u, len=%u)",
+ count, len);
+ return noninteractive_quit(EXIT_FAILURE);
}
for (i = 0; i < count; i++) {
ba2str(&rp->addr[i].bdaddr, addr);
- printf("%s type %s\n", addr, typestr(rp->addr[i].type));
+ print("%s type %s", addr, typestr(rp->addr[i].type));
}
-done:
- mainloop_quit();
+ noninteractive_quit(EXIT_SUCCESS);
}
static void cmd_con(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
if (mgmt_send(mgmt, MGMT_OP_GET_CONNECTIONS, index, 0, NULL,
con_rsp, NULL, NULL) == 0) {
- fprintf(stderr, "Unable to send get_connections cmd\n");
- exit(EXIT_FAILURE);
+ error("Unable to send get_connections cmd");
+ return noninteractive_quit(EXIT_FAILURE);
}
}
void *user_data)
{
if (status != 0) {
- fprintf(stderr,
- "Unable to start service discovery. status 0x%02x (%s)\n",
- status, mgmt_errstr(status));
- mainloop_quit();
- return;
+ error("Start Service Discovery failed: status 0x%02x (%s)",
+ status, mgmt_errstr(status));
+ return noninteractive_quit(EXIT_FAILURE);
}
- printf("Service discovery started\n");
+ print("Service discovery started");
discovery = true;
}
static void find_service_usage(void)
{
- printf("Usage: btmgmt find-service [-u UUID] [-r RSSI_Threshold] [-l|-b]\n");
+ print("Usage: find-service [-u UUID] [-r RSSI_Threshold] [-l|-b]");
}
static struct option find_service_options[] = {
index = 0;
type = 0;
- hci_set_bit(BDADDR_BREDR, &type);
- hci_set_bit(BDADDR_LE_PUBLIC, &type);
- hci_set_bit(BDADDR_LE_RANDOM, &type);
+ type |= (1 << BDADDR_BREDR);
+ type |= (1 << BDADDR_LE_PUBLIC);
+ type |= (1 << BDADDR_LE_RANDOM);
rssi = 127;
count = 0;
if (argc == 1) {
find_service_usage();
- exit(EXIT_FAILURE);
+ return noninteractive_quit(EXIT_FAILURE);
}
while ((opt = getopt_long(argc, argv, "+lbu:r:p:h",
find_service_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);
+ type &= ~(1 << BDADDR_BREDR);
+ type |= (1 << BDADDR_LE_PUBLIC);
+ type |= (1 << BDADDR_LE_RANDOM);
break;
case 'b':
- hci_set_bit(BDADDR_BREDR, &type);
- hci_clear_bit(BDADDR_LE_PUBLIC, &type);
- hci_clear_bit(BDADDR_LE_RANDOM, &type);
+ type |= (1 << BDADDR_BREDR);
+ type &= ~(1 << BDADDR_LE_PUBLIC);
+ type &= ~(1 << BDADDR_LE_RANDOM);
break;
case 'u':
if (count == MAX_UUIDS) {
- printf("Max %u UUIDs supported\n", MAX_UUIDS);
- exit(EXIT_FAILURE);
+ print("Max %u UUIDs supported", MAX_UUIDS);
+ return noninteractive_quit(EXIT_FAILURE);
}
if (bt_string2uuid(&uuid, optarg) < 0) {
- printf("Invalid UUID: %s\n", optarg);
- exit(EXIT_FAILURE);
+ print("Invalid UUID: %s", optarg);
+ return noninteractive_quit(EXIT_FAILURE);
}
cp = (void *) buf;
uuid_to_uuid128(&uuid128, &uuid);
break;
case 'h':
find_service_usage();
- exit(EXIT_SUCCESS);
+ return noninteractive_quit(EXIT_SUCCESS);
default:
find_service_usage();
- exit(EXIT_FAILURE);
+ return noninteractive_quit(EXIT_FAILURE);
}
}
if (argc > 0) {
find_service_usage();
- exit(EXIT_FAILURE);
+ return noninteractive_quit(EXIT_FAILURE);
}
cp = (void *) buf;
if (mgmt_send(mgmt, MGMT_OP_START_SERVICE_DISCOVERY, index,
sizeof(*cp) + count * 16, cp,
find_service_rsp, NULL, NULL) == 0) {
- fprintf(stderr, "Unable to send start_service_discovery cmd\n");
- exit(EXIT_FAILURE);
+ error("Unable to send start_service_discovery cmd");
+ return noninteractive_quit(EXIT_FAILURE);
}
}
void *user_data)
{
if (status != 0) {
- fprintf(stderr,
- "Unable to start discovery. status 0x%02x (%s)\n",
+ error("Unable to start discovery. status 0x%02x (%s)",
status, mgmt_errstr(status));
- mainloop_quit();
- return;
+ return noninteractive_quit(EXIT_FAILURE);
}
- printf("Discovery started\n");
+ print("Discovery started");
discovery = true;
}
static void find_usage(void)
{
- printf("Usage: btmgmt find [-l|-b]>\n");
+ print("Usage: find [-l|-b]>");
}
static struct option find_options[] = {
index = 0;
type = 0;
- hci_set_bit(BDADDR_BREDR, &type);
- hci_set_bit(BDADDR_LE_PUBLIC, &type);
- hci_set_bit(BDADDR_LE_RANDOM, &type);
+ type |= (1 << BDADDR_BREDR);
+ type |= (1 << BDADDR_LE_PUBLIC);
+ type |= (1 << BDADDR_LE_RANDOM);
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);
+ type &= ~(1 << BDADDR_BREDR);
+ type |= (1 << BDADDR_LE_PUBLIC);
+ type |= (1 << BDADDR_LE_RANDOM);
break;
case 'b':
- hci_set_bit(BDADDR_BREDR, &type);
- hci_clear_bit(BDADDR_LE_PUBLIC, &type);
- hci_clear_bit(BDADDR_LE_RANDOM, &type);
+ type |= (1 << BDADDR_BREDR);
+ type &= ~(1 << BDADDR_LE_PUBLIC);
+ type &= ~(1 << BDADDR_LE_RANDOM);
break;
case 'h':
+ find_usage();
+ return noninteractive_quit(EXIT_SUCCESS);
default:
find_usage();
- exit(EXIT_SUCCESS);
+ return noninteractive_quit(EXIT_FAILURE);
}
}
if (mgmt_send(mgmt, MGMT_OP_START_DISCOVERY, index, sizeof(cp), &cp,
find_rsp, NULL, NULL) == 0) {
- fprintf(stderr, "Unable to send start_discovery cmd\n");
- exit(EXIT_FAILURE);
+ error("Unable to send start_discovery cmd");
+ return noninteractive_quit(EXIT_FAILURE);
}
}
void *user_data)
{
if (status != 0)
- fprintf(stderr, "Unable to set local name "
- "with status 0x%02x (%s)\n",
+ error("Unable to set local name with status 0x%02x (%s)",
status, mgmt_errstr(status));
- mainloop_quit();
+ noninteractive_quit(EXIT_SUCCESS);
}
static void cmd_name(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
struct mgmt_cp_set_local_name cp;
if (argc < 2) {
- printf("Usage: btmgmt %s <name> [shortname]\n", argv[0]);
- exit(EXIT_FAILURE);
+ print("Usage: %s <name> [shortname]", argv[0]);
+ return noninteractive_quit(EXIT_FAILURE);
}
if (index == MGMT_INDEX_NONE)
if (mgmt_send(mgmt, MGMT_OP_SET_LOCAL_NAME, index, sizeof(cp), &cp,
name_rsp, NULL, NULL) == 0) {
- fprintf(stderr, "Unable to send set_name cmd\n");
- exit(EXIT_FAILURE);
+ error("Unable to send set_name cmd");
+ return noninteractive_quit(EXIT_FAILURE);
}
}
char addr[18];
if (len == 0 && status != 0) {
- fprintf(stderr, "Pairing failed with status 0x%02x (%s)\n",
+ error("Pairing failed with status 0x%02x (%s)",
status, mgmt_errstr(status));
- goto done;
+ return noninteractive_quit(EXIT_FAILURE);
}
if (len != sizeof(*rp)) {
- fprintf(stderr, "Unexpected pair_rsp len %u\n", len);
- goto done;
+ error("Unexpected pair_rsp len %u", len);
+ return noninteractive_quit(EXIT_FAILURE);
}
+ if (!memcmp(&rp->addr, &prompt.addr, sizeof(rp->addr)))
+ release_prompt();
+
ba2str(&rp->addr.bdaddr, addr);
- if (status != 0) {
- fprintf(stderr,
- "Pairing with %s (%s) failed. status 0x%02x (%s)\n",
+ if (status)
+ error("Pairing with %s (%s) failed. status 0x%02x (%s)",
addr, typestr(rp->addr.type), status,
mgmt_errstr(status));
- goto done;
- }
-
- printf("Paired with %s (%s)\n", addr, typestr(rp->addr.type));
+ else
+ print("Paired with %s (%s)", addr, typestr(rp->addr.type));
-done:
- mainloop_quit();
+ noninteractive_quit(EXIT_SUCCESS);
}
static void pair_usage(void)
{
- printf("Usage: btmgmt pair [-c cap] [-t type] <remote address>\n");
+ print("Usage: pair [-c cap] [-t type] <remote address>");
}
static struct option pair_options[] = {
type = strtol(optarg, NULL, 0);
break;
case 'h':
+ pair_usage();
+ return noninteractive_quit(EXIT_SUCCESS);
default:
pair_usage();
- exit(EXIT_SUCCESS);
+ return noninteractive_quit(EXIT_FAILURE);
}
}
if (argc < 1) {
pair_usage();
- exit(EXIT_FAILURE);
+ return noninteractive_quit(EXIT_FAILURE);
}
if (index == MGMT_INDEX_NONE)
cp.io_cap = cap;
ba2str(&cp.addr.bdaddr, addr);
- printf("Pairing with %s (%s)\n", addr, typestr(cp.addr.type));
+ print("Pairing with %s (%s)", addr, typestr(cp.addr.type));
if (mgmt_send(mgmt, MGMT_OP_PAIR_DEVICE, index, sizeof(cp), &cp,
pair_rsp, NULL, NULL) == 0) {
- fprintf(stderr, "Unable to send pair_device cmd\n");
- exit(EXIT_FAILURE);
+ error("Unable to send pair_device cmd");
+ return noninteractive_quit(EXIT_FAILURE);
}
}
char addr[18];
if (len == 0 && status != 0) {
- fprintf(stderr, "Cancel Pairing failed with 0x%02x (%s)\n",
+ error("Cancel Pairing failed with 0x%02x (%s)",
status, mgmt_errstr(status));
- goto done;
+ return noninteractive_quit(EXIT_FAILURE);
}
if (len != sizeof(*rp)) {
- fprintf(stderr, "Unexpected cancel_pair_rsp len %u\n", len);
- goto done;
+ error("Unexpected cancel_pair_rsp len %u", len);
+ return noninteractive_quit(EXIT_FAILURE);
}
ba2str(&rp->bdaddr, addr);
- if (status != 0) {
- fprintf(stderr,
- "Cancel Pairing with %s (%s) failed. 0x%02x (%s)\n",
+ if (status)
+ error("Cancel Pairing with %s (%s) failed. 0x%02x (%s)",
addr, typestr(rp->type), status,
mgmt_errstr(status));
- goto done;
- }
-
- printf("Pairing Cancelled with %s\n", addr);
+ else
+ print("Pairing Cancelled with %s", addr);
-done:
- mainloop_quit();
+ noninteractive_quit(EXIT_SUCCESS);
}
static void cancel_pair_usage(void)
{
- printf("Usage: btmgmt cancelpair [-t type] <remote address>\n");
+ print("Usage: cancelpair [-t type] <remote address>");
}
static struct option cancel_pair_options[] = {
type = strtol(optarg, NULL, 0);
break;
case 'h':
+ cancel_pair_usage();
+ return noninteractive_quit(EXIT_SUCCESS);
default:
cancel_pair_usage();
- exit(EXIT_SUCCESS);
+ return noninteractive_quit(EXIT_FAILURE);
}
}
if (argc < 1) {
cancel_pair_usage();
- exit(EXIT_FAILURE);
+ return noninteractive_quit(EXIT_FAILURE);
}
if (index == MGMT_INDEX_NONE)
if (mgmt_send(mgmt, MGMT_OP_CANCEL_PAIR_DEVICE, index, sizeof(cp), &cp,
cancel_pair_rsp, NULL, NULL) == 0) {
- fprintf(stderr, "Unable to send cancel_pair_device cmd\n");
- exit(EXIT_FAILURE);
+ error("Unable to send cancel_pair_device cmd");
+ return noninteractive_quit(EXIT_FAILURE);
}
}
char addr[18];
if (len == 0 && status != 0) {
- fprintf(stderr, "Unpair device failed. status 0x%02x (%s)\n",
+ error("Unpair device failed. status 0x%02x (%s)",
status, mgmt_errstr(status));
- goto done;
+ return noninteractive_quit(EXIT_FAILURE);
}
if (len != sizeof(*rp)) {
- fprintf(stderr, "Unexpected unpair_device_rsp len %u\n", len);
- goto done;
+ error("Unexpected unpair_device_rsp len %u", len);
+ return noninteractive_quit(EXIT_FAILURE);
}
ba2str(&rp->addr.bdaddr, addr);
- if (status != 0) {
- fprintf(stderr,
- "Unpairing %s failed. status 0x%02x (%s)\n",
+ if (status)
+ error("Unpairing %s failed. status 0x%02x (%s)",
addr, status, mgmt_errstr(status));
- goto done;
- }
-
- printf("%s unpaired\n", addr);
+ else
+ print("%s unpaired", addr);
-done:
- mainloop_quit();
+ noninteractive_quit(EXIT_SUCCESS);
}
static void unpair_usage(void)
{
- printf("Usage: btmgmt unpair [-t type] <remote address>\n");
+ print("Usage: unpair [-t type] <remote address>");
}
static struct option unpair_options[] = {
type = strtol(optarg, NULL, 0);
break;
case 'h':
+ unpair_usage();
+ return noninteractive_quit(EXIT_SUCCESS);
default:
unpair_usage();
- exit(EXIT_SUCCESS);
+ return noninteractive_quit(EXIT_FAILURE);
}
}
if (argc < 1) {
unpair_usage();
- exit(EXIT_FAILURE);
+ return noninteractive_quit(EXIT_FAILURE);
}
if (index == MGMT_INDEX_NONE)
if (mgmt_send(mgmt, MGMT_OP_UNPAIR_DEVICE, index, sizeof(cp), &cp,
unpair_rsp, NULL, NULL) == 0) {
- fprintf(stderr, "Unable to send unpair_device cmd\n");
- exit(EXIT_FAILURE);
+ error("Unable to send unpair_device cmd");
+ return noninteractive_quit(EXIT_FAILURE);
}
}
void *user_data)
{
if (status != 0)
- fprintf(stderr, "Load keys failed with status 0x%02x (%s)\n",
+ error("Load keys failed with status 0x%02x (%s)",
status, mgmt_errstr(status));
else
- printf("Keys successfully loaded\n");
+ print("Keys successfully loaded");
- mainloop_quit();
+ noninteractive_quit(EXIT_SUCCESS);
}
static void cmd_keys(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
if (mgmt_send(mgmt, MGMT_OP_LOAD_LINK_KEYS, index, sizeof(cp), &cp,
keys_rsp, NULL, NULL) == 0) {
- fprintf(stderr, "Unable to send load_keys cmd\n");
- exit(EXIT_FAILURE);
+ error("Unable to send load_keys cmd");
+ return noninteractive_quit(EXIT_FAILURE);
}
}
void *user_data)
{
if (status != 0)
- fprintf(stderr, "Load keys failed with status 0x%02x (%s)\n",
+ error("Load keys failed with status 0x%02x (%s)",
status, mgmt_errstr(status));
else
- printf("Long term keys successfully loaded\n");
+ print("Long term keys successfully loaded");
- mainloop_quit();
+ noninteractive_quit(EXIT_SUCCESS);
}
static void cmd_ltks(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
if (mgmt_send(mgmt, MGMT_OP_LOAD_LONG_TERM_KEYS, index, sizeof(cp), &cp,
ltks_rsp, NULL, NULL) == 0) {
- fprintf(stderr, "Unable to send load_ltks cmd\n");
- exit(EXIT_FAILURE);
+ error("Unable to send load_ltks cmd");
+ return noninteractive_quit(EXIT_SUCCESS);
}
}
void *user_data)
{
if (status != 0)
- fprintf(stderr, "Load IRKs failed with status 0x%02x (%s)\n",
+ error("Load IRKs failed with status 0x%02x (%s)",
status, mgmt_errstr(status));
else
- printf("Identity Resolving Keys successfully loaded\n");
+ print("Identity Resolving Keys successfully loaded");
- mainloop_quit();
+ noninteractive_quit(EXIT_SUCCESS);
}
static void irks_usage(void)
{
- printf("Usage: btmgmt irks [--local]\n");
+ print("Usage: irks [--local]");
}
static struct option irks_options[] = {
switch (opt) {
case 'l':
if (count >= MAX_IRKS) {
- fprintf(stderr, "Number of IRKs exceeded\n");
- exit(EXIT_FAILURE);
+ error("Number of IRKs exceeded");
+ return noninteractive_quit(EXIT_FAILURE);
}
if (strlen(optarg) > 3 &&
strncasecmp(optarg, "hci", 3) == 0)
else
local_index = atoi(optarg);
if (!load_identity(local_index, &cp->irks[count])) {
- fprintf(stderr, "Unable to load identity\n");
- exit(EXIT_FAILURE);
+ error("Unable to load identity");
+ return noninteractive_quit(EXIT_FAILURE);
}
count++;
break;
case 'h':
irks_usage();
- exit(EXIT_SUCCESS);
+ return noninteractive_quit(EXIT_SUCCESS);
default:
irks_usage();
- exit(EXIT_FAILURE);
+ return noninteractive_quit(EXIT_FAILURE);
}
}
if (argc > 0) {
irks_usage();
- exit(EXIT_FAILURE);
+ return noninteractive_quit(EXIT_FAILURE);
}
cp->irk_count = cpu_to_le16(count);
if (mgmt_send(mgmt, MGMT_OP_LOAD_IRKS, index,
sizeof(*cp) + count * 23, cp,
irks_rsp, NULL, NULL) == 0) {
- fprintf(stderr, "Unable to send load_irks cmd\n");
- exit(EXIT_FAILURE);
+ error("Unable to send load_irks cmd");
+ return noninteractive_quit(EXIT_FAILURE);
}
}
char addr[18];
if (len == 0 && status != 0) {
- fprintf(stderr, "%s failed, status 0x%02x (%s)\n",
+ error("%s failed, status 0x%02x (%s)",
mgmt_opstr(op), status, mgmt_errstr(status));
- goto done;
+ return noninteractive_quit(EXIT_FAILURE);
}
if (len != sizeof(*rp)) {
- fprintf(stderr, "Unexpected %s len %u\n", mgmt_opstr(op), len);
- goto done;
+ error("Unexpected %s len %u", mgmt_opstr(op), len);
+ return noninteractive_quit(EXIT_FAILURE);
}
ba2str(&rp->bdaddr, addr);
- if (status != 0) {
- fprintf(stderr, "%s %s (%s) failed. status 0x%02x (%s)\n",
+ if (status)
+ error("%s %s (%s) failed. status 0x%02x (%s)",
mgmt_opstr(op), addr, typestr(rp->type),
status, mgmt_errstr(status));
- goto done;
- }
-
- printf("%s %s succeeded\n", mgmt_opstr(op), addr);
+ else
+ print("%s %s succeeded", mgmt_opstr(op), addr);
-done:
- mainloop_quit();
+ noninteractive_quit(EXIT_SUCCESS);
}
static void block_usage(void)
{
- printf("Usage: btmgmt block [-t type] <remote address>\n");
+ print("Usage: block [-t type] <remote address>");
}
static struct option block_options[] = {
type = strtol(optarg, NULL, 0);
break;
case 'h':
+ block_usage();
+ return noninteractive_quit(EXIT_SUCCESS);
default:
block_usage();
- exit(EXIT_SUCCESS);
+ return noninteractive_quit(EXIT_FAILURE);
}
}
if (argc < 1) {
block_usage();
- exit(EXIT_FAILURE);
+ return noninteractive_quit(EXIT_FAILURE);
}
if (index == MGMT_INDEX_NONE)
if (send_cmd(mgmt, MGMT_OP_BLOCK_DEVICE, index, sizeof(cp), &cp,
block_rsp) == 0) {
- fprintf(stderr, "Unable to send block_device cmd\n");
- exit(EXIT_FAILURE);
+ error("Unable to send block_device cmd");
+ return noninteractive_quit(EXIT_FAILURE);
}
}
static void unblock_usage(void)
{
- printf("Usage: btmgmt unblock [-t type] <remote address>\n");
+ print("Usage: unblock [-t type] <remote address>");
}
static void cmd_unblock(struct mgmt *mgmt, uint16_t index, int argc,
type = strtol(optarg, NULL, 0);
break;
case 'h':
+ unblock_usage();
+ return noninteractive_quit(EXIT_SUCCESS);
default:
unblock_usage();
- exit(EXIT_SUCCESS);
+ return noninteractive_quit(EXIT_FAILURE);
}
}
if (argc < 1) {
unblock_usage();
- exit(EXIT_FAILURE);
+ return noninteractive_quit(EXIT_FAILURE);
}
if (index == MGMT_INDEX_NONE)
if (send_cmd(mgmt, MGMT_OP_UNBLOCK_DEVICE, index, sizeof(cp), &cp,
block_rsp) == 0) {
- fprintf(stderr, "Unable to send unblock_device cmd\n");
- exit(EXIT_FAILURE);
+ error("Unable to send unblock_device cmd");
+ return noninteractive_quit(EXIT_FAILURE);
}
}
uuid_t uuid, uuid128;
if (argc < 3) {
- printf("UUID and service hint needed\n");
- exit(EXIT_FAILURE);
+ print("UUID and service hint needed");
+ return noninteractive_quit(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);
+ print("Invalid UUID: %s", argv[1]);
+ return noninteractive_quit(EXIT_FAILURE);
}
memset(&cp, 0, sizeof(cp));
if (send_cmd(mgmt, MGMT_OP_ADD_UUID, index, sizeof(cp), &cp,
class_rsp) == 0) {
- fprintf(stderr, "Unable to send add_uuid cmd\n");
- exit(EXIT_FAILURE);
+ error("Unable to send add_uuid cmd");
+ return noninteractive_quit(EXIT_FAILURE);
}
}
uuid_t uuid, uuid128;
if (argc < 2) {
- printf("UUID needed\n");
- exit(EXIT_FAILURE);
+ print("UUID needed");
+ return noninteractive_quit(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);
+ print("Invalid UUID: %s", argv[1]);
+ return noninteractive_quit(EXIT_FAILURE);
}
memset(&cp, 0, sizeof(cp));
if (send_cmd(mgmt, MGMT_OP_REMOVE_UUID, index, sizeof(cp), &cp,
class_rsp) == 0) {
- fprintf(stderr, "Unable to send remove_uuid cmd\n");
- exit(EXIT_FAILURE);
+ error("Unable to send remove_uuid cmd");
+ return noninteractive_quit(EXIT_FAILURE);
}
}
{
const struct mgmt_rp_read_local_oob_data *rp = param;
const struct mgmt_rp_read_local_oob_ext_data *rp_ext = param;
- int i;
+ char str[33];
if (status != 0) {
- fprintf(stderr, "Read Local OOB Data failed "
- "with status 0x%02x (%s)\n",
+ error("Read Local OOB Data failed with status 0x%02x (%s)",
status, mgmt_errstr(status));
- goto done;
+ return noninteractive_quit(EXIT_FAILURE);
}
if (len < sizeof(*rp)) {
- fprintf(stderr, "Too small (%u bytes) read_local_oob rsp\n",
- len);
- goto done;
+ error("Too small (%u bytes) read_local_oob rsp", len);
+ return noninteractive_quit(EXIT_FAILURE);
}
- printf("Hash C from P-192: ");
- for (i = 0; i < 16; i++)
- printf("%02x", rp->hash[i]);
- printf("\n");
+ bin2hex(rp->hash, 16, str, sizeof(str));
+ print("Hash C from P-192: %s", str);
- printf("Randomizer R with P-192: ");
- for (i = 0; i < 16; i++)
- printf("%02x", rp->randomizer[i]);
- printf("\n");
+ bin2hex(rp->randomizer, 16, str, sizeof(str));
+ print("Randomizer R with P-192: %s", str);
if (len < sizeof(*rp_ext))
- goto done;
+ return noninteractive_quit(EXIT_SUCCESS);
- printf("Hash C from P-256: ");
- for (i = 0; i < 16; i++)
- printf("%02x", rp_ext->hash256[i]);
- printf("\n");
+ bin2hex(rp_ext->hash256, 16, str, sizeof(str));
+ print("Hash C from P-256: %s", str);
- printf("Randomizer R with P-256: ");
- for (i = 0; i < 16; i++)
- printf("%02x", rp_ext->randomizer256[i]);
- printf("\n");
+ bin2hex(rp_ext->randomizer256, 16, str, sizeof(str));
+ print("Randomizer R with P-256: %s", str);
-done:
- mainloop_quit();
+ noninteractive_quit(EXIT_SUCCESS);
}
static void cmd_local_oob(struct mgmt *mgmt, uint16_t index,
if (mgmt_send(mgmt, MGMT_OP_READ_LOCAL_OOB_DATA, index, 0, NULL,
local_oob_rsp, NULL, NULL) == 0) {
- fprintf(stderr, "Unable to send read_local_oob cmd\n");
- exit(EXIT_FAILURE);
+ error("Unable to send read_local_oob cmd");
+ return noninteractive_quit(EXIT_FAILURE);
}
}
char addr[18];
if (status != 0) {
- fprintf(stderr, "Add Remote OOB Data failed: 0x%02x (%s)\n",
+ error("Add Remote OOB Data failed: 0x%02x (%s)",
status, mgmt_errstr(status));
return;
}
if (len < sizeof(*rp)) {
- fprintf(stderr, "Too small (%u bytes) add_remote_oob rsp\n",
- len);
+ error("Too small (%u bytes) add_remote_oob rsp", len);
return;
}
ba2str(&rp->bdaddr, addr);
- printf("Remote OOB data added for %s (%u)\n", addr, rp->type);
+ print("Remote OOB data added for %s (%u)", addr, rp->type);
}
static void remote_oob_usage(void)
{
- printf("Usage: btmgmt remote-oob [-t <addr_type>] "
+ print("Usage: remote-oob [-t <addr_type>] "
"[-r <rand192>] [-h <hash192>] [-R <rand256>] [-H <hash256>] "
- "<addr>\n");
+ "<addr>");
}
static struct option remote_oob_opt[] = {
cp.addr.type = strtol(optarg, NULL, 0);
break;
case 'r':
- convert_hexstr(optarg, cp.rand192, 16);
+ hex2bin(optarg, cp.rand192, 16);
break;
case 'h':
- convert_hexstr(optarg, cp.hash192, 16);
+ hex2bin(optarg, cp.hash192, 16);
break;
case 'R':
- convert_hexstr(optarg, cp.rand256, 16);
+ hex2bin(optarg, cp.rand256, 16);
break;
case 'H':
- convert_hexstr(optarg, cp.hash256, 16);
+ hex2bin(optarg, cp.hash256, 16);
break;
default:
remote_oob_usage();
- exit(EXIT_SUCCESS);
+ return noninteractive_quit(EXIT_FAILURE);
}
}
if (argc < 1) {
remote_oob_usage();
- exit(EXIT_FAILURE);
+ return noninteractive_quit(EXIT_FAILURE);
}
if (index == MGMT_INDEX_NONE)
str2ba(argv[0], &cp.addr.bdaddr);
- printf("Adding OOB data for %s (%s)\n", argv[0], typestr(cp.addr.type));
+ print("Adding OOB data for %s (%s)", argv[0], typestr(cp.addr.type));
if (mgmt_send(mgmt, MGMT_OP_ADD_REMOTE_OOB_DATA, index,
sizeof(cp), &cp, remote_oob_rsp,
NULL, NULL) == 0) {
- fprintf(stderr, "Unable to send add_remote_oob cmd\n");
- exit(EXIT_FAILURE);
+ error("Unable to send add_remote_oob cmd");
+ return noninteractive_quit(EXIT_FAILURE);
}
}
void *user_data)
{
if (status != 0)
- fprintf(stderr, "Set Device ID failed "
- "with status 0x%02x (%s)\n",
+ error("Set Device ID failed with status 0x%02x (%s)",
status, mgmt_errstr(status));
else
- printf("Device ID successfully set\n");
+ print("Device ID successfully set");
- mainloop_quit();
+ noninteractive_quit(EXIT_SUCCESS);
}
static void did_usage(void)
{
- printf("Usage: btmgmt did <source>:<vendor>:<product>:<version>\n");
- printf(" possible source values: bluetooth, usb\n");
+ print("Usage: did <source>:<vendor>:<product>:<version>");
+ print(" possible source values: bluetooth, usb");
}
static void cmd_did(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
if (argc < 2) {
did_usage();
- exit(EXIT_FAILURE);
+ return noninteractive_quit(EXIT_FAILURE);
}
result = sscanf(argv[1], "bluetooth:%4hx:%4hx:%4hx", &vendor, &product,
}
did_usage();
- exit(EXIT_FAILURE);
+ return noninteractive_quit(EXIT_FAILURE);
done:
if (index == MGMT_INDEX_NONE)
if (mgmt_send(mgmt, MGMT_OP_SET_DEVICE_ID, index, sizeof(cp), &cp,
did_rsp, NULL, NULL) == 0) {
- fprintf(stderr, "Unable to send set_device_id cmd\n");
- exit(EXIT_FAILURE);
+ error("Unable to send set_device_id cmd");
+ return noninteractive_quit(EXIT_FAILURE);
}
}
void *user_data)
{
if (status != 0)
- fprintf(stderr, "Set static address failed "
- "with status 0x%02x (%s)\n",
+ error("Set static address failed with status 0x%02x (%s)",
status, mgmt_errstr(status));
else
- printf("Static address successfully set\n");
+ print("Static address successfully set");
- mainloop_quit();
+ noninteractive_quit(EXIT_SUCCESS);
}
static void static_addr_usage(void)
{
- printf("Usage: btmgmt static-addr <address>\n");
+ print("Usage: static-addr <address>");
}
static void cmd_static_addr(struct mgmt *mgmt, uint16_t index,
if (argc < 2) {
static_addr_usage();
- exit(EXIT_FAILURE);
+ return noninteractive_quit(EXIT_FAILURE);
}
if (index == MGMT_INDEX_NONE)
if (mgmt_send(mgmt, MGMT_OP_SET_STATIC_ADDRESS, index, sizeof(cp), &cp,
static_addr_rsp, NULL, NULL) == 0) {
- fprintf(stderr, "Unable to send set_static_address cmd\n");
- exit(EXIT_FAILURE);
+ error("Unable to send set_static_address cmd");
+ return noninteractive_quit(EXIT_FAILURE);
}
}
const uint32_t *rp = param;
if (status != 0) {
- fprintf(stderr,
- "%s for hci%u failed with status 0x%02x (%s)\n",
+ error("%s for hci%u failed with status 0x%02x (%s)",
mgmt_opstr(op), id, status, mgmt_errstr(status));
- goto done;
+ return noninteractive_quit(EXIT_FAILURE);
}
if (len < sizeof(*rp)) {
- fprintf(stderr, "Too small %s response (%u bytes)\n",
+ error("Too small %s response (%u bytes)",
mgmt_opstr(op), len);
- goto done;
+ return noninteractive_quit(EXIT_FAILURE);
}
- printf("hci%u %s complete, options: ", id, mgmt_opstr(op));
- print_options(get_le32(rp));
- printf("\n");
+ print("hci%u %s complete, options: %s", id, mgmt_opstr(op),
+ options2str(get_le32(rp)));
-done:
- mainloop_quit();
+ noninteractive_quit(EXIT_SUCCESS);
}
static void cmd_public_addr(struct mgmt *mgmt, uint16_t index,
struct mgmt_cp_set_public_address cp;
if (argc < 2) {
- printf("Usage: btmgmt public-addr <address>\n");
- exit(EXIT_FAILURE);
+ print("Usage: public-addr <address>");
+ return noninteractive_quit(EXIT_FAILURE);
}
if (index == MGMT_INDEX_NONE)
if (send_cmd(mgmt, MGMT_OP_SET_PUBLIC_ADDRESS, index, sizeof(cp), &cp,
options_rsp) == 0) {
- fprintf(stderr, "Unable to send Set Public Address cmd\n");
- exit(EXIT_FAILURE);
+ error("Unable to send Set Public Address cmd");
+ return noninteractive_quit(EXIT_FAILURE);
}
}
struct mgmt_cp_set_external_config cp;
if (argc < 2) {
- printf("Specify \"on\" or \"off\"\n");
- exit(EXIT_FAILURE);
+ print("Specify \"on\" or \"off\"");
+ return noninteractive_quit(EXIT_FAILURE);
}
if (strcasecmp(argv[1], "on") == 0 || strcasecmp(argv[1], "yes") == 0)
if (send_cmd(mgmt, MGMT_OP_SET_EXTERNAL_CONFIG, index, sizeof(cp), &cp,
options_rsp) == 0) {
- fprintf(stderr, "Unable to send Set External Config cmd\n");
- exit(EXIT_FAILURE);
+ error("Unable to send Set External Config cmd");
+ return noninteractive_quit(EXIT_FAILURE);
}
}
const struct mgmt_rp_get_conn_info *rp = param; char addr[18];
if (len == 0 && status != 0) {
- fprintf(stderr, "Get Conn Info failed, status 0x%02x (%s)\n",
+ error("Get Conn Info failed, status 0x%02x (%s)",
status, mgmt_errstr(status));
- goto done;
+ return noninteractive_quit(EXIT_FAILURE);
}
if (len < sizeof(*rp)) {
- fprintf(stderr, "Unexpected Get Conn Info len %u\n", len);
- goto done;
+ error("Unexpected Get Conn Info len %u", len);
+ return noninteractive_quit(EXIT_FAILURE);
}
ba2str(&rp->addr.bdaddr, addr);
- if (status != 0) {
- fprintf(stderr, "Get Conn Info for %s (%s) failed. status 0x%02x (%s)\n",
+ if (status) {
+ error("Get Conn Info for %s (%s) failed. status 0x%02x (%s)",
addr, typestr(rp->addr.type),
status, mgmt_errstr(status));
- goto done;
- }
-
- printf("Connection Information for %s (%s)\n",
+ } else {
+ print("Connection Information for %s (%s)",
addr, typestr(rp->addr.type));
- printf("\tRSSI %d\n\tTX power %d\n\tmaximum TX power %d\n",
+ print("\tRSSI %d\tTX power %d\tmaximum TX power %d",
rp->rssi, rp->tx_power, rp->max_tx_power);
+ }
-done:
- mainloop_quit();
+ noninteractive_quit(EXIT_SUCCESS);
}
static void conn_info_usage(void)
{
- printf("Usage: btmgmt conn-info [-t type] <remote address>\n");
+ print("Usage: conn-info [-t type] <remote address>");
}
static struct option conn_info_options[] = {
type = strtol(optarg, NULL, 0);
break;
case 'h':
+ conn_info_usage();
+ return noninteractive_quit(EXIT_SUCCESS);
default:
conn_info_usage();
- exit(EXIT_SUCCESS);
+ return noninteractive_quit(EXIT_FAILURE);
}
}
if (argc < 1) {
conn_info_usage();
- exit(EXIT_FAILURE);
+ return noninteractive_quit(EXIT_FAILURE);
}
if (index == MGMT_INDEX_NONE)
if (mgmt_send(mgmt, MGMT_OP_GET_CONN_INFO, index, sizeof(cp), &cp,
conn_info_rsp, NULL, NULL) == 0) {
- fprintf(stderr, "Unable to send get_conn_info cmd\n");
- exit(EXIT_FAILURE);
+ error("Unable to send get_conn_info cmd");
+ return noninteractive_quit(EXIT_FAILURE);
}
}
void *user_data)
{
if (status != 0)
- fprintf(stderr, "Could not set IO Capability with "
- "status 0x%02x (%s)\n",
+ error("Could not set IO Capability with status 0x%02x (%s)",
status, mgmt_errstr(status));
else
- printf("IO Capabilities successfully set\n");
+ print("IO Capabilities successfully set");
- mainloop_quit();
+ noninteractive_quit(EXIT_SUCCESS);
}
static void io_cap_usage(void)
{
- printf("Usage: btmgmt io-cap <cap>\n");
+ print("Usage: io-cap <cap>");
}
static void cmd_io_cap(struct mgmt *mgmt, uint16_t index,
if (argc < 2) {
io_cap_usage();
- exit(EXIT_FAILURE);
+ return noninteractive_quit(EXIT_FAILURE);
}
if (index == MGMT_INDEX_NONE)
if (mgmt_send(mgmt, MGMT_OP_SET_IO_CAPABILITY, index, sizeof(cp), &cp,
io_cap_rsp, NULL, NULL) == 0) {
- fprintf(stderr, "Unable to send set-io-cap cmd\n");
- exit(EXIT_FAILURE);
+ error("Unable to send set-io-cap cmd");
+ return noninteractive_quit(EXIT_FAILURE);
}
}
void *user_data)
{
if (status != 0)
- fprintf(stderr, "Set scan parameters failed with status 0x%02x (%s)\n",
+ error("Set scan parameters failed with status 0x%02x (%s)",
status, mgmt_errstr(status));
else
- printf("Scan parameters successfully set\n");
+ print("Scan parameters successfully set");
- mainloop_quit();
+ noninteractive_quit(EXIT_SUCCESS);
}
static void scan_params_usage(void)
{
- printf("Usage: btmgmt scan-params <interval> <window>\n");
+ print("Usage: scan-params <interval> <window>");
}
static void cmd_scan_params(struct mgmt *mgmt, uint16_t index,
if (argc < 3) {
scan_params_usage();
- exit(EXIT_FAILURE);
+ return noninteractive_quit(EXIT_FAILURE);
}
if (index == MGMT_INDEX_NONE)
if (mgmt_send(mgmt, MGMT_OP_SET_SCAN_PARAMS, index, sizeof(cp), &cp,
scan_params_rsp, NULL, NULL) == 0) {
- fprintf(stderr, "Unable to send set_scan_params cmd\n");
- exit(EXIT_FAILURE);
+ error("Unable to send set_scan_params cmd");
+ return noninteractive_quit(EXIT_FAILURE);
}
}
const struct mgmt_rp_get_clock_info *rp = param;
if (len < sizeof(*rp)) {
- fprintf(stderr, "Unexpected Get Clock Info len %u\n", len);
- exit(EXIT_FAILURE);
+ error("Unexpected Get Clock Info len %u", len);
+ return noninteractive_quit(EXIT_FAILURE);
}
if (status) {
- fprintf(stderr, "Get Clock Info failed with status 0x%02x (%s)\n",
+ error("Get Clock Info failed with status 0x%02x (%s)",
status, mgmt_errstr(status));
- exit(EXIT_FAILURE);
+ return noninteractive_quit(EXIT_FAILURE);
}
- printf("Local Clock: %u\n", le32_to_cpu(rp->local_clock));
- printf("Piconet Clock: %u\n", le32_to_cpu(rp->piconet_clock));
- printf("Accurary: %u\n", le16_to_cpu(rp->accuracy));
+ print("Local Clock: %u", le32_to_cpu(rp->local_clock));
+ print("Piconet Clock: %u", le32_to_cpu(rp->piconet_clock));
+ print("Accurary: %u", le16_to_cpu(rp->accuracy));
- mainloop_quit();
+ noninteractive_quit(EXIT_SUCCESS);
}
static void cmd_clock_info(struct mgmt *mgmt, uint16_t index,
if (mgmt_send(mgmt, MGMT_OP_GET_CLOCK_INFO, index, sizeof(cp), &cp,
clock_info_rsp, NULL, NULL) == 0) {
- fprintf(stderr, "Unable to send get_clock_info cmd\n");
- exit(EXIT_FAILURE);
+ error("Unable to send get_clock_info cmd");
+ return noninteractive_quit(EXIT_FAILURE);
}
}
void *user_data)
{
if (status != 0)
- fprintf(stderr, "Add device failed with status 0x%02x (%s)\n",
+ error("Add device failed with status 0x%02x (%s)",
status, mgmt_errstr(status));
- mainloop_quit();
+ noninteractive_quit(EXIT_SUCCESS);
}
static void add_device_usage(void)
{
- printf("Usage: btmgmt add-device [-a action] [-t type] <address>\n");
+ print("Usage: add-device [-a action] [-t type] <address>");
}
static struct option add_device_options[] = {
type = strtol(optarg, NULL, 0);
break;
case 'h':
+ add_device_usage();
+ return noninteractive_quit(EXIT_SUCCESS);
default:
add_device_usage();
- exit(EXIT_SUCCESS);
+ return noninteractive_quit(EXIT_FAILURE);
}
}
if (argc < 1) {
add_device_usage();
- exit(EXIT_FAILURE);
+ return noninteractive_quit(EXIT_FAILURE);
}
if (index == MGMT_INDEX_NONE)
cp.action = action;
ba2str(&cp.addr.bdaddr, addr);
- printf("Adding device with %s (%s)\n", addr, typestr(cp.addr.type));
+ print("Adding device with %s (%s)", addr, typestr(cp.addr.type));
if (mgmt_send(mgmt, MGMT_OP_ADD_DEVICE, index, sizeof(cp), &cp,
add_device_rsp, NULL, NULL) == 0) {
- fprintf(stderr, "Unable to send add device command\n");
- exit(EXIT_FAILURE);
+ error("Unable to send add device command");
+ return noninteractive_quit(EXIT_FAILURE);
}
}
void *user_data)
{
if (status != 0)
- fprintf(stderr, "Remove device failed with status 0x%02x (%s)\n",
+ error("Remove device failed with status 0x%02x (%s)",
status, mgmt_errstr(status));
- mainloop_quit();
+ noninteractive_quit(EXIT_SUCCESS);
}
static void del_device_usage(void)
{
- printf("Usage: btmgmt del-device [-t type] <address>\n");
+ print("Usage: del-device [-t type] <address>");
}
static struct option del_device_options[] = {
type = strtol(optarg, NULL, 0);
break;
case 'h':
+ del_device_usage();
+ return noninteractive_quit(EXIT_SUCCESS);
default:
del_device_usage();
- exit(EXIT_SUCCESS);
+ return noninteractive_quit(EXIT_FAILURE);
}
}
if (argc < 1) {
del_device_usage();
- exit(EXIT_FAILURE);
+ return noninteractive_quit(EXIT_FAILURE);
}
if (index == MGMT_INDEX_NONE)
cp.addr.type = type;
ba2str(&cp.addr.bdaddr, addr);
- printf("Removing device with %s (%s)\n", addr, typestr(cp.addr.type));
+ print("Removing device with %s (%s)", addr, typestr(cp.addr.type));
if (mgmt_send(mgmt, MGMT_OP_REMOVE_DEVICE, index, sizeof(cp), &cp,
remove_device_rsp, NULL, NULL) == 0) {
- fprintf(stderr, "Unable to send remove device command\n");
- exit(EXIT_FAILURE);
+ error("Unable to send remove device command");
+ return noninteractive_quit(EXIT_FAILURE);
}
}
cmd_del_device(mgmt, index, 2, rm_argv);
}
-static struct {
+struct cmd_info {
char *cmd;
void (*func)(struct mgmt *mgmt, uint16_t index, int argc, char **argv);
char *doc;
-} command[] = {
- { "monitor", cmd_monitor, "Monitor events" },
+ char * (*gen) (const char *text, int state);
+ void (*disp) (char **matches, int num_matches, int max_length);
+};
+
+static struct cmd_info all_cmd[] = {
{ "version", cmd_version, "Get the MGMT Version" },
{ "commands", cmd_commands, "List supported commands" },
{ "config", cmd_config, "Show configuration info" },
{ "add-device", cmd_add_device, "Add Device" },
{ "del-device", cmd_del_device, "Remove Device" },
{ "clr-devices",cmd_clr_devices,"Clear Devices" },
- { }
};
-static void gap_ready(bool status, void *user_data)
+static void cmd_quit(struct mgmt *mgmt, uint16_t index,
+ int argc, char **argv)
+{
+ mainloop_exit_success();
+}
+
+static void register_mgmt_callbacks(struct mgmt *mgmt, uint16_t index)
+{
+ mgmt_register(mgmt, MGMT_EV_CONTROLLER_ERROR, index, controller_error,
+ NULL, NULL);
+ mgmt_register(mgmt, MGMT_EV_INDEX_ADDED, index, index_added,
+ NULL, NULL);
+ mgmt_register(mgmt, MGMT_EV_INDEX_REMOVED, index, index_removed,
+ NULL, NULL);
+ mgmt_register(mgmt, MGMT_EV_NEW_SETTINGS, index, new_settings,
+ NULL, NULL);
+ mgmt_register(mgmt, MGMT_EV_DISCOVERING, index, discovering,
+ NULL, NULL);
+ mgmt_register(mgmt, MGMT_EV_NEW_LINK_KEY, index, new_link_key,
+ NULL, NULL);
+ mgmt_register(mgmt, MGMT_EV_DEVICE_CONNECTED, index, connected,
+ NULL, NULL);
+ mgmt_register(mgmt, MGMT_EV_DEVICE_DISCONNECTED, index, disconnected,
+ NULL, NULL);
+ mgmt_register(mgmt, MGMT_EV_CONNECT_FAILED, index, conn_failed,
+ NULL, NULL);
+ mgmt_register(mgmt, MGMT_EV_AUTH_FAILED, index, auth_failed,
+ NULL, NULL);
+ mgmt_register(mgmt, MGMT_EV_LOCAL_NAME_CHANGED, index,
+ local_name_changed, NULL, NULL);
+ mgmt_register(mgmt, MGMT_EV_DEVICE_FOUND, index, device_found,
+ mgmt, NULL);
+ mgmt_register(mgmt, MGMT_EV_PIN_CODE_REQUEST, index, request_pin,
+ mgmt, NULL);
+ mgmt_register(mgmt, MGMT_EV_USER_CONFIRM_REQUEST, index, user_confirm,
+ mgmt, NULL);
+ mgmt_register(mgmt, MGMT_EV_USER_PASSKEY_REQUEST, index,
+ request_passkey, mgmt, NULL);
+ mgmt_register(mgmt, MGMT_EV_PASSKEY_NOTIFY, index,
+ passkey_notify, mgmt, NULL);
+ mgmt_register(mgmt, MGMT_EV_UNCONF_INDEX_ADDED, index,
+ unconf_index_added, NULL, NULL);
+ mgmt_register(mgmt, MGMT_EV_UNCONF_INDEX_REMOVED, index,
+ unconf_index_removed, NULL, NULL);
+ mgmt_register(mgmt, MGMT_EV_NEW_CONFIG_OPTIONS, index,
+ new_config_options, NULL, NULL);
+
+}
+
+static void cmd_select(struct mgmt *mgmt, uint16_t index,
+ int argc, char **argv)
+{
+ if (argc != 2) {
+ error("Usage: select <index>");
+ return;
+ }
+
+ mgmt_cancel_all(mgmt);
+ mgmt_unregister_all(mgmt);
+
+ if (!strcmp(argv[1], "none") || !strcmp(argv[1], "any") ||
+ !strcmp(argv[1], "all"))
+ mgmt_index = MGMT_INDEX_NONE;
+ else if (!strncmp(argv[1], "hci", 3))
+ mgmt_index = atoi(&argv[1][3]);
+ else
+ mgmt_index = atoi(argv[1]);
+
+ register_mgmt_callbacks(mgmt, mgmt_index);
+
+ print("Selected index %u", mgmt_index);
+
+ update_prompt(mgmt_index);
+}
+
+static struct cmd_info interactive_cmd[] = {
+ { "select", cmd_select, "Select a different index" },
+ { "quit", cmd_quit, "Exit program" },
+ { "exit", cmd_quit, "Exit program" },
+ { "help", NULL, "List supported commands" },
+};
+
+static char *cmd_generator(const char *text, int state)
+{
+ static size_t i, j, len;
+ const char *cmd;
+
+ if (!state) {
+ i = 0;
+ j = 0;
+ len = strlen(text);
+ }
+
+ while (i < NELEM(all_cmd)) {
+ cmd = all_cmd[i++].cmd;
+
+ if (!strncmp(cmd, text, len))
+ return strdup(cmd);
+ }
+
+ while (j < NELEM(interactive_cmd)) {
+ cmd = interactive_cmd[j++].cmd;
+
+ if (!strncmp(cmd, text, len))
+ return strdup(cmd);
+ }
+
+ return NULL;
+}
+
+static char **cmd_completion(const char *text, int start, int end)
+{
+ char **matches = NULL;
+
+ if (start > 0) {
+ unsigned int i;
+
+ for (i = 0; i < NELEM(all_cmd); i++) {
+ struct cmd_info *c = &all_cmd[i];
+
+ if (strncmp(c->cmd, rl_line_buffer, start - 1))
+ continue;
+
+ if (!c->gen)
+ continue;
+
+ rl_completion_display_matches_hook = c->disp;
+ matches = rl_completion_matches(text, c->gen);
+ break;
+ }
+ } else {
+ rl_completion_display_matches_hook = NULL;
+ matches = rl_completion_matches(text, cmd_generator);
+ }
+
+ if (!matches)
+ rl_attempted_completion_over = 1;
+
+ return matches;
+}
+
+static struct cmd_info *find_cmd(const char *cmd, struct cmd_info table[],
+ size_t cmd_count)
+{
+ size_t i;
+
+ for (i = 0; i < cmd_count; i++) {
+ if (!strcmp(table[i].cmd, cmd))
+ return &table[i];
+ }
+
+ return NULL;
+}
+
+static void rl_handler(char *input)
{
+ struct cmd_info *c;
+ wordexp_t w;
+ char *cmd, **argv;
+ size_t argc, i;
+
+ if (!input) {
+ rl_insert_text("quit");
+ rl_redisplay();
+ rl_crlf();
+ mainloop_quit();
+ return;
+ }
+
+ if (!strlen(input))
+ goto done;
+
+ if (prompt_input(input))
+ goto done;
+
+ add_history(input);
+
+ if (wordexp(input, &w, WRDE_NOCMD))
+ goto done;
+
+ if (w.we_wordc == 0)
+ goto free_we;
+
+ cmd = w.we_wordv[0];
+ argv = w.we_wordv;
+ argc = w.we_wordc;
+
+ c = find_cmd(cmd, all_cmd, NELEM(all_cmd));
+ if (!c && interactive)
+ c = find_cmd(cmd, interactive_cmd, NELEM(interactive_cmd));
+
+ if (c && c->func) {
+ c->func(mgmt, mgmt_index, argc, argv);
+ goto free_we;
+ }
+
+ if (strcmp(cmd, "help")) {
+ print("Invalid command");
+ goto free_we;
+ }
+
+ print("Available commands:");
+
+ for (i = 0; i < NELEM(all_cmd); i++) {
+ c = &all_cmd[i];
+ if (c->doc)
+ print(" %s %-*s %s", c->cmd,
+ (int)(25 - strlen(c->cmd)), "", c->doc ? : "");
+ }
+
+ if (!interactive)
+ goto free_we;
+
+ for (i = 0; i < NELEM(interactive_cmd); i++) {
+ c = &interactive_cmd[i];
+ if (c->doc)
+ print(" %s %-*s %s", c->cmd,
+ (int)(25 - strlen(c->cmd)), "", c->doc ? : "");
+ }
+
+free_we:
+ wordfree(&w);
+done:
+ free(input);
}
static void usage(void)
{
- int i;
+ unsigned int i;
printf("btmgmt ver %s\n", VERSION);
printf("Usage:\n"
"\t--help\tDisplay help\n");
printf("Commands:\n");
- for (i = 0; command[i].cmd; i++)
- printf("\t%-15s\t%s\n", command[i].cmd, command[i].doc);
+ for (i = 0; i < NELEM(all_cmd); i++)
+ printf("\t%-15s\t%s\n", all_cmd[i].cmd, all_cmd[i].doc);
printf("\n"
"For more information on the usage of each command use:\n"
{ 0, 0, 0, 0 }
};
+static bool prompt_read(struct io *io, void *user_data)
+{
+ rl_callback_read_char();
+ return true;
+}
+
+static struct io *setup_stdin(void)
+{
+ struct io *io;
+
+ io = io_new(STDIN_FILENO);
+ if (!io)
+ return io;
+
+ io_set_read_handler(io, prompt_read, NULL, NULL);
+
+ return io;
+}
+
int main(int argc, char *argv[])
{
- struct bt_gap *gap;
- int opt, i;
+ struct io *input;
uint16_t index = MGMT_INDEX_NONE;
- struct mgmt *mgmt;
- int exit_status;
+ int status, opt;
- while ((opt = getopt_long(argc, argv, "+hvi:",
+ while ((opt = getopt_long(argc, argv, "+hi:",
main_options, NULL)) != -1) {
switch (opt) {
case 'i':
else
index = atoi(optarg);
break;
- case 'v':
- monitor = true;
- break;
case 'h':
default:
usage();
argv += optind;
optind = 0;
- if (argc < 1) {
- usage();
- return 0;
- }
-
mainloop_init();
- if (index == MGMT_INDEX_NONE)
- gap = bt_gap_new_default();
- else
- gap = bt_gap_new_index(index);
-
- bt_gap_set_ready_handler(gap, gap_ready, NULL, NULL);
-
mgmt = mgmt_new_default();
if (!mgmt) {
fprintf(stderr, "Unable to open mgmt_socket\n");
return EXIT_FAILURE;
}
- for (i = 0; command[i].cmd; i++) {
- if (strcmp(command[i].cmd, argv[0]) != 0)
- continue;
+ if (argc > 0) {
+ struct cmd_info *c;
+
+ c = find_cmd(argv[0], all_cmd, NELEM(all_cmd));
+ if (!c) {
+ fprintf(stderr, "Unknown command: %s\n", argv[0]);
+ mgmt_unref(mgmt);
+ return EXIT_FAILURE;
+ }
- command[i].func(mgmt, index, argc, argv);
- break;
+ c->func(mgmt, index, argc, argv);
}
- if (command[i].cmd == NULL) {
- fprintf(stderr, "Unknown command: %s\n", argv[0]);
- mgmt_unref(mgmt);
- return EXIT_FAILURE;
+ register_mgmt_callbacks(mgmt, index);
+
+ /* Interactive mode */
+ if (!argc)
+ input = setup_stdin();
+ else
+ input = NULL;
+
+ if (input) {
+ interactive = true;
+
+ rl_attempted_completion_function = cmd_completion;
+
+ rl_erase_empty_line = 1;
+ rl_callback_handler_install(NULL, rl_handler);
+
+ update_prompt(index);
+ rl_redisplay();
}
- mgmt_register(mgmt, MGMT_EV_CONTROLLER_ERROR, index, controller_error,
- NULL, NULL);
- mgmt_register(mgmt, MGMT_EV_INDEX_ADDED, index, index_added,
- NULL, NULL);
- mgmt_register(mgmt, MGMT_EV_INDEX_REMOVED, index, index_removed,
- NULL, NULL);
- mgmt_register(mgmt, MGMT_EV_NEW_SETTINGS, index, new_settings,
- NULL, NULL);
- mgmt_register(mgmt, MGMT_EV_DISCOVERING, index, discovering,
- NULL, NULL);
- mgmt_register(mgmt, MGMT_EV_NEW_LINK_KEY, index, new_link_key,
- NULL, NULL);
- mgmt_register(mgmt, MGMT_EV_DEVICE_CONNECTED, index, connected,
- NULL, NULL);
- mgmt_register(mgmt, MGMT_EV_DEVICE_DISCONNECTED, index, disconnected,
- NULL, NULL);
- mgmt_register(mgmt, MGMT_EV_CONNECT_FAILED, index, conn_failed,
- NULL, NULL);
- mgmt_register(mgmt, MGMT_EV_AUTH_FAILED, index, auth_failed,
- NULL, NULL);
- mgmt_register(mgmt, MGMT_EV_LOCAL_NAME_CHANGED, index,
- local_name_changed, NULL, NULL);
- mgmt_register(mgmt, MGMT_EV_DEVICE_FOUND, index, device_found,
- mgmt, NULL);
- mgmt_register(mgmt, MGMT_EV_PIN_CODE_REQUEST, index, request_pin,
- mgmt, NULL);
- mgmt_register(mgmt, MGMT_EV_USER_CONFIRM_REQUEST, index, user_confirm,
- mgmt, NULL);
- mgmt_register(mgmt, MGMT_EV_USER_PASSKEY_REQUEST, index,
- request_passkey, mgmt, NULL);
- mgmt_register(mgmt, MGMT_EV_PASSKEY_NOTIFY, index,
- passkey_notify, mgmt, NULL);
- mgmt_register(mgmt, MGMT_EV_UNCONF_INDEX_ADDED, index,
- unconf_index_added, NULL, NULL);
- mgmt_register(mgmt, MGMT_EV_UNCONF_INDEX_REMOVED, index,
- unconf_index_removed, NULL, NULL);
- mgmt_register(mgmt, MGMT_EV_NEW_CONFIG_OPTIONS, index,
- new_config_options, NULL, NULL);
+ mgmt_index = index;
- exit_status = mainloop_run();
+ status = mainloop_run();
+
+ if (input) {
+ io_destroy(input);
+
+ rl_message("");
+ rl_callback_handler_remove();
+ }
mgmt_cancel_all(mgmt);
mgmt_unregister_all(mgmt);
mgmt_unref(mgmt);
- bt_gap_unref(gap);
-
- return exit_status;
+ return status;
}
#include <arpa/inet.h>
#include "src/shared/util.h"
-#include "monitor/mainloop.h"
+#include "src/shared/mainloop.h"
#include "monitor/bt.h"
+#define HCI_BREDR 0x00
+#define HCI_AMP 0x01
+
#define BTPROTO_HCI 1
struct sockaddr_hci {
sa_family_t hci_family;
"\t-u, --unix [path] Use Unix server\n"
"\t-p, --port <port> Use specified TCP port\n"
"\t-i, --index <num> Use specified controller\n"
+ "\t-a, --amp Create AMP controller\n"
"\t-d, --debug Enable debugging output\n"
"\t-h, --help Show help options\n");
}
{ "unix", optional_argument, NULL, 'u' },
{ "port", required_argument, NULL, 'p' },
{ "index", required_argument, NULL, 'i' },
+ { "amp", no_argument, NULL, 'a' },
{ "debug", no_argument, NULL, 'd' },
{ "version", no_argument, NULL, 'v' },
{ "help", no_argument, NULL, 'h' },
const char *server_address = NULL;
const char *unix_path = NULL;
unsigned short tcp_port = 0xb1ee; /* 45550 */
+ uint8_t type = HCI_BREDR;
const char *str;
sigset_t mask;
for (;;) {
int opt;
- opt = getopt_long(argc, argv, "c:l::u::p:i:dvh",
+ opt = getopt_long(argc, argv, "ac:l::u::p:i:dvh",
main_options, NULL);
if (opt < 0)
break;
switch (opt) {
+ case 'a':
+ type = HCI_AMP;
+ break;
case 'c':
connect_address = optarg;
break;
printf("Opening virtual device\n");
- host_fd = open_vhci(0x00);
+ host_fd = open_vhci(type);
if (host_fd < 0) {
close(dev_fd);
return EXIT_FAILURE;
if (flags & 0x01)
opcode = BTSNOOP_OPCODE_SCO_RX_PKT;
else
- opcode = BTSNOOP_OPCODE_ACL_TX_PKT;
+ opcode = BTSNOOP_OPCODE_SCO_TX_PKT;
break;
case 0x04:
opcode = BTSNOOP_OPCODE_EVENT_PKT;
#include <sys/ioctl.h>
#include <sys/socket.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/hci_lib.h>
-#include <bluetooth/l2cap.h>
-#include <bluetooth/sdp.h>
-#include <bluetooth/sdp_lib.h>
-#include <bluetooth/cmtp.h>
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+#include "lib/l2cap.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+#include "lib/cmtp.h"
static volatile sig_atomic_t __io_canceled = 0;
#include <sys/ioctl.h>
#include <sys/socket.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/hci_lib.h>
-#include <bluetooth/l2cap.h>
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+#include "lib/l2cap.h"
-#include "monitor/mainloop.h"
+#include "src/shared/mainloop.h"
static bool send_message(const bdaddr_t *src, const bdaddr_t *dst,
uint16_t psm)
#include <sys/mman.h>
#include <sys/socket.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/hci_lib.h>
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
#include "csr.h"
#include <string.h>
#include <sys/socket.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/hci_lib.h>
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
#include "csr.h"
#include <config.h>
#endif
-#include <gdbus.h>
+#include "gdbus/gdbus.h"
#include "src/shared/tester.h"
#include "emulator/hciemu.h"
--- /dev/null
+#!/usr/bin/python
+
+import dbus
+import dbus.exceptions
+import dbus.mainloop.glib
+import dbus.service
+
+import array
+import gobject
+
+from random import randint
+
+mainloop = None
+
+BLUEZ_SERVICE_NAME = 'org.bluez'
+GATT_MANAGER_IFACE = 'org.bluez.GattManager1'
+DBUS_OM_IFACE = 'org.freedesktop.DBus.ObjectManager'
+DBUS_PROP_IFACE = 'org.freedesktop.DBus.Properties'
+
+GATT_SERVICE_IFACE = 'org.bluez.GattService1'
+GATT_CHRC_IFACE = 'org.bluez.GattCharacteristic1'
+GATT_DESC_IFACE = 'org.bluez.GattDescriptor1'
+
+class InvalidArgsException(dbus.exceptions.DBusException):
+ _dbus_error_name = 'org.freedesktop.DBus.Error.InvalidArgs'
+
+class NotSupportedException(dbus.exceptions.DBusException):
+ _dbus_error_name = 'org.bluez.Error.NotSupported'
+
+class NotPermittedException(dbus.exceptions.DBusException):
+ _dbus_error_name = 'org.bluez.Error.NotPermitted'
+
+class InvalidValueLengthException(dbus.exceptions.DBusException):
+ _dbus_error_name = 'org.bluez.Error.InvalidValueLength'
+
+class FailedException(dbus.exceptions.DBusException):
+ _dbus_error_name = 'org.bluez.Error.Failed'
+
+
+class Service(dbus.service.Object):
+ PATH_BASE = '/org/bluez/example/service'
+
+ def __init__(self, bus, index, uuid, primary):
+ self.path = self.PATH_BASE + str(index)
+ self.bus = bus
+ self.uuid = uuid
+ self.primary = primary
+ self.characteristics = []
+ dbus.service.Object.__init__(self, bus, self.path)
+
+ def get_properties(self):
+ return {
+ GATT_SERVICE_IFACE: {
+ 'UUID': self.uuid,
+ 'Primary': self.primary,
+ 'Characteristics': dbus.Array(
+ self.get_characteristic_paths(),
+ signature='o')
+ }
+ }
+
+ def get_path(self):
+ return dbus.ObjectPath(self.path)
+
+ def add_characteristic(self, characteristic):
+ self.characteristics.append(characteristic)
+
+ def get_characteristic_paths(self):
+ result = []
+ for chrc in self.characteristics:
+ result.append(chrc.get_path())
+ return result
+
+ def get_characteristics(self):
+ return self.characteristics
+
+ @dbus.service.method(DBUS_PROP_IFACE,
+ in_signature='s',
+ out_signature='a{sv}')
+ def GetAll(self, interface):
+ if interface != GATT_SERVICE_IFACE:
+ raise InvalidArgsException()
+
+ return self.get_properties[GATT_SERVICE_IFACE]
+
+ @dbus.service.method(DBUS_OM_IFACE, out_signature='a{oa{sa{sv}}}')
+ def GetManagedObjects(self):
+ response = {}
+ print 'GetManagedObjects'
+
+ response[self.get_path()] = self.get_properties()
+ chrcs = self.get_characteristics()
+ for chrc in chrcs:
+ response[chrc.get_path()] = chrc.get_properties()
+ descs = chrc.get_descriptors()
+ for desc in descs:
+ response[desc.get_path()] = desc.get_properties()
+
+ return response
+
+
+class Characteristic(dbus.service.Object):
+ def __init__(self, bus, index, uuid, flags, service):
+ self.path = service.path + '/char' + str(index)
+ self.bus = bus
+ self.uuid = uuid
+ self.service = service
+ self.flags = flags
+ self.descriptors = []
+ dbus.service.Object.__init__(self, bus, self.path)
+
+ def get_properties(self):
+ return {
+ GATT_CHRC_IFACE: {
+ 'Service': self.service.get_path(),
+ 'UUID': self.uuid,
+ 'Flags': self.flags,
+ 'Descriptors': dbus.Array(
+ self.get_descriptor_paths(),
+ signature='o')
+ }
+ }
+
+ def get_path(self):
+ return dbus.ObjectPath(self.path)
+
+ def add_descriptor(self, descriptor):
+ self.descriptors.append(descriptor)
+
+ def get_descriptor_paths(self):
+ result = []
+ for desc in self.descriptors:
+ result.append(desc.get_path())
+ return result
+
+ def get_descriptors(self):
+ return self.descriptors
+
+ @dbus.service.method(DBUS_PROP_IFACE,
+ in_signature='s',
+ out_signature='a{sv}')
+ def GetAll(self, interface):
+ if interface != GATT_CHRC_IFACE:
+ raise InvalidArgsException()
+
+ return self.get_properties[GATT_CHRC_IFACE]
+
+ @dbus.service.method(GATT_CHRC_IFACE, out_signature='ay')
+ def ReadValue(self):
+ print 'Default ReadValue called, returning error'
+ raise NotSupportedException()
+
+ @dbus.service.method(GATT_CHRC_IFACE, in_signature='ay')
+ def WriteValue(self, value):
+ print 'Default WriteValue called, returning error'
+ raise NotSupportedException()
+
+ @dbus.service.method(GATT_CHRC_IFACE)
+ def StartNotify(self):
+ print 'Default StartNotify called, returning error'
+ raise NotSupportedException()
+
+ @dbus.service.method(GATT_CHRC_IFACE)
+ def StopNotify(self):
+ print 'Default StopNotify called, returning error'
+ raise NotSupportedException()
+
+ @dbus.service.signal(DBUS_PROP_IFACE,
+ signature='sa{sv}as')
+ def PropertiesChanged(self, interface, changed, invalidated):
+ pass
+
+
+class Descriptor(dbus.service.Object):
+ def __init__(self, bus, index, uuid, characteristic):
+ self.path = characteristic.path + '/desc' + str(index)
+ self.bus = bus
+ self.uuid = uuid
+ self.chrc = characteristic
+ dbus.service.Object.__init__(self, bus, self.path)
+
+ def get_properties(self):
+ return {
+ GATT_DESC_IFACE: {
+ 'Characteristic': self.chrc.get_path(),
+ 'UUID': self.uuid,
+ }
+ }
+
+ def get_path(self):
+ return dbus.ObjectPath(self.path)
+
+ @dbus.service.method(DBUS_PROP_IFACE,
+ in_signature='s',
+ out_signature='a{sv}')
+ def GetAll(self, interface):
+ if interface != GATT_DESC_IFACE:
+ raise InvalidArgsException()
+
+ return self.get_properties[GATT_CHRC_IFACE]
+
+ @dbus.service.method(GATT_DESC_IFACE, out_signature='ay')
+ def ReadValue(self):
+ print 'Default ReadValue called, returning error'
+ raise NotSupportedException()
+
+ @dbus.service.method(GATT_DESC_IFACE, in_signature='ay')
+ def WriteValue(self, value):
+ print 'Default WriteValue called, returning error'
+ raise NotSupportedException()
+
+
+class HeartRateService(Service):
+ """
+ Fake Heart Rate Service that simulates a fake heart beat and control point
+ behavior.
+
+ """
+ HR_UUID = '0000180d-0000-1000-8000-00805f9b34fb'
+
+ def __init__(self, bus, index):
+ Service.__init__(self, bus, index, self.HR_UUID, True)
+ self.add_characteristic(HeartRateMeasurementChrc(bus, 0, self))
+ self.add_characteristic(BodySensorLocationChrc(bus, 1, self))
+ self.add_characteristic(HeartRateControlPointChrc(bus, 2, self))
+ self.energy_expended = 0
+
+
+class HeartRateMeasurementChrc(Characteristic):
+ HR_MSRMT_UUID = '00002a37-0000-1000-8000-00805f9b34fb'
+
+ def __init__(self, bus, index, service):
+ Characteristic.__init__(
+ self, bus, index,
+ self.HR_MSRMT_UUID,
+ ['notify'],
+ service)
+ self.notifying = False
+ self.hr_ee_count = 0
+
+ def hr_msrmt_cb(self):
+ value = []
+ value.append(dbus.Byte(0x06))
+
+ value.append(dbus.Byte(randint(90, 130)))
+
+ if self.hr_ee_count % 10 == 0:
+ value[0] = dbus.Byte(value[0] | 0x08)
+ value.append(dbus.Byte(self.service.energy_expended & 0xff))
+ value.append(dbus.Byte((self.service.energy_expended >> 8) & 0xff))
+
+ self.service.energy_expended = \
+ min(0xffff, self.service.energy_expended + 1)
+ self.hr_ee_count += 1
+
+ print 'Updating value: ' + repr(value)
+
+ self.PropertiesChanged(GATT_CHRC_IFACE, { 'Value': value }, [])
+
+ return self.notifying
+
+ def _update_hr_msrmt_simulation(self):
+ print 'Update HR Measurement Simulation'
+
+ if not self.notifying:
+ return
+
+ gobject.timeout_add(1000, self.hr_msrmt_cb)
+
+ def StartNotify(self):
+ if self.notifying:
+ print 'Already notifying, nothing to do'
+ return
+
+ self.notifying = True
+ self._update_hr_msrmt_simulation()
+
+ def StopNotify(self):
+ if not self.notifying:
+ print 'Not notifying, nothing to do'
+ return
+
+ self.notifying = False
+ self._update_hr_msrmt_simulation()
+
+
+class BodySensorLocationChrc(Characteristic):
+ BODY_SNSR_LOC_UUID = '00002a38-0000-1000-8000-00805f9b34fb'
+
+ def __init__(self, bus, index, service):
+ Characteristic.__init__(
+ self, bus, index,
+ self.BODY_SNSR_LOC_UUID,
+ ['read'],
+ service)
+
+ def ReadValue(self):
+ # Return 'Chest' as the sensor location.
+ return [ 0x01 ]
+
+class HeartRateControlPointChrc(Characteristic):
+ HR_CTRL_PT_UUID = '00002a39-0000-1000-8000-00805f9b34fb'
+
+ def __init__(self, bus, index, service):
+ Characteristic.__init__(
+ self, bus, index,
+ self.HR_CTRL_PT_UUID,
+ ['write'],
+ service)
+
+ def WriteValue(self, value):
+ print 'Heart Rate Control Point WriteValue called'
+
+ if len(value) != 1:
+ raise InvalidValueLengthException()
+
+ byte = value[0]
+ print 'Control Point value: ' + repr(byte)
+
+ if byte != 1:
+ raise FailedException("0x80")
+
+ print 'Energy Expended field reset!'
+ self.service.energy_expended = 0
+
+
+class BatteryService(Service):
+ """
+ Fake Battery service that emulates a draining battery.
+
+ """
+ BATTERY_UUID = '180f'
+
+ def __init__(self, bus, index):
+ Service.__init__(self, bus, index, self.BATTERY_UUID, True)
+ self.add_characteristic(BatteryLevelCharacteristic(bus, 0, self))
+
+
+class BatteryLevelCharacteristic(Characteristic):
+ """
+ Fake Battery Level characteristic. The battery level is drained by 2 points
+ every 5 seconds.
+
+ """
+ BATTERY_LVL_UUID = '2a19'
+
+ def __init__(self, bus, index, service):
+ Characteristic.__init__(
+ self, bus, index,
+ self.BATTERY_LVL_UUID,
+ ['read', 'notify'],
+ service)
+ self.notifying = False
+ self.battery_lvl = 100
+ gobject.timeout_add(5000, self.drain_battery)
+
+ def notify_battery_level(self):
+ if not self.notifying:
+ return
+ self.PropertiesChanged(
+ GATT_CHRC_IFACE,
+ { 'Value': [dbus.Byte(self.battery_lvl)] }, [])
+
+ def drain_battery(self):
+ if self.battery_lvl > 0:
+ self.battery_lvl -= 2
+ if self.battery_lvl < 0:
+ self.battery_lvl = 0
+ print 'Battery Level drained: ' + repr(self.battery_lvl)
+ self.notify_battery_level()
+ return True
+
+ def ReadValue(self):
+ print 'Battery Level read: ' + repr(self.battery_lvl)
+ return [dbus.Byte(self.battery_lvl)]
+
+ def StartNotify(self):
+ if self.notifying:
+ print 'Already notifying, nothing to do'
+ return
+
+ self.notifying = True
+ self.notify_battery_level()
+
+ def StopNotify(self):
+ if not self.notifying:
+ print 'Not notifying, nothing to do'
+ return
+
+ self.notifying = False
+
+
+class TestService(Service):
+ """
+ Dummy test service that provides characteristics and descriptors that
+ exercise various API functionality.
+
+ """
+ TEST_SVC_UUID = '12345678-1234-5678-1234-56789abcdef0'
+
+ def __init__(self, bus, index):
+ Service.__init__(self, bus, index, self.TEST_SVC_UUID, False)
+ self.add_characteristic(TestCharacteristic(bus, 0, self))
+
+
+class TestCharacteristic(Characteristic):
+ """
+ Dummy test characteristic. Allows writing arbitrary bytes to its value, and
+ contains "extended properties", as well as a test descriptor.
+
+ """
+ TEST_CHRC_UUID = '12345678-1234-5678-1234-56789abcdef1'
+
+ def __init__(self, bus, index, service):
+ Characteristic.__init__(
+ self, bus, index,
+ self.TEST_CHRC_UUID,
+ ['read', 'write', 'writable-auxiliaries'],
+ service)
+ self.value = []
+ self.add_descriptor(TestDescriptor(bus, 0, self))
+ self.add_descriptor(
+ CharacteristicUserDescriptionDescriptor(bus, 1, self))
+
+ def ReadValue(self):
+ print 'TestCharacteristic Read: ' + repr(self.value)
+ return self.value
+
+ def WriteValue(self, value):
+ print 'TestCharacteristic Write: ' + repr(value)
+ self.value = value
+
+
+class TestDescriptor(Descriptor):
+ """
+ Dummy test descriptor. Returns a static value.
+
+ """
+ TEST_DESC_UUID = '12345678-1234-5678-1234-56789abcdef2'
+
+ def __init__(self, bus, index, characteristic):
+ Descriptor.__init__(
+ self, bus, index,
+ self.TEST_DESC_UUID,
+ characteristic)
+
+ def ReadValue(self):
+ return [
+ dbus.Byte('T'), dbus.Byte('e'), dbus.Byte('s'), dbus.Byte('t')
+ ]
+
+
+class CharacteristicUserDescriptionDescriptor(Descriptor):
+ """
+ Writable CUD descriptor.
+
+ """
+ CUD_UUID = '2901'
+
+ def __init__(self, bus, index, characteristic):
+ self.writable = 'writable-auxiliaries' in characteristic.flags
+ self.value = array.array('B', 'This is a characteristic for testing')
+ self.value = self.value.tolist()
+ Descriptor.__init__(
+ self, bus, index,
+ self.CUD_UUID,
+ characteristic)
+
+ def ReadValue(self):
+ return self.value
+
+ def WriteValue(self, value):
+ if not self.writable:
+ raise NotPermittedException()
+ self.value = value
+
+
+def register_service_cb():
+ print 'GATT service registered'
+
+
+def register_service_error_cb(error):
+ print 'Failed to register service: ' + str(error)
+ mainloop.quit()
+
+
+def find_adapter(bus):
+ remote_om = dbus.Interface(bus.get_object(BLUEZ_SERVICE_NAME, '/'),
+ DBUS_OM_IFACE)
+ objects = remote_om.GetManagedObjects()
+
+ for o, props in objects.iteritems():
+ if props.has_key(GATT_MANAGER_IFACE):
+ return o
+
+ return None
+
+def main():
+ global mainloop
+
+ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+ bus = dbus.SystemBus()
+
+ adapter = find_adapter(bus)
+ if not adapter:
+ print 'GattManager1 interface not found'
+ return
+
+ service_manager = dbus.Interface(
+ bus.get_object(BLUEZ_SERVICE_NAME, adapter),
+ GATT_MANAGER_IFACE)
+
+ hr_service = HeartRateService(bus, 0)
+ bat_service = BatteryService(bus, 1)
+ test_service = TestService(bus, 2)
+
+ mainloop = gobject.MainLoop()
+
+ service_manager.RegisterService(hr_service.get_path(), {},
+ reply_handler=register_service_cb,
+ error_handler=register_service_error_cb)
+ service_manager.RegisterService(bat_service.get_path(), {},
+ reply_handler=register_service_cb,
+ error_handler=register_service_error_cb)
+ service_manager.RegisterService(test_service.get_path(), {},
+ reply_handler=register_service_cb,
+ error_handler=register_service_error_cb)
+
+ mainloop.run()
+
+if __name__ == '__main__':
+ main()
#include <glib.h>
#include <dbus/dbus.h>
-#include <gdbus/gdbus.h>
+
+#include "gdbus/gdbus.h"
#include "src/error.h"
#include <sys/param.h>
#include <sys/ioctl.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/hci_lib.h>
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
#include "hciattach.h"
if (tcgetattr(fd, &ti) < 0) {
perror("Can't get port settings");
- return -1;
+ goto fail;
}
cfmakeraw(&ti);
if (tcsetattr(fd, TCSANOW, &ti) < 0) {
perror("Can't set port settings");
- return -1;
+ goto fail;
}
/* Set initial baudrate */
if (set_speed(fd, &ti, u->init_speed) < 0) {
perror("Can't set initial baud rate");
- return -1;
+ goto fail;
}
tcflush(fd, TCIOFLUSH);
#endif
if (u->init && u->init(fd, u, &ti) < 0)
- return -1;
+ goto fail;
tcflush(fd, TCIOFLUSH);
/* Set actual baudrate */
if (set_speed(fd, &ti, u->speed) < 0) {
perror("Can't set baud rate");
- return -1;
+ goto fail;
}
/* Set TTY to N_HCI line discipline */
i = N_HCI;
if (ioctl(fd, TIOCSETD, &i) < 0) {
perror("Can't set line discipline");
- return -1;
+ goto fail;
}
if (flags && ioctl(fd, HCIUARTSETFLAGS, flags) < 0) {
perror("Can't set UART flags");
- return -1;
+ goto fail;
}
if (ioctl(fd, HCIUARTSETPROTO, u->proto) < 0) {
perror("Can't set device");
- return -1;
+ goto fail;
}
#if 0
if (u->post && u->post(fd, u, &ti) < 0)
- return -1;
+ goto fail;
#endif
return fd;
+
+fail:
+ close(fd);
+ return -1;
}
#endif /* __BROADCOM_PATCH__ */
dev[0] = 0;
if (!strchr(opt, '/'))
strcpy(dev, "/dev/");
- strncat(dev, opt, PATH_MAX);
+ strcat(dev, opt);
break;
case 1:
#include <sys/param.h>
#include <sys/ioctl.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/hci_lib.h>
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
#include "hciattach.h"
goto download_cmplete;
}
- /*
- * It is not necessary that Patch file be available,
- * continue with PS Operations if patch file is not available.
- */
- if (patch_file[0] == '\0')
- err = 0;
-
stream = fopen(patch_file, "r");
- if (!stream)
- err = 0;
- else {
+ if(stream) {
patch_count = ps_patch_download(fd, stream);
fclose(stream);
#include <time.h>
#include <limits.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/hci_lib.h>
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
#include "hciattach.h"
#include <sys/ioctl.h>
#include <time.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/hci_lib.h>
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
#include "hciattach.h"
#include <sys/ioctl.h>
#include <sys/uio.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/hci_lib.h>
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
#include "hciattach.h"
#include <dirent.h>
#include <sys/param.h>
-#include <bluetooth/bluetooth.h>
+#include "lib/bluetooth.h"
#include "hciattach.h"
#include <sys/param.h>
#include <sys/ioctl.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/hci_lib.h>
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
#include "hciattach.h"
#include <sys/ioctl.h>
#include <sys/uio.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/hci_lib.h>
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
#include "hciattach.h"
#include <sys/socket.h>
#include <sys/stat.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/hci_lib.h>
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
#include "src/textfile.h"
#include "src/shared/util.h"
exit(1);
}
+ if (max_page < 1 && (features[6] & LMP_SIMPLE_PAIR))
+ max_page = 1;
+
print_dev_hdr(&di);
printf("\tFeatures%s: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x "
"0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n",
#include <getopt.h>
#include <sys/socket.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/hci_lib.h>
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
static struct option main_options[] = {
{ "device", 1, 0, 'i' },
#include <stdio.h>
#include <sys/socket.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/hci_lib.h>
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
-int main(void)
+int main(int argc, char *argv[])
{
uint32_t type_mask;
uint32_t event_mask[2];
for the local clock or
.BR 1
for the piconet clock (which is default).
+.TP
+.BI lescan " [--privacy] [--passive] [--whitelist] [--discovery=g|l] \
+[--duplicates]"
+Start LE scan
+.TP
+.BI leinfo " [--static] [--random] <bdaddr>"
+Get LE remote information
+.TP
+.BI lewladd " [--random] <bdaddr>"
+Add device to LE White List
+.TP
+.BI lewlrm " <bdaddr>"
+Remove device from LE White List
+.TP
+.BI lewlsz
+Read size of LE White List
+.TP
+.BI lewlclr
+Clear LE White List
+.TP
+.BI lerladd " [--local irk] [--peer irk] [--random] <bdaddr>"
+Add device to LE Resolving List
+.TP
+.BI lerlrm " <bdaddr>"
+Remove device from LE Resolving List
+.TP
+.BI lerlclr
+Clear LE Resolving List
+.TP
+.BI lerlsz
+Read size of LE Resolving List
+.TP
+.BI lerlon
+Enable LE Address Resolution
+.TP
+.BI lerloff
+Disable LE Address Resolution
+.TP
+.BI lecc " [--static] [--random] <bdaddr> | [--whitelist]"
+Create a LE Connection
+.TP
+.BI ledc " <handle> [reason]"
+Disconnect a LE Connection
+.TP
+.BI lecup " <handle> <min> <max> <latency> <timeout>"
+LE Connection Update
.SH AUTHORS
Written by Maxim Krasnyansky <maxk@qualcomm.com> and Marcel Holtmann <marcel@holtmann.org>
.PP
#include <sys/socket.h>
#include <signal.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/hci_lib.h>
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
#include "src/oui.h"
hci_read_remote_ext_features(dd, handle, 0, &max_page,
features, 20000);
+ if (max_page < 1 && (features[6] & LMP_SIMPLE_PAIR))
+ max_page = 1;
+
printf("\tFeatures%s: 0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x "
"0x%2.2x 0x%2.2x 0x%2.2x 0x%2.2x\n",
(max_page > 0) ? " page 0" : "",
static int print_advertising_devices(int dd, uint8_t filter_type)
{
- unsigned char buf_array[HCI_MAX_EVENT_SIZE+1] = {0};
- unsigned char *buf = buf_array;
- unsigned char *ptr = NULL;
+ unsigned char buf[HCI_MAX_EVENT_SIZE], *ptr;
struct hci_filter nf, of;
struct sigaction sa;
socklen_t olen;
sigaction(SIGINT, &sa, NULL);
while (1) {
- evt_le_meta_event *meta = NULL;
- le_advertising_info *info = NULL;
- char addr_array[18];
- char *addr = addr_array;
-
- buf[HCI_MAX_EVENT_SIZE] = 0;
+ evt_le_meta_event *meta;
+ le_advertising_info *info;
+ char addr[18];
- while ((len = read(dd, buf, HCI_MAX_EVENT_SIZE)) < 0) {
+ while ((len = read(dd, buf, sizeof(buf))) < 0) {
if (errno == EINTR && signal_received == SIGINT) {
len = 0;
goto done;
/* Ignoring multiple reports */
info = (le_advertising_info *) (meta->data + 1);
if (check_report_filter(filter_type, info)) {
- char name_array[30];
- char *name = name_array;
+ char name[30];
- memset(name, 0, 30);
+ memset(name, 0, sizeof(name));
ba2str(&info->bdaddr, addr);
eir_parse_name(info->data, info->length,
- name, 29);
+ name, sizeof(name) - 1);
- name[29] = '\0';
printf("%s %s\n", addr, name);
}
}
"Usage:\n"
"\tlecup <handle> <min> <max> <latency> <timeout>\n"
"\tOptions:\n"
- "\t -H, --handle <0xXXXX> LE connection handle\n"
- "\t -m, --min <interval> Range: 0x0006 to 0x0C80\n"
- "\t -M, --max <interval> Range: 0x0006 to 0x0C80\n"
- "\t -l, --latency <range> Slave latency. Range: 0x0000 to 0x03E8\n"
- "\t -t, --timeout <time> N * 10ms. Range: 0x000A to 0x0C80\n"
+ "\t --handle=<0xXXXX> LE connection handle\n"
+ "\t --min=<interval> Range: 0x0006 to 0x0C80\n"
+ "\t --max=<interval> Range: 0x0006 to 0x0C80\n"
+ "\t --latency=<range> Slave latency. Range: 0x0000 to 0x03E8\n"
+ "\t --timeout=<time> N * 10ms. Range: 0x000A to 0x0C80\n"
"\n\t min/max range: 7.5ms to 4s. Multiply factor: 1.25ms"
"\n\t timeout range: 100ms to 32.0s. Larger than max interval\n";
static void cmd_lecup(int dev_id, int argc, char **argv)
{
uint16_t handle = 0, min, max, latency, timeout;
- int opt, dd, base;
+ int opt, dd;
+ int options = 0;
/* Aleatory valid values */
min = 0x0C8;
timeout = 0x0C80;
for_each_opt(opt, lecup_options, NULL) {
- if (optarg && strncasecmp("0x", optarg, 2) == 0)
- base = 16;
- else
- base = 10;
-
switch (opt) {
case 'H':
- handle = strtoul(optarg, NULL, base);
+ handle = strtoul(optarg, NULL, 0);
break;
case 'm':
- min = strtoul(optarg, NULL, base);
+ min = strtoul(optarg, NULL, 0);
break;
case 'M':
- max = strtoul(optarg, NULL, base);
+ max = strtoul(optarg, NULL, 0);
break;
case 'l':
- latency = strtoul(optarg, NULL, base);
+ latency = strtoul(optarg, NULL, 0);
break;
case 't':
- timeout = strtoul(optarg, NULL, base);
+ timeout = strtoul(optarg, NULL, 0);
break;
default:
printf("%s", lecup_help);
return;
}
+
+ options = 1;
+ }
+
+ if (options == 0) {
+ helper_arg(5, 5, &argc, &argv, lecup_help);
+
+ handle = strtoul(argv[0], NULL, 0);
+ min = strtoul(argv[1], NULL, 0);
+ max = strtoul(argv[2], NULL, 0);
+ latency = strtoul(argv[3], NULL, 0);
+ timeout = strtoul(argv[4], NULL, 0);
}
if (handle == 0) {
*
* BlueZ - Bluetooth protocol stack for Linux
*
- * Copyright (C) 2012 Canonical
+ * Copyright (C) 2012-2013 Intel Corporation
*
*
* This program is free software; you can redistribute it and/or modify
#include <config.h>
#endif
-#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <getopt.h>
+#include <dirent.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <sys/stat.h>
+
+static ssize_t process_record(int fd, const char *line, uint16_t *upper_addr)
+{
+ const char *ptr = line + 1;
+ char str[3];
+ size_t len;
+ uint8_t *buf;
+ uint32_t addr;
+ uint8_t sum = 0;
+ int n = 0;
+
+ if (line[0] != ':') {
+ fprintf(stderr, "Invalid record start code (%c)\n", line[0]);
+ return -EINVAL;
+ }
+
+ len = strlen(line);
+ if (len < 11) {
+ fprintf(stderr, "Record information is too short\n");
+ return -EILSEQ;
+ }
-#define RBUF_SIZE 640
+ buf = malloc((len / 2) + 3);
+ if (!buf) {
+ fprintf(stderr, "Failed to allocate memory for record data\n");
+ return -ENOMEM;
+ }
+
+ while (1) {
+ str[0] = *ptr++;
+ str[1] = *ptr++;
+ str[2] = '\0';
+
+ buf[3 + n] = strtol(str, NULL, 16);
+
+ if (*ptr == '\r' || *ptr == '\n')
+ break;
+
+ sum += buf[3 + n++];
+ }
+
+ sum = 0x100 - (sum & 0xff);
+
+ if (n < 4 || buf[3] + 4 != n) {
+ fprintf(stderr, "Record length is not matching data\n");
+ free(buf);
+ return -EILSEQ;
+ }
+
+ if (buf[3 + n] != sum) {
+ fprintf(stderr, "Checksum mismatch\n");
+ free(buf);
+ return -EILSEQ;
+ }
+
+ switch (buf[6]) {
+ case 0x00:
+ addr = (*upper_addr << 16) + (buf[4] << 8) + buf[5];
+
+ buf[0] = 0x4c;
+ buf[1] = 0xfc;
+ buf[2] = n;
+
+ buf[3] = (addr & 0x000000ff);
+ buf[4] = (addr & 0x0000ff00) >> 8;
+ buf[5] = (addr & 0x00ff0000) >> 16;
+ buf[6] = (addr & 0xff000000) >> 24;
+
+ if (write(fd, buf, n + 3) < 0) {
+ perror("Failed to write data record");
+ free(buf);
+ return -errno;
+ }
+ break;
+ case 0x01:
+ buf[0] = 0x4e;
+ buf[1] = 0xfc;
+ buf[2] = 0x04;
+
+ buf[3] = 0xff;
+ buf[4] = 0xff;
+ buf[5] = 0xff;
+ buf[6] = 0xff;
+
+ if (write(fd, buf, 7) < 0) {
+ perror("Failed to write end record");
+ free(buf);
+ return -errno;
+ }
+ break;
+ case 0x04:
+ *upper_addr = (buf[7] << 8) + buf[8];
+ break;
+ default:
+ fprintf(stderr, "Unsupported record type (%02X)\n", buf[3]);
+ free(buf);
+ return -EILSEQ;
+ }
-static unsigned int asc_to_int(char a)
+ free(buf);
+
+ return len;
+}
+
+static void convert_file(const char *input_path, const char *output_path)
{
- if (a >= 'A')
- return (a - 'A') + 10;
- else
- return a - '0';
+ uint16_t upper_addr = 0x0000;
+ size_t line_size = 1024;
+ char line_buffer[line_size];
+ char *path;
+ const char *ptr;
+ FILE *fp;
+ struct stat st;
+ off_t cur = 0;
+ int fd;
+
+ if (output_path) {
+ path = strdup(output_path);
+ if (!path) {
+ perror("Failed to allocate string");
+ return;
+ }
+ } else {
+ ptr = strrchr(input_path, '.');
+ if (ptr) {
+ path = malloc(ptr - input_path + 6);
+ if (!path) {
+ perror("Failed to allocate string");
+ return;
+ }
+ strncpy(path, input_path, ptr - input_path);
+ strcpy(path + (ptr - input_path), ".hcd");
+ } else {
+ if (asprintf(&path, "%s.hcd", input_path) < 0) {
+ perror("Failed to allocate string");
+ return;
+ }
+ }
+ }
+
+ printf("Converting %s to %s\n", input_path, path);
+
+ fd = open(path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+
+ free(path);
+
+ if (fd < 0) {
+ perror("Failed to create output file");
+ return;
+ }
+
+ if (stat(input_path, &st) < 0) {
+ fprintf(stderr, "Failed get file size\n");
+ close(fd);
+ return;
+ }
+
+ if (st.st_size == 0) {
+ fprintf(stderr, "Empty file\n");
+ close(fd);
+ return;
+ }
+
+ fp = fopen(input_path, "r");
+ if (!fp) {
+ fprintf(stderr, "Failed to open input file\n");
+ close(fd);
+ return;
+ }
+
+ while (1) {
+ char *str;
+ ssize_t len;
+
+ str = fgets(line_buffer, line_size - 1, fp);
+ if (!str)
+ break;
+
+ len = process_record(fd, str, &upper_addr);
+ if (len < 0)
+ goto done;
+
+ cur += len;
+ }
+
+ if (cur != st.st_size) {
+ fprintf(stderr, "Data length does not match file length\n");
+ goto done;
+ }
+
+done:
+ fclose(fp);
+
+ close(fd);
}
-static unsigned int hex_to_int(const char *h)
+struct ver_data {
+ uint16_t num;
+ char name[20];
+ char major[4];
+ char minor[4];
+ char build[4];
+ struct ver_data *next;
+};
+
+static struct ver_data *ver_list = NULL;
+
+static void ver_parse_file(const char *pathname)
{
- return asc_to_int(*h) * 0x10 + asc_to_int(*(h + 1));
+ struct ver_data *ver, *tmp, *prev;
+ char dummy1[5], dummy2[5];
+
+ if (strlen(pathname) < 7)
+ return;
+
+ if (strncmp(pathname, "BCM", 3))
+ return;
+
+ ver = malloc(sizeof(*ver));
+ if (!ver)
+ return;
+
+ memset(ver, 0, sizeof(*ver));
+
+ if (sscanf(pathname, "%[A-Z0-9]_%3c.%3c.%3c.%4c.%4c.hex",
+ ver->name, ver->major, ver->minor,
+ ver->build, dummy1, dummy2) != 6) {
+ printf("\t/* failed to parse %s */\n", pathname);
+ free(ver);
+ return;
+ }
+
+ ver->num = atoi(ver->build) + (atoi(ver->minor) << 8) +
+ (atoi(ver->major) << 13);
+
+ if (!ver_list) {
+ ver_list = ver;
+ return;
+ }
+
+ for (tmp = ver_list, prev = NULL; tmp; prev = tmp, tmp = tmp->next) {
+ if (ver->num == tmp->num) {
+ free(ver);
+ return;
+ }
+
+ if (ver->num < tmp->num) {
+ if (prev) {
+ prev->next = ver;
+ ver->next = tmp;
+ } else {
+ ver->next = ver_list;
+ ver_list = ver;
+ }
+ return;
+ }
+ }
+
+ prev->next = ver;
}
-static unsigned int lhex_to_int(const char *h)
+static void ver_parse_entry(const char *pathname)
{
- return hex_to_int(h) * 0x100 + hex_to_int(h + 2);
+ struct stat st;
+ int fd;
+
+ fd = open(pathname, O_RDONLY);
+ if (fd < 0) {
+ printf("\t/* failed to open %s */\n", pathname);
+ return;
+ }
+
+ if (fstat(fd, &st) < 0) {
+ printf("\t/* failed to stat %s */\n", pathname);
+ goto done;
+ }
+
+ if (S_ISREG(st.st_mode)) {
+ ver_parse_file(basename(pathname));
+ goto done;
+ }
+
+ if (S_ISDIR(st.st_mode)) {
+ DIR *dir;
+
+ dir = fdopendir(fd);
+ if (!dir)
+ goto done;
+
+ while (1) {
+ struct dirent *d;
+
+ d = readdir(dir);
+ if (!d)
+ break;
+
+ if (d->d_type == DT_REG)
+ ver_parse_file(d->d_name);
+ }
+
+ closedir(dir);
+ }
+
+done:
+ close(fd);
}
-static int check_sum(const char *str, int len)
+static void ver_print_table(int argc, char *argv[])
{
- unsigned int sum, cal;
- int i;
- sum = hex_to_int(str + len - 2);
- for (cal = 0, i = 1; i < len - 2; i += 2)
- cal += hex_to_int(str + i);
- cal = 0x100 - (cal & 0xFF);
- return sum == cal;
+ struct ver_data *ver;
+
+ printf("static const struct {\n");
+ printf("\tuint16_t ver;\n");
+ printf("\tconst char *str\n");
+ printf("} table[] = {\n");
+
+ if (argc > 0) {
+ int i;
+
+ for (i = 0; i < argc; i++)
+ ver_parse_entry(argv[i]);
+ } else
+ ver_parse_entry(".");
+
+ for (ver = ver_list; ver; ) {
+ struct ver_data *tmp = ver;
+
+ printf("\t{ 0x%4.4x, \"%s\"\t},\t/* %s.%s.%s */\n",
+ ver->num, ver->name,
+ ver->major, ver->minor, ver->build);
+
+ ver = ver->next;
+ free(tmp);
+ }
+
+ printf(" { }\n");
+ printf("};\n");
}
-static int check_hex_line(const char *str, unsigned int len)
+static void usage(void)
{
- if ((str[0] != ':') || (len < 11) || !check_sum(str, len) ||
- (hex_to_int(str + 1) * 2 + 11 != len))
- return 0;
- return 1;
+ printf("Broadcom Bluetooth firmware converter\n"
+ "Usage:\n");
+ printf("\thex2hcd [options] <file>\n");
+ printf("Options:\n"
+ "\t-o, --output <file> Provide firmware output file\n"
+ "\t-h, --help Show help options\n");
}
+static const struct option main_options[] = {
+ { "table", no_argument, NULL, 'T' },
+ { "output", required_argument, NULL, 'o' },
+ { "version", no_argument, NULL, 'v' },
+ { "help", no_argument, NULL, 'h' },
+ { }
+};
+
int main(int argc, char *argv[])
{
- unsigned int i, addr = 0;
- FILE *ifp, *ofp;
- char *rbuf;
- ssize_t len;
- size_t buflen;
-
- if (argc != 3) {
- printf("Usage: %s <input hex file> <output file>\n", argv[0]);
- return 0;
- }
-
- ifp = fopen(argv[1], "r");
- ofp = fopen(argv[2], "w");
- if ((ifp == NULL) || (ofp == NULL)) {
- puts("failed to open file.");
- return -EIO;
- }
-
- rbuf = NULL;
- while ((len = getline(&rbuf, &buflen, ifp)) > 0) {
- int type;
- char obuf[7];
- unsigned int dest_addr;
- while ((rbuf[len - 1] == '\r') || (rbuf[len - 1] == '\n'))
- len--;
- printf("%d, %s\n", (int)len, rbuf);
- if (!check_hex_line(rbuf, len))
+ const char *output_path = NULL;
+ bool print_table = false;
+ int i;
+
+ for (;;) {
+ int opt;
+
+ opt = getopt_long(argc, argv, "To:vh", main_options, NULL);
+ if (opt < 0)
break;
- type = hex_to_int(rbuf + 7);
- switch (type) {
- case 4:
- addr = lhex_to_int(rbuf + 9) * 0x10000;
- printf("bump addr to 0x%08X\n", addr);
- break;
- case 0:
- dest_addr = addr + lhex_to_int(rbuf + 3);
- obuf[0] = 0x4c;
- obuf[1] = 0xfc;
- obuf[2] = hex_to_int(rbuf + 1) + 4;
- obuf[3] = dest_addr;
- obuf[4] = dest_addr >> 8;
- obuf[5] = dest_addr >> 16;
- obuf[6] = dest_addr >> 24;
- if (fwrite(obuf, 7, 1, ofp) != 1)
- goto output_err;
- for (i = 0; i < hex_to_int(rbuf + 1); i++) {
- obuf[0] = hex_to_int(rbuf + 9 + i * 2);
- if (fwrite(obuf, 1, 1, ofp) != 1)
- goto output_err;
- }
- break;
- case 1:
- if (fwrite("\x4e\xfc\x04\xff\xff\xff\xff", 7, 1, ofp) != 1)
- goto output_err;
- goto end;
- default:
- return -EINVAL;
+
+ switch (opt) {
+ case 'T':
+ print_table = true;
+ break;
+ case 'o':
+ output_path = optarg;
+ break;
+ case 'v':
+ printf("%s\n", VERSION);
+ return EXIT_SUCCESS;
+ case 'h':
+ usage();
+ return EXIT_SUCCESS;
+ default:
+ return EXIT_FAILURE;
}
}
- puts("hex file formatting error");
- return -EINVAL;
+ if (print_table) {
+ ver_print_table(argc - optind, argv + optind);
+ return EXIT_SUCCESS;
+ }
-output_err:
- puts("error on writing output file");
- return -EIO;
+ if (argc - optind < 1) {
+ fprintf(stderr, "No input firmware files provided\n");
+ return EXIT_FAILURE;
+ }
-end:
- return 0;
-}
+ if (output_path && argc - optind > 1) {
+ fprintf(stderr, "Only single input firmware supported\n");
+ return EXIT_FAILURE;
+ }
+
+ for (i = optind; i < argc; i++)
+ convert_file(argv[i], output_path);
+ return EXIT_SUCCESS;
+}
.B --mode= [hid, hci]
Sets the mode to switch the device into
.TP
-.B --method= [csr, logitech-hid, dell]
+.B --method= [csr, csr2, logitech-hid, dell]
Which vendor method to use for switching the device.
.TP
.B --devpath=
uint16_t wIndex;
uint16_t wLength;
uint32_t timeout; /* in milliseconds */
- void *data; /* pointer to data */
+ const void *data; /* pointer to data */
};
static int control_message(int fd, int requesttype, int request,
int value, int index,
- char *bytes, int size, int timeout)
+ const uint8_t *bytes, int size, int timeout)
{
struct usbfs_ctrltransfer transfer;
return err;
}
+static int usb_switch_csr2(int fd, enum mode mode)
+{
+ int err = 0;
+ struct usbfs_disconnect disconnect;
+ const uint8_t report[] = {
+ 0x01, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+ };
+
+ switch (mode) {
+ case HCI:
+ /* send report as is */
+ disconnect.interface = 0;
+ disconnect.flags = USBFS_DISCONNECT_EXCEPT_DRIVER;
+ strcpy(disconnect.driver, "usbfs");
+
+ if (ioctl(fd, USBFS_IOCTL_DISCONNECT, &disconnect) < 0) {
+ fprintf(stderr, "Can't claim interface: %s (%d)\n",
+ strerror(errno), errno);
+ return -1;
+ }
+
+ /* Set_report request with
+ * report id: 0x01, report type: feature (0x03)
+ * on interface 0
+ */
+ err = control_message(fd,
+ USB_ENDPOINT_OUT | USB_TYPE_CLASS |
+ USB_RECIP_INTERFACE,
+ USB_REQ_SET_CONFIGURATION,
+ 0x01 | (0x03 << 8),
+ 0, report, sizeof(report), 5000);
+ /* unable to detect whether the previous state
+ * already was HCI (EALREADY)
+ */
+ break;
+ case HID:
+ /* currently unknown how to switch to HID */
+ fprintf(stderr,
+ "csr2: Switching to hid mode is not implemented\n");
+ err = -1;
+ break;
+ }
+
+ return err;
+}
+
static int hid_logitech_send_report(int fd, const char *buf, size_t size)
{
struct hiddev_report_info rinfo;
static int usb_switch_dell(int fd, enum mode mode)
{
- char report[] = { 0x7f, 0x00, 0x00, 0x00 };
+ uint8_t report[] = { 0x7f, 0x00, 0x00, 0x00 };
struct usbfs_disconnect disconnect;
int err;
printf("Usage: hid2hci [options]\n"
" --mode= mode to switch to [hid|hci] (default hci)\n"
" --devpath= sys device path\n"
- " --method= method to use to switch [csr|logitech-hid|dell]\n"
+ " --method= method to use to switch [csr|csr2|logitech-hid|dell]\n"
" --help\n\n");
}
if (!strcmp(optarg, "csr")) {
method = METHOD_CSR;
usb_switch = usb_switch_csr;
+ } else if (!strcmp(optarg, "csr2")) {
+ method = METHOD_CSR;
+ usb_switch = usb_switch_csr2;
} else if (!strcmp(optarg, "logitech-hid")) {
method = METHOD_LOGITECH_HID;
} else if (!strcmp(optarg, "dell")) {
#include <stdio.h>
-#include <bluetooth/bluetooth.h>
+#include "lib/bluetooth.h"
static const struct {
uint16_t vendor;
#include <sys/ioctl.h>
#include <sys/socket.h>
-#include "monitor/mainloop.h"
#include "monitor/bt.h"
+#include "src/shared/mainloop.h"
#include "src/shared/timeout.h"
#include "src/shared/util.h"
#include "src/shared/hci.h"
const void *pin;
uint8_t client_pin_len;
const void *client_pin;
+
+ bool addr_type_avail;
+ uint8_t addr_type;
};
static void mgmt_debug(const char *str, void *user_data)
.expect_err = ECONNREFUSED,
};
+static const struct l2cap_data le_client_connect_reject_test_2 = {
+ .client_psm = 0x0080,
+ .addr_type_avail = true,
+ .addr_type = BDADDR_LE_PUBLIC,
+};
+
static const struct l2cap_data le_client_connect_nval_psm_test = {
.client_psm = 0x0080,
.expect_err = ECONNREFUSED,
static int create_l2cap_sock(struct test_data *data, uint16_t psm,
uint16_t cid, int sec_level)
{
+ const struct l2cap_data *l2data = data->test_data;
const uint8_t *master_bdaddr;
struct sockaddr_l2 addr;
int sk, err;
addr.l2_psm = htobs(psm);
addr.l2_cid = htobs(cid);
bacpy(&addr.l2_bdaddr, (void *) master_bdaddr);
- if (data->hciemu_type == HCIEMU_TYPE_LE)
+
+ if (l2data && l2data->addr_type_avail)
+ addr.l2_bdaddr_type = l2data->addr_type;
+ else if (data->hciemu_type == HCIEMU_TYPE_LE)
addr.l2_bdaddr_type = BDADDR_LE_PUBLIC;
else
addr.l2_bdaddr_type = BDADDR_BREDR;
static int connect_l2cap_sock(struct test_data *data, int sk, uint16_t psm,
uint16_t cid)
{
+ const struct l2cap_data *l2data = data->test_data;
const uint8_t *client_bdaddr;
struct sockaddr_l2 addr;
int err;
bacpy(&addr.l2_bdaddr, (void *) client_bdaddr);
addr.l2_psm = htobs(psm);
addr.l2_cid = htobs(cid);
- if (data->hciemu_type == HCIEMU_TYPE_LE)
+
+ if (l2data && l2data->addr_type_avail)
+ addr.l2_bdaddr_type = l2data->addr_type;
+ else if (data->hciemu_type == HCIEMU_TYPE_LE)
addr.l2_bdaddr_type = BDADDR_LE_PUBLIC;
else
addr.l2_bdaddr_type = BDADDR_BREDR;
tester_print("Connect in progress");
}
+static void test_connect_reject(const void *test_data)
+{
+ struct test_data *data = tester_get_data();
+ const struct l2cap_data *l2data = data->test_data;
+ int sk;
+
+ sk = create_l2cap_sock(data, 0, l2data->cid, l2data->sec_level);
+ if (sk < 0) {
+ tester_test_failed();
+ return;
+ }
+
+ if (connect_l2cap_sock(data, sk, l2data->client_psm,
+ l2data->cid) < 0)
+ tester_test_passed();
+ else
+ tester_test_failed();
+
+ close(sk);
+}
+
static gboolean l2cap_listen_cb(GIOChannel *io, GIOCondition cond,
gpointer user_data)
{
test_l2cap_le("L2CAP LE Client - Command Reject",
&le_client_connect_reject_test_1,
setup_powered_client, test_connect);
+ test_l2cap_bredr("L2CAP LE Client - Connection Reject",
+ &le_client_connect_reject_test_2,
+ setup_powered_client, test_connect_reject);
test_l2cap_le("L2CAP LE Client - Invalid PSM",
&le_client_connect_nval_psm_test,
setup_powered_client, test_connect);
#include <poll.h>
#include <sys/socket.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/hci_lib.h>
-#include <bluetooth/l2cap.h>
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+#include "lib/l2cap.h"
/* Defaults */
static bdaddr_t bdaddr;
#include <sys/ioctl.h>
#include <sys/socket.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/hci_lib.h>
-#include <bluetooth/l2cap.h>
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+#include "lib/l2cap.h"
#include "src/shared/util.h"
#include <getopt.h>
#include <unistd.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/hci_lib.h>
-
#include <glib.h>
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+
#include "btio/btio.h"
#include "lib/l2cap.h"
#include "profiles/health/mcap.h"
#define REQ_CLOCK_ACC 0x1400
-static void mdl_connected_cb(struct mcap_mdl *mdl, void *data)
+static void mdl_close(struct mcap_mdl *mdl)
{
int fd = -1;
printf("%s\n", __func__);
- if (mdl_disconnect && mdl_disconnect_timeout >= 0) {
+ if (mdl_disconnect_timeout >= 0)
sleep(mdl_disconnect_timeout);
- fd = mcap_mdl_get_fd(mdl);
+ fd = mcap_mdl_get_fd(mdl);
- if (fd > 0)
- close(fd);
- }
+ if (fd > 0)
+ close(fd);
+}
+
+static void mdl_connected_cb(struct mcap_mdl *mdl, void *data)
+{
+ printf("%s\n", __func__);
+
+ if (mdl_disconnect)
+ mdl_close(mdl);
}
static void mdl_closed_cb(struct mcap_mdl *mdl, void *data)
if (mcl_disconnect && mcl_disconnect_timeout >= 0) {
sleep(mcl_disconnect_timeout);
+ printf("Closing MCAP communication link\n");
mcap_close_mcl(mcl, TRUE);
+
+ if (no_close)
+ return;
+
+ g_main_loop_quit(mloop);
}
}
{
/* TODO */
printf("%s\n", __func__);
+
+ /* Disconnecting MDL latency timeout */
+ if (mdl_disconnect_timeout >= 0)
+ sleep(mdl_disconnect_timeout);
}
static void mdl_aborted_cb(struct mcap_mdl *mdl, void *data)
static void mcl_disconnected(struct mcap_mcl *mcl, gpointer data)
{
/* TODO */
- printf("MCL disconnected\n");
+ printf("%s\n", __func__);
if (no_close)
return;
static void mcl_uncached(struct mcap_mcl *mcl, gpointer data)
{
/* TODO */
- printf("MCL uncached unsupported\n");
+ printf("%s\n", __func__);
}
static void connect_mdl_cb(struct mcap_mdl *mdl, GError *gerr, gpointer data)
{
mdlid = mcap_mdl_get_mdlid(mdl);
- printf("MDL %d connected\n", mdlid);
+ printf("%s\n", __func__);
+
+ if (mdlid == MCAP_MDLID_RESERVED)
+ printf("MCAP mdlid is reserved");
+ else
+ printf("MDL %d connected\n", mdlid);
}
static void create_mdl_cb(struct mcap_mdl *mcap_mdl, uint8_t type, GError *gerr,
{
GError *err = NULL;
+ printf("%s\n", __func__);
+
if (gerr) {
printf("MDL error: %s\n", gerr->message);
}
if (mode == MODE_CONNECT) {
+ printf("Creating MCAP Data End Point\n");
mcap_create_mdl(mcl, 1, 0, create_mdl_cb, NULL, NULL, &gerr);
if (gerr) {
printf("Could not connect MDL: %s\n", gerr->message);
mcl = mcap_mcl_ref(mcap_mcl);
trigger_mdl_action(data_mode);
}
+
static void usage(void)
{
printf("mcaptest - MCAP testing ver %s\n", VERSION);
"\tmcaptest <control_mode> <data_mode> [options]\n");
printf("Control Link Mode:\n"
"\t-c connect <dst_addr>\n"
+ "\t-b close control link after closing data link\n"
"\t-e <timeout> disconnect MCL and quit after MDL is closed\n"
"\t-g send clock sync capability request if MCL connected\n");
printf("Data Link Mode:\n"
"\t-d connect\n"
+ "\t-a close data link immediately after being connected"
"\t-f <timeout> disconnect MDL after it's connected\n"
"\t-u send \'Unavailable\' on first MDL connection request\n");
printf("Options:\n"
{ "disconnect_cl", 1, 0, 'e' },
{ "synccap_req", 0, 0, 'g' },
{ "connect_dl", 0, 0, 'd' },
+ { "disconnect_da", 0, 0, 'a' },
+ { "disconnect_ca", 0, 0, 'b' },
{ "disconnect_dl", 1, 0, 'f' },
{ "unavailable_dl", 0, 0, 'u' },
{ "no exit mcl dis/err",0, 0, 'n' },
{ "data_ch", 1, 0, 'D' },
{ 0, 0, 0, 0 }
};
+
int main(int argc, char *argv[])
{
GError *err = NULL;
exit(1);
}
- while ((opt = getopt_long(argc, argv, "+i:c:C:D:e:f:dghun",
+ while ((opt = getopt_long(argc, argv, "+i:c:C:D:e:f:dghunab",
main_options, NULL)) != EOF) {
switch (opt) {
case 'i':
break;
- case 'e':
+ case 'a':
+ mdl_disconnect = TRUE;
+
+ break;
+
+ case 'b':
mcl_disconnect = TRUE;
+
+ break;
+
+ case 'e':
mcl_disconnect_timeout = atoi(optarg);
break;
case 'f':
- mdl_disconnect = TRUE;
mdl_disconnect_timeout = atoi(optarg);
break;
unsigned int mgmt_settings_id;
unsigned int mgmt_alt_settings_id;
unsigned int mgmt_alt_ev_id;
+ unsigned int mgmt_discov_ev_id;
uint8_t mgmt_version;
uint16_t mgmt_revision;
uint16_t mgmt_index;
user->test_data = data; \
user->expected_version = 0x08; \
user->expected_manufacturer = 0x003f; \
- user->expected_supported_settings = 0x00003fff; \
+ user->expected_supported_settings = 0x0000bfff; \
user->initial_settings = 0x00000080; \
user->unmet_conditions = 0; \
tester_add_full(name, data, \
user->test_data = data; \
user->expected_version = 0x08; \
user->expected_manufacturer = 0x003f; \
- user->expected_supported_settings = 0x00003e1b; \
+ user->expected_supported_settings = 0x0000be1b; \
user->initial_settings = 0x00000200; \
user->unmet_conditions = 0; \
tester_add_full(name, data, \
bool client_enable_sc;
bool expect_sc_key;
bool force_power_off;
+ bool addr_type_avail;
+ uint8_t addr_type;
};
static const char dummy_data[] = { 0x00 };
static uint8_t param[8];
memcpy(param, hciemu_get_client_bdaddr(data->hciemu), 6);
- if (data->hciemu_type == HCIEMU_TYPE_LE)
+
+ if (test->addr_type_avail)
+ param[6] = test->addr_type;
+ else if (data->hciemu_type == HCIEMU_TYPE_LE)
param[6] = 0x01; /* Address type */
else
param[6] = 0x00; /* Address type */
static const void *pair_device_expect_param_func(uint16_t *len)
{
struct test_data *data = tester_get_data();
+ const struct generic_data *test = data->test_data;
static uint8_t param[7];
memcpy(param, hciemu_get_client_bdaddr(data->hciemu), 6);
- if (data->hciemu_type == HCIEMU_TYPE_LE)
+
+ if (test->addr_type_avail)
+ param[6] = test->addr_type;
+ else if (data->hciemu_type == HCIEMU_TYPE_LE)
param[6] = 0x01; /* Address type */
else
param[6] = 0x00; /* Address type */
return bdaddr;
}
+static const struct generic_data pair_device_not_supported_test_1 = {
+ .setup_settings = settings_powered_bondable,
+ .send_opcode = MGMT_OP_PAIR_DEVICE,
+ .send_func = pair_device_send_param_func,
+ .expect_status = MGMT_STATUS_NOT_SUPPORTED,
+ .expect_func = pair_device_expect_param_func,
+ .addr_type_avail = true,
+ .addr_type = BDADDR_BREDR,
+};
+
+static const struct generic_data pair_device_not_supported_test_2 = {
+ .setup_settings = settings_powered_bondable,
+ .send_opcode = MGMT_OP_PAIR_DEVICE,
+ .send_func = pair_device_send_param_func,
+ .expect_status = MGMT_STATUS_NOT_SUPPORTED,
+ .expect_func = pair_device_expect_param_func,
+ .addr_type_avail = true,
+ .addr_type = BDADDR_LE_PUBLIC,
+};
+
+static uint16_t settings_powered_bondable_le[] = { MGMT_OP_SET_LE,
+ MGMT_OP_SET_BONDABLE,
+ MGMT_OP_SET_POWERED,
+ 0 };
+
+static const struct generic_data pair_device_reject_transport_not_enabled_1 = {
+ .setup_settings = settings_powered_bondable_le,
+ .setup_nobredr = true,
+ .send_opcode = MGMT_OP_PAIR_DEVICE,
+ .send_func = pair_device_send_param_func,
+ .expect_status = MGMT_STATUS_REJECTED,
+ .expect_func = pair_device_expect_param_func,
+ .addr_type_avail = true,
+ .addr_type = BDADDR_BREDR,
+};
+
+static const struct generic_data pair_device_reject_transport_not_enabled_2 = {
+ .setup_settings = settings_powered_bondable,
+ .send_opcode = MGMT_OP_PAIR_DEVICE,
+ .send_func = pair_device_send_param_func,
+ .expect_status = MGMT_STATUS_REJECTED,
+ .expect_func = pair_device_expect_param_func,
+ .addr_type_avail = true,
+ .addr_type = BDADDR_LE_PUBLIC,
+};
+
static const struct generic_data pair_device_reject_test_1 = {
.setup_settings = settings_powered_bondable,
.send_opcode = MGMT_OP_PAIR_DEVICE,
.client_io_cap = 0x03, /* NoInputNoOutput */
};
+static const struct generic_data pair_device_smp_bredr_test_2 = {
+ .setup_settings = settings_powered_sc_bondable_le_ssp,
+ .client_enable_ssp = true,
+ .client_enable_le = true,
+ .client_enable_sc = true,
+ .expect_sc_key = true,
+ .send_opcode = MGMT_OP_PAIR_DEVICE,
+ .send_func = pair_device_send_param_func,
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_func = pair_device_expect_param_func,
+ .expect_alt_ev = MGMT_EV_NEW_LONG_TERM_KEY,
+ .expect_alt_ev_len = sizeof(struct mgmt_ev_new_long_term_key),
+ .verify_alt_ev_func = verify_ltk,
+ .expect_hci_command = BT_HCI_CMD_USER_CONFIRM_REQUEST_REPLY,
+ .expect_hci_func = client_bdaddr_param_func,
+ .io_cap = 0x01, /* DisplayYesNo */
+ .client_io_cap = 0x01, /* DisplayYesNo */
+};
+
static const struct generic_data pair_device_le_reject_test_1 = {
.setup_settings = settings_powered_bondable,
.io_cap = 0x02, /* KeyboardOnly */
.client_auth_req = 0x02, /* Dedicated Bonding - No MITM */
};
+static uint16_t settings_powered_sc_bondable_connectable_le_ssp[] = {
+ MGMT_OP_SET_BONDABLE,
+ MGMT_OP_SET_CONNECTABLE,
+ MGMT_OP_SET_LE,
+ MGMT_OP_SET_SSP,
+ MGMT_OP_SET_SECURE_CONN,
+ MGMT_OP_SET_POWERED,
+ 0 };
+
+static const struct generic_data pairing_acceptor_smp_bredr_1 = {
+ .setup_settings = settings_powered_sc_bondable_connectable_le_ssp,
+ .client_enable_ssp = true,
+ .client_enable_le = true,
+ .client_enable_sc = true,
+ .expect_sc_key = true,
+ .expect_alt_ev = MGMT_EV_NEW_LONG_TERM_KEY,
+ .expect_alt_ev_len = sizeof(struct mgmt_ev_new_long_term_key),
+ .verify_alt_ev_func = verify_ltk,
+ .just_works = true,
+ .io_cap = 0x03, /* NoInputNoOutput */
+ .client_io_cap = 0x03, /* No InputNoOutput */
+ .client_auth_req = 0x00, /* No Bonding - No MITM */
+};
+
+static const struct generic_data pairing_acceptor_smp_bredr_2 = {
+ .setup_settings = settings_powered_sc_bondable_connectable_le_ssp,
+ .client_enable_ssp = true,
+ .client_enable_le = true,
+ .client_enable_sc = true,
+ .expect_sc_key = true,
+ .expect_alt_ev = MGMT_EV_NEW_LONG_TERM_KEY,
+ .expect_alt_ev_len = sizeof(struct mgmt_ev_new_long_term_key),
+ .verify_alt_ev_func = verify_ltk,
+ .io_cap = 0x01, /* DisplayYesNo */
+ .client_io_cap = 0x01, /* DisplayYesNo */
+ .client_auth_req = 0x02, /* Dedicated Bonding - No MITM */
+};
+
static uint16_t settings_powered_bondable_connectable_advertising[] = {
MGMT_OP_SET_BONDABLE,
MGMT_OP_SET_CONNECTABLE,
static const char set_static_addr_valid_param[] = {
0x11, 0x22, 0x33, 0x44, 0x55, 0xc0 };
+static const char set_static_addr_settings[] = { 0x00, 0x82, 0x00, 0x00 };
static const struct generic_data set_static_addr_success_test = {
.send_opcode = MGMT_OP_SET_STATIC_ADDRESS,
.send_param = set_static_addr_valid_param,
.send_len = sizeof(set_static_addr_valid_param),
.expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = set_static_addr_settings,
+ .expect_len = sizeof(set_static_addr_settings),
+ .expect_settings_set = MGMT_SETTING_STATIC_ADDRESS,
+};
+
+static const char set_static_addr_settings_dual[] = { 0x80, 0x00, 0x00, 0x00 };
+
+static const struct generic_data set_static_addr_success_test_2 = {
+ .send_opcode = MGMT_OP_SET_STATIC_ADDRESS,
+ .send_param = set_static_addr_valid_param,
+ .send_len = sizeof(set_static_addr_valid_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = set_static_addr_settings_dual,
+ .expect_len = sizeof(set_static_addr_settings_dual),
};
static const struct generic_data set_static_addr_failure_test = {
.expect_status = MGMT_STATUS_REJECTED,
};
+static const struct generic_data set_static_addr_failure_test_2 = {
+ .setup_settings = settings_powered,
+ .send_opcode = MGMT_OP_SET_STATIC_ADDRESS,
+ .send_param = set_static_addr_valid_param,
+ .send_len = sizeof(set_static_addr_valid_param),
+ .expect_status = MGMT_STATUS_NOT_SUPPORTED,
+};
+
static const char set_scan_params_valid_param[] = { 0x60, 0x00, 0x30, 0x00 };
static const struct generic_data set_scan_params_success_test = {
const void *param, void *user_data)
{
struct test_data *data = tester_get_data();
- unsigned int id = PTR_TO_UINT(user_data);
const struct mgmt_ev_discovering *ev = param;
- mgmt_unregister(data->mgmt, id);
+ mgmt_unregister(data->mgmt, data->mgmt_discov_ev_id);
if (length != sizeof(*ev)) {
tester_warn("Incorrect discovering event length");
const struct generic_data *test = data->test_data;
const void *send_param = test->setup_send_param;
uint16_t send_len = test->setup_send_len;
- unsigned int id = 0;
+ unsigned int id;
id = mgmt_register(data->mgmt, MGMT_EV_DISCOVERING, data->mgmt_index,
- discovering_event, UINT_TO_PTR(id), NULL);
+ discovering_event, NULL, NULL);
+ data->mgmt_discov_ev_id = id;
mgmt_send(data->mgmt, test->setup_send_opcode, data->mgmt_index,
send_len, send_param, setup_discovery_callback,
test_bredrle("Pair Device - Power off 1",
&pair_device_power_off_test_1,
NULL, test_command_generic);
+ test_le("Pair Device - Incorrect transport reject 1",
+ &pair_device_not_supported_test_1,
+ NULL, test_command_generic);
+ test_bredr("Pair Device - Incorrect transport reject 2",
+ &pair_device_not_supported_test_2,
+ NULL, test_command_generic);
+ test_bredrle("Pair Device - Reject on not enabled transport 1",
+ &pair_device_reject_transport_not_enabled_1,
+ NULL, test_command_generic);
+ test_bredrle("Pair Device - Reject on not enabled transport 2",
+ &pair_device_reject_transport_not_enabled_2,
+ NULL, test_command_generic);
test_bredrle("Pair Device - Invalid Parameters 1",
&pair_device_invalid_param_test_1,
NULL, test_command_generic);
test_bredrle("Pair Device - SSP Non-bondable 1",
&pair_device_ssp_nonbondable_1,
NULL, test_command_generic);
- test_bredrle("Pair Device - SMP over BR/EDR Just-Works Success 1",
+ test_bredrle("Pair Device - SMP over BR/EDR Success 1",
&pair_device_smp_bredr_test_1,
NULL, test_command_generic);
+ test_bredrle("Pair Device - SMP over BR/EDR Success 2",
+ &pair_device_smp_bredr_test_2,
+ NULL, test_command_generic);
test_le("Pair Device - LE Success 1",
&pair_device_le_success_test_1,
NULL, test_command_generic);
test_bredrle("Pairing Acceptor - SSP 4",
&pairing_acceptor_ssp_4, setup_pairing_acceptor,
test_pairing_acceptor);
+ test_bredrle("Pairing Acceptor - SMP over BR/EDR 1",
+ &pairing_acceptor_smp_bredr_1,
+ setup_pairing_acceptor, test_pairing_acceptor);
+ test_bredrle("Pairing Acceptor - SMP over BR/EDR 2",
+ &pairing_acceptor_smp_bredr_2,
+ setup_pairing_acceptor, test_pairing_acceptor);
test_le("Pairing Acceptor - LE 1",
&pairing_acceptor_le_1, setup_pairing_acceptor,
test_pairing_acceptor);
&unblock_device_invalid_param_test_1,
NULL, test_command_generic);
- test_bredrle("Set Static Address - Success",
+ test_le("Set Static Address - Success 1",
&set_static_addr_success_test,
NULL, test_command_generic);
- test_bredrle("Set Static Address - Failure",
+ test_bredrle("Set Static Address - Success 2",
+ &set_static_addr_success_test_2,
+ NULL, test_command_generic);
+ test_bredrle("Set Static Address - Failure 1",
&set_static_addr_failure_test,
NULL, test_command_generic);
+ test_bredr("Set Static Address - Failure 2",
+ &set_static_addr_failure_test_2,
+ NULL, test_command_generic);
test_bredrle("Set Scan Parameters - Success",
&set_scan_params_success_test,
+++ /dev/null
-/*
- *
- * BlueZ - Bluetooth protocol stack for Linux
- *
- * Copyright (C) 2004-2010 Marcel Holtmann <marcel@holtmann.org>
- *
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- *
- */
-
-#ifdef HAVE_CONFIG_H
-#include <config.h>
-#endif
-
-#include <stdio.h>
-#include <errno.h>
-#include <unistd.h>
-#include <stdlib.h>
-#include <signal.h>
-#include <getopt.h>
-#include <string.h>
-#include <inttypes.h>
-
-#include <dbus/dbus.h>
-#include <glib.h>
-#include <gdbus/gdbus.h>
-
-#define BLUEZ_BUS_NAME "org.bluez"
-#define BLUEZ_PATH "/org/bluez"
-#define BLUEZ_ADAPTER_INTERFACE "org.bluez.Adapter1"
-#define BLUEZ_MEDIA_INTERFACE "org.bluez.Media1"
-#define BLUEZ_MEDIA_PLAYER_INTERFACE "org.bluez.MediaPlayer1"
-#define BLUEZ_MEDIA_FOLDER_INTERFACE "org.bluez.MediaFolder1"
-#define BLUEZ_MEDIA_ITEM_INTERFACE "org.bluez.MediaItem1"
-#define BLUEZ_MEDIA_TRANSPORT_INTERFACE "org.bluez.MediaTransport1"
-#define MPRIS_BUS_NAME "org.mpris.MediaPlayer2."
-#define MPRIS_INTERFACE "org.mpris.MediaPlayer2"
-#define MPRIS_PLAYER_INTERFACE "org.mpris.MediaPlayer2.Player"
-#define MPRIS_TRACKLIST_INTERFACE "org.mpris.MediaPlayer2.TrackList"
-#define MPRIS_PLAYLISTS_INTERFACE "org.mpris.MediaPlayer2.Playlists"
-#define MPRIS_PLAYER_PATH "/org/mpris/MediaPlayer2"
-#define ERROR_INTERFACE "org.mpris.MediaPlayer2.Error"
-
-static GMainLoop *main_loop;
-static GDBusProxy *adapter = NULL;
-static DBusConnection *sys = NULL;
-static DBusConnection *session = NULL;
-static GDBusClient *client = NULL;
-static GSList *players = NULL;
-static GSList *transports = NULL;
-
-static gboolean option_version = FALSE;
-static gboolean option_export = FALSE;
-
-struct tracklist {
- GDBusProxy *proxy;
- GSList *items;
-};
-
-struct player {
- char *bus_name;
- DBusConnection *conn;
- GDBusProxy *proxy;
- GDBusProxy *folder;
- GDBusProxy *device;
- GDBusProxy *transport;
- GDBusProxy *playlist;
- struct tracklist *tracklist;
-};
-
-typedef int (* parse_metadata_func) (DBusMessageIter *iter, const char *key,
- DBusMessageIter *metadata);
-
-static void dict_append_entry(DBusMessageIter *dict, const char *key, int type,
- void *val);
-
-static void sig_term(int sig)
-{
- g_main_loop_quit(main_loop);
-}
-
-static DBusMessage *get_all(DBusConnection *conn, const char *name)
-{
- DBusMessage *msg, *reply;
- DBusError err;
- const char *iface = MPRIS_PLAYER_INTERFACE;
-
- msg = dbus_message_new_method_call(name, MPRIS_PLAYER_PATH,
- DBUS_INTERFACE_PROPERTIES, "GetAll");
- if (!msg) {
- fprintf(stderr, "Can't allocate new method call\n");
- return NULL;
- }
-
- dbus_message_append_args(msg, DBUS_TYPE_STRING, &iface,
- DBUS_TYPE_INVALID);
-
- dbus_error_init(&err);
-
- reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
-
- dbus_message_unref(msg);
-
- if (!reply) {
- if (dbus_error_is_set(&err)) {
- fprintf(stderr, "%s\n", err.message);
- dbus_error_free(&err);
- }
- return NULL;
- }
-
- return reply;
-}
-
-static void append_variant(DBusMessageIter *iter, int type, void *val)
-{
- DBusMessageIter value;
- char sig[2] = { type, '\0' };
-
- dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, sig, &value);
-
- dbus_message_iter_append_basic(&value, type, val);
-
- dbus_message_iter_close_container(iter, &value);
-}
-
-static void append_array_variant(DBusMessageIter *iter, int type, void *val,
- int n_elements)
-{
- DBusMessageIter variant, array;
- char type_sig[2] = { type, '\0' };
- char array_sig[3] = { DBUS_TYPE_ARRAY, type, '\0' };
-
- dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT,
- array_sig, &variant);
-
- dbus_message_iter_open_container(&variant, DBUS_TYPE_ARRAY,
- type_sig, &array);
-
- if (dbus_type_is_fixed(type) == TRUE) {
- dbus_message_iter_append_fixed_array(&array, type, val,
- n_elements);
- } else if (type == DBUS_TYPE_STRING || type == DBUS_TYPE_OBJECT_PATH) {
- const char ***str_array = val;
- int i;
-
- for (i = 0; i < n_elements; i++)
- dbus_message_iter_append_basic(&array, type,
- &((*str_array)[i]));
- }
-
- dbus_message_iter_close_container(&variant, &array);
-
- dbus_message_iter_close_container(iter, &variant);
-}
-
-static void dict_append_array(DBusMessageIter *dict, const char *key, int type,
- void *val, int n_elements)
-{
- DBusMessageIter entry;
-
- dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
- NULL, &entry);
-
- dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
-
- append_array_variant(&entry, type, val, n_elements);
-
- dbus_message_iter_close_container(dict, &entry);
-}
-
-static void append_basic(DBusMessageIter *base, DBusMessageIter *iter,
- int type)
-{
- const void *value;
-
- dbus_message_iter_get_basic(iter, &value);
- dbus_message_iter_append_basic(base, type, &value);
-}
-
-static void append_iter(DBusMessageIter *base, DBusMessageIter *iter);
-static void append_container(DBusMessageIter *base, DBusMessageIter *iter,
- int type)
-{
- DBusMessageIter iter_sub, base_sub;
- char *sig;
-
- dbus_message_iter_recurse(iter, &iter_sub);
-
- switch (type) {
- case DBUS_TYPE_ARRAY:
- case DBUS_TYPE_VARIANT:
- sig = dbus_message_iter_get_signature(&iter_sub);
- break;
- default:
- sig = NULL;
- break;
- }
-
- dbus_message_iter_open_container(base, type, sig, &base_sub);
-
- if (sig != NULL)
- dbus_free(sig);
-
- append_iter(&base_sub, &iter_sub);
-
- dbus_message_iter_close_container(base, &base_sub);
-}
-
-static void append_iter(DBusMessageIter *base, DBusMessageIter *iter)
-{
- int type;
-
- while ((type = dbus_message_iter_get_arg_type(iter)) !=
- DBUS_TYPE_INVALID) {
- if (dbus_type_is_basic(type))
- append_basic(base, iter, type);
- else if (dbus_type_is_container(type))
- append_container(base, iter, type);
-
- dbus_message_iter_next(iter);
- }
-}
-
-static void dict_append_iter(DBusMessageIter *dict, const char *key,
- DBusMessageIter *iter)
-{
- DBusMessageIter entry;
-
- dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
- NULL, &entry);
-
- dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
-
- append_iter(&entry, iter);
-
- dbus_message_iter_close_container(dict, &entry);
-}
-
-static int parse_metadata_entry(DBusMessageIter *entry, const char *key,
- DBusMessageIter *metadata)
-{
- if (dbus_message_iter_get_arg_type(entry) != DBUS_TYPE_VARIANT)
- return -EINVAL;
-
- dict_append_iter(metadata, key, entry);
-
- return 0;
-}
-
-static int parse_metadata(DBusMessageIter *args, DBusMessageIter *metadata,
- parse_metadata_func func)
-{
- DBusMessageIter dict;
- int ctype;
-
- ctype = dbus_message_iter_get_arg_type(args);
- if (ctype != DBUS_TYPE_ARRAY)
- return -EINVAL;
-
- dbus_message_iter_recurse(args, &dict);
-
- while ((ctype = dbus_message_iter_get_arg_type(&dict)) !=
- DBUS_TYPE_INVALID) {
- DBusMessageIter entry;
- const char *key;
-
- if (ctype != DBUS_TYPE_DICT_ENTRY)
- return -EINVAL;
-
- dbus_message_iter_recurse(&dict, &entry);
- if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_STRING)
- return -EINVAL;
-
- dbus_message_iter_get_basic(&entry, &key);
- dbus_message_iter_next(&entry);
-
- if (func(&entry, key, metadata) < 0)
- return -EINVAL;
-
- dbus_message_iter_next(&dict);
- }
-
- return 0;
-}
-
-static void append_metadata(DBusMessageIter *iter, DBusMessageIter *dict,
- parse_metadata_func func)
-{
- DBusMessageIter value, metadata;
-
- dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, "a{sv}",
- &value);
-
- dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY,
- DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
- DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
- DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &metadata);
-
- parse_metadata(dict, &metadata, func);
-
- dbus_message_iter_close_container(&value, &metadata);
- dbus_message_iter_close_container(iter, &value);
-}
-
-static void dict_append_entry(DBusMessageIter *dict, const char *key, int type,
- void *val)
-{
- DBusMessageIter entry;
-
- if (type == DBUS_TYPE_STRING) {
- const char *str = *((const char **) val);
- if (str == NULL)
- return;
- }
-
- dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
- NULL, &entry);
-
- dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
-
- if (strcasecmp(key, "Metadata") == 0)
- append_metadata(&entry, val, parse_metadata_entry);
- else
- append_variant(&entry, type, val);
-
- dbus_message_iter_close_container(dict, &entry);
-}
-
-static char *sender2path(const char *sender)
-{
- char *path;
-
- path = g_strconcat("/", sender, NULL);
- return g_strdelimit(path, ":.", '_');
-}
-
-static void copy_reply(DBusPendingCall *call, void *user_data)
-{
- DBusMessage *msg = user_data;
- DBusMessage *reply = dbus_pending_call_steal_reply(call);
- DBusMessage *copy;
- DBusMessageIter args, iter;
-
- copy = dbus_message_new_method_return(msg);
- if (copy == NULL) {
- dbus_message_unref(reply);
- return;
- }
-
- dbus_message_iter_init_append(copy, &iter);
-
- if (!dbus_message_iter_init(reply, &args))
- goto done;
-
- append_iter(&iter, &args);
-
- dbus_connection_send(sys, copy, NULL);
-
-done:
- dbus_message_unref(copy);
- dbus_message_unref(reply);
-}
-
-static DBusHandlerResult player_message(DBusConnection *conn,
- DBusMessage *msg, void *data)
-{
- char *owner = data;
- DBusMessage *copy;
- DBusMessageIter args, iter;
- DBusPendingCall *call;
-
- dbus_message_iter_init(msg, &args);
-
- copy = dbus_message_new_method_call(owner,
- MPRIS_PLAYER_PATH,
- dbus_message_get_interface(msg),
- dbus_message_get_member(msg));
- if (copy == NULL)
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-
- dbus_message_iter_init_append(copy, &iter);
- append_iter(&iter, &args);
-
- if (!dbus_connection_send_with_reply(session, copy, &call, -1))
- goto done;
-
- dbus_message_ref(msg);
- dbus_pending_call_set_notify(call, copy_reply, msg, NULL);
- dbus_pending_call_unref(call);
-
-done:
- dbus_message_unref(copy);
-
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-}
-
-static struct player *find_player_by_bus_name(const char *name)
-{
- GSList *l;
-
- for (l = players; l; l = l->next) {
- struct player *player = l->data;
-
- if (strcmp(player->bus_name, name) == 0)
- return player;
- }
-
- return NULL;
-}
-
-static const DBusObjectPathVTable player_table = {
- .message_function = player_message,
-};
-
-static void add_player(DBusConnection *conn, const char *name,
- const char *sender)
-{
- DBusMessage *reply = NULL;
- DBusMessage *msg;
- DBusMessageIter iter, args;
- DBusError err;
- char *path, *owner;
- struct player *player;
-
- if (!adapter)
- return;
-
- player = find_player_by_bus_name(name);
- if (player == NULL) {
- reply = get_all(conn, name);
- if (reply == NULL)
- return;
- dbus_message_iter_init(reply, &args);
- }
-
- msg = dbus_message_new_method_call(BLUEZ_BUS_NAME,
- g_dbus_proxy_get_path(adapter),
- BLUEZ_MEDIA_INTERFACE,
- "RegisterPlayer");
- if (!msg) {
- fprintf(stderr, "Can't allocate new method call\n");
- return;
- }
-
- path = sender2path(sender);
- dbus_connection_get_object_path_data(sys, path, (void **) &owner);
-
- if (owner != NULL)
- goto done;
-
- dbus_message_iter_init_append(msg, &iter);
-
- dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH, &path);
-
- if (player != NULL) {
- if (!g_dbus_get_properties(player->conn,
- MPRIS_PLAYER_PATH,
- MPRIS_PLAYER_INTERFACE,
- &iter))
- goto done;
- } else {
- append_iter(&iter, &args);
- dbus_message_unref(reply);
- }
-
- dbus_error_init(&err);
-
- owner = strdup(sender);
-
- if (!dbus_connection_register_object_path(sys, path, &player_table,
- owner)) {
- fprintf(stderr, "Can't register object path for player\n");
- free(owner);
- goto done;
- }
-
- reply = dbus_connection_send_with_reply_and_block(sys, msg, -1, &err);
- if (!reply) {
- fprintf(stderr, "Can't register player\n");
- free(owner);
- if (dbus_error_is_set(&err)) {
- fprintf(stderr, "%s\n", err.message);
- dbus_error_free(&err);
- }
- }
-
-done:
- if (reply)
- dbus_message_unref(reply);
- dbus_message_unref(msg);
- g_free(path);
-}
-
-static void remove_player(DBusConnection *conn, const char *sender)
-{
- DBusMessage *msg;
- char *path, *owner;
-
- if (!adapter)
- return;
-
- path = sender2path(sender);
- dbus_connection_get_object_path_data(sys, path, (void **) &owner);
-
- if (owner == NULL) {
- g_free(path);
- return;
- }
-
- msg = dbus_message_new_method_call(BLUEZ_BUS_NAME,
- g_dbus_proxy_get_path(adapter),
- BLUEZ_MEDIA_INTERFACE,
- "UnregisterPlayer");
- if (!msg) {
- fprintf(stderr, "Can't allocate new method call\n");
- g_free(path);
- return;
- }
-
- dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &path,
- DBUS_TYPE_INVALID);
-
- dbus_connection_send(sys, msg, NULL);
-
- dbus_connection_unregister_object_path(sys, path);
-
- dbus_message_unref(msg);
- g_free(path);
- g_free(owner);
-}
-
-static gboolean player_signal(DBusConnection *conn, DBusMessage *msg,
- void *user_data)
-{
- DBusMessage *signal;
- DBusMessageIter iter, args;
- char *path, *owner;
-
- dbus_message_iter_init(msg, &iter);
-
- path = sender2path(dbus_message_get_sender(msg));
- dbus_connection_get_object_path_data(sys, path, (void **) &owner);
-
- if (owner == NULL)
- goto done;
-
- signal = dbus_message_new_signal(path, dbus_message_get_interface(msg),
- dbus_message_get_member(msg));
- if (signal == NULL) {
- fprintf(stderr, "Unable to allocate new %s.%s signal",
- dbus_message_get_interface(msg),
- dbus_message_get_member(msg));
- goto done;
- }
-
- dbus_message_iter_init_append(signal, &args);
-
- append_iter(&args, &iter);
-
- dbus_connection_send(sys, signal, NULL);
- dbus_message_unref(signal);
-
-done:
- g_free(path);
-
- return TRUE;
-}
-
-static gboolean name_owner_changed(DBusConnection *conn,
- DBusMessage *msg, void *data)
-{
- const char *name, *old, *new;
-
- if (!dbus_message_get_args(msg, NULL,
- DBUS_TYPE_STRING, &name,
- DBUS_TYPE_STRING, &old,
- DBUS_TYPE_STRING, &new,
- DBUS_TYPE_INVALID)) {
- fprintf(stderr, "Invalid arguments for NameOwnerChanged signal");
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
- }
-
- if (!g_str_has_prefix(name, "org.mpris"))
- return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
-
- if (*new == '\0') {
- printf("player %s at %s disappear\n", name, old);
- remove_player(conn, old);
- } else if (option_export || find_player_by_bus_name(name) == NULL) {
- printf("player %s at %s found\n", name, new);
- add_player(conn, name, new);
- }
-
- return TRUE;
-}
-
-static char *get_name_owner(DBusConnection *conn, const char *name)
-{
- DBusMessage *msg, *reply;
- DBusError err;
- char *owner;
-
- msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS,
- DBUS_INTERFACE_DBUS, "GetNameOwner");
-
- if (!msg) {
- fprintf(stderr, "Can't allocate new method call\n");
- return NULL;
- }
-
- dbus_message_append_args(msg, DBUS_TYPE_STRING, &name,
- DBUS_TYPE_INVALID);
-
- dbus_error_init(&err);
-
- reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
-
- dbus_message_unref(msg);
-
- if (!reply) {
- if (dbus_error_is_set(&err)) {
- fprintf(stderr, "%s\n", err.message);
- dbus_error_free(&err);
- }
- return NULL;
- }
-
- if (!dbus_message_get_args(reply, NULL,
- DBUS_TYPE_STRING, &owner,
- DBUS_TYPE_INVALID)) {
- dbus_message_unref(reply);
- return NULL;
- }
-
- owner = g_strdup(owner);
-
- dbus_message_unref(reply);
-
- dbus_connection_flush(conn);
-
- return owner;
-}
-
-static void parse_list_names(DBusConnection *conn, DBusMessageIter *args)
-{
- DBusMessageIter array;
- int ctype;
-
- ctype = dbus_message_iter_get_arg_type(args);
- if (ctype != DBUS_TYPE_ARRAY)
- return;
-
- dbus_message_iter_recurse(args, &array);
-
- while ((ctype = dbus_message_iter_get_arg_type(&array)) !=
- DBUS_TYPE_INVALID) {
- const char *name;
- char *owner;
-
- if (ctype != DBUS_TYPE_STRING)
- goto next;
-
- dbus_message_iter_get_basic(&array, &name);
-
- if (!g_str_has_prefix(name, "org.mpris"))
- goto next;
-
- owner = get_name_owner(conn, name);
-
- if (owner == NULL)
- goto next;
-
- printf("player %s at %s found\n", name, owner);
-
- add_player(conn, name, owner);
-
- g_free(owner);
-next:
- dbus_message_iter_next(&array);
- }
-}
-
-static void list_names(DBusConnection *conn)
-{
- DBusMessage *msg, *reply;
- DBusMessageIter iter;
- DBusError err;
-
- msg = dbus_message_new_method_call(DBUS_SERVICE_DBUS, DBUS_PATH_DBUS,
- DBUS_INTERFACE_DBUS, "ListNames");
-
- if (!msg) {
- fprintf(stderr, "Can't allocate new method call\n");
- return;
- }
-
- dbus_error_init(&err);
-
- reply = dbus_connection_send_with_reply_and_block(conn, msg, -1, &err);
-
- dbus_message_unref(msg);
-
- if (!reply) {
- if (dbus_error_is_set(&err)) {
- fprintf(stderr, "%s\n", err.message);
- dbus_error_free(&err);
- }
- return;
- }
-
- dbus_message_iter_init(reply, &iter);
-
- parse_list_names(conn, &iter);
-
- dbus_message_unref(reply);
-
- dbus_connection_flush(conn);
-}
-
-static void usage(void)
-{
- printf("Bluetooth mpris-player ver %s\n\n", VERSION);
-
- printf("Usage:\n");
-}
-
-static GOptionEntry options[] = {
- { "version", 'v', 0, G_OPTION_ARG_NONE, &option_version,
- "Show version information and exit" },
- { "export", 'e', 0, G_OPTION_ARG_NONE, &option_export,
- "Export remote players" },
- { NULL },
-};
-
-static void connect_handler(DBusConnection *connection, void *user_data)
-{
- printf("org.bluez appeared\n");
-}
-
-static void disconnect_handler(DBusConnection *connection, void *user_data)
-{
- printf("org.bluez disappeared\n");
-}
-
-static void unregister_tracklist(struct player *player)
-{
- struct tracklist *tracklist = player->tracklist;
-
- g_slist_free(tracklist->items);
- g_dbus_proxy_unref(tracklist->proxy);
- g_free(tracklist);
- player->tracklist = NULL;
-}
-
-static void player_free(void *data)
-{
- struct player *player = data;
-
- if (player->tracklist != NULL)
- unregister_tracklist(player);
-
- if (player->conn) {
- dbus_connection_close(player->conn);
- dbus_connection_unref(player->conn);
- }
-
- g_dbus_proxy_unref(player->device);
- g_dbus_proxy_unref(player->proxy);
-
- if (player->transport)
- g_dbus_proxy_unref(player->transport);
-
- if (player->playlist)
- g_dbus_proxy_unref(player->playlist);
-
- g_free(player->bus_name);
- g_free(player);
-}
-
-struct pending_call {
- struct player *player;
- DBusMessage *msg;
-};
-
-static void pending_call_free(void *data)
-{
- struct pending_call *p = data;
-
- if (p->msg)
- dbus_message_unref(p->msg);
-
- g_free(p);
-}
-
-static void player_reply(DBusMessage *message, void *user_data)
-{
- struct pending_call *p = user_data;
- struct player *player = p->player;
- DBusMessage *msg = p->msg;
- DBusMessage *reply;
- DBusError err;
-
- dbus_error_init(&err);
- if (dbus_set_error_from_message(&err, message)) {
- fprintf(stderr, "error: %s", err.name);
- reply = g_dbus_create_error(msg, err.name, "%s", err.message);
- dbus_error_free(&err);
- } else
- reply = g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
-
- g_dbus_send_message(player->conn, reply);
-}
-
-static void player_control(struct player *player, DBusMessage *msg,
- const char *name)
-{
- struct pending_call *p;
-
- p = g_new0(struct pending_call, 1);
- p->player = player;
- p->msg = dbus_message_ref(msg);
-
- g_dbus_proxy_method_call(player->proxy, name, NULL, player_reply,
- p, pending_call_free);
-}
-
-static const char *status_to_playback(const char *status)
-{
- if (strcasecmp(status, "playing") == 0)
- return "Playing";
- else if (strcasecmp(status, "paused") == 0)
- return "Paused";
- else
- return "Stopped";
-}
-
-static const char *player_get_status(struct player *player)
-{
- const char *status;
- DBusMessageIter value;
-
- if (g_dbus_proxy_get_property(player->proxy, "Status", &value)) {
- dbus_message_iter_get_basic(&value, &status);
- return status_to_playback(status);
- }
-
- if (player->transport == NULL)
- goto done;
-
- if (!g_dbus_proxy_get_property(player->transport, "State", &value))
- goto done;
-
- dbus_message_iter_get_basic(&value, &status);
-
- if (strcasecmp(status, "active") == 0)
- return "Playing";
-
-done:
- return "Stopped";
-}
-
-static DBusMessage *player_toggle(DBusConnection *conn, DBusMessage *msg,
- void *data)
-{
- struct player *player = data;
- const char *status;
-
- status = player_get_status(player);
-
- if (strcasecmp(status, "Playing") == 0)
- player_control(player, msg, "Pause");
- else
- player_control(player, msg, "Play");
-
- return NULL;
-}
-
-static DBusMessage *player_play(DBusConnection *conn, DBusMessage *msg,
- void *data)
-{
- struct player *player = data;
-
- player_control(player, msg, "Play");
-
- return NULL;
-}
-
-static DBusMessage *player_pause(DBusConnection *conn, DBusMessage *msg,
- void *data)
-{
- struct player *player = data;
-
- player_control(player, msg, "Pause");
-
- return NULL;
-}
-
-static DBusMessage *player_stop(DBusConnection *conn, DBusMessage *msg,
- void *data)
-{
- struct player *player = data;
-
- player_control(player, msg, "Stop");
-
- return NULL;
-}
-
-static DBusMessage *player_next(DBusConnection *conn, DBusMessage *msg,
- void *data)
-{
- struct player *player = data;
-
- player_control(player, msg, "Next");
-
- return NULL;
-}
-
-static DBusMessage *player_previous(DBusConnection *conn, DBusMessage *msg,
- void *data)
-{
- struct player *player = data;
-
- player_control(player, msg, "Previous");
-
- return NULL;
-}
-
-static gboolean status_exists(const GDBusPropertyTable *property, void *data)
-{
- struct player *player = data;
-
- return player_get_status(player) != NULL;
-}
-
-static gboolean get_status(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
-{
- struct player *player = data;
- const char *status;
-
- status = player_get_status(player);
-
- dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &status);
-
- return TRUE;
-}
-
-static gboolean repeat_exists(const GDBusPropertyTable *property, void *data)
-{
- DBusMessageIter iter;
- struct player *player = data;
-
- return g_dbus_proxy_get_property(player->proxy, "Repeat", &iter);
-}
-
-static const char *repeat_to_loopstatus(const char *value)
-{
- if (strcasecmp(value, "off") == 0)
- return "None";
- else if (strcasecmp(value, "singletrack") == 0)
- return "Track";
- else if (strcasecmp(value, "alltracks") == 0)
- return "Playlist";
-
- return NULL;
-}
-
-static gboolean get_repeat(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
-{
- struct player *player = data;
- DBusMessageIter value;
- const char *status;
-
- if (!g_dbus_proxy_get_property(player->proxy, "Repeat", &value))
- return FALSE;
-
- dbus_message_iter_get_basic(&value, &status);
-
- status = repeat_to_loopstatus(status);
- if (status == NULL)
- return FALSE;
-
- dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &status);
-
- return TRUE;
-}
-
-static const char *loopstatus_to_repeat(const char *value)
-{
- if (strcasecmp(value, "None") == 0)
- return "off";
- else if (strcasecmp(value, "Track") == 0)
- return "singletrack";
- else if (strcasecmp(value, "Playlist") == 0)
- return "alltracks";
-
- return NULL;
-}
-
-static void property_result(const DBusError *err, void *user_data)
-{
- GDBusPendingPropertySet id = GPOINTER_TO_UINT(user_data);
-
- if (!dbus_error_is_set(err))
- return g_dbus_pending_property_success(id);
-
- g_dbus_pending_property_error(id, err->name, err->message);
-}
-
-static void set_repeat(const GDBusPropertyTable *property,
- DBusMessageIter *iter, GDBusPendingPropertySet id,
- void *data)
-{
- struct player *player = data;
- const char *value;
-
- if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) {
- g_dbus_pending_property_error(id,
- ERROR_INTERFACE ".InvalidArguments",
- "Invalid arguments in method call");
- return;
- }
-
- dbus_message_iter_get_basic(iter, &value);
-
- value = loopstatus_to_repeat(value);
- if (value == NULL) {
- g_dbus_pending_property_error(id,
- ERROR_INTERFACE ".InvalidArguments",
- "Invalid arguments in method call");
- return;
- }
-
- g_dbus_proxy_set_property_basic(player->proxy, "Repeat",
- DBUS_TYPE_STRING, &value,
- property_result, GUINT_TO_POINTER(id),
- NULL);
-}
-
-static gboolean get_double(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
-{
- double value = 1.0;
-
- dbus_message_iter_append_basic(iter, DBUS_TYPE_DOUBLE, &value);
-
- return TRUE;
-}
-
-static gboolean shuffle_exists(const GDBusPropertyTable *property, void *data)
-{
- DBusMessageIter iter;
- struct player *player = data;
-
- return g_dbus_proxy_get_property(player->proxy, "Shuffle", &iter);
-}
-
-static gboolean get_shuffle(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
-{
- struct player *player = data;
- DBusMessageIter value;
- const char *string;
- dbus_bool_t shuffle;
-
- if (!g_dbus_proxy_get_property(player->proxy, "Shuffle", &value))
- return FALSE;
-
- dbus_message_iter_get_basic(&value, &string);
-
- shuffle = strcmp(string, "off") != 0;
-
- dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &shuffle);
-
- return TRUE;
-}
-
-static void set_shuffle(const GDBusPropertyTable *property,
- DBusMessageIter *iter, GDBusPendingPropertySet id,
- void *data)
-{
- struct player *player = data;
- dbus_bool_t shuffle;
- const char *value;
-
- if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_BOOLEAN) {
- g_dbus_pending_property_error(id,
- ERROR_INTERFACE ".InvalidArguments",
- "Invalid arguments in method call");
- return;
- }
-
- dbus_message_iter_get_basic(iter, &shuffle);
- value = shuffle ? "alltracks" : "off";
-
- g_dbus_proxy_set_property_basic(player->proxy, "Shuffle",
- DBUS_TYPE_STRING, &value,
- property_result, GUINT_TO_POINTER(id),
- NULL);
-}
-
-static gboolean position_exists(const GDBusPropertyTable *property, void *data)
-{
- DBusMessageIter iter;
- struct player *player = data;
-
- return g_dbus_proxy_get_property(player->proxy, "Position", &iter);
-}
-
-static gboolean get_position(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
-{
- struct player *player = data;
- DBusMessageIter var;
- uint32_t position;
- int64_t value;
-
- if (!g_dbus_proxy_get_property(player->proxy, "Position", &var))
- return FALSE;
-
- dbus_message_iter_get_basic(&var, &position);
-
- value = position * 1000;
-
- dbus_message_iter_append_basic(iter, DBUS_TYPE_INT64, &value);
-
- return TRUE;
-}
-
-static gboolean track_exists(const GDBusPropertyTable *property, void *data)
-{
- DBusMessageIter iter;
- struct player *player = data;
-
- return g_dbus_proxy_get_property(player->proxy, "Track", &iter);
-}
-
-static gboolean parse_string_metadata(DBusMessageIter *iter, const char *key,
- DBusMessageIter *metadata)
-{
- const char *value;
-
- if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
- return FALSE;
-
- dbus_message_iter_get_basic(iter, &value);
-
- dict_append_entry(metadata, key, DBUS_TYPE_STRING, &value);
-
- return TRUE;
-}
-
-static gboolean parse_array_metadata(DBusMessageIter *iter, const char *key,
- DBusMessageIter *metadata)
-{
- char **value;
-
- if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
- return FALSE;
-
- value = dbus_malloc0(sizeof(char *));
-
- dbus_message_iter_get_basic(iter, &(value[0]));
-
- dict_append_array(metadata, key, DBUS_TYPE_STRING, &value, 1);
-
- dbus_free(value);
-
- return TRUE;
-}
-
-static gboolean parse_int64_metadata(DBusMessageIter *iter, const char *key,
- DBusMessageIter *metadata)
-{
- uint32_t duration;
- int64_t value;
-
- if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT32)
- return FALSE;
-
- dbus_message_iter_get_basic(iter, &duration);
-
- value = duration * 1000;
-
- dict_append_entry(metadata, key, DBUS_TYPE_INT64, &value);
-
- return TRUE;
-}
-
-static gboolean parse_int32_metadata(DBusMessageIter *iter, const char *key,
- DBusMessageIter *metadata)
-{
- uint32_t value;
-
- if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT32)
- return FALSE;
-
- dbus_message_iter_get_basic(iter, &value);
-
- dict_append_entry(metadata, key, DBUS_TYPE_INT32, &value);
-
- return TRUE;
-}
-
-static gboolean parse_path_metadata(DBusMessageIter *iter, const char *key,
- DBusMessageIter *metadata)
-{
- const char *value;
-
- if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_OBJECT_PATH)
- return FALSE;
-
- dbus_message_iter_get_basic(iter, &value);
-
- dict_append_entry(metadata, key, DBUS_TYPE_OBJECT_PATH, &value);
-
- return TRUE;
-}
-
-static int parse_track_entry(DBusMessageIter *entry, const char *key,
- DBusMessageIter *metadata)
-{
- DBusMessageIter var;
-
- if (dbus_message_iter_get_arg_type(entry) != DBUS_TYPE_VARIANT)
- return -EINVAL;
-
- dbus_message_iter_recurse(entry, &var);
-
- if (strcasecmp(key, "Title") == 0) {
- if (!parse_string_metadata(&var, "xesam:title", metadata))
- return -EINVAL;
- } else if (strcasecmp(key, "Artist") == 0) {
- if (!parse_array_metadata(&var, "xesam:artist", metadata))
- return -EINVAL;
- } else if (strcasecmp(key, "Album") == 0) {
- if (!parse_string_metadata(&var, "xesam:album", metadata))
- return -EINVAL;
- } else if (strcasecmp(key, "Genre") == 0) {
- if (!parse_array_metadata(&var, "xesam:genre", metadata))
- return -EINVAL;
- } else if (strcasecmp(key, "Duration") == 0) {
- if (!parse_int64_metadata(&var, "mpris:length", metadata))
- return -EINVAL;
- } else if (strcasecmp(key, "TrackNumber") == 0) {
- if (!parse_int32_metadata(&var, "xesam:trackNumber", metadata))
- return -EINVAL;
- } else if (strcasecmp(key, "Item") == 0) {
- if (!parse_path_metadata(&var, "mpris:trackid", metadata))
- return -EINVAL;
- }
-
- return 0;
-}
-
-static gboolean get_track(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
-{
- struct player *player = data;
- DBusMessageIter var, metadata;
-
- if (!g_dbus_proxy_get_property(player->proxy, "Track", &var))
- return FALSE;
-
- dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
- DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
- DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
- DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &metadata);
-
- parse_metadata(&var, &metadata, parse_track_entry);
-
- dbus_message_iter_close_container(iter, &metadata);
-
- return TRUE;
-}
-
-static gboolean get_enable(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
-{
- dbus_bool_t value = TRUE;
-
- dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
-
- return TRUE;
-}
-
-
-static gboolean get_volume(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
-{
- struct player *player = data;
- double value = 0.0;
- uint16_t volume;
- DBusMessageIter var;
-
- if (player->transport == NULL)
- goto done;
-
- if (!g_dbus_proxy_get_property(player->transport, "Volume", &var))
- goto done;
-
- dbus_message_iter_get_basic(&var, &volume);
-
- value = (double) volume / 127;
-
-done:
- dbus_message_iter_append_basic(iter, DBUS_TYPE_DOUBLE, &value);
-
- return TRUE;
-}
-
-static const GDBusMethodTable player_methods[] = {
- { GDBUS_ASYNC_METHOD("PlayPause", NULL, NULL, player_toggle) },
- { GDBUS_ASYNC_METHOD("Play", NULL, NULL, player_play) },
- { GDBUS_ASYNC_METHOD("Pause", NULL, NULL, player_pause) },
- { GDBUS_ASYNC_METHOD("Stop", NULL, NULL, player_stop) },
- { GDBUS_ASYNC_METHOD("Next", NULL, NULL, player_next) },
- { GDBUS_ASYNC_METHOD("Previous", NULL, NULL, player_previous) },
- { }
-};
-
-static const GDBusSignalTable player_signals[] = {
- { GDBUS_SIGNAL("Seeked", GDBUS_ARGS({"Position", "x"})) },
- { }
-};
-
-static const GDBusPropertyTable player_properties[] = {
- { "PlaybackStatus", "s", get_status, NULL, status_exists },
- { "LoopStatus", "s", get_repeat, set_repeat, repeat_exists },
- { "Rate", "d", get_double, NULL, NULL },
- { "MinimumRate", "d", get_double, NULL, NULL },
- { "MaximumRate", "d", get_double, NULL, NULL },
- { "Shuffle", "b", get_shuffle, set_shuffle, shuffle_exists },
- { "Position", "x", get_position, NULL, position_exists },
- { "Metadata", "a{sv}", get_track, NULL, track_exists },
- { "Volume", "d", get_volume, NULL, NULL },
- { "CanGoNext", "b", get_enable, NULL, NULL },
- { "CanGoPrevious", "b", get_enable, NULL, NULL },
- { "CanPlay", "b", get_enable, NULL, NULL },
- { "CanPause", "b", get_enable, NULL, NULL },
- { "CanSeek", "b", get_enable, NULL, NULL },
- { "CanControl", "b", get_enable, NULL, NULL },
- { }
-};
-
-static gboolean get_disable(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
-{
- dbus_bool_t value = FALSE;
-
- dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
-
- return TRUE;
-}
-
-static gboolean get_name(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
-{
- struct player *player = data;
- DBusMessageIter var;
- const char *alias;
- char *name;
-
- if (!g_dbus_proxy_get_property(player->device, "Alias", &var))
- return FALSE;
-
- dbus_message_iter_get_basic(&var, &alias);
-
- if (g_dbus_proxy_get_property(player->proxy, "Name", &var)) {
- dbus_message_iter_get_basic(&var, &name);
- name = g_strconcat(alias, " ", name, NULL);
- } else
- name = g_strdup(alias);
-
- dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &name);
-
- g_free(name);
-
- return TRUE;
-}
-
-static const GDBusMethodTable mpris_methods[] = {
- { }
-};
-
-static gboolean get_tracklist(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
-{
- struct player *player = data;
- dbus_bool_t value;
-
- value = player->tracklist != NULL ? TRUE : FALSE;
-
- dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &value);
-
- return TRUE;
-}
-
-static const GDBusPropertyTable mpris_properties[] = {
- { "CanQuit", "b", get_disable, NULL, NULL },
- { "Fullscreen", "b", get_disable, NULL, NULL },
- { "CanSetFullscreen", "b", get_disable, NULL, NULL },
- { "CanRaise", "b", get_disable, NULL, NULL },
- { "HasTrackList", "b", get_tracklist, NULL, NULL },
- { "Identity", "s", get_name, NULL, NULL },
- { }
-};
-
-static GDBusProxy *find_item(struct player *player, const char *path)
-{
- struct tracklist *tracklist = player->tracklist;
- GSList *l;
-
- for (l = tracklist->items; l; l = l->next) {
- GDBusProxy *proxy = l->data;
- const char *p = g_dbus_proxy_get_path(proxy);
-
- if (g_str_equal(path, p))
- return proxy;
- }
-
- return NULL;
-}
-
-static void append_item_metadata(void *data, void *user_data)
-{
- GDBusProxy *item = data;
- DBusMessageIter *iter = user_data;
- DBusMessageIter var, metadata;
- const char *path = g_dbus_proxy_get_path(item);
-
- dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
- DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
- DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
- DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &metadata);
-
- dict_append_entry(&metadata, "mpris:trackid", DBUS_TYPE_OBJECT_PATH,
- &path);
-
- if (g_dbus_proxy_get_property(item, "Metadata", &var))
- parse_metadata(&var, &metadata, parse_track_entry);
-
- dbus_message_iter_close_container(iter, &metadata);
-
- return;
-}
-
-static DBusMessage *tracklist_get_metadata(DBusConnection *conn,
- DBusMessage *msg, void *data)
-{
- struct player *player = data;
- DBusMessage *reply;
- DBusMessageIter args, array;
- GSList *l = NULL;
-
- dbus_message_iter_init(msg, &args);
-
- if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_ARRAY)
- return g_dbus_create_error(msg,
- ERROR_INTERFACE ".InvalidArguments",
- "Invalid Arguments");
-
- dbus_message_iter_recurse(&args, &array);
-
- while (dbus_message_iter_get_arg_type(&array) ==
- DBUS_TYPE_OBJECT_PATH) {
- const char *path;
- GDBusProxy *item;
-
- dbus_message_iter_get_basic(&array, &path);
-
- item = find_item(player, path);
- if (item == NULL)
- return g_dbus_create_error(msg,
- ERROR_INTERFACE ".InvalidArguments",
- "Invalid Arguments");
-
- l = g_slist_append(l, item);
-
- dbus_message_iter_next(&array);
- }
-
- reply = dbus_message_new_method_return(msg);
-
- dbus_message_iter_init_append(reply, &args);
-
- dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY,
- DBUS_TYPE_ARRAY_AS_STRING
- DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
- DBUS_TYPE_STRING_AS_STRING
- DBUS_TYPE_VARIANT_AS_STRING
- DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
- &array);
-
- g_slist_foreach(l, append_item_metadata, &array);
-
- dbus_message_iter_close_container(&args, &array);
-
- return reply;
-}
-
-static void item_play_reply(DBusMessage *message, void *user_data)
-{
- struct pending_call *p = user_data;
- struct player *player = p->player;
- DBusMessage *msg = p->msg;
- DBusMessage *reply;
- DBusError err;
-
- dbus_error_init(&err);
- if (dbus_set_error_from_message(&err, message)) {
- fprintf(stderr, "error: %s", err.name);
- reply = g_dbus_create_error(msg, err.name, "%s", err.message);
- dbus_error_free(&err);
- } else
- reply = g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
-
- g_dbus_send_message(player->conn, reply);
-}
-
-static void item_play(struct player *player, DBusMessage *msg,
- GDBusProxy *item)
-{
- struct pending_call *p;
-
- p = g_new0(struct pending_call, 1);
- p->player = player;
- p->msg = dbus_message_ref(msg);
-
- g_dbus_proxy_method_call(item, "Play", NULL, item_play_reply,
- p, pending_call_free);
-}
-
-static DBusMessage *tracklist_goto(DBusConnection *conn,
- DBusMessage *msg, void *data)
-{
- struct player *player = data;
- GDBusProxy *item;
- const char *path;
-
- if (!dbus_message_get_args(msg, NULL,
- DBUS_TYPE_OBJECT_PATH, &path,
- DBUS_TYPE_INVALID))
- return g_dbus_create_error(msg,
- ERROR_INTERFACE ".InvalidArguments",
- "Invalid arguments");
-
- item = find_item(player, path);
- if (item == NULL)
- return g_dbus_create_error(msg,
- ERROR_INTERFACE ".InvalidArguments",
- "Invalid arguments");
-
- item_play(player, msg, item);
-
- return NULL;
-}
-
-static DBusMessage *tracklist_add_track(DBusConnection *conn,
- DBusMessage *msg, void *data)
-{
- return g_dbus_create_error(msg, ERROR_INTERFACE ".NotImplemented",
- "Not implemented");
-}
-
-static DBusMessage *tracklist_remove_track(DBusConnection *conn,
- DBusMessage *msg, void *data)
-{
- return g_dbus_create_error(msg, ERROR_INTERFACE ".NotImplemented",
- "Not implemented");
-}
-
-static const GDBusMethodTable tracklist_methods[] = {
- { GDBUS_METHOD("GetTracksMetadata",
- GDBUS_ARGS({ "tracks", "ao" }),
- GDBUS_ARGS({ "metadata", "aa{sv}" }),
- tracklist_get_metadata) },
- { GDBUS_METHOD("AddTrack",
- GDBUS_ARGS({ "uri", "s" }, { "after", "o" },
- { "current", "b" }),
- NULL,
- tracklist_add_track) },
- { GDBUS_METHOD("RemoveTrack",
- GDBUS_ARGS({ "track", "o" }), NULL,
- tracklist_remove_track) },
- { GDBUS_ASYNC_METHOD("GoTo",
- GDBUS_ARGS({ "track", "o" }), NULL,
- tracklist_goto) },
- { },
-};
-
-static const GDBusSignalTable tracklist_signals[] = {
- { GDBUS_SIGNAL("TrackAdded", GDBUS_ARGS({"metadata", "a{sv}"},
- {"after", "o"})) },
- { GDBUS_SIGNAL("TrackRemoved", GDBUS_ARGS({"track", "o"})) },
- { GDBUS_SIGNAL("TrackMetadataChanged", GDBUS_ARGS({"track", "o"},
- {"metadata", "a{sv}"})) },
- { }
-};
-
-static gboolean tracklist_exists(const GDBusPropertyTable *property, void *data)
-{
- struct player *player = data;
-
- return player->tracklist != NULL;
-}
-
-static void append_path(gpointer data, gpointer user_data)
-{
- GDBusProxy *proxy = data;
- DBusMessageIter *iter = user_data;
- const char *path = g_dbus_proxy_get_path(proxy);
-
- dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
-}
-
-static gboolean get_tracks(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
-{
- struct player *player = data;
- struct tracklist *tracklist = player->tracklist;
- DBusMessageIter value;
-
- if (tracklist == NULL)
- return FALSE;
-
- dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
- DBUS_TYPE_OBJECT_PATH_AS_STRING,
- &value);
- g_slist_foreach(player->tracklist->items, append_path, &value);
- dbus_message_iter_close_container(iter, &value);
-
- return TRUE;
-}
-
-static const GDBusPropertyTable tracklist_properties[] = {
- { "Tracks", "ao", get_tracks, NULL, tracklist_exists },
- { "CanEditTracks", "b", get_disable, NULL, NULL },
- { }
-};
-
-static void list_items_setup(DBusMessageIter *iter, void *user_data)
-{
- DBusMessageIter dict;
-
- 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);
- dbus_message_iter_close_container(iter, &dict);
-}
-
-static void change_folder_reply(DBusMessage *message, void *user_data)
-{
- struct player *player = user_data;
- struct tracklist *tracklist = player->tracklist;
- DBusError err;
-
- dbus_error_init(&err);
- if (dbus_set_error_from_message(&err, message)) {
- fprintf(stderr, "error: %s", err.name);
- return;
- }
-
- g_dbus_emit_property_changed(player->conn, MPRIS_PLAYER_PATH,
- MPRIS_PLAYLISTS_INTERFACE,
- "ActivePlaylist");
-
- g_dbus_proxy_method_call(tracklist->proxy, "ListItems",
- list_items_setup, NULL, NULL, NULL);
-}
-
-static void change_folder_setup(DBusMessageIter *iter, void *user_data)
-{
- struct player *player = user_data;
- const char *path;
-
- path = g_dbus_proxy_get_path(player->playlist);
-
- dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
-}
-
-static DBusMessage *playlist_activate(DBusConnection *conn,
- DBusMessage *msg, void *data)
-{
- struct player *player = data;
- struct tracklist *tracklist = player->tracklist;
- const char *path;
-
- if (player->playlist == NULL || tracklist == NULL)
- return g_dbus_create_error(msg,
- ERROR_INTERFACE ".InvalidArguments",
- "Invalid Arguments");
-
- if (!dbus_message_get_args(msg, NULL,
- DBUS_TYPE_OBJECT_PATH, &path,
- DBUS_TYPE_INVALID))
- return g_dbus_create_error(msg,
- ERROR_INTERFACE ".InvalidArguments",
- "Invalid Arguments");
-
- if (!g_str_equal(path, g_dbus_proxy_get_path(player->playlist)))
- return g_dbus_create_error(msg,
- ERROR_INTERFACE ".InvalidArguments",
- "Invalid Arguments");
-
- g_dbus_proxy_method_call(tracklist->proxy, "ChangeFolder",
- change_folder_setup, change_folder_reply,
- player, NULL);
-
- return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
-}
-
-static DBusMessage *playlist_get(DBusConnection *conn, DBusMessage *msg,
- void *data)
-{
- struct player *player = data;
- uint32_t index, count;
- const char *order;
- dbus_bool_t reverse;
- DBusMessage *reply;
- DBusMessageIter iter, entry, value, name;
- const char *string, *path;
- const char *empty = "";
-
- if (player->playlist == NULL)
- return g_dbus_create_error(msg,
- ERROR_INTERFACE ".InvalidArguments",
- "Invalid Arguments");
-
- if (!dbus_message_get_args(msg, NULL,
- DBUS_TYPE_UINT32, &index,
- DBUS_TYPE_UINT32, &count,
- DBUS_TYPE_STRING, &order,
- DBUS_TYPE_BOOLEAN, &reverse,
- DBUS_TYPE_INVALID))
- return g_dbus_create_error(msg,
- ERROR_INTERFACE ".InvalidArguments",
- "Invalid Arguments");
-
- path = g_dbus_proxy_get_path(player->playlist);
-
- reply = dbus_message_new_method_return(msg);
-
- dbus_message_iter_init_append(reply, &iter);
-
- dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "(oss)",
- &entry);
- dbus_message_iter_open_container(&entry, DBUS_TYPE_STRUCT, NULL,
- &value);
- dbus_message_iter_append_basic(&value, DBUS_TYPE_OBJECT_PATH, &path);
- if (g_dbus_proxy_get_property(player->playlist, "Name", &name)) {
- dbus_message_iter_get_basic(&name, &string);
- dbus_message_iter_append_basic(&value, DBUS_TYPE_STRING,
- &string);
- } else {
- dbus_message_iter_append_basic(&value, DBUS_TYPE_STRING,
- &path);
- }
- dbus_message_iter_append_basic(&value, DBUS_TYPE_STRING, &empty);
- dbus_message_iter_close_container(&entry, &value);
- dbus_message_iter_close_container(&iter, &entry);
-
- return reply;
-}
-
-static const GDBusMethodTable playlist_methods[] = {
- { GDBUS_METHOD("ActivatePlaylist",
- GDBUS_ARGS({ "playlist", "o" }), NULL,
- playlist_activate) },
- { GDBUS_METHOD("GetPlaylists",
- GDBUS_ARGS({ "index", "u" }, { "maxcount", "u"},
- { "order", "s" }, { "reverse", "b" }),
- GDBUS_ARGS({ "playlists", "a(oss)"}),
- playlist_get) },
- { },
-};
-
-static gboolean playlist_exists(const GDBusPropertyTable *property, void *data)
-{
- struct player *player = data;
-
- return player->playlist != NULL;
-}
-
-static gboolean get_playlist_count(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
-{
- struct player *player = data;
- uint32_t count = 1;
-
- if (player->playlist == NULL)
- return FALSE;
-
- dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &count);
-
- return TRUE;
-}
-
-static gboolean get_orderings(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
-{
- DBusMessageIter value;
- const char *order = "User";
-
- dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
- DBUS_TYPE_OBJECT_PATH_AS_STRING,
- &value);
- dbus_message_iter_append_basic(&value, DBUS_TYPE_STRING, &order);
- dbus_message_iter_close_container(iter, &value);
-
- return TRUE;
-}
-
-static gboolean get_active_playlist(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *data)
-{
- struct player *player = data;
- DBusMessageIter value, entry;
- dbus_bool_t enabled = TRUE;
- const char *path, *empty = "";
-
- if (player->playlist == NULL)
- return FALSE;
-
- path = g_dbus_proxy_get_path(player->playlist);
-
- dbus_message_iter_open_container(iter, DBUS_TYPE_STRUCT,
- NULL, &value);
- dbus_message_iter_append_basic(&value, DBUS_TYPE_BOOLEAN, &enabled);
- dbus_message_iter_open_container(&value, DBUS_TYPE_STRUCT, NULL,
- &entry);
- dbus_message_iter_append_basic(&entry, DBUS_TYPE_OBJECT_PATH, &path);
- dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &path);
- dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &empty);
- dbus_message_iter_close_container(&value, &entry);
- dbus_message_iter_close_container(iter, &value);
-
- return TRUE;
-}
-
-static const GDBusPropertyTable playlist_properties[] = {
- { "PlaylistCount", "u", get_playlist_count, NULL, playlist_exists },
- { "Orderings", "as", get_orderings, NULL, NULL },
- { "ActivePlaylist", "(b(oss))", get_active_playlist, NULL,
- playlist_exists },
- { }
-};
-
-#define a_z "abcdefghijklmnopqrstuvwxyz"
-#define A_Z "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
-#define _0_9 "_0123456789"
-
-static char *mpris_busname(char *name)
-{
- if (g_ascii_isdigit(name[0]))
- return g_strconcat(MPRIS_BUS_NAME, "bt_",
- g_strcanon(name, A_Z a_z _0_9, '_'), NULL);
- else
- return g_strconcat(MPRIS_BUS_NAME,
- g_strcanon(name, A_Z a_z _0_9, '_'), NULL);
-}
-
-static GDBusProxy *find_transport_by_path(const char *path)
-{
- GSList *l;
-
- for (l = transports; l; l = l->next) {
- GDBusProxy *transport = l->data;
- DBusMessageIter iter;
- const char *value;
-
- if (!g_dbus_proxy_get_property(transport, "Device", &iter))
- continue;
-
- dbus_message_iter_get_basic(&iter, &value);
-
- if (strcmp(path, value) == 0)
- return transport;
- }
-
- return NULL;
-}
-
-static struct player *find_player(GDBusProxy *proxy)
-{
- GSList *l;
-
- for (l = players; l; l = l->next) {
- struct player *player = l->data;
- const char *path, *p;
-
- if (player->proxy == proxy)
- return player;
-
- path = g_dbus_proxy_get_path(proxy);
- p = g_dbus_proxy_get_path(player->proxy);
- if (g_str_equal(path, p))
- return player;
- }
-
- return NULL;
-}
-
-static void register_tracklist(GDBusProxy *proxy)
-{
- struct player *player;
- struct tracklist *tracklist;
-
- player = find_player(proxy);
- if (player == NULL)
- return;
-
- if (player->tracklist != NULL)
- return;
-
- tracklist = g_new0(struct tracklist, 1);
- tracklist->proxy = g_dbus_proxy_ref(proxy);
-
- player->tracklist = tracklist;
-
- g_dbus_emit_property_changed(player->conn, MPRIS_PLAYER_PATH,
- MPRIS_INTERFACE,
- "HasTrackList");
-
- if (player->playlist == NULL)
- return;
-
- g_dbus_proxy_method_call(player->tracklist->proxy, "ChangeFolder",
- change_folder_setup, change_folder_reply,
- player, NULL);
-}
-
-static void register_player(GDBusProxy *proxy)
-{
- struct player *player;
- DBusMessageIter iter;
- const char *path, *alias, *name;
- char *busname;
- GDBusProxy *device, *transport;
-
- if (!g_dbus_proxy_get_property(proxy, "Device", &iter))
- return;
-
- dbus_message_iter_get_basic(&iter, &path);
-
- device = g_dbus_proxy_new(client, path, "org.bluez.Device1");
- if (device == NULL)
- return;
-
- if (!g_dbus_proxy_get_property(device, "Alias", &iter))
- return;
-
- dbus_message_iter_get_basic(&iter, &alias);
-
- if (g_dbus_proxy_get_property(proxy, "Name", &iter)) {
- dbus_message_iter_get_basic(&iter, &name);
- busname = g_strconcat(alias, " ", name, NULL);
- } else
- busname = g_strdup(alias);
-
- player = g_new0(struct player, 1);
- player->bus_name = mpris_busname(busname);
- player->proxy = g_dbus_proxy_ref(proxy);
- player->device = device;
-
- g_free(busname);
-
- players = g_slist_prepend(players, player);
-
- printf("Player %s created\n", player->bus_name);
-
- player->conn = g_dbus_setup_private(DBUS_BUS_SESSION, player->bus_name,
- NULL);
- if (!session) {
- fprintf(stderr, "Could not register bus name %s",
- player->bus_name);
- goto fail;
- }
-
- if (!g_dbus_register_interface(player->conn, MPRIS_PLAYER_PATH,
- MPRIS_INTERFACE,
- mpris_methods,
- NULL,
- mpris_properties,
- player, NULL)) {
- fprintf(stderr, "Could not register interface %s",
- MPRIS_INTERFACE);
- goto fail;
- }
-
- if (!g_dbus_register_interface(player->conn, MPRIS_PLAYER_PATH,
- MPRIS_PLAYER_INTERFACE,
- player_methods,
- player_signals,
- player_properties,
- player, player_free)) {
- fprintf(stderr, "Could not register interface %s",
- MPRIS_PLAYER_INTERFACE);
- goto fail;
- }
-
- if (!g_dbus_register_interface(player->conn, MPRIS_PLAYER_PATH,
- MPRIS_TRACKLIST_INTERFACE,
- tracklist_methods,
- tracklist_signals,
- tracklist_properties,
- player, NULL)) {
- fprintf(stderr, "Could not register interface %s",
- MPRIS_TRACKLIST_INTERFACE);
- goto fail;
- }
-
- if (!g_dbus_register_interface(player->conn, MPRIS_PLAYER_PATH,
- MPRIS_PLAYLISTS_INTERFACE,
- playlist_methods,
- NULL,
- playlist_properties,
- player, NULL)) {
- fprintf(stderr, "Could not register interface %s",
- MPRIS_PLAYLISTS_INTERFACE);
- goto fail;
- }
-
- transport = find_transport_by_path(path);
- if (transport)
- player->transport = g_dbus_proxy_ref(transport);
-
- return;
-
-fail:
- players = g_slist_remove(players, player);
- player_free(player);
-}
-
-static struct player *find_player_by_device(const char *device)
-{
- GSList *l;
-
- for (l = players; l; l = l->next) {
- struct player *player = l->data;
- const char *path = g_dbus_proxy_get_path(player->device);
-
- if (g_strcmp0(device, path) == 0)
- return player;
- }
-
- return NULL;
-}
-
-static void register_transport(GDBusProxy *proxy)
-{
- struct player *player;
- DBusMessageIter iter;
- const char *path;
-
- if (g_slist_find(transports, proxy) != NULL)
- return;
-
- if (!g_dbus_proxy_get_property(proxy, "Volume", &iter))
- return;
-
- if (!g_dbus_proxy_get_property(proxy, "Device", &iter))
- return;
-
- dbus_message_iter_get_basic(&iter, &path);
-
- transports = g_slist_append(transports, proxy);
-
- player = find_player_by_device(path);
- if (player == NULL || player->transport != NULL)
- return;
-
- player->transport = g_dbus_proxy_ref(proxy);
-}
-
-static struct player *find_player_by_item(const char *item)
-{
- GSList *l;
-
- for (l = players; l; l = l->next) {
- struct player *player = l->data;
- const char *path = g_dbus_proxy_get_path(player->proxy);
-
- if (g_str_has_prefix(item, path))
- return player;
- }
-
- return NULL;
-}
-
-static void register_playlist(struct player *player, GDBusProxy *proxy)
-{
- const char *path;
- DBusMessageIter iter;
-
- if (!g_dbus_proxy_get_property(player->proxy, "Playlist", &iter))
- return;
-
- dbus_message_iter_get_basic(&iter, &path);
-
- if (!g_str_equal(path, g_dbus_proxy_get_path(proxy)))
- return;
-
- player->playlist = g_dbus_proxy_ref(proxy);
-
- g_dbus_emit_property_changed(player->conn, MPRIS_PLAYER_PATH,
- MPRIS_PLAYLISTS_INTERFACE,
- "PlaylistCount");
-
- if (player->tracklist == NULL)
- return;
-
- g_dbus_proxy_method_call(player->tracklist->proxy, "ChangeFolder",
- change_folder_setup, change_folder_reply,
- player, NULL);
-}
-
-static void register_item(struct player *player, GDBusProxy *proxy)
-{
- struct tracklist *tracklist;
- const char *path, *playlist;
- DBusMessage *signal;
- DBusMessageIter iter, args, metadata;
- GSList *l;
- GDBusProxy *after;
-
- if (player->playlist == NULL) {
- register_playlist(player, proxy);
- return;
- }
-
- tracklist = player->tracklist;
- if (tracklist == NULL)
- return;
-
- path = g_dbus_proxy_get_path(proxy);
- playlist = g_dbus_proxy_get_path(player->playlist);
- if (!g_str_has_prefix(path, playlist))
- return;
-
- l = g_slist_last(tracklist->items);
- tracklist->items = g_slist_append(tracklist->items, proxy);
-
- g_dbus_emit_property_changed(player->conn, MPRIS_PLAYER_PATH,
- MPRIS_TRACKLIST_INTERFACE,
- "Tracks");
-
- if (l == NULL)
- return;
-
- signal = dbus_message_new_signal(MPRIS_PLAYER_PATH,
- MPRIS_TRACKLIST_INTERFACE,
- "TrackAdded");
- if (!signal) {
- fprintf(stderr, "Unable to allocate new %s.TrackAdded signal",
- MPRIS_TRACKLIST_INTERFACE);
- return;
- }
-
- dbus_message_iter_init_append(signal, &args);
-
- if (!g_dbus_proxy_get_property(proxy, "Metadata", &iter)) {
- dbus_message_unref(signal);
- return;
- }
-
- dbus_message_iter_open_container(&args, DBUS_TYPE_ARRAY,
- DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
- DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
- DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &metadata);
-
- parse_metadata(&iter, &metadata, parse_track_entry);
-
- dbus_message_iter_close_container(&args, &metadata);
-
- after = l->data;
- path = g_dbus_proxy_get_path(after);
- dbus_message_iter_append_basic(&args, DBUS_TYPE_OBJECT_PATH, &path);
-
- g_dbus_send_message(player->conn, signal);
-}
-
-static void proxy_added(GDBusProxy *proxy, void *user_data)
-{
- const char *interface;
- const char *path;
-
- interface = g_dbus_proxy_get_interface(proxy);
- path = g_dbus_proxy_get_path(proxy);
-
- if (!strcmp(interface, BLUEZ_ADAPTER_INTERFACE)) {
- if (adapter != NULL)
- return;
-
- printf("Bluetooth Adapter %s found\n", path);
- adapter = proxy;
- list_names(session);
- } else if (!strcmp(interface, BLUEZ_MEDIA_PLAYER_INTERFACE)) {
- printf("Bluetooth Player %s found\n", path);
- register_player(proxy);
- } else if (!strcmp(interface, BLUEZ_MEDIA_TRANSPORT_INTERFACE)) {
- printf("Bluetooth Transport %s found\n", path);
- register_transport(proxy);
- } else if (!strcmp(interface, BLUEZ_MEDIA_FOLDER_INTERFACE)) {
- printf("Bluetooth Folder %s found\n", path);
- register_tracklist(proxy);
- } else if (!strcmp(interface, BLUEZ_MEDIA_ITEM_INTERFACE)) {
- struct player *player;
-
- player = find_player_by_item(path);
- if (player == NULL)
- return;
-
- printf("Bluetooth Item %s found\n", path);
- register_item(player, proxy);
- }
-}
-
-static void unregister_player(struct player *player)
-{
- players = g_slist_remove(players, player);
-
- if (player->tracklist != NULL) {
- g_dbus_unregister_interface(player->conn, MPRIS_PLAYER_PATH,
- MPRIS_PLAYLISTS_INTERFACE);
- g_dbus_unregister_interface(player->conn, MPRIS_PLAYER_PATH,
- MPRIS_TRACKLIST_INTERFACE);
- }
-
- g_dbus_unregister_interface(player->conn, MPRIS_PLAYER_PATH,
- MPRIS_INTERFACE);
-
- g_dbus_unregister_interface(player->conn, MPRIS_PLAYER_PATH,
- MPRIS_PLAYER_INTERFACE);
-}
-
-static struct player *find_player_by_transport(GDBusProxy *proxy)
-{
- GSList *l;
-
- for (l = players; l; l = l->next) {
- struct player *player = l->data;
-
- if (player->transport == proxy)
- return player;
- }
-
- return NULL;
-}
-
-static void unregister_transport(GDBusProxy *proxy)
-{
- struct player *player;
-
- if (g_slist_find(transports, proxy) == NULL)
- return;
-
- transports = g_slist_remove(transports, proxy);
-
- player = find_player_by_transport(proxy);
- if (player == NULL)
- return;
-
- g_dbus_proxy_unref(player->transport);
- player->transport = NULL;
-}
-
-static void unregister_item(struct player *player, GDBusProxy *proxy)
-{
- struct tracklist *tracklist = player->tracklist;
- const char *path;
-
- if (tracklist == NULL)
- return;
-
- if (g_slist_find(tracklist->items, proxy) == NULL)
- return;
-
- path = g_dbus_proxy_get_path(proxy);
-
- tracklist->items = g_slist_remove(tracklist->items, proxy);
-
- g_dbus_emit_property_changed(player->conn, MPRIS_PLAYER_PATH,
- MPRIS_TRACKLIST_INTERFACE,
- "Tracks");
-
- g_dbus_emit_signal(player->conn, MPRIS_PLAYER_PATH,
- MPRIS_TRACKLIST_INTERFACE, "TrackRemoved",
- DBUS_TYPE_OBJECT_PATH, &path,
- DBUS_TYPE_INVALID);
-}
-
-static void remove_players(DBusConnection *conn)
-{
- char **paths;
- int i;
-
- dbus_connection_list_registered(conn, "/", &paths);
-
- for (i = 0; paths[i]; i++) {
- char *path;
- void *data;
-
- path = g_strdup_printf("/%s", paths[i]);
- dbus_connection_get_object_path_data(sys, path, &data);
- dbus_connection_unregister_object_path(sys, path);
-
- g_free(path);
- g_free(data);
- }
-
- dbus_free_string_array(paths);
-}
-
-static void proxy_removed(GDBusProxy *proxy, void *user_data)
-{
- const char *interface;
- const char *path;
-
- if (adapter == NULL)
- return;
-
- interface = g_dbus_proxy_get_interface(proxy);
- path = g_dbus_proxy_get_path(proxy);
-
- if (strcmp(interface, BLUEZ_ADAPTER_INTERFACE) == 0) {
- if (adapter != proxy)
- return;
- printf("Bluetooth Adapter %s removed\n", path);
- adapter = NULL;
- remove_players(sys);
- } else if (strcmp(interface, BLUEZ_MEDIA_PLAYER_INTERFACE) == 0) {
- struct player *player;
-
- player = find_player(proxy);
- if (player == NULL)
- return;
-
- printf("Bluetooth Player %s removed\n", path);
- unregister_player(player);
- } else if (strcmp(interface, BLUEZ_MEDIA_TRANSPORT_INTERFACE) == 0) {
- printf("Bluetooth Transport %s removed\n", path);
- unregister_transport(proxy);
- } else if (strcmp(interface, BLUEZ_MEDIA_ITEM_INTERFACE) == 0) {
- struct player *player;
-
- player = find_player_by_item(path);
- if (player == NULL)
- return;
-
- printf("Bluetooth Item %s removed\n", path);
- unregister_item(player, proxy);
- }
-}
-
-static const char *property_to_mpris(const char *property)
-{
- if (strcasecmp(property, "Repeat") == 0)
- return "LoopStatus";
- else if (strcasecmp(property, "Shuffle") == 0)
- return "Shuffle";
- else if (strcasecmp(property, "Status") == 0)
- return "PlaybackStatus";
- else if (strcasecmp(property, "Position") == 0)
- return "Position";
- else if (strcasecmp(property, "Track") == 0)
- return "Metadata";
-
- return NULL;
-}
-
-static void player_property_changed(GDBusProxy *proxy, const char *name,
- DBusMessageIter *iter, void *user_data)
-{
- struct player *player;
- const char *property;
- uint32_t position;
- uint64_t value;
-
- player = find_player(proxy);
- if (player == NULL)
- return;
-
- property = property_to_mpris(name);
- if (property == NULL)
- return;
-
- g_dbus_emit_property_changed(player->conn, MPRIS_PLAYER_PATH,
- MPRIS_PLAYER_INTERFACE,
- property);
-
- if (strcasecmp(name, "Position") != 0)
- return;
-
- dbus_message_iter_get_basic(iter, &position);
-
- value = position * 1000;
-
- g_dbus_emit_signal(player->conn, MPRIS_PLAYER_PATH,
- MPRIS_PLAYER_INTERFACE, "Seeked",
- DBUS_TYPE_INT64, &value,
- DBUS_TYPE_INVALID);
-}
-
-static void transport_property_changed(GDBusProxy *proxy, const char *name,
- DBusMessageIter *iter, void *user_data)
-{
- struct player *player;
- DBusMessageIter var;
- const char *path;
-
- if (strcasecmp(name, "Volume") != 0 && strcasecmp(name, "State") != 0)
- return;
-
- if (!g_dbus_proxy_get_property(proxy, "Device", &var))
- return;
-
- dbus_message_iter_get_basic(&var, &path);
-
- player = find_player_by_device(path);
- if (player == NULL)
- return;
-
- if (strcasecmp(name, "State") == 0) {
- if (!g_dbus_proxy_get_property(player->proxy, "Status", &var))
- g_dbus_emit_property_changed(player->conn,
- MPRIS_PLAYER_PATH,
- MPRIS_PLAYER_INTERFACE,
- "PlaybackStatus");
- return;
- }
-
- g_dbus_emit_property_changed(player->conn, MPRIS_PLAYER_PATH,
- MPRIS_PLAYER_INTERFACE,
- name);
-}
-
-static void item_property_changed(GDBusProxy *proxy, const char *name,
- DBusMessageIter *iter, void *user_data)
-{
- struct player *player;
- DBusMessage *signal;
- DBusMessageIter args;
- const char *path;
-
- path = g_dbus_proxy_get_path(proxy);
-
- player = find_player_by_item(path);
- if (player == NULL)
- return;
-
- if (strcasecmp(name, "Metadata") != 0)
- return;
-
- signal = dbus_message_new_signal(MPRIS_PLAYER_PATH,
- MPRIS_TRACKLIST_INTERFACE,
- "TrackMetadataChanged");
- if (!signal) {
- fprintf(stderr, "Unable to allocate new %s.TrackAdded signal",
- MPRIS_TRACKLIST_INTERFACE);
- return;
- }
-
- dbus_message_iter_init_append(signal, &args);
-
- dbus_message_iter_append_basic(&args, DBUS_TYPE_OBJECT_PATH, &path);
-
- append_iter(&args, iter);
-
- g_dbus_send_message(player->conn, signal);
-}
-
-static void property_changed(GDBusProxy *proxy, const char *name,
- DBusMessageIter *iter, void *user_data)
-{
- const char *interface;
-
- interface = g_dbus_proxy_get_interface(proxy);
-
- if (strcmp(interface, BLUEZ_MEDIA_PLAYER_INTERFACE) == 0)
- return player_property_changed(proxy, name, iter, user_data);
-
- if (strcmp(interface, BLUEZ_MEDIA_TRANSPORT_INTERFACE) == 0)
- return transport_property_changed(proxy, name, iter,
- user_data);
-
- if (strcmp(interface, BLUEZ_MEDIA_ITEM_INTERFACE) == 0)
- return item_property_changed(proxy, name, iter, user_data);
-}
-
-int main(int argc, char *argv[])
-{
- GOptionContext *context;
- GError *error = NULL;
- guint owner_watch, properties_watch, signal_watch;
- struct sigaction sa;
-
- context = g_option_context_new(NULL);
- g_option_context_add_main_entries(context, options, NULL);
-
- if (g_option_context_parse(context, &argc, &argv, &error) == FALSE) {
- if (error != NULL) {
- g_printerr("%s\n", error->message);
- g_error_free(error);
- } else
- g_printerr("An unknown error occurred\n");
- exit(1);
- }
-
- g_option_context_free(context);
-
- if (option_version == TRUE) {
- usage();
- exit(0);
- }
-
- main_loop = g_main_loop_new(NULL, FALSE);
-
- sys = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
- if (!sys) {
- fprintf(stderr, "Can't get on system bus");
- exit(1);
- }
-
- session = g_dbus_setup_bus(DBUS_BUS_SESSION, NULL, NULL);
- if (!session) {
- fprintf(stderr, "Can't get on session bus");
- exit(1);
- }
-
- owner_watch = g_dbus_add_signal_watch(session, NULL, NULL,
- DBUS_INTERFACE_DBUS,
- "NameOwnerChanged",
- name_owner_changed,
- NULL, NULL);
-
- properties_watch = g_dbus_add_properties_watch(session, NULL, NULL,
- MPRIS_PLAYER_INTERFACE,
- player_signal,
- NULL, NULL);
-
- signal_watch = g_dbus_add_signal_watch(session, NULL, NULL,
- MPRIS_PLAYER_INTERFACE,
- NULL, player_signal,
- NULL, NULL);
-
- memset(&sa, 0, sizeof(sa));
- sa.sa_flags = SA_NOCLDSTOP;
- sa.sa_handler = sig_term;
- sigaction(SIGTERM, &sa, NULL);
- sigaction(SIGINT, &sa, NULL);
-
- client = g_dbus_client_new(sys, BLUEZ_BUS_NAME, BLUEZ_PATH);
-
- g_dbus_client_set_connect_watch(client, connect_handler, NULL);
- g_dbus_client_set_disconnect_watch(client, disconnect_handler, NULL);
-
- g_dbus_client_set_proxy_handlers(client, proxy_added, proxy_removed,
- property_changed, NULL);
-
- g_main_loop_run(main_loop);
-
- g_dbus_remove_watch(session, owner_watch);
- g_dbus_remove_watch(session, properties_watch);
- g_dbus_remove_watch(session, signal_watch);
-
- g_dbus_client_unref(client);
-
- dbus_connection_unref(session);
- dbus_connection_unref(sys);
-
- g_main_loop_unref(main_loop);
-
- return 0;
-}
#include <dbus/dbus.h>
#include <glib.h>
-#include <gdbus/gdbus.h>
+
+#include "gdbus/gdbus.h"
#define BLUEZ_BUS_NAME "org.bluez"
#define BLUEZ_PATH "/org/bluez"
#include <readline/readline.h>
#include <readline/history.h>
-#include <gobex/gobex.h>
+#include "gobex/gobex.h"
#include "btio/btio.h"
static GMainLoop *main_loop = NULL;
#include <string.h>
#include <errno.h>
-#include <gobex/gobex.h>
+#include "gobex/gobex.h"
#include "btio/btio.h"
static GMainLoop *main_loop = NULL;
#include <signal.h>
#include <sys/signalfd.h>
#include <inttypes.h>
+#include <wordexp.h>
#include <readline/readline.h>
#include <readline/history.h>
#include <glib.h>
-#include <gdbus.h>
-#include <client/display.h>
+#include "gdbus/gdbus.h"
+#include "client/display.h"
/* String display constants */
#define COLORED_NEW COLOR_GREEN "NEW" COLOR_OFF
{
DBusError error;
DBusMessageIter iter, array;
- int ctype;
dbus_error_init(&error);
dbus_message_iter_recurse(&iter, &array);
- while ((ctype = dbus_message_iter_get_arg_type(&array)) ==
- DBUS_TYPE_DICT_ENTRY) {
+ while ((dbus_message_iter_get_arg_type(&array)) ==
+ DBUS_TYPE_DICT_ENTRY) {
DBusMessageIter entry;
const char *obj;
static void rl_handler(char *input)
{
+ wordexp_t w;
int argc;
- char **argv = NULL;
+ char **argv;
int i;
if (!input) {
if (!strlen(input))
goto done;
- g_strstrip(input);
add_history(input);
- argv = g_strsplit(input, " ", -1);
- if (argv == NULL)
+ if (wordexp(input, &w, WRDE_NOCMD))
goto done;
- for (argc = 0; argv[argc];)
- argc++;
+ if (w.we_wordc == 0)
+ goto free_we;
- if (argc == 0)
- goto done;
+ argv = w.we_wordv;
+ argc = w.we_wordc;
for (i = 0; cmd_table[i].cmd; i++) {
if (strcmp(argv[0], cmd_table[i].cmd))
if (cmd_table[i].func) {
cmd_table[i].func(argc, argv);
- goto done;
+ goto free_we;
}
}
if (strcmp(argv[0], "help")) {
printf("Invalid command\n");
- goto done;
+ goto free_we;
}
printf("Available commands:\n");
cmd_table[i].desc ? : "");
}
+free_we:
+ wordfree(&w);
done:
- g_strfreev(argv);
free(input);
}
--- /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 <getopt.h>
+
+#include "lib/bluetooth.h"
+#include "lib/mgmt.h"
+
+#include "src/shared/mainloop.h"
+#include "src/shared/util.h"
+#include "src/shared/mgmt.h"
+
+static bool use_bredr = false;
+static bool use_le = false;
+static bool use_sc = false;
+static bool use_sconly = false;
+static bool use_legacy = false;
+static bool use_random = false;
+static bool use_debug = false;
+static bool use_cross = false;
+static bool provide_p192 = false;
+static bool provide_p256 = false;
+
+static struct mgmt *mgmt;
+static uint16_t index1 = MGMT_INDEX_NONE;
+static uint16_t index2 = MGMT_INDEX_NONE;
+static bdaddr_t bdaddr1;
+static bdaddr_t bdaddr2;
+
+static void pin_code_request_event(uint16_t index, uint16_t len,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_pin_code_request *ev = param;
+ struct mgmt_cp_pin_code_reply cp;
+ char str[18];
+
+ ba2str(&ev->addr.bdaddr, str);
+
+ printf("[Index %u]\n", index);
+ printf(" Pin code request: %s\n", str);
+
+ memset(&cp, 0, sizeof(cp));
+ memcpy(&cp.addr, &ev->addr, sizeof(cp.addr));
+ cp.pin_len = 4;
+ memset(cp.pin_code, '0', 4);
+
+ mgmt_reply(mgmt, MGMT_OP_PIN_CODE_REPLY, index, sizeof(cp), &cp,
+ NULL, NULL, NULL);
+}
+
+static void new_link_key_event(uint16_t index, uint16_t len,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_new_link_key *ev = param;
+ const char *type;
+ char str[18];
+ int i;
+
+ ba2str(&ev->key.addr.bdaddr, str);
+
+ switch (ev->key.type) {
+ case 0x00:
+ type = "Legacy";
+ break;
+ case 0x01:
+ type = "Local Unit";
+ break;
+ case 0x02:
+ type = "Remote Unit";
+ break;
+ case 0x03:
+ type = "Debug";
+ break;
+ case 0x04:
+ type = "Unauthenticated, P-192";
+ break;
+ case 0x05:
+ type = "Authenticated, P-192";
+ break;
+ case 0x06:
+ type = "Changed";
+ break;
+ case 0x07:
+ type = "Unauthenticated, P-256";
+ break;
+ case 0x08:
+ type = "Authenticated, P-256";
+ break;
+ default:
+ type = "<unknown>";
+ break;
+ }
+
+ printf("[Index %u]\n", index);
+ printf(" New link key: %s\n", str);
+ printf(" Type: %s (%u)\n", type, ev->key.type);
+ printf(" Key: ");
+ for (i = 0; i < 16; i++)
+ printf("%02x", ev->key.val[i]);
+ printf("\n");
+}
+
+static void new_long_term_key_event(uint16_t index, uint16_t len,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_new_long_term_key *ev = param;
+ const char *type;
+ char str[18];
+ int i;
+
+ ba2str(&ev->key.addr.bdaddr, str);
+
+ switch (ev->key.type) {
+ case 0x00:
+ if (ev->key.master)
+ type = "Unauthenticated, Master";
+ else
+ type = "Unauthenticated, Slave";
+ break;
+ case 0x01:
+ if (ev->key.master)
+ type = "Authenticated, Master";
+ else
+ type = "Authenticated, Slave";
+ break;
+ case 0x02:
+ type = "Unauthenticated, P-256";
+ break;
+ case 0x03:
+ type = "Authenticated, P-256";
+ break;
+ case 0x04:
+ type = "Debug";
+ break;
+ default:
+ type = "<unknown>";
+ break;
+ }
+
+ printf("[Index %u]\n", index);
+ printf(" New long term key: %s\n", str);
+ printf(" Type: %s (%u)\n", type, ev->key.type);
+ printf(" Key: ");
+ for (i = 0; i < 16; i++)
+ printf("%02x", ev->key.val[i]);
+ printf("\n");
+}
+
+static void pair_device_complete(uint8_t status, uint16_t len,
+ const void *param, void *user_data)
+{
+ uint16_t index = PTR_TO_UINT(user_data);
+
+ if (status) {
+ fprintf(stderr, "Pair device from index %u failed: %s\n",
+ index, mgmt_errstr(status));
+ }
+
+ mainloop_quit();
+}
+
+static void pair_device(uint16_t index, const bdaddr_t *bdaddr)
+{
+ struct mgmt_cp_pair_device cp;
+ char str[18];
+
+ ba2str(bdaddr, str);
+
+ printf("[Index %u]\n", index);
+ printf(" Starting pairing: %s\n", str);
+
+ memset(&cp, 0, sizeof(cp));
+ bacpy(&cp.addr.bdaddr, bdaddr);
+ if (use_bredr)
+ cp.addr.type = BDADDR_BREDR;
+ else if (use_random)
+ cp.addr.type = BDADDR_LE_RANDOM;
+ else
+ cp.addr.type = BDADDR_LE_PUBLIC;
+ cp.io_cap = 0x03;
+
+ mgmt_send(mgmt, MGMT_OP_PAIR_DEVICE, index, sizeof(cp), &cp,
+ pair_device_complete,
+ UINT_TO_PTR(index), NULL);
+}
+
+static void add_remote_oob_data_complete(uint8_t status, uint16_t len,
+ const void *param, void *user_data)
+{
+ const struct mgmt_addr_info *rp = param;
+ uint16_t index = PTR_TO_UINT(user_data);
+ char str[18];
+
+ if (status) {
+ fprintf(stderr, "Adding OOB data for index %u failed: %s\n",
+ index, mgmt_errstr(status));
+ }
+
+ ba2str(&rp->bdaddr, str);
+
+ printf("[Index %u]\n", index);
+ printf(" Remote data added: %s\n", str);
+
+ if (index == index1) {
+ uint8_t val = 0x01;
+
+ mgmt_send(mgmt, MGMT_OP_SET_CONNECTABLE, index2, 1, &val,
+ NULL, NULL, NULL);
+
+ if (use_le)
+ mgmt_send(mgmt, MGMT_OP_SET_ADVERTISING, index2,
+ 1, &val, NULL, NULL, NULL);
+
+ pair_device(index1, &bdaddr2);
+ }
+}
+
+static void add_remote_oob_data(uint16_t index, const bdaddr_t *bdaddr,
+ const uint8_t *hash192, const uint8_t *rand192,
+ const uint8_t *hash256, const uint8_t *rand256)
+{
+ struct mgmt_cp_add_remote_oob_data cp;
+
+ memset(&cp, 0, sizeof(cp));
+ bacpy(&cp.addr.bdaddr, bdaddr);
+ if (use_bredr)
+ cp.addr.type = BDADDR_BREDR;
+ else if (use_random)
+ cp.addr.type = BDADDR_LE_RANDOM;
+ else
+ cp.addr.type = BDADDR_LE_PUBLIC;
+ if (hash192 && rand192) {
+ memcpy(cp.hash192, hash192, 16);
+ memcpy(cp.rand192, rand192, 16);
+ } else {
+ memset(cp.hash192, 0, 16);
+ memset(cp.rand192, 0, 16);
+ }
+ if (hash256 && rand256) {
+ memcpy(cp.hash256, hash256, 16);
+ memcpy(cp.rand256, rand256, 16);
+ } else {
+ memset(cp.hash256, 0, 16);
+ memset(cp.rand256, 0, 16);
+ }
+
+ mgmt_send(mgmt, MGMT_OP_ADD_REMOTE_OOB_DATA, index, sizeof(cp), &cp,
+ add_remote_oob_data_complete,
+ UINT_TO_PTR(index), NULL);
+}
+
+static void read_oob_data_complete(uint8_t status, uint16_t len,
+ const void *param, void *user_data)
+{
+ const struct mgmt_rp_read_local_oob_ext_data *rp = param;
+ uint16_t index = PTR_TO_UINT(user_data);
+ const uint8_t *hash192, *rand192, *hash256, *rand256;
+ int i;
+
+ if (status) {
+ fprintf(stderr, "Reading OOB data for index %u failed: %s\n",
+ index, mgmt_errstr(status));
+ mainloop_quit();
+ return;
+ }
+
+ printf("[Index %u]\n", index);
+
+ if (provide_p192) {
+ hash192 = rp->hash192;
+ rand192 = rp->randomizer192;
+ } else {
+ hash192 = NULL;
+ rand192 = NULL;
+ }
+
+ printf(" Hash C from P-192: ");
+ for (i = 0; i < 16; i++)
+ printf("%02x", rp->hash192[i]);
+ printf("\n");
+
+ printf(" Randomizer R with P-192: ");
+ for (i = 0; i < 16; i++)
+ printf("%02x", rp->randomizer192[i]);
+ printf("\n");
+
+ if (len < sizeof(*rp)) {
+ hash256 = NULL;
+ rand256 = NULL;
+ goto done;
+ }
+
+ if (provide_p256) {
+ hash256 = rp->hash256;
+ rand256 = rp->randomizer256;
+ } else {
+ hash256 = NULL;
+ rand256 = NULL;
+ }
+
+ printf(" Hash C from P-256: ");
+ for (i = 0; i < 16; i++)
+ printf("%02x", rp->hash256[i]);
+ printf("\n");
+
+ printf(" Randomizer R with P-256: ");
+ for (i = 0; i < 16; i++)
+ printf("%02x", rp->randomizer256[i]);
+ printf("\n");
+
+done:
+ if (index == index1)
+ add_remote_oob_data(index2, &bdaddr1,
+ hash192, rand192, hash256, rand256);
+ else if (index == index2)
+ add_remote_oob_data(index1, &bdaddr2,
+ hash192, rand192, hash256, rand256);
+}
+
+static void set_powered_complete(uint8_t status, uint16_t len,
+ const void *param, void *user_data)
+{
+ uint16_t index = PTR_TO_UINT(user_data);
+ uint32_t settings;
+ uint8_t val;
+
+ if (status) {
+ fprintf(stderr, "Powering on for index %u failed: %s\n",
+ index, mgmt_errstr(status));
+ mainloop_quit();
+ return;
+ }
+
+ settings = get_le32(param);
+
+ if (!(settings & MGMT_SETTING_POWERED)) {
+ fprintf(stderr, "Controller is not powered\n");
+ mainloop_quit();
+ return;
+ }
+
+ if (use_debug) {
+ if (index == index1) {
+ val = 0x02;
+ mgmt_send(mgmt, MGMT_OP_SET_DEBUG_KEYS, index, 1, &val,
+ NULL, NULL, NULL);
+ } else if (index == index2) {
+ val = 0x01;
+ mgmt_send(mgmt, MGMT_OP_SET_DEBUG_KEYS, index, 1, &val,
+ NULL, NULL, NULL);
+ }
+ }
+
+ if (use_bredr && (provide_p192 || provide_p256)) {
+ mgmt_send(mgmt, MGMT_OP_READ_LOCAL_OOB_DATA, index, 0, NULL,
+ read_oob_data_complete,
+ UINT_TO_PTR(index), NULL);
+ } else {
+ if (index == index1)
+ add_remote_oob_data(index2, &bdaddr1,
+ NULL, NULL, NULL, NULL);
+ else if (index == index2)
+ add_remote_oob_data(index1, &bdaddr2,
+ NULL, NULL, NULL, NULL);
+ }
+}
+
+static void clear_link_keys(uint16_t index)
+{
+ struct mgmt_cp_load_link_keys cp;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.debug_keys = 0x00;
+ cp.key_count = cpu_to_le16(0);
+
+ mgmt_send(mgmt, MGMT_OP_LOAD_LINK_KEYS, index,
+ sizeof(cp), &cp, NULL, NULL, NULL);
+}
+
+static void clear_long_term_keys(uint16_t index)
+{
+ struct mgmt_cp_load_long_term_keys cp;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.key_count = cpu_to_le16(0);
+
+ mgmt_send(mgmt, MGMT_OP_LOAD_LONG_TERM_KEYS, index,
+ sizeof(cp), &cp, NULL, NULL, NULL);
+}
+
+static void clear_remote_oob_data(uint16_t index)
+{
+ struct mgmt_cp_remove_remote_oob_data cp;
+
+ memset(&cp, 0, sizeof(cp));
+ bacpy(&cp.addr.bdaddr, BDADDR_ANY);
+ cp.addr.type = BDADDR_BREDR;
+
+ mgmt_send(mgmt, MGMT_OP_REMOVE_REMOTE_OOB_DATA, index,
+ sizeof(cp), &cp, NULL, NULL, NULL);
+}
+
+static void read_info(uint8_t status, uint16_t len, const void *param,
+ void *user_data)
+{
+ const struct mgmt_rp_read_info *rp = param;
+ uint16_t index = PTR_TO_UINT(user_data);
+ uint32_t supported_settings;
+ uint8_t val;
+ char str[18];
+
+ if (status) {
+ fprintf(stderr, "Reading info for index %u failed: %s\n",
+ index, mgmt_errstr(status));
+ mainloop_quit();
+ return;
+ }
+
+ ba2str(&rp->bdaddr, str);
+
+ printf("[Index %u]\n", index);
+ printf(" Address: %s\n", str);
+
+ if (index == index1)
+ bacpy(&bdaddr1, &rp->bdaddr);
+ else if (index == index2)
+ bacpy(&bdaddr2, &rp->bdaddr);
+
+ supported_settings = le32_to_cpu(rp->supported_settings);
+
+ if (use_bredr && !(supported_settings & MGMT_SETTING_BREDR)) {
+ fprintf(stderr, "BR/EDR support missing\n");
+ mainloop_quit();
+ return;
+ }
+
+ if (!use_legacy && !(supported_settings & MGMT_SETTING_SSP)) {
+ fprintf(stderr, "Secure Simple Pairing support missing\n");
+ mainloop_quit();
+ return;
+ }
+
+ if (use_le && !(supported_settings & MGMT_SETTING_LE)) {
+ fprintf(stderr, "Low Energy support missing\n");
+ mainloop_quit();
+ return;
+ }
+
+ if (use_sc && !(supported_settings & MGMT_SETTING_SECURE_CONN)) {
+ fprintf(stderr, "Secure Connections support missing\n");
+ mainloop_quit();
+ return;
+ }
+
+ if (use_sconly && !(supported_settings & MGMT_SETTING_SECURE_CONN)) {
+ fprintf(stderr, "Secure Connections Only support missing\n");
+ mainloop_quit();
+ return;
+ }
+
+ if (use_debug && !(supported_settings & MGMT_SETTING_DEBUG_KEYS)) {
+ fprintf(stderr, "Debug keys support missing\n");
+ mainloop_quit();
+ return;
+ }
+
+ if (use_cross && (!(supported_settings & MGMT_SETTING_BREDR) ||
+ !(supported_settings & MGMT_SETTING_LE))) {
+ fprintf(stderr, "Dual-mode support is support missing\n");
+ mainloop_quit();
+ return;
+ }
+
+ mgmt_register(mgmt, MGMT_EV_PIN_CODE_REQUEST, index,
+ pin_code_request_event,
+ UINT_TO_PTR(index), NULL);
+
+ mgmt_register(mgmt, MGMT_EV_NEW_LINK_KEY, index,
+ new_link_key_event,
+ UINT_TO_PTR(index), NULL);
+
+ mgmt_register(mgmt, MGMT_EV_NEW_LONG_TERM_KEY, index,
+ new_long_term_key_event,
+ UINT_TO_PTR(index), NULL);
+
+ val = 0x00;
+ mgmt_send(mgmt, MGMT_OP_SET_POWERED, index, 1, &val,
+ NULL, NULL, NULL);
+
+ clear_link_keys(index);
+ clear_long_term_keys(index);
+ clear_remote_oob_data(index);
+
+ if (use_bredr) {
+ val = 0x01;
+ mgmt_send(mgmt, MGMT_OP_SET_BREDR, index, 1, &val,
+ NULL, NULL, NULL);
+
+ val = use_cross ? 0x01 : 0x00;
+ mgmt_send(mgmt, MGMT_OP_SET_LE, index, 1, &val,
+ NULL, NULL, NULL);
+
+ val = use_legacy ? 0x00 : 0x01;
+ mgmt_send(mgmt, MGMT_OP_SET_SSP, index, 1, &val,
+ NULL, NULL, NULL);
+ } else if (use_le) {
+ val = 0x01;
+ mgmt_send(mgmt, MGMT_OP_SET_LE, index, 1, &val,
+ NULL, NULL, NULL);
+
+ val = use_cross ? 0x01 : 0x00;
+ mgmt_send(mgmt, MGMT_OP_SET_BREDR, index, 1, &val,
+ NULL, NULL, NULL);
+ } else {
+ fprintf(stderr, "Invalid transport for pairing\n");
+ mainloop_quit();
+ return;
+ }
+
+ if (use_random) {
+ bdaddr_t bdaddr;
+
+ str2ba("c0:00:aa:bb:00:00", &bdaddr);
+ bdaddr.b[0] = index;
+
+ mgmt_send(mgmt, MGMT_OP_SET_STATIC_ADDRESS, index,
+ 6, &bdaddr, NULL, NULL, NULL);
+
+ if (index == index1)
+ bacpy(&bdaddr1, &bdaddr);
+ else if (index == index2)
+ bacpy(&bdaddr2, &bdaddr);
+ } else {
+ bdaddr_t bdaddr;
+
+ bacpy(&bdaddr, BDADDR_ANY);
+
+ mgmt_send(mgmt, MGMT_OP_SET_STATIC_ADDRESS, index,
+ 6, &bdaddr, NULL, NULL, NULL);
+ }
+
+ if (use_sc) {
+ val = 0x01;
+ mgmt_send(mgmt, MGMT_OP_SET_SECURE_CONN, index, 1, &val,
+ NULL, NULL, NULL);
+ } else if (use_sconly) {
+ val = 0x02;
+ mgmt_send(mgmt, MGMT_OP_SET_SECURE_CONN, index, 1, &val,
+ NULL, NULL, NULL);
+ } else {
+ val = 0x00;
+ mgmt_send(mgmt, MGMT_OP_SET_SECURE_CONN, index, 1, &val,
+ NULL, NULL, NULL);
+ }
+
+ val = 0x00;
+ mgmt_send(mgmt, MGMT_OP_SET_DEBUG_KEYS, index, 1, &val,
+ NULL, NULL, NULL);
+
+ val = 0x01;
+ mgmt_send(mgmt, MGMT_OP_SET_BONDABLE, index, 1, &val,
+ NULL, NULL, NULL);
+
+ val = 0x01;
+ mgmt_send(mgmt, MGMT_OP_SET_POWERED, index, 1, &val,
+ set_powered_complete,
+ UINT_TO_PTR(index), NULL);
+}
+
+static void read_index_list(uint8_t status, uint16_t len, const void *param,
+ void *user_data)
+{
+ const struct mgmt_rp_read_index_list *rp = param;
+ uint16_t count;
+ int i;
+
+ if (status) {
+ fprintf(stderr, "Reading index list failed: %s\n",
+ mgmt_errstr(status));
+ mainloop_quit();
+ return;
+ }
+
+ count = le16_to_cpu(rp->num_controllers);
+
+ if (count < 2) {
+ fprintf(stderr, "At least 2 controllers are required\n");
+ mainloop_quit();
+ return;
+ }
+
+ for (i = 0; i < count; i++) {
+ uint16_t index = cpu_to_le16(rp->index[i]);
+
+ if (index < index1)
+ index1 = index;
+ }
+
+ for (i = 0; i < count; i++) {
+ uint16_t index = cpu_to_le16(rp->index[i]);
+
+ if (index < index2 && index > index1)
+ index2 = index;
+ }
+
+ printf("Selecting index %u for initiator\n", index1);
+ printf("Selecting index %u for acceptor\n", index2);
+
+ mgmt_send(mgmt, MGMT_OP_READ_INFO, index1, 0, NULL,
+ read_info, UINT_TO_PTR(index1), NULL);
+ mgmt_send(mgmt, MGMT_OP_READ_INFO, index2, 0, NULL,
+ read_info, UINT_TO_PTR(index2), NULL);
+}
+
+static void signal_callback(int signum, void *user_data)
+{
+ switch (signum) {
+ case SIGINT:
+ case SIGTERM:
+ mainloop_quit();
+ break;
+ }
+}
+
+static void usage(void)
+{
+ printf("oobtest - Out-of-band pairing testing\n"
+ "Usage:\n");
+ printf("\toobtest [options]\n");
+ printf("options:\n"
+ "\t-B, --bredr Use BR/EDR transport\n"
+ "\t-L, --le Use LE transport\n"
+ "\t-S, --sc Use Secure Connections\n"
+ "\t-O, --sconly Use Secure Connections Only\n"
+ "\t-P, --legacy Use Legacy Pairing\n"
+ "\t-R, --random Use Static random address\n"
+ "\t-D, --debug Use Pairing debug keys\n"
+ "\t-C, --cross Use cross-transport pairing\n"
+ "\t-1, --p192 Provide P-192 OOB data\n"
+ "\t-2, --p256 Provide P-256 OOB data\n"
+ "\t-h, --help Show help options\n");
+}
+
+static const struct option main_options[] = {
+ { "bredr", no_argument, NULL, 'B' },
+ { "le", no_argument, NULL, 'L' },
+ { "sc", no_argument, NULL, 'S' },
+ { "sconly", no_argument, NULL, 'O' },
+ { "legacy", no_argument, NULL, 'P' },
+ { "random", no_argument, NULL, 'R' },
+ { "static", no_argument, NULL, 'R' },
+ { "debug", no_argument, NULL, 'D' },
+ { "cross", no_argument, NULL, 'C' },
+ { "dual", no_argument, NULL, 'C' },
+ { "p192", no_argument, NULL, '1' },
+ { "p256", no_argument, NULL, '2' },
+ { "version", no_argument, NULL, 'v' },
+ { "help", no_argument, NULL, 'h' },
+ { }
+};
+
+int main(int argc ,char *argv[])
+{
+ sigset_t mask;
+ int exit_status;
+
+ for (;;) {
+ int opt;
+
+ opt = getopt_long(argc, argv, "BLSOPRDC12vh",
+ main_options, NULL);
+ if (opt < 0)
+ break;
+
+ switch (opt) {
+ case 'B':
+ use_bredr = true;
+ break;
+ case 'L':
+ use_le = true;
+ break;
+ case 'S':
+ use_sc = true;
+ break;
+ case 'O':
+ use_sconly = true;
+ break;
+ case 'P':
+ use_legacy = true;
+ break;
+ case 'R':
+ use_random = true;
+ break;
+ case 'D':
+ use_debug = true;
+ break;
+ case 'C':
+ use_cross = true;
+ break;
+ case '1':
+ provide_p192 = true;
+ break;
+ case '2':
+ provide_p256 = true;
+ break;
+ case 'v':
+ printf("%s\n", VERSION);
+ return EXIT_SUCCESS;
+ case 'h':
+ usage();
+ return EXIT_SUCCESS;
+ default:
+ return EXIT_FAILURE;
+ }
+ }
+
+ if (argc - optind > 0) {
+ fprintf(stderr, "Invalid command line parameters\n");
+ return EXIT_FAILURE;
+ }
+
+ if (use_bredr == use_le) {
+ fprintf(stderr, "Specify either --bredr or --le\n");
+ return EXIT_FAILURE;
+ }
+
+ if (use_legacy && !use_bredr) {
+ fprintf(stderr, "Specify --legacy with --bredr\n");
+ return EXIT_FAILURE;
+ }
+
+ if (use_random && !use_le) {
+ fprintf(stderr, "Specify --random with --le\n");
+ return EXIT_FAILURE;
+ }
+
+ if (use_random && use_cross) {
+ fprintf(stderr, "Only --random or --cross can be used\n");
+ return EXIT_FAILURE;
+ }
+
+ if (use_sc && use_sconly) {
+ fprintf(stderr, "Only --sc or --sconly can be used\n");
+ return EXIT_FAILURE;
+ }
+
+ mainloop_init();
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGINT);
+ sigaddset(&mask, SIGTERM);
+
+ mainloop_set_signal(&mask, signal_callback, NULL, NULL);
+
+ mgmt = mgmt_new_default();
+ if (!mgmt) {
+ fprintf(stderr, "Failed to open management socket\n");
+ return EXIT_FAILURE;
+ }
+
+ if (!mgmt_send(mgmt, MGMT_OP_READ_INDEX_LIST,
+ MGMT_INDEX_NONE, 0, NULL,
+ read_index_list, NULL, NULL)) {
+ fprintf(stderr, "Failed to read index list\n");
+ exit_status = EXIT_FAILURE;
+ goto done;
+ }
+
+ exit_status = mainloop_run();
+
+done:
+ mgmt_unref(mgmt);
+
+ return exit_status;
+}
"AMP Status Change",
};
-#define LE_EV_NUM 5
+#define LE_EV_NUM 11
static char *ev_le_meta_str[LE_EV_NUM + 1] = {
"Unknown",
"LE Connection Complete",
"LE Connection Update Complete",
"LE Read Remote Used Features Complete",
"LE Long Term Key Request",
+ "LE Remote Connection Parameter Request",
+ "LE Data Length Change",
+ "LE Read Local P-256 Public Key Complete",
+ "LE Generate DHKey Complete",
+ "LE Enhanced Connection Complete",
+ "LE Direct Advertising Report",
};
#define CMD_LINKCTL_NUM 60
"SCO Offset Rejected",
"SCO Interval Rejected",
"SCO Air Mode Rejected",
- "Invalid LMP Parameters",
+ "Invalid LMP Parameters / Invalid LL Parameters",
"Unspecified Error",
- "Unsupported LMP Parameter Value",
+ "Unsupported LMP Parameter Value / Unsupported LL Parameter Value",
"Role Change Not Allowed",
"LMP Response Timeout",
"LMP Error Transaction Collision",
"Host Busy - Pairing",
"Connection Rejected due to No Suitable Channel Found",
"Controller Busy",
- "Unacceptable Connection Interval",
+ "Unacceptable Connection Parameters",
"Directed Advertising Timeout",
"Connection Terminated Due to MIC Failure",
"Connection Failed to be Established",
frm->len -= EVT_LE_META_EVENT_SIZE;
p_indent(level, frm);
- printf("%s\n", ev_le_meta_str[subevent]);
+
+ if (subevent <= LE_EV_NUM)
+ printf("%s\n", ev_le_meta_str[subevent]);
+ else
+ printf("%s\n", ev_le_meta_str[0]);
switch (mevt->subevent) {
case EVT_LE_CONN_COMPLETE:
#include <sys/socket.h>
#include <sys/stat.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/hci_lib.h>
-#include <bluetooth/rfcomm.h>
-#include <bluetooth/sdp.h>
-#include <bluetooth/sdp_lib.h>
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+#include "lib/rfcomm.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
#include "src/shared/util.h"
gpointer user_data)
{
struct test_data *data = tester_get_data();
- const struct rfcomm_client_data *client_data = data->test_data;
+ const struct rfcomm_client_data *cli = data->test_data;
int sk;
ssize_t ret;
char buf[248];
sk = g_io_channel_unix_get_fd(io);
- ret = read(sk, buf, client_data->data_len);
- if (client_data->data_len != ret) {
+ ret = read(sk, buf, cli->data_len);
+ if (cli->data_len != ret) {
tester_test_failed();
return false;
}
- if (memcmp(client_data->read_data, buf, client_data->data_len))
+ if (memcmp(cli->read_data, buf, cli->data_len))
tester_test_failed();
else
tester_test_passed();
}
static gboolean rc_connect_cb(GIOChannel *io, GIOCondition cond,
- gpointer user_data)
+ gpointer user_data)
{
struct test_data *data = tester_get_data();
- const struct rfcomm_client_data *client_data = data->test_data;
+ const struct rfcomm_client_data *cli = data->test_data;
socklen_t len = sizeof(int);
int sk, err, sk_err;
+ tester_print("Connected");
+
data->io_id = 0;
sk = g_io_channel_unix_get_fd(io);
else
err = -sk_err;
- if (client_data->expected_connect_err &&
- err == client_data->expected_connect_err) {
+ if (cli->expected_connect_err && err == cli->expected_connect_err) {
tester_test_passed();
return false;
}
- if (client_data->send_data) {
+ if (cli->send_data) {
ssize_t ret;
- ret = write(sk, client_data->send_data, client_data->data_len);
- if (client_data->data_len != ret)
+ tester_print("Writing %u bytes of data", cli->data_len);
+
+ ret = write(sk, cli->send_data, cli->data_len);
+ if (cli->data_len != ret) {
+ tester_warn("Failed to write %u bytes: %s (%d)",
+ cli->data_len, strerror(errno), errno);
tester_test_failed();
+ }
return false;
- } else if (client_data->read_data) {
+ } else if (cli->read_data) {
g_io_add_watch(io, G_IO_IN, client_received_data, NULL);
bthost_send_rfcomm_data(hciemu_client_get_host(data->hciemu),
data->conn_handle,
- client_data->client_channel,
- client_data->read_data,
- client_data->data_len);
+ cli->client_channel,
+ cli->read_data, cli->data_len);
return false;
}
void *user_data)
{
struct test_data *test_data = tester_get_data();
- const struct rfcomm_client_data *client_data = test_data->test_data;
+ const struct rfcomm_client_data *cli = test_data->test_data;
ssize_t ret;
- if (client_data->data_len != len) {
+ tester_print("bthost received %u bytes of data", len);
+
+ if (cli->data_len != len) {
tester_test_failed();
return;
}
- ret = memcmp(client_data->send_data, data, len);
+ ret = memcmp(cli->send_data, data, len);
if (ret)
tester_test_failed();
else
void *user_data)
{
struct test_data *test_data = tester_get_data();
- const struct rfcomm_server_data *server_data = test_data->test_data;
+ const struct rfcomm_server_data *srv = test_data->test_data;
ssize_t ret;
- if (server_data->data_len != len) {
+ if (srv->data_len != len) {
tester_test_failed();
return;
}
- ret = memcmp(server_data->send_data, data, len);
+ ret = memcmp(srv->send_data, data, len);
if (ret)
tester_test_failed();
else
void *user_data, bool status)
{
struct test_data *data = tester_get_data();
- const struct rfcomm_client_data *client_data = data->test_data;
+ const struct rfcomm_client_data *cli = data->test_data;
struct bthost *bthost = hciemu_client_get_host(data->hciemu);
- if (client_data->send_data)
+ if (cli->send_data)
bthost_add_rfcomm_chan_hook(bthost, handle,
- client_data->client_channel,
+ cli->client_channel,
client_hook_func, NULL);
- else if (client_data->read_data)
+ else if (cli->read_data)
data->conn_handle = handle;
}
{
struct test_data *data = tester_get_data();
struct bthost *bthost = hciemu_client_get_host(data->hciemu);
- const struct rfcomm_client_data *client_data = data->test_data;
+ const struct rfcomm_client_data *cli = data->test_data;
const uint8_t *client_addr, *master_addr;
GIOChannel *io;
int sk;
bthost_add_l2cap_server(bthost, 0x0003, NULL, NULL);
- bthost_add_rfcomm_server(bthost, client_data->server_channel,
+ bthost_add_rfcomm_server(bthost, cli->server_channel,
rfcomm_connect_cb, NULL);
master_addr = hciemu_get_master_bdaddr(data->hciemu);
sk = create_rfcomm_sock((bdaddr_t *) master_addr, 0);
if (connect_rfcomm_sock(sk, (const bdaddr_t *) client_addr,
- client_data->client_channel) < 0) {
+ cli->client_channel) < 0) {
close(sk);
tester_test_failed();
return;
gpointer user_data)
{
struct test_data *data = tester_get_data();
- const struct rfcomm_server_data *server_data = data->test_data;
+ const struct rfcomm_server_data *srv = data->test_data;
char buf[1024];
ssize_t ret;
int sk;
sk = g_io_channel_unix_get_fd(io);
- ret = read(sk, buf, server_data->data_len);
- if (ret != server_data->data_len) {
+ ret = read(sk, buf, srv->data_len);
+ if (ret != srv->data_len) {
tester_test_failed();
return false;
}
- if (memcmp(buf, server_data->read_data, server_data->data_len))
+ if (memcmp(buf, srv->read_data, srv->data_len))
tester_test_failed();
else
tester_test_passed();
gpointer user_data)
{
struct test_data *data = tester_get_data();
- const struct rfcomm_server_data *server_data = data->test_data;
+ const struct rfcomm_server_data *srv = data->test_data;
int sk, new_sk;
data->io_id = 0;
return false;
}
- if (server_data->send_data) {
+ if (srv->send_data) {
ssize_t ret;
- ret = write(new_sk, server_data->send_data,
- server_data->data_len);
- if (ret != server_data->data_len)
+ ret = write(new_sk, srv->send_data, srv->data_len);
+ if (ret != srv->data_len)
tester_test_failed();
close(new_sk);
return false;
- } else if (server_data->read_data) {
+ } else if (srv->read_data) {
GIOChannel *new_io;
new_io = g_io_channel_unix_new(new_sk);
bool status)
{
struct test_data *data = tester_get_data();
- const struct rfcomm_server_data *server_data = data->test_data;
+ const struct rfcomm_server_data *srv = data->test_data;
struct bthost *bthost = hciemu_client_get_host(data->hciemu);
- if (server_data->read_data) {
+ if (srv->read_data) {
data->conn_handle = handle;
bthost_send_rfcomm_data(bthost, data->conn_handle,
- server_data->client_channel,
- server_data->read_data,
- server_data->data_len);
+ srv->client_channel,
+ srv->read_data, srv->data_len);
return;
- } else if (server_data->data_len) {
+ } else if (srv->data_len) {
return;
}
- if (server_data->expected_status == status)
+ if (srv->expected_status == status)
tester_test_passed();
else
tester_test_failed();
static void client_new_conn(uint16_t handle, void *user_data)
{
struct test_data *data = tester_get_data();
- const struct rfcomm_server_data *server_data = data->test_data;
+ const struct rfcomm_server_data *srv = data->test_data;
struct bthost *bthost;
bthost = hciemu_client_get_host(data->hciemu);
- bthost_add_rfcomm_chan_hook(bthost, handle,
- server_data->client_channel,
+ bthost_add_rfcomm_chan_hook(bthost, handle, srv->client_channel,
server_hook_func, NULL);
- bthost_connect_rfcomm(bthost, handle, server_data->client_channel,
+ bthost_connect_rfcomm(bthost, handle, srv->client_channel,
connection_cb, NULL);
}
static void test_server(const void *test_data)
{
struct test_data *data = tester_get_data();
- const struct rfcomm_server_data *server_data = data->test_data;
+ const struct rfcomm_server_data *srv = data->test_data;
const uint8_t *master_addr;
struct bthost *bthost;
GIOChannel *io;
master_addr = hciemu_get_master_bdaddr(data->hciemu);
- sk = create_rfcomm_sock((bdaddr_t *) master_addr,
- server_data->server_channel);
+ sk = create_rfcomm_sock((bdaddr_t *) master_addr, srv->server_channel);
if (sk < 0) {
tester_test_failed();
return;
#include <sys/socket.h>
#include <sys/wait.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/hci_lib.h>
-#include <bluetooth/rfcomm.h>
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+#include "lib/rfcomm.h"
static int rfcomm_raw_tty = 0;
static int auth = 0;
#include <sys/time.h>
#include <sys/socket.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/sco.h>
+#include "lib/bluetooth.h"
+#include "lib/sco.h"
#include "src/shared/util.h"
#include <string.h>
#include <getopt.h>
#include <sys/socket.h>
-
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/hci_lib.h>
-#include <bluetooth/sdp.h>
-#include <bluetooth/sdp_lib.h>
-
#include <netinet/in.h>
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+#include "lib/sdp.h"
+#include "lib/sdp_lib.h"
+
#include "src/sdp-xml.h"
#ifndef APPLE_AGENT_SVCLASS_ID
#include <glib.h>
#include "src/shared/util.h"
+#include "src/shared/queue.h"
#include "src/log.h"
#include "android/avdtp.h"
+#define MAX_SEID 0x3E
+
struct test_pdu {
bool valid;
bool fragmented;
struct avdtp *session;
struct avdtp_local_sep *sep;
struct avdtp_stream *stream;
+ struct queue *lseps;
guint source;
guint process;
int fd;
err = socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sv);
g_assert(err == 0);
- context->session = avdtp_new(sv[0], imtu, omtu, version);
+ context->lseps = queue_new();
+ g_assert(context->lseps);
+
+ context->session = avdtp_new(sv[0], imtu, omtu, version,
+ context->lseps);
g_assert(context->session != NULL);
channel = g_io_channel_unix_new(sv[1]);
return context_new(version, 672, 672, data);
}
-static void execute_context(struct context *context)
+static void unregister_sep(void *data)
{
- g_main_loop_run(context->main_loop);
+ struct avdtp_local_sep *sep = data;
+ /* Removed from the queue by caller */
+ avdtp_unregister_sep(NULL, sep);
+}
+
+static void destroy_context(struct context *context)
+{
if (context->source > 0)
g_source_remove(context->source);
avdtp_unref(context->session);
g_main_loop_unref(context->main_loop);
test_free(context->data);
+ queue_destroy(context->lseps, unregister_sep);
+
g_free(context);
}
+static void execute_context(struct context *context)
+{
+ g_main_loop_run(context->main_loop);
+
+ destroy_context(context);
+}
+
static gboolean sep_getcap_ind(struct avdtp *session,
struct avdtp_local_sep *sep,
GSList **caps, uint8_t *err,
struct context *context = create_context(0x0100, data);
struct avdtp_local_sep *sep;
- sep = avdtp_register_sep(AVDTP_SEP_TYPE_SOURCE, AVDTP_MEDIA_TYPE_AUDIO,
+ sep = avdtp_register_sep(context->lseps, AVDTP_SEP_TYPE_SOURCE,
+ AVDTP_MEDIA_TYPE_AUDIO,
0x00, FALSE, &sep_ind, &sep_cfm,
context);
+ g_assert(sep);
g_idle_add(send_pdu, context);
execute_context(context);
-
- avdtp_unregister_sep(sep);
}
static void test_server_1_3(gconstpointer data)
struct context *context = create_context(0x0103, data);
struct avdtp_local_sep *sep;
- sep = avdtp_register_sep(AVDTP_SEP_TYPE_SOURCE, AVDTP_MEDIA_TYPE_AUDIO,
+ sep = avdtp_register_sep(context->lseps, AVDTP_SEP_TYPE_SOURCE,
+ AVDTP_MEDIA_TYPE_AUDIO,
0x00, TRUE, &sep_ind, NULL, context);
+ g_assert(sep);
g_idle_add(send_pdu, context);
execute_context(context);
-
- avdtp_unregister_sep(sep);
}
static void test_server_1_3_sink(gconstpointer data)
struct context *context = create_context(0x0103, data);
struct avdtp_local_sep *sep;
- sep = avdtp_register_sep(AVDTP_SEP_TYPE_SINK, AVDTP_MEDIA_TYPE_AUDIO,
+ sep = avdtp_register_sep(context->lseps, AVDTP_SEP_TYPE_SINK,
+ AVDTP_MEDIA_TYPE_AUDIO,
0x00, TRUE, &sep_ind, NULL, context);
+ g_assert(sep);
g_idle_add(send_pdu, context);
execute_context(context);
-
- avdtp_unregister_sep(sep);
}
static void test_server_0_sep(gconstpointer data)
execute_context(context);
}
+static void test_server_seid(gconstpointer data)
+{
+ struct context *context = create_context(0x0103, data);
+ struct avdtp_local_sep *sep;
+ unsigned int i;
+
+ for (i = 0; i < sizeof(int) * 8; i++) {
+ sep = avdtp_register_sep(context->lseps, AVDTP_SEP_TYPE_SINK,
+ AVDTP_MEDIA_TYPE_AUDIO,
+ 0x00, TRUE, &sep_ind, NULL,
+ context);
+ g_assert(sep);
+ }
+
+ /* Now add (MAX_SEID + 1) SEP -> it shall fail */
+ sep = avdtp_register_sep(context->lseps, AVDTP_SEP_TYPE_SINK,
+ AVDTP_MEDIA_TYPE_AUDIO,
+ 0x00, TRUE, &sep_ind, NULL,
+ context);
+ g_assert(!sep);
+
+ destroy_context(context);
+}
+
+static void test_server_seid_duplicate(gconstpointer data)
+{
+ struct context *context = create_context(0x0103, data);
+ struct avdtp_local_sep *sep;
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ sep = avdtp_register_sep(context->lseps, AVDTP_SEP_TYPE_SINK,
+ AVDTP_MEDIA_TYPE_AUDIO,
+ 0x00, TRUE, &sep_ind, NULL,
+ context);
+ g_assert(sep);
+ }
+
+ /* Remove 1st element */
+ sep = queue_peek_head(context->lseps);
+ g_assert(sep);
+
+ avdtp_unregister_sep(context->lseps, sep);
+
+ /* Now register new element */
+ sep = avdtp_register_sep(context->lseps, AVDTP_SEP_TYPE_SINK,
+ AVDTP_MEDIA_TYPE_AUDIO,
+ 0x00, TRUE, &sep_ind, NULL,
+ context);
+ g_assert(sep);
+
+ /* Check SEID ids with DISCOVER */
+
+ g_idle_add(send_pdu, context);
+
+ execute_context(context);
+}
+
static gboolean sep_getcap_ind_frg(struct avdtp *session,
struct avdtp_local_sep *sep,
GSList **caps, uint8_t *err,
struct context *context = context_new(0x0100, 48, 48, data);
struct avdtp_local_sep *sep;
- sep = avdtp_register_sep(AVDTP_SEP_TYPE_SOURCE, AVDTP_MEDIA_TYPE_AUDIO,
+ sep = avdtp_register_sep(context->lseps, AVDTP_SEP_TYPE_SOURCE,
+ AVDTP_MEDIA_TYPE_AUDIO,
0x00, TRUE, &sep_ind_frg,
NULL, context);
+ g_assert(sep);
g_idle_add(send_pdu, context);
execute_context(context);
-
- avdtp_unregister_sep(sep);
}
static void discover_cb(struct avdtp *session, GSList *seps,
struct context *context = create_context(0x0100, data);
struct avdtp_local_sep *sep;
- sep = avdtp_register_sep(AVDTP_SEP_TYPE_SINK, AVDTP_MEDIA_TYPE_AUDIO,
- 0x00, FALSE, NULL, &sep_cfm,
- context);
+ sep = avdtp_register_sep(context->lseps, AVDTP_SEP_TYPE_SINK,
+ AVDTP_MEDIA_TYPE_AUDIO,
+ 0x00, FALSE, NULL, &sep_cfm, context);
+
context->sep = sep;
avdtp_discover(context->session, discover_cb, context);
execute_context(context);
-
- avdtp_unregister_sep(sep);
}
static void test_client_1_3(gconstpointer data)
struct context *context = create_context(0x0103, data);
struct avdtp_local_sep *sep;
- sep = avdtp_register_sep(AVDTP_SEP_TYPE_SINK, AVDTP_MEDIA_TYPE_AUDIO,
- 0x00, TRUE, NULL, &sep_cfm,
- context);
+ sep = avdtp_register_sep(context->lseps, AVDTP_SEP_TYPE_SINK,
+ AVDTP_MEDIA_TYPE_AUDIO,
+ 0x00, TRUE, NULL, &sep_cfm, context);
+
context->sep = sep;
avdtp_discover(context->session, discover_cb, context);
execute_context(context);
-
- avdtp_unregister_sep(sep);
}
static void test_client_frg(gconstpointer data)
struct context *context = context_new(0x0100, 48, 48, data);
struct avdtp_local_sep *sep;
- sep = avdtp_register_sep(AVDTP_SEP_TYPE_SINK, AVDTP_MEDIA_TYPE_AUDIO,
- 0x00, TRUE, NULL, &sep_cfm,
- context);
+ sep = avdtp_register_sep(context->lseps, AVDTP_SEP_TYPE_SINK,
+ AVDTP_MEDIA_TYPE_AUDIO,
+ 0x00, TRUE, NULL, &sep_cfm, context);
+
context->sep = sep;
avdtp_discover(context->session, discover_cb, context);
execute_context(context);
-
- avdtp_unregister_sep(sep);
}
int main(int argc, char *argv[])
* To verify that the following procedures are implemented according to
* their specification in AVDTP.
*/
+ define_test("/TP/SIG/SMG/BV-06-C-SEID-1", test_server_seid,
+ raw_pdu(0x00));
+ define_test("/TP/SIG/SMG/BV-06-C-SEID-2", test_server_seid_duplicate,
+ raw_pdu(0x00, 0x01),
+ raw_pdu(0x02, 0x01, 0x08, 0x08, 0x04, 0x08));
define_test("/TP/SIG/SMG/BV-05-C", test_client,
raw_pdu(0x00, 0x01));
define_test("/TP/SIG/SMG/BV-06-C", test_server,
#include <glib.h>
-#include <bluetooth/bluetooth.h>
-#include <bluetooth/hci.h>
-#include <bluetooth/sdp.h>
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/sdp.h"
#include "src/eir.h"
#include <glib.h>
+#include "lib/bluetooth.h"
#include "lib/uuid.h"
#include "src/shared/util.h"
#include "src/shared/att.h"
#include "src/shared/gatt-db.h"
#include "src/shared/gatt-server.h"
#include "src/shared/gatt-client.h"
+#include "src/shared/tester.h"
struct test_pdu {
bool valid;
};
struct context {
- GMainLoop *main_loop;
struct bt_gatt_client *client;
struct bt_gatt_server *server;
struct bt_att *att;
int fd;
unsigned int pdu_offset;
const struct test_data *data;
+ struct bt_gatt_request *req;
};
#define data(args...) ((const unsigned char[]) { args })
data.source_db = db; \
data.pdu_list = g_malloc(sizeof(pdus)); \
memcpy(data.pdu_list, pdus, sizeof(pdus)); \
- g_test_add_data_func(name, &data, function); \
+ tester_add(name, &data, NULL, function, NULL); \
} while (0)
#define define_test_att(name, function, bt_uuid, test_step, args...) \
raw_pdu(0x04, 0x04, 0x00, 0x04, 0x00), \
raw_pdu(0x05, 0x01, 0x04, 0x00, 0x01, 0x29), \
raw_pdu(0x08, 0x05, 0x00, 0x08, 0x00, 0x03, 0x28), \
- raw_pdu(0x09, 0x07, 0x06, 0x00, 0x02, 0x07, 0x00, 0x29, \
+ raw_pdu(0x09, 0x07, 0x06, 0x00, 0x0a, 0x07, 0x00, 0x29, \
0x2a), \
raw_pdu(0x08, 0x07, 0x00, 0x08, 0x00, 0x03, 0x28), \
raw_pdu(0x01, 0x08, 0x07, 0x00, 0x0a), \
#define PRIMARY_DISC_SMALL_DB \
raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28), \
- raw_pdu(0x11, 0x06, 0x10, 0xF0, 0x15, 0xF0, 0x00, 0x18, \
+ raw_pdu(0x11, 0x06, 0x10, 0xF0, 0x17, 0xF0, 0x00, 0x18, \
0xFF, 0xFF, 0xFF, 0xFF, 0x0a, 0x18)
+#define PRIMARY_DISC_LARGE_DB_1 \
+ raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28), \
+ raw_pdu(0x11, 0x06, 0x10, 0x00, 0x13, 0x00, 0x01, 0x18, \
+ 0x20, 0x00, 0x29, 0x00, 0x0A, 0xA0, \
+ 0x30, 0x00, 0x32, 0x00, 0x0B, 0xA0), \
+ raw_pdu(0x10, 0x33, 0x00, 0xff, 0xff, 0x00, 0x28), \
+ raw_pdu(0x11, 0x06, 0x40, 0x00, 0x46, 0x00, 0x00, 0x18, \
+ 0x50, 0x00, 0x52, 0x00, 0x0B, 0xA0, \
+ 0x60, 0x00, 0x6B, 0x00, 0x0B, 0xA0), \
+ raw_pdu(0x10, 0x6C, 0x00, 0xff, 0xff, 0x00, 0x28), \
+ raw_pdu(0x11, 0x06, 0x70, 0x00, 0x76, 0x00, 0x0B, 0xA0, \
+ 0x80, 0x00, 0x85, 0x00, 0x0B, 0xA0), \
+ raw_pdu(0x10, 0x86, 0x00, 0xff, 0xff, 0x00, 0x28), \
+ raw_pdu(0x11, 0x14, 0x90, 0x00, 0x96, 0x00, \
+ 0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01, \
+ 0x00, 0x00, 0x00, 0x00, 0x0C, 0xA0, 0x00, 0x00),\
+ raw_pdu(0x10, 0x97, 0x00, 0xff, 0xff, 0x00, 0x28), \
+ raw_pdu(0x11, 0x06, 0xa0, 0x00, 0xb1, 0x00, 0x0f, 0xa0),\
+ raw_pdu(0x10, 0xb2, 0x00, 0xff, 0xff, 0x00, 0x28), \
+ raw_pdu(0x11, 0x14, 0xC0, 0x00, 0xDD, 0x00, \
+ 0xef, 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01, \
+ 0x00, 0x00, 0x00, 0x00, 0x0C, 0xA0, 0x00, 0x00),\
+ raw_pdu(0x10, 0xde, 0x00, 0xff, 0xff, 0x00, 0x28), \
+ raw_pdu(0x01, 0x10, 0xde, 0x00, 0x0a)
+
#define SECONDARY_DISC_SMALL_DB \
raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x01, 0x28), \
- raw_pdu(0x11, 0x06, 0x01, 0x00, 0x0F, 0x00, 0x0a, 0x18),\
- raw_pdu(0x10, 0x10, 0x00, 0xff, 0xff, 0x01, 0x28), \
- raw_pdu(0x01, 0x10, 0x10, 0x00, 0x0a)
+ raw_pdu(0x11, 0x06, 0x01, 0x00, 0x10, 0x00, 0x0a, 0x18),\
+ raw_pdu(0x10, 0x11, 0x00, 0xff, 0xff, 0x01, 0x28), \
+ raw_pdu(0x01, 0x10, 0x11, 0x00, 0x0a)
+
+#define INCLUDE_DISC_SMALL_DB \
+ raw_pdu(0x08, 0x10, 0xf0, 0x17, 0xf0, 0x02, 0x28), \
+ raw_pdu(0x09, 0x08, 0x11, 0xf0, 0x01, 0x00, 0x0f, 0x00, \
+ 0x0a, 0x18), \
+ raw_pdu(0x08, 0x12, 0xf0, 0x17, 0xf0, 0x02, 0x28), \
+ raw_pdu(0x01, 0x08, 0x12, 0xf0, 0x0a), \
+ raw_pdu(0x08, 0x01, 0x00, 0x10, 0x00, 0x02, 0x28), \
+ raw_pdu(0x01, 0x08, 0x01, 0x00, 0x0a)
+
+#define CHARACTERISTIC_DISC_SMALL_DB \
+ raw_pdu(0x08, 0x10, 0xf0, 0x17, 0xf0, 0x03, 0x28), \
+ raw_pdu(0x09, 0x07, 0x12, 0xf0, 0x02, 0x13, 0xf0, 0x00, \
+ 0x2a), \
+ raw_pdu(0x08, 0x13, 0xf0, 0x17, 0xf0, 0x03, 0x28), \
+ raw_pdu(0x09, 0x15, 0x14, 0xf0, 0x02, 0x15, 0xf0, 0xef, \
+ 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01, 0x00, \
+ 0x00, 0x00, 0x00, 0x09, 0xB0, 0x00, 0x00), \
+ raw_pdu(0x08, 0x15, 0xf0, 0x17, 0xf0, 0x03, 0x28), \
+ raw_pdu(0x09, 0x07, 0x16, 0xf0, 0x02, 0x17, 0xf0, 0x01, \
+ 0x2a), \
+ raw_pdu(0x08, 0x17, 0xf0, 0x17, 0xf0, 0x03, 0x28), \
+ raw_pdu(0x01, 0x08, 0x17, 0xf0, 0x0a), \
+ raw_pdu(0x08, 0x01, 0x00, 0x10, 0x00, 0x03, 0x28), \
+ raw_pdu(0x09, 0x07, 0x02, 0x00, 0x32, 0x03, 0x00, 0x29, \
+ 0x2a), \
+ raw_pdu(0x08, 0x03, 0x00, 0x10, 0x00, 0x03, 0x28), \
+ raw_pdu(0x01, 0x08, 0x03, 0x00, 0x0a)
+
+#define DESCRIPTOR_DISC_SMALL_DB \
+ raw_pdu(0x04, 0x04, 0x00, 0x10, 0x00), \
+ raw_pdu(0x05, 0x01, 0x04, 0x00, 0x02, 0x29, 0x05, 0x00, \
+ 0x01, 0x29), \
+ raw_pdu(0x04, 0x06, 0x00, 0x10, 0x00), \
+ raw_pdu(0x01, 0x04, 0x06, 0x00, 0x0a)
+
+#define SMALL_DB_DISCOVERY_PDUS \
+ PRIMARY_DISC_SMALL_DB, \
+ SECONDARY_DISC_SMALL_DB, \
+ INCLUDE_DISC_SMALL_DB, \
+ CHARACTERISTIC_DISC_SMALL_DB, \
+ DESCRIPTOR_DISC_SMALL_DB
+
#define SERVER_MTU_EXCHANGE_PDU raw_pdu(0x02, 0x17, 0x00)
0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f}
};
-static void test_debug(const char *str, void *user_data)
-{
- const char *prefix = user_data;
-
- g_print("%s%s\n", prefix, str);
-}
-
static void test_free(gconstpointer user_data)
{
const struct test_data *data = user_data;
g_free(data->pdu_list);
}
+typedef void (*test_step_t)(struct context *context);
+
+struct test_step {
+ test_step_t func;
+ test_step_t post_func;
+ uint16_t handle;
+ uint16_t end_handle;
+ uint8_t uuid[16];
+ uint8_t expected_att_ecode;
+ const uint8_t *value;
+ uint16_t length;
+};
+
+static void destroy_context(struct context *context)
+{
+ if (context->source > 0)
+ g_source_remove(context->source);
+
+ if (context->req)
+ bt_gatt_request_unref(context->req);
+
+ bt_gatt_client_unref(context->client);
+ bt_gatt_server_unref(context->server);
+ gatt_db_unref(context->client_db);
+ gatt_db_unref(context->server_db);
+
+ if (context->att)
+ bt_att_unref(context->att);
+
+ test_free(context->data);
+ g_free(context);
+}
+
static gboolean context_quit(gpointer user_data)
{
struct context *context = user_data;
+ const struct test_step *step = context->data->step;
if (context->process > 0)
g_source_remove(context->process);
- g_main_loop_quit(context->main_loop);
+ if (step && step->post_func)
+ step->post_func(context);
+
+ destroy_context(context);
+
+ tester_test_passed();
return FALSE;
}
+static void test_debug(const char *str, void *user_data)
+{
+ const char *prefix = user_data;
+
+ tester_debug("%s%s", prefix, str);
+}
+
static gboolean send_pdu(gpointer user_data)
{
struct context *context = user_data;
len = write(context->fd, pdu->data, pdu->size);
- if (g_test_verbose())
- util_hexdump('<', pdu->data, len, test_debug, "GATT: ");
+ util_hexdump('<', pdu->data, len, test_debug, "GATT: ");
g_assert_cmpint(len, ==, pdu->size);
context->process = 0;
+
+ pdu = &context->data->pdu_list[context->pdu_offset];
+ if (pdu->valid && (pdu->size == 0)) {
+ test_debug("(no action expected)", "GATT: ");
+ context->pdu_offset++;
+ return send_pdu(context);
+ }
+
return FALSE;
}
gpointer user_data)
{
struct context *context = user_data;
+ const struct test_step *step = context->data->step;
const struct test_pdu *pdu;
unsigned char buf[512];
ssize_t len;
g_assert(len > 0);
- if (g_test_verbose())
- util_hexdump('>', buf, len, test_debug, "GATT: ");
+ util_hexdump('>', buf, len, test_debug, "GATT: ");
g_assert_cmpint(len, ==, pdu->size);
g_assert(memcmp(buf, pdu->data, pdu->size) == 0);
+ /* Empty client PDU means to trigger something out-of-band. */
+ pdu = &context->data->pdu_list[context->pdu_offset];
+
+ if (pdu->valid && (pdu->size == 0)) {
+ context->pdu_offset++;
+ test_debug("triggering server action", "Empty client pdu: ");
+ g_assert(step && step->func);
+ step->func(context);
+ return TRUE;
+ }
+
context_process(context);
return TRUE;
{
const char *prefix = user_data;
- g_print("%s%s\n", prefix, str);
+ tester_debug("%s%s", prefix, str);
}
-typedef void (*test_step_t)(struct context *context);
-
-struct test_step {
- test_step_t func;
- uint16_t handle;
- uint16_t end_handle;
- uint8_t uuid[16];
- uint8_t expected_att_ecode;
- const uint8_t *value;
- uint16_t length;
-};
-
struct db_attribute_test_data {
struct gatt_db_attribute *match;
bool found;
const struct test_data *test_data = data;
GIOChannel *channel;
int err, sv[2];
- struct bt_att *att;
-
- context->main_loop = g_main_loop_new(NULL, FALSE);
- g_assert(context->main_loop);
err = socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sv);
g_assert(err == 0);
- att = bt_att_new(sv[0]);
- g_assert(att);
+ context->att = bt_att_new(sv[0]);
+ g_assert(context->att);
switch (test_data->context_type) {
case ATT:
- context->att = att;
-
- if (g_test_verbose())
- bt_att_set_debug(context->att, print_debug, "bt_att:",
- NULL);
+ bt_att_set_debug(context->att, print_debug, "bt_att:", NULL);
bt_gatt_exchange_mtu(context->att, mtu, NULL, NULL, NULL);
break;
context->server_db = gatt_db_ref(test_data->source_db);
g_assert(context->server_db);
- context->server = bt_gatt_server_new(context->server_db, att,
- mtu);
+ context->server = bt_gatt_server_new(context->server_db,
+ context->att, mtu);
g_assert(context->server);
- if (g_test_verbose())
- bt_gatt_server_set_debug(context->server, print_debug,
+ bt_gatt_server_set_debug(context->server, print_debug,
"bt_gatt_server:", NULL);
- bt_att_unref(att);
break;
case CLIENT:
context->client_db = gatt_db_new();
g_assert(context->client_db);
- context->client = bt_gatt_client_new(context->client_db, att,
- mtu);
+ context->client = bt_gatt_client_new(context->client_db,
+ context->att, mtu);
g_assert(context->client);
- if (g_test_verbose())
- bt_gatt_client_set_debug(context->client, print_debug,
+ bt_gatt_client_set_debug(context->client, print_debug,
"bt_gatt_client:", NULL);
bt_gatt_client_set_ready_handler(context->client,
client_ready_cb, context, NULL);
-
- bt_att_unref(att);
break;
default:
break;
{
struct context *context = user_data;
+ bt_gatt_request_unref(context->req);
+ context->req = NULL;
+
g_assert(success);
context_quit(context);
}
-static void destroy_context(struct context *context)
-{
- if (context->source > 0)
- g_source_remove(context->source);
-
- bt_gatt_client_unref(context->client);
- bt_gatt_server_unref(context->server);
- gatt_db_unref(context->client_db);
- gatt_db_unref(context->server_db);
-
- if (context->att)
- bt_att_unref(context->att);
-
- g_main_loop_unref(context->main_loop);
-
- test_free(context->data);
- g_free(context);
-}
-
-static void execute_context(struct context *context)
-{
- g_main_loop_run(context->main_loop);
-
- destroy_context(context);
-}
-
static void test_read_cb(bool success, uint8_t att_ecode,
const uint8_t *value, uint16_t length,
void *user_data)
test_read_cb, context, NULL));
}
-const uint8_t read_data_1[] = {0x01, 0x02, 0x03};
+static const uint8_t read_data_1[] = {0x01, 0x02, 0x03};
-const struct test_step test_read_1 = {
+static const struct test_step test_read_1 = {
.handle = 0x0003,
.func = test_read,
.expected_att_ecode = 0,
.length = 0x03
};
-const struct test_step test_read_2 = {
+static const struct test_step test_read_2 = {
.handle = 0x0000,
.func = test_read,
.expected_att_ecode = 0x01,
};
-const struct test_step test_read_3 = {
+static const struct test_step test_read_3 = {
.handle = 0x0003,
.func = test_read,
.expected_att_ecode = 0x02,
};
-const struct test_step test_read_4 = {
+static const struct test_step test_read_4 = {
.handle = 0x0003,
.func = test_read,
.expected_att_ecode = 0x08,
};
-static void att_write_cb(struct gatt_db_attribute *att, int err,
- void *user_data)
-{
- g_assert(!err);
-}
-
-static struct gatt_db_attribute *add_char_with_value(struct gatt_db *db,
- struct gatt_db_attribute *service_att,
- bt_uuid_t *uuid,
- uint32_t att_permissions,
- uint8_t char_properties,
- const void *value, size_t len)
-{
- struct gatt_db_attribute *attrib;
-
- attrib = gatt_db_service_add_characteristic(service_att, uuid,
- att_permissions,
- char_properties,
- NULL, NULL,
- NULL);
+static const struct test_step test_read_5 = {
+ .handle = 0x0003,
+ .func = test_read,
+ .expected_att_ecode = 0x05,
+};
- gatt_db_attribute_write(attrib, 0, value, len, 0x00, NULL, att_write_cb,
- NULL);
+static const struct test_step test_read_6 = {
+ .handle = 0x0003,
+ .func = test_read,
+ .expected_att_ecode = 0x0c,
+};
- return attrib;
-}
+static const struct test_step test_read_7 = {
+ .handle = 0x0004,
+ .func = test_read,
+ .expected_att_ecode = 0x00,
+ .value = read_data_1,
+ .length = 0x03
+};
-static struct gatt_db_attribute *add_ccc(struct gatt_db_attribute *chrc_att,
- bool writable)
-{
- struct gatt_db_attribute *desc_att;
- bt_uuid_t uuid;
- uint32_t permissions = BT_ATT_PERM_READ;
- uint16_t tmp;
+static const struct test_step test_read_8 = {
+ .handle = 0x0004,
+ .func = test_read,
+ .expected_att_ecode = 0x02,
+};
- if (writable)
- permissions |= BT_ATT_PERM_WRITE;
+static const struct test_step test_read_9 = {
+ .handle = 0x0004,
+ .func = test_read,
+ .expected_att_ecode = 0x08,
+};
- bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
- desc_att = gatt_db_service_add_descriptor(chrc_att, &uuid, permissions,
- NULL, NULL, NULL);
+static const struct test_step test_read_10 = {
+ .handle = 0x0004,
+ .func = test_read,
+ .expected_att_ecode = 0x05,
+};
- tmp = 0x0000;
- gatt_db_attribute_write(desc_att, 0, (uint8_t *)&tmp, sizeof(uint16_t),
- 0x00, NULL, att_write_cb, NULL);
+static const struct test_step test_read_11 = {
+ .handle = 0x0004,
+ .func = test_read,
+ .expected_att_ecode = 0x0c,
+};
- return desc_att;
-}
+static const struct test_step test_read_12 = {
+ .handle = 0x0003,
+ .func = test_read,
+ .expected_att_ecode = 0x80,
+};
-static struct gatt_db_attribute *
-add_user_description(struct gatt_db_attribute *chrc_att, const char *desc,
- bool writable)
+static void test_write_cb(bool success, uint8_t att_ecode, void *user_data)
{
- struct gatt_db_attribute *desc_att;
- bt_uuid_t uuid;
- uint32_t permissions = BT_ATT_PERM_READ;
-
- if (writable)
- permissions |= BT_ATT_PERM_WRITE;
-
- bt_uuid16_create(&uuid, GATT_CHARAC_USER_DESC_UUID);
- desc_att = gatt_db_service_add_descriptor(chrc_att, &uuid, permissions,
- NULL, NULL, NULL);
+ struct context *context = user_data;
+ const struct test_step *step = context->data->step;
- gatt_db_attribute_write(desc_att, 0, (uint8_t *)desc, strlen(desc),
- 0x00, NULL, att_write_cb, NULL);
+ g_assert(att_ecode == step->expected_att_ecode);
- return desc_att;
+ context_quit(context);
}
-
-typedef struct gatt_db_attribute (*add_service_func) (struct gatt_db *db,
- uint16_t handle,
- bool primary,
- uint16_t extra_handles);
-
-static struct gatt_db_attribute *
-add_device_information_service(struct gatt_db *db, uint16_t handle,
- bool primary, uint16_t extra_handles)
+static void test_write(struct context *context)
{
- bt_uuid_t uuid;
- struct gatt_db_attribute *serv_att;
-
- bt_string_to_uuid(&uuid, DEVICE_INFORMATION_UUID);
- serv_att = gatt_db_insert_service(db, handle, &uuid, primary,
- 1 + extra_handles);
+ const struct test_step *step = context->data->step;
- return serv_att;
+ g_assert(bt_gatt_client_write_value(context->client, step->handle,
+ step->value, step->length, test_write_cb,
+ context, NULL));
}
-static struct gatt_db_attribute *add_gap(struct gatt_db *db, uint16_t handle,
- bool primary,
- uint16_t extra_handles)
-{
- bt_uuid_t uuid;
- struct gatt_db_attribute *serv_att;
-
- bt_string_to_uuid(&uuid, GAP_UUID);
- serv_att = gatt_db_insert_service(db, handle, &uuid, primary,
- 1 + extra_handles);
+static const uint8_t write_data_1[] = {0x01, 0x02, 0x03};
- return serv_att;
-}
+static const struct test_step test_write_1 = {
+ .handle = 0x0007,
+ .func = test_write,
+ .expected_att_ecode = 0,
+ .value = write_data_1,
+ .length = 0x03
+};
-static struct gatt_db *make_service_data_1_db(void)
-{
- struct gatt_db *db = gatt_db_new();
- struct gatt_db_attribute *serv_att, *chrc_att;
- bt_uuid_t uuid;
+static const struct test_step test_write_2 = {
+ .handle = 0x0000,
+ .func = test_write,
+ .expected_att_ecode = 0x01,
+ .value = write_data_1,
+ .length = 0x03
+};
- bt_uuid16_create(&uuid, 0x1801);
- serv_att = gatt_db_insert_service(db, 0x0001, &uuid, true, 4);
+static const struct test_step test_write_3 = {
+ .handle = 0x0007,
+ .func = test_write,
+ .expected_att_ecode = 0x03,
+ .value = write_data_1,
+ .length = 0x03
+};
- bt_uuid16_create(&uuid, GATT_CHARAC_DEVICE_NAME);
- chrc_att = add_char_with_value(db, serv_att, &uuid, BT_ATT_PERM_READ,
- BT_GATT_CHRC_PROP_READ, "BlueZ", 5);
+static const struct test_step test_write_4 = {
+ .handle = 0x0007,
+ .func = test_write,
+ .expected_att_ecode = 0x08,
+ .value = write_data_1,
+ .length = 0x03
+};
- add_user_description(chrc_att, "Device Name", false);
+static const struct test_step test_write_5 = {
+ .handle = 0x0007,
+ .func = test_write,
+ .expected_att_ecode = 0x05,
+ .value = write_data_1,
+ .length = 0x03
+};
- gatt_db_service_set_active(serv_att, true);
+static const struct test_step test_write_6 = {
+ .handle = 0x0007,
+ .func = test_write,
+ .expected_att_ecode = 0x0c,
+ .value = write_data_1,
+ .length = 0x03
+};
- bt_uuid16_create(&uuid, 0x180d);
- serv_att = gatt_db_insert_service(db, 0x0005, &uuid, true, 4);
+static const struct test_step test_write_7 = {
+ .handle = 0x0008,
+ .func = test_write,
+ .expected_att_ecode = 0,
+ .value = write_data_1,
+ .length = 0x03
+};
- bt_uuid16_create(&uuid, GATT_CHARAC_MANUFACTURER_NAME_STRING);
- chrc_att = gatt_db_service_add_characteristic(serv_att, &uuid,
- BT_ATT_PERM_READ,
- BT_GATT_CHRC_PROP_READ,
- NULL, NULL, NULL);
+static const struct test_step test_write_8 = {
+ .handle = 0x0000,
+ .func = test_write,
+ .expected_att_ecode = 0x01,
+ .value = write_data_1,
+ .length = 0x03
+};
- add_user_description(chrc_att, "Manufacturer Name", false);
+static const struct test_step test_write_9 = {
+ .handle = 0x0008,
+ .func = test_write,
+ .expected_att_ecode = 0x03,
+ .value = write_data_1,
+ .length = 0x03
+};
- gatt_db_service_set_active(serv_att, true);
+static const struct test_step test_write_10 = {
+ .handle = 0x0008,
+ .func = test_write,
+ .expected_att_ecode = 0x08,
+ .value = write_data_1,
+ .length = 0x03
+};
- return db;
-}
+static const struct test_step test_write_11 = {
+ .handle = 0x0008,
+ .func = test_write,
+ .expected_att_ecode = 0x05,
+ .value = write_data_1,
+ .length = 0x03
+};
-/*
- * Defined Test database 1:
- * Tiny database fits into a single minimum sized-pdu.
- * Satisfies:
- * 3. At least one primary seervice at the MAX handle
- * For each / all databases:
- * X 7. at least one service uuid with multiple instances
- * X 8. Some simple services, some with included services
- * X 9. an instance where handle of included service comes before the including
- * service
- * X 11. Simple characteristics (no desc) and complex characteristics
- * (multiple descriptors)
- * X 12. Instances of complex chars with 16-bit and 128-bit uuids
- * (although not in scrambled order)
- */
+static const struct test_step test_write_12 = {
+ .handle = 0x0008,
+ .func = test_write,
+ .expected_att_ecode = 0x0c,
+ .value = write_data_1,
+ .length = 0x03
+};
-static struct gatt_db *make_test_spec_small_db(void)
+static void test_write_without_response(struct context *context)
{
- struct gatt_db *db;
- struct gatt_db_attribute *serv_att, *dis_att;
- bt_uuid_t uuid;
- const char *manuf_device_string = "BlueZ";
- const char *device_name_string = "BlueZ Unit Tester";
- const char *user_desc_manuf_name = "Manufacturer Name";
- uint16_t u16_value;
- uint8_t u8_value;
-
- db = gatt_db_new();
+ const struct test_step *step = context->data->step;
- dis_att = add_device_information_service(db, 0x0001, false, 15);
+ g_assert(bt_gatt_client_write_without_response(context->client,
+ step->handle,
+ false, step->value,
+ step->length));
+}
- bt_uuid16_create(&uuid, GATT_CHARAC_MANUFACTURER_NAME_STRING);
- add_char_with_value(db, dis_att, &uuid, BT_ATT_PERM_READ,
- BT_GATT_CHRC_PROP_READ,
- manuf_device_string,
- strlen(manuf_device_string));
- add_ccc(dis_att, false);
- add_user_description(dis_att, user_desc_manuf_name, false);
+static const struct test_step test_write_without_response_1 = {
+ .handle = 0x0007,
+ .func = test_write_without_response,
+ .expected_att_ecode = 0,
+ .value = write_data_1,
+ .length = 0x03
+};
- gatt_db_service_set_active(dis_att, true);
+static bool local_counter(uint32_t *sign_cnt, void *user_data)
+{
+ static uint32_t cnt = 0;
- serv_att = add_gap(db, 0xF010, true, 5);
+ *sign_cnt = cnt++;
- gatt_db_service_add_included(serv_att, dis_att);
+ return true;
+}
- bt_uuid16_create(&uuid, GATT_CHARAC_DEVICE_NAME);
- add_char_with_value(db, serv_att, &uuid, BT_ATT_PERM_READ,
- BT_GATT_CHRC_PROP_READ,
- device_name_string,
- strlen(device_name_string));
+static void test_signed_write(struct context *context)
+{
+ const struct test_step *step = context->data->step;
+ uint8_t key[16] = {0xD8, 0x51, 0x59, 0x48, 0x45, 0x1F, 0xEA, 0x32, 0x0D,
+ 0xC0, 0x5A, 0x2E, 0x88, 0x30, 0x81, 0x88 };
- bt_string_to_uuid(&uuid, "0000B009-0000-0000-0123-456789abcdef");
- u8_value = 0x09;
- add_char_with_value(db, serv_att, &uuid, BT_ATT_PERM_READ,
- BT_GATT_CHRC_PROP_READ,
- &u8_value, sizeof(uint8_t));
+ g_assert(bt_att_set_local_key(context->att, key, local_counter,
+ context));
- gatt_db_service_set_active(serv_att, true);
+ g_assert(bt_gatt_client_write_without_response(context->client,
+ step->handle,
+ true, step->value,
+ step->length));
+}
- u16_value = 0x0000; /* "Unknown" Appearance */
- bt_uuid16_create(&uuid, GATT_CHARAC_APPEARANCE);
- add_char_with_value(db, serv_att, &uuid, BT_ATT_PERM_READ,
- BT_GATT_CHRC_PROP_READ, &u16_value,
- sizeof(uint16_t));
+static const struct test_step test_signed_write_1 = {
+ .handle = 0x0007,
+ .func = test_signed_write,
+ .expected_att_ecode = 0,
+ .value = write_data_1,
+ .length = 0x03
+};
+static void test_signed_write_seclevel(struct context *context)
+{
+ const struct test_step *step = context->data->step;
+ uint8_t key[16] = {0xD8, 0x51, 0x59, 0x48, 0x45, 0x1F, 0xEA, 0x32, 0x0D,
+ 0xC0, 0x5A, 0x2E, 0x88, 0x30, 0x81, 0x88 };
- serv_att = add_device_information_service(db, 0xFFFF, true, 0);
+ g_assert(bt_att_set_local_key(context->att, key, local_counter,
+ context));
- gatt_db_service_set_active(serv_att, true);
+ g_assert(bt_att_set_sec_level(context->att, BT_SECURITY_MEDIUM));
- return db;
+ g_assert(bt_gatt_client_write_without_response(context->client,
+ step->handle,
+ true, step->value,
+ step->length));
}
-static void test_client(gconstpointer data)
-{
- struct context *context = create_context(512, data);
+static const struct test_step test_signed_write_seclevel_1 = {
+ .handle = 0x0007,
+ .func = test_signed_write_seclevel,
+ .expected_att_ecode = 0,
+ .value = write_data_1,
+ .length = 0x03
+};
- execute_context(context);
+static void att_write_cb(struct gatt_db_attribute *att, int err,
+ void *user_data)
+{
+ g_assert(!err);
}
-static void test_server(gconstpointer data)
+static struct gatt_db_attribute *add_char_with_value(struct gatt_db *db,
+ struct gatt_db_attribute *service_att,
+ bt_uuid_t *uuid,
+ uint32_t att_permissions,
+ uint8_t char_properties,
+ const void *value, size_t len)
{
- struct context *context = create_context(512, data);
+ struct gatt_db_attribute *attrib;
+
+ attrib = gatt_db_service_add_characteristic(service_att, uuid,
+ att_permissions,
+ char_properties,
+ NULL, NULL,
+ NULL);
+
+ g_assert(attrib != NULL);
+
+ gatt_db_attribute_write(attrib, 0, value, len, 0x00, NULL,
+ att_write_cb, NULL);
+
+ return attrib;
+}
+
+static struct gatt_db_attribute *
+add_desc_with_value(struct gatt_db_attribute *att, bt_uuid_t *uuid,
+ uint32_t att_perms, const uint8_t *value,
+ size_t len)
+{
+ struct gatt_db_attribute *desc_att;
+
+ desc_att = gatt_db_service_add_descriptor(att, uuid, att_perms, NULL,
+ NULL, NULL);
+
+ gatt_db_attribute_write(desc_att, 0, value, len, 0x00, NULL,
+ att_write_cb, NULL);
+
+ return desc_att;
+}
+
+enum gatt_type {
+ PRIMARY,
+ SECONDARY,
+ INCLUDE,
+ CHARACTERISTIC,
+ DESCRIPTOR
+};
+
+struct att_handle_spec {
+ uint16_t handle;
+ const char *uuid;
+ enum gatt_type type;
+ uint8_t char_properties;
+ uint32_t att_permissions;
+ const uint8_t *value;
+ size_t len;
+ bool valid;
+};
+
+#define PRIMARY_SERVICE(start_handle, srv_uuid, num_handles) \
+ { \
+ .valid = true, \
+ .handle = start_handle, \
+ .type = PRIMARY, \
+ .uuid = srv_uuid, \
+ .len = num_handles, \
+ }
+
+#define SECONDARY_SERVICE(start_handle, srv_uuid, num_handles) \
+ { \
+ .valid = true, \
+ .handle = start_handle, \
+ .type = SECONDARY, \
+ .uuid = srv_uuid, \
+ .len = num_handles, \
+ }
+
+#define INCLUDE(include_handle) \
+ { \
+ .valid = true, \
+ .type = INCLUDE, \
+ .handle = include_handle, \
+ }
+
+#define STR(x) #x
+
+#define CHARACTERISTIC(chr_uuid, permissions, properties, bytes...) \
+ { \
+ .valid = true, \
+ .type = CHARACTERISTIC, \
+ .uuid = STR(chr_uuid), \
+ .att_permissions = permissions, \
+ .char_properties = properties, \
+ .value = data(bytes), \
+ .len = sizeof(data(bytes)), \
+ }
+
+#define CHARACTERISTIC_STR(chr_uuid, permissions, properties, string) \
+ { \
+ .valid = true, \
+ .type = CHARACTERISTIC, \
+ .uuid = STR(chr_uuid), \
+ .att_permissions = permissions, \
+ .char_properties = properties, \
+ .value = (uint8_t *)string, \
+ .len = strlen(string), \
+ }
+
+#define DESCRIPTOR(desc_uuid, permissions, bytes...) \
+ { \
+ .valid = true, \
+ .type = DESCRIPTOR, \
+ .uuid = STR(desc_uuid), \
+ .att_permissions = permissions, \
+ .value = data(bytes), \
+ .len = sizeof(data(bytes)), \
+ }
+
+#define DESCRIPTOR_STR(desc_uuid, permissions, string) \
+ { \
+ .valid = true, \
+ .type = DESCRIPTOR, \
+ .uuid = STR(desc_uuid), \
+ .att_permissions = permissions, \
+ .value = (uint8_t *)string, \
+ .len = strlen(string), \
+ }
+
+
+static struct gatt_db *make_db(const struct att_handle_spec *spec)
+{
+ struct gatt_db *db = gatt_db_new();
+ struct gatt_db_attribute *att, *include_att;
+ bt_uuid_t uuid;
+
+ att = include_att = NULL;
+
+ for (; spec->valid; spec++) {
+ switch (spec->type) {
+ case PRIMARY:
+ case SECONDARY:
+ bt_string_to_uuid(&uuid, spec->uuid);
+
+ if (att)
+ gatt_db_service_set_active(att, true);
+
+ att = gatt_db_insert_service(db, spec->handle, &uuid,
+ spec->type == PRIMARY, spec->len);
+ break;
+
+ case INCLUDE:
+ include_att = gatt_db_get_attribute(db, spec->handle);
+
+ gatt_db_service_add_included(att, include_att);
+ break;
+
+ case CHARACTERISTIC:
+ bt_string_to_uuid(&uuid, spec->uuid);
+
+ add_char_with_value(db, att, &uuid,
+ spec->att_permissions,
+ spec->char_properties,
+ spec->value, spec->len);
+
+ break;
+
+ case DESCRIPTOR:
+ bt_string_to_uuid(&uuid, spec->uuid);
+
+ add_desc_with_value(att, &uuid, spec->att_permissions,
+ spec->value, spec->len);
+
+ break;
+ };
+ }
+
+ if (att)
+ gatt_db_service_set_active(att, true);
+
+ return db;
+}
+
+static struct gatt_db *make_service_data_1_db(void)
+{
+ const struct att_handle_spec specs[] = {
+ PRIMARY_SERVICE(0x0001, GATT_UUID, 4),
+ CHARACTERISTIC_STR(GATT_CHARAC_DEVICE_NAME, BT_ATT_PERM_READ,
+ BT_GATT_CHRC_PROP_READ, "BlueZ"),
+ DESCRIPTOR_STR(GATT_CHARAC_USER_DESC_UUID, BT_ATT_PERM_READ,
+ "Device Name"),
+ PRIMARY_SERVICE(0x0005, HEART_RATE_UUID, 4),
+ CHARACTERISTIC_STR(GATT_CHARAC_MANUFACTURER_NAME_STRING,
+ BT_ATT_PERM_READ,
+ BT_GATT_CHRC_PROP_READ |
+ BT_GATT_CHRC_PROP_WRITE, ""),
+ DESCRIPTOR_STR(GATT_CHARAC_USER_DESC_UUID, BT_ATT_PERM_READ,
+ "Manufacturer Name"),
+ { }
+ };
+
+ return make_db(specs);
+}
+
+/*
+ * Defined Test database 1:
+ * Tiny database fits into a single minimum sized-pdu.
+ * Satisfies requirements:
+ * 3. At least one primary service at the MAX handle
+ * 7. at least one service uuid with multiple instances
+ * 8. Some simple services, some with included services
+ * 9. an instance where handle of included service comes before the including
+ * service
+ * 11. Simple characteristics (no desc) and complex characteristics
+ * (multiple descriptors)
+ * 12. Instances of complex chars with 16-bit and 128-bit uuids
+ * (although not in scrambled order)
+ */
+
+static struct gatt_db *make_test_spec_small_db(void)
+{
+ const struct att_handle_spec specs[] = {
+ SECONDARY_SERVICE(0x0001, DEVICE_INFORMATION_UUID, 16),
+ CHARACTERISTIC_STR(GATT_CHARAC_MANUFACTURER_NAME_STRING,
+ BT_ATT_PERM_READ |
+ BT_ATT_PERM_WRITE,
+ BT_GATT_CHRC_PROP_READ |
+ BT_GATT_CHRC_PROP_NOTIFY |
+ BT_GATT_CHRC_PROP_INDICATE,
+ "BlueZ"),
+ DESCRIPTOR(GATT_CLIENT_CHARAC_CFG_UUID, BT_ATT_PERM_READ |
+ BT_ATT_PERM_WRITE, 0x00, 0x00),
+ DESCRIPTOR_STR(GATT_CHARAC_USER_DESC_UUID, BT_ATT_PERM_READ,
+ "Manufacturer Name"),
+ PRIMARY_SERVICE(0xF010, GAP_UUID, 8),
+ INCLUDE(0x0001),
+ CHARACTERISTIC_STR(GATT_CHARAC_DEVICE_NAME, BT_ATT_PERM_READ,
+ BT_GATT_CHRC_PROP_READ,
+ "BlueZ Unit Tester"),
+ CHARACTERISTIC(0000B009-0000-0000-0123-456789abcdef,
+ BT_ATT_PERM_READ,
+ BT_GATT_CHRC_PROP_READ, 0x09),
+ CHARACTERISTIC(GATT_CHARAC_APPEARANCE, BT_ATT_PERM_READ,
+ BT_GATT_CHRC_PROP_READ, 0x00, 0x00),
+ PRIMARY_SERVICE(0xFFFF, DEVICE_INFORMATION_UUID, 1),
+ { }
+ };
+
+ return make_db(specs);
+}
+
+/*
+ * Defined Test database 2:
+ * Large Database with 128-bit services at the end
+ * Satisfies requirements:
+ * 4. at least one primary service without any include or characteristic
+ * at the max handle.
+ * 6. at least one secondary service
+ * 7. at least one each of 16-bit and 128-bit UUID with multiple instances
+ * 8. some simple services, some some with included services
+ * 9. one instance where an included service comes before the including
+ * 10. one or more services with both 16-bit and 128-bit service UUIDs
+ * 11. simple and complex characteristics
+ * 12. complex chars with 16-bit and 128-bit uuids
+ */
+
+#define STRING_512BYTES "11111222223333344444555556666677777888889999900000" \
+ "11111222223333344444555556666677777888889999900000" \
+ "11111222223333344444555556666677777888889999900000" \
+ "11111222223333344444555556666677777888889999900000" \
+ "11111222223333344444555556666677777888889999900000" \
+ "11111222223333344444555556666677777888889999900000" \
+ "11111222223333344444555556666677777888889999900000" \
+ "11111222223333344444555556666677777888889999900000" \
+ "11111222223333344444555556666677777888889999900000" \
+ "11111222223333344444555556666677777888889999900000" \
+ "111112222233"
+
+static struct gatt_db *make_test_spec_large_db_1(void)
+{
+ const struct att_handle_spec specs[] = {
+ PRIMARY_SERVICE(0x0080, "a00b", 6),
+ CHARACTERISTIC(0xb008, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+ BT_GATT_CHRC_PROP_READ |
+ BT_GATT_CHRC_PROP_WRITE,
+ 0x08),
+ DESCRIPTOR(0xb015, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE, 0x01),
+ DESCRIPTOR(0xb016, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE, 0x02),
+ DESCRIPTOR(0xb017, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE |
+ BT_ATT_PERM_ENCRYPT, 0x03),
+
+ SECONDARY_SERVICE(0x0001, "a00d", 6),
+ INCLUDE(0x0080),
+ CHARACTERISTIC(0xb00c, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ,
+ 0x0C),
+ CHARACTERISTIC(0000b00b-0000-0000-0123-456789abcdef,
+ BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ, 0x0B),
+
+ PRIMARY_SERVICE(0x0010, GATT_UUID, 4),
+ CHARACTERISTIC(GATT_CHARAC_SERVICE_CHANGED, BT_ATT_PERM_READ,
+ BT_GATT_CHRC_PROP_INDICATE,
+ 0x01, 0x00, 0xFF, 0xFF),
+ DESCRIPTOR(GATT_CLIENT_CHARAC_CFG_UUID,
+ BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+ 0x00, 0x00),
+
+ PRIMARY_SERVICE(0x0020, "a00a", 10),
+ INCLUDE(0x0001),
+ CHARACTERISTIC(0xb001, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ,
+ 0x01),
+ CHARACTERISTIC_STR(0xb002, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+ BT_GATT_CHRC_PROP_READ |
+ BT_GATT_CHRC_PROP_WRITE,
+ STRING_512BYTES),
+ CHARACTERISTIC_STR(0xb002, BT_ATT_PERM_WRITE,
+ BT_GATT_CHRC_PROP_WRITE,
+ "1111122222333334444455555"
+ "6666677777888889999900000"),
+ CHARACTERISTIC(0xb003, BT_ATT_PERM_WRITE,
+ BT_GATT_CHRC_PROP_WRITE, 0x03),
+
+ PRIMARY_SERVICE(0x0030, "a00b", 3),
+ CHARACTERISTIC(0xb007, BT_ATT_PERM_WRITE,
+ BT_GATT_CHRC_PROP_WRITE, 0x07),
+
+ PRIMARY_SERVICE(0x0040, GAP_UUID, 7),
+ CHARACTERISTIC_STR(GATT_CHARAC_DEVICE_NAME, BT_ATT_PERM_READ,
+ BT_GATT_CHRC_PROP_READ,
+ "Test Database"),
+ CHARACTERISTIC(GATT_CHARAC_APPEARANCE, BT_ATT_PERM_READ,
+ BT_GATT_CHRC_PROP_READ, 17),
+ CHARACTERISTIC(GATT_CHARAC_PERIPHERAL_PREF_CONN,
+ BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ,
+ 0x64, 0x00, 0xC8, 0x00, 0x00, 0x00, 0x07, 0xD0),
+
+ PRIMARY_SERVICE(0x0050, "a00b", 3),
+ CHARACTERISTIC(0xb006, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+ BT_GATT_CHRC_PROP_READ |
+ BT_GATT_CHRC_PROP_WRITE |
+ BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP |
+ BT_GATT_CHRC_PROP_NOTIFY |
+ BT_GATT_CHRC_PROP_INDICATE, 0x06),
+
+ PRIMARY_SERVICE(0x0060, "a00b", 12),
+ CHARACTERISTIC(0xb004, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+ BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_WRITE, 0x04),
+ CHARACTERISTIC(0xb004, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+ BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_WRITE, 0x04),
+ DESCRIPTOR(GATT_SERVER_CHARAC_CFG_UUID,
+ BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+ 0x00, 0x00),
+ CHARACTERISTIC(0xb004, 0, 0, 0x04),
+ DESCRIPTOR(0xb012, 0, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+ 0x88, 0x99, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55,
+ 0x66, 0x77, 0x88, 0x99, 0x00, 0x11, 0x22, 0x33,
+ 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00, 0x11,
+ 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
+ 0x00, 0x11, 0x22, 0x33),
+ CHARACTERISTIC(0xb004, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ,
+ 0x04),
+ DESCRIPTOR(0xb012, BT_ATT_PERM_READ, 0x11, 0x22, 0x33, 0x44,
+ 0x55, 0x66, 0x77, 0x88, 0x99, 0x00, 0x11, 0x22,
+ 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00,
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
+ 0x99, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66,
+ 0x77, 0x88, 0x99, 0x00, 0x11, 0x22, 0x33),
+
+ PRIMARY_SERVICE(0x0070, "a00b", 7),
+ CHARACTERISTIC(0xb005, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+ BT_GATT_CHRC_PROP_READ |
+ BT_GATT_CHRC_PROP_WRITE |
+ BT_GATT_CHRC_PROP_EXT_PROP,
+ 0x05),
+ DESCRIPTOR(GATT_CHARAC_EXT_PROPER_UUID, BT_ATT_PERM_READ, 0x03,
+ 0x00),
+ DESCRIPTOR_STR(GATT_CHARAC_USER_DESC_UUID,
+ BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"),
+ DESCRIPTOR(GATT_CHARAC_FMT_UUID, 0x04, 0x00, 0x01, 0x30, 0x01,
+ 0x11, 0x31),
+ DESCRIPTOR(0000d5d4-0000-0000-0123-456789abcdef,
+ BT_ATT_PERM_READ, 0x44),
+
+ /* 0x0080 service defined earlier, included in 0x0001 */
+
+ PRIMARY_SERVICE(0x0090, "0000a00c-0000-0000-0123-456789abcdef",
+ 7),
+ INCLUDE(0x0001),
+ CHARACTERISTIC(0000b009-0000-0000-0123-456789abcdef,
+ BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+ BT_GATT_CHRC_PROP_READ |
+ BT_GATT_CHRC_PROP_WRITE |
+ BT_GATT_CHRC_PROP_EXT_PROP, 0x09),
+ DESCRIPTOR(GATT_CHARAC_EXT_PROPER_UUID, BT_ATT_PERM_READ, 0x01,
+ 0x00),
+ DESCRIPTOR(0000d9d2-0000-0000-0123-456789abcdef,
+ BT_ATT_PERM_READ | BT_ATT_PERM_WRITE, 0x22),
+ DESCRIPTOR(0000d9d3-0000-0000-0123-456789abcdef,
+ BT_ATT_PERM_WRITE, 0x33),
+
+ PRIMARY_SERVICE(0x00a0, "a00f", 18),
+ CHARACTERISTIC_STR(0xb00e, BT_ATT_PERM_READ,
+ BT_GATT_CHRC_PROP_READ, "Length is "),
+ DESCRIPTOR(GATT_CHARAC_FMT_UUID, BT_ATT_PERM_READ, 0x19, 0x00,
+ 0x00, 0x30, 0x01, 0x00, 0x00),
+ CHARACTERISTIC(0xb00f, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+ BT_GATT_CHRC_PROP_READ |
+ BT_GATT_CHRC_PROP_WRITE, 0x65),
+ DESCRIPTOR(GATT_CHARAC_FMT_UUID, BT_ATT_PERM_READ, 0x04, 0x00,
+ 0x01, 0x27, 0x01, 0x01, 0x00),
+ CHARACTERISTIC(0xb006, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+ BT_GATT_CHRC_PROP_READ |
+ BT_GATT_CHRC_PROP_WRITE,
+ 0x34, 0x12),
+ DESCRIPTOR(GATT_CHARAC_FMT_UUID, BT_ATT_PERM_READ, 0x06, 0x00,
+ 0x10, 0x27, 0x01, 0x02, 0x00),
+ CHARACTERISTIC(0xb007, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+ BT_GATT_CHRC_PROP_READ |
+ BT_GATT_CHRC_PROP_WRITE,
+ 0x04, 0x03, 0x02, 0x01),
+ DESCRIPTOR(GATT_CHARAC_FMT_UUID, BT_ATT_PERM_READ, 0x08, 0x00,
+ 0x17, 0x27, 0x01, 0x03, 0x00),
+ CHARACTERISTIC(0xb010, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ,
+ 0x65, 0x34, 0x12, 0x04, 0x03, 0x02,
+ 0x01),
+ DESCRIPTOR(GATT_CHARAC_AGREG_FMT_UUID, BT_ATT_PERM_READ, 0xA6,
+ 0x00, 0xa9, 0x00, 0xac, 0x00),
+ CHARACTERISTIC(0xb011, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+ BT_GATT_CHRC_PROP_READ |
+ BT_GATT_CHRC_PROP_AUTH, 0x012),
+
+ PRIMARY_SERVICE(0x00C0, "0000a00c-0000-0000-0123-456789abcdef",
+ 30),
+ CHARACTERISTIC(0xb00a, BT_ATT_PERM_READ, BT_GATT_CHRC_PROP_READ,
+ 0x0A),
+ CHARACTERISTIC_STR(0xb002, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+ BT_GATT_CHRC_PROP_READ |
+ BT_GATT_CHRC_PROP_WRITE,
+ "111112222233333444445"),
+ DESCRIPTOR(0xb012, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
+ 0x99, 0x00, 0x12, 0x34, 0x56, 0x78, 0x90, 0x11),
+ CHARACTERISTIC_STR(0xb002, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+ BT_GATT_CHRC_PROP_READ |
+ BT_GATT_CHRC_PROP_WRITE,
+ "2222233333444445555566"),
+ DESCRIPTOR(0xb013, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
+ 0x99, 0x00, 0x12, 0x34, 0x56, 0x78, 0x90, 0x11,
+ 0x22),
+ CHARACTERISTIC_STR(0xb002, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+ BT_GATT_CHRC_PROP_READ |
+ BT_GATT_CHRC_PROP_WRITE,
+ "33333444445555566666777"),
+ DESCRIPTOR(0xb014, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
+ 0x99, 0x00, 0x12, 0x34, 0x56, 0x78, 0x90, 0x11,
+ 0x22, 0x33),
+ CHARACTERISTIC(0xb002, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+ BT_GATT_CHRC_PROP_READ |
+ BT_GATT_CHRC_PROP_WRITE,
+ 0x11, 0x22, 0x33, 0x44, 0x55,
+ 0x66, 0x77, 0x88, 0x99, 0x00,
+ 0x11, 0x22, 0x33, 0x44, 0x55,
+ 0x66, 0x77, 0x88, 0x99, 0x00,
+ 0x11, 0x22, 0x33, 0x44, 0x55,
+ 0x66, 0x77, 0x88, 0x99, 0x00,
+ 0x11, 0x22, 0x33, 0x44, 0x55,
+ 0x66, 0x77, 0x88, 0x99, 0x00,
+ 0x11, 0x22, 0x33),
+ DESCRIPTOR(0xb012, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
+ 0x99, 0x00, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12,
+ 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78,
+ 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x11, 0x22,
+ 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00,
+ 0x11, 0x22, 0x33),
+ CHARACTERISTIC(0xb002, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+ BT_GATT_CHRC_PROP_READ |
+ BT_GATT_CHRC_PROP_WRITE,
+ 0x11, 0x22, 0x33, 0x44, 0x55,
+ 0x66, 0x77, 0x88, 0x99, 0x00,
+ 0x11, 0x22, 0x33, 0x44, 0x55,
+ 0x66, 0x77, 0x88, 0x99, 0x00,
+ 0x11, 0x22, 0x33, 0x44, 0x55,
+ 0x66, 0x77, 0x88, 0x99, 0x00,
+ 0x11, 0x22, 0x33, 0x44, 0x55,
+ 0x66, 0x77, 0x88, 0x99, 0x00,
+ 0x11, 0x22, 0x33, 0x44),
+ DESCRIPTOR(0xb013, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
+ 0x99, 0x00, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12,
+ 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78,
+ 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x11, 0x22,
+ 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00,
+ 0x11, 0x22, 0x33, 0x44),
+ CHARACTERISTIC(0xb002, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+ BT_GATT_CHRC_PROP_READ |
+ BT_GATT_CHRC_PROP_WRITE,
+ 0x11, 0x22, 0x33, 0x44, 0x55,
+ 0x66, 0x77, 0x88, 0x99, 0x00,
+ 0x11, 0x22, 0x33, 0x44, 0x55,
+ 0x66, 0x77, 0x88, 0x99, 0x00,
+ 0x11, 0x22, 0x33, 0x44, 0x55,
+ 0x66, 0x77, 0x88, 0x99, 0x00,
+ 0x11, 0x22, 0x33, 0x44, 0x55,
+ 0x66, 0x77, 0x88, 0x99, 0x00,
+ 0x11, 0x22, 0x33, 0x44, 0x55),
+ DESCRIPTOR(0xb014, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
+ 0x99, 0x00, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12,
+ 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78,
+ 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x11, 0x22,
+ 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00,
+ 0x11, 0x22, 0x33, 0x44, 0x55),
+ CHARACTERISTIC_STR(0xb002, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+ BT_GATT_CHRC_PROP_READ |
+ BT_GATT_CHRC_PROP_WRITE,
+ "1111122222333334444455555"
+ "666667777788888999"),
+ DESCRIPTOR(0xb012, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
+ 0x99, 0x00, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12,
+ 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78,
+ 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x11, 0x22,
+ 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00,
+ 0x11, 0x22, 0x33),
+ CHARACTERISTIC_STR(0xb002, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+ BT_GATT_CHRC_PROP_READ |
+ BT_GATT_CHRC_PROP_WRITE,
+ "2222233333444445555566666"
+ "7777788888999990000"),
+ DESCRIPTOR(0xb013, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
+ 0x99, 0x00, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12,
+ 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78,
+ 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x11, 0x22,
+ 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00,
+ 0x11, 0x22, 0x33, 0x44),
+ CHARACTERISTIC_STR(0xb002, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+ BT_GATT_CHRC_PROP_READ |
+ BT_GATT_CHRC_PROP_WRITE,
+ "3333344444555556666677777"
+ "88888999990000011111"),
+ DESCRIPTOR(0xb014, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+ 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88,
+ 0x99, 0x00, 0x12, 0x34, 0x56, 0x78, 0x90, 0x12,
+ 0x34, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78,
+ 0x90, 0x12, 0x34, 0x56, 0x78, 0x90, 0x11, 0x22,
+ 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99, 0x00,
+ 0x11, 0x22, 0x33, 0x44, 0x55),
+ { }
+ };
+
+ return make_db(specs);
+}
+
+static void test_client(gconstpointer data)
+{
+ create_context(512, data);
+}
+
+static void test_server(gconstpointer data)
+{
+ struct context *context = create_context(512, data);
ssize_t len;
const struct test_pdu pdu = SERVER_MTU_EXCHANGE_PDU;
g_assert_cmpint(len, ==, pdu.size);
- if (g_test_verbose())
- util_hexdump('<', pdu.data, len, test_debug, "GATT: ");
-
- execute_context(context);
+ util_hexdump('<', pdu.data, len, test_debug, "GATT: ");
}
static void test_search_primary(gconstpointer data)
struct context *context = create_context(512, data);
const struct test_data *test_data = data;
- bt_gatt_discover_all_primary_services(context->att, test_data->uuid,
+ context->req = bt_gatt_discover_all_primary_services(context->att,
+ test_data->uuid,
generic_search_cb,
context, NULL);
-
- execute_context(context);
}
static void test_search_included(gconstpointer data)
{
struct context *context = create_context(512, data);
- bt_gatt_discover_included_services(context->att, 0x0001, 0xffff,
+ context->req = bt_gatt_discover_included_services(context->att,
+ 0x0001, 0xffff,
generic_search_cb,
context, NULL);
-
- execute_context(context);
}
static void test_search_chars(gconstpointer data)
{
struct context *context = create_context(512, data);
- g_assert(bt_gatt_discover_characteristics(context->att, 0x0010, 0x0020,
+ context->req = bt_gatt_discover_characteristics(context->att,
+ 0x0010, 0x0020,
generic_search_cb,
- context, NULL));
-
- execute_context(context);
+ context, NULL);
+ g_assert(context->req);
}
static void test_search_descs(gconstpointer data)
{
struct context *context = create_context(512, data);
- g_assert(bt_gatt_discover_descriptors(context->att, 0x0013, 0x0016,
+ context->req = bt_gatt_discover_descriptors(context->att,
+ 0x0013, 0x0016,
generic_search_cb,
- context, NULL));
-
- execute_context(context);
+ context, NULL);
+ g_assert(context->req);
}
-const struct test_step test_read_by_type_1 = {
+static const struct test_step test_read_by_type_1 = {
.handle = 0x0001,
.end_handle = 0xffff,
.expected_att_ecode = 0x0a,
.length = 0x03
};
-const struct test_step test_read_by_type_2 = {
+static const struct test_step test_read_by_type_2 = {
.handle = 0x0001,
.end_handle = 0xffff,
.expected_att_ecode = 0x02,
};
-const struct test_step test_read_by_type_3 = {
+static const struct test_step test_read_by_type_3 = {
.handle = 0x0001,
.end_handle = 0xffff,
.expected_att_ecode = 0x0a,
};
-const struct test_step test_read_by_type_4 = {
+static const struct test_step test_read_by_type_4 = {
.handle = 0x0001,
.end_handle = 0xffff,
.expected_att_ecode = 0x08,
};
-const struct test_step test_read_by_type_5 = {
+static const struct test_step test_read_by_type_5 = {
.handle = 0x0001,
.end_handle = 0xffff,
.expected_att_ecode = 0x05,
};
-const struct test_step test_read_by_type_6 = {
+static const struct test_step test_read_by_type_6 = {
.handle = 0x0001,
.end_handle = 0xffff,
.expected_att_ecode = 0x0c,
struct context *context = user_data;
const struct test_step *step = context->data->step;
- g_assert(att_ecode == step->expected_att_ecode);
+ g_assert_cmpint(att_ecode, ==, step->expected_att_ecode);
if (success) {
- g_assert(length == step->length);
+ g_assert_cmpint(length, ==, step->length);
g_assert(memcmp(value, step->value, length) == 0);
}
NULL));
}
-const struct test_step test_multiple_read_1 = {
+static const struct test_step test_multiple_read_1 = {
.handle = 0x0003,
.end_handle = 0x0007,
.func = test_multiple_read,
.length = 0x03
};
-const struct test_step test_multiple_read_2 = {
+static const struct test_step test_multiple_read_2 = {
.handle = 0x0003,
.end_handle = 0x0007,
.func = test_multiple_read,
.expected_att_ecode = 0x02
};
-const struct test_step test_multiple_read_3 = {
+static const struct test_step test_multiple_read_3 = {
.handle = 0x0003,
.end_handle = 0x0007,
.func = test_multiple_read,
.expected_att_ecode = 0x01
};
-const struct test_step test_multiple_read_4 = {
+static const struct test_step test_multiple_read_4 = {
.handle = 0x0003,
.end_handle = 0x0007,
.func = test_multiple_read,
.expected_att_ecode = 0x08
};
-const struct test_step test_multiple_read_5 = {
+static const struct test_step test_multiple_read_5 = {
.handle = 0x0003,
.end_handle = 0x0007,
.func = test_multiple_read,
.expected_att_ecode = 0x05
};
-const struct test_step test_multiple_read_6 = {
+static const struct test_step test_multiple_read_6 = {
.handle = 0x0003,
.end_handle = 0x0007,
.func = test_multiple_read,
g_assert(bt_gatt_read_by_type(context->att, step->handle,
step->end_handle, test_data->uuid,
read_by_type_cb, context, NULL));
+}
+
+static void test_long_read(struct context *context)
+{
+ const struct test_step *step = context->data->step;
+
+ g_assert(bt_gatt_client_read_long_value(context->client, step->handle,
+ 0, multiple_read_cb, context,
+ NULL));
+}
+
+static const struct test_step test_long_read_1 = {
+ .handle = 0x0003,
+ .func = test_long_read,
+ .expected_att_ecode = 0,
+ .value = read_data_1,
+ .length = 0x03
+};
+
+/* The maximum length of an attribute value shall be 512 octets. */
+static const uint8_t long_data_2[512] = { [0 ... 511] = 0xff };
+
+static const struct test_step test_long_read_2 = {
+ .handle = 0x0003,
+ .func = test_long_read,
+ .expected_att_ecode = 0,
+ .value = long_data_2,
+ .length = sizeof(long_data_2)
+};
+
+static const struct test_step test_long_read_3 = {
+ .handle = 0x0003,
+ .func = test_long_read,
+ .expected_att_ecode = 0x02
+};
+
+static const struct test_step test_long_read_4 = {
+ .handle = 0x0003,
+ .func = test_long_read,
+ .expected_att_ecode = 0x07
+};
+
+static const struct test_step test_long_read_5 = {
+ .handle = 0x0000,
+ .func = test_long_read,
+ .expected_att_ecode = 0x01
+};
+
+static const struct test_step test_long_read_6 = {
+ .handle = 0x0003,
+ .func = test_long_read,
+ .expected_att_ecode = 0x08
+};
+
+static const struct test_step test_long_read_7 = {
+ .handle = 0x0003,
+ .func = test_long_read,
+ .expected_att_ecode = 0x05
+};
+
+static const struct test_step test_long_read_8 = {
+ .handle = 0x0003,
+ .func = test_long_read,
+ .expected_att_ecode = 0x0c
+};
+
+/* Descriptor test data's */
+
+static const struct test_step test_long_read_9 = {
+ .handle = 0x0004,
+ .func = test_long_read,
+ .expected_att_ecode = 0,
+ .value = read_data_1,
+ .length = 0x03
+};
+
+static const struct test_step test_long_read_10 = {
+ .handle = 0x0004,
+ .func = test_long_read,
+ .expected_att_ecode = 0,
+ .value = long_data_2,
+ .length = sizeof(long_data_2)
+};
+
+static const struct test_step test_long_read_11 = {
+ .handle = 0x0004,
+ .func = test_long_read,
+ .expected_att_ecode = 0x02
+};
+
+static const struct test_step test_long_read_12 = {
+ .handle = 0x0004,
+ .func = test_long_read,
+ .expected_att_ecode = 0x07
+};
- execute_context(context);
+static const struct test_step test_long_read_13 = {
+ .handle = 0x0004,
+ .func = test_long_read,
+ .expected_att_ecode = 0x08
+};
+
+static const struct test_step test_long_read_14 = {
+ .handle = 0x0004,
+ .func = test_long_read,
+ .expected_att_ecode = 0x05
+};
+
+static const struct test_step test_long_read_15 = {
+ .handle = 0x0004,
+ .func = test_long_read,
+ .expected_att_ecode = 0x0c
+};
+
+static void notification_cb(uint16_t value_handle, const uint8_t *value,
+ uint16_t length, void *user_data)
+{
+ struct context *context = user_data;
+ const struct test_step *step = context->data->step;
+
+ if (value_handle == step->handle) {
+ g_assert_cmpint(length, ==, step->length);
+
+ g_assert(memcmp(value, step->value, length) == 0);
+
+ context_quit(context);
+ }
+}
+
+static void notification_register_cb(uint16_t att_ecode, void *user_data)
+{
+ g_assert(!att_ecode);
+}
+
+static void test_notification(struct context *context)
+{
+ const struct test_step *step = context->data->step;
+
+ g_assert(bt_gatt_client_register_notify(context->client, step->handle,
+ notification_register_cb,
+ notification_cb, context,
+ NULL));
+}
+
+static const struct test_step test_notification_1 = {
+ .handle = 0x0003,
+ .func = test_notification,
+ .value = read_data_1,
+ .length = 0x03,
+};
+
+static void test_server_notification(struct context *context)
+{
+ const struct test_step *step = context->data->step;
+
+ bt_gatt_server_send_notification(context->server, step->handle,
+ step->value, step->length);
}
+static const struct test_step test_notification_server_1 = {
+ .handle = 0x0003,
+ .func = test_server_notification,
+ .value = read_data_1,
+ .length = 0x03,
+};
+
+static uint8_t indication_received;
+
+static void test_indication_cb(void *user_data)
+{
+ struct context *context = user_data;
+
+ indication_received = 1;
+
+ context_quit(context);
+}
+
+static void test_server_indication_confirm(struct context *context)
+{
+ g_assert(indication_received == 1);
+}
+
+static void indication_cb(uint16_t value_handle, const uint8_t *value,
+ uint16_t length, void *user_data)
+{
+ struct context *context = user_data;
+ const struct test_step *step = context->data->step;
+
+ if (value_handle == step->handle) {
+ g_assert_cmpint(length, ==, step->length);
+
+ g_assert(memcmp(value, step->value, length) == 0);
+ }
+}
+
+static void test_indication(struct context *context)
+{
+ const struct test_step *step = context->data->step;
+
+ g_assert(bt_gatt_client_register_notify(context->client, step->handle,
+ notification_register_cb,
+ indication_cb, context,
+ NULL));
+}
+
+static const struct test_step test_indication_1 = {
+ .handle = 0x0003,
+ .func = test_indication,
+ .value = read_data_1,
+ .length = 0x03,
+};
+
+static void test_server_indication(struct context *context)
+{
+ const struct test_step *step = context->data->step;
+
+ bt_gatt_server_send_indication(context->server, step->handle,
+ step->value, step->length,
+ test_indication_cb,
+ context, NULL);
+}
+
+static const struct test_step test_indication_server_1 = {
+ .handle = 0x0003,
+ .func = test_server_indication,
+ .post_func = test_server_indication_confirm,
+ .value = read_data_1,
+ .length = 0x03,
+};
+
int main(int argc, char *argv[])
{
- struct gatt_db *service_db_1, *ts_small_db;
+ struct gatt_db *service_db_1, *ts_small_db, *ts_large_db_1;
- g_test_init(&argc, &argv, NULL);
+ tester_init(&argc, &argv);
service_db_1 = make_service_data_1_db();
ts_small_db = make_test_spec_small_db();
+ ts_large_db_1 = make_test_spec_large_db_1();
/*
* Server Configuration
raw_pdu(0x03, 0x00, 0x02),
PRIMARY_DISC_SMALL_DB);
+ define_test_server("/TP/GAD/SR/BV-01-C-large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ PRIMARY_DISC_LARGE_DB_1);
+
define_test_att("/TP/GAD/CL/BV-02-C-1", test_search_primary, &uuid_16,
NULL,
MTU_EXCHANGE_CLIENT_PDUS,
0x34, 0x9b, 0x5f, 0x80, 0x00, 0x00,
0x80, 0x00, 0x10, 0x00, 0x00, 0x0d,
0x18, 0x00, 0x00),
- raw_pdu(0x01, 0x06, 0x08, 0x00, 0x0a));
+ raw_pdu(0x01, 0x06, 0x18, 0x00, 0x0a));
+
+ define_test_server("/TP/GAD/SR/BV-02-C/exists-16/small", test_server,
+ ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x06, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28, 0x00,
+ 0x18),
+ raw_pdu(0x07, 0x10, 0xf0, 0x17, 0xf0),
+ raw_pdu(0x06, 0x18, 0xf0, 0xff, 0xff, 0x00, 0x28, 0x00,
+ 0x18),
+ raw_pdu(0x01, 0x06, 0x18, 0xf0, 0x0a));
+
+ define_test_server("/TP/GAD/SR/BV-02-C/exists-16/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x06, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28, 0x0b,
+ 0xa0),
+ raw_pdu(0x07, 0x30, 0x00, 0x32, 0x00, 0x50, 0x00, 0x52,
+ 0x00, 0x60, 0x00, 0x6b, 0x00, 0x70, 0x00, 0x76,
+ 0x00, 0x80, 0x00, 0x85, 0x00),
+ raw_pdu(0x06, 0x86, 0x00, 0xff, 0xff, 0x00, 0x28, 0x0b,
+ 0xa0),
+ raw_pdu(0x01, 0x06, 0x86, 0x00, 0x0a));
+
+ define_test_server("/TP/GAD/SR/BV-02-C/missing-16/small", test_server,
+ ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x06, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28, 0x01,
+ 0x18),
+ raw_pdu(0x01, 0x06, 0x01, 0x00, 0x0a));
+
+ define_test_server("/TP/GAD/SR/BV-02-C/missing-16/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x06, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28, 0x0f,
+ 0xf0),
+ raw_pdu(0x01, 0x06, 0x01, 0x00, 0x0a));
+
+ define_test_server("/TP/GAD/SR/BV-02-C/exists-128/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x06, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28, 0xef,
+ 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x0c, 0xa0, 0x00, 0x00),
+ raw_pdu(0x07, 0x90, 0x00, 0x96, 0x00, 0xc0, 0x00, 0xdd,
+ 0x00),
+ raw_pdu(0x06, 0xde, 0x00, 0xff, 0xff, 0x00, 0x28, 0xef,
+ 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x0c, 0xa0, 0x00, 0x00),
+ raw_pdu(0x01, 0x06, 0xde, 0x00, 0x0a));
+
+ define_test_server("/TP/GAD/SR/BV-02-C/missing-128/large-1",
+ test_server, ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x06, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28, 0xff,
+ 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x0c, 0xa0, 0x00, 0x00),
+ raw_pdu(0x01, 0x06, 0x01, 0x00, 0x0a));
define_test_att("/TP/GAD/CL/BV-03-C", test_search_included, NULL,
NULL,
raw_pdu(0x08, 0x06, 0x00, 0xff, 0xff, 0x02, 0x28),
raw_pdu(0x01, 0x08, 0x06, 0x00, 0x0a));
+ define_test_server("/TP/GAD/SR/BV-03-C/small", test_server,
+ ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x08, 0x01, 0x00, 0xff, 0xff, 0x02, 0x28),
+ raw_pdu(0x09, 0x08, 0x11, 0xf0, 0x01, 0x00, 0x10, 0x00,
+ 0x0a, 0x18),
+ raw_pdu(0x08, 0x12, 0xf0, 0xff, 0xff, 0x02, 0x28),
+ raw_pdu(0x01, 0x08, 0x12, 0xf0, 0x0a));
+
+ define_test_server("/TP/GAD/SR/BV-03-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x08, 0x01, 0x00, 0xff, 0xff, 0x02, 0x28),
+ raw_pdu(0x09, 0x08, 0x02, 0x00, 0x80, 0x00, 0x85, 0x00,
+ 0x0b, 0xa0, 0x21, 0x00, 0x01, 0x00, 0x06, 0x00,
+ 0x0d, 0xa0),
+ raw_pdu(0x08, 0x22, 0x00, 0xff, 0xff, 0x02, 0x28),
+ raw_pdu(0x09, 0x08, 0x91, 0x00, 0x01, 0x00, 0x06, 0x00,
+ 0x0d, 0xa0),
+ raw_pdu(0x08, 0x92, 0x00, 0xff, 0xff, 0x02, 0x28),
+ raw_pdu(0x01, 0x08, 0x92, 0x00, 0x0a));
+
define_test_att("/TP/GAD/CL/BV-04-C", test_search_chars, NULL,
NULL,
MTU_EXCHANGE_CLIENT_PDUS,
raw_pdu(0x08, 0x10, 0x00, 0x20, 0x00, 0x03, 0x28),
- raw_pdu(0x09, 0x07, 0x11, 0x00, 02, 0x12, 0x00, 0x25,
+ raw_pdu(0x09, 0x07, 0x11, 0x00, 0x02, 0x12, 0x00, 0x25,
+ 0x2a),
+ raw_pdu(0x08, 0x12, 0x00, 0x20, 0x00, 0x03, 0x28),
+ raw_pdu(0x09, 0x15, 0x13, 0x00, 0x02, 0x14, 0x00, 0x85,
+ 0x00, 0xef, 0xcd, 0xab, 0x89, 0x67,
+ 0x45, 0x23, 0x01, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00),
+ raw_pdu(0x08, 0x14, 0x00, 0x20, 0x00, 0x03, 0x28),
+ raw_pdu(0x01, 0x08, 0x12, 0x00, 0x0a));
+
+ define_test_server("/TP/GAD/SR/BV-04-C/small/1", test_server,
+ ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x08, 0x10, 0xf0, 0x17, 0xf0, 0x03, 0x28),
+ raw_pdu(0x09, 0x07, 0x12, 0xf0, 0x02, 0x13, 0xf0, 0x00,
+ 0x2a),
+ raw_pdu(0x08, 0x13, 0xf0, 0x17, 0xf0, 0x03, 0x28),
+ raw_pdu(0x09, 0x15, 0x14, 0xf0, 0x02, 0x15, 0xf0, 0xef,
+ 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x09,
+ 0xb0, 0x00, 0x00),
+ raw_pdu(0x08, 0x15, 0xf0, 0x17, 0xf0, 0x03, 0x28),
+ raw_pdu(0x09, 0x07, 0x16, 0xf0, 0x02, 0x17, 0xf0, 0x01,
+ 0x2a),
+ raw_pdu(0x08, 0x17, 0xf0, 0x17, 0xf0, 0x03, 0x28),
+ raw_pdu(0x01, 0x08, 0x17, 0xf0, 0x0a));
+
+ define_test_server("/TP/GAD/SR/BV-04-C/small/2", test_server,
+ ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x08, 0x01, 0x00, 0x0f, 0x00, 0x03, 0x28),
+ raw_pdu(0x09, 0x07, 0x02, 0x00, 0x32, 0x03, 0x00, 0x29,
+ 0x2a),
+ raw_pdu(0x08, 0x03, 0x00, 0x0f, 0x00, 0x03, 0x28),
+ raw_pdu(0x01, 0x08, 0x03, 0x00, 0x0a));
+
+ define_test_server("/TP/GAD/SR/BV-04-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x08, 0x20, 0x00, 0x29, 0x00, 0x03, 0x28),
+ raw_pdu(0x09, 0x07, 0x22, 0x00, 0x02, 0x23, 0x00, 0x01,
+ 0xb0, 0x24, 0x00, 0x0a, 0x25, 0x00, 0x02, 0xb0,
+ 0x26, 0x00, 0x08, 0x27, 0x00, 0x02, 0xb0),
+ raw_pdu(0x08, 0x27, 0x00, 0x29, 0x00, 0x03, 0x28),
+ raw_pdu(0x09, 0x07, 0x28, 0x00, 0x08, 0x29, 0x00, 0x03,
+ 0xb0),
+ raw_pdu(0x08, 0x29, 0x00, 0x29, 0x00, 0x03, 0x28),
+ raw_pdu(0x01, 0x08, 0x29, 0x00, 0x0a));
+
+ define_test_att("/TP/GAD/CL/BV-05-C", test_search_chars, NULL,
+ NULL,
+ MTU_EXCHANGE_CLIENT_PDUS,
+ raw_pdu(0x08, 0x10, 0x00, 0x20, 0x00, 0x03, 0x28),
+ raw_pdu(0x09, 0x07, 0x11, 0x00, 0x02, 0x12, 0x00, 0x25,
0x2a),
raw_pdu(0x08, 0x12, 0x00, 0x20, 0x00, 0x03, 0x28),
raw_pdu(0x09, 0x15, 0x13, 0x00, 0x02, 0x14, 0x00, 0x85,
raw_pdu(0x08, 0x14, 0x00, 0x20, 0x00, 0x03, 0x28),
raw_pdu(0x01, 0x08, 0x12, 0x00, 0x0a));
+ define_test_server("/TP/GAD/SR/BV-05-C/small/1", test_server,
+ ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x08, 0x10, 0xf0, 0x17, 0xf0, 0x03, 0x28),
+ raw_pdu(0x09, 0x07, 0x12, 0xf0, 0x02, 0x13, 0xf0, 0x00,
+ 0x2a),
+ raw_pdu(0x08, 0x13, 0xf0, 0x17, 0xf0, 0x03, 0x28),
+ raw_pdu(0x09, 0x15, 0x14, 0xf0, 0x02, 0x15, 0xf0, 0xef,
+ 0xcd, 0xab, 0x89, 0x67, 0x45, 0x23,
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x09,
+ 0xb0, 0x00, 0x00),
+ raw_pdu(0x08, 0x15, 0xf0, 0x17, 0xf0, 0x03, 0x28),
+ raw_pdu(0x09, 0x07, 0x16, 0xf0, 0x02, 0x17, 0xf0, 0x01,
+ 0x2a),
+ raw_pdu(0x08, 0x17, 0xf0, 0x17, 0xf0, 0x03, 0x28),
+ raw_pdu(0x01, 0x08, 0x17, 0xf0, 0x0a));
+
+ define_test_server("/TP/GAD/SR/BV-05-C/small/2", test_server,
+ ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x08, 0x01, 0x00, 0x0f, 0x00, 0x03, 0x28),
+ raw_pdu(0x09, 0x07, 0x02, 0x00, 0x32, 0x03, 0x00, 0x29,
+ 0x2a),
+ raw_pdu(0x08, 0x03, 0x00, 0x0f, 0x00, 0x03, 0x28),
+ raw_pdu(0x01, 0x08, 0x03, 0x00, 0x0a));
+
+ define_test_server("/TP/GAD/SR/BV-05-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x08, 0x20, 0x00, 0x29, 0x00, 0x03, 0x28),
+ raw_pdu(0x09, 0x07, 0x22, 0x00, 0x02, 0x23, 0x00, 0x01,
+ 0xb0, 0x24, 0x00, 0x0a, 0x25, 0x00, 0x02, 0xb0,
+ 0x26, 0x00, 0x08, 0x27, 0x00, 0x02, 0xb0),
+ raw_pdu(0x08, 0x27, 0x00, 0x29, 0x00, 0x03, 0x28),
+ raw_pdu(0x09, 0x07, 0x28, 0x00, 0x08, 0x29, 0x00, 0x03,
+ 0xb0),
+ raw_pdu(0x08, 0x29, 0x00, 0x29, 0x00, 0x03, 0x28),
+ raw_pdu(0x01, 0x08, 0x29, 0x00, 0x0a));
+
define_test_att("/TP/GAD/CL/BV-06-C", test_search_descs, NULL, NULL,
MTU_EXCHANGE_CLIENT_PDUS,
raw_pdu(0x04, 0x13, 0x00, 0x16, 0x00),
raw_pdu(0x05, 0x01, 0x15, 0x00, 0x04, 0x29, 0x16, 0x00,
0x05, 0x29));
+ define_test_server("/TP/GAD/SR/BV-06-C/small", test_server,
+ ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x04, 0x04, 0x00, 0x05, 0x00),
+ raw_pdu(0x05, 0x01, 0x04, 0x00, 0x02, 0x29, 0x05, 0x00,
+ 0x01, 0x29));
+
+ define_test_server("/TP/GAD/SR/BV-06-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x04, 0x73, 0x00, 0x76, 0x00),
+ raw_pdu(0x05, 0x01, 0x73, 0x00, 0x00, 0x29, 0x74, 0x00,
+ 0x01, 0x29, 0x75, 0x00, 0x04, 0x29),
+ raw_pdu(0x04, 0x76, 0x00, 0x76, 0x00),
+ raw_pdu(0x05, 0x02, 0x76, 0x00, 0xef, 0xcd, 0xab, 0x89,
+ 0x67, 0x45, 0x23, 0x01, 0x00, 0x00, 0x00, 0x00,
+ 0xd4, 0xd5, 0x00, 0x00));
+
define_test_client("/TP/GAR/CL/BV-01-C", test_client, service_db_1,
&test_read_1,
SERVICE_DATA_1_PDUS,
raw_pdu(0x0a, 0x03, 0x00),
raw_pdu(0x01, 0x0a, 0x03, 0x00, 0x08));
+ define_test_client("/TP/GAR/CL/BI-04-C", test_client, service_db_1,
+ &test_read_5,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x0a, 0x03, 0x00),
+ raw_pdu(0x01, 0x0a, 0x03, 0x00, 0x05));
+
+ define_test_client("/TP/GAR/CL/BI-05-C", test_client, service_db_1,
+ &test_read_6,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x0a, 0x03, 0x00),
+ raw_pdu(0x01, 0x0a, 0x03, 0x00, 0x0c));
+
+ define_test_server("/TP/GAR/SR/BV-01-C/small", test_server,
+ ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x0a, 0x03, 0x00),
+ raw_pdu(0x0b, 0x42, 0x6c, 0x75, 0x65, 0x5a));
+
+ define_test_server("/TP/GAR/SR/BV-01-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x0a, 0xc4, 0x00),
+ raw_pdu(0x0b, '1', '1', '1', '1', '1', '2', '2', '2',
+ '2', '2', '3', '3', '3', '3', '3', '4', '4',
+ '4', '4', '4', '5'),
+ raw_pdu(0x0a, 0xca, 0x00),
+ raw_pdu(0x0b, '3', '3', '3', '3', '3', '4', '4', '4',
+ '4', '4', '5', '5', '5', '5', '5', '6', '6',
+ '6', '6', '6', '7', '7'));
+
+ define_test_server("/TP/GAR/SR/BI-02-C/small", test_server,
+ ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x0a, 0x00, 0x00),
+ raw_pdu(0x01, 0x0a, 0x00, 0x00, 0x01));
+
+ define_test_server("/TP/GAR/SR/BI-02-C/large", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x0a, 0x0f, 0xf0),
+ raw_pdu(0x01, 0x0a, 0x0f, 0xf0, 0x01));
+
define_test_att("/TP/GAR/CL/BV-03-C-1", test_read_by_type,
&uuid_char_16, &test_read_by_type_1,
raw_pdu(0x02, 0x00, 0x02),
raw_pdu(0x08, 0x01, 0x00, 0xff, 0xff, 0x0d, 0x2a),
raw_pdu(0x01, 0x08, 0x0b, 0x00, 0x0c));
+ define_test_server("/TP/GAR/SR/BV-03-C/small", test_server, ts_small_db,
+ NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x08, 0x01, 0x00, 0xFF, 0xFF, 0xef, 0xcd, 0xab,
+ 0x89, 0x67, 0x45, 0x23, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0x09, 0xB0, 0x00,
+ 0x00),
+ raw_pdu(0x09, 0x03, 0x15, 0xF0, 0x09),
+ raw_pdu(0x08, 0x01, 0x00, 0xFF, 0xFF, 0x01, 0x2a),
+ raw_pdu(0x09, 0x04, 0x17, 0xF0, 0x00, 0x00));
+
+ define_test_server("/TP/GAR/SR/BV-03-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x08, 0x01, 0x00, 0xFF, 0xFF, 0xef, 0xcd, 0xab,
+ 0x89, 0x67, 0x45, 0x23, 0x01, 0x00,
+ 0x00, 0x00, 0x00, 0xd4, 0xd5, 0x00,
+ 0x00),
+ raw_pdu(0x09, 0x03, 0x76, 0x00, 0x44),
+ raw_pdu(0x08, 0x01, 0x00, 0xFF, 0xFF, 0x02, 0xB0),
+ raw_pdu(0x09, 0x15, 0x25, 0x00, '1', '1', '1', '1', '1',
+ '2', '2', '2', '2', '2', '3', '3', '3', '3',
+ '3', '4', '4', '4', '4'));
+
+ define_test_server("/TP/GAR/SR/BI-06-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x08, 0x01, 0x00, 0xFF, 0xFF, 0x07, 0xB0),
+ raw_pdu(0x01, 0x08, 0x32, 0x00, 0x02));
+
+ define_test_server("/TP/GAR/SR/BI-07-C/small", test_server, ts_small_db,
+ NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x08, 0x01, 0x00, 0xFF, 0xFF, 0xF0, 0x0F),
+ raw_pdu(0x01, 0x08, 0x01, 0x00, 0x0a));
+
+ define_test_server("/TP/GAR/SR/BI-07-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x08, 0x01, 0x00, 0xFF, 0xFF, 0xF0, 0x0F),
+ raw_pdu(0x01, 0x08, 0x01, 0x00, 0x0a));
+
+ define_test_server("/TP/GAR/SR/BI-08-C/small", test_server, ts_small_db,
+ NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x08, 0x02, 0x00, 0x01, 0x00, 0x00, 0x28),
+ raw_pdu(0x01, 0x08, 0x02, 0x00, 0x01));
+
+ define_test_server("/TP/GAR/SR/BI-08-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x08, 0x02, 0x00, 0x01, 0x00, 0x00, 0x28),
+ raw_pdu(0x01, 0x08, 0x02, 0x00, 0x01));
+
+ define_test_server("/TP/GAR/SR/BV-04-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x0C, 0xD3, 0x00, 0x00, 0x00),
+ raw_pdu(0x0D, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+ 0x88, 0x99, 0x00, 0x11, 0x22, 0x33, 0x44, 0x55,
+ 0x66, 0x77, 0x88, 0x99, 0x00, 0x11, 0x22),
+ raw_pdu(0x0C, 0xD3, 0x00, 0x16, 0x00),
+ raw_pdu(0x0D, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88, 0x99,
+ 0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+ 0x88, 0x99, 0x00, 0x11, 0x22, 0x33, 0x44),
+ raw_pdu(0x0C, 0xD3, 0x00, 0x2C, 0x00),
+ raw_pdu(0x0D, 0x55),
+ raw_pdu(0x0C, 0xD3, 0x00, 0x2D, 0x00),
+ raw_pdu(0x0D));
+
+ define_test_server("/TP/GAR/SR/BI-12-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x0C, 0x27, 0x00, 0x00, 0x00),
+ raw_pdu(0x01, 0x0C, 0x27, 0x00, 0x02));
+
+ define_test_server("/TP/GAR/SR/BI-13-C/small", test_server,
+ ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x0C, 0x13, 0xF0, 0xF0, 0x00),
+ raw_pdu(0x01, 0x0C, 0x13, 0xF0, 0x07));
+
+ define_test_server("/TP/GAR/SR/BI-13-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x0C, 0xD3, 0x00, 0xF0, 0x00),
+ raw_pdu(0x01, 0x0C, 0xD3, 0x00, 0x07));
+
+ define_test_server("/TP/GAR/SR/BI-14-C/small", test_server,
+ ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x0C, 0xF0, 0x0F, 0x00, 0x00),
+ raw_pdu(0x01, 0x0C, 0xF0, 0x0F, 0x01));
+
+ define_test_server("/TP/GAR/SR/BI-14-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x0C, 0xF0, 0x0F, 0x00, 0x00),
+ raw_pdu(0x01, 0x0C, 0xF0, 0x0F, 0x01));
+
+ define_test_client("/TP/GAR/CL/BV-04-C", test_client, service_db_1,
+ &test_long_read_1,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x0c, 0x03, 0x00, 0x00, 0x00),
+ raw_pdu(0x0b, 0x01, 0x02, 0x03));
+
+ define_test_client("/TP/GAR/CL/BV-04-C/512B", test_client, service_db_1,
+ &test_long_read_2,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x0c, 0x03, 0x00, 0x00, 0x00),
+ raw_pdu(0x0d, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff),
+ raw_pdu(0x0c, 0x03, 0x00, 0xff, 0x01),
+ raw_pdu(0x0d, 0xff));
+
define_test_client("/TP/GAR/CL/BV-05-C", test_client, service_db_1,
&test_multiple_read_1,
SERVICE_DATA_1_PDUS,
raw_pdu(0x0e, 0x03, 0x00, 0x07, 0x00),
raw_pdu(0x0f, 0x01, 0x02, 0x03));
+ define_test_client("/TP/GAR/CL/BI-12-C", test_client, service_db_1,
+ &test_long_read_3,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x0c, 0x03, 0x00, 0x00, 0x00),
+ raw_pdu(0x01, 0x0c, 0x03, 0x00, 0x02));
+
+ define_test_client("/TP/GAR/CL/BI-13-C", test_client, service_db_1,
+ &test_long_read_4,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x0c, 0x03, 0x00, 0x00, 0x00),
+ raw_pdu(0x01, 0x0c, 0x03, 0x00, 0x07));
+
+ define_test_client("/TP/GAR/CL/BI-14-C", test_client, service_db_1,
+ &test_long_read_5,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x0c, 0x00, 0x00, 0x00, 0x00),
+ raw_pdu(0x01, 0x0c, 0x00, 0x00, 0x01));
+
+ define_test_client("/TP/GAR/CL/BI-15-C", test_client, service_db_1,
+ &test_long_read_6,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x0c, 0x03, 0x00, 0x00, 0x00),
+ raw_pdu(0x01, 0x0c, 0x03, 0x00, 0x08));
+
+ define_test_client("/TP/GAR/CL/BI-16-C", test_client, service_db_1,
+ &test_long_read_7,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x0c, 0x03, 0x00, 0x00, 0x00),
+ raw_pdu(0x01, 0x0c, 0x03, 0x00, 0x05));
+
+ define_test_client("/TP/GAR/CL/BI-17-C", test_client, service_db_1,
+ &test_long_read_8,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x0c, 0x03, 0x00, 0x00, 0x00),
+ raw_pdu(0x01, 0x0c, 0x03, 0x00, 0x0c));
+
define_test_client("/TP/GAR/CL/BI-18-C", test_client, service_db_1,
&test_multiple_read_2,
SERVICE_DATA_1_PDUS,
raw_pdu(0x0e, 0x03, 0x00, 0x07, 0x00),
raw_pdu(0x01, 0x0e, 0x03, 0x00, 0x05));
- define_test_client("/TP/GAR/CL/BI-21-C", test_client, service_db_1,
+ define_test_client("/TP/GAR/CL/BI-22-C", test_client, service_db_1,
&test_multiple_read_6,
SERVICE_DATA_1_PDUS,
raw_pdu(0x0e, 0x03, 0x00, 0x07, 0x00),
raw_pdu(0x01, 0x0e, 0x03, 0x00, 0x0c));
- return g_test_run();
+ define_test_server("/TP/GAR/SR/BV-05-C/small", test_server,
+ ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x0e, 0x15, 0xF0, 0x03, 0x00),
+ raw_pdu(0x0f, 0x09, 'B', 'l', 'u', 'e', 'Z'));
+
+ define_test_server("/TP/GAR/SR/BV-05-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x0e, 0x44, 0x00, 0x06, 0x00, 0xC4, 0x00),
+ raw_pdu(0x0f, 0x11, 0x0B, '1', '1', '1', '1', '1', '2',
+ '2', '2', '2', '2', '3', '3', '3', '3', '3',
+ '4', '4', '4', '4', '4'));
+
+ define_test_server("/TP/GAR/SR/BI-18-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x0e, 0x44, 0x00, 0x06, 0x00, 0x27, 0x00),
+ raw_pdu(0x01, 0x0e, 0x27, 0x00, 0x02));
+
+ define_test_server("/TP/GAR/SR/BI-19-C/small", test_server,
+ ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x0e, 0x15, 0xF0, 0xF0, 0x0F),
+ raw_pdu(0x01, 0x0e, 0xF0, 0x0F, 0x01));
+
+ define_test_server("/TP/GAR/SR/BI-19-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x0e, 0x44, 0x00, 0xF0, 0x0F),
+ raw_pdu(0x01, 0x0e, 0xF0, 0x0F, 0x01));
+
+ define_test_server("/TP/GAR/SR/BV-06-C/small", test_server,
+ ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x0A, 0x05, 0x00),
+ raw_pdu(0x0B, 'M', 'a', 'n', 'u', 'f', 'a', 'c', 't',
+ 'u', 'r', 'e', 'r', ' ', 'N', 'a', 'm', 'e'));
+
+ define_test_server("/TP/GAR/SR/BV-06-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x0A, 0xD4, 0x00),
+ raw_pdu(0x0B, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+ 0x88, 0x99, 0x00, 0x12, 0x34, 0x56, 0x78, 0x90,
+ 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34));
+
+ define_test_server("/TP/GAR/SR/BI-23-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x0A, 0x96, 0x00),
+ raw_pdu(0x01, 0x0A, 0x96, 0x00, 0x02));
+
+ define_test_server("/TP/GAR/SR/BI-24-C/small", test_server,
+ ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x0A, 0xF0, 0x0F),
+ raw_pdu(0x01, 0x0A, 0xF0, 0x0F, 0x01));
+
+ define_test_server("/TP/GAR/SR/BI-24-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x0A, 0xF0, 0x0F),
+ raw_pdu(0x01, 0x0A, 0xF0, 0x0F, 0x01));
+
+ define_test_server("/TP/GAR/SR/BV-07-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x0C, 0xD1, 0x00, 0x00, 0x00),
+ raw_pdu(0x0D, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+ 0x88, 0x99, 0x00, 0x12, 0x34, 0x56, 0x78, 0x90,
+ 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34),
+ raw_pdu(0x0C, 0xD1, 0x00, 0x16, 0x00),
+ raw_pdu(0x0D, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78,
+ 0x90, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+ 0x88, 0x99, 0x00, 0x11, 0x22, 0x33, 0x44),
+ raw_pdu(0x0C, 0xD1, 0x00, 0x2C, 0x00),
+ raw_pdu(0x0D));
+
+ define_test_server("/TP/GAR/SR/BV-08-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x0C, 0xCE, 0x00, 0x00, 0x00),
+ raw_pdu(0x0D, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+ 0x88, 0x99, 0x00, 0x12, 0x34, 0x56, 0x78, 0x90,
+ 0x12, 0x34, 0x56, 0x78, 0x90, 0x12, 0x34),
+ raw_pdu(0x0C, 0xCE, 0x00, 0x16, 0x00),
+ raw_pdu(0x0D, 0x56, 0x78, 0x90, 0x12, 0x34, 0x56, 0x78,
+ 0x90, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
+ 0x88, 0x99, 0x00, 0x11, 0x22, 0x33),
+ raw_pdu(0x0C, 0xCE, 0x00, 0x2B, 0x00),
+ raw_pdu(0x0D));
+
+ define_test_server("/TP/GAR/SR/BI-28-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x0C, 0x96, 0x00, 0x00, 0x00),
+ raw_pdu(0x01, 0x0C, 0x96, 0x00, 0x02));
+
+ define_test_server("/TP/GAR/SR/BI-29-C/small", test_server,
+ ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x0C, 0x05, 0x00, 0xF0, 0x00),
+ raw_pdu(0x01, 0x0C, 0x05, 0x00, 0x07));
+
+ define_test_server("/TP/GAR/SR/BI-29-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x0C, 0xCE, 0x00, 0xF0, 0x00),
+ raw_pdu(0x01, 0x0C, 0xCE, 0x00, 0x07));
+
+ define_test_server("/TP/GAR/SR/BI-30-C/small", test_server,
+ ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x0C, 0xF0, 0x0F, 0x00, 0x00),
+ raw_pdu(0x01, 0x0C, 0xF0, 0x0F, 0x01));
+
+ define_test_server("/TP/GAR/SR/BI-30-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x0C, 0xF0, 0x0F, 0x00, 0x00),
+ raw_pdu(0x01, 0x0C, 0xF0, 0x0F, 0x01));
+
+ define_test_client("/TP/GAN/CL/BV-01-C", test_client, ts_small_db,
+ &test_notification_1,
+ MTU_EXCHANGE_CLIENT_PDUS,
+ SMALL_DB_DISCOVERY_PDUS,
+ raw_pdu(0x12, 0x04, 0x00, 0x03, 0x00),
+ raw_pdu(0x13),
+ raw_pdu(),
+ raw_pdu(0x1B, 0x03, 0x00, 0x01, 0x02, 0x03));
+
+ define_test_server("/TP/GAN/SR/BV-01-C", test_server, ts_small_db,
+ &test_notification_server_1,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x12, 0x04, 0x00, 0x01, 0x00),
+ raw_pdu(0x13),
+ raw_pdu(),
+ raw_pdu(0x1B, 0x03, 0x00, 0x01, 0x02, 0x03));
+
+ define_test_server("/TP/GAI/SR/BV-01-C", test_server, ts_small_db,
+ &test_indication_server_1,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x12, 0x04, 0x00, 0x02, 0x00),
+ raw_pdu(0x13),
+ raw_pdu(),
+ raw_pdu(0x1D, 0x03, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x1E));
+
+ define_test_client("/TP/GAI/CL/BV-01-C", test_client, ts_small_db,
+ &test_indication_1,
+ MTU_EXCHANGE_CLIENT_PDUS,
+ SMALL_DB_DISCOVERY_PDUS,
+ raw_pdu(0x12, 0x04, 0x00, 0x03, 0x00),
+ raw_pdu(0x13),
+ raw_pdu(),
+ raw_pdu(0x1D, 0x03, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x1E));
+
+ define_test_client("/TP/GAR/CL/BV-06-C", test_client, service_db_1,
+ &test_read_7,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x0a, 0x04, 0x00),
+ raw_pdu(0x0b, 0x01, 0x02, 0x03));
+
+ define_test_client("/TP/GAR/CL/BI-23-C", test_client, service_db_1,
+ &test_read_8,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x0a, 0x04, 0x00),
+ raw_pdu(0x01, 0x0a, 0x04, 0x00, 0x02));
+
+ define_test_client("/TP/GAR/CL/BI-24-C", test_client, service_db_1,
+ &test_read_2,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x0a, 0x00, 0x00),
+ raw_pdu(0x01, 0x0a, 0x00, 0x00, 0x01));
+
+ define_test_client("/TP/GAR/CL/BI-25-C", test_client, service_db_1,
+ &test_read_9,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x0a, 0x04, 0x00),
+ raw_pdu(0x01, 0x0a, 0x04, 0x00, 0x08));
+
+ define_test_client("/TP/GAR/CL/BI-26-C", test_client, service_db_1,
+ &test_read_10,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x0a, 0x04, 0x00),
+ raw_pdu(0x01, 0x0a, 0x04, 0x00, 0x05));
+
+ define_test_client("/TP/GAR/CL/BI-27-C", test_client, service_db_1,
+ &test_read_11,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x0a, 0x04, 0x00),
+ raw_pdu(0x01, 0x0a, 0x04, 0x00, 0x0c));
+
+ define_test_client("/TP/GAR/CL/BV-07-C", test_client, service_db_1,
+ &test_long_read_9,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x0c, 0x04, 0x00, 0x00, 0x00),
+ raw_pdu(0x0b, 0x01, 0x02, 0x03));
+
+ define_test_client("/TP/GAR/CL/BV-07-C/512B", test_client, service_db_1,
+ &test_long_read_10,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x0c, 0x04, 0x00, 0x00, 0x00),
+ raw_pdu(0x0d, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff),
+ raw_pdu(0x0c, 0x04, 0x00, 0xff, 0x01),
+ raw_pdu(0x0d, 0xff));
+
+ define_test_client("/TP/GAR/CL/BI-28-C", test_client, service_db_1,
+ &test_long_read_11,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x0c, 0x04, 0x00, 0x00, 0x00),
+ raw_pdu(0x01, 0x0c, 0x04, 0x00, 0x02));
+
+ define_test_client("/TP/GAR/CL/BI-29-C", test_client, service_db_1,
+ &test_long_read_12,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x0c, 0x04, 0x00, 0x00, 0x00),
+ raw_pdu(0x01, 0x0c, 0x04, 0x00, 0x07));
+
+ define_test_client("/TP/GAR/CL/BI-30-C", test_client, service_db_1,
+ &test_long_read_5,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x0c, 0x00, 0x00, 0x00, 0x00),
+ raw_pdu(0x01, 0x0c, 0x00, 0x00, 0x01));
+
+ define_test_client("/TP/GAR/CL/BI-31-C", test_client, service_db_1,
+ &test_long_read_13,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x0c, 0x04, 0x00, 0x00, 0x00),
+ raw_pdu(0x01, 0x0c, 0x04, 0x00, 0x08));
+
+ define_test_client("/TP/GAR/CL/BI-32-C", test_client, service_db_1,
+ &test_long_read_14,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x0c, 0x04, 0x00, 0x00, 0x00),
+ raw_pdu(0x01, 0x0c, 0x04, 0x00, 0x05));
+
+ define_test_client("/TP/GAR/CL/BI-33-C", test_client, service_db_1,
+ &test_long_read_15,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x0c, 0x04, 0x00, 0x00, 0x00),
+ raw_pdu(0x01, 0x0c, 0x04, 0x00, 0x0c));
+
+ define_test_client("/TP/GAR/CL/BI-34-C", test_client, service_db_1,
+ &test_read_12,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x0a, 0x03, 0x00),
+ raw_pdu(0x01, 0x0a, 0x03, 0x00, 0x80));
+
+ define_test_client("/TP/GAR/CL/BI-35-C", test_client, service_db_1,
+ &test_read_12,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x0a, 0x03, 0x00),
+ raw_pdu(0x01, 0x0a, 0x03, 0x00, 0x80));
+
+ define_test_client("/TP/GAW/CL/BV-01-C", test_client, service_db_1,
+ &test_write_without_response_1,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x52, 0x07, 0x00, 0x01, 0x02, 0x03));
+
+ define_test_client("/TP/GAW/CL/BV-02-C", test_client, service_db_1,
+ &test_signed_write_1,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0xd2, 0x07, 0x00, 0x01, 0x02, 0x03, 0x00, 0x00,
+ 0x00, 0x00, 0x31, 0x1f, 0x0a, 0xcd, 0x1c, 0x3a,
+ 0x5b, 0x0a));
+
+ define_test_client("/TP/GAW/CL/BV-02-C/seclevel", test_client,
+ service_db_1, &test_signed_write_seclevel_1,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x52, 0x07, 0x00, 0x01, 0x02, 0x03));
+
+ define_test_client("/TP/GAW/CL/BV-03-C", test_client, service_db_1,
+ &test_write_1,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x12, 0x07, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x13));
+
+ define_test_client("/TP/GAW/CL/BI-02-C", test_client, service_db_1,
+ &test_write_2,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x12, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x01, 0x12, 0x00, 0x00, 0x01));
+
+ define_test_client("/TP/GAW/CL/BI-03-C", test_client, service_db_1,
+ &test_write_3,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x12, 0x07, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x01, 0x12, 0x07, 0x00, 0x03));
+
+ define_test_client("/TP/GAW/CL/BI-04-C", test_client, service_db_1,
+ &test_write_4,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x12, 0x07, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x01, 0x12, 0x07, 0x00, 0x08));
+
+ define_test_client("/TP/GAW/CL/BI-05-C", test_client, service_db_1,
+ &test_write_5,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x12, 0x07, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x01, 0x12, 0x07, 0x00, 0x05));
+
+ define_test_client("/TP/GAW/CL/BI-06-C", test_client, service_db_1,
+ &test_write_6,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x12, 0x07, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x01, 0x12, 0x07, 0x00, 0x0c));
+
+ define_test_server("/TP/GAW/SR/BV-03-C/small", test_server,
+ ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x12, 0x03, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x13));
+
+ define_test_server("/TP/GAW/SR/BV-03-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x12, 0x82, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x13));
+
+ define_test_server("/TP/GAW/SR/BI-02-C/small", test_server,
+ ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x12, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x01, 0x12, 0x00, 0x00, 0x01));
+
+ define_test_server("/TP/GAW/SR/BI-02-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x12, 0x0f, 0xf0, 0x01, 0x02, 0x03),
+ raw_pdu(0x01, 0x12, 0x0f, 0xf0, 0x01));
+
+ define_test_server("/TP/GAW/SR/BI-03-C/small", test_server,
+ ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x12, 0x13, 0xf0, 0x01, 0x02, 0x03),
+ raw_pdu(0x01, 0x12, 0x13, 0xf0, 0x03));
+
+ define_test_server("/TP/GAW/SR/BI-03-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x12, 0x04, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x01, 0x12, 0x04, 0x00, 0x03));
+
+ define_test_client("/TP/GAW/CL/BV-08-C", test_client, service_db_1,
+ &test_write_7,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x12, 0x08, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x13));
+
+ define_test_client("/TP/GAW/CL/BI-20-C", test_client, service_db_1,
+ &test_write_8,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x12, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x01, 0x12, 0x00, 0x00, 0x01));
+
+ define_test_client("/TP/GAW/CL/BI-21-C", test_client, service_db_1,
+ &test_write_9,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x12, 0x08, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x01, 0x12, 0x08, 0x00, 0x03));
+
+ define_test_client("/TP/GAW/CL/BI-22-C", test_client, service_db_1,
+ &test_write_10,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x12, 0x08, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x01, 0x12, 0x08, 0x00, 0x08));
+
+ define_test_client("/TP/GAW/CL/BI-23-C", test_client, service_db_1,
+ &test_write_11,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x12, 0x08, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x01, 0x12, 0x08, 0x00, 0x05));
+
+ define_test_client("/TP/GAW/CL/BI-24-C", test_client, service_db_1,
+ &test_write_12,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x12, 0x08, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x01, 0x12, 0x08, 0x00, 0x0c));
+
+ return tester_run();
}
#include <glib.h>
#include "src/shared/util.h"
+#include "lib/bluetooth.h"
#include "lib/uuid.h"
#include "attrib/att.h"
#include "attrib/gattrib.h"
#endif
#include <glib.h>
-#include <gdbus.h>
+
+#include "gdbus/gdbus.h"
#define SERVICE_NAME "org.bluez.unit.test-gdbus-client"
#define SERVICE_NAME1 "org.bluez.unit.test-gdbus-client1"
#include <stdint.h>
#include <string.h>
-#include <gobex/gobex-apparam.h>
+#include "gobex/gobex.h"
+#include "gobex/gobex-apparam.h"
#include "util.h"
#include <stdint.h>
#include <string.h>
-#include <gobex/gobex-header.h>
+#include "gobex/gobex.h"
+#include "gobex/gobex-header.h"
#include "util.h"
#include <string.h>
#include <errno.h>
-#include <gobex/gobex-packet.h>
+#include "gobex/gobex.h"
+#include "gobex/gobex-packet.h"
#include "util.h"
#include <stdint.h>
#include <fcntl.h>
-#include <gobex/gobex.h>
+#include "gobex/gobex.h"
#include "util.h"
#include <string.h>
#include <stdint.h>
-#include <gobex/gobex.h>
+#include "gobex/gobex.h"
#include "util.h"
queue_destroy(queue, NULL);
}
+static bool match_int(const void *a, const void *b)
+{
+ int i = PTR_TO_INT(a);
+ int j = PTR_TO_INT(b);
+
+ return i == j;
+}
+
+static bool match_ptr(const void *a, const void *b)
+{
+ return a == b;
+}
+
+static void test_remove_all(void)
+{
+ struct queue *queue;
+
+ queue = queue_new();
+ g_assert(queue != NULL);
+
+ g_assert(queue_push_tail(queue, INT_TO_PTR(10)));
+
+ g_assert(queue_remove_all(queue, match_int, INT_TO_PTR(10), NULL) == 1);
+ g_assert(queue_isempty(queue));
+
+ g_assert(queue_push_tail(queue, NULL));
+ g_assert(queue_remove_all(queue, match_ptr, NULL, NULL) == 1);
+ g_assert(queue_isempty(queue));
+
+ g_assert(queue_push_tail(queue, UINT_TO_PTR(0)));
+ g_assert(queue_remove_all(queue, match_int, UINT_TO_PTR(0), NULL) == 1);
+ g_assert(queue_isempty(queue));
+
+ queue_destroy(queue, NULL);
+}
+
int main(int argc, char *argv[])
{
g_test_init(&argc, &argv, NULL);
test_foreach_remove_backward);
g_test_add_func("/queue/destroy_remove", test_destroy_remove);
g_test_add_func("/queue/push_after", test_push_after);
+ g_test_add_func("/queue/remove_all", test_remove_all);
return g_test_run();
}
#include <glib.h>
-#include <bluetooth/bluetooth.h>
-
+#include "lib/bluetooth.h"
#include "lib/uuid.h"
struct uuid_test_data {
#include <glib.h>
+#include "gobex/gobex.h"
+
#include "util.h"
GQuark test_error_quark(void)
*
*/
-#include <gobex/gobex.h>
-
enum {
TEST_ERROR_TIMEOUT,
TEST_ERROR_UNEXPECTED,