summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog30
-rw-r--r--Makefile.am26
-rw-r--r--Makefile.plugins2
-rw-r--r--Makefile.tools25
-rw-r--r--android/Android.mk35
-rw-r--r--android/Makefile.am7
-rw-r--r--android/avctp.c84
-rw-r--r--android/avdtp.c22
-rw-r--r--android/avrcp-lib.c20
-rw-r--r--android/avrcp-lib.h10
-rw-r--r--android/avrcp.c5
-rw-r--r--android/bluetoothd-snoop.c4
-rw-r--r--android/gatt.c79
-rw-r--r--android/hal-gatt.c13
-rw-r--r--android/hal-ipc-api.txt5
-rw-r--r--android/hal-msg.h6
-rw-r--r--android/hal-sco.c20
-rw-r--r--android/hardware/bt_gatt_server.h4
-rw-r--r--android/hidhost.c2
-rw-r--r--android/hog.c39
-rw-r--r--android/hog.h9
-rw-r--r--android/pan.c49
-rw-r--r--android/pics-bnep.txt26
-rw-r--r--android/pixit-bnep.txt30
-rw-r--r--android/pts-bnep.txt57
-rw-r--r--attrib/gatt-service.c4
-rw-r--r--attrib/gatt.h4
-rw-r--r--attrib/gattrib.c19
-rw-r--r--client/gatt.c157
-rw-r--r--client/gatt.h7
-rw-r--r--client/main.c39
-rw-r--r--configure.ac4
-rw-r--r--doc/adapter-api.txt77
-rw-r--r--doc/advertising-api.txt101
-rw-r--r--doc/gatt-api.txt9
-rw-r--r--doc/mgmt-api.txt387
-rw-r--r--doc/settings-storage.txt2
-rw-r--r--doc/test-coverage.txt9
-rw-r--r--emulator/btdev.c54
-rw-r--r--emulator/btdev.h1
-rw-r--r--emulator/hciemu.c3
-rw-r--r--emulator/hciemu.h1
-rw-r--r--emulator/hfp.c1
-rw-r--r--emulator/le.c1
-rw-r--r--emulator/vhci.c1
-rw-r--r--gdbus/client.c9
-rw-r--r--gdbus/gdbus.h6
-rw-r--r--gdbus/object.c59
-rw-r--r--lib/bluetooth.c24
-rw-r--r--lib/bluetooth.h4
-rw-r--r--lib/bnep.h11
-rw-r--r--lib/hci_lib.h13
-rwxr-xr-xlib/mgmt.h136
-rw-r--r--lib/uuid.h3
-rw-r--r--monitor/btsnoop.c124
-rw-r--r--monitor/btsnoop.h5
-rw-r--r--monitor/control.c95
-rw-r--r--monitor/control.h4
-rw-r--r--monitor/l2cap.c9
-rw-r--r--monitor/main.c33
-rw-r--r--monitor/packet.c2
-rw-r--r--obexd/client/mns-tizen.c13
-rw-r--r--obexd/client/pbap.c4
-rw-r--r--obexd/plugins/mas.c9
-rw-r--r--obexd/plugins/opp.c16
-rw-r--r--obexd/plugins/pbap.c11
-rw-r--r--packaging/bluez.spec3
-rw-r--r--plugins/policy.c60
-rw-r--r--plugins/sixaxis.c2
-rw-r--r--profiles/audio/a2dp.c113
-rw-r--r--profiles/audio/a2dp.h5
-rw-r--r--profiles/audio/avctp.c45
-rw-r--r--profiles/audio/avctp.h2
-rw-r--r--profiles/audio/avdtp.c44
-rw-r--r--profiles/audio/avrcp.c442
-rw-r--r--profiles/audio/avrcp.h2
-rw-r--r--profiles/audio/control.c13
-rw-r--r--profiles/audio/control.h1
-rw-r--r--profiles/audio/media.c16
-rw-r--r--profiles/audio/player.c33
-rw-r--r--profiles/audio/player.h4
-rw-r--r--profiles/audio/sink.c8
-rw-r--r--profiles/audio/source.c8
-rw-r--r--profiles/audio/transport.c17
-rw-r--r--profiles/gap/gas.c38
-rw-r--r--profiles/network/bnep.c308
-rw-r--r--profiles/network/bnep.h17
-rw-r--r--profiles/network/connection.c37
-rw-r--r--profiles/network/server.c129
-rwxr-xr-xsrc/adapter.c304
-rwxr-xr-xsrc/adapter.h25
-rw-r--r--src/advertising.c399
-rw-r--r--src/advertising.h25
-rw-r--r--src/attrib-server.c6
-rwxr-xr-xsrc/device.c187
-rw-r--r--src/device.h7
-rw-r--r--src/eir.c1
-rw-r--r--src/gatt-client.c231
-rw-r--r--src/gatt-database.c805
-rw-r--r--src/gatt.c4
-rw-r--r--src/hcid.h1
-rw-r--r--src/main.c11
-rw-r--r--src/main_m.conf10
-rw-r--r--src/main_w.conf7
-rw-r--r--src/profile.c39
-rw-r--r--src/shared/att-types.h10
-rw-r--r--src/shared/att.c29
-rw-r--r--src/shared/att.h1
-rw-r--r--src/shared/btsnoop.c118
-rw-r--r--src/shared/btsnoop.h5
-rw-r--r--src/shared/gatt-client.c362
-rw-r--r--src/shared/gatt-client.h12
-rw-r--r--src/shared/gatt-db.c35
-rw-r--r--src/shared/gatt-db.h12
-rw-r--r--src/shared/gatt-helpers.c136
-rw-r--r--src/shared/tester.c57
-rw-r--r--src/shared/tester.h1
-rw-r--r--test/advertisement-example170
-rw-r--r--tools/bneptest.c710
-rw-r--r--tools/btgatt-client.c218
-rw-r--r--tools/btgatt-server.c23
-rw-r--r--tools/btmgmt.c999
-rw-r--r--tools/btsnoop.c100
-rw-r--r--tools/mgmt-tester.c1123
-rw-r--r--tools/mpris-proxy.c2
-rw-r--r--tools/obexctl.c37
-rw-r--r--tools/oobtest.c451
-rw-r--r--unit/test-avctp.c73
-rw-r--r--unit/test-avdtp.c107
-rw-r--r--unit/test-avrcp.c80
-rw-r--r--unit/test-gatt.c809
-rw-r--r--unit/test-gdbus-client.c55
-rw-r--r--unit/test-hfp.c6
-rw-r--r--unit/test-hog.c440
-rw-r--r--unit/test-sdp.c3
-rw-r--r--unit/test-uhid.c3
136 files changed, 9915 insertions, 1761 deletions
diff --git a/ChangeLog b/ChangeLog
index 2c388284..8aed8be7 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,33 @@
+ver 5.30:
+ Fix compilation error in C++ due to inline function.
+ Fix issue with missing storage of device information.
+ Fix issue with GATT client and gaps in service handles.
+ Fix issue with AVDTP discovery callback crashing.
+ Fix issue with AVCTP channel handling in case of conflicts.
+ Fix issue with AVRCP target and get capabilities command.
+ Add experimental support for LE advertising manager API.
+ Add support for Android 5.1 GATT MTU exchange API.
+
+ver 5.29:
+ Fix issue with AVCTP initial key repeat timeout.
+ Fix issue with Android application disconnect handling.
+ Fix issue with Android support and service notifications.
+ Fix issue with Android support and Exchange MTU Request.
+ Fix issue with Android HFP support and AT+CMER handling.
+ Fix issue with Android HFP support and SLC setup.
+ Fix issue with Android HFP support and call hold status.
+ Fix issue with Android HFP support and indicator handling.
+ Fix issue with Android HFP support and SCO/eSCO disconnection.
+ Fix issue with Android HID over GATT support and battery service.
+ Fix issue with GATT sending Exchange MTU Request for BR/EDR.
+ Fix issue with GATT notification support without CCC.
+ Fix issue with GATT object life-time after disconnects.
+ Fix issue with GATT notification handling API.
+ Add experimental support for GATT client D-Bus API.
+ Add experimental support for GATT server D-Bus API.
+ Add support for Multi Profile Specification.
+ Update Android qualification documentation to PTS 6.0 release.
+
ver 5.28:
Fix issue with GATT device discovery and probing.
Fix issue with bearer selection for dual-mode devices.
diff --git a/Makefile.am b/Makefile.am
index 70f31c97..f6c4ac83 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -80,7 +80,7 @@ include_HEADERS += $(lib_headers)
lib_LTLIBRARIES += lib/libbluetooth.la
lib_libbluetooth_la_SOURCES = $(lib_headers) $(lib_sources)
-lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 21:1:18
+lib_libbluetooth_la_LDFLAGS = $(AM_LDFLAGS) -version-info 21:3:18
lib_libbluetooth_la_DEPENDENCIES = $(local_headers)
endif
@@ -130,7 +130,7 @@ src_libshared_mainloop_la_SOURCES = $(shared_sources) \
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/gattrib.h attrib/gattrib.c \
attrib/gatt-service.h attrib/gatt-service.c
btio_sources = btio/btio.h btio/btio.c
@@ -175,6 +175,7 @@ src_bluetoothd_SOURCES = $(builtin_sources) \
src/uinput.h \
src/plugin.h src/plugin.c \
src/storage.h src/storage.c \
+ src/advertising.h src/advertising.c \
src/agent.h src/agent.c \
src/error.h src/error.c \
src/adapter.h src/adapter.c \
@@ -252,11 +253,13 @@ EXTRA_DIST += doc/mgmt-api.txt \
doc/adapter-api.txt doc/device-api.txt \
doc/agent-api.txt doc/profile-api.txt \
doc/network-api.txt doc/media-api.txt \
- doc/health-api.txt doc/sap-api.txt
+ doc/health-api.txt doc/sap-api.txt \
+ doc/input-api.txt
EXTRA_DIST += doc/alert-api.txt \
doc/proximity-api.txt doc/heartrate-api.txt \
- doc/thermometer-api.txt doc/cyclingspeed-api.txt
+ doc/thermometer-api.txt doc/cyclingspeed-api.txt \
+ doc/gatt-api.txt doc/advertising-api.txt
EXTRA_DIST += doc/obex-api.txt doc/obex-agent-api.txt
@@ -392,6 +395,21 @@ unit_test_gatt_SOURCES = unit/test-gatt.c
unit_test_gatt_LDADD = src/libshared-glib.la \
lib/libbluetooth-internal.la @GLIB_LIBS@
+unit_tests += unit/test-hog
+
+unit_test_hog_SOURCES = unit/test-hog.c \
+ $(btio_sources) \
+ android/hog.h android/hog.c \
+ android/scpp.h android/scpp.c \
+ android/bas.h android/bas.c \
+ android/dis.h android/dis.c \
+ src/log.h src/log.c \
+ attrib/att.h attrib/att.c \
+ attrib/gatt.h attrib/gatt.c \
+ attrib/gattrib.h attrib/gattrib.c
+unit_test_hog_LDADD = src/libshared-glib.la \
+ lib/libbluetooth-internal.la @GLIB_LIBS@
+
unit_tests += unit/test-gattrib
unit_test_gattrib_SOURCES = unit/test-gattrib.c attrib/gattrib.c $(btio_sources) src/log.h src/log.c
diff --git a/Makefile.plugins b/Makefile.plugins
index b8508789..38f27ece 100644
--- a/Makefile.plugins
+++ b/Makefile.plugins
@@ -97,6 +97,7 @@ builtin_sources += profiles/health/mcap.h profiles/health/mcap.c \
endif
endif
+if TIZEN_HEALTH_PLUGIN
builtin_modules += gap
builtin_sources += profiles/gap/gas.c
@@ -105,6 +106,7 @@ builtin_sources += profiles/scanparam/scan.c
builtin_modules += deviceinfo
builtin_sources += profiles/deviceinfo/deviceinfo.c
+endif
if EXPERIMENTAL
if TIZEN_UNUSED_PLUGIN
diff --git a/Makefile.tools b/Makefile.tools
index d171dd2f..44f4f57f 100644
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -122,7 +122,8 @@ 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/hex2hcd tools/mpris-proxy
+ tools/bluemoon tools/hex2hcd tools/mpris-proxy \
+ tools/btsnoop
tools_hciattach_SOURCES = tools/hciattach.c tools/hciattach.h \
tools/hciattach_st.c \
@@ -204,6 +205,9 @@ tools_mcaptest_SOURCES = tools/mcaptest.c \
profiles/health/mcap.h profiles/health/mcap.c
tools_mcaptest_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@ -lrt
+tools_btsnoop_SOURCES = tools/btsnoop.c
+tools_btsnoop_LDADD = src/libshared-mainloop.la
+
dist_man_MANS += tools/hciattach.1 tools/hciconfig.1 \
tools/hcitool.1 tools/hcidump.1 \
tools/rfcomm.1 tools/rctest.1 tools/l2ping.1 \
@@ -232,10 +236,10 @@ noinst_PROGRAMS += tools/bdaddr tools/avinfo tools/avtest \
tools/scotest tools/amptest tools/hwdb \
tools/hcieventmask tools/hcisecfilter \
tools/btmgmt tools/btinfo tools/btattach \
- tools/btsnoop tools/btproxy \
- tools/btiotest tools/mcaptest tools/cltest \
- tools/oobtest tools/seq2bseq tools/ibeacon \
- tools/btgatt-client tools/btgatt-server
+ tools/btproxy \
+ tools/btiotest tools/bneptest 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@
@@ -262,9 +266,6 @@ tools_btinfo_LDADD = src/libshared-mainloop.la
tools_btattach_SOURCES = tools/btattach.c monitor/bt.h
tools_btattach_LDADD = src/libshared-mainloop.la
-tools_btsnoop_SOURCES = tools/btsnoop.c
-tools_btsnoop_LDADD = src/libshared-mainloop.la
-
tools_btproxy_SOURCES = tools/btproxy.c monitor/bt.h
tools_btproxy_LDADD = src/libshared-mainloop.la
@@ -277,6 +278,12 @@ tools_mcaptest_SOURCES = tools/mcaptest.c \
profiles/health/mcap.h profiles/health/mcap.c
tools_mcaptest_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+tools_bneptest_SOURCES = tools/bneptest.c \
+ btio/btio.h btio/btio.c \
+ src/log.h src/log.c \
+ profiles/network/bnep.h profiles/network/bnep.c
+tools_bneptest_LDADD = lib/libbluetooth-internal.la @GLIB_LIBS@
+
tools_cltest_SOURCES = tools/cltest.c
tools_cltest_LDADD = lib/libbluetooth-internal.la src/libshared-mainloop.la
@@ -372,4 +379,4 @@ test_scripts += test/sap_client.py test/bluezutils.py \
test/service-ftp.xml test/simple-player test/test-nap \
test/test-heartrate test/test-alert test/test-hfp \
test/test-cyclingspeed test/opp-client test/ftp-client \
- test/pbap-client test/map-client
+ test/pbap-client test/map-client test/advertisement-example
diff --git a/android/Android.mk b/android/Android.mk
index 6c0eda82..f2188059 100644
--- a/android/Android.mk
+++ b/android/Android.mk
@@ -256,6 +256,41 @@ LOCAL_MODULE := mcaptest
include $(BUILD_EXECUTABLE)
#
+# bneptest
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := \
+ bluez/src/log.c \
+ bluez/btio/btio.c \
+ bluez/lib/bluetooth.c \
+ bluez/lib/hci.c \
+ bluez/profiles/network/bnep.c \
+ bluez/tools/bneptest.c \
+
+LOCAL_C_INCLUDES := \
+ $(call include-path-for, glib) \
+ $(call include-path-for, glib)/glib \
+
+LOCAL_C_INCLUDES += \
+ $(LOCAL_PATH)/bluez \
+
+LOCAL_CFLAGS := $(BLUEZ_COMMON_CFLAGS)
+
+LOCAL_SHARED_LIBRARIES := \
+ libglib \
+
+LOCAL_STATIC_LIBRARIES := \
+ bluetooth-headers \
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_OPTIONAL_EXECUTABLES)
+LOCAL_MODULE_TAGS := debug
+LOCAL_MODULE := bneptest
+
+include $(BUILD_EXECUTABLE)
+
+#
# avdtptest
#
diff --git a/android/Makefile.am b/android/Makefile.am
index 676daab3..cb32bc3d 100644
--- a/android/Makefile.am
+++ b/android/Makefile.am
@@ -1,6 +1,6 @@
if ANDROID
-AM_CFLAGS += -DANDROID_VERSION=0x050000
+AM_CFLAGS += -DANDROID_VERSION=0x050100
android_plugindir = $(abs_top_srcdir)/android/.libs
@@ -258,6 +258,7 @@ EXTRA_DIST += android/Android.mk android/README \
android/pics-dis.txt \
android/pics-avdtp.txt \
android/pics-gavdp.txt \
+ android/pics-bnep.txt \
android/pixit-l2cap.txt \
android/pixit-gap.txt \
android/pixit-did.txt \
@@ -285,6 +286,7 @@ EXTRA_DIST += android/Android.mk android/README \
android/pixit-avdtp.txt \
android/pixit-gavdp.txt \
android/pixit-sdp.txt \
+ android/pixit-bnep.txt \
android/pts-rfcomm.txt \
android/pts-spp.txt \
android/pts-l2cap.txt \
@@ -311,4 +313,5 @@ EXTRA_DIST += android/Android.mk android/README \
android/pts-dis.txt \
android/pts-avdtp.txt \
android/pts-gavdp.txt \
- android/pts-sdp.txt
+ android/pts-sdp.txt \
+ android/pts-bnep.txt
diff --git a/android/avctp.c b/android/avctp.c
index 758dbd4e..6aa64cf0 100644
--- a/android/avctp.c
+++ b/android/avctp.c
@@ -162,6 +162,7 @@ struct key_pressed {
};
struct avctp {
+ unsigned int ref;
int uinput;
unsigned int passthrough_id;
@@ -708,6 +709,47 @@ static gboolean process_queue(void *user_data)
}
+static struct avctp *avctp_ref(struct avctp *session)
+{
+ __sync_fetch_and_add(&session->ref, 1);
+
+ DBG("%p: ref=%d", session, session->ref);
+
+ return session;
+}
+
+static void avctp_unref(struct avctp *session)
+{
+ DBG("%p: ref=%d", session, session->ref);
+
+ if (__sync_sub_and_fetch(&session->ref, 1))
+ return;
+
+ if (session->browsing)
+ avctp_channel_destroy(session->browsing);
+
+ if (session->control)
+ avctp_channel_destroy(session->control);
+
+ if (session->destroy)
+ session->destroy(session->data);
+
+ g_free(session->handler);
+
+ if (session->key.timer > 0)
+ g_source_remove(session->key.timer);
+
+ if (session->uinput >= 0) {
+ DBG("AVCTP: closing uinput");
+
+ ioctl(session->uinput, UI_DEV_DESTROY);
+ close(session->uinput);
+ session->uinput = -1;
+ }
+
+ g_free(session);
+}
+
static void control_response(struct avctp_channel *control,
struct avctp_header *avctp,
struct avc_header *avc,
@@ -733,6 +775,8 @@ static void control_response(struct avctp_channel *control,
control);
}
+ avctp_ref(control->session);
+
for (l = control->processed; l; l = l->next) {
p = l->data;
req = p->data;
@@ -744,13 +788,15 @@ static void control_response(struct avctp_channel *control,
avc->subunit_type,
operands, operand_count,
req->user_data))
- return;
+ break;
control->processed = g_slist_remove(control->processed, p);
pending_destroy(p, NULL);
- return;
+ break;
}
+
+ avctp_unref(control->session);
}
static void browsing_response(struct avctp_channel *browsing,
@@ -777,6 +823,8 @@ static void browsing_response(struct avctp_channel *browsing,
browsing);
}
+ avctp_ref(browsing->session);
+
for (l = browsing->processed; l; l = l->next) {
p = l->data;
req = p->data;
@@ -786,13 +834,15 @@ static void browsing_response(struct avctp_channel *browsing,
if (req->func && req->func(browsing->session, operands,
operand_count, req->user_data))
- return;
+ break;
browsing->processed = g_slist_remove(browsing->processed, p);
pending_destroy(p, NULL);
- return;
+ break;
}
+
+ avctp_unref(browsing->session);
}
static gboolean session_browsing_cb(GIOChannel *chan, GIOCondition cond,
@@ -1563,7 +1613,7 @@ struct avctp *avctp_new(int fd, size_t imtu, size_t omtu, uint16_t version)
control->watch = g_io_add_watch(session->control->io, cond,
(GIOFunc) session_cb, session);
- return session;
+ return avctp_ref(session);
}
int avctp_connect_browsing(struct avctp *session, int fd, size_t imtu,
@@ -1599,27 +1649,5 @@ void avctp_shutdown(struct avctp *session)
if (!session)
return;
- if (session->browsing)
- avctp_channel_destroy(session->browsing);
-
- if (session->control)
- avctp_channel_destroy(session->control);
-
- if (session->destroy)
- session->destroy(session->data);
-
- g_free(session->handler);
-
- if (session->key.timer > 0)
- g_source_remove(session->key.timer);
-
- if (session->uinput >= 0) {
- DBG("AVCTP: closing uinput");
-
- ioctl(session->uinput, UI_DEV_DESTROY);
- close(session->uinput);
- session->uinput = -1;
- }
-
- g_free(session);
+ avctp_unref(session);
}
diff --git a/android/avdtp.c b/android/avdtp.c
index e4fd2b78..7e61280c 100644
--- a/android/avdtp.c
+++ b/android/avdtp.c
@@ -784,6 +784,10 @@ static void handle_unanswered_req(struct avdtp *session,
struct avdtp_local_sep *lsep;
struct avdtp_error err;
+ if (!session->req->timeout)
+ /* Request is in process */
+ return;
+
if (session->req->signal_id == AVDTP_ABORT) {
/* Avoid freeing the Abort request here */
DBG("handle_unanswered_req: Abort req, returning");
@@ -2017,6 +2021,9 @@ static gboolean session_cb(GIOChannel *chan, GIOCondition cond,
break;
}
+ /* Take a reference to protect against callback destroying session */
+ avdtp_ref(session);
+
if (session->in.message_type == AVDTP_MSG_TYPE_COMMAND) {
if (!avdtp_parse_cmd(session, session->in.transaction,
session->in.signal_id,
@@ -2031,21 +2038,25 @@ static gboolean session_cb(GIOChannel *chan, GIOCondition cond,
goto next;
}
+ avdtp_unref(session);
return TRUE;
}
if (session->req == NULL) {
error("No pending request, ignoring message");
+ avdtp_unref(session);
return TRUE;
}
if (header->transaction != session->req->transaction) {
error("Transaction label doesn't match");
+ avdtp_unref(session);
return TRUE;
}
if (session->in.signal_id != session->req->signal_id) {
error("Response signal doesn't match");
+ avdtp_unref(session);
return TRUE;
}
@@ -2085,7 +2096,10 @@ next:
pending_req_free(session->req);
session->req = NULL;
- process_queue(session);
+ if (session->ref > 1)
+ process_queue(session);
+
+ avdtp_unref(session);
return TRUE;
@@ -2508,12 +2522,12 @@ static gboolean avdtp_set_configuration_resp(struct avdtp *session,
{
struct avdtp_local_sep *sep = stream->lsep;
+ avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED);
+
if (sep->cfm && sep->cfm->set_configuration)
sep->cfm->set_configuration(session, sep, stream, NULL,
sep->user_data);
- avdtp_sep_set_state(session, sep, AVDTP_STATE_CONFIGURED);
-
return TRUE;
}
@@ -2794,9 +2808,9 @@ static gboolean avdtp_parse_rej(struct avdtp *session,
error("START request rejected: %s (%d)",
avdtp_strerror(&err), err.err.error_code);
if (sep && sep->cfm && sep->cfm->start) {
+ stream->starting = FALSE;
sep->cfm->start(session, sep, stream, &err,
sep->user_data);
- stream->starting = FALSE;
}
return TRUE;
case AVDTP_SUSPEND:
diff --git a/android/avrcp-lib.c b/android/avrcp-lib.c
index 23dea623..4edfd0e9 100644
--- a/android/avrcp-lib.c
+++ b/android/avrcp-lib.c
@@ -367,6 +367,10 @@ void avrcp_shutdown(struct avrcp *session)
avctp_unregister_passthrough_handler(session->conn,
session->passthrough_id);
+ if (session->browsing_id > 0)
+ avctp_unregister_browsing_pdu_handler(session->conn,
+ session->browsing_id);
+
/* clear destroy callback that would call shutdown again */
avctp_set_destroy_cb(session->conn, NULL, NULL);
avctp_shutdown(session->conn);
@@ -2443,8 +2447,12 @@ static int parse_attribute_list(uint8_t *params, uint16_t params_len,
if (item->len > 0) {
text[i] = g_strndup(item->data, item->len);
+ attrs[i] = item->attr;
params_len -= item->len;
params += item->len;
+ } else {
+ text[i] = NULL;
+ attrs[i] = 0;
}
}
@@ -2457,6 +2465,12 @@ fail:
return -EPROTO;
}
+static void free_attribute_list(uint8_t number, char **text)
+{
+ while(number--)
+ g_free(text[number]);
+}
+
static int parse_elements(struct avrcp_header *pdu, uint8_t *number,
uint32_t *attrs, char **text)
{
@@ -2531,6 +2545,9 @@ done:
player->cfm->get_element_attributes(session, err, number, attrs, text,
player->user_data);
+ if (err == 0)
+ free_attribute_list(number, text);
+
return FALSE;
}
@@ -2855,6 +2872,9 @@ done:
player->cfm->get_item_attributes(session, err, number, attrs, text,
player->user_data);
+ if (err == 0)
+ free_attribute_list(number, text);
+
return FALSE;
}
diff --git a/android/avrcp-lib.h b/android/avrcp-lib.h
index 8e6ee40f..add7739c 100644
--- a/android/avrcp-lib.h
+++ b/android/avrcp-lib.h
@@ -126,6 +126,16 @@
#define AVRCP_MEDIA_SEARCH 0x02
#define AVRCP_MEDIA_NOW_PLAYING 0x03
+/* SDP features */
+#define AVRCP_FEATURE_CATEGORY_1 0x0001
+#define AVRCP_FEATURE_CATEGORY_2 0x0002
+#define AVRCP_FEATURE_CATEGORY_3 0x0004
+#define AVRCP_FEATURE_CATEGORY_4 0x0008
+#define AVRCP_FEATURE_PLAYER_SETTINGS 0x0010
+#define AVRCP_FEATURE_GROUP_NAVIGATION 0x0020
+#define AVRCP_FEATURE_BROWSING 0x0040
+#define AVRCP_FEATURE_MULTIPLE_PLAYERS 0x0080
+
/* Company IDs for vendor dependent commands */
#define IEEEID_BTSIG 0x001958
diff --git a/android/avrcp.c b/android/avrcp.c
index a0d412d8..bcb42284 100644
--- a/android/avrcp.c
+++ b/android/avrcp.c
@@ -49,11 +49,6 @@
#define L2CAP_PSM_AVCTP 0x17
-#define AVRCP_FEATURE_CATEGORY_1 0x0001
-#define AVRCP_FEATURE_CATEGORY_2 0x0002
-#define AVRCP_FEATURE_CATEGORY_3 0x0004
-#define AVRCP_FEATURE_CATEGORY_4 0x0008
-
static bdaddr_t adapter_addr;
static uint32_t record_tg_id = 0;
static uint32_t record_ct_id = 0;
diff --git a/android/bluetoothd-snoop.c b/android/bluetoothd-snoop.c
index 035875a0..ff2b7e51 100644
--- a/android/bluetoothd-snoop.c
+++ b/android/bluetoothd-snoop.c
@@ -145,7 +145,11 @@ static int open_monitor(const char *path)
struct sockaddr_hci addr;
int opt = 1;
+#ifdef __TIZEN_PATCH__
+ snoop = btsnoop_create(path, BTSNOOP_TYPE_HCI, -1, -1);
+#else
snoop = btsnoop_create(path, BTSNOOP_TYPE_HCI);
+#endif
if (!snoop)
return -1;
diff --git a/android/gatt.c b/android/gatt.c
index 9c2a2803..4da959fe 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -941,6 +941,55 @@ static bool get_local_mtu(struct gatt_device *dev, uint16_t *mtu)
return true;
}
+static void notify_client_mtu_change(struct app_connection *conn, bool success)
+{
+ struct hal_ev_gatt_client_configure_mtu ev;
+ size_t mtu;
+
+ g_attrib_get_buffer(conn->device->attrib, &mtu);
+
+ ev.conn_id = conn->id;
+ ev.status = success ? GATT_SUCCESS : GATT_FAILURE;
+ ev.mtu = mtu;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_CLIENT_CONFIGURE_MTU, sizeof(ev), &ev);
+}
+
+static void notify_server_mtu(struct app_connection *conn)
+{
+ struct hal_ev_gatt_server_mtu_changed ev;
+ size_t mtu;
+
+ g_attrib_get_buffer(conn->device->attrib, &mtu);
+
+ ev.conn_id = conn->id;
+ ev.mtu = mtu;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_SERVER_MTU_CHANGED, sizeof(ev), &ev);
+}
+
+static void notify_mtu_change(void *data, void *user_data)
+{
+ struct gatt_device *device = user_data;
+ struct app_connection *conn = data;
+
+ if (conn->device != device)
+ return;
+
+ switch (conn->app->type) {
+ case GATT_CLIENT:
+ notify_client_mtu_change(conn, true);
+ break;
+ case GATT_SERVER:
+ notify_server_mtu(conn);
+ break;
+ default:
+ break;
+ }
+}
+
static bool update_mtu(struct gatt_device *device, uint16_t rmtu)
{
uint16_t mtu, lmtu;
@@ -965,6 +1014,8 @@ static bool update_mtu(struct gatt_device *device, uint16_t rmtu)
return false;
}
+ queue_foreach(app_connections, notify_mtu_change, device);
+
return true;
}
@@ -1600,6 +1651,11 @@ reply:
data.status = status;
queue_foreach(app_connections, notify_app_connect_status_by_device,
&data);
+
+ /* For BR/EDR notify about MTU since it is not negotiable*/
+ if (cid != ATT_CID)
+ queue_foreach(app_connections, notify_mtu_change, dev);
+
device_unref(dev);
/* Check if we should restart scan */
@@ -5676,14 +5732,31 @@ static void handle_client_scan_filter_enable(const void *buf, uint16_t len)
static void handle_client_configure_mtu(const void *buf, uint16_t len)
{
const struct hal_cmd_gatt_client_configure_mtu *cmd = buf;
+ static struct app_connection *conn;
+ uint8_t status;
- DBG("conn_id %u", cmd->conn_id);
+ DBG("conn_id %u mtu %d", cmd->conn_id, cmd->mtu);
- /* TODO */
+ conn = find_connection_by_id(cmd->conn_id);
+ if (!conn) {
+ status = HAL_STATUS_FAILED;
+ goto failed;
+ }
+ /*
+ * currently MTU is always exchanged on connection, just report current
+ * value
+ *
+ * TODO figure out when send failed status in notification
+ * TODO should we fail for BR/EDR?
+ */
+ notify_client_mtu_change(conn, false);
+ status = HAL_STATUS_SUCCESS;
+
+failed:
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
HAL_OP_GATT_CLIENT_CONFIGURE_MTU,
- HAL_STATUS_UNSUPPORTED);
+ status);
}
static void handle_client_conn_param_update(const void *buf, uint16_t len)
diff --git a/android/hal-gatt.c b/android/hal-gatt.c
index 438ad619..f7217c78 100644
--- a/android/hal-gatt.c
+++ b/android/hal-gatt.c
@@ -641,6 +641,16 @@ static void handle_server_congestion(void *buf, uint16_t len, int fd)
#endif
}
+static void handle_server_mtu_changed(void *buf, uint16_t len, int fd)
+{
+#if ANDROID_VERSION >= PLATFORM_VER(5, 1, 0)
+ struct hal_ev_gatt_server_mtu_changed *ev = buf;
+
+ if (cbs->server->mtu_changed_cb)
+ cbs->server->mtu_changed_cb(ev->conn_id, ev->mtu);
+#endif
+}
+
/*
* handlers will be called from notification thread context,
* index in table equals to 'opcode - HAL_MINIMUM_EVENT'
@@ -784,6 +794,9 @@ static const struct hal_ipc_handler ev_handlers[] = {
/* HAL_EV_GATT_SERVER_CONGESTION */
{ handle_server_congestion, false,
sizeof(struct hal_ev_gatt_server_congestion) },
+ /* HAL_EV_GATT_SERVER_MTU_CHANGED */
+ { handle_server_mtu_changed, false,
+ sizeof(struct hal_ev_gatt_server_mtu_changed) },
};
/* Client API */
diff --git a/android/hal-ipc-api.txt b/android/hal-ipc-api.txt
index 77f2f57e..503b47ad 100644
--- a/android/hal-ipc-api.txt
+++ b/android/hal-ipc-api.txt
@@ -2329,6 +2329,11 @@ Notifications:
Notification parameters: Connection ID (4 octets)
Congested (1 octet)
+ Opcode 0xb0 - Server MTU Changed notification
+
+ Notification parameters: Connection ID (4 octets)
+ MTU (4 octets)
+
Bluetooth Handsfree Client HAL (ID 10)
======================================
diff --git a/android/hal-msg.h b/android/hal-msg.h
index 0ec07c72..698f45ad 100644
--- a/android/hal-msg.h
+++ b/android/hal-msg.h
@@ -2092,6 +2092,12 @@ struct hal_ev_gatt_server_congestion {
uint8_t congested;
} __attribute__((packed));
+#define HAL_EV_GATT_SERVER_MTU_CHANGED 0xb0
+struct hal_ev_gatt_server_mtu_changed {
+ int32_t conn_id;
+ int32_t mtu;
+} __attribute__((packed));
+
#define HAL_GATT_PERMISSION_READ 0x0001
#define HAL_GATT_PERMISSION_READ_ENCRYPTED 0x0002
#define HAL_GATT_PERMISSION_READ_ENCRYPTED_MITM 0x0004
diff --git a/android/hal-sco.c b/android/hal-sco.c
index 380b2a89..2c95866e 100644
--- a/android/hal-sco.c
+++ b/android/hal-sco.c
@@ -1054,17 +1054,23 @@ static ssize_t in_read(struct audio_stream_in *stream, void *buffer,
size_t bytes)
{
struct sco_stream_in *in = (struct sco_stream_in *) stream;
-#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
- size_t frame_size = audio_stream_in_frame_size(&in->stream);
-#else
- size_t frame_size = audio_stream_frame_size(&stream->common);
-#endif
- size_t frame_num = bytes / frame_size;
- size_t input_frame_num = frame_num;
+ size_t frame_size, frame_num, input_frame_num;
void *read_buf = buffer;
size_t total = bytes;
int ret;
+#if ANDROID_VERSION >= PLATFORM_VER(5, 0, 0)
+ frame_size = audio_stream_in_frame_size(&in->stream);
+#else
+ frame_size = audio_stream_frame_size(&stream->common);
+#endif
+
+ if (!frame_size)
+ return -1;
+
+ frame_num = bytes / frame_size;
+ input_frame_num = frame_num;
+
DBG("Read from fd %d bytes %zu", sco_fd, bytes);
if (ipc_get_sco_fd(&in->bd_addr) != SCO_STATUS_SUCCESS)
diff --git a/android/hardware/bt_gatt_server.h b/android/hardware/bt_gatt_server.h
index 2b1de278..0d6cc1e8 100644
--- a/android/hardware/bt_gatt_server.h
+++ b/android/hardware/bt_gatt_server.h
@@ -117,6 +117,9 @@ typedef void (*indication_sent_callback)(int conn_id, int status);
*/
typedef void (*congestion_callback)(int conn_id, bool congested);
+/** Callback invoked when the MTU for a given connection changes */
+typedef void (*mtu_changed_callback)(int conn_id, int mtu);
+
typedef struct {
register_server_callback register_server_cb;
connection_callback connection_cb;
@@ -133,6 +136,7 @@ typedef struct {
response_confirmation_callback response_confirmation_cb;
indication_sent_callback indication_sent_cb;
congestion_callback congestion_cb;
+ mtu_changed_callback mtu_changed_cb;
} btgatt_server_callbacks_t;
/** Represents the standard BT-GATT server interface. */
diff --git a/android/hidhost.c b/android/hidhost.c
index 2e589f4c..729b8847 100644
--- a/android/hidhost.c
+++ b/android/hidhost.c
@@ -817,7 +817,7 @@ static void hog_conn_cb(const bdaddr_t *addr, int err, void *attrib)
if (!dev->hog) {
/* TODO: Get device details and primary */
- dev->hog = bt_hog_new("bluez-input-device", dev->vendor,
+ dev->hog = bt_hog_new_default("bluez-input-device", dev->vendor,
dev->product, dev->version, NULL);
if (!dev->hog) {
error("HoG: unable to create session");
diff --git a/android/hog.c b/android/hog.c
index 88a5460a..ff77bb37 100644
--- a/android/hog.c
+++ b/android/hog.c
@@ -87,6 +87,7 @@ struct bt_hog {
GAttrib *attrib;
GSList *reports;
struct bt_uhid *uhid;
+ int uhid_fd;
gboolean has_report_id;
uint16_t bcdhid;
uint8_t bcountrycode;
@@ -1170,8 +1171,16 @@ static void hog_free(void *data)
g_free(hog);
}
-struct bt_hog *bt_hog_new(const char *name, uint16_t vendor, uint16_t product,
- uint16_t version, void *primary)
+struct bt_hog *bt_hog_new_default(const char *name, uint16_t vendor,
+ uint16_t product, uint16_t version,
+ void *primary)
+{
+ return bt_hog_new(-1, name, vendor, product, version, primary);
+}
+
+struct bt_hog *bt_hog_new(int fd, const char *name, uint16_t vendor,
+ uint16_t product, uint16_t version,
+ void *primary)
{
struct bt_hog *hog;
@@ -1180,23 +1189,17 @@ struct bt_hog *bt_hog_new(const char *name, uint16_t vendor, uint16_t product,
return NULL;
hog->gatt_op = queue_new();
- if (!hog->gatt_op) {
- hog_free(hog);
- 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) {
+ if (fd < 0)
+ hog->uhid = bt_uhid_new_default();
+ else
+ hog->uhid = bt_uhid_new(fd);
+
+ hog->uhid_fd = fd;
+
+ if (!hog->gatt_op || !hog->bas || !hog->uhid) {
hog_free(hog);
- queue_destroy(hog->gatt_op, NULL);
- queue_destroy(hog->bas, NULL);
return NULL;
}
@@ -1317,8 +1320,8 @@ static void hog_attach_hog(struct bt_hog *hog, struct gatt_primary *primary)
return;
}
- instance = bt_hog_new(hog->name, hog->vendor, hog->product,
- hog->version, primary);
+ instance = bt_hog_new(hog->uhid_fd, hog->name, hog->vendor,
+ hog->product, hog->version, primary);
if (!instance)
return;
diff --git a/android/hog.h b/android/hog.h
index ddb2ceac..2a9b899c 100644
--- a/android/hog.h
+++ b/android/hog.h
@@ -23,8 +23,13 @@
struct bt_hog;
-struct bt_hog *bt_hog_new(const char *name, uint16_t vendor, uint16_t product,
- uint16_t version, void *primary);
+struct bt_hog *bt_hog_new_default(const char *name, uint16_t vendor,
+ uint16_t product, uint16_t version,
+ void *primary);
+
+struct bt_hog *bt_hog_new(int fd, const char *name, uint16_t vendor,
+ uint16_t product, uint16_t version,
+ void *primary);
struct bt_hog *bt_hog_ref(struct bt_hog *hog);
void bt_hog_unref(struct bt_hog *hog);
diff --git a/android/pan.c b/android/pan.c
index 2afc92a1..a1b173b5 100644
--- a/android/pan.c
+++ b/android/pan.c
@@ -324,14 +324,13 @@ static void connect_cb(GIOChannel *chan, GError *err, gpointer data)
if (!dev->session)
goto fail;
- perr = bnep_connect(dev->session, bnep_conn_cb, dev);
+ perr = bnep_connect(dev->session, bnep_conn_cb, bnep_disconn_cb, dev,
+ dev);
if (perr < 0) {
error("bnep connect req failed: %s", strerror(-perr));
goto fail;
}
- bnep_set_disconnect(dev->session, bnep_disconn_cb, dev);
-
if (dev->io) {
g_io_channel_unref(dev->io);
dev->io = NULL;
@@ -463,8 +462,6 @@ static gboolean nap_setup_cb(GIOChannel *chan, GIOCondition cond,
{
struct pan_device *dev = user_data;
uint8_t packet[BNEP_MTU];
- struct bnep_setup_conn_req *req = (void *) packet;
- uint16_t src_role, dst_role, rsp = BNEP_CONN_NOT_ALLOWED;
int sk, n, err;
if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) {
@@ -474,51 +471,38 @@ static gboolean nap_setup_cb(GIOChannel *chan, GIOCondition cond,
sk = g_io_channel_unix_get_fd(chan);
+#ifndef __TIZEN_PATCH__
/* Reading BNEP_SETUP_CONNECTION_REQUEST_MSG */
n = read(sk, packet, sizeof(packet));
+#else
+ /*
+ * BNEP_SETUP_CONNECTION_REQUEST_MSG should be read and left in case
+ * of kernel setup connection msg handling.
+ */
+ n = recv(sk, packet, sizeof(packet), MSG_PEEK);
+#endif
+
if (n < 0) {
error("read(): %s(%d)", strerror(errno), errno);
goto failed;
}
- /* Highest known control command id BNEP_FILTER_MULT_ADDR_RSP 0x06 */
- if (req->type == BNEP_CONTROL &&
- req->ctrl > BNEP_FILTER_MULT_ADDR_RSP) {
- error("cmd not understood");
- bnep_send_ctrl_rsp(sk, BNEP_CONTROL, BNEP_CMD_NOT_UNDERSTOOD,
- req->ctrl);
- goto failed;
- }
-
- if (req->type != BNEP_CONTROL || req->ctrl != BNEP_SETUP_CONN_REQ) {
- error("cmd is not BNEP_SETUP_CONN_REQ %02X %02X", req->type,
- req->ctrl);
- goto failed;
- }
-
- rsp = bnep_setup_decode(req, &dst_role, &src_role);
- if (rsp != BNEP_SUCCESS) {
- error("bnep_setup_decode failed");
+ if (n < 3) {
+ error("pan: to few setup connection request data received");
goto failed;
}
err = nap_create_bridge();
- if (err < 0) {
+ if (err < 0)
error("pan: Failed to create bridge: %s (%d)", strerror(-err),
-err);
- goto failed;
- }
- if (bnep_server_add(sk, dst_role, BNEP_BRIDGE, dev->iface,
- &dev->dst) < 0) {
+ if (bnep_server_add(sk, (err < 0) ? NULL : BNEP_BRIDGE, dev->iface,
+ &dev->dst, packet, n) < 0) {
error("pan: server_connadd failed");
- rsp = BNEP_CONN_NOT_ALLOWED;
goto failed;
}
- rsp = BNEP_SUCCESS;
- bnep_send_ctrl_rsp(sk, BNEP_CONTROL, BNEP_SETUP_CONN_RSP, rsp);
-
dev->watch = g_io_add_watch(chan, G_IO_HUP | G_IO_ERR | G_IO_NVAL,
nap_watchdog_cb, dev);
g_io_channel_unref(dev->io);
@@ -530,7 +514,6 @@ static gboolean nap_setup_cb(GIOChannel *chan, GIOCondition cond,
return FALSE;
failed:
- bnep_send_ctrl_rsp(sk, BNEP_CONTROL, BNEP_SETUP_CONN_RSP, rsp);
pan_device_remove(dev);
return FALSE;
diff --git a/android/pics-bnep.txt b/android/pics-bnep.txt
new file mode 100644
index 00000000..09aa725e
--- /dev/null
+++ b/android/pics-bnep.txt
@@ -0,0 +1,26 @@
+BNEP PICS for the PTS tool.
+
+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_BNEP_1_1 True BNEP Connection Setup (M)
+TSPC_BNEP_1_2 True (*) BNEP Data Packet Reception (M)
+TSPC_BNEP_1_3 True (*) BNEP Data Packet Transmission (M)
+TSPC_BNEP_1_3a True (*) BNEP Compressed Packet Transmission (O)
+TSPC_BNEP_1_3b True (*) BNEP Compressed Packet Transmission Source Only
+ (O)
+TSPC_BNEP_1_4 True BNEP Control Message Processing (M)
+TSPC_BNEP_1_5 True BNEP Extension Header Processing (M)
+TSPC_BNEP_1_6 True Network Protocol Filter Message Transmission (O)
+TSPC_BNEP_1_7 True Multicast Address Filter Message Transmission
+ (O)
+-------------------------------------------------------------------------------
diff --git a/android/pixit-bnep.txt b/android/pixit-bnep.txt
new file mode 100644
index 00000000..9a26cd2a
--- /dev/null
+++ b/android/pixit-bnep.txt
@@ -0,0 +1,30 @@
+BNEP PIXIT for the PTS tool.
+
+PTS version: 6.0
+
+* - different than PTS defaults
+& - should be set to IUT Bluetooth address
+# - should be set to PTS's bin/audio folder
+
+Required PIXIT settings
+-------------------------------------------------------------------------------
+Parameter Name Value
+-------------------------------------------------------------------------------
+TSPX_class_of_device 04041C
+TSPX_security_control_data
+TSPX_content_protection_data
+TSPX_bd_addr_iut 112233445566 (*&)
+TSPX_delete_link_key FALSE
+TSPX_pin_code 1234
+TSPX_security_enabled FALSE
+TSPX_time_guard 300000
+TSPX_use_implicit_send TRUE
+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 1116
+TSPX_UUID_source_address 1115
+TSPX_MAC_dest_address 000000000000 (*&)
+TSPX_MAC_source_address 000000000000 (*&)
diff --git a/android/pts-bnep.txt b/android/pts-bnep.txt
new file mode 100644
index 00000000..91ef1c5e
--- /dev/null
+++ b/android/pts-bnep.txt
@@ -0,0 +1,57 @@
+PTS test results for BNEP
+
+PTS version: 6.0
+Tested: 12-March-2015
+Android version: 5.0
+Kernel version: 3.20
+
+Results:
+PASS test passed
+FAIL test failed
+INC test is inconclusive
+N/A test is disabled due to PICS setup
+
+--------------------------------------------------------------------------------
+Test Name Result Notes
+--------------------------------------------------------------------------------
+TC_CTRL_BV_01_C PASS bneptest -s -b <bridge> -n <iface>
+TC_CTRL_BV_02_C PASS bneptest -c <PTS addr> -b <bridge> -n <iface>
+TC_CTRL_BV_03_C PASS bneptest -s -b <bridge> -n <iface>
+TC_CTRL_BV_04_C PASS PTS issue #13169
+ bneptest -s -b <bridge> -n <iface>
+TC_CTRL_BV_05_C PASS PTS issue #13169
+ bneptest -s -b <bridge> -n <iface>
+TC_CTRL_BV_06_C PASS PTS issue #13169
+ bneptest -s -b <bridge> -n <iface>
+TC_CTRL_BV_07_C PASS PTS issue #13169
+ bneptest -c <PTS addr> -b <bridge> -n <iface>
+ -t 3 -d 0 -e 1500 -y 1
+TC_CTRL_BV_08_C PASS PTS issue #13169
+ bneptest -s -b <bridge> -n <iface>
+TC_CTRL_BV_09_C PASS bneptest -c <PTS addr> -b <bridge> -n <iface>
+ -t 5 -g 00:00:00:00:00:00
+ -j ff:ff:ff:ff:ff:ff -y 1
+TC_CTRL_BV_10_C PASS PTS issue #13169
+ bneptest -s -b <bridge> -n <iface>
+TC_CTRL_BV_19_C INC JIRA #BA-343
+ bneptest -s -b <bridge> -n <iface>
+TC_RX_TYPE_0_BV_11_C PASS bneptest -s -b <bridge> -n <iface>
+TC_RX_C_BV_12_C PASS bneptest -s -b <bridge> -n <iface>
+TC_RX_C_S_BV_13_C PASS bneptest -s -b <bridge> -n <iface>
+TC_RX_C_S_BV_14_C PASS bneptest -s -b <bridge> -n <iface>
+TC_RX_TYPE_0_BV_15_C PASS PTS issue #13169
+ bneptest -s -b <bridge> -n <iface>
+TC_RX_TYPE_0_BV_16_C PASS PTS issue #13171
+ bneptest -s -b <bridge> -n <iface>
+TC_RX_TYPE_0_BV_17_C PASS PTS issue #13169
+ bneptest -s -b <bridge> -n <iface>
+TC_RX_TYPE_0_BV_18_C PASS PTS issue #13171
+ bneptest -s -b <bridge> -n <iface>
+TC_TX_TYPE_0_BV_20_C PASS bneptest -c <PTS addr> -b <bridge> -n <iface>
+ -w 0 -k <src hw addr> -f <dst hw addr>
+TC_TX_C_BV_21_C PASS bneptest -c <PTS addr> -b <bridge> -n <iface>
+ -w 2 -k <src hw addr> -f <dst hw addr>
+TC_TX_C_S_BV_22_C PASS bneptest -c <PTS addr> -b <bridge> -n <iface>
+ -w 3 -k <src hw addr> -f <dst hw addr>
+TC_TX_C_D_BV_23_C PASS bneptest -c <PTS addr> -b <bridge> -n <iface>
+ -w 4 -k <src hw addr> -f <dst hw addr>
diff --git a/attrib/gatt-service.c b/attrib/gatt-service.c
index ea14e75d..d4df218e 100644
--- a/attrib/gatt-service.c
+++ b/attrib/gatt-service.c
@@ -241,8 +241,8 @@ static gboolean add_descriptor(struct btd_adapter *adapter,
/* API not available in bluez 5.25
* att_put_uuid(info->uuid, &atval[0]);*/
- a = attrib_db_add(adapter, h++, &bt_uuid, ATT_NONE, ATT_NOT_PERMITTED,
- atval, info->uuid.type / 8);
+ a = attrib_db_add(adapter, h++, &bt_uuid, ATT_AUTHENTICATION,
+ ATT_AUTHENTICATION, atval, info->uuid.type / 8);
if (a == NULL) {
return FALSE;
diff --git a/attrib/gatt.h b/attrib/gatt.h
index 1883b778..7a0cf953 100644
--- a/attrib/gatt.h
+++ b/attrib/gatt.h
@@ -126,6 +126,4 @@ 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
+#endif
diff --git a/attrib/gattrib.c b/attrib/gattrib.c
index 699f9276..2f61977c 100644
--- a/attrib/gattrib.c
+++ b/attrib/gattrib.c
@@ -231,8 +231,8 @@ void g_attrib_channel_unref(GAttrib *attrib)
}
#endif
-gboolean g_attrib_set_destroy_function(GAttrib *attrib,
- GDestroyNotify destroy, gpointer user_data)
+gboolean g_attrib_set_destroy_function(GAttrib *attrib, GDestroyNotify destroy,
+ gpointer user_data)
{
if (!attrib)
return FALSE;
@@ -440,9 +440,24 @@ guint g_attrib_register(GAttrib *attrib, guint8 opcode, guint16 handle,
uint8_t *g_attrib_get_buffer(GAttrib *attrib, size_t *len)
{
+ uint16_t mtu;
+
if (!attrib || !len)
return NULL;
+ mtu = bt_att_get_mtu(attrib->att);
+
+ /*
+ * Clients of this expect a buffer to use.
+ *
+ * Pdu encoding in shared/att verifies if whole buffer fits the mtu,
+ * thus we should set the buflen also when mtu is reduced. But we
+ * need to reallocate the buffer only if mtu is larger.
+ */
+ if (mtu > attrib->buflen)
+ attrib->buf = g_realloc(attrib->buf, mtu);
+
+ attrib->buflen = mtu;
*len = attrib->buflen;
return attrib->buf;
}
diff --git a/client/gatt.c b/client/gatt.c
index 884c4f2e..11c90819 100644
--- a/client/gatt.c
+++ b/client/gatt.c
@@ -31,6 +31,7 @@
#include <stdlib.h>
#include <stdbool.h>
#include <sys/uio.h>
+#include <wordexp.h>
#include <readline/readline.h>
#include <readline/history.h>
@@ -41,6 +42,9 @@
#include "display.h"
#include "gatt.h"
+#define PROFILE_PATH "/org/bluez/profile"
+#define PROFILE_INTERFACE "org.bluez.GattProfile1"
+
/* String display constants */
#define COLORED_NEW COLOR_GREEN "NEW" COLOR_OFF
#define COLORED_CHG COLOR_YELLOW "CHG" COLOR_OFF
@@ -49,6 +53,7 @@
static GList *services;
static GList *characteristics;
static GList *descriptors;
+static GList *managers;
static void print_service(GDBusProxy *proxy, const char *description)
{
@@ -424,12 +429,17 @@ static void write_attribute(GDBusProxy *proxy, char *arg)
if (*entry == '\0')
continue;
-
+#ifndef __TIZEN_PATCH__
if (i > 512) {
rl_printf("Too much data\n");
return;
}
-
+#else
+ if (i >= 512) {
+ rl_printf("Too much data\n");
+ return;
+ }
+#endif
val = strtol(entry, &endptr, 0);
if (!endptr || *endptr != '\0' || val > UINT8_MAX) {
rl_printf("Invalid value at index %d\n", i);
@@ -512,3 +522,146 @@ void gatt_notify_attribute(GDBusProxy *proxy, bool enable)
rl_printf("Unable to notify attribute %s\n",
g_dbus_proxy_get_path(proxy));
}
+
+static void register_profile_setup(DBusMessageIter *iter, void *user_data)
+{
+ wordexp_t *w = user_data;
+ DBusMessageIter uuids, opt;
+ const char *path = PROFILE_PATH;
+ size_t i;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "s", &uuids);
+ for (i = 0; i < w->we_wordc; i++)
+ dbus_message_iter_append_basic(&uuids, DBUS_TYPE_STRING,
+ &w->we_wordv[i]);
+ dbus_message_iter_close_container(iter, &uuids);
+
+ 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,
+ &opt);
+ dbus_message_iter_close_container(iter, &opt);
+
+}
+
+static void register_profile_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 register profile: %s\n", error.name);
+ dbus_error_free(&error);
+ return;
+ }
+
+ rl_printf("Profile registered\n");
+}
+
+void gatt_add_manager(GDBusProxy *proxy)
+{
+ managers = g_list_append(managers, proxy);
+}
+
+void gatt_remove_manager(GDBusProxy *proxy)
+{
+ managers = g_list_remove(managers, proxy);
+}
+
+static int match_proxy(const void *a, const void *b)
+{
+ GDBusProxy *proxy1 = (void *) a;
+ GDBusProxy *proxy2 = (void *) b;
+
+ return strcmp(g_dbus_proxy_get_path(proxy1),
+ g_dbus_proxy_get_path(proxy2));
+}
+
+static DBusMessage *release_profile(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ g_dbus_unregister_interface(conn, PROFILE_PATH, PROFILE_INTERFACE);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable methods[] = {
+ { GDBUS_METHOD("Release", NULL, NULL, release_profile) },
+ { }
+};
+
+void gatt_register_profile(DBusConnection *conn, GDBusProxy *proxy,
+ wordexp_t *w)
+{
+ GList *l;
+
+ l = g_list_find_custom(managers, proxy, match_proxy);
+ if (!l) {
+ rl_printf("Unable to find GattManager proxy\n");
+ return;
+ }
+
+ if (g_dbus_register_interface(conn, PROFILE_PATH,
+ PROFILE_INTERFACE, methods,
+ NULL, NULL, NULL, NULL) == FALSE) {
+ rl_printf("Failed to register profile object\n");
+ return;
+ }
+
+ if (g_dbus_proxy_method_call(l->data, "RegisterProfile",
+ register_profile_setup,
+ register_profile_reply, w,
+ NULL) == FALSE) {
+ rl_printf("Failed register profile\n");
+ return;
+ }
+}
+
+static void unregister_profile_reply(DBusMessage *message, void *user_data)
+{
+ DBusConnection *conn = user_data;
+ DBusError error;
+
+ dbus_error_init(&error);
+
+ if (dbus_set_error_from_message(&error, message) == TRUE) {
+ rl_printf("Failed to unregister profile: %s\n", error.name);
+ dbus_error_free(&error);
+ return;
+ }
+
+ rl_printf("Profile unregistered\n");
+
+ g_dbus_unregister_interface(conn, PROFILE_PATH, PROFILE_INTERFACE);
+}
+
+static void unregister_profile_setup(DBusMessageIter *iter, void *user_data)
+{
+ const char *path = PROFILE_PATH;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_OBJECT_PATH, &path);
+}
+
+void gatt_unregister_profile(DBusConnection *conn, GDBusProxy *proxy)
+{
+ GList *l;
+
+ l = g_list_find_custom(managers, proxy, match_proxy);
+ if (!l) {
+ rl_printf("Unable to find GattManager proxy\n");
+ return;
+ }
+
+ if (g_dbus_proxy_method_call(l->data, "UnregisterProfile",
+ unregister_profile_setup,
+ unregister_profile_reply, conn,
+ NULL) == FALSE) {
+ rl_printf("Failed unregister profile\n");
+ return;
+ }
+}
diff --git a/client/gatt.h b/client/gatt.h
index effee5e4..689bb4d5 100644
--- a/client/gatt.h
+++ b/client/gatt.h
@@ -37,3 +37,10 @@ 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);
+
+void gatt_add_manager(GDBusProxy *proxy);
+void gatt_remove_manager(GDBusProxy *proxy);
+
+void gatt_register_profile(DBusConnection *conn, GDBusProxy *proxy,
+ wordexp_t *w);
+void gatt_unregister_profile(DBusConnection *conn, GDBusProxy *proxy);
diff --git a/client/main.c b/client/main.c
index b9e2eb76..4e7a54aa 100644
--- a/client/main.c
+++ b/client/main.c
@@ -32,6 +32,7 @@
#include <stdbool.h>
#include <signal.h>
#include <sys/signalfd.h>
+#include <wordexp.h>
#include <readline/readline.h>
#include <readline/history.h>
@@ -348,6 +349,8 @@ static void proxy_added(GDBusProxy *proxy, void *user_data)
gatt_add_characteristic(proxy);
} else if (!strcmp(interface, "org.bluez.GattDescriptor1")) {
gatt_add_descriptor(proxy);
+ } else if (!strcmp(interface, "org.bluez.GattManager1")) {
+ gatt_add_manager(proxy);
}
}
@@ -443,6 +446,8 @@ static void proxy_removed(GDBusProxy *proxy, void *user_data)
if (default_attr == proxy)
set_default_attribute(NULL);
+ } else if (!strcmp(interface, "org.bluez.GattManager1")) {
+ gatt_remove_manager(proxy);
}
}
@@ -1337,6 +1342,36 @@ static void cmd_notify(const char *arg)
gatt_notify_attribute(default_attr, enable ? true : false);
}
+static void cmd_register_profile(const char *arg)
+{
+ wordexp_t w;
+
+ if (check_default_ctrl() == FALSE)
+ return;
+
+ if (wordexp(arg, &w, WRDE_NOCMD)) {
+ rl_printf("Invalid argument\n");
+ return;
+ }
+
+ if (w.we_wordc == 0) {
+ rl_printf("Missing argument\n");
+ return;
+ }
+
+ gatt_register_profile(dbus_conn, default_ctrl, &w);
+
+ wordfree(&w);
+}
+
+static void cmd_unregister_profile(const char *arg)
+{
+ if (check_default_ctrl() == FALSE)
+ return;
+
+ gatt_unregister_profile(dbus_conn, default_ctrl);
+}
+
static void cmd_version(const char *arg)
{
rl_printf("Version %s\n", VERSION);
@@ -1470,6 +1505,10 @@ static const struct {
{ "write", "<data=[xx xx ...]>", cmd_write,
"Write attribute value" },
{ "notify", "<on/off>", cmd_notify, "Notify attribute value" },
+ { "register-profile", "<UUID ...>", cmd_register_profile,
+ "Register profile to connect" },
+ { "unregister-profile", NULL, cmd_unregister_profile,
+ "Unregister profile" },
{ "version", NULL, cmd_version, "Display version" },
{ "quit", NULL, cmd_quit, "Quit program" },
{ "exit", NULL, cmd_quit },
diff --git a/configure.ac b/configure.ac
index a58720c9..e10659b8 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,5 +1,5 @@
AC_PREREQ(2.60)
-AC_INIT(bluez, 5.28)
+AC_INIT(bluez, 5.30)
AM_INIT_AUTOMAKE([foreign subdir-objects color-tests silent-rules
tar-pax no-dist-gzip dist-xz])
@@ -220,7 +220,7 @@ AC_ARG_ENABLE(manpages, AC_HELP_STRING([--enable-manpages],
AM_CONDITIONAL(MANPAGES, test "${enable_manpages}" = "yes")
AC_ARG_ENABLE(experimental, AC_HELP_STRING([--enable-experimental],
- [enable experimental plugins (NFC, ...)]),
+ [enable experimental plugins (SAP, NFC, ...)]),
[enable_experimental=${enableval}])
AM_CONDITIONAL(EXPERIMENTAL, test "${enable_experimental}" = "yes")
diff --git a/doc/adapter-api.txt b/doc/adapter-api.txt
index 55279938..c815cf03 100644
--- a/doc/adapter-api.txt
+++ b/doc/adapter-api.txt
@@ -19,6 +19,8 @@ Methods void StartDiscovery()
This process will start creating Device objects as
new devices are discovered.
+ During discovery RSSI delta-threshold is imposed.
+
Possible errors: org.bluez.Error.NotReady
org.bluez.Error.Failed
@@ -35,6 +37,73 @@ Methods void StartDiscovery()
org.bluez.Error.Failed
org.bluez.Error.NotAuthorized
+ void RemoveDevice(object device)
+
+ This removes the remote device object at the given
+ path. It will remove also the pairing information.
+
+ Possible errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.Failed
+
+ void SetDiscoveryFilter(dict filter) [Experimental]
+
+ This method sets the device discovery filter for the
+ caller. When this method is called with no filter
+ parameter, filter is removed.
+
+ Parameters that may be set in the filter dictionary
+ include the following:
+
+ array{string} UUIDs : filtered service UUIDs
+ int16 RSSI : RSSI threshold value
+ uint16 Pathloss : Pathloss threshold value
+ string Transport : type of scan to run
+
+ When a remote device is found that advertises any UUID
+ from UUIDs, it will be reported if:
+ - Pathloss and RSSI are both empty,
+ - only Pathloss param is set, device advertise TX pwer,
+ and computed pathloss is less than Pathloss param,
+ - only RSSI param is set, and received RSSI is higher
+ than RSSI param,
+
+ Transport parameter determines the type of scan:
+ "auto" - interleaved scan, default value
+ "bredr" - BR/EDR inquiry
+ "le" - LE scan only
+
+ If "le" or "bredr" Transport is requested, and the
+ controller doesn't support it, org.bluez.Error.Failed
+ error will be returned. If "auto" transport is
+ requested, scan will use LE, BREDR, or both, depending
+ on what's currently enabled on the controller.
+
+ When discovery filter is set, Device objects will be
+ created as new devices with matching criteria are
+ discovered. PropertiesChanged signals will be emitted
+ for already existing Device objects, with updated RSSI
+ value. If one or more discovery filters have been set,
+ the RSSI delta-threshold, that is imposed by
+ StartDiscovery by default, will not be applied.
+
+ When multiple clients call SetDiscoveryFilter, their
+ filters are internally merged, and notifications about
+ new devices are sent to all clients. Therefore, each
+ client must check that device updates actually match
+ its filter.
+
+ When SetDiscoveryFilter is called multiple times by the
+ same client, last filter passed will be active for
+ given client.
+
+ SetDiscoveryFilter can be called before StartDiscovery.
+ It is useful when client will create first discovery
+ session, to ensure that proper scan will be started
+ right after call to StartDiscovery.
+
+ Possible errors: org.bluez.Error.NotReady
+ org.bluez.Error.Failed
+
#ifdef __TIZEN_PATCH__
void StartCustomDiscovery(string pattern)
@@ -179,14 +248,6 @@ Methods void StartDiscovery()
Possible errors: org.bluez.Error.InvalidArguments
#endif
- void RemoveDevice(object device)
-
- This removes the remote device object at the given
- path. It will remove also the pairing information.
-
- Possible errors: org.bluez.Error.InvalidArguments
- org.bluez.Error.Failed
-
Properties string Address [readonly]
The Bluetooth device address.
diff --git a/doc/advertising-api.txt b/doc/advertising-api.txt
new file mode 100644
index 00000000..7fb34eeb
--- /dev/null
+++ b/doc/advertising-api.txt
@@ -0,0 +1,101 @@
+BlueZ D-Bus LE Advertising API Description
+******************************************
+
+Advertising packets are structured data which is broadcast on the LE Advertising
+channels and available for all devices in range. Because of the limited space
+available in LE Advertising packets (32 bytes), each packet's contents must be
+carefully controlled.
+
+BlueZ acts as a store for the Advertisement Data which is meant to be sent.
+It constructs the correct Advertisement Data from the structured
+data and configured the kernel to send the correct advertisement.
+
+Advertisement Data objects are registered freely and then referenced by BlueZ
+when constructing the data sent to the kernel.
+
+LE Advertisement Data hierarchy
+===============================
+
+Specifies the Advertisement Data to be broadcast and some advertising
+parameters. Properties which are not present will not be included in the
+data. Required advertisement data types will always be included.
+All UUIDs are 128-bit versions in the API, and 16 or 32-bit
+versions of the same UUID will be used in the advertising data as appropriate.
+
+Service org.bluez
+Interface org.bluez.LEAdvertisement1
+Object path freely definable
+
+Methods void Release() [noreply]
+
+ This method gets called when the service daemon
+ removes the Advertisement. A client can use it to do
+ cleanup tasks. There is no need to call
+ UnregisterAdvertisement because when this method gets
+ called it has already been unregistered.
+
+Properties string Type
+
+ Determines the type of advertising packet requested.
+
+ Possible values: "broadcast" or "peripheral"
+
+ array{string} ServiceUUIDs
+
+ List of UUIDs to include in the "Service UUID" field of
+ the Advertising Data.
+
+ dict ManufacturerData
+
+ Manufactuer Data fields to include in
+ the Advertising Data. Keys are the Manufacturer ID
+ to associate with the data.
+
+ array{string} SolicitUUIDs
+
+ Array of UUIDs to include in "Service Solicitation"
+ Advertisement Data.
+
+ dict ServiceData
+
+ Service Data elements to include. The keys are the
+ UUID to associate with the data.
+
+
+LE Advertising Manager hierarchy
+================================
+
+The Advertising Manager allows external applications to register Advertisement
+Data which should be broadcast to devices. Advertisement Data elements must
+follow the API for LE Advertisement Data described above.
+
+Service org.bluez
+Interface org.bluez.LEAdvertisingManager1 [Experimental]
+Object path /org/bluez/{hci0,hci1,...}
+
+Methods RegisterAdvertisement(object advertisement, dict options)
+
+ Registers an advertisement object to be sent over the LE
+ Advertising channel. The service must be exported
+ under interface LEAdvertisement1. InvalidArguments
+ indicates that the object has invalid or conflicting
+ properties. InvalidLength indicates that the data
+ provided generates a data packet which is too long.
+ The properties of this object are parser when it is
+ registered, and any changes are ignored.
+ Currently only one advertisement at a time is supported,
+ attempting to register two advertisements will result in
+ an AlreadyExists error.
+
+ Possible errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.AlreadyExists
+ org.bluez.Error.InvalidLength
+
+ UnregisterAdvertisement(object advertisement)
+
+ This unregisters an advertisement that has been
+ prevously 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
diff --git a/doc/gatt-api.txt b/doc/gatt-api.txt
index c5eb827d..3329ffd8 100644
--- a/doc/gatt-api.txt
+++ b/doc/gatt-api.txt
@@ -157,6 +157,13 @@ Properties string UUID [read-only]
descriptor objects will become available via
ObjectManager as soon as they get discovered.
+#ifdef __TIZEN_PATCH__
+ string Unicast [read-only]
+
+ 12:34:56:78:9A:BC remote device address, if address is set then
+ notifications or indications shall be sent to only "XX_XX_XX_XX_XX_XX"
+ device otherwise notification or indication shall be multicasted.
+#endif
Characteristic Descriptors hierarchy
====================================
@@ -354,4 +361,4 @@ Methods void RegisterService(object service, dict options)
DescriptorX /serviceX/CharacterisitcX/DescriptorX
Possible errors: org.bluez.Error.InvalidArguments
-#endif \ No newline at end of file
+#endif
diff --git a/doc/mgmt-api.txt b/doc/mgmt-api.txt
index 9bdf1778..b4ddf770 100644
--- a/doc/mgmt-api.txt
+++ b/doc/mgmt-api.txt
@@ -23,6 +23,7 @@ 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
+Linux kernel v4.1 Version 1.9 (not yet released)
Version 1.1 introduces Set Device ID command.
@@ -54,6 +55,11 @@ an extra setting for enabling SSP debug mode.
Version 1.8 introduces Start Service Discovery command. It also adds new
Long Term Key types for LE Secure Connection feature.
+Version 1.9 introduces a new static address setting and allows the usage
+of Set Fast Connectable when controller is powered off. The existing Set
+Advertising command gained an extra setting for enabling undirected
+connectable advertising.
+
Example
=======
@@ -142,6 +148,9 @@ and Command Complete events:
0x0F Not Powered
0x10 Cancelled
0x11 Invalid Index
+0x12 RFKilled
+0x13 Already Paired
+0x14 Permission Denied
As a general rule all commands generate the events as specified below,
however invalid lengths or unknown commands will always generate a
@@ -415,21 +424,18 @@ Set Fast Connectable Command
(e.g. not for single-mode LE ones). It will return Not Supported
otherwise.
- This command can only be used when the controller is powered on
- and will return Not Powerd otherwise.
+ This command can be used when the controller is not powered and
+ all settings will be programmed once powered.
- If connectable is not set, then this command will fail with
- Rejected error.
+ The setting will be remembered during power down/up toggles.
This command generates a Command Complete event on success or
a Command Status event on failure.
Possible errors: Failed
Busy
- Rejected
Not Supported
Invalid Parameters
- Not Powered
Invalid Index
@@ -1040,6 +1046,7 @@ Pair Device Command
Invalid Parameters
Not Powered
Invalid Index
+ Already Paired
Cancel Pair Device Command
@@ -1299,16 +1306,14 @@ Add Remote Out Of Band Data Command
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.
+ When providing data for remote LE devices, then the Hash_192 and
+ and Randomizer_192 fields are not used and shell be set to zero.
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.
+ to zero. Currently there is no support for providing the Security
+ Manager TK Value for LE legacy pairing.
If Secure Connections Only mode has been enabled, then providing
Hash_P192 and Randomizer_P192 is not allowed. They are required
@@ -1538,7 +1543,7 @@ Set Device ID Command
0x0000 Disable Device ID
0x0001 Bluetooth SIG
- 0x0002 USB Implementer?™s Forum
+ 0x0002 USB Implementer’s 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
@@ -1561,12 +1566,37 @@ Set Advertising Command
Return Parameters: Current_Settings (4 Octets)
This command is used to enable LE advertising on a controller
- that supports it. The allowed values for the Advertising
- command parameter are 0x00 and 0x01. All other values will
- return Invalid Parameters.
+ that supports it. The allowed values for the Advertising command
+ parameter are 0x00, 0x01 and 0x02. All other values will return
+ Invalid Parameters.
- A pre-requisite is that LE is already enabled, otherwise
- this command will return a "rejected" response.
+ The value 0x00 disables advertising, the value 0x01 enables
+ advertising with considering of connectable setting and the
+ value 0x02 enables advertising in connectable mode.
+
+ Using value 0x01 means that when connectable setting is disabled,
+ the advertising happens with undirected non-connectable advertising
+ packets and a non-resovable random address is used. If connectable
+ setting is enabled, then undirected connectable advertising packets
+ and the identity address or resolvable private address are used.
+
+ LE Devices configured via Add Device command with Action 0x01
+ have no effect when using Advertising value 0x01 since only the
+ connectable setting is taken into account.
+
+ To utilize undirected connectable advertising without changing the
+ connectable setting, the value 0x02 can be utilized. It makes the
+ device connectable via LE without the requirement for being
+ connectable on BR/EDR (and/or LE).
+
+ The value 0x02 should be the preferred mode of operation when
+ implementing peripheral mode.
+
+ Using this command will temporarily deactive any configuration
+ made by the Add Advertising command. This command takes precedence.
+
+ A pre-requisite is that LE is already enabled, otherwise this
+ command will return a "rejected" response.
This command generates a Command Complete event on success or a
Command Status event on failure.
@@ -2361,7 +2391,6 @@ Read Local Out Of Band Extended Data Command
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)
@@ -2372,15 +2401,21 @@ Read Local Out Of Band Extended Data Command
Random Value fields are only included when secure connections has been
enabled.
+ The Security Manager TK Value from the Bluetooth specification can
+ not be provided by this command. The Out Of Band information here are
+ for asymmetric exchanges based on Diffie-Hellman key exchange. The
+ Security Manager TK Value is a symmetric random number that has to
+ be acquired and agreed upon differently.
+
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.
+ for Simple Pairing Hash C-192 and Simple Pairing Randomizer R-192
+ 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.
@@ -2407,6 +2442,7 @@ Read Extended Controller Index List Command
Return Parameters: Num_Controllers (2 Octets)
Controller_Index[i] (2 Octets)
Controller_Type[i] (1 Octet)
+ Controller_Bus[i] (1 Octet)
This command returns the list of currently known controllers. It
includes configured, unconfigured and alternate controllers.
@@ -2441,6 +2477,16 @@ Read Extended Controller Index List Command
Alternate MAC/PHY controllers will be listed as 0x02. They do not
support the difference between configured and unconfigured state.
+ The Controller_Bus parameter has these values:
+
+ 0x00 Virtual
+ 0x01 USB
+ 0x02 PCMCIA
+ 0x03 UART
+ 0x04 RS232
+ 0x05 PCI
+ 0x06 SDIO
+
Controllers marked as RAW only operation are currently not listed
by this command.
@@ -2448,6 +2494,229 @@ Read Extended Controller Index List Command
a Command Status event on failure.
+Read Advertising Features Command
+=================================
+
+ Command Code: 0x003d
+ Controller Index: <controller id>
+ Command Parameters:
+ Return Parameters: Supported_Flags (4 Octets)
+ Max_Adv_Data_Len (1 Octet)
+ Max_Scan_Rsp_Len (1 Octet)
+ Max_Instances (1 Octet)
+ Num_Instances (1 Octet)
+ Instance[i] (1 Octet)
+
+ This command is used to read the advertising features supported
+ by the controller and stack.
+
+ With the Supported_Flags field the possible values for the Flags
+ field in Add Advertising command provided:
+
+ 0 Switch into Connectable mode
+ 1 Advertise as Discoverable
+ 2 Advertise as Limited Discoverable
+ 3 Add Flags field to Adv_Data
+ 4 Add TX Power field to Adv_Data
+ 5 Add Appearance field to Scan_Rsp
+ 6 Add Local Name in Scan_Rsp
+
+ The Flags bit 0 indicates support for connectable advertising
+ and for switching to connectable advertising independent of the
+ connectable global setting. When this flag is not supported, then
+ the global connectable setting determines if undirected connectable,
+ undirected scannable or undirected non-connectable advertising is
+ used. It also determines the use of non-resolvable random address
+ versus identity address or resolvable private address.
+
+ The Flags bit 1 indicates support for advertising with discoverable
+ mode enabled. Users of this flag will decrease the Max_Adv_Data_Len
+ by 3 octets. In this case the advertising data flags are managed
+ and added in front of the provided advertising data.
+
+ The Flags bit 2 indicates support for advertising with limited
+ discoverable mode enabled. Users of this flag will decrease the
+ Max_Adv_Data_Len by 3 octets. In this case the advertising data
+ flags are managed and added in front of the provided advertising
+ data.
+
+ The Flags bit 3 indicates support for automatically keeping the
+ Flags field of the advertising data updated. Users of this flag
+ will decrease the Max_Adv_Data_Len by 3 octets and need to keep
+ that in mind. The Flags field will be added in front of the
+ advertising data provided by the user. Note that with Flags bit 1
+ and Flags bit 2, this one will be implicitly used even if it is
+ not marked as supported.
+
+ The Flags bit 4 indicates support for automatically adding the
+ TX Power value to the advertising data. Users of this flag will
+ decrease the Max_Adv_Data_Len by 3 octets. The TX Power field will
+ be added at the end of the user provided advertising data. If the
+ controller does not support TX Power information, then this bit will
+ not be set.
+
+ The Flags bit 5 indicates support for automatically adding the
+ Apperance value to the scan response data. Users of this flag
+ will decrease the Max_Scan_Rsp_len by 4 octets. The Appearance
+ field will be added in front of the scan response data provided
+ by the user. If the appearance value is not supported, then this
+ bit will not be set.
+
+ The Flags bit 6 indicates support for automatically adding the
+ Local Name value to the scan response data. This flag indicates
+ an opportunistic approach for the Local Name. If enough space
+ in the scan response data is available, it will be added. If the
+ space is limited a short version or no name information. The
+ Local Name will be added at the end of the scan response data.
+
+ The valid range for Instance identifiers is 1-254. The value 0
+ is reserved for internal use and the value 255 is reserved for
+ future extensions. However the Max_Instances value for indicating
+ the number of supported Instances can be also 0 if the controller
+ does not support any advertising.
+
+ The Max_Adv_Data_Len and Max_Scan_Rsp_Len provides extra
+ information about the maximum length of the data fields. For
+ now this will always return the value 31. Different flags
+ however might decrease the actual available length in these
+ data fields.
+
+ With Num_Instances and Instance array the current occupied
+ Instance identifiers can be retrieved.
+
+ This command generates a Command Complete event on success or
+ a Command Status event on failure.
+
+ Possible errors: Invalid Parameters
+ Invalid Index
+
+
+Add Advertising Command
+=======================
+
+ Command Code: 0x003e
+ Controller Index: <controller id>
+ Command Parameters: Instance (1 Octet)
+ Flags (4 Octets)
+ Duration (2 Octets)
+ Timeout (2 Octets)
+ Adv_Data_Len (1 Octet)
+ Scan_Rsp_len (1 Octet)
+ Adv_Data (0-255 Octets)
+ Scan_Rsp (0-255 Octets)
+ Return Parameters: Instance (1 Octet)
+
+ This command is used to configure an advertising instance that
+ can be used to switch a Bluetooth Low Energy controller into
+ advertising mode.
+
+ Added advertising information with this command will be ignored
+ when using the Set Advertising command to enable advertising. The
+ usage of Set Advertising command take precedence over this command.
+
+ The Instance identifier is a value between 1 and the number of
+ supported instances. The value 0 is reserved.
+
+ With the Flags value the type of advertising is controlled and
+ the following flags are defined:
+
+ 0 Switch into Connectable mode
+ 1 Advertise as Discoverable
+ 2 Advertise as Limited Discoverable
+ 3 Add Flags field to Adv_Data
+ 4 Add TX Power field to Adv_Data
+ 5 Add Appearance field to Scan_Rsp
+ 6 Add Local Name in Scan_Rsp
+
+ When the connectable flag is set, then the controller will use
+ undirected connectable advertising. The value of the connectable
+ setting can be overwritten this way. This is useful to switch a
+ controller into connectable mode only for LE operation. This is
+ similar to the mode 0x02 from the Set Advertising command.
+
+ When the connectable flag is not set, then the controller will
+ use advertising based on the connectable setting. When using
+ non-connectable or scannable advertising, the controller will
+ be programmed with a non-resolvable random address. When the
+ system is connectable, then the identity address or resolvable
+ private address will be used.
+
+ Using the connectable flag is useful for peripheral mode support
+ where BR/EDR (and/or LE) is controlled by Add Device. This allows
+ making the peripheral connectable without having to interfere
+ with the global connectable setting.
+
+ If Scan_Rsp_Len is zero and connectable flag is not set and
+ the global connectable setting is off, then non-connectable
+ advertising is used. If Scan_Rsp_Len is larger than zero and
+ connectable flag is not set and the global advertising is off,
+ then scannable advertising is used. This small difference is
+ supported to provide less air traffic for devices implementing
+ broadcaster role.
+
+ The Duration parameter configures the length of an Instance. The
+ value is in seconds and a value of 0 indicates an automatic choice
+ for the Duration. If only one advertising Instance has been added,
+ then the Duration value will be ignored. It only applies for the
+ case where multiple Instances are configured. In that case every
+ Instance will be available for the Duration time and after that
+ it switches to the next one. This is a simple round-robin based
+ approach.
+
+ The Timeout parameter configures the life-time of an Instance. In
+ case the value 0 is used it indicates no expiration time. If a
+ timeout value is provided, then the advertising Instace will be
+ automatically removed when the timeout passes. The value for the
+ timeout is in seconds. Powering down a controller will invalidate
+ all advertising Instances and it is not possible to add a new
+ Instance with a timeout when the controller is powered down.
+
+ When a Timeout is provided, then the Duration substracts from
+ the actual Timeout value of that Instance. For example an Instance
+ with Timeout of 6 and Duration of 2 will be scheduled exactly 3
+ times. Other Instances have no influence on the Timeout.
+
+ A pre-requisite is that LE is already enabled, otherwise this
+ command will return a "rejected" response.
+
+ This command can be used when the controller is not powered and
+ all settings will be programmed once powered.
+
+ This command generates a Command Complete event on success or a
+ Command Status event on failure.
+
+ Possible errors: Failed
+ Rejected
+ Not Supported
+ Invalid Parameters
+ Invalid Index
+
+
+Remove Advertising Command
+==========================
+
+ Command Code: 0x003f
+ Controller Index: <controller id>
+ Command Parameters: Instance (1 Octet)
+ Return Parameters: Instance (1 Octet)
+
+ This command is used to remove an advertising instance that
+ can be used to switch a Bluetooth Low Energy controller into
+ advertising mode.
+
+ When the Instance parameter is zero, then all previously added
+ advertising Instances will be removed.
+
+ This command can be used when the controller is not powered and
+ all settings will be programmed once powered.
+
+ This command generates a Command Complete event on success or
+ a Command Status event on failure.
+
+ Possible errors: Invalid Parameters
+ Invalid Index
+
+
Command Complete Event
======================
@@ -3200,13 +3469,14 @@ Extended Index Added Event
Event Code: 0x0020
Controller Index: <controller id>
Event Parameters: Controller_Type (1 Octet)
+ Controller_Bus (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.
+ Index Added and Unconfigured Index Added are sent instead.
Extended Index Removed Event
@@ -3215,10 +3485,79 @@ Extended Index Removed Event
Event Code: 0x0021
Controller Index: <controller id>
Event Parameters: Controller_Type (1 Octet)
+ Controller_Bus (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.
+ Index Removed and Unconfigured Index Removed are sent instead.
+
+
+Local Out Of Band Extended Data Updated Event
+=============================================
+
+ Event Code: 0x0022
+ Controller Index: <controller id>
+ Event Parameters: Address_Type (1 Octet)
+ EIR_Data_Length (2 Octets)
+ EIR_Data (0-65535 Octets)
+
+ This event is used when the Read Local Out Of Band Extended Data
+ command has been used and some other user requested a new set
+ of local out-of-band data. This allows for the original caller
+ to adjust the 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)
+
+ The value for EIR_Data_Length and content for EIR_Data is the
+ same as described in Read Local Out Of Band Extended Data command.
+
+ When LE Privacy is used and LE Secure Connections out-of-band
+ data has been requested, then this event will be emitted every
+ time the Resolvable Private Address (RPA) gets changed. The new
+ RPA will be included in the EIR_Data.
+
+ The event will only be sent to management sockets other than the
+ one through which the command was sent. It will additionally also
+ only be sent to sockets that have used the command at least once.
+
+
+Advertising Added Event
+=======================
+
+ Event Code: 0x0023
+ Controller Index: <controller id>
+ Event Parameters: Instance (1 Octet)
+
+ This event indicates that an advertising instance has been added
+ using the Add Advertising command.
+
+ The event will only be sent to management sockets other than the
+ one through which the command was sent.
+
+
+Advertising Removed Event
+=========================
+
+ Event Code: 0x0024
+ Controller Index: <controller id>
+ Event Parameters: Instance (1 Octet)
+
+ This event indicates that an advertising instance has been removed
+ using the Remove Advertising command.
+
+ The event will only be sent to management sockets other than the
+ one through which the command was sent.
diff --git a/doc/settings-storage.txt b/doc/settings-storage.txt
index 6a95ebfc..1839ba3f 100644
--- a/doc/settings-storage.txt
+++ b/doc/settings-storage.txt
@@ -265,4 +265,4 @@ Long term key) related to a remote device.
IdentityAddress String Identity Address of the device
IdentityAddressType String Type of Identity Address of the device
-#endif \ No newline at end of file
+#endif
diff --git a/doc/test-coverage.txt b/doc/test-coverage.txt
index 505bb276..06253c66 100644
--- a/doc/test-coverage.txt
+++ b/doc/test-coverage.txt
@@ -29,9 +29,10 @@ test-gobex-header 28 OBEX header handling
test-gobex-apparam 18 OBEX apparam handling
test-gobex-transfer 36 OBEX transfer handling
test-gdbus-client 13 D-Bus client handling
-test-gatt 131 GATT qualification test cases
+test-gatt 159 GATT qualification test cases
+test-hog 6 HID Over GATT qualification test cases
-----
- 706
+ 740
Automated end-to-end testing
@@ -39,7 +40,7 @@ Automated end-to-end testing
Application Count Description
-------------------------------------------
-mgmt-tester 243 Kernel management interface testing
+mgmt-tester 293 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
@@ -47,7 +48,7 @@ sco-tester 8 Kernel SCO implementation testing
gap-tester 1 Daemon D-Bus API testing
hci-tester 14 Controller hardware testing
-----
- 307
+ 357
Android end-to-end testing
diff --git a/emulator/btdev.c b/emulator/btdev.c
index b50df0f6..9642e866 100644
--- a/emulator/btdev.c
+++ b/emulator/btdev.c
@@ -314,11 +314,8 @@ static void set_common_commands_bredrle(struct btdev *btdev)
btdev->commands[15] |= 0x02; /* Read BD ADDR */
}
-static void set_bredr_commands(struct btdev *btdev)
+static void set_common_commands_bredr20(struct btdev *btdev)
{
- set_common_commands_all(btdev);
- set_common_commands_bredrle(btdev);
-
btdev->commands[0] |= 0x01; /* Inquiry */
btdev->commands[0] |= 0x02; /* Inquiry Cancel */
btdev->commands[0] |= 0x10; /* Create Connection */
@@ -371,6 +368,14 @@ static void set_bredr_commands(struct btdev *btdev)
btdev->commands[14] |= 0x40; /* Read Local Extended Features */
btdev->commands[15] |= 0x01; /* Read Country Code */
btdev->commands[16] |= 0x04; /* Enable Device Under Test Mode */
+}
+
+static void set_bredr_commands(struct btdev *btdev)
+{
+ set_common_commands_all(btdev);
+ set_common_commands_bredrle(btdev);
+ set_common_commands_bredr20(btdev);
+
btdev->commands[16] |= 0x08; /* Setup Synchronous Connection */
btdev->commands[17] |= 0x01; /* Read Extended Inquiry Response */
btdev->commands[17] |= 0x02; /* Write Extended Inquiry Response */
@@ -385,6 +390,13 @@ static void set_bredr_commands(struct btdev *btdev)
btdev->commands[30] |= 0x08; /* Get MWS Transport Layer Config */
}
+static void set_bredr20_commands(struct btdev *btdev)
+{
+ set_common_commands_all(btdev);
+ set_common_commands_bredrle(btdev);
+ set_common_commands_bredr20(btdev);
+}
+
static void set_le_commands(struct btdev *btdev)
{
set_common_commands_all(btdev);
@@ -496,6 +508,25 @@ static void set_bredr_features(struct btdev *btdev)
btdev->max_page = 1;
}
+static void set_bredr20_features(struct btdev *btdev)
+{
+ btdev->features[0] |= 0x04; /* Encryption */
+ btdev->features[0] |= 0x20; /* Role switch */
+ btdev->features[0] |= 0x80; /* Sniff mode */
+ btdev->features[1] |= 0x08; /* SCO link */
+ btdev->features[3] |= 0x40; /* RSSI with inquiry results */
+ btdev->features[3] |= 0x80; /* Extended SCO link */
+ btdev->features[4] |= 0x08; /* AFH capable slave */
+ btdev->features[4] |= 0x10; /* AFH classification slave */
+ btdev->features[5] |= 0x02; /* Sniff subrating */
+ btdev->features[5] |= 0x04; /* Pause encryption */
+ btdev->features[5] |= 0x08; /* AFH capable master */
+ btdev->features[5] |= 0x10; /* AFH classification master */
+ btdev->features[7] |= 0x80; /* Extended features */
+
+ btdev->max_page = 1;
+}
+
static void set_le_features(struct btdev *btdev)
{
btdev->features[4] |= 0x20; /* BR/EDR Not Supported */
@@ -521,31 +552,34 @@ struct btdev *btdev_create(enum btdev_type type, uint16_t id)
btdev->type = type;
btdev->manufacturer = 63;
-
- if (type == BTDEV_TYPE_BREDR)
- btdev->version = 0x05;
- else
- btdev->version = 0x08;
-
btdev->revision = 0x0000;
switch (btdev->type) {
case BTDEV_TYPE_BREDRLE:
+ btdev->version = 0x08;
set_bredrle_features(btdev);
set_bredrle_commands(btdev);
break;
case BTDEV_TYPE_BREDR:
+ btdev->version = 0x05;
set_bredr_features(btdev);
set_bredr_commands(btdev);
break;
case BTDEV_TYPE_LE:
+ btdev->version = 0x08;
set_le_features(btdev);
set_le_commands(btdev);
break;
case BTDEV_TYPE_AMP:
+ btdev->version = 0x01;
set_amp_features(btdev);
set_amp_commands(btdev);
break;
+ case BTDEV_TYPE_BREDR20:
+ btdev->version = 0x04;
+ set_bredr20_features(btdev);
+ set_bredr20_commands(btdev);
+ break;
}
btdev->page_scan_interval = 0x0800;
diff --git a/emulator/btdev.h b/emulator/btdev.h
index 32c708f7..4b724a7b 100644
--- a/emulator/btdev.h
+++ b/emulator/btdev.h
@@ -62,6 +62,7 @@ enum btdev_type {
BTDEV_TYPE_BREDR,
BTDEV_TYPE_LE,
BTDEV_TYPE_AMP,
+ BTDEV_TYPE_BREDR20,
};
enum btdev_hook_type {
diff --git a/emulator/hciemu.c b/emulator/hciemu.c
index fe2f6628..4881a249 100644
--- a/emulator/hciemu.c
+++ b/emulator/hciemu.c
@@ -327,6 +327,9 @@ struct hciemu *hciemu_new(enum hciemu_type type)
case HCIEMU_TYPE_LE:
hciemu->btdev_type = BTDEV_TYPE_LE;
break;
+ case HCIEMU_TYPE_LEGACY:
+ hciemu->btdev_type = BTDEV_TYPE_BREDR20;
+ break;
default:
return NULL;
}
diff --git a/emulator/hciemu.h b/emulator/hciemu.h
index d948867b..41ca3fce 100644
--- a/emulator/hciemu.h
+++ b/emulator/hciemu.h
@@ -30,6 +30,7 @@ enum hciemu_type {
HCIEMU_TYPE_BREDRLE,
HCIEMU_TYPE_BREDR,
HCIEMU_TYPE_LE,
+ HCIEMU_TYPE_LEGACY,
};
enum hciemu_hook_type {
diff --git a/emulator/hfp.c b/emulator/hfp.c
index 1edecb94..e70054ad 100644
--- a/emulator/hfp.c
+++ b/emulator/hfp.c
@@ -30,6 +30,7 @@
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
+#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
diff --git a/emulator/le.c b/emulator/le.c
index 755c6ae1..dc51469c 100644
--- a/emulator/le.c
+++ b/emulator/le.c
@@ -32,6 +32,7 @@
#include <string.h>
#include <sys/socket.h>
#include <sys/un.h>
+#include <sys/uio.h>
#include "lib/bluetooth.h"
#include "lib/hci.h"
diff --git a/emulator/vhci.c b/emulator/vhci.c
index 0001a4b6..6bba4e2c 100644
--- a/emulator/vhci.c
+++ b/emulator/vhci.c
@@ -32,6 +32,7 @@
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
+#include <sys/uio.h>
#include "lib/bluetooth.h"
#include "lib/hci.h"
diff --git a/gdbus/client.c b/gdbus/client.c
index fe0c0dbf..48711ae8 100644
--- a/gdbus/client.c
+++ b/gdbus/client.c
@@ -1111,7 +1111,8 @@ static void get_managed_objects(GDBusClient *client)
if (!client->connected)
return;
- if (!client->proxy_added && !client->proxy_removed) {
+ if ((!client->proxy_added && !client->proxy_removed) ||
+ !client->root_path) {
refresh_properties(client);
return;
}
@@ -1212,7 +1213,7 @@ GDBusClient *g_dbus_client_new_full(DBusConnection *connection,
GDBusClient *client;
unsigned int i;
- if (!connection || !service || !root_path)
+ if (!connection || !service)
return NULL;
client = g_try_new0(GDBusClient, 1);
@@ -1238,6 +1239,10 @@ GDBusClient *g_dbus_client_new_full(DBusConnection *connection,
service_connect,
service_disconnect,
client, NULL);
+
+ if (!root_path)
+ return g_dbus_client_ref(client);
+
client->added_watch = g_dbus_add_signal_watch(connection, service,
client->root_path,
DBUS_INTERFACE_OBJECT_MANAGER,
diff --git a/gdbus/gdbus.h b/gdbus/gdbus.h
index 98148386..ffb12cb7 100644
--- a/gdbus/gdbus.h
+++ b/gdbus/gdbus.h
@@ -271,6 +271,12 @@ gboolean g_dbus_emit_signal_valist(DBusConnection *connection,
const char *path, const char *interface,
const char *name, int type, va_list args);
+#ifdef GATT_NO_RELAY
+gboolean g_dbus_emit_signal_to_dest(DBusConnection *connection,
+ const char *dest, const char *path,
+ const char *interface, const char *name, int type, ...);
+#endif
+
guint g_dbus_add_service_watch(DBusConnection *connection, const char *name,
GDBusWatchFunction connect,
GDBusWatchFunction disconnect,
diff --git a/gdbus/object.c b/gdbus/object.c
index d5456d2b..94f074a0 100644
--- a/gdbus/object.c
+++ b/gdbus/object.c
@@ -1673,6 +1673,65 @@ gboolean g_dbus_emit_signal(DBusConnection *connection,
return result;
}
+#ifdef GATT_NO_RELAY
+static gboolean g_dbus_emit_signal_valist_to_dest(DBusConnection *connection,
+ const char *dest, const char *path, const char *interface,
+ const char *name, int type, va_list args)
+{
+ DBusMessage *signal;
+ dbus_bool_t ret;
+ const GDBusArgInfo *args_info;
+
+ if (!check_signal(connection, path, interface, name, &args_info))
+ return FALSE;
+
+ signal = dbus_message_new_signal(path, interface, name);
+ if (signal == NULL) {
+ error("Unable to allocate new %s.%s signal", interface, name);
+ return FALSE;
+ }
+
+ ret = dbus_message_append_args_valist(signal, type, args);
+ if (!ret)
+ goto fail;
+
+ if (g_dbus_args_have_signature(args_info, signal) == FALSE) {
+ error("%s.%s: got unexpected signature '%s'", interface, name,
+ dbus_message_get_signature(signal));
+ ret = FALSE;
+ goto fail;
+ }
+
+ ret = dbus_message_set_destination(signal, dest);
+ if (!ret)
+ error("Fail to set destination");
+
+ return g_dbus_send_message(connection, signal);
+
+fail:
+ dbus_message_unref(signal);
+
+ return ret;
+}
+
+gboolean g_dbus_emit_signal_to_dest(DBusConnection *connection,
+ const char *dest, const char *path,
+ const char *interface, const char *name, int type, ...)
+{
+ va_list args;
+ gboolean result;
+
+ va_start(args, type);
+
+ result = g_dbus_emit_signal_valist_to_dest(connection, dest, path,
+ interface, name, type, args);
+
+ va_end(args);
+
+ return result;
+}
+#endif
+
gboolean g_dbus_emit_signal_valist(DBusConnection *connection,
const char *path, const char *interface,
const char *name, int type, va_list args)
diff --git a/lib/bluetooth.c b/lib/bluetooth.c
index 4fe5941a..91e714e7 100644
--- a/lib/bluetooth.c
+++ b/lib/bluetooth.c
@@ -1239,6 +1239,30 @@ const char *bt_compidtostr(int compid)
return "IPS Group Inc.";
case 488:
return "STIR";
+ case 489:
+ return "Sano, Inc";
+ case 490:
+ return "Advanced Application Design, Inc.";
+ case 491:
+ return "AutoMap LLC";
+ case 492:
+ return "Spreadtrum Communications Shanghai Ltd";
+ case 493:
+ return "CuteCircuit LTD";
+ case 494:
+ return "Valeo Service";
+ case 495:
+ return "Fullpower Technologies, Inc.";
+ case 496:
+ return "KloudNation";
+ case 497:
+ return "Zebra Technologies Corporation";
+ case 498:
+ return "Itron, Inc.";
+ case 499:
+ return "The University of Tokyo";
+ case 500:
+ return "UTC Fire and Security";
case 65535:
return "internal use";
default:
diff --git a/lib/bluetooth.h b/lib/bluetooth.h
index 6ca64b68..852a6b23 100644
--- a/lib/bluetooth.h
+++ b/lib/bluetooth.h
@@ -345,8 +345,8 @@ typedef struct {
static inline void bswap_128(const void *src, void *dst)
{
- const uint8_t *s = src;
- uint8_t *d = dst;
+ const uint8_t *s = (const uint8_t *) src;
+ uint8_t *d = (uint8_t *) dst;
int i;
for (i = 0; i < 16; i++)
diff --git a/lib/bnep.h b/lib/bnep.h
index 2bbfb177..0a748361 100644
--- a/lib/bnep.h
+++ b/lib/bnep.h
@@ -103,6 +103,12 @@ struct bnep_set_filter_req {
uint8_t list[0];
} __attribute__((packed));
+struct bnep_ctrl_cmd_not_understood_cmd {
+ uint8_t type;
+ uint8_t ctrl;
+ uint8_t unkn_ctrl;
+} __attribute__((packed));
+
struct bnep_control_rsp {
uint8_t type;
uint8_t ctrl;
@@ -120,6 +126,11 @@ struct bnep_ext_hdr {
#define BNEPCONNDEL _IOW('B', 201, int)
#define BNEPGETCONNLIST _IOR('B', 210, int)
#define BNEPGETCONNINFO _IOR('B', 211, int)
+#ifdef __TIZEN_PATCH__
+#define BNEPGETSUPPFEAT _IOR('B', 212, int)
+
+#define BNEP_SETUP_RESPONSE 0
+#endif
struct bnep_connadd_req {
int sock; /* Connected socket */
diff --git a/lib/hci_lib.h b/lib/hci_lib.h
index 55aeb176..c766f118 100644
--- a/lib/hci_lib.h
+++ b/lib/hci_lib.h
@@ -141,7 +141,18 @@ int hci_le_clear_resolving_list(int dd, int to);
int hci_le_read_resolving_list_size(int dd, uint8_t *size, int to);
int hci_le_set_address_resolution_enable(int dev_id, uint8_t enable, int to);
int hci_le_read_remote_features(int dd, uint16_t handle, uint8_t *features, int to);
-
+#ifdef __TIZEN_PATCH__
+int hci_le_read_maximum_data_length(
+ int dd, uint8_t *status, uint16_t *tx_octets,
+ uint16_t *tx_time, uint16_t *rx_octets,
+ uint16_t *rx_time, int to );
+int hci_le_write_host_suggested_data_length(int dd, uint16_t *def_tx_octets,
+ uint16_t *def_tx_time, int to);
+int hci_le_read_host_suggested_data_length(int dd, uint8_t *status,
+ uint16_t *def_tx_octets, uint16_t *def_tx_time, int to);
+int hci_le_set_data_length(int dd, const bdaddr_t *bdaddr,
+ uint16_t *max_tx_octets, uint16_t *max_tx_time, int to);
+#endif
int hci_for_each_dev(int flag, int(*func)(int dd, int dev_id, long arg), long arg);
int hci_get_route(bdaddr_t *bdaddr);
diff --git a/lib/mgmt.h b/lib/mgmt.h
index 6bec10e6..09448f56 100755
--- a/lib/mgmt.h
+++ b/lib/mgmt.h
@@ -46,6 +46,8 @@
#define MGMT_STATUS_CANCELLED 0x10
#define MGMT_STATUS_INVALID_INDEX 0x11
#define MGMT_STATUS_RFKILLED 0x12
+#define MGMT_STATUS_ALREADY_PAIRED 0x13
+#define MGMT_STATUS_PERMISSION_DENIED 0x14
struct mgmt_hdr {
uint16_t opcode;
@@ -272,14 +274,10 @@ struct mgmt_cp_user_passkey_neg_reply {
#define MGMT_OP_READ_LOCAL_OOB_DATA 0x0020
struct mgmt_rp_read_local_oob_data {
- uint8_t hash[16];
- uint8_t randomizer[16];
-} __packed;
-struct mgmt_rp_read_local_oob_ext_data {
uint8_t hash192[16];
- uint8_t randomizer192[16];
+ uint8_t rand192[16];
uint8_t hash256[16];
- uint8_t randomizer256[16];
+ uint8_t rand256[16];
} __packed;
#define MGMT_OP_ADD_REMOTE_OOB_DATA 0x0021
@@ -455,7 +453,67 @@ struct mgmt_cp_start_service_discovery {
uint16_t uuid_count;
uint8_t uuids[0][16];
} __packed;
-#define MGMT_START_SERVICE_DISCOVERY_SIZE 4
+
+#define MGMT_OP_READ_LOCAL_OOB_EXT_DATA 0x003B
+struct mgmt_cp_read_local_oob_ext_data {
+ uint8_t type;
+} __packed;
+struct mgmt_rp_read_local_oob_ext_data {
+ uint8_t type;
+ uint16_t eir_len;
+ uint8_t eir[0];
+} __packed;
+
+#define MGMT_OP_READ_EXT_INDEX_LIST 0x003C
+struct mgmt_rp_read_ext_index_list {
+ uint16_t num_controllers;
+ struct {
+ uint16_t index;
+ uint8_t type;
+ uint8_t bus;
+ } entry[0];
+} __packed;
+
+#define MGMT_OP_READ_ADV_FEATURES 0x003D
+struct mgmt_rp_read_adv_features {
+ uint32_t supported_flags;
+ uint8_t max_adv_data_len;
+ uint8_t max_scan_rsp_len;
+ uint8_t max_instances;
+ uint8_t num_instances;
+ uint8_t instance[0];
+} __packed;
+
+#define MGMT_OP_ADD_ADVERTISING 0x003E
+struct mgmt_cp_add_advertising {
+ uint8_t instance;
+ uint32_t flags;
+ uint16_t duration;
+ uint16_t timeout;
+ uint8_t adv_data_len;
+ uint8_t scan_rsp_len;
+ uint8_t data[0];
+} __packed;
+struct mgmt_rp_add_advertising {
+ uint8_t instance;
+} __packed;
+
+#define MGMT_ADV_FLAG_CONNECTABLE (1 << 0)
+#define MGMT_ADV_FLAG_DISCOV (1 << 1)
+#define MGMT_ADV_FLAG_LIMITED_DISCOV (1 << 2)
+#define MGMT_ADV_FLAG_MANAGED_FLAGS (1 << 3)
+#define MGMT_ADV_FLAG_TX_POWER (1 << 4)
+#define MGMT_ADV_FLAG_APPEARANCE (1 << 5)
+#define MGMT_ADV_FLAG_LOCAL_NAME (1 << 6)
+
+#define MGMT_OP_REMOVE_ADVERTISING 0x003F
+struct mgmt_cp_remove_advertising {
+ uint8_t instance;
+} __packed;
+#define MGMT_REMOVE_ADVERTISING_SIZE 1
+struct mgmt_rp_remove_advertising {
+ uint8_t instance;
+} __packed;
#define MGMT_EV_CMD_COMPLETE 0x0001
struct mgmt_ev_cmd_complete {
@@ -483,7 +541,7 @@ struct mgmt_ev_controller_error {
#define MGMT_EV_CLASS_OF_DEV_CHANGED 0x0007
struct mgmt_ev_class_of_dev_changed {
- uint8_t class_of_dev[3];
+ uint8_t dev_class[3];
} __packed;
#define MGMT_EV_LOCAL_NAME_CHANGED 0x0008
@@ -640,6 +698,35 @@ struct mgmt_ev_new_conn_param {
#define MGMT_EV_NEW_CONFIG_OPTIONS 0x001f
+#define MGMT_EV_EXT_INDEX_ADDED 0x0020
+struct mgmt_ev_ext_index_added {
+ uint8_t type;
+ uint8_t bus;
+} __packed;
+
+#define MGMT_EV_EXT_INDEX_REMOVED 0x0021
+struct mgmt_ev_ext_index_removed {
+ uint8_t type;
+ uint8_t bus;
+} __packed;
+
+#define MGMT_EV_LOCAL_OOB_DATA_UPDATED 0x0022
+struct mgmt_ev_local_oob_data_updated {
+ uint8_t type;
+ uint16_t eir_len;
+ uint8_t eir[0];
+} __packed;
+
+#define MGMT_EV_ADVERTISING_ADDED 0x0023
+struct mgmt_ev_advertising_added {
+ uint8_t instance;
+} __packed;
+
+#define MGMT_EV_ADVERTISING_REMOVED 0x0024
+struct mgmt_ev_advertising_removed {
+ uint8_t instance;
+} __packed;
+
static const char *mgmt_op[] = {
"<0x0000>",
"Read Version",
@@ -649,7 +736,7 @@ static const char *mgmt_op[] = {
"Set Powered",
"Set Discoverable",
"Set Connectable",
- "Set Fast Connectable", /* 0x0008 */
+ "Set Fast Connectable", /* 0x0008 */
"Set Bondable",
"Set Link Security",
"Set Secure Simple Pairing",
@@ -657,7 +744,7 @@ static const char *mgmt_op[] = {
"Set Low Energy",
"Set Dev Class",
"Set Local Name",
- "Add UUID", /* 0x0010 */
+ "Add UUID", /* 0x0010 */
"Remove UUID",
"Load Link Keys",
"Load Long Term Keys",
@@ -665,7 +752,7 @@ static const char *mgmt_op[] = {
"Get Connections",
"PIN Code Reply",
"PIN Code Neg Reply",
- "Set IO Capability", /* 0x0018 */
+ "Set IO Capability", /* 0x0018 */
"Pair Device",
"Cancel Pair Device",
"Unpair Device",
@@ -673,7 +760,7 @@ static const char *mgmt_op[] = {
"User Confirm Neg Reply",
"User Passkey Reply",
"User Passkey Neg Reply",
- "Read Local OOB Data", /* 0x0020 */
+ "Read Local OOB Data", /* 0x0020 */
"Add Remote OOB Data",
"Remove Remove OOB Data",
"Start Discovery",
@@ -681,7 +768,7 @@ static const char *mgmt_op[] = {
"Confirm Name",
"Block Device",
"Unblock Device",
- "Set Device ID",
+ "Set Device ID", /* 0x0028 */
"Set Advertising",
"Set BR/EDR",
"Set Static Address",
@@ -689,7 +776,7 @@ static const char *mgmt_op[] = {
"Set Secure Connections",
"Set Debug Keys",
"Set Privacy",
- "Load Identity Resolving Keys",
+ "Load Identity Resolving Keys", /* 0x0030 */
"Get Connection Information",
"Get Clock Information",
"Add Device",
@@ -697,9 +784,14 @@ static const char *mgmt_op[] = {
"Load Connection Parameters",
"Read Unconfigured Index List",
"Read Controller Configuration Information",
- "Set External Configuration",
+ "Set External Configuration", /* 0x0038 */
"Set Public Address",
"Start Service Discovery",
+ "Read Local Out Of Band Extended Data",
+ "Read Extended Controller Index List",
+ "Read Advertising Features",
+ "Add Advertising",
+ "Remove Advertising",
};
static const char *mgmt_ev[] = {
@@ -735,6 +827,11 @@ static const char *mgmt_ev[] = {
"Unconfigured Index Added",
"Unconfigured Index Removed",
"New Configuration Options",
+ "Extended Index Added",
+ "Extended Index Removed",
+ "Local Out Of Band Extended Data Updated",
+ "Advertising Added",
+ "Advertising Removed",
};
static const char *mgmt_status[] = {
@@ -757,6 +854,8 @@ static const char *mgmt_status[] = {
"Cancelled",
"Invalid Index",
"Blocked through rfkill",
+ "Already Paired",
+ "Permission Denied",
};
#ifdef __TIZEN_PATCH__
@@ -938,6 +1037,12 @@ struct mgmt_cp_le_set_data_length {
} __packed;
#define MGMT_LE_SET_DATA_LENGTH_SIZE 10
+#define MGMT_OP_SET_DEV_RPA_RES_SUPPORT (TIZEN_OP_CODE_BASE + 0x19)
+struct mgmt_cp_set_dev_rpa_res_support {
+ struct mgmt_addr_info addr;
+ uint8_t res_support;
+} __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)
@@ -1069,6 +1174,7 @@ struct mgmt_ev_6lowpan_conn_state_changed {
uint8_t connected;
} __packed;
+
#define MGMT_EV_LE_DATA_LENGTH_CHANGED (TIZEN_EV_BASE + 0x0d)
struct mgmt_ev_le_data_length_changed {
struct mgmt_addr_info addr;
diff --git a/lib/uuid.h b/lib/uuid.h
index 05968f10..98e96276 100644
--- a/lib/uuid.h
+++ b/lib/uuid.h
@@ -130,6 +130,9 @@ extern "C" {
#define GATT_CHARAC_SOFTWARE_REVISION_STRING 0x2A28
#define GATT_CHARAC_MANUFACTURER_NAME_STRING 0x2A29
#define GATT_CHARAC_PNP_ID 0x2A50
+#ifdef __TIZEN_PATCH__
+#define GATT_CHARAC_CENTRAL_RPA_RESOLUTION 0x2AA6
+#endif
/* GATT Characteristic Descriptors */
#define GATT_CHARAC_EXT_PROPER_UUID 0x2900
diff --git a/monitor/btsnoop.c b/monitor/btsnoop.c
index a32335ac..35005ffa 100644
--- a/monitor/btsnoop.c
+++ b/monitor/btsnoop.c
@@ -66,7 +66,16 @@ static uint32_t btsnoop_type = 0;
static int btsnoop_fd = -1;
static uint16_t btsnoop_index = 0xffff;
+#ifdef __TIZEN_PATCH__
+static char *btsnoop_path = NULL;
+static int16_t btsnoop_rotate = -1;
+static ssize_t btsnoop_size = -1;
+
+void btsnoop_create(const char *path, uint32_t type,
+ int16_t rotate_count, ssize_t file_size)
+#else
void btsnoop_create(const char *path, uint32_t type)
+#endif
{
struct btsnoop_hdr hdr;
ssize_t written;
@@ -91,8 +100,103 @@ void btsnoop_create(const char *path, uint32_t type)
btsnoop_fd = -1;
return;
}
+
+#ifdef __TIZEN_PATCH__
+ if (rotate_count > 0 && file_size > 0) {
+ btsnoop_path = strdup(path);
+ btsnoop_rotate = rotate_count;
+ btsnoop_size = file_size;
+ }
+#endif
+}
+
+#ifdef __TIZEN_PATCH__
+static void btsnoop_create_2(void)
+{
+ struct btsnoop_hdr hdr;
+ ssize_t written;
+
+ if (btsnoop_fd >= 0)
+ close(btsnoop_fd);
+
+ btsnoop_fd = open(btsnoop_path,
+ O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ if (btsnoop_fd < 0)
+ goto fail;
+
+ memcpy(hdr.id, btsnoop_id, sizeof(btsnoop_id));
+ hdr.version = htobe32(btsnoop_version);
+ hdr.type = htobe32(btsnoop_type);
+
+ written = write(btsnoop_fd, &hdr, BTSNOOP_HDR_SIZE);
+ if (written < 0) {
+ close(btsnoop_fd);
+ btsnoop_fd = -1;
+ goto fail;
+ }
+
+ return;
+
+fail:
+ free(btsnoop_path);
+ btsnoop_rotate = -1;
+ btsnoop_size = -1;
+ btsnoop_index = 0xffff;
+
+ return;
}
+static void btsnoop_rotate_files(void)
+{
+ char *filename = NULL;
+ char *new_filename = NULL;
+ int i;
+ int postfix_width = 0;
+ int err;
+
+ if (btsnoop_rotate <= 1)
+ return;
+
+ for (i = btsnoop_rotate / 10; i; i /= 10)
+ postfix_width++;
+
+ for (i = btsnoop_rotate - 2; i >= 0; i--) {
+ if (i == 0) {
+ filename = strdup(btsnoop_path);
+ err = (filename == NULL) ? -1 : 0;
+ } else {
+ err = asprintf(&filename, "%s.%0*d",
+ btsnoop_path, postfix_width, i);
+ }
+
+ if (err < 0 || access(filename, F_OK) < 0)
+ goto done;
+
+ err = asprintf(&new_filename, "%s.%0*d",
+ btsnoop_path, postfix_width, i + 1);
+ if (err < 0)
+ goto done;
+
+ err = rename(filename, new_filename)
+
+done:
+ if (new_filename) {
+ free(new_filename);
+ new_filename = NULL;
+ }
+
+ if (filename) {
+ free(filename);
+ filename = NULL;
+ }
+
+ if (err < 0)
+ break;
+ }
+}
+#endif
+
void btsnoop_write(struct timeval *tv, uint32_t flags,
const void *data, uint16_t size)
{
@@ -108,6 +212,17 @@ void btsnoop_write(struct timeval *tv, uint32_t flags,
pkt.drops = htobe32(0);
pkt.ts = htobe64(ts + 0x00E03AB44A676000ll);
+#ifdef __TIZEN_PATCH__
+ if ((btsnoop_rotate > 0 && btsnoop_size > 0) &&
+ lseek(btsnoop_fd, 0x00, SEEK_CUR) +
+ BTSNOOP_PKT_SIZE + size > btsnoop_size) {
+ btsnoop_rotate_files();
+ btsnoop_create_2();
+ if (btsnoop_fd < 0)
+ return;
+ }
+#endif
+
written = write(btsnoop_fd, &pkt, BTSNOOP_PKT_SIZE);
if (written < 0)
return;
@@ -423,6 +538,15 @@ void btsnoop_close(void)
if (btsnoop_fd < 0)
return;
+#ifdef __TIZEN_PATCH__
+ if (btsnoop_path) {
+ free(btsnoop_path);
+ btsnoop_path = NULL;
+ }
+ btsnoop_rotate = -1;
+ btsnoop_size = -1;
+#endif
+
close(btsnoop_fd);
btsnoop_fd = -1;
diff --git a/monitor/btsnoop.h b/monitor/btsnoop.h
index be4e2ed2..10f76f20 100644
--- a/monitor/btsnoop.h
+++ b/monitor/btsnoop.h
@@ -48,7 +48,12 @@ struct btsnoop_opcode_new_index {
char name[8];
} __attribute__((packed));
+#ifdef __TIZEN_PATCH__
+void btsnoop_create(const char *path, uint32_t type,
+ int16_t rotate_count, ssize_t file_size);
+#else
void btsnoop_create(const char *path, uint32_t type);
+#endif
void btsnoop_write(struct timeval *tv, uint32_t flags,
const void *data, uint16_t size);
void btsnoop_write_hci(struct timeval *tv, uint16_t index, uint16_t opcode,
diff --git a/monitor/control.c b/monitor/control.c
index 44cc1daf..e88a6e34 100644
--- a/monitor/control.c
+++ b/monitor/control.c
@@ -97,6 +97,40 @@ static void mgmt_unconf_index_removed(uint16_t len, const void *buf)
packet_hexdump(buf, len);
}
+static void mgmt_ext_index_added(uint16_t len, const void *buf)
+{
+ const struct mgmt_ev_ext_index_added *ev = buf;
+
+ if (len < sizeof(*ev)) {
+ printf("* Malformed Extended Index Added control\n");
+ return;
+ }
+
+ printf("@ Extended Index Added: %u (%u)\n", ev->type, ev->bus);
+
+ buf += sizeof(*ev);
+ len -= sizeof(*ev);
+
+ packet_hexdump(buf, len);
+}
+
+static void mgmt_ext_index_removed(uint16_t len, const void *buf)
+{
+ const struct mgmt_ev_ext_index_removed *ev = buf;
+
+ if (len < sizeof(*ev)) {
+ printf("* Malformed Extended Index Removed control\n");
+ return;
+ }
+
+ printf("@ Extended Index Removed: %u (%u)\n", ev->type, ev->bus);
+
+ buf += sizeof(*ev);
+ len -= sizeof(*ev);
+
+ packet_hexdump(buf, len);
+}
+
static void mgmt_controller_error(uint16_t len, const void *buf)
{
const struct mgmt_ev_controller_error *ev = buf;
@@ -197,9 +231,9 @@ static void mgmt_class_of_dev_changed(uint16_t len, const void *buf)
}
printf("@ Class of Device Changed: 0x%2.2x%2.2x%2.2x\n",
- ev->class_of_dev[2],
- ev->class_of_dev[1],
- ev->class_of_dev[0]);
+ ev->dev_class[2],
+ ev->dev_class[1],
+ ev->dev_class[0]);
buf += sizeof(*ev);
len -= sizeof(*ev);
@@ -704,6 +738,40 @@ static void mgmt_new_conn_param(uint16_t len, const void *buf)
packet_hexdump(buf, len);
}
+static void mgmt_advertising_added(uint16_t len, const void *buf)
+{
+ const struct mgmt_ev_advertising_added *ev = buf;
+
+ if (len < sizeof(*ev)) {
+ printf("* Malformed Advertising Added control\n");
+ return;
+ }
+
+ printf("@ Advertising Added: %u\n", ev->instance);
+
+ buf += sizeof(*ev);
+ len -= sizeof(*ev);
+
+ packet_hexdump(buf, len);
+}
+
+static void mgmt_advertising_removed(uint16_t len, const void *buf)
+{
+ const struct mgmt_ev_advertising_removed *ev = buf;
+
+ if (len < sizeof(*ev)) {
+ printf("* Malformed Advertising Removed control\n");
+ return;
+ }
+
+ printf("@ Advertising Removed: %u\n", ev->instance);
+
+ buf += sizeof(*ev);
+ len -= sizeof(*ev);
+
+ packet_hexdump(buf, len);
+}
+
void control_message(uint16_t opcode, const void *data, uint16_t size)
{
switch (opcode) {
@@ -794,6 +862,18 @@ void control_message(uint16_t opcode, const void *data, uint16_t size)
case MGMT_EV_NEW_CONFIG_OPTIONS:
mgmt_new_config_options(size, data);
break;
+ case MGMT_EV_EXT_INDEX_ADDED:
+ mgmt_ext_index_added(size, data);
+ break;
+ case MGMT_EV_EXT_INDEX_REMOVED:
+ mgmt_ext_index_removed(size, data);
+ break;
+ case MGMT_EV_ADVERTISING_ADDED:
+ mgmt_advertising_added(size, data);
+ break;
+ case MGMT_EV_ADVERTISING_REMOVED:
+ mgmt_advertising_removed(size, data);
+ break;
default:
printf("* Unknown control (code %d len %d)\n", opcode, size);
packet_hexdump(data, size);
@@ -1044,9 +1124,18 @@ void control_server(const char *path)
server_fd = fd;
}
+#ifdef __TIZEN_PATCH__
+bool control_writer(const char *path, int16_t rotate_count, ssize_t file_size)
+#else
bool control_writer(const char *path)
+#endif
{
+#ifdef __TIZEN_PATCH__
+ btsnoop_file = btsnoop_create(path, BTSNOOP_TYPE_MONITOR,
+ rotate_count, file_size);
+#else
btsnoop_file = btsnoop_create(path, BTSNOOP_TYPE_MONITOR);
+#endif
return !!btsnoop_file;
}
diff --git a/monitor/control.h b/monitor/control.h
index 28f16db2..87876d02 100644
--- a/monitor/control.h
+++ b/monitor/control.h
@@ -24,7 +24,11 @@
#include <stdint.h>
+#ifdef __TIZEN_PATCH__
+bool control_writer(const char *path, int16_t rotate_count, ssize_t file_size);
+#else
bool control_writer(const char *path);
+#endif
void control_reader(const char *path);
void control_server(const char *path);
int control_tracing(void);
diff --git a/monitor/l2cap.c b/monitor/l2cap.c
index 5a2ee911..5faa26fd 100644
--- a/monitor/l2cap.c
+++ b/monitor/l2cap.c
@@ -2045,15 +2045,6 @@ static void att_error_response(const struct l2cap_frame *frame)
case 0x11:
str = "Insufficient Resources";
break;
- case 0xfd:
- str = "CCC Improperly Configured";
- break;
- case 0xfe:
- str = "Procedure Already in Progress";
- break;
- case 0xff:
- str = "Out of Range";
- break;
default:
str = "Reserved";
break;
diff --git a/monitor/main.c b/monitor/main.c
index de48db57..90ecf35e 100644
--- a/monitor/main.c
+++ b/monitor/main.c
@@ -66,6 +66,10 @@ static void usage(void)
"\t-T, --date Show time and date information\n"
"\t-S, --sco Dump SCO traffic\n"
"\t-E, --ellisys [ip] Send Ellisys HCI Injection\n"
+#ifdef __TIZEN_PATCH__
+ "\t-C, --count <num> Save traces by <num> rotation\n"
+ "\t-W, --size <num> Save traces at most <num> size\n"
+#endif
"\t-h, --help Show help options\n");
}
@@ -79,6 +83,10 @@ static const struct option main_options[] = {
{ "date", no_argument, NULL, 'T' },
{ "sco", no_argument, NULL, 'S' },
{ "ellisys", required_argument, NULL, 'E' },
+#ifdef __TIZEN_PATCH__
+ { "count", required_argument, NULL, 'C' },
+ { "size", required_argument, NULL, 'W' },
+#endif
{ "todo", no_argument, NULL, '#' },
{ "version", no_argument, NULL, 'v' },
{ "help", no_argument, NULL, 'h' },
@@ -93,6 +101,10 @@ int main(int argc, char *argv[])
const char *analyze_path = NULL;
const char *ellisys_server = NULL;
unsigned short ellisys_port = 0;
+#ifdef __TIZEN_PATCH__
+ int16_t rotate_count = -1;
+ ssize_t file_size = -1;
+#endif
const char *str;
int exit_status;
sigset_t mask;
@@ -104,8 +116,13 @@ int main(int argc, char *argv[])
for (;;) {
int opt;
+#ifdef __TIZEN_PATCH__
+ opt = getopt_long(argc, argv, "r:w:a:s:i:tTSE:C:W:vh",
+ main_options, NULL);
+#else
opt = getopt_long(argc, argv, "r:w:a:s:i:tTSE:vh",
main_options, NULL);
+#endif
if (opt < 0)
break;
@@ -149,6 +166,14 @@ int main(int argc, char *argv[])
ellisys_server = optarg;
ellisys_port = 24352;
break;
+#ifdef __TIZEN_PATCH__
+ case 'C':
+ rotate_count = atoi(optarg);
+ break;
+ case 'W':
+ file_size = atoll(optarg);
+ break;
+#endif
case '#':
packet_todo();
lmp_todo();
@@ -199,10 +224,18 @@ int main(int argc, char *argv[])
return EXIT_SUCCESS;
}
+#ifdef __TIZEN_PATCH__
+ if (writer_path && !control_writer(writer_path,
+ rotate_count, file_size)) {
+ printf("Failed to open '%s'\n", writer_path);
+ return EXIT_FAILURE;
+ }
+#else
if (writer_path && !control_writer(writer_path)) {
printf("Failed to open '%s'\n", writer_path);
return EXIT_FAILURE;
}
+#endif
if (ellisys_server)
ellisys_enable(ellisys_server, ellisys_port);
diff --git a/monitor/packet.c b/monitor/packet.c
index 8db485f9..b2267a66 100644
--- a/monitor/packet.c
+++ b/monitor/packet.c
@@ -5872,7 +5872,7 @@ static void le_set_adv_parameters_cmd(const void *data, uint8_t size)
str = "Scannable undirected - ADV_SCAN_IND";
break;
case 0x03:
- str = "Non connectable undirect - ADV_NONCONN_IND";
+ str = "Non connectable undirected - ADV_NONCONN_IND";
break;
case 0x04:
str = "Connectable directed - ADV_DIRECT_IND (low duty cycle)";
diff --git a/obexd/client/mns-tizen.c b/obexd/client/mns-tizen.c
index 49522259..b254cd3a 100644
--- a/obexd/client/mns-tizen.c
+++ b/obexd/client/mns-tizen.c
@@ -30,6 +30,8 @@
#include <string.h>
#include "log.h"
+#include "gobex/gobex.h"
+#include "gobex/gobex-apparam.h"
#include "transfer.h"
#include "session.h"
@@ -135,7 +137,8 @@ static DBusMessage *send_event(DBusConnection *connection,
{
struct mns_data *mns = user_data;
struct obc_transfer *transfer;
- struct sendevent_apparam apparam;
+ guint8 masinstanceid;
+ GObexApparam *apparam;
gchar *event_type;
gchar *folder;
gchar *old_folder;
@@ -170,12 +173,12 @@ static DBusMessage *send_event(DBusConnection *connection,
if (transfer == NULL)
goto fail;
- apparam.masinstanceid_tag = MAP_AP_MASINSTANCEID;
- apparam.masinstanceid_len = 1;
/* Obexd currently supports single SDP for MAS */
- apparam.masinstanceid = 0;
+ masinstanceid = 0;
+ apparam = g_obex_apparam_set_uint8(NULL, MAP_AP_MASINSTANCEID,
+ masinstanceid);
- obc_transfer_set_apparam(transfer, &apparam);
+ obc_transfer_set_apparam(transfer, apparam);
if (obc_session_queue(mns->session, transfer, NULL, NULL, &err))
return dbus_message_new_method_return(message);
diff --git a/obexd/client/pbap.c b/obexd/client/pbap.c
index 4b868f00..c803437d 100644
--- a/obexd/client/pbap.c
+++ b/obexd/client/pbap.c
@@ -218,7 +218,11 @@ static char *build_phonebook_path(const char *location, const char *item)
internal = TRUE;
} else if (!g_ascii_strncasecmp(location, "sim", 3)) {
if (strlen(location) == 3)
+#ifdef __TIZEN_PATCH__
+ tmp = g_strdup("SIM1");
+#else
tmp = g_strdup("sim1");
+#endif
else
tmp = g_ascii_strup(location, 4);
diff --git a/obexd/plugins/mas.c b/obexd/plugins/mas.c
index 7036497f..8d0531a2 100644
--- a/obexd/plugins/mas.c
+++ b/obexd/plugins/mas.c
@@ -178,9 +178,14 @@ static void mas_disconnect(struct obex_session *os, void *user_data)
DBG("");
manager_unregister_session(os);
- messages_disconnect(mas->backend_data);
+#ifdef __TIZEN_PATCH__
+ if (mas)
+#endif
+ {
+ messages_disconnect(mas->backend_data);
- mas_clean(mas);
+ mas_clean(mas);
+ }
}
static int mas_get(struct obex_session *os, void *user_data)
diff --git a/obexd/plugins/opp.c b/obexd/plugins/opp.c
index aa0722e6..5228ba82 100644
--- a/obexd/plugins/opp.c
+++ b/obexd/plugins/opp.c
@@ -42,6 +42,7 @@
#include "filesystem.h"
#define VCARD_TYPE "text/x-vcard"
+#define VCARD_FILE CONFIGDIR "/vcard.vcf"
static void *opp_connect(struct obex_session *os, int *err)
{
@@ -133,8 +134,6 @@ static int opp_put(struct obex_session *os, void *user_data)
static int opp_get(struct obex_session *os, void *user_data)
{
const char *type;
- char *folder, *path;
- int err = 0;
if (obex_get_name(os))
return -EPERM;
@@ -144,19 +143,14 @@ static int opp_get(struct obex_session *os, void *user_data)
if (type == NULL)
return -EPERM;
- folder = g_strdup(obex_option_root_folder());
- path = g_build_filename(folder, "/vcard.vcf", NULL);
-
if (g_ascii_strcasecmp(type, VCARD_TYPE) == 0) {
- if (obex_get_stream_start(os, path) < 0)
- err = -ENOENT;
+ if (obex_get_stream_start(os, VCARD_FILE) < 0)
+ return -ENOENT;
} else
- err = -EPERM;
+ return -EPERM;
- g_free(folder);
- g_free(path);
- return err;
+ return 0;
}
static void opp_disconnect(struct obex_session *os, void *user_data)
diff --git a/obexd/plugins/pbap.c b/obexd/plugins/pbap.c
index 68417a0b..78926644 100644
--- a/obexd/plugins/pbap.c
+++ b/obexd/plugins/pbap.c
@@ -828,18 +828,17 @@ static void *vobject_list_open(const char *name, int oflag, mode_t mode,
int ret;
void *request;
+ if (name == NULL) {
+ ret = -EBADR;
+ goto fail;
+ }
+
DBG("name %s context %p valid %d", name, context, pbap->cache.valid);
if (oflag != O_RDONLY) {
ret = -EPERM;
goto fail;
}
-
- if (name == NULL) {
- ret = -EBADR;
- goto fail;
- }
-
#ifdef __TIZEN_PATCH__
if (strcmp(name, "/telecom/mch") == 0)
pbap->params->required_missedcall_call_header = TRUE;
diff --git a/packaging/bluez.spec b/packaging/bluez.spec
index ee3b2273..a0006abc 100644
--- a/packaging/bluez.spec
+++ b/packaging/bluez.spec
@@ -196,6 +196,7 @@ ln -sf bluetooth.service %{buildroot}%{_libpath}/systemd/system/dbus-org.bluez.s
%{_bindir}/l2ping
%{_bindir}/obexctl
%{_bindir}/rfcomm
+%{_bindir}/btsnoop
%{_bindir}/mpris-proxy
%{_bindir}/sdptool
%{_bindir}/ciptool
@@ -224,7 +225,7 @@ ln -sf bluetooth.service %{buildroot}%{_libpath}/systemd/system/dbus-org.bluez.s
%files devel
%manifest %{name}.manifest
%defattr(-, root, root)
-/usr/include/bluetooth
+%{_includedir}/bluetooth/*
%{_libdir}/libbluetooth.so
%{_libdir}/pkgconfig/bluez.pc
diff --git a/plugins/policy.c b/plugins/policy.c
index bc9beba4..978a2d60 100644
--- a/plugins/policy.c
+++ b/plugins/policy.c
@@ -53,8 +53,12 @@
#endif
#define SOURCE_RETRY_TIMEOUT 2
#define SINK_RETRY_TIMEOUT SOURCE_RETRY_TIMEOUT
+#define CT_RETRY_TIMEOUT 1
+#define TG_RETRY_TIMEOUT CT_RETRY_TIMEOUT
#define SOURCE_RETRIES 1
#define SINK_RETRIES SOURCE_RETRIES
+#define CT_RETRIES 1
+#define TG_RETRIES CT_RETRIES
/* Tracking of remote services to be auto-reconnected upon link loss */
@@ -87,7 +91,9 @@ struct policy_data {
guint sink_timer;
uint8_t sink_retries;
guint ct_timer;
+ uint8_t ct_retries;
guint tg_timer;
+ uint8_t tg_retries;
};
static void policy_connect(struct policy_data *data,
@@ -116,6 +122,7 @@ static gboolean policy_connect_ct(gpointer user_data)
struct btd_service *service;
data->ct_timer = 0;
+ data->ct_retries++;
service = btd_device_get_service(data->dev, AVRCP_REMOTE_UUID);
if (service != NULL)
@@ -124,13 +131,13 @@ static gboolean policy_connect_ct(gpointer user_data)
return FALSE;
}
-static void policy_set_ct_timer(struct policy_data *data)
+static void policy_set_ct_timer(struct policy_data *data, int timeout)
{
if (data->ct_timer > 0)
g_source_remove(data->ct_timer);
- data->ct_timer = g_timeout_add_seconds(CONTROL_CONNECT_TIMEOUT,
- policy_connect_ct, data);
+ data->ct_timer = g_timeout_add_seconds(timeout, policy_connect_ct,
+ data);
}
static struct policy_data *find_data(struct btd_device *dev)
@@ -267,13 +274,13 @@ static void sink_cb(struct btd_service *service, btd_service_state_t old_state,
* avrcp connection immediately; irrespective of local
* or remote initiated a2dp connection
*/
- policy_set_ct_timer(data);
+ policy_set_ct_timer(data, CONTROL_CONNECT_TIMEOUT);
#else
policy_connect(data, controller);
#endif
else if (btd_service_get_state(controller) !=
BTD_SERVICE_STATE_CONNECTED)
- policy_set_ct_timer(data);
+ policy_set_ct_timer(data, CONTROL_CONNECT_TIMEOUT);
break;
case BTD_SERVICE_STATE_DISCONNECTING:
break;
@@ -286,6 +293,7 @@ static gboolean policy_connect_tg(gpointer user_data)
struct btd_service *service;
data->tg_timer = 0;
+ data->tg_retries++;
service = btd_device_get_service(data->dev, AVRCP_TARGET_UUID);
if (service != NULL)
@@ -294,18 +302,18 @@ static gboolean policy_connect_tg(gpointer user_data)
return FALSE;
}
-static void policy_set_tg_timer(struct policy_data *data)
+static void policy_set_tg_timer(struct policy_data *data, int timeout)
{
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->tg_timer = g_timeout_add_seconds(timeout, policy_connect_tg,
data);
#endif
}
@@ -394,7 +402,7 @@ static void source_cb(struct btd_service *service,
policy_connect(data, target);
else if (btd_service_get_state(target) !=
BTD_SERVICE_STATE_CONNECTED)
- policy_set_tg_timer(data);
+ policy_set_tg_timer(data, CONTROL_CONNECT_TIMEOUT);
break;
case BTD_SERVICE_STATE_DISCONNECTING:
break;
@@ -420,6 +428,23 @@ static void controller_cb(struct btd_service *service,
}
break;
case BTD_SERVICE_STATE_DISCONNECTED:
+ if (old_state == BTD_SERVICE_STATE_CONNECTING) {
+ int err = btd_service_get_error(service);
+
+ if (err == -EAGAIN) {
+ if (data->ct_retries < CT_RETRIES)
+ policy_set_ct_timer(data,
+ CT_RETRY_TIMEOUT);
+ else
+ data->ct_retries = 0;
+ break;
+ } else if (data->ct_timer > 0) {
+ g_source_remove(data->ct_timer);
+ data->ct_timer = 0;
+ }
+ } else if (old_state == BTD_SERVICE_STATE_CONNECTED) {
+ data->ct_retries = 0;
+ }
break;
case BTD_SERVICE_STATE_CONNECTING:
break;
@@ -453,6 +478,23 @@ static void target_cb(struct btd_service *service,
}
break;
case BTD_SERVICE_STATE_DISCONNECTED:
+ if (old_state == BTD_SERVICE_STATE_CONNECTING) {
+ int err = btd_service_get_error(service);
+
+ if (err == -EAGAIN) {
+ if (data->tg_retries < TG_RETRIES)
+ policy_set_tg_timer(data,
+ TG_RETRY_TIMEOUT);
+ else
+ data->tg_retries = 0;
+ break;
+ } else if (data->tg_timer > 0) {
+ g_source_remove(data->tg_timer);
+ data->tg_timer = 0;
+ }
+ } else if (old_state == BTD_SERVICE_STATE_CONNECTED) {
+ data->tg_retries = 0;
+ }
break;
case BTD_SERVICE_STATE_CONNECTING:
break;
diff --git a/plugins/sixaxis.c b/plugins/sixaxis.c
index c93f5d4f..fcc93bc6 100644
--- a/plugins/sixaxis.c
+++ b/plugins/sixaxis.c
@@ -302,7 +302,7 @@ static bool setup_device(int fd, int index, struct btd_adapter *adapter)
btd_device_device_set_name(device, devices[index].name);
btd_device_set_pnpid(device, devices[index].source, devices[index].vid,
devices[index].pid, devices[index].version);
- btd_device_set_temporary(device, FALSE);
+ btd_device_set_temporary(device, false);
return true;
}
diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
index 14de80c3..ef816144 100644
--- a/profiles/audio/a2dp.c
+++ b/profiles/audio/a2dp.c
@@ -82,7 +82,6 @@ struct a2dp_sep {
struct a2dp_setup_cb {
struct a2dp_setup *setup;
- a2dp_discover_cb_t discover_cb;
a2dp_select_cb_t select_cb;
a2dp_config_cb_t config_cb;
a2dp_stream_cb_t resume_cb;
@@ -99,7 +98,6 @@ struct a2dp_setup {
struct avdtp_stream *stream;
struct avdtp_error *err;
avdtp_set_configuration_cb setconf_cb;
- GSList *seps;
GSList *caps;
gboolean reconfigure;
gboolean start;
@@ -304,23 +302,6 @@ static void finalize_select(struct a2dp_setup *s)
}
}
-static void finalize_discover(struct a2dp_setup *s)
-{
- GSList *l;
-
- for (l = s->cb; l != NULL; ) {
- struct a2dp_setup_cb *cb = l->data;
-
- l = l->next;
-
- if (!cb->discover_cb)
- continue;
-
- cb->discover_cb(s->session, s->seps, s->err, cb->user_data);
- setup_cb_free(cb);
- }
-}
-
static struct a2dp_setup *find_setup_by_session(struct avdtp *session)
{
GSList *l;
@@ -394,13 +375,6 @@ static void stream_state_changed(struct avdtp_stream *stream,
return;
}
-#ifdef __TIZEN_PATCH__
- if (new_state == AVDTP_STATE_STREAMING && sep->suspend_timer) {
- g_source_remove(sep->suspend_timer);
- sep->suspend_timer = 0;
- }
-#endif
-
if (new_state != AVDTP_STATE_IDLE)
return;
@@ -424,7 +398,6 @@ static gboolean auto_config(gpointer data)
{
struct a2dp_setup *setup = data;
struct btd_device *dev = NULL;
-
struct btd_service *service;
/* Check if configuration was aborted */
@@ -1386,6 +1359,13 @@ struct avdtp *a2dp_avdtp_get(struct btd_device *device)
return NULL;
}
+#ifdef __TIZEN_PATCH__
+ if (chan->auth_id) {
+ DBG("auth is already going...");
+ return NULL;
+ }
+#endif
+
if (chan->session)
return avdtp_ref(chan->session);
@@ -1779,6 +1759,42 @@ done:
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)
{
@@ -1818,8 +1834,9 @@ static struct a2dp_sep *a2dp_find_sep(struct avdtp *session, GSList *list,
continue;
}
#else
- if (avdtp_find_remote_sep(session, sep->lsep) == NULL)
- continue;
+ if (check_vendor_codec(sep, cap->data,
+ service->length - sizeof(*cap)))
+ return sep;
#endif
return sep;
@@ -1857,40 +1874,6 @@ static struct a2dp_sep *a2dp_select_sep(struct avdtp *session, uint8_t type,
return a2dp_find_sep(session, l, NULL);
}
-static void discover_cb(struct avdtp *session, GSList *seps,
- struct avdtp_error *err, void *user_data)
-{
- struct a2dp_setup *setup = user_data;
-
- DBG("err %p", err);
-
- setup->seps = seps;
- setup->err = err;
-
- finalize_discover(setup);
-}
-
-unsigned int a2dp_discover(struct avdtp *session, a2dp_discover_cb_t cb,
- void *user_data)
-{
- struct a2dp_setup *setup;
- struct a2dp_setup_cb *cb_data;
-
- setup = a2dp_setup_get(session);
- if (!setup)
- return 0;
-
- cb_data = setup_cb_new(setup);
- cb_data->discover_cb = cb;
- cb_data->user_data = user_data;
-
- if (avdtp_discover(session, discover_cb, setup) == 0)
- return cb_data->id;
-
- setup_cb_free(cb_data);
- return 0;
-}
-
unsigned int a2dp_select_capabilities(struct avdtp *session,
uint8_t type, const char *sender,
a2dp_select_cb_t cb,
@@ -2184,8 +2167,8 @@ gboolean a2dp_cancel(unsigned int id)
if (!setup->cb) {
DBG("aborting setup %p", setup);
- if (!avdtp_abort(setup->session, setup->stream))
- return TRUE;
+ avdtp_abort(setup->session, setup->stream);
+ return TRUE;
}
setup_unref(setup);
diff --git a/profiles/audio/a2dp.h b/profiles/audio/a2dp.h
index 50c1eeba..544eea1e 100644
--- a/profiles/audio/a2dp.h
+++ b/profiles/audio/a2dp.h
@@ -52,9 +52,6 @@ struct a2dp_endpoint {
void *user_data);
};
-typedef void (*a2dp_discover_cb_t) (struct avdtp *session, GSList *seps,
- struct avdtp_error *err,
- void *user_data);
typedef void (*a2dp_select_cb_t) (struct avdtp *session,
struct a2dp_sep *sep, GSList *caps,
void *user_data);
@@ -73,8 +70,6 @@ struct a2dp_sep *a2dp_add_sep(struct btd_adapter *adapter, uint8_t type,
int *err);
void a2dp_remove_sep(struct a2dp_sep *sep);
-unsigned int a2dp_discover(struct avdtp *session, a2dp_discover_cb_t cb,
- void *user_data);
unsigned int a2dp_select_capabilities(struct avdtp *session,
uint8_t type, const char *sender,
a2dp_select_cb_t cb,
diff --git a/profiles/audio/avctp.c b/profiles/audio/avctp.c
index 197373a7..34b01830 100644
--- a/profiles/audio/avctp.c
+++ b/profiles/audio/avctp.c
@@ -580,7 +580,8 @@ static void avctp_disconnected(struct avctp *session)
g_free(session);
}
-static void avctp_set_state(struct avctp *session, avctp_state_t new_state)
+static void avctp_set_state(struct avctp *session, avctp_state_t new_state,
+ int err)
{
GSList *l;
avctp_state_t old_state = session->state;
@@ -593,7 +594,8 @@ static void avctp_set_state(struct avctp *session, avctp_state_t new_state)
if (cb->dev && cb->dev != session->device)
continue;
- cb->cb(session->device, old_state, new_state, cb->user_data);
+ cb->cb(session->device, old_state, new_state, err,
+ cb->user_data);
}
switch (new_state) {
@@ -947,7 +949,7 @@ send:
failed:
DBG("AVCTP Browsing: disconnected");
- avctp_set_state(session, AVCTP_STATE_CONNECTED);
+ avctp_set_state(session, AVCTP_STATE_CONNECTED, 0);
if (session->browsing) {
avctp_channel_destroy(session->browsing);
@@ -1043,7 +1045,7 @@ done:
failed:
DBG("AVCTP session %p got disconnected", session);
- avctp_set_state(session, AVCTP_STATE_DISCONNECTED);
+ avctp_set_state(session, AVCTP_STATE_DISCONNECTED, -EIO);
return FALSE;
}
@@ -1200,7 +1202,7 @@ static void avctp_connect_browsing_cb(GIOChannel *chan, GError *err,
G_IO_IN | G_IO_ERR | G_IO_HUP | G_IO_NVAL,
(GIOFunc) session_browsing_cb, session);
- avctp_set_state(session, AVCTP_STATE_BROWSING_CONNECTED);
+ avctp_set_state(session, AVCTP_STATE_BROWSING_CONNECTED, 0);
/* Process any request that was pending the connection to complete */
if (browsing->process_id == 0 && !g_queue_is_empty(browsing->queue))
@@ -1209,7 +1211,7 @@ static void avctp_connect_browsing_cb(GIOChannel *chan, GError *err,
return;
fail:
- avctp_set_state(session, AVCTP_STATE_CONNECTED);
+ avctp_set_state(session, AVCTP_STATE_CONNECTED, 0);
if (session->browsing) {
avctp_channel_destroy(session->browsing);
@@ -1225,7 +1227,7 @@ static void avctp_connect_cb(GIOChannel *chan, GError *err, gpointer data)
GError *gerr = NULL;
if (err) {
- avctp_set_state(session, AVCTP_STATE_DISCONNECTED);
+ avctp_set_state(session, AVCTP_STATE_DISCONNECTED, -EIO);
error("%s", err->message);
return;
}
@@ -1236,7 +1238,7 @@ static void avctp_connect_cb(GIOChannel *chan, GError *err, gpointer data)
BT_IO_OPT_IMTU, &omtu,
BT_IO_OPT_INVALID);
if (gerr) {
- avctp_set_state(session, AVCTP_STATE_DISCONNECTED);
+ avctp_set_state(session, AVCTP_STATE_DISCONNECTED, -EIO);
error("%s", gerr->message);
g_error_free(gerr);
return;
@@ -1269,7 +1271,7 @@ static void avctp_connect_cb(GIOChannel *chan, GError *err, gpointer data)
init_uinput(session);
- avctp_set_state(session, AVCTP_STATE_CONNECTED);
+ avctp_set_state(session, AVCTP_STATE_CONNECTED, 0);
}
static void auth_cb(DBusError *derr, void *user_data)
@@ -1286,7 +1288,7 @@ static void auth_cb(DBusError *derr, void *user_data)
if (derr && dbus_error_is_set(derr)) {
error("Access denied: %s", derr->message);
- avctp_set_state(session, AVCTP_STATE_DISCONNECTED);
+ avctp_set_state(session, AVCTP_STATE_DISCONNECTED, -EIO);
return;
}
@@ -1294,7 +1296,7 @@ static void auth_cb(DBusError *derr, void *user_data)
NULL, &err)) {
error("bt_io_accept: %s", err->message);
g_error_free(err);
- avctp_set_state(session, AVCTP_STATE_DISCONNECTED);
+ avctp_set_state(session, AVCTP_STATE_DISCONNECTED, -EIO);
}
}
@@ -1356,10 +1358,17 @@ static void avctp_control_confirm(struct avctp *session, GIOChannel *chan,
if (session->control != NULL) {
error("Control: Refusing unexpected connect");
g_io_channel_shutdown(chan, TRUE, NULL);
+
+ /*
+ * Close AVCTP channel if remote tried connect
+ * at the same time
+ * AVRCP SPEC V1.5 4.1.1 Connection Establishment
+ */
+ avctp_set_state(session, AVCTP_STATE_DISCONNECTED, -EAGAIN);
return;
}
- avctp_set_state(session, AVCTP_STATE_CONNECTING);
+ avctp_set_state(session, AVCTP_STATE_CONNECTING, 0);
session->control = avctp_channel_create(session, chan, NULL);
src = btd_adapter_get_address(device_get_adapter(dev));
@@ -1382,7 +1391,7 @@ static void avctp_control_confirm(struct avctp *session, GIOChannel *chan,
return;
drop:
- avctp_set_state(session, AVCTP_STATE_DISCONNECTED);
+ avctp_set_state(session, AVCTP_STATE_DISCONNECTED, -EIO);
}
static void avctp_browsing_confirm(struct avctp *session, GIOChannel *chan,
@@ -1398,7 +1407,7 @@ static void avctp_browsing_confirm(struct avctp *session, GIOChannel *chan,
if (bt_io_accept(chan, avctp_connect_browsing_cb, session, NULL,
&err)) {
- avctp_set_state(session, AVCTP_STATE_BROWSING_CONNECTING);
+ avctp_set_state(session, AVCTP_STATE_BROWSING_CONNECTING, 0);
return;
}
@@ -1988,7 +1997,7 @@ struct avctp *avctp_connect(struct btd_device *device)
if (session->state > AVCTP_STATE_DISCONNECTED)
return session;
- avctp_set_state(session, AVCTP_STATE_CONNECTING);
+ avctp_set_state(session, AVCTP_STATE_CONNECTING, 0);
src = btd_adapter_get_address(session->server->adapter);
@@ -2000,7 +2009,7 @@ struct avctp *avctp_connect(struct btd_device *device)
BT_IO_OPT_PSM, AVCTP_CONTROL_PSM,
BT_IO_OPT_INVALID);
if (err) {
- avctp_set_state(session, AVCTP_STATE_DISCONNECTED);
+ avctp_set_state(session, AVCTP_STATE_DISCONNECTED, -EIO);
error("%s", err->message);
g_error_free(err);
return NULL;
@@ -2025,7 +2034,7 @@ int avctp_connect_browsing(struct avctp *session)
if (session->browsing != NULL)
return 0;
- avctp_set_state(session, AVCTP_STATE_BROWSING_CONNECTING);
+ avctp_set_state(session, AVCTP_STATE_BROWSING_CONNECTING, 0);
src = btd_adapter_get_address(session->server->adapter);
@@ -2055,7 +2064,7 @@ void avctp_disconnect(struct avctp *session)
if (session->state == AVCTP_STATE_DISCONNECTED)
return;
- avctp_set_state(session, AVCTP_STATE_DISCONNECTED);
+ avctp_set_state(session, AVCTP_STATE_DISCONNECTED, -EIO);
}
struct avctp *avctp_get(struct btd_device *device)
diff --git a/profiles/audio/avctp.h b/profiles/audio/avctp.h
index 05fceb4b..6c19ce42 100644
--- a/profiles/audio/avctp.h
+++ b/profiles/audio/avctp.h
@@ -122,7 +122,7 @@ typedef enum {
typedef void (*avctp_state_cb) (struct btd_device *dev,
avctp_state_t old_state,
avctp_state_t new_state,
- void *user_data);
+ int err, void *user_data);
typedef bool (*avctp_passthrough_cb) (struct avctp *session,
uint8_t op, bool pressed,
diff --git a/profiles/audio/avdtp.c b/profiles/audio/avdtp.c
index 481991e0..92bd5612 100644
--- a/profiles/audio/avdtp.c
+++ b/profiles/audio/avdtp.c
@@ -34,9 +34,11 @@
#include <unistd.h>
#include <assert.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 "lib/uuid.h"
#ifdef __TIZEN_PATCH__
@@ -46,9 +48,6 @@
#endif /* __BROADCOM_QOS_PATCH__ */
#endif /* __TIZEN_PATCH__ */
-#include <glib.h>
-
-
#include "btio/btio.h"
#include "src/log.h"
#include "src/shared/util.h"
@@ -971,6 +970,9 @@ static gboolean send_broadcom_a2dp_qos(bdaddr_t *dst, gboolean qos_high)
dd = hci_open_dev(0);
+ if (dd < 0)
+ return FALSE;
+
cr = g_malloc0(sizeof(*cr) + sizeof(struct hci_conn_info));
cr->type = ACL_LINK;
@@ -1188,7 +1190,8 @@ static void finalize_discovery(struct avdtp *session, int err)
if (discover->id > 0)
g_source_remove(discover->id);
- discover->cb(session, session->seps, err ? &avdtp_err : NULL,
+ if (discover->cb)
+ discover->cb(session, session->seps, err ? &avdtp_err : NULL,
discover->user_data);
g_free(discover);
session->discover = NULL;
@@ -1340,12 +1343,14 @@ static gboolean disconnect_timeout(gpointer user_data)
}
#ifdef __TIZEN_PATCH__
- 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");
+ 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");
+ }
#endif
connection_lost(session, ETIMEDOUT);
@@ -1932,7 +1937,8 @@ static gboolean avdtp_start_cmd(struct avdtp *session, uint8_t transaction,
for (i = 0; i < seid_count; i++, seid++) {
failed_seid = seid->seid;
- sep = find_local_sep_by_seid(session, seid->seid);
+ sep = find_local_sep_by_seid(session,
+ req->first_seid.seid);
if (!sep || !sep->stream) {
err = AVDTP_BAD_ACP_SEID;
goto failed;
@@ -2043,7 +2049,8 @@ static gboolean avdtp_suspend_cmd(struct avdtp *session, uint8_t transaction,
for (i = 0; i < seid_count; i++, seid++) {
failed_seid = seid->seid;
- sep = find_local_sep_by_seid(session, seid->seid);
+ sep = find_local_sep_by_seid(session,
+ req->first_seid.seid);
if (!sep || !sep->stream) {
err = AVDTP_BAD_ACP_SEID;
goto failed;
@@ -3701,13 +3708,6 @@ int avdtp_abort(struct avdtp *session, struct avdtp_stream *stream)
struct seid_req req;
int ret;
- if (!stream && session->discover) {
- /* Don't call cb since it being aborted */
- session->discover->cb = NULL;
- finalize_discovery(session, -ECANCELED);
- return -EALREADY;
- }
-
if (!g_slist_find(session->streams, stream))
return -EINVAL;
diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index 80c1ff22..911a8895 100644
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -103,13 +103,11 @@
#define AVRCP_REQUEST_CONTINUING 0x40
#define AVRCP_ABORT_CONTINUING 0x41
#define AVRCP_SET_ABSOLUTE_VOLUME 0x50
-#define AVRCP_SET_ADDRESSED_PLAYER 0x60
#define AVRCP_SET_BROWSED_PLAYER 0x70
#define AVRCP_GET_FOLDER_ITEMS 0x71
#define AVRCP_CHANGE_PATH 0x72
#define AVRCP_GET_ITEM_ATTRIBUTES 0x73
#define AVRCP_PLAY_ITEM 0x74
-#define AVRCP_GET_TOTAL_NUMBER_OF_ITEMS 0x75
#define AVRCP_SEARCH 0x80
#define AVRCP_ADD_TO_NOW_PLAYING 0x90
#define AVRCP_GENERAL_REJECT 0xA0
@@ -137,8 +135,6 @@
#define AVRCP_CHARSET_UTF8 106
#define AVRCP_BROWSING_TIMEOUT 1
-#define AVRCP_CT_VERSION 0x0106
-#define AVRCP_TG_VERSION 0x0105
#if __BYTE_ORDER == __LITTLE_ENDIAN
@@ -207,10 +203,8 @@ struct avrcp_player {
uint64_t uid;
uint16_t uid_counter;
bool browsed;
- bool addressed;
uint8_t *features;
char *path;
- guint changed_id;
struct pending_list_items *p;
char *change_path;
@@ -280,6 +274,7 @@ void avrcp_stop_position_timer(void);
unsigned int pos_timer_id = 0;
#endif
+#ifdef SUPPORT_AVRCP_CONTROL
static sdp_record_t *avrcp_ct_record(void)
{
sdp_list_t *svclass_id, *pfseq, *apseq, *apseq1, *root;
@@ -299,7 +294,7 @@ static sdp_record_t *avrcp_ct_record(void)
feat = feat | AVRCP_FEATURE_CATEGORY_2;
#endif
#else
- uint16_t avctp_ver = 0x0103;
+ uint16_t avrcp_ver = 0x0105, avctp_ver = 0x0103;
uint16_t feat = ( AVRCP_FEATURE_CATEGORY_1 |
AVRCP_FEATURE_CATEGORY_2 |
AVRCP_FEATURE_CATEGORY_3 |
@@ -355,7 +350,7 @@ static sdp_record_t *avrcp_ct_record(void)
/* Bluetooth Profile Descriptor List */
sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
- profile[0].version = AVRCP_CT_VERSION;
+ profile[0].version = avrcp_ver;
#ifdef __TIZEN_PATCH__
adapter_avrcp_ct_ver = avrcp_ver;
#endif
@@ -384,7 +379,9 @@ static sdp_record_t *avrcp_ct_record(void)
return record;
}
+#endif
+#ifdef SUPPORT_AVRCP_TARGET
static sdp_record_t *avrcp_tg_record(void)
{
sdp_list_t *svclass_id, *pfseq, *apseq, *root, *apseq_browsing;
@@ -410,7 +407,7 @@ static sdp_record_t *avrcp_tg_record(void)
feat = feat | AVRCP_FEATURE_CATEGORY_2;
#endif
#else
- uint16_t avctp_ver = 0x0103;
+ uint16_t avrcp_ver = 0x0104, avctp_ver = 0x0103;
uint16_t feat = ( AVRCP_FEATURE_CATEGORY_1 |
AVRCP_FEATURE_CATEGORY_2 |
AVRCP_FEATURE_CATEGORY_3 |
@@ -462,7 +459,7 @@ static sdp_record_t *avrcp_tg_record(void)
/* Bluetooth Profile Descriptor List */
sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
- profile[0].version = AVRCP_TG_VERSION;
+ profile[0].version = avrcp_ver;
#ifdef __TIZEN_PATCH__
adapter_avrcp_tg_ver = avrcp_ver;
#endif
@@ -494,6 +491,7 @@ static sdp_record_t *avrcp_tg_record(void)
return record;
}
+#endif
static unsigned int attr_get_max_val(uint8_t attr)
{
@@ -685,7 +683,6 @@ void avrcp_player_event(struct avrcp_player *player, uint8_t id,
{
uint8_t buf[AVRCP_HEADER_LENGTH + 9];
struct avrcp_header *pdu = (void *) buf;
- uint8_t code;
uint16_t size;
GSList *l;
int attr;
@@ -703,19 +700,10 @@ void avrcp_player_event(struct avrcp_player *player, uint8_t id,
set_company_id(pdu->company_id, IEEEID_BTSIG);
pdu->pdu_id = AVRCP_REGISTER_NOTIFICATION;
+ pdu->params[0] = id;
DBG("id=%u", id);
- if (id != AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED && player->changed_id) {
- code = AVC_CTYPE_REJECTED;
- size = 1;
- pdu->params[0] = AVRCP_STATUS_ADDRESSED_PLAYER_CHANGED;
- goto done;
- }
-
- code = AVC_CTYPE_CHANGED;
- pdu->params[0] = id;
-
switch (id) {
case AVRCP_EVENT_STATUS_CHANGED:
size = 2;
@@ -776,27 +764,17 @@ void avrcp_player_event(struct avrcp_player *player, uint8_t id,
memcpy(&pdu->params[1], position_val, sizeof(uint32_t));
break;
#endif
- case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED:
- size = 5;
- memcpy(&pdu->params[1], &player->id, sizeof(uint16_t));
- memcpy(&pdu->params[3], &player->uid_counter, sizeof(uint16_t));
- break;
- case AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED:
- size = 1;
- break;
default:
error("Unknown event %u", id);
return;
}
-done:
pdu->params_len = htons(size);
#ifdef __TIZEN_PATCH__
if (id == AVRCP_EVENT_PLAYBACK_POS_CHANGED &&
pos_timer_id > 0) {
- /* Remove the timer function which was added for register
- * notification. As we are sending changed event eariler
- * then time interval.
+ /* Remove the timer function which was added for register notification.
+ * As we are sending changed event eariler then time interval.
*/
DBG("Removing the timer function added by register notification");
g_source_remove(pos_timer_id);
@@ -813,7 +791,7 @@ done:
err = avctp_send_vendordep(session->conn,
session->transaction_events[id],
- code, AVC_SUBUNIT_PANEL,
+ AVC_CTYPE_CHANGED, AVC_SUBUNIT_PANEL,
buf, size + AVRCP_HEADER_LENGTH);
if (err < 0)
continue;
@@ -1430,22 +1408,6 @@ static uint8_t player_get_status(struct avrcp_player *player)
return play_status_to_val(value);
}
-static uint16_t player_get_id(struct avrcp_player *player)
-{
- if (player == NULL)
- return 0x0000;
-
- return player->id;
-}
-
-static uint16_t player_get_uid_counter(struct avrcp_player *player)
-{
- if (player == NULL)
- return 0x0000;
-
- return player->uid_counter;
-}
-
static uint8_t avrcp_handle_get_play_status(struct avrcp *session,
struct avrcp_header *pdu,
uint8_t transaction)
@@ -1675,8 +1637,6 @@ static uint8_t avrcp_handle_register_notification(struct avrcp *session,
pdu->params[len++] = val;
}
- g_list_free(settings);
-
break;
case AVRCP_EVENT_VOLUME_CHANGED:
pdu->params[1] = media_transport_get_device_volume(dev);
@@ -1690,9 +1650,8 @@ static uint8_t avrcp_handle_register_notification(struct avrcp *session,
case AVRCP_EVENT_PLAYBACK_POS_CHANGED:
len = 5;
- /* time interval in seconds at which the change in
- * playback position shall be notified.
- */
+ /* time interval in seconds at which the change in playback position
+ shall be notified */
memcpy(&playback_interval, &pdu->params[1], sizeof(uint32_t));
playback_interval = ((playback_interval>>24)&0xff) |
((playback_interval<<8)&0xff0000) |
@@ -1702,8 +1661,7 @@ static uint8_t avrcp_handle_register_notification(struct avrcp *session,
play_status = player_get_status(player);
if (play_status != AVRCP_PLAY_STATUS_PLAYING) {
- DBG("Play Pos Changed Event is skipped(%d)",
- play_status);
+ DBG("Play Pos Changed Event is skipped(%d)", play_status);
} else {
DBG("Playback interval : %d secs", playback_interval);
pos_timer_id = g_timeout_add_seconds(
@@ -1714,23 +1672,14 @@ static uint8_t avrcp_handle_register_notification(struct avrcp *session,
/* retrieve current playback position for interim response */
playback_position = player_get_playback_position(player);
playback_position = (playback_position & 0x000000ff) << 24 |
- (playback_position & 0x0000ff00) << 8 |
- (playback_position & 0x00ff0000) >> 8 |
- (playback_position & 0xff000000) >> 24;
+ (playback_position & 0x0000ff00) << 8 |
+ (playback_position & 0x00ff0000) >> 8 |
+ (playback_position & 0xff000000) >> 24;
memcpy(&pdu->params[1], &playback_position, sizeof(uint32_t));
break;
#endif
- case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED:
- len = 5;
- memcpy(&pdu->params[1], player_get_id(player),
- sizeof(uint16_t));
- memcpy(&pdu->params[3], player_get_uid_counter(player),
- sizeof(uint16_t));
- break;
- case AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED:
- len = 1;
- break;
+
default:
/* All other events are not supported yet */
goto err;
@@ -1821,12 +1770,20 @@ static uint8_t avrcp_handle_set_absolute_volume(struct avrcp *session,
struct avrcp_header *pdu,
uint8_t transaction)
{
+#ifndef __TIZEN_PATCH__
+ struct avrcp_player *player = session->controller->player;
+#else
+ struct avrcp_player *player = target_get_player(session);
+#endif
uint16_t len = ntohs(pdu->params_len);
uint8_t volume;
if (len != 1)
goto err;
+ if (!player)
+ goto err;
+
volume = pdu->params[0] & 0x7F;
media_transport_update_device_volume(session->dev, volume);
@@ -1839,90 +1796,6 @@ err:
return AVC_CTYPE_REJECTED;
}
-static struct avrcp_player *find_tg_player(struct avrcp *session, uint16_t id)
-{
- struct avrcp_server *server = session->server;
- GSList *l;
-
- for (l = server->players; l; l = l->next) {
- struct avrcp_player *player = l->data;
-
- if (player->id == id)
- return player;
- }
-
- return NULL;
-}
-
-static gboolean notify_addressed_player_changed(gpointer user_data)
-{
- struct avrcp_player *player = user_data;
- uint8_t events[6] = { AVRCP_EVENT_STATUS_CHANGED,
- AVRCP_EVENT_TRACK_CHANGED,
- AVRCP_EVENT_TRACK_REACHED_START,
- AVRCP_EVENT_TRACK_REACHED_END,
- AVRCP_EVENT_SETTINGS_CHANGED,
- AVRCP_EVENT_PLAYBACK_POS_CHANGED
- };
- uint8_t i;
-
- avrcp_player_event(player, AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED, NULL);
-
- /*
- * TG shall complete all player specific
- * notifications with AV/C C-Type REJECTED
- * with error code as Addressed Player Changed.
- */
- for (i = 0; i < sizeof(events); i++)
- avrcp_player_event(player, events[i], NULL);
-
- player->changed_id = 0;
-
- return FALSE;
-}
-
-static uint8_t avrcp_handle_set_addressed_player(struct avrcp *session,
- struct avrcp_header *pdu,
- uint8_t transaction)
-{
- struct avrcp_player *player;
- uint16_t len = ntohs(pdu->params_len);
- uint16_t player_id = 0;
- uint8_t status;
-
- if (len < 1) {
- status = AVRCP_STATUS_INVALID_PARAM;
- goto err;
- }
-
- player_id = bt_get_be16(&pdu->params[0]);
- player = find_tg_player(session, player_id);
- pdu->packet_type = AVRCP_PACKET_TYPE_SINGLE;
-
- if (player) {
- player->addressed = true;
- status = AVRCP_STATUS_SUCCESS;
- pdu->params_len = htons(len);
- pdu->params[0] = status;
- } else {
- status = AVRCP_STATUS_INVALID_PLAYER_ID;
- goto err;
- }
-
- /* Don't emit player changed immediately since PTS expect the
- * response of SetAddressedPlayer before the event.
- */
- player->changed_id = g_idle_add(notify_addressed_player_changed,
- player);
-
- return AVC_CTYPE_ACCEPTED;
-
-err:
- pdu->params_len = htons(sizeof(status));
- pdu->params[0] = status;
- return AVC_CTYPE_REJECTED;
-}
-
static const struct control_pdu_handler control_handlers[] = {
{ AVRCP_GET_CAPABILITIES, AVC_CTYPE_STATUS,
avrcp_handle_get_capabilities },
@@ -1954,8 +1827,6 @@ static const struct control_pdu_handler control_handlers[] = {
avrcp_handle_request_continuing },
{ AVRCP_ABORT_CONTINUING, AVC_CTYPE_CONTROL,
avrcp_handle_abort_continuing },
- { AVRCP_SET_ADDRESSED_PLAYER, AVC_CTYPE_CONTROL,
- avrcp_handle_set_addressed_player },
{ },
};
@@ -3064,7 +2935,7 @@ static void avrcp_play_item(struct avrcp *session, uint64_t uid)
length = AVRCP_HEADER_LENGTH + ntohs(pdu->params_len);
- avctp_send_vendordep_req(session->conn, AVC_CTYPE_CONTROL,
+ avctp_send_vendordep_req(session->conn, AVC_CTYPE_STATUS,
AVC_SUBUNIT_PANEL, buf, length,
NULL, session);
}
@@ -3110,7 +2981,7 @@ static void avrcp_add_to_nowplaying(struct avrcp *session, uint64_t uid)
length = AVRCP_HEADER_LENGTH + ntohs(pdu->params_len);
- avctp_send_vendordep_req(session->conn, AVC_CTYPE_CONTROL,
+ avctp_send_vendordep_req(session->conn, AVC_CTYPE_STATUS,
AVC_SUBUNIT_PANEL, buf, length,
NULL, session);
}
@@ -3136,78 +3007,6 @@ static int ct_add_to_nowplaying(struct media_player *mp, const char *name,
return 0;
}
-static gboolean avrcp_get_total_numberofitems_rsp(struct avctp *conn,
- uint8_t *operands, size_t operand_count,
- void *user_data)
-{
- struct avrcp_browsing_header *pdu = (void *) operands;
- struct avrcp *session = user_data;
- struct avrcp_player *player = session->controller->player;
- struct media_player *mp = player->user_data;
- uint32_t num_of_items;
-
- if (pdu == NULL)
- return -ETIMEDOUT;
-
- if (pdu->params[0] != AVRCP_STATUS_SUCCESS || operand_count < 7)
- return -EINVAL;
-
- if (pdu->params[0] == AVRCP_STATUS_OUT_OF_BOUNDS)
- goto done;
-
- player->uid_counter = get_be16(&pdu->params[1]);
- num_of_items = get_be32(&pdu->params[3]);
-
- if (!num_of_items)
- return -EINVAL;
-
-done:
- media_player_total_items_complete(mp, num_of_items);
- return FALSE;
-}
-
-static void avrcp_get_total_numberofitems(struct avrcp *session)
-{
- uint8_t buf[AVRCP_BROWSING_HEADER_LENGTH + 7];
- struct avrcp_player *player = session->controller->player;
- struct avrcp_browsing_header *pdu = (void *) buf;
-
- memset(buf, 0, sizeof(buf));
-
- pdu->pdu_id = AVRCP_GET_TOTAL_NUMBER_OF_ITEMS;
- pdu->param_len = htons(7 + sizeof(uint32_t));
-
- pdu->params[0] = player->scope;
-
- avctp_send_browsing_req(session->conn, buf, sizeof(buf),
- avrcp_get_total_numberofitems_rsp, session);
-}
-
-static int ct_get_total_numberofitems(struct media_player *mp, const char *name,
- void *user_data)
-{
- struct avrcp_player *player = user_data;
- struct avrcp *session;
-
- session = player->sessions->data;
-
- if (session->controller->version != 0x0106) {
- error("version not supported");
- return -1;
- }
-
- if (g_str_has_prefix(name, "/NowPlaying"))
- player->scope = 0x03;
- else if (g_str_has_suffix(name, "/search"))
- player->scope = 0x02;
- else
- player->scope = 0x01;
-
- avrcp_get_total_numberofitems(session);
-
- return 0;
-}
-
static const struct media_player_callback ct_cbs = {
.set_setting = ct_set_setting,
.play = ct_play,
@@ -3222,7 +3021,6 @@ static const struct media_player_callback ct_cbs = {
.search = ct_search,
.play_item = ct_play_item,
.add_to_nowplaying = ct_add_to_nowplaying,
- .total_items = ct_get_total_numberofitems,
};
static struct avrcp_player *create_ct_player(struct avrcp *session,
@@ -3341,9 +3139,6 @@ static void player_destroy(gpointer data)
avrcp_stop_position_timer();
#endif
- if (player->changed_id > 0)
- g_source_remove(player->changed_id);
-
g_slist_free(player->sessions);
g_free(player->path);
g_free(player->change_path);
@@ -3358,15 +3153,10 @@ static void player_remove(gpointer data)
for (l = player->sessions; l; l = l->next) {
struct avrcp *session = l->data;
- struct avrcp_data *controller = session->controller;
-
- controller->players = g_slist_remove(controller->players,
- player);
- /* Check if current player is being removed */
- if (controller->player == player)
- controller->player = g_slist_nth_data(
- controller->players, 0);
+ session->controller->players = g_slist_remove(
+ session->controller->players,
+ player);
}
player_destroy(player);
@@ -3417,6 +3207,9 @@ static gboolean avrcp_get_media_player_list_rsp(struct avctp *conn,
i += len;
}
+ if (g_slist_find(removed, session->controller->player))
+ session->controller->player = NULL;
+
g_slist_free_full(removed, player_remove);
return FALSE;
@@ -3650,6 +3443,9 @@ static gboolean avrcp_get_capabilities_resp(struct avctp *conn,
case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED:
case AVRCP_EVENT_UIDS_CHANGED:
case AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED:
+ /* These events above are controller specific */
+ if (!session->controller)
+ break;
case AVRCP_EVENT_VOLUME_CHANGED:
#endif
avrcp_register_notification(session, event);
@@ -3657,6 +3453,9 @@ static gboolean avrcp_get_capabilities_resp(struct avctp *conn,
}
}
+ if (!session->controller)
+ return FALSE;
+
if (!(events & (1 << AVRCP_EVENT_SETTINGS_CHANGED)))
avrcp_list_player_attributes(session);
@@ -3777,6 +3576,7 @@ static void avrcp_connect_browsing(struct avrcp *session)
session);
}
+#ifdef SUPPORT_AVRCP_TARGET
static void target_init(struct avrcp *session)
{
struct avrcp_server *server = session->server;
@@ -3793,8 +3593,7 @@ static void target_init(struct avrcp *session)
DBG("%p version 0x%04x", target, target->version);
service = btd_device_get_service(session->dev, AVRCP_REMOTE_UUID);
- if (service != NULL)
- btd_service_connecting_complete(service, 0);
+ btd_service_connecting_complete(service, 0);
player = g_slist_nth_data(server->players, 0);
if (player != NULL) {
@@ -3816,11 +3615,6 @@ static void target_init(struct avrcp *session)
if (target->version < 0x0104)
return;
- session->supported_events |=
- (1 << AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED) |
- (1 << AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED) |
- (1 << AVRCP_EVENT_VOLUME_CHANGED);
-
#ifdef __TIZEN_PATCH__
if (adapter_avrcp_tg_ver < 0x0104)
return;
@@ -3835,7 +3629,9 @@ static void target_init(struct avrcp *session)
avrcp_connect_browsing(session);
}
+#endif
+#ifdef SUPPORT_AVRCP_CONTROL
static void controller_init(struct avrcp *session)
{
struct avrcp_player *player;
@@ -3866,8 +3662,7 @@ static void controller_init(struct avrcp *session)
#endif
service = btd_device_get_service(session->dev, AVRCP_TARGET_UUID);
- if (service != NULL)
- btd_service_connecting_complete(service, 0);
+ btd_service_connecting_complete(service, 0);
/* Only create player if category 1 is supported */
if (!(controller->features & AVRCP_FEATURE_CATEGORY_1))
@@ -3895,6 +3690,7 @@ static void controller_init(struct avrcp *session)
avrcp_connect_browsing(session);
}
+#endif
static void session_init_control(struct avrcp *session)
{
@@ -3908,29 +3704,24 @@ static void session_init_control(struct avrcp *session)
handle_vendordep_pdu,
session);
session->control_handlers = control_handlers;
-
+#ifdef SUPPORT_AVRCP_CONTROL
if (btd_device_get_service(session->dev, AVRCP_TARGET_UUID) != NULL)
controller_init(session);
+#endif
+#ifdef SUPPORT_AVRCP_TARGET
if (btd_device_get_service(session->dev, AVRCP_REMOTE_UUID) != NULL)
target_init(session);
+#endif
}
static void controller_destroy(struct avrcp *session)
{
struct avrcp_data *controller = session->controller;
- struct btd_service *service;
DBG("%p", controller);
g_slist_free_full(controller->players, player_destroy);
- service = btd_device_get_service(session->dev, AVRCP_TARGET_UUID);
-
- if (session->control_id == 0)
- btd_service_connecting_complete(service, -EIO);
- else
- btd_service_disconnecting_complete(service, 0);
-
g_free(controller);
}
@@ -3938,31 +3729,40 @@ static void target_destroy(struct avrcp *session)
{
struct avrcp_data *target = session->target;
struct avrcp_player *player = target->player;
- struct btd_service *service;
DBG("%p", target);
if (player != NULL)
player->sessions = g_slist_remove(player->sessions, session);
- service = btd_device_get_service(session->dev, AVRCP_REMOTE_UUID);
-
- if (session->control_id == 0)
- btd_service_connecting_complete(service, -EIO);
- else
- btd_service_disconnecting_complete(service, 0);
-
g_free(target);
}
-static void session_destroy(struct avrcp *session)
+static void session_destroy(struct avrcp *session, int err)
{
struct avrcp_server *server = session->server;
+ struct btd_service *service;
server->sessions = g_slist_remove(server->sessions, session);
session_abort_pending_pdu(session);
+ service = btd_device_get_service(session->dev, AVRCP_TARGET_UUID);
+ if (service != NULL) {
+ if (session->control_id == 0)
+ btd_service_connecting_complete(service, err);
+ else
+ btd_service_disconnecting_complete(service, 0);
+ }
+
+ service = btd_device_get_service(session->dev, AVRCP_REMOTE_UUID);
+ if (service != NULL) {
+ if (session->control_id == 0)
+ btd_service_connecting_complete(service, err);
+ else
+ btd_service_disconnecting_complete(service, 0);
+ }
+
if (session->browsing_timer > 0)
g_source_remove(session->browsing_timer);
@@ -4000,7 +3800,8 @@ static struct avrcp *session_create(struct avrcp_server *server,
}
static void state_changed(struct btd_device *device, avctp_state_t old_state,
- avctp_state_t new_state, void *user_data)
+ avctp_state_t new_state, int err,
+ void *user_data)
{
struct avrcp_server *server;
struct avrcp *session;
@@ -4016,7 +3817,7 @@ static void state_changed(struct btd_device *device, avctp_state_t old_state,
if (session == NULL)
break;
- session_destroy(session);
+ session_destroy(session, err);
break;
case AVCTP_STATE_CONNECTING:
@@ -4092,14 +3893,12 @@ struct avrcp_player *avrcp_register_player(struct btd_adapter *adapter,
struct avrcp_server *server;
struct avrcp_player *player;
GSList *l;
- static uint16_t id = 0;
server = find_server(servers, adapter);
if (!server)
return NULL;
player = g_new0(struct avrcp_player, 1);
- player->id = ++id;
player->server = server;
player->cb = cb;
player->user_data = user_data;
@@ -4122,9 +3921,6 @@ struct avrcp_player *avrcp_register_player(struct btd_adapter *adapter,
}
}
- avrcp_player_event(player,
- AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED, NULL);
-
return player;
}
@@ -4147,9 +3943,6 @@ void avrcp_unregister_player(struct avrcp_player *player)
target->player = g_slist_nth_data(server->players, 0);
}
- avrcp_player_event(player,
- AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED, NULL);
-
player_destroy(player);
}
@@ -4175,53 +3968,7 @@ static gboolean avrcp_handle_set_volume(struct avctp *conn,
return FALSE;
}
-static int avrcp_event(struct avrcp *session, uint8_t id, const void *data)
-{
- uint8_t buf[AVRCP_HEADER_LENGTH + 2];
- struct avrcp_header *pdu = (void *) buf;
- uint8_t code;
- uint16_t size;
- int err;
-
- /* Verify that the event is registered */
- if (!(session->registered_events & (1 << id)))
- return -ENOENT;
-
- memset(buf, 0, sizeof(buf));
-
- set_company_id(pdu->company_id, IEEEID_BTSIG);
- pdu->pdu_id = AVRCP_REGISTER_NOTIFICATION;
- code = AVC_CTYPE_CHANGED;
- pdu->params[0] = id;
-
- DBG("id=%u", id);
-
- switch (id) {
- case AVRCP_EVENT_VOLUME_CHANGED:
- size = 2;
- memcpy(&pdu->params[1], data, sizeof(uint8_t));
- break;
- default:
- error("Unknown event %u", id);
- return -EINVAL;
- }
-
- pdu->params_len = htons(size);
-
- err = avctp_send_vendordep(session->conn,
- session->transaction_events[id],
- code, AVC_SUBUNIT_PANEL,
- buf, size + AVRCP_HEADER_LENGTH);
- if (err < 0)
- return err;
-
- /* Unregister event as per AVRCP 1.3 spec, section 5.4.2 */
- session->registered_events ^= 1 << id;
-
- return err;
-}
-
-int avrcp_set_volume(struct btd_device *dev, uint8_t volume, bool notify)
+int avrcp_set_volume(struct btd_device *dev, uint8_t volume)
{
struct avrcp_server *server;
struct avrcp *session;
@@ -4233,23 +3980,18 @@ int avrcp_set_volume(struct btd_device *dev, uint8_t volume, bool notify)
return -EINVAL;
session = find_session(server->sessions, dev);
- if (session == NULL)
+ if (session == NULL || session->target == NULL)
return -ENOTCONN;
- if (notify) {
- if (!session->target)
- return -ENOTSUP;
- return avrcp_event(session, AVRCP_EVENT_VOLUME_CHANGED,
- &volume);
- }
-
- if (!session->controller || session->controller->version < 0x0104)
+ if (session->target->version < 0x0104)
return -ENOTSUP;
memset(buf, 0, sizeof(buf));
set_company_id(pdu->company_id, IEEEID_BTSIG);
+ DBG("volume=%u", volume);
+
pdu->pdu_id = AVRCP_SET_ABSOLUTE_VOLUME;
pdu->params[0] = volume;
pdu->params_len = htons(1);
@@ -4294,6 +4036,7 @@ static void avrcp_target_remove(struct btd_service *service)
control_unregister(service);
}
+#ifdef SUPPORT_AVRCP_TARGET
static void avrcp_target_server_remove(struct btd_profile *p,
struct btd_adapter *adapter)
{
@@ -4313,7 +4056,9 @@ static void avrcp_target_server_remove(struct btd_profile *p,
if (server->ct_record_id == 0)
avrcp_server_unregister(server);
}
+#endif
+#ifdef SUPPORT_AVRCP_TARGET
static int avrcp_target_server_probe(struct btd_profile *p,
struct btd_adapter *adapter)
{
@@ -4348,6 +4093,7 @@ done:
return 0;
}
+#endif
static struct btd_profile avrcp_target_profile = {
.name = "audio-avrcp-target",
@@ -4358,9 +4104,10 @@ static struct btd_profile avrcp_target_profile = {
.connect = avrcp_connect,
.disconnect = avrcp_disconnect,
-
+#ifdef SUPPORT_AVRCP_TARGET
.adapter_probe = avrcp_target_server_probe,
.adapter_remove = avrcp_target_server_remove,
+#endif
};
static int avrcp_controller_probe(struct btd_service *service)
@@ -4377,6 +4124,7 @@ static void avrcp_controller_remove(struct btd_service *service)
control_unregister(service);
}
+#ifdef SUPPORT_AVRCP_CONTROL
static void avrcp_controller_server_remove(struct btd_profile *p,
struct btd_adapter *adapter)
{
@@ -4396,7 +4144,9 @@ static void avrcp_controller_server_remove(struct btd_profile *p,
if (server->tg_record_id == 0)
avrcp_server_unregister(server);
}
+#endif
+#ifdef SUPPORT_AVRCP_CONTROL
static int avrcp_controller_server_probe(struct btd_profile *p,
struct btd_adapter *adapter)
{
@@ -4431,6 +4181,7 @@ done:
return 0;
}
+#endif
static struct btd_profile avrcp_controller_profile = {
.name = "avrcp-controller",
@@ -4441,37 +4192,24 @@ static struct btd_profile avrcp_controller_profile = {
.connect = avrcp_connect,
.disconnect = avrcp_disconnect,
+#ifdef SUPPORT_AVRCP_CONTROL
.adapter_probe = avrcp_controller_server_probe,
.adapter_remove = avrcp_controller_server_remove,
+#endif
};
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,
diff --git a/profiles/audio/avrcp.h b/profiles/audio/avrcp.h
index f60e0361..28074db5 100644
--- a/profiles/audio/avrcp.h
+++ b/profiles/audio/avrcp.h
@@ -104,7 +104,7 @@ struct avrcp_player_cb {
bool (*previous) (void *user_data);
};
-int avrcp_set_volume(struct btd_device *dev, uint8_t volume, bool notify);
+int avrcp_set_volume(struct btd_device *dev, uint8_t volume);
struct avrcp_player *avrcp_register_player(struct btd_adapter *adapter,
struct avrcp_player_cb *cb,
diff --git a/profiles/audio/control.c b/profiles/audio/control.c
index 9110b0fb..f4656d85 100644
--- a/profiles/audio/control.c
+++ b/profiles/audio/control.c
@@ -71,7 +71,8 @@ struct control {
};
static void state_changed(struct btd_device *dev, avctp_state_t old_state,
- avctp_state_t new_state, void *user_data)
+ avctp_state_t new_state, int err,
+ void *user_data)
{
struct control *control = user_data;
DBusConnection *conn = btd_get_dbus_connection();
@@ -337,13 +338,3 @@ int control_init_remote(struct btd_service *service)
return 0;
}
-
-gboolean control_is_active(struct btd_service *service)
-{
- struct control *control = btd_service_get_user_data(service);
-
- if (control && control->session)
- return TRUE;
-
- return FALSE;
-}
diff --git a/profiles/audio/control.h b/profiles/audio/control.h
index da8f16c9..4bda8968 100644
--- a/profiles/audio/control.h
+++ b/profiles/audio/control.h
@@ -29,7 +29,6 @@ struct btd_service;
int control_init_target(struct btd_service *service);
int control_init_remote(struct btd_service *service);
void control_unregister(struct btd_service *service);
-gboolean control_is_active(struct btd_service *service);
int control_connect(struct btd_service *service);
int control_disconnect(struct btd_service *service);
diff --git a/profiles/audio/media.c b/profiles/audio/media.c
index 72e20e80..35566829 100644
--- a/profiles/audio/media.c
+++ b/profiles/audio/media.c
@@ -62,7 +62,6 @@
#include "sink.h"
#endif
-
#define MEDIA_INTERFACE "org.bluez.Media1"
#define MEDIA_ENDPOINT_INTERFACE "org.bluez.MediaEndpoint1"
#define MEDIA_PLAYER_INTERFACE "org.mpris.MediaPlayer2.Player"
@@ -73,6 +72,8 @@
#define SINK_SUSPEND_TIMEOUT 4 /* 4 seconds */
unsigned int suspend_timer_id = 0;
+static gboolean a2dp_sink_support = false;
+static gboolean a2dp_source_support = true;
#endif
struct media_adapter {
@@ -704,9 +705,8 @@ static void config_cb(struct media_endpoint *endpoint, void *ret, int size,
void *user_data)
{
struct a2dp_config_data *data = user_data;
- gboolean *ret_value = ret;
- data->cb(data->setup, *ret_value ? TRUE : FALSE);
+ data->cb(data->setup, ret ? TRUE : FALSE);
}
static int set_config(struct a2dp_sep *sep, uint8_t *configuration,
@@ -1037,7 +1037,17 @@ static DBusMessage *register_endpoint(DBusConnection *conn, DBusMessage *msg,
if (parse_properties(&props, &uuid, &delay_reporting, &codec,
&capabilities, &size) < 0)
return btd_error_invalid_args(msg);
+#ifdef __TIZEN_PATCH__
+ if(!a2dp_sink_support && !g_strcmp0(uuid, A2DP_SINK_UUID)) {
+ error("A2DP sink role is not supported.");
+ return btd_error_not_supported(msg);
+ }
+ if(!a2dp_source_support && !g_strcmp0(uuid, A2DP_SOURCE_UUID)) {
+ error("A2DP source role is not supported.");
+ return btd_error_not_supported(msg);
+ }
+#endif
if (media_endpoint_create(adapter, sender, path, uuid, delay_reporting,
codec, capabilities, size, &err) == NULL) {
if (err == -EPROTONOSUPPORT)
diff --git a/profiles/audio/player.c b/profiles/audio/player.c
index c5f44dd6..94eb2eba 100644
--- a/profiles/audio/player.c
+++ b/profiles/audio/player.c
@@ -702,23 +702,6 @@ done:
folder->msg = NULL;
}
-void media_player_total_items_complete(struct media_player *mp,
- uint32_t num_of_items)
-{
- struct media_folder *folder = mp->scope;
-
- if (folder == NULL || folder->msg == NULL)
- return;
-
- if (folder->number_of_items != num_of_items) {
- folder->number_of_items = num_of_items;
-
- g_dbus_emit_property_changed(btd_get_dbus_connection(),
- mp->path, MEDIA_FOLDER_INTERFACE,
- "NumberOfItems");
- }
-}
-
static const GDBusMethodTable media_player_methods[] = {
{ GDBUS_METHOD("Play", NULL, NULL, media_player_play) },
{ GDBUS_METHOD("Pause", NULL, NULL, media_player_pause) },
@@ -908,9 +891,6 @@ static void media_folder_destroy(void *data)
static void media_player_change_scope(struct media_player *mp,
struct media_folder *folder)
{
- struct player_callback *cb = mp->cb;
- int err;
-
if (mp->scope == folder)
return;
@@ -940,19 +920,10 @@ cleanup:
done:
mp->scope = folder;
- if (cb->cbs->total_items) {
- err = cb->cbs->total_items(mp, folder->item->name,
- cb->user_data);
- if (err < 0)
- DBG("Failed to get total num of items");
- } else {
- g_dbus_emit_property_changed(btd_get_dbus_connection(),
- mp->path, MEDIA_FOLDER_INTERFACE,
- "NumberOfItems");
- }
-
g_dbus_emit_property_changed(btd_get_dbus_connection(), mp->path,
MEDIA_FOLDER_INTERFACE, "Name");
+ g_dbus_emit_property_changed(btd_get_dbus_connection(), mp->path,
+ MEDIA_FOLDER_INTERFACE, "NumberOfItems");
}
static struct media_folder *find_folder(GSList *folders, const char *pattern)
diff --git a/profiles/audio/player.h b/profiles/audio/player.h
index 082db868..ac2a3daf 100644
--- a/profiles/audio/player.h
+++ b/profiles/audio/player.h
@@ -64,8 +64,6 @@ struct media_player_callback {
uint64_t uid, void *user_data);
int (*add_to_nowplaying) (struct media_player *mp, const char *name,
uint64_t uid, void *user_data);
- int (*total_items) (struct media_player *mp, const char *name,
- void *user_data);
};
struct media_player *media_player_controller_create(const char *path,
@@ -106,8 +104,6 @@ void media_player_list_complete(struct media_player *mp, GSList *items,
void media_player_change_folder_complete(struct media_player *player,
const char *path, int ret);
void media_player_search_complete(struct media_player *mp, int ret);
-void media_player_total_items_complete(struct media_player *mp,
- uint32_t num_of_items);
void media_player_set_callbacks(struct media_player *mp,
const struct media_player_callback *cbs,
diff --git a/profiles/audio/sink.c b/profiles/audio/sink.c
index 80caa091..78a68872 100644
--- a/profiles/audio/sink.c
+++ b/profiles/audio/sink.c
@@ -242,8 +242,6 @@ static void discovery_complete(struct avdtp *session, GSList *seps, struct avdtp
struct sink *sink = user_data;
int id, perr;
- sink->connect_id = 0;
-
if (err) {
avdtp_unref(sink->session);
sink->session = NULL;
@@ -289,9 +287,7 @@ gboolean sink_setup_stream(struct btd_service *service, struct avdtp *session)
if (!sink->session)
return FALSE;
- sink->connect_id = a2dp_discover(sink->session, discovery_complete,
- sink);
- if (sink->connect_id == 0)
+ if (avdtp_discover(sink->session, discovery_complete, sink) < 0)
return FALSE;
return TRUE;
@@ -445,7 +441,7 @@ int sink_disconnect(struct btd_service *service)
if (sink->connect_id > 0) {
a2dp_cancel(sink->connect_id);
sink->connect_id = 0;
- btd_service_connecting_complete(sink->service, 0);
+ btd_service_connecting_complete(sink->service, -ECANCELED);
avdtp_unref(sink->session);
sink->session = NULL;
diff --git a/profiles/audio/source.c b/profiles/audio/source.c
index e834c77f..b235a7d9 100644
--- a/profiles/audio/source.c
+++ b/profiles/audio/source.c
@@ -227,8 +227,6 @@ static void discovery_complete(struct avdtp *session, GSList *seps, struct avdtp
struct source *source = user_data;
int id, perr;
- source->connect_id = 0;
-
if (err) {
avdtp_unref(source->session);
source->session = NULL;
@@ -275,9 +273,7 @@ gboolean source_setup_stream(struct btd_service *service,
if (!source->session)
return FALSE;
- source->connect_id = a2dp_discover(source->session, discovery_complete,
- source);
- if (source->connect_id == 0)
+ if (avdtp_discover(source->session, discovery_complete, source) < 0)
return FALSE;
return TRUE;
@@ -402,7 +398,7 @@ int source_disconnect(struct btd_service *service)
if (source->connect_id > 0) {
a2dp_cancel(source->connect_id);
source->connect_id = 0;
- btd_service_connecting_complete(source->service, 0);
+ btd_service_connecting_complete(source->service, -ECANCELED);
avdtp_unref(source->session);
source->session = NULL;
diff --git a/profiles/audio/transport.c b/profiles/audio/transport.c
index 1eb951f4..112ec176 100644
--- a/profiles/audio/transport.c
+++ b/profiles/audio/transport.c
@@ -653,7 +653,6 @@ static void set_volume(const GDBusPropertyTable *property,
struct media_transport *transport = data;
struct a2dp_transport *a2dp = transport->data;
uint16_t volume;
- bool notify;
if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16) {
g_dbus_pending_property_error(id,
@@ -671,21 +670,12 @@ static void set_volume(const GDBusPropertyTable *property,
return;
}
- g_dbus_pending_property_success(id);
-
- if (a2dp->volume == volume)
- return;
+ if (a2dp->volume != volume)
+ avrcp_set_volume(transport->device, volume);
a2dp->volume = volume;
- notify = transport->source_watch ? true : false;
- if (notify)
- g_dbus_emit_property_changed(btd_get_dbus_connection(),
- transport->path,
- MEDIA_TRANSPORT_INTERFACE,
- "Volume");
-
- avrcp_set_volume(transport->device, volume, notify);
+ g_dbus_pending_property_success(id);
}
static const GDBusMethodTable transport_methods[] = {
@@ -827,6 +817,7 @@ static int media_transport_init_sink(struct media_transport *transport)
transport->destroy = destroy_a2dp;
a2dp->volume = 127;
+ avrcp_set_volume(transport->device, a2dp->volume);
transport->source_watch = source_add_state_cb(service,
source_state_changed,
transport);
diff --git a/profiles/gap/gas.c b/profiles/gap/gas.c
index 819a9672..09aa832e 100644
--- a/profiles/gap/gas.c
+++ b/profiles/gap/gas.c
@@ -166,6 +166,40 @@ static void handle_appearance(struct gas *gas, uint16_t value_handle)
DBG("Failed to send request to read appearance");
}
+#ifdef __TIZEN_PATCH__
+static void read_rpa_res_characteristic_value_cb(bool success, uint8_t att_ecode,
+ const uint8_t *value, uint16_t length,
+ void *user_data)
+{
+ struct gas *gas = user_data;
+ uint8_t rpa_res_support;
+
+ if (!success) {
+ DBG("Reading RPA Resolution Char Value failed with ATT error: %u", att_ecode);
+ return;
+ }
+
+ /* The RPA Resolution Char Value value is a 8-bit unsigned integer */
+ if (length != 1) {
+ DBG("Malformed RPA resolution char value");
+ return;
+ }
+
+ rpa_res_support = *value;
+
+ DBG("GAP RPA Resolution Char Value: %d", rpa_res_support);
+
+ device_set_rpa_res_char_value(gas->device, rpa_res_support);
+}
+
+static void handle_rpa_res_characteristic_value(struct gas *gas, uint16_t value_handle)
+{
+ if (!bt_gatt_client_read_value(gas->client, value_handle,
+ read_rpa_res_characteristic_value_cb, gas, NULL))
+ DBG("Failed to send request to read RPA resolution Char Value");
+}
+#endif
+
static bool uuid_cmp(uint16_t u16, const bt_uuid_t *uuid)
{
bt_uuid_t lhs;
@@ -192,6 +226,10 @@ static void handle_characteristic(struct gatt_db_attribute *attr,
handle_device_name(gas, value_handle);
else if (uuid_cmp(GATT_CHARAC_APPEARANCE, &uuid))
handle_appearance(gas, value_handle);
+#ifdef __TIZEN_PATCH__
+ else if (uuid_cmp(GATT_CHARAC_CENTRAL_RPA_RESOLUTION, &uuid))
+ handle_rpa_res_characteristic_value(gas, value_handle);
+#endif
else {
char uuid_str[MAX_LEN_UUID_STR];
diff --git a/profiles/network/bnep.c b/profiles/network/bnep.c
index 0350f92a..1a1e1a6f 100644
--- a/profiles/network/bnep.c
+++ b/profiles/network/bnep.c
@@ -67,17 +67,6 @@ static unsigned long bnepgetconnlist;
static unsigned long bnepgetconninfo;
#endif
-static struct {
- const char *name; /* Friendly name */
- const char *uuid128; /* UUID 128 */
- uint16_t id; /* Service class identifier */
-} __svc[] = {
- { "panu", PANU_UUID, BNEP_SVC_PANU },
- { "gn", GN_UUID, BNEP_SVC_GN },
- { "nap", NAP_UUID, BNEP_SVC_NAP },
- { NULL }
-};
-
struct __service_16 {
uint16_t dst;
uint16_t src;
@@ -98,26 +87,6 @@ struct bnep {
void *disconn_data;
};
-const char *bnep_uuid(uint16_t id)
-{
- int i;
-
- for (i = 0; __svc[i].uuid128; i++)
- if (__svc[i].id == id)
- return __svc[i].uuid128;
- return NULL;
-}
-
-const char *bnep_name(uint16_t id)
-{
- int i;
-
- for (i = 0; __svc[i].name; i++)
- if (__svc[i].id == id)
- return __svc[i].name;
- return NULL;
-}
-
int bnep_init(void)
{
ctl = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_BNEP);
@@ -191,6 +160,9 @@ static int bnep_connadd(int sk, uint16_t role, char *dev)
req.sock = sk;
req.role = role;
+#ifdef __TIZEN_PATCH__
+ req.flags = (1 << BNEP_SETUP_RESPONSE);
+#endif
if (ioctl(ctl, BNEPCONNADD, &req) < 0) {
int err = -errno;
error("bnep: Failed to add device %s: %s(%d)",
@@ -202,6 +174,20 @@ static int bnep_connadd(int sk, uint16_t role, char *dev)
return 0;
}
+#ifdef __TIZEN_PATCH__
+static uint32_t bnep_getsuppfeat(void)
+{
+ uint32_t feat;
+
+ if (ioctl(ctl, BNEPGETSUPPFEAT, &feat) < 0)
+ feat = 0;
+
+ DBG("supported features: 0x%x", feat);
+
+ return feat;
+}
+#endif
+
static int bnep_if_up(const char *devname)
{
struct ifreq ifr;
@@ -441,17 +427,21 @@ void bnep_free(struct bnep *session)
g_free(session);
}
-int bnep_connect(struct bnep *session, bnep_connect_cb conn_cb, void *data)
+int bnep_connect(struct bnep *session, bnep_connect_cb conn_cb,
+ bnep_disconnect_cb disconn_cb,
+ void *conn_data, void *disconn_data)
{
GError *gerr = NULL;
int err;
- if (!session || !conn_cb)
+ if (!session || !conn_cb || !disconn_cb)
return -EINVAL;
session->attempts = 0;
session->conn_cb = conn_cb;
- session->conn_data = data;
+ session->disconn_cb = disconn_cb;
+ session->conn_data = conn_data;
+ session->disconn_data = disconn_data;
bt_io_get(session->io, &gerr, BT_IO_OPT_DEST_BDADDR, &session->dst_addr,
BT_IO_OPT_INVALID);
@@ -490,18 +480,6 @@ void bnep_disconnect(struct bnep *session)
bnep_conndel(&session->dst_addr);
}
-void bnep_set_disconnect(struct bnep *session, bnep_disconnect_cb disconn_cb,
- void *data)
-{
- if (!session || !disconn_cb)
- return;
-
- if (!session->disconn_cb && !session->disconn_data) {
- session->disconn_cb = disconn_cb;
- session->disconn_data = data;
- }
-}
-
#ifndef __TIZEN_PATCH__
static int bnep_add_to_bridge(const char *devname, const char *bridge)
{
@@ -576,65 +554,67 @@ static int bnep_del_from_bridge(const char *devname, const char *bridge)
return err;
}
-int bnep_server_add(int sk, uint16_t dst, char *bridge, char *iface,
- const bdaddr_t *addr)
+static ssize_t bnep_send_ctrl_rsp(int sk, uint8_t ctrl, uint16_t resp)
{
- int err;
+ ssize_t sent;
- if (!bridge || !iface || !addr)
- return -EINVAL;
+ switch (ctrl) {
+ case BNEP_CMD_NOT_UNDERSTOOD: {
+ struct bnep_ctrl_cmd_not_understood_cmd rsp;
- err = bnep_connadd(sk, dst, iface);
- if (err < 0)
- return err;
+ rsp.type = BNEP_CONTROL;
+ rsp.ctrl = ctrl;
+ rsp.unkn_ctrl = (uint8_t) resp;
-#ifndef __TIZEN_PATCH__
- err = bnep_add_to_bridge(iface, bridge);
- if (err < 0) {
- bnep_conndel(addr);
- return err;
+ sent = send(sk, &rsp, sizeof(rsp), 0);
+ break;
}
-#endif
-
- return bnep_if_up(iface);
-}
-
-void bnep_server_delete(char *bridge, char *iface, const bdaddr_t *addr)
-{
- if (!bridge || !iface || !addr)
- return;
+ case BNEP_FILTER_MULT_ADDR_RSP:
+ case BNEP_FILTER_NET_TYPE_RSP:
+ case BNEP_SETUP_CONN_RSP: {
+ struct bnep_control_rsp rsp;
- bnep_del_from_bridge(iface, bridge);
- bnep_if_down(iface);
- bnep_conndel(addr);
-}
+ rsp.type = BNEP_CONTROL;
+ rsp.ctrl = ctrl;
+ rsp.resp = htons(resp);
-ssize_t bnep_send_ctrl_rsp(int sk, uint8_t type, uint8_t ctrl, uint16_t resp)
-{
- struct bnep_control_rsp rsp;
-
- rsp.type = type;
- rsp.ctrl = ctrl;
- rsp.resp = htons(resp);
+ sent = send(sk, &rsp, sizeof(rsp), 0);
+ break;
+ }
+ default:
+ error("bnep: wrong response type");
+ sent = -1;
+ break;
+ }
- return send(sk, &rsp, sizeof(rsp), 0);
+ return sent;
}
-uint16_t bnep_setup_decode(struct bnep_setup_conn_req *req, uint16_t *dst,
- uint16_t *src)
+static uint16_t bnep_setup_decode(int sk, struct bnep_setup_conn_req *req,
+ uint16_t *dst)
{
const uint8_t bt_base[] = { 0x00, 0x00, 0x10, 0x00, 0x80, 0x00,
0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB };
+ uint16_t src;
uint8_t *dest, *source;
uint32_t val;
+#ifdef __TIZEN_PATCH__
+ if (((req->type != BNEP_CONTROL) &&
+ (req->type != (BNEP_CONTROL | BNEP_EXT_HEADER))) ||
+ req->ctrl != BNEP_SETUP_CONN_REQ)
+#else
+ if (req->type != BNEP_CONTROL || req->ctrl != BNEP_SETUP_CONN_REQ)
+#endif
+ return BNEP_CONN_NOT_ALLOWED;
+
dest = req->service;
source = req->service + req->uuid_size;
switch (req->uuid_size) {
case 2: /* UUID16 */
*dst = get_be16(dest);
- *src = get_be16(source);
+ src = get_be16(source);
break;
case 16: /* UUID128 */
/* Check that the bytes in the UUID, except the service ID
@@ -658,7 +638,7 @@ uint16_t bnep_setup_decode(struct bnep_setup_conn_req *req, uint16_t *dst,
if (val > 0xffff)
return BNEP_CONN_INVALID_SRC;
- *src = val;
+ src = val;
break;
default:
return BNEP_CONN_INVALID_SVC;
@@ -668,12 +648,12 @@ uint16_t bnep_setup_decode(struct bnep_setup_conn_req *req, uint16_t *dst,
switch (*dst) {
case BNEP_SVC_NAP:
case BNEP_SVC_GN:
- if (*src == BNEP_SVC_PANU)
+ 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)
+ if (src == BNEP_SVC_PANU || src == BNEP_SVC_GN ||
+ src == BNEP_SVC_NAP)
return BNEP_SUCCESS;
return BNEP_CONN_INVALID_SRC;
@@ -696,3 +676,157 @@ int bnep_conndel_wrapper(const bdaddr_t *dst)
}
#endif
+#ifdef __TIZEN_PATCH__
+static int bnep_server_add_legacy(int sk, uint16_t dst, char *bridge,
+ char *iface, const bdaddr_t *addr,
+ uint8_t *setup_data, int len)
+{
+ int err, n;
+ uint16_t rsp;
+
+ n = read(sk, setup_data, len);
+ if (n != len) {
+ err = -EIO;
+ rsp = BNEP_CONN_NOT_ALLOWED;
+ goto reply;
+ }
+
+ err = bnep_connadd(sk, dst, iface);
+ if (err < 0) {
+ rsp = BNEP_CONN_NOT_ALLOWED;
+ goto reply;
+ }
+
+ err = bnep_if_up(iface);
+ if (err < 0) {
+ bnep_del_from_bridge(iface, bridge);
+ bnep_conndel(addr);
+ rsp = BNEP_CONN_NOT_ALLOWED;
+ goto reply;
+ }
+
+ rsp = BNEP_SUCCESS;
+
+reply:
+ if (bnep_send_ctrl_rsp(sk, BNEP_SETUP_CONN_RSP, rsp) < 0) {
+ err = -errno;
+ error("bnep: send ctrl rsp error: %s (%d)", strerror(errno),
+ errno);
+ }
+
+ return err;
+}
+#endif
+
+int bnep_server_add(int sk, char *bridge, char *iface, const bdaddr_t *addr,
+ uint8_t *setup_data, int len)
+{
+ int err;
+#ifdef __TIZEN_PATCH__
+ uint32_t feat;
+#endif
+ uint16_t rsp, dst;
+ struct bnep_setup_conn_req *req = (void *) setup_data;
+
+ /* Highest known Control command ID
+ * is BNEP_FILTER_MULT_ADDR_RSP = 0x06 */
+ if (req->type == BNEP_CONTROL &&
+ req->ctrl > BNEP_FILTER_MULT_ADDR_RSP) {
+ error("bnep: cmd not understood");
+ err = bnep_send_ctrl_rsp(sk, BNEP_CMD_NOT_UNDERSTOOD,
+ req->ctrl);
+ if (err < 0)
+ error("send not understood ctrl rsp error: %s (%d)",
+ strerror(errno), errno);
+
+ return err;
+ }
+
+ /* Processing BNEP_SETUP_CONNECTION_REQUEST_MSG */
+ rsp = bnep_setup_decode(sk, req, &dst);
+ if (rsp != BNEP_SUCCESS) {
+ err = -rsp;
+ error("bnep: error while decoding setup connection request: %d",
+ rsp);
+#ifdef __TIZEN_PATCH__
+ goto failed;
+#else
+ goto reply;
+#endif
+ }
+
+#ifdef __TIZEN_PATCH__
+ feat = bnep_getsuppfeat();
+
+ /*
+ * Take out setup data if kernel doesn't support handling it, especially
+ * setup request. If kernel would have set session flags, they should
+ * be checked and handled respectively.
+ */
+ if (!feat || !(feat & (1 << BNEP_SETUP_RESPONSE)))
+ return bnep_server_add_legacy(sk, dst, bridge, iface, addr,
+ setup_data, len);
+#endif
+
+ err = bnep_connadd(sk, dst, iface);
+ if (err < 0) {
+ rsp = BNEP_CONN_NOT_ALLOWED;
+#ifdef __TIZEN_PATCH__
+ goto failed;
+#else
+ goto reply;
+#endif
+ }
+
+#ifndef __TIZEN_PATCH__
+ err = bnep_add_to_bridge(iface, bridge);
+ if (err < 0)
+ goto failed_conn;
+#endif
+
+ err = bnep_if_up(iface);
+ if (err < 0)
+#ifdef __TIZEN_PATCH__
+ goto failed_bridge;
+#else
+ rsp = BNEP_CONN_NOT_ALLOWED;
+#endif
+
+#ifdef __TIZEN_PATCH__
+ return 0;
+
+failed_bridge:
+ bnep_del_from_bridge(iface, bridge);
+
+failed_conn:
+ bnep_conndel(addr);
+
+ return err;
+
+failed:
+ if (bnep_send_ctrl_rsp(sk, BNEP_SETUP_CONN_RSP, rsp) < 0) {
+ err = -errno;
+ error("bnep: send ctrl rsp error: %s (%d)", strerror(-err),
+ -err);
+ }
+#else
+reply:
+ if (bnep_send_ctrl_rsp(sk, BNEP_SETUP_CONN_RSP, rsp) < 0) {
+ err = -errno;
+ error("bnep: send ctrl rsp error: %s (%d)", strerror(errno),
+ errno);
+ }
+#endif
+
+ return err;
+}
+
+void bnep_server_delete(char *bridge, char *iface, const bdaddr_t *addr)
+{
+ if (!bridge || !iface || !addr)
+ return;
+
+ bnep_del_from_bridge(iface, bridge);
+ bnep_if_down(iface);
+ bnep_conndel(addr);
+}
diff --git a/profiles/network/bnep.h b/profiles/network/bnep.h
index 7255f825..f915cf30 100644
--- a/profiles/network/bnep.h
+++ b/profiles/network/bnep.h
@@ -26,27 +26,20 @@ struct bnep;
int bnep_init(void);
int bnep_cleanup(void);
-const char *bnep_uuid(uint16_t id);
-const char *bnep_name(uint16_t id);
-
struct bnep *bnep_new(int sk, uint16_t local_role, uint16_t remote_role,
char *iface);
void bnep_free(struct bnep *session);
typedef void (*bnep_connect_cb) (char *iface, int err, void *data);
-int bnep_connect(struct bnep *b, bnep_connect_cb conn_cb, void *data);
typedef void (*bnep_disconnect_cb) (void *data);
-void bnep_set_disconnect(struct bnep *session, bnep_disconnect_cb disconn_cb,
- void *data);
+int bnep_connect(struct bnep *session, bnep_connect_cb conn_cb,
+ bnep_disconnect_cb disconn_cb,
+ void *conn_data, void *disconn_data);
void bnep_disconnect(struct bnep *session);
-int bnep_server_add(int sk, uint16_t dst, char *bridge, char *iface,
- const bdaddr_t *addr);
+int bnep_server_add(int sk, char *bridge, char *iface, const bdaddr_t *addr,
+ uint8_t *setup_data, int len);
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_decode(struct bnep_setup_conn_req *req, uint16_t *dst,
- uint16_t *src);
#ifdef __TIZEN_PATCH__
int bnep_if_down_wrapper(const char *devname);
int bnep_conndel_wrapper(const bdaddr_t *dst);
diff --git a/profiles/network/connection.c b/profiles/network/connection.c
index bb8e34c3..e19e945e 100644
--- a/profiles/network/connection.c
+++ b/profiles/network/connection.c
@@ -274,14 +274,12 @@ static void connect_cb(GIOChannel *chan, GError *err, gpointer data)
if (!nc->session)
goto failed;
- perr = bnep_connect(nc->session, bnep_conn_cb, nc);
+ perr = bnep_connect(nc->session, bnep_conn_cb, bnep_disconn_cb, nc, nc);
if (perr < 0) {
error("bnep connect(): %s (%d)", strerror(-perr), -perr);
goto failed;
}
- bnep_set_disconnect(nc->session, bnep_disconn_cb, nc);
-
if (nc->io) {
g_io_channel_unref(nc->io);
nc->io = NULL;
@@ -300,21 +298,26 @@ static DBusMessage *local_connect(DBusConnection *conn,
struct btd_service *service;
struct network_conn *nc;
const char *svc;
- const char *uuid;
uint16_t id;
int err;
+ char uuid_str[MAX_LEN_UUID_STR];
+ bt_uuid_t uuid16, uuid128;
if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &svc,
DBUS_TYPE_INVALID) == FALSE)
return btd_error_invalid_args(msg);
id = get_pan_srv_id(svc);
- uuid = bnep_uuid(id);
-
- if (uuid == NULL)
+ bt_uuid16_create(&uuid16, id);
+#ifdef __TIZEN_PATCH__
+ bt_uuid_to_uuid128(&uuid16, &uuid128);
+#else
+ bt_uuid_to_uuid128(&uuid128, &uuid16);
+#endif
+ if (bt_uuid_to_string(&uuid128, uuid_str, MAX_LEN_UUID_STR) < 0)
return btd_error_invalid_args(msg);
- service = btd_device_get_service(peer->device, uuid);
+ service = btd_device_get_service(peer->device, uuid_str);
if (service == NULL)
return btd_error_not_supported(msg);
@@ -455,15 +458,29 @@ static gboolean network_property_get_uuid(const GDBusPropertyTable *property,
{
struct network_peer *peer = data;
struct network_conn *nc;
- const char *uuid;
+ char uuid_str[MAX_LEN_UUID_STR];
+#ifdef __TIZEN_PATCH__
+ const char *uuid = uuid_str;
+#endif
+ bt_uuid_t uuid16, uuid128;
nc = find_connection_by_state(peer->connections, CONNECTED);
if (nc == NULL)
return FALSE;
- uuid = bnep_uuid(nc->id);
+ bt_uuid16_create(&uuid16, nc->id);
+#ifdef __TIZEN_PATCH__
+ bt_uuid_to_uuid128(&uuid16, &uuid128);
+#else
+ bt_uuid_to_uuid128(&uuid128, &uuid16);
+#endif
+ bt_uuid_to_string(&uuid128, uuid_str, MAX_LEN_UUID_STR);
+#ifdef __TIZEN_PATCH__
dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid);
+#else
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid_str);
+#endif
return TRUE;
}
diff --git a/profiles/network/server.c b/profiles/network/server.c
index 4f139020..2450fbea 100644
--- a/profiles/network/server.c
+++ b/profiles/network/server.c
@@ -47,6 +47,7 @@
#include "src/log.h"
#include "src/error.h"
#include "src/sdpd.h"
+#include "src/shared/util.h"
#include "bnep.h"
#include "server.h"
@@ -118,14 +119,38 @@ static struct network_server *find_server(GSList *list, uint16_t id)
static struct network_server *find_server_by_uuid(GSList *list,
const char *uuid)
{
- for (; list; list = list->next) {
- struct network_server *ns = list->data;
+ bt_uuid_t srv_uuid, bnep_uuid;
- if (strcasecmp(uuid, bnep_uuid(ns->id)) == 0)
- return ns;
+ if (!bt_string_to_uuid(&srv_uuid, uuid)) {
+ for (; list; list = list->next) {
+ struct network_server *ns = list->data;
- if (strcasecmp(uuid, bnep_name(ns->id)) == 0)
- return ns;
+ bt_uuid16_create(&bnep_uuid, ns->id);
+
+ /* UUID value compare */
+ if (!bt_uuid_cmp(&srv_uuid, &bnep_uuid))
+ return ns;
+ }
+ } else {
+ for (; list; list = list->next) {
+ struct network_server *ns = list->data;
+
+ /* String value compare */
+ switch (ns->id) {
+ case BNEP_SVC_PANU:
+ if (!strcasecmp(uuid, "panu"))
+ return ns;
+ break;
+ case BNEP_SVC_NAP:
+ if (!strcasecmp(uuid, "nap"))
+ return ns;
+ break;
+ case BNEP_SVC_GN:
+ if (!strcasecmp(uuid, "gn"))
+ return ns;
+ break;
+ }
+ }
}
return NULL;
@@ -373,12 +398,16 @@ static gboolean server_disconnected_cb(GIOChannel *chan,
static gboolean bnep_setup(GIOChannel *chan,
GIOCondition cond, gpointer user_data)
{
+ const uint8_t bt_base[] = { 0x00, 0x00, 0x10, 0x00, 0x80, 0x00,
+ 0x00, 0x80, 0x5F, 0x9B, 0x34, 0xFB };
struct network_adapter *na = user_data;
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;
+ uint16_t dst_role = 0;
+ uint32_t val;
int n, sk;
+ char *bridge = NULL;
if (cond & G_IO_NVAL)
return FALSE;
@@ -390,59 +419,64 @@ static gboolean bnep_setup(GIOChannel *chan,
sk = g_io_channel_unix_get_fd(chan);
+#ifdef __TIZEN_PATCH__
+ /*
+ * BNEP_SETUP_CONNECTION_REQUEST_MSG should be read and left in case
+ * of kernel setup connection msg handling.
+ */
+ n = recv(sk, packet, sizeof(packet), MSG_PEEK);
+#else
/* Reading BNEP_SETUP_CONNECTION_REQUEST_MSG */
n = read(sk, packet, sizeof(packet));
+#endif
if (n < 0) {
error("read(): %s(%d)", strerror(errno), errno);
return FALSE;
}
- /* Highest known Control command ID
- * is BNEP_FILTER_MULT_ADDR_RSP = 0x06 */
- if (req->type == BNEP_CONTROL &&
- req->ctrl > BNEP_FILTER_MULT_ADDR_RSP) {
- uint8_t pkt[3];
-
- pkt[0] = BNEP_CONTROL;
- pkt[1] = BNEP_CMD_NOT_UNDERSTOOD;
- pkt[2] = req->ctrl;
-
- send(sk, pkt, sizeof(pkt), 0);
-
+ /*
+ * Initial received data packet is BNEP_SETUP_CONNECTION_REQUEST_MSG
+ * minimal size of this frame is 3 octets: 1 byte of BNEP Type +
+ * 1 byte of BNEP Control Type + 1 byte of BNEP services UUID size.
+ */
+ if (n < 3) {
+ error("To few setup connection request data received");
return FALSE;
}
- if (req->type != BNEP_CONTROL || req->ctrl != BNEP_SETUP_CONN_REQ)
- return FALSE;
-
- rsp = bnep_setup_decode(req, &dst_role, &src_role);
- if (rsp != BNEP_SUCCESS)
- goto reply;
+ switch (req->uuid_size) {
+ case 2:
+ dst_role = get_be16(req->service);
+ break;
+ case 16:
+ if (memcmp(&req->service[4], bt_base, sizeof(bt_base)) != 0)
+ break;
- rsp = BNEP_CONN_NOT_ALLOWED;
+ /* Intentional no-brake */
- ns = find_server(na->servers, dst_role);
- if (!ns) {
- error("Server unavailable: (0x%x)", dst_role);
- goto reply;
- }
+ case 4:
+ val = get_be32(req->service);
+ if (val > 0xffff)
+ break;
- if (!ns->record_id) {
- error("Service record not available");
- goto reply;
+ dst_role = val;
+ break;
+ default:
+ break;
}
- if (!ns->bridge) {
- error("Bridge interface not configured");
- goto reply;
- }
+ ns = find_server(na->servers, dst_role);
+ if (!ns || !ns->record_id || !ns->bridge)
+ error("Server error, bridge not initialized: (0x%x)", dst_role);
+ else
+ bridge = ns->bridge;
strncpy(na->setup->dev, BNEP_INTERFACE, 16);
na->setup->dev[15] = '\0';
- if (bnep_server_add(sk, dst_role, ns->bridge, na->setup->dev,
- &na->setup->dst) < 0)
- goto reply;
+ if (bnep_server_add(sk, bridge, na->setup->dev, &na->setup->dst,
+ packet, n) < 0)
+ error("BNEP server cannot be added");
#ifdef __TIZEN_PATCH__
{
@@ -470,11 +504,6 @@ static gboolean bnep_setup(GIOChannel *chan,
na->setup = NULL;
- rsp = BNEP_SUCCESS;
-
-reply:
- bnep_send_ctrl_rsp(sk, BNEP_CONTROL, BNEP_SETUP_CONN_RSP, rsp);
-
return FALSE;
}
@@ -908,10 +937,10 @@ int server_register(struct btd_adapter *adapter, uint16_t id)
goto done;
#ifndef __TIZEN_PATCH__
- if (!g_dbus_register_interface(btd_get_dbus_connection(),
- path, NETWORK_SERVER_INTERFACE,
- server_methods, NULL, NULL,
- na, path_unregister)) {
+ if (!g_dbus_register_interface(btd_get_dbus_connection(), path,
+ NETWORK_SERVER_INTERFACE,
+ server_methods, NULL, NULL, na,
+ path_unregister)) {
error("D-Bus failed to register %s interface",
NETWORK_SERVER_INTERFACE);
server_free(ns);
diff --git a/src/adapter.c b/src/adapter.c
index 1e55b6a4..829581cd 100755
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -74,6 +74,7 @@
#include "attrib/gatt.h"
#include "attrib-server.h"
#include "gatt-database.h"
+#include "advertising.h"
#include "eir.h"
#ifdef __TIZEN_PATCH__
@@ -201,9 +202,6 @@ struct le_data_length_read_request {
struct btd_adapter *adapter;
DBusMessage *msg;
};
-
-static GSList *read_host_requests = NULL;
-
#endif
struct btd_adapter {
@@ -237,9 +235,7 @@ struct btd_adapter {
bool le_privacy_enabled; /* whether LE Privacy feature enabled */
char local_irk[MGMT_IRK_SIZE]; /* adapter local IRK */
uint8_t disc_type;
-#ifdef IPSP_SUPPORT
bool ipsp_intialized; /* Ipsp Initialization state */
-#endif
struct le_data_length_read_handler *read_handler;
struct le_data_length_read_default_data_length_handler *def_read_handler;
#endif
@@ -265,6 +261,7 @@ struct btd_adapter {
sdp_list_t *services; /* Services associated to adapter */
struct btd_gatt_database *database;
+ struct btd_advertising *adv_manager;
gboolean initialized;
#ifdef __TIZEN_PATCH__
@@ -302,9 +299,11 @@ struct btd_adapter {
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;
+ uint8_t central_rpa_res_support;
#endif
bool is_default; /* true if adapter is default one */
};
@@ -1236,6 +1235,13 @@ static void adapter_service_insert(struct btd_adapter *adapter, sdp_record_t *re
DBG("%s", adapter->path);
+#ifdef __TIZEN_PATCH__
+ if (rec == NULL) {
+ DBG("record is NULL return");
+ return;
+ }
+#endif
+
/* skip record without a browse group */
if (sdp_get_browse_groups(rec, &browse_list) < 0) {
DBG("skipping record without browse group");
@@ -1334,6 +1340,7 @@ static void service_auth_cancel(struct service_auth *auth)
g_free(auth);
}
+
#ifdef __TIZEN_PATCH__
void btd_adapter_unpair_device(struct btd_adapter *adapter,
struct btd_device *dev)
@@ -1709,6 +1716,9 @@ static void start_discovery_complete(uint8_t status, uint16_t length,
const struct mgmt_cp_start_discovery *rp = param;
DBG("status 0x%02x", status);
+#ifndef __TIZEN_PATCH__
+ DBG("Discovery Type 0x%02x", rp->type);
+#endif
if (length < sizeof(*rp)) {
error("Wrong size of start discovery return parameters");
@@ -1716,10 +1726,6 @@ static void start_discovery_complete(uint8_t status, uint16_t length,
}
if (status == MGMT_STATUS_SUCCESS) {
-#ifndef __TIZEN_PATCH__
- DBG("Discovery Type 0x%02x", rp->type);
-#endif
-
#ifdef __TIZEN_PATCH__
DBG("Return param discovery type 0x%02x", rp->type);
adapter->discovery_type |= rp->type;
@@ -1763,13 +1769,13 @@ static void start_le_discovery_complete(uint8_t status, uint16_t length,
const struct mgmt_cp_start_discovery *rp = param;
DBG("status 0x%02x", status);
+ DBG("Discovery Type 0x%02x", rp->type);
if (length < sizeof(*rp)) {
- error("Wrong size of start LE discovery return parameters");
+ error("Wrong size of start discovery return parameters");
return;
}
if (status == MGMT_STATUS_SUCCESS) {
- DBG("Discovery Type 0x%02x", rp->type);
adapter->discovery_type |= rp->type;
adapter->discovery_enable = 0x01;
@@ -2567,14 +2573,14 @@ static int set_adv_data_device_name(uint8_t *adv_data, int adv_len, char *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);
+ name_len = ADV_DATA_MAX_LENGTH - adv_len;
} else {
adv_data[j + 1] = EIR_NAME_COMPLETE;
- memcpy(adv_data + j + 2, name, name_len);
}
+ adv_data[j] = name_len + 1;
+ memcpy(adv_data + j + 2, name, name_len);
g_free(data);
return adv_len + name_len;
@@ -3696,7 +3702,6 @@ static void le_read_maximum_data_length_return_param_complete(
const struct mgmt_rp_le_read_maximum_data_length *rp = param;
uint16_t max_tx_octects, max_tx_time;
uint16_t max_rx_octects, max_rx_time;
- int32_t err;
if (status != MGMT_STATUS_SUCCESS) {
error("le read maximum data length failed: %s (0x%02x)",
@@ -3705,22 +3710,18 @@ static void le_read_maximum_data_length_return_param_complete(
max_tx_time =0;
max_rx_octects = 0;
max_rx_time = 0;
- err = -EIO;
+ return;
}
if (length < sizeof(*rp)) {
error("Too small le read maximum data length response");
- max_tx_octects = 0;
- max_tx_time =0;
- max_rx_octects = 0;
- max_rx_time = 0;
- err = -EIO;
+ g_free(adapter->read_handler);
+ return;
} else {
max_tx_octects = rp->max_tx_octets;
max_tx_time =rp->max_tx_time;
max_rx_octects = rp->max_rx_octets;
max_rx_time = rp->max_rx_time;
- err = 0;
}
if (!adapter->read_handler ||
@@ -3732,7 +3733,7 @@ static void le_read_maximum_data_length_return_param_complete(
adapter->read_handler->read_callback(adapter,
max_tx_octects, max_tx_time,
max_rx_octects, max_rx_time,
- err, adapter->read_handler->user_data);
+ adapter->read_handler->user_data);
g_free(adapter->read_handler);
adapter->read_handler = NULL;
@@ -3776,13 +3777,11 @@ static void le_read_data_length_complete(
struct btd_adapter *adapter,
uint16_t max_tx_octects, uint16_t max_tx_time,
uint16_t max_rx_octects, uint16_t max_rx_time,
- int32_t err, void *user_data)
+ void *user_data)
{
DBusMessage *reply;
struct le_data_length_read_request *read_request;
- DBG("inside le_read_data_length_complete");
-
read_request = find_read_le_data_length_request(adapter);
if (!read_request)
return;
@@ -3792,7 +3791,6 @@ static void le_read_data_length_complete(
DBUS_TYPE_UINT16, &max_tx_time,
DBUS_TYPE_UINT16, &max_rx_octects,
DBUS_TYPE_UINT16, &max_rx_time,
- DBUS_TYPE_INT32, &err,
DBUS_TYPE_INVALID);
if (!reply) {
@@ -3893,23 +3891,20 @@ static void le_read_suggested_default_data_length_return_param_complete(
struct btd_adapter *adapter = user_data;
const struct mgmt_rp_le_read_host_suggested_data_length *rp = param;
uint16_t def_tx_octects, def_tx_time;
- int32_t err;
if (status != MGMT_STATUS_SUCCESS) {
error("Read host suggested def le data length values failed: %s (0x%02x)",
mgmt_errstr(status), status);
def_tx_octects = 0;
def_tx_time =0;
- err = -EIO;
+ return;
}
if (length < sizeof(*rp)) {
- DBG("Too small le read host data length response");
- err = -EIO;
+ goto done;
} else {
def_tx_octects = rp->def_tx_octets;
def_tx_time =rp->def_tx_time;
- err = 0;
DBG("retrieving host suggested data length values %d %d", def_tx_octects, def_tx_time);
}
@@ -3922,7 +3917,7 @@ static void le_read_suggested_default_data_length_return_param_complete(
adapter->def_read_handler->read_callback(adapter,
def_tx_octects, def_tx_time,
- err, adapter->def_read_handler->user_data);
+ adapter->def_read_handler->user_data);
done:
if (adapter->def_read_handler)
g_free(adapter->def_read_handler->user_data);
@@ -3945,35 +3940,21 @@ int btd_adapter_le_read_suggested_default_data_length(
return -EIO;
}
-static struct le_data_length_read_request *find_read_le_host_data_length_request(
- struct btd_adapter *adapter)
-{
- GSList *match;
-
- match = g_slist_find_custom(read_host_requests, adapter, read_request_cmp);
-
- if (match)
- return match->data;
-
- return NULL;
-}
-
static void le_read_host_suggested_default_length_complete(
struct btd_adapter *adapter,
uint16_t def_tx_octects, uint16_t def_tx_time,
- int32_t err, void *user_data)
+ void *user_data)
{
DBusMessage *reply;
struct le_data_length_read_request *read_request;
- read_request = find_read_le_host_data_length_request(adapter);
+ read_request = find_read_le_data_length_request(adapter);
if (!read_request)
return;
reply = g_dbus_create_reply(read_request->msg,
DBUS_TYPE_UINT16, &def_tx_octects,
DBUS_TYPE_UINT16, &def_tx_time,
- DBUS_TYPE_INT32, &err,
DBUS_TYPE_INVALID);
if (!reply) {
@@ -3982,7 +3963,7 @@ static void le_read_host_suggested_default_length_complete(
return;
}
- read_host_requests = g_slist_remove(read_host_requests, read_request);
+ read_requests = g_slist_remove(read_requests, read_request);
dbus_message_unref(read_request->msg);
g_free(read_request);
@@ -3998,7 +3979,7 @@ static DBusMessage *le_read_host_suggested_default_data_length(
struct le_data_length_read_request *read_request;
struct le_data_length_read_default_data_length_handler *handler;
- if (find_read_le_host_data_length_request(adapter))
+ if (find_read_le_data_length_request(adapter))
return btd_error_in_progress(msg);
if (btd_adapter_le_read_suggested_default_data_length(adapter))
@@ -4009,7 +3990,7 @@ static DBusMessage *le_read_host_suggested_default_data_length(
read_request->msg = dbus_message_ref(msg);
read_request->adapter = adapter;
- read_host_requests = g_slist_append(read_host_requests, read_request);
+ read_requests = g_slist_append(read_requests, read_request);
handler = g_new0(struct le_data_length_read_default_data_length_handler, 1);
@@ -4389,11 +4370,13 @@ static void property_set_mode(struct btd_adapter *adapter, uint32_t setting,
data->adapter = adapter;
data->id = id;
-#ifdef __TIZEN_PATCH__
+/* Because of timing issue, sometimes pscan / iscan value was wrong. */
+#if 0
+/* #ifdef __TIZEN_PATCH__ */
/*
- * Use mgmt_reply to avoid dbus timeout in a state of bonding.
+ * Use mgmt_send_nowait to avoid dbus timeout in a state of bonding.
*/
- if (mgmt_reply(adapter->mgmt, opcode, adapter->dev_id, len, param,
+ if (mgmt_send_nowait(adapter->mgmt, opcode, adapter->dev_id, len, param,
property_set_mode_complete, data, g_free) > 0)
#else
if (mgmt_send(adapter->mgmt, opcode, adapter->dev_id, len, param,
@@ -4429,14 +4412,6 @@ static void property_set_powered(const GDBusPropertyTable *property,
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);
}
@@ -4693,7 +4668,7 @@ static gboolean property_get_supported_le_features(
return TRUE;
}
-#ifdef IPSP_SUPPORT
+
static gboolean property_get_ipsp_init_state(
const GDBusPropertyTable *property,
DBusMessageIter *iter, void *data)
@@ -4713,7 +4688,6 @@ static gboolean property_get_ipsp_init_state(
return TRUE;
}
#endif
-#endif
static gboolean property_get_uuids(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *user_data)
@@ -4877,7 +4851,24 @@ static DBusMessage *find_device(DBusConnection *conn, DBusMessage *msg,
return reply;
}
-#ifdef IPSP_SUPPORT
+static gboolean adapter_ipsp_connected(struct btd_adapter *adapter)
+{
+ GSList *l, *next;
+
+ DBG("%s", adapter->path);
+
+ for (l = adapter->connections; l != NULL; l = next) {
+ struct btd_device *dev = l->data;
+
+ next = g_slist_next(l);
+
+ if (device_is_ipsp_connected(dev))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
static void adapter_set_ipsp_init_state(struct btd_adapter *adapter, gboolean initialized)
{
if (adapter->ipsp_intialized == initialized)
@@ -4971,12 +4962,6 @@ static DBusMessage *adapter_initialize_ipsp(DBusConnection *conn,
if (adapter->ipsp_intialized)
return btd_error_already_exists(msg);
- /* Register IPSP service as GATT primary service */
- err = gatt_register_internet_protocol_service(adapter);
-
- if (!err)
- return btd_error_failed(msg, "Failed to register IPSP service");
-
/* Enable BT 6lowpan in kernel */
err = initialize_6lowpan(adapter);
@@ -5000,11 +4985,8 @@ static DBusMessage *adapter_deinitialize_ipsp(DBusConnection *conn,
if (!adapter->ipsp_intialized)
return btd_error_not_permitted(msg, "IPSP not initialized");
- /* Un-register IPSP service as GATT primary service */
- err = gatt_unregister_internet_protocol_service(adapter);
-
- if (!err)
- return btd_error_failed(msg, "Failed to un-register IPSP service");
+ if (adapter_ipsp_connected(adapter))
+ return btd_error_not_permitted(msg, "IPSP Client device found connected");
/* Disable BT 6lowpan in kernel */
err = deinitialize_6lowpan(adapter);
@@ -5015,7 +4997,6 @@ static DBusMessage *adapter_deinitialize_ipsp(DBusConnection *conn,
return dbus_message_new_method_return(msg);
}
#endif
-#endif
static DBusMessage *remove_device(DBusConnection *conn,
DBusMessage *msg, void *user_data)
@@ -5038,7 +5019,7 @@ static DBusMessage *remove_device(DBusConnection *conn,
device = list->data;
- btd_device_set_temporary(device, TRUE);
+ btd_device_set_temporary(device, true);
if (!btd_device_is_connected(device)) {
btd_adapter_remove_device(adapter, device);
@@ -5113,14 +5094,12 @@ static const GDBusMethodTable adapter_methods[] = {
{ GDBUS_ASYNC_METHOD("scan_filter_enable",
GDBUS_ARGS({ "client_if", "i" }, { "enable", "b" }), NULL,
adapter_le_scan_filter_enable) },
-#ifdef IPSP_SUPPORT
- { GDBUS_ASYNC_METHOD("InitializeIpsp",
+ { GDBUS_METHOD("InitializeIpsp",
NULL, NULL,
adapter_initialize_ipsp) },
- { GDBUS_ASYNC_METHOD("DeinitializeIpsp",
+ { GDBUS_METHOD("DeinitializeIpsp",
NULL, NULL,
adapter_deinitialize_ipsp) },
-#endif
{ GDBUS_METHOD("SetScanRespData",
GDBUS_ARGS({ "value", "ay" },
{ "slot_id", "i" }), NULL,
@@ -5157,6 +5136,7 @@ static const GDBusMethodTable adapter_methods[] = {
GDBUS_ARGS({ "address", "s" }),
GDBUS_ARGS({ "device", "o" }),
find_device) },
+#ifdef __TIZEN_PATCH__
#ifdef __BROADCOM_PATCH__
{ GDBUS_METHOD("SetWbsParameters",
GDBUS_ARGS({ "role", "s" }, { "bt_address", "s" }),
@@ -5166,6 +5146,7 @@ static const GDBusMethodTable adapter_methods[] = {
GDBUS_ARGS({ "role", "s" }, { "bt_address", "s" }),
NULL,
set_nb_parameters) },
+#endif
{ GDBUS_METHOD("SetManufacturerData",
GDBUS_ARGS({ "value", "ay" }), NULL,
adapter_set_manufacturer_data) },
@@ -5179,15 +5160,13 @@ static const GDBusMethodTable adapter_methods[] = {
#ifdef __TIZEN_PATCH__
{ GDBUS_ASYNC_METHOD("LEReadMaximumDataLength", NULL,
GDBUS_ARGS({"maxTxOctets", "q" }, { "maxTxTime", "q" },
- {"maxRxOctets", "q" }, { "maxRxTime", "q" },
- { "read_error", "i" }),
+ {"maxRxOctets", "q" }, { "maxRxTime", "q" }),
le_read_maximum_data_length)},
{ GDBUS_ASYNC_METHOD("LEWriteHostSuggestedDataLength",
GDBUS_ARGS({"def_tx_octets", "q" }, { "def_tx_time", "q" }), NULL,
le_write_host_suggested_default_data_length)},
{ GDBUS_ASYNC_METHOD("LEReadHostSuggestedDataLength", NULL,
- GDBUS_ARGS({"def_tx_octets", "q" }, { "def_tx_time", "q" },
- { "read_error", "i" }),
+ GDBUS_ARGS({"def_tx_octets", "q" }, { "def_tx_time", "q" }),
le_read_host_suggested_default_data_length)},
#endif
{ }
@@ -5245,10 +5224,8 @@ static const GDBusPropertyTable adapter_properties[] = {
property_set_connectable },
{ "Version", "s", property_get_version },
{ "SupportedLEFeatures", "as", property_get_supported_le_features},
-#ifdef IPSP_SUPPORT
{ "IpspInitStateChanged", "b", property_get_ipsp_init_state},
#endif
-#endif
{ }
};
@@ -5647,6 +5624,21 @@ static void load_irks_complete(uint8_t status, uint16_t length,
DBG("IRKs loaded for hci%u", adapter->dev_id);
}
+#ifdef __TIZEN_PATCH__
+static void load_devices_rpa_res_support(struct btd_adapter *adapter)
+{
+ GSList *l;
+
+ DBG("%s", adapter->path);
+
+ for (l = adapter->devices; l; l = l->next) {
+ struct btd_device *device = l->data;
+
+ btd_adapter_set_dev_rpa_res_support(adapter, device);
+ }
+}
+#endif
+
static void load_irks(struct btd_adapter *adapter, GSList *irks)
{
struct mgmt_cp_load_irks *cp;
@@ -5856,7 +5848,7 @@ static void load_devices(struct btd_adapter *adapter)
if (!device)
goto free;
- btd_device_set_temporary(device, FALSE);
+ btd_device_set_temporary(device, false);
adapter->devices = g_slist_append(adapter->devices, device);
/* TODO: register services from pre-loaded list of primaries */
@@ -5891,6 +5883,10 @@ free:
g_slist_free_full(irks, g_free);
load_conn_params(adapter, params);
g_slist_free_full(params, g_free);
+
+#ifdef __TIZEN_PATCH__
+ load_devices_rpa_res_support(adapter);
+#endif
}
int btd_adapter_block_address(struct btd_adapter *adapter,
@@ -6127,6 +6123,13 @@ const char *btd_adapter_get_name(struct btd_adapter *adapter)
return NULL;
}
+#ifdef __TIZEN_PATCH__
+uint8_t btd_adapter_get_rpa_res_support_value(struct btd_adapter *adapter)
+{
+ return adapter->central_rpa_res_support;
+}
+#endif
+
int adapter_connect_list_add(struct btd_adapter *adapter,
struct btd_device *device)
{
@@ -6425,6 +6428,14 @@ void adapter_auto_connect_remove(struct btd_adapter *adapter,
static void adapter_start(struct btd_adapter *adapter)
{
+#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
+
g_dbus_emit_property_changed(dbus_conn, adapter->path,
ADAPTER_INTERFACE, "Powered");
@@ -7499,6 +7510,8 @@ static struct btd_adapter *btd_adapter_new(uint16_t index)
DBG("LE Privacy is enabled.");
else
DBG("LE Privacy is disabled.");
+
+ adapter->central_rpa_res_support = 0x00;
#endif
adapter->auths = g_queue_new();
@@ -7548,6 +7561,9 @@ static void adapter_remove(struct btd_adapter *adapter)
btd_adapter_gatt_server_stop(adapter);
#endif
+ btd_advertising_manager_destroy(adapter->adv_manager);
+ adapter->adv_manager = NULL;
+
g_slist_free(adapter->pin_callbacks);
adapter->pin_callbacks = NULL;
@@ -7763,9 +7779,7 @@ static void update_found_devices(struct btd_adapter *adapter,
}
device_set_last_addr_type(dev, bdaddr_type);
-#ifdef IPSP_SUPPORT
device_set_ipsp_connected(dev, FALSE);
-#endif
#else
if (device_is_temporary(dev) && !adapter->discovery_list) {
eir_data_free(&eir_data);
@@ -7781,18 +7795,22 @@ static void update_found_devices(struct btd_adapter *adapter,
/* Report an unknown name to the kernel even if there is a short name
* known, but still update the name with the known short name. */
+ name_known = device_name_known(dev);
+
+ if (eir_data.name && (eir_data.name_complete || !name_known))
+ btd_device_device_set_name(dev, eir_data.name);
+
#ifdef __TIZEN_PATCH__
+ /* As this logic, (eir_data.name_complete || !name_known) is always true.
+ So some abnormal UI bug occurs. Such like complete name display and
+ next time short name display.
+ */
if (eir_data.name_complete)
name_known = device_name_known(dev);
else
name_known = false;
-#else
- name_known = device_name_known(dev);
#endif
- if (eir_data.name && (eir_data.name_complete || !name_known))
- btd_device_device_set_name(dev, eir_data.name);
-
if (eir_data.class != 0)
device_set_class(dev, eir_data.class);
@@ -8172,9 +8190,6 @@ static gboolean process_auth_queue(gpointer user_data)
if (auth->svc_id > 0)
return FALSE;
-/* The below patch should be removed after we provide the API
- * to control autorization for the specific UUID.
- */
if (device_is_trusted(device) == TRUE) {
auth->cb(NULL, auth->user_data);
goto next;
@@ -8929,9 +8944,8 @@ static void bt_6lowpan_conn_state_change_callback(uint16_t index, uint16_t lengt
connected = TRUE;
else
connected = FALSE;
-#ifdef IPSP_SUPPORT
+
device_set_ipsp_connected(device, connected);
-#endif
}
static void bt_le_data_length_changed_callback(uint16_t index, uint16_t length,
@@ -8961,7 +8975,6 @@ static void bt_le_data_length_changed_callback(uint16_t index, uint16_t length,
device_le_data_length_changed(device, ev->max_tx_octets, ev->max_tx_time,
ev->max_rx_octets, ev->max_rx_time);
}
-
#endif
struct btd_adapter_pin_cb_iter *btd_adapter_pin_cb_iter_new(
@@ -9488,9 +9501,6 @@ static void new_link_key_callback(uint16_t index, uint16_t length,
key->pin_len);
device_set_bonded(device, BDADDR_BREDR);
-
- if (device_is_temporary(device))
- btd_device_set_temporary(device, FALSE);
}
bonding_complete(adapter, &addr->bdaddr, addr->type, 0);
@@ -9606,9 +9616,6 @@ static void new_long_term_key_callback(uint16_t index, uint16_t length,
key->type, key->enc_size, ediv, rand);
device_set_bonded(device, addr->type);
-
- if (device_is_temporary(device))
- btd_device_set_temporary(device, FALSE);
}
bonding_complete(adapter, &addr->bdaddr, addr->type, 0);
@@ -9709,8 +9716,7 @@ static void new_csrk_callback(uint16_t index, uint16_t length,
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);
+ btd_device_set_temporary(device, false);
}
static void store_irk(struct btd_adapter *adapter, const bdaddr_t *peer,
@@ -9797,8 +9803,11 @@ static void new_irk_callback(uint16_t index, uint16_t length,
store_irk(adapter, &addr->bdaddr, addr->type, irk->val);
- if (device_is_temporary(device))
- btd_device_set_temporary(device, FALSE);
+ btd_device_set_temporary(device, false);
+
+#ifdef __TIZEN_PATCH__
+ btd_adapter_set_dev_rpa_res_support(adapter, device);
+#endif
}
static void store_conn_param(struct btd_adapter *adapter, const bdaddr_t *peer,
@@ -9977,8 +9986,8 @@ static void read_local_oob_data_complete(uint8_t status, uint16_t length,
return;
#endif
} else {
- hash = rp->hash;
- randomizer = rp->randomizer;
+ hash = rp->hash192;
+ randomizer = rp->rand192;
}
if (!adapter->oob_handler || !adapter->oob_handler->read_local_cb)
@@ -10077,7 +10086,6 @@ static int set_did(struct btd_adapter *adapter, uint16_t vendor,
return -EIO;
}
-#ifndef __TIZEN_PATCH__
static void services_modified(struct gatt_db_attribute *attrib, void *user_data)
{
struct btd_adapter *adapter = user_data;
@@ -10085,14 +10093,11 @@ static void services_modified(struct gatt_db_attribute *attrib, void *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;
@@ -10127,7 +10132,6 @@ static int adapter_register(struct btd_adapter *adapter)
agent_unref(agent);
}
-#ifndef __TIZEN_PATCH__
adapter->database = btd_gatt_database_new(adapter);
if (!adapter->database) {
error("Failed to create GATT database for adapter");
@@ -10139,10 +10143,23 @@ static int adapter_register(struct btd_adapter *adapter)
adapter->db_id = gatt_db_register(db, services_modified,
services_modified,
adapter, NULL);
-#else
+
+/* Disable Old GATT Server */
+#if 0
btd_adapter_gatt_server_start(adapter);
#endif
+ /* Don't start advertising managers on non-LE controllers. */
+ if (adapter->supported_settings & MGMT_SETTING_LE) {
+ adapter->adv_manager = btd_advertising_manager_new(adapter);
+
+ /* LEAdvertisingManager1 is experimental so optional */
+ if (!adapter->adv_manager)
+ error("Failed to register LEAdvertisingManager1 "
+ "interface for adapter");
+ } else {
+ info("Not starting LEAdvertisingManager, LE not supported");
+ }
load_config(adapter);
fix_storage(adapter);
load_drivers(adapter);
@@ -10498,7 +10515,6 @@ static bool set_privacy(struct btd_adapter *adapter, bool privacy)
return false;
}
-#ifdef IPSP_SUPPORT
int btd_adapter_connect_ipsp(struct btd_adapter *adapter,
const bdaddr_t *bdaddr,
uint8_t bdaddr_type)
@@ -10536,7 +10552,39 @@ int btd_adapter_disconnect_ipsp(struct btd_adapter *adapter,
return -EIO;
}
-#endif
+
+static void set_dev_rpa_res_support_complete(uint8_t status,
+ uint16_t length, const void *param,
+ void *user_data)
+{
+ if (status != MGMT_STATUS_SUCCESS)
+ error("Failed to set RPA resolution support of device : %s (0x%02x)",
+ mgmt_errstr(status), status);
+ else
+ DBG("Set RPA resolution support successful");
+}
+
+int btd_adapter_set_dev_rpa_res_support(struct btd_adapter *adapter,
+ struct btd_device *device)
+
+{
+ struct mgmt_cp_set_dev_rpa_res_support cp;
+
+ DBG("btd_adapter_set_dev_rpa_res_support called");
+
+ memset(&cp, 0, sizeof(cp));
+
+ bacpy(&cp.addr.bdaddr, device_get_address(device));
+ cp.addr.type = btd_device_get_bdaddr_type(device);
+ cp.res_support = device_get_rpa_res_char_value(device);
+
+ if (mgmt_send(adapter->mgmt, MGMT_OP_SET_DEV_RPA_RES_SUPPORT,
+ adapter->dev_id, sizeof(cp), &cp,
+ set_dev_rpa_res_support_complete, NULL, NULL) > 0)
+ return 0;
+
+ return -EIO;
+}
#endif
static void clear_devices_complete(uint8_t status, uint16_t length,
@@ -10658,9 +10706,17 @@ static void read_info_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);
+ if (main_opts.fast_conn &&
+ (missing_settings & MGMT_SETTING_FAST_CONNECTABLE))
+ set_mode(adapter, MGMT_OP_SET_FAST_CONNECTABLE, 0x01);
+
+#ifdef __TIZEN_PATCH__
+ /* Set the RPA resolution value to '1' if privacy is supported */
+ if (adapter->le_privacy_enabled &&
+ adapter->supported_settings & MGMT_SETTING_PRIVACY)
+ adapter->central_rpa_res_support = 0x01;
#endif
err = adapter_register(adapter);
diff --git a/src/adapter.h b/src/adapter.h
index afa649f7..aed1c0b3 100755
--- a/src/adapter.h
+++ b/src/adapter.h
@@ -267,21 +267,21 @@ void btd_adapter_set_le_auto_connect(struct btd_adapter *adapter, gboolean auto_
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);
-#ifdef IPSP_SUPPORT
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
+
+int btd_adapter_set_dev_rpa_res_support(struct btd_adapter *adapter,
+ struct btd_device *device);
typedef void (*read_max_data_length_cb_t) (struct btd_adapter *adapter,
- uint16_t max_txOctects,
- uint16_t max_txTime,
- uint16_t max_rxOctects,
- uint16_t max_rxTime,
- int32_t read_error,
+ const uint16_t max_txOctects,
+ const uint16_t max_txTime,
+ const uint16_t max_rxOctects,
+ const uint16_t max_rxTime,
void *user_data);
struct le_data_length_read_handler {
@@ -290,19 +290,12 @@ struct le_data_length_read_handler {
};
typedef void (*read_host_suggested_default_data_length_cb_t) (struct btd_adapter *adapter,
- uint16_t def_txOctects,
- uint16_t def_txTime,
- int32_t read_error,
+ const uint16_t def_txOctects,
+ const uint16_t def_txTime,
void *user_data);
struct le_data_length_read_default_data_length_handler {
read_host_suggested_default_data_length_cb_t read_callback;
void *user_data;
};
-
-int btd_adapter_le_set_data_length(struct btd_adapter *adapter, bdaddr_t *bdaddr,
- uint16_t max_tx_octets, uint16_t max_tx_time);
-void device_le_data_length_changed(struct btd_device *device, uint16_t max_tx_octets,
- uint16_t max_tx_time, uint16_t max_rx_octets, uint16_t max_rx_time);
-
#endif
diff --git a/src/advertising.c b/src/advertising.c
new file mode 100644
index 00000000..04492f7a
--- /dev/null
+++ b/src/advertising.c
@@ -0,0 +1,399 @@
+/*
+ *
+ * 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.
+ *
+ */
+
+#include "advertising.h"
+
+#include <stdint.h>
+#include <stdbool.h>
+
+#include <dbus/dbus.h>
+#include <gdbus/gdbus.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+
+#include "adapter.h"
+#include "dbus-common.h"
+#include "error.h"
+#include "log.h"
+#include "src/shared/queue.h"
+#include "src/shared/util.h"
+
+#define LE_ADVERTISING_MGR_IFACE "org.bluez.LEAdvertisingManager1"
+#define LE_ADVERTISEMENT_IFACE "org.bluez.LEAdvertisement1"
+
+struct btd_advertising {
+ struct btd_adapter *adapter;
+ struct queue *ads;
+};
+
+#define AD_TYPE_BROADCAST 0
+#define AD_TYPE_PERIPHERAL 1
+
+struct advertisement {
+ struct btd_advertising *manager;
+ char *owner;
+ char *path;
+ GDBusClient *client;
+ GDBusProxy *proxy;
+ DBusMessage *reg;
+ uint8_t type; /* Advertising type */
+};
+
+static bool match_advertisement_path(const void *a, const void *b)
+{
+ const struct advertisement *ad = a;
+ const char *path = b;
+
+ return g_strcmp0(ad->path, path);
+}
+
+static void advertisement_free(void *data)
+{
+ struct advertisement *ad = data;
+
+ if (ad->client) {
+ g_dbus_client_set_disconnect_watch(ad->client, NULL, NULL);
+ g_dbus_client_unref(ad->client);
+ }
+
+ if (ad->proxy)
+ g_dbus_proxy_unref(ad->proxy);
+
+ if (ad->owner)
+ g_free(ad->owner);
+
+ if (ad->path)
+ g_free(ad->path);
+
+ free(ad);
+}
+
+static gboolean advertisement_free_idle_cb(void *data)
+{
+ advertisement_free(data);
+
+ return FALSE;
+}
+
+static void advertisement_release(void *data)
+{
+ struct advertisement *ad = data;
+ DBusMessage *message;
+
+ DBG("Releasing advertisement %s, %s", ad->owner, ad->path);
+
+ message = dbus_message_new_method_call(ad->owner, ad->path,
+ LE_ADVERTISEMENT_IFACE,
+ "Release");
+
+ if (!message) {
+ error("Couldn't allocate D-Bus message");
+ return;
+ }
+
+ g_dbus_send_message(btd_get_dbus_connection(), message);
+}
+
+static void advertisement_destroy(void *data)
+{
+ advertisement_release(data);
+ advertisement_free(data);
+}
+
+static void advertisement_remove(void *data)
+{
+ struct advertisement *ad = data;
+
+ g_dbus_client_set_disconnect_watch(ad->client, NULL, NULL);
+
+ /* TODO: mgmt API call to remove advert */
+
+ queue_remove(ad->manager->ads, ad);
+
+ g_idle_add(advertisement_free_idle_cb, ad);
+}
+
+static void client_disconnect_cb(DBusConnection *conn, void *user_data)
+{
+ DBG("Client disconnected");
+
+ advertisement_remove(user_data);
+}
+
+static bool parse_advertising_type(GDBusProxy *proxy, uint8_t *type)
+{
+ DBusMessageIter iter;
+ const char *msg_type;
+
+ if (!g_dbus_proxy_get_property(proxy, "Type", &iter))
+ return false;
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+ return false;
+
+ dbus_message_iter_get_basic(&iter, &msg_type);
+
+ if (!g_strcmp0(msg_type, "broadcast")) {
+ *type = AD_TYPE_BROADCAST;
+ return true;
+ }
+
+ if (!g_strcmp0(msg_type, "peripheral")) {
+ *type = AD_TYPE_PERIPHERAL;
+ return true;
+ }
+
+ return false;
+}
+
+static void refresh_advertisement(struct advertisement *ad)
+{
+ DBG("Refreshing advertisement: %s", ad->path);
+}
+
+static bool parse_advertisement(struct advertisement *ad)
+{
+ if (!parse_advertising_type(ad->proxy, &ad->type)) {
+ error("Failed to read \"Type\" property of advertisement");
+ return false;
+ }
+
+ /* TODO: parse the remaining properties into a shared structure */
+
+ refresh_advertisement(ad);
+
+ return true;
+}
+
+static void advertisement_proxy_added(GDBusProxy *proxy, void *data)
+{
+ struct advertisement *ad = data;
+ DBusMessage *reply;
+
+ if (!parse_advertisement(ad)) {
+ error("Failed to parse advertisement");
+
+ reply = btd_error_failed(ad->reg,
+ "Failed to register advertisement");
+ goto done;
+ }
+
+ g_dbus_client_set_disconnect_watch(ad->client, client_disconnect_cb,
+ ad);
+
+ reply = dbus_message_new_method_return(ad->reg);
+
+ DBG("Advertisement registered: %s", ad->path);
+
+done:
+ g_dbus_send_message(btd_get_dbus_connection(), reply);
+
+ dbus_message_unref(ad->reg);
+ ad->reg = NULL;
+}
+
+static struct advertisement *advertisement_create(DBusConnection *conn,
+ DBusMessage *msg, const char *path)
+{
+ struct advertisement *ad;
+ const char *sender = dbus_message_get_sender(msg);
+
+ if (!path || !g_str_has_prefix(path, "/"))
+ return NULL;
+
+ ad = new0(struct advertisement, 1);
+ if (!ad)
+ return NULL;
+
+ ad->client = g_dbus_client_new_full(conn, sender, path, path);
+ if (!ad->client)
+ goto fail;
+
+ ad->owner = g_strdup(sender);
+ if (!ad->owner)
+ goto fail;
+
+ ad->path = g_strdup(path);
+ if (!ad->path)
+ goto fail;
+
+ DBG("Adding proxy for %s", path);
+ ad->proxy = g_dbus_proxy_new(ad->client, path, LE_ADVERTISEMENT_IFACE);
+ if (!ad->proxy)
+ goto fail;
+
+ g_dbus_client_set_proxy_handlers(ad->client, advertisement_proxy_added,
+ NULL, NULL, ad);
+
+ ad->reg = dbus_message_ref(msg);
+
+ return ad;
+
+fail:
+ advertisement_free(ad);
+ return NULL;
+}
+
+static DBusMessage *register_advertisement(DBusConnection *conn,
+ DBusMessage *msg,
+ void *user_data)
+{
+ struct btd_advertising *manager = user_data;
+ DBusMessageIter args;
+ const char *path;
+ struct advertisement *ad;
+
+ DBG("RegisterAdvertisement");
+
+ 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);
+
+ if (queue_find(manager->ads, match_advertisement_path, path))
+ return btd_error_already_exists(msg);
+
+ /* TODO: support more than one advertisement */
+ if (!queue_isempty(manager->ads))
+ return btd_error_failed(msg, "Already advertising");
+
+ dbus_message_iter_next(&args);
+
+ if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_ARRAY)
+ return btd_error_invalid_args(msg);
+
+ ad = advertisement_create(conn, msg, path);
+ if (!ad)
+ return btd_error_failed(msg,
+ "Failed to register advertisement");
+
+ DBG("Registered advertisement at path %s", path);
+
+ ad->manager = manager;
+ queue_push_tail(manager->ads, ad);
+
+ return NULL;
+}
+
+static DBusMessage *unregister_advertisement(DBusConnection *conn,
+ DBusMessage *msg,
+ void *user_data)
+{
+ struct btd_advertising *manager = user_data;
+ DBusMessageIter args;
+ const char *path;
+ struct advertisement *ad;
+
+ DBG("UnregisterAdvertisement");
+
+ 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);
+
+ ad = queue_find(manager->ads, match_advertisement_path, path);
+ if (!ad)
+ return btd_error_does_not_exist(msg);
+
+ advertisement_remove(ad);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable methods[] = {
+ { GDBUS_EXPERIMENTAL_ASYNC_METHOD("RegisterAdvertisement",
+ GDBUS_ARGS({ "advertisement", "o" },
+ { "options", "a{sv}" }),
+ NULL, register_advertisement) },
+ { GDBUS_EXPERIMENTAL_ASYNC_METHOD("UnregisterAdvertisement",
+ GDBUS_ARGS({ "service", "o" }),
+ NULL,
+ unregister_advertisement) },
+ { }
+};
+
+static void advertising_manager_destroy(void *user_data)
+{
+ struct btd_advertising *manager = user_data;
+
+ queue_destroy(manager->ads, advertisement_destroy);
+
+ free(manager);
+}
+
+static struct btd_advertising *
+advertising_manager_create(struct btd_adapter *adapter)
+{
+ struct btd_advertising *manager;
+
+ manager = new0(struct btd_advertising, 1);
+ if (!manager)
+ return NULL;
+
+ manager->adapter = adapter;
+
+ if (!g_dbus_register_interface(btd_get_dbus_connection(),
+ adapter_get_path(adapter),
+ LE_ADVERTISING_MGR_IFACE,
+ methods, NULL, NULL, manager,
+ advertising_manager_destroy)) {
+ error("Failed to register " LE_ADVERTISING_MGR_IFACE);
+ free(manager);
+ return NULL;
+ }
+
+ manager->ads = queue_new();
+
+ return manager;
+}
+
+struct btd_advertising *
+btd_advertising_manager_new(struct btd_adapter *adapter)
+{
+ struct btd_advertising *manager;
+
+ if (!adapter)
+ return NULL;
+
+ manager = advertising_manager_create(adapter);
+ if (!manager)
+ return NULL;
+
+ DBG("LE Advertising Manager created for adapter: %s",
+ adapter_get_path(adapter));
+
+ return manager;
+}
+
+void btd_advertising_manager_destroy(struct btd_advertising *manager)
+{
+ if (!manager)
+ return;
+
+ g_dbus_unregister_interface(btd_get_dbus_connection(),
+ adapter_get_path(manager->adapter),
+ LE_ADVERTISING_MGR_IFACE);
+}
diff --git a/src/advertising.h b/src/advertising.h
new file mode 100644
index 00000000..a4b99ff8
--- /dev/null
+++ b/src/advertising.h
@@ -0,0 +1,25 @@
+/*
+ *
+ * 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_adapter;
+struct btd_advertising;
+
+struct btd_advertising *btd_advertising_manager_new(
+ struct btd_adapter *adapter);
+void btd_advertising_manager_destroy(struct btd_advertising *manager);
diff --git a/src/attrib-server.c b/src/attrib-server.c
index 7602ac46..d334a36b 100644
--- a/src/attrib-server.c
+++ b/src/attrib-server.c
@@ -264,7 +264,7 @@ static int attribute_cmp(gconstpointer a1, gconstpointer a2)
}
#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;
@@ -743,7 +743,7 @@ static uint16_t find_info(struct gatt_channel *channel, uint16_t start,
put_le16(a->handle, value);
/* Attribute Value */
- put_uuid_le(&a->uuid, &value[2]);
+ bt_uuid_to_le(&a->uuid, &value[2]);
}
length = enc_find_info_resp(format, adl, pdu, len);
@@ -798,7 +798,6 @@ static uint16_t find_by_type(struct gatt_channel *channel, uint16_t start,
if (bt_uuid_cmp(&a->uuid, &prim_uuid) == 0 ||
bt_uuid_cmp(&a->uuid, &snd_uuid) == 0)
range = NULL;
-
else
range->end = a->handle;
}
@@ -1423,7 +1422,6 @@ static gboolean register_core_services(struct gatt_server *server)
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) {
diff --git a/src/device.c b/src/device.c
index cf479ede..7188ecde 100755
--- a/src/device.c
+++ b/src/device.c
@@ -310,9 +310,8 @@ struct btd_device {
uint8_t last_bdaddr_type;
gboolean le_auto_connect;
guint auto_id;
-#ifdef IPSP_SUPPORT
gboolean ipsp_connected; /* IPSP Connection state */
-#endif
+ uint8_t rpa_res_support; /* RPA Resolution capability of device */
uint16_t max_tx_octets;
uint16_t max_tx_time;
uint16_t max_rx_octets;
@@ -506,6 +505,15 @@ static gboolean store_device_info_cb(gpointer user_data)
g_key_file_remove_key(key_file, "General", "Appearance", NULL);
}
+#ifdef __TIZEN_PATCH__
+ if (device->rpa_res_support) {
+ g_key_file_set_integer(key_file, "General", "RPAResSupport",
+ device->rpa_res_support);
+ } else {
+ g_key_file_remove_key(key_file, "General", "RPAResSupport", NULL);
+ }
+#endif
+
update_technologies(key_file, device);
g_key_file_set_boolean(key_file, "General", "Trusted",
@@ -673,7 +681,7 @@ static void gatt_client_cleanup(struct btd_device *device)
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)
@@ -682,7 +690,7 @@ static void gatt_server_cleanup(struct btd_device *device)
bt_gatt_server_unref(device->server);
device->server = NULL;
}
-#endif
+
static void attio_cleanup(struct btd_device *device)
{
#ifdef __TIZEN_PATCH__
@@ -703,9 +711,7 @@ static void attio_cleanup(struct btd_device *device)
}
gatt_client_cleanup(device);
-#ifndef __TIZEN_PATCH__
gatt_server_cleanup(device);
-#endif
if (device->att) {
bt_att_unref(device->att);
@@ -1233,7 +1239,7 @@ static gboolean dev_property_get_last_addr_type(const GDBusPropertyTable *proper
return TRUE;
}
-#ifdef IPSP_SUPPORT
+
static gboolean dev_property_get_ipsp_conn_state(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *data)
{
@@ -1251,7 +1257,6 @@ static gboolean dev_property_get_ipsp_conn_state(const GDBusPropertyTable *prope
return TRUE;
}
#endif
-#endif
static gboolean dev_property_get_connected(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *data)
@@ -1459,7 +1464,7 @@ int device_block(struct btd_device *device, gboolean update_only)
store_device_info(device);
- btd_device_set_temporary(device, FALSE);
+ btd_device_set_temporary(device, false);
g_dbus_emit_property_changed(dbus_conn, device->path,
DEVICE_INTERFACE, "Blocked");
@@ -1658,7 +1663,7 @@ static void device_profile_connected(struct btd_device *dev,
DBG("%s %s (%d)", profile->name, strerror(-err), -err);
if (!err)
- btd_device_set_temporary(dev, FALSE);
+ btd_device_set_temporary(dev, false);
if (dev->pending == NULL)
return;
@@ -1901,7 +1906,7 @@ static DBusMessage *connect_profiles(struct btd_device *dev, uint8_t bdaddr_type
if (!btd_adapter_get_powered(dev->adapter))
return btd_error_not_ready(msg);
- btd_device_set_temporary(dev, FALSE);
+ btd_device_set_temporary(dev, false);
if (!state->svc_resolved)
goto resolve_services;
@@ -1994,7 +1999,7 @@ static DBusMessage *dev_connect(DBusConnection *conn, DBusMessage *msg,
if (dev->le_state.connected)
return dbus_message_new_method_return(msg);
- btd_device_set_temporary(dev, FALSE);
+ btd_device_set_temporary(dev, false);
if (dev->disable_auto_connect) {
dev->disable_auto_connect = FALSE;
@@ -2372,7 +2377,7 @@ static DBusMessage *pair_device(DBusConnection *conn, DBusMessage *msg,
#endif
int err;
- btd_device_set_temporary(device, FALSE);
+ btd_device_set_temporary(device, false);
#ifdef __TIZEN_PATCH__
if (dbus_message_get_args(msg, NULL, DBUS_TYPE_BYTE, &conn_type,
@@ -2458,8 +2463,7 @@ static DBusMessage *pair_device(DBusConnection *conn, DBusMessage *msg,
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);
+ bdaddr_type, io_cap);
else
err = adapter_create_bonding(adapter, &device->bdaddr,
BDADDR_BREDR, io_cap);
@@ -2508,6 +2512,10 @@ static DBusMessage *new_authentication_return(DBusMessage *msg, uint8_t status)
return dbus_message_new_error(msg,
ERROR_INTERFACE ".AuthenticationCanceled",
"Authentication Canceled");
+ case MGMT_STATUS_ALREADY_PAIRED:
+ return dbus_message_new_error(msg,
+ ERROR_INTERFACE ".AlreadyExists",
+ "Already Paired");
default:
return dbus_message_new_error(msg,
ERROR_INTERFACE ".AuthenticationFailed",
@@ -2842,10 +2850,8 @@ void device_set_gatt_connected(struct btd_device *device, gboolean connected)
return;
}
- if (device->gatt_connected == connected)
- return;
-
- device->gatt_connected = connected;
+ if (device->gatt_connected != connected)
+ device->gatt_connected = connected;
DBG("GattConnected %d", connected);
@@ -2880,9 +2886,12 @@ static DBusMessage *connect_le(DBusConnection *conn, DBusMessage *msg,
if (device->bdaddr_type == BDADDR_BREDR) {
if(device->le)
device->bdaddr_type = BDADDR_LE_PUBLIC;
- else
+ else {
device = btd_adapter_get_device(device->adapter,
&device->bdaddr, BDADDR_LE_PUBLIC);
+ if (device == NULL)
+ return btd_error_no_such_adapter(msg);
+ }
}
if (device->gatt_connected)
@@ -2922,7 +2931,8 @@ static DBusMessage *connect_le(DBusConnection *conn, DBusMessage *msg,
if (device->auto_id == 0)
device->auto_id = g_timeout_add(200, att_connect, device);
- return dbus_message_new_method_return(msg);
+ device->connect = dbus_message_ref(msg);
+ return NULL;
}
static DBusMessage *disconnect_le(DBusConnection *conn, DBusMessage *msg,
@@ -2950,6 +2960,18 @@ static DBusMessage *disconnect_le(DBusConnection *conn, DBusMessage *msg,
if (!device->le_state.connected)
return btd_error_not_connected(msg);
+ if (device->connect) {
+ DBusMessage *reply = btd_error_failed(device->connect,
+ "Cancelled");
+ g_dbus_send_message(dbus_conn, reply);
+ dbus_message_unref(device->connect);
+ device->connect = NULL;
+ }
+
+ if (device->le_state.connected)
+ device->disconnects = g_slist_append(device->disconnects,
+ dbus_message_ref(msg));
+
disconnect_all(device);
/*
@@ -2960,10 +2982,9 @@ static DBusMessage *disconnect_le(DBusConnection *conn, DBusMessage *msg,
if(device->bredr)
device->bdaddr_type = BDADDR_BREDR;
- return dbus_message_new_method_return(msg);
+ return NULL;
}
-#ifdef IPSP_SUPPORT
static DBusMessage *connect_ipsp(DBusConnection *conn, DBusMessage *msg,
void *user_data)
{
@@ -2974,9 +2995,12 @@ static DBusMessage *connect_ipsp(DBusConnection *conn, DBusMessage *msg,
if (device->bdaddr_type == BDADDR_BREDR) {
if(device->le)
device->bdaddr_type = BDADDR_LE_PUBLIC;
- else
+ else {
device = btd_adapter_get_device(device->adapter,
&device->bdaddr, BDADDR_LE_PUBLIC);
+ if (device == NULL)
+ return btd_error_no_such_adapter(msg);
+ }
}
if (device->ipsp_connected)
@@ -3012,15 +3036,15 @@ static DBusMessage *disconnect_ipsp(DBusConnection *conn, DBusMessage *msg,
return dbus_message_new_method_return(msg);;
}
-#endif
-#ifdef __TIZEN_PATCH__
static DBusMessage *le_set_data_length(
DBusConnection *conn, DBusMessage *msg,
void *user_data)
{
dbus_uint16_t max_tx_octets;
dbus_uint16_t max_tx_time;
+ const gchar *address;
+ bdaddr_t bdaddr;
struct btd_device *device = user_data;
int status;
char addr[BT_ADDRESS_STRING_SIZE];
@@ -3051,7 +3075,6 @@ static DBusMessage *le_set_data_length(
else
return dbus_message_new_method_return(msg);
}
-#endif
static DBusMessage *is_connected_profile(DBusConnection *conn, DBusMessage *msg,
void *user_data)
@@ -3166,10 +3189,10 @@ static const GDBusMethodTable device_methods[] = {
{ GDBUS_METHOD("ReadPayloadTimeout", NULL,
NULL, read_auth_payload_timeout)},
#endif
- { GDBUS_METHOD("ConnectLE",
+ { GDBUS_ASYNC_METHOD("ConnectLE",
GDBUS_ARGS({ "auto_connect", "b"}),
NULL, connect_le) },
- { GDBUS_METHOD("DisconnectLE", NULL, NULL, disconnect_le) },
+ { GDBUS_ASYNC_METHOD("DisconnectLE", NULL, NULL, disconnect_le) },
{ GDBUS_METHOD("IsConnectedProfile", GDBUS_ARGS({ "UUID", "s" }),
GDBUS_ARGS({ "IsConnected", "b" }), is_connected_profile)},
{ GDBUS_METHOD("LeConnUpdate",
@@ -3181,10 +3204,8 @@ static const GDBusMethodTable device_methods[] = {
GDBUS_ARGS({ "pattern", "s" }), NULL,
discover_services) },
{ GDBUS_METHOD("CancelDiscovery", NULL, NULL, cancel_discover) },
-#ifdef IPSP_SUPPORT
{ GDBUS_ASYNC_METHOD("ConnectIpsp", NULL, NULL, connect_ipsp) },
{ GDBUS_ASYNC_METHOD("DisconnectIpsp", NULL, NULL, disconnect_ipsp) },
-#endif
{ GDBUS_ASYNC_METHOD("LESetDataLength",
GDBUS_ARGS({"max_tx_octets", "q" },
{ "max_tx_time", "q" }), NULL,
@@ -3231,10 +3252,8 @@ static const GDBusPropertyTable device_properties[] = {
{ "GattConnected", "b", dev_property_get_gatt_connected },
{ "PayloadTimeout", "q", dev_property_get_payload},
{ "LastAddrType", "y", dev_property_get_last_addr_type},
-#ifdef IPSP_SUPPORT
{ "IpspConnected", "b", dev_property_get_ipsp_conn_state },
#endif
-#endif
{ }
};
@@ -3503,6 +3522,10 @@ static void load_info(struct btd_device *device, const char *local,
}
#ifdef __TIZEN_PATCH__
+ /* Load RPA Resolution Support value */
+ device->rpa_res_support = g_key_file_get_integer(key_file,
+ "General", "RPAResSupport", NULL);
+
str = g_key_file_get_string(key_file, "General", "ManufacturerDataLen", NULL);
if (str) {
device->manufacturer_data_len = strtol(str, NULL, 10);
@@ -4077,7 +4100,7 @@ static struct btd_device *device_new(struct btd_adapter *adapter,
}
device->adapter = adapter;
- device->temporary = TRUE;
+ device->temporary = true;
gatt_db_register(device->db, gatt_service_added, gatt_service_removed,
device, NULL);
@@ -4489,7 +4512,6 @@ void device_remove(struct btd_device *device, gboolean remove_stored)
if (device->browse)
browse_request_cancel(device->browse);
-
while (device->services != NULL) {
struct btd_service *service = device->services->data;
@@ -5136,6 +5158,9 @@ static void att_disconnected_cb(int err, void *user_data)
adapter_connect_list_add(device->adapter, device);
done:
+#ifdef __TIZEN_PATCH__
+ device_set_gatt_connected(device, FALSE);
+#endif
attio_cleanup(device);
}
@@ -5153,7 +5178,7 @@ static void register_gatt_services(struct btd_device *device)
*/
gatt_db_foreach_service(device->db, NULL, add_primary, &services);
- btd_device_set_temporary(device, FALSE);
+ btd_device_set_temporary(device, false);
if (req)
update_gatt_uuids(req, device->primaries, services);
@@ -5205,17 +5230,10 @@ static void gatt_client_ready_cb(bool success, uint8_t att_ecode,
return;
}
- device->att_mtu = bt_att_get_mtu(device->att);
- g_attrib_set_mtu(device->attrib, device->att_mtu);
-
- DBG("MTU: %u", device->att_mtu);
-
register_gatt_services(device);
device_accept_gatt_profiles(device);
- g_slist_foreach(device->attios, attio_connected, device->attrib);
-
btd_gatt_client_ready(device->client_dbus);
}
@@ -5226,6 +5244,11 @@ static void gatt_client_service_changed(uint16_t start_handle,
DBG("start 0x%04x, end: 0x%04x", start_handle, end_handle);
}
+static void gatt_debug(const char *str, void *user_data)
+{
+ DBG("%s", str);
+}
+
static void gatt_client_init(struct btd_device *device)
{
gatt_client_cleanup(device);
@@ -5237,12 +5260,17 @@ static void gatt_client_init(struct btd_device *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");
}
+#else
+ bt_gatt_client_set_debug(device->client, gatt_debug, NULL, NULL);
#endif
+ /* Notify attio so it can react to notifications */
+ g_slist_foreach(device->attios, attio_connected, device->attrib);
if (!bt_gatt_client_set_ready_handler(device->client,
gatt_client_ready_cb,
@@ -5261,7 +5289,6 @@ static void gatt_client_init(struct btd_device *device)
}
}
-#ifndef __TIZEN_PATCH__
static void gatt_server_init(struct btd_device *device, struct gatt_db *db)
{
if (!db) {
@@ -5274,8 +5301,9 @@ static void gatt_server_init(struct btd_device *device, struct gatt_db *db)
device->server = bt_gatt_server_new(db, device->att, device->att_mtu);
if (!device->server)
error("Failed to initialize bt_gatt_server");
+
+ bt_gatt_server_set_debug(device->server, gatt_debug, NULL, NULL);
}
-#endif
static bool local_counter(uint32_t *sign_cnt, void *user_data)
{
@@ -5312,9 +5340,7 @@ bool device_attach_att(struct btd_device *dev, GIOChannel *io)
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,
@@ -5349,7 +5375,7 @@ bool device_attach_att(struct btd_device *dev, GIOChannel *io)
return false;
}
-#ifdef __TIZEN_PATCH__
+#if 0
dev->attachid = attrib_channel_attach(attrib);
if (dev->attachid == 0) {
g_attrib_unref(attrib);
@@ -5375,14 +5401,10 @@ bool device_attach_att(struct btd_device *dev, GIOChannel *io)
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
@@ -5775,7 +5797,11 @@ void device_set_last_addr_type(struct btd_device *device, uint8_t type)
device->last_bdaddr_type = type;
}
-#ifdef IPSP_SUPPORT
+gboolean device_is_ipsp_connected(struct btd_device * device)
+{
+ return device->ipsp_connected;
+}
+
void device_set_ipsp_connected(struct btd_device *device, gboolean connected)
{
if (device == NULL) {
@@ -5793,8 +5819,6 @@ void device_set_ipsp_connected(struct btd_device *device, gboolean connected)
g_dbus_emit_property_changed(dbus_conn, device->path,
DEVICE_INTERFACE, "IpspConnected");
}
-#endif
-
void device_le_data_length_changed(struct btd_device *device, uint16_t max_tx_octets,
uint16_t max_tx_time, uint16_t max_rx_octets, uint16_t max_rx_time)
{
@@ -5864,7 +5888,7 @@ gboolean device_is_temporary(struct btd_device *device)
return device->temporary;
}
-void btd_device_set_temporary(struct btd_device *device, gboolean temporary)
+void btd_device_set_temporary(struct btd_device *device, bool temporary)
{
if (!device)
return;
@@ -5874,17 +5898,19 @@ void btd_device_set_temporary(struct btd_device *device, gboolean temporary)
DBG("temporary %d", temporary);
+ device->temporary = temporary;
+
if (temporary) {
if (device->bredr)
adapter_whitelist_remove(device->adapter, device);
adapter_connect_list_remove(device->adapter, device);
- } else {
- if (device->bredr)
- adapter_whitelist_add(device->adapter, device);
- store_device_info(device);
+ return;
}
- device->temporary = temporary;
+ if (device->bredr)
+ adapter_whitelist_add(device->adapter, device);
+
+ store_device_info(device);
}
void btd_device_set_trusted(struct btd_device *device, gboolean trusted)
@@ -5916,6 +5942,8 @@ void device_set_bonded(struct btd_device *device, uint8_t bdaddr_type)
device->bredr_state.bonded = true;
else
device->le_state.bonded = true;
+
+ btd_device_set_temporary(device, false);
}
void device_set_legacy(struct btd_device *device, bool legacy)
@@ -6039,7 +6067,7 @@ void device_set_unpaired(struct btd_device *dev, uint8_t bdaddr_type)
g_dbus_emit_property_changed(dbus_conn, dev->path,
DEVICE_INTERFACE, "Paired");
- btd_device_set_temporary(dev, TRUE);
+ btd_device_set_temporary(dev, true);
if (btd_device_is_connected(dev))
device_request_disconnect(dev, NULL);
@@ -6096,8 +6124,18 @@ void device_bonding_complete(struct btd_device *device, uint8_t bdaddr_type,
device_auth_req_free(device);
/* If we're already paired nothing more is needed */
- if (state->paired)
+ if (state->paired) {
+#ifdef __TIZEN_PATCH__
+#ifdef TIZEN_WEARABLE
+ DBG("Already paired. Send Paired Signal for Wearble syspopup termn");
+ DBG("state->svc_resolved [%d]", state->svc_resolved);
+ if (state->svc_resolved)
+ g_dbus_emit_property_changed(dbus_conn, device->path,
+ DEVICE_INTERFACE, "Paired");
+#endif /* TIZEN_WEARABLE */
+#endif /* __TIZEN_PATCH__ */
return;
+ }
device_set_paired(device, bdaddr_type);
@@ -6350,9 +6388,9 @@ static void confirm_cb(struct agent *agent, DBusError *err, void *data)
err ? FALSE : TRUE);
else
#endif
- btd_adapter_confirm_reply(device->adapter, &device->bdaddr,
- device->bdaddr_type,
- err ? FALSE : TRUE);
+ btd_adapter_confirm_reply(device->adapter, &device->bdaddr,
+ device->bdaddr_type,
+ err ? FALSE : TRUE);
agent_unref(device->authr->agent);
device->authr->agent = NULL;
@@ -6781,6 +6819,23 @@ void device_set_appearance(struct btd_device *device, uint16_t value)
}
#ifdef __TIZEN_PATCH__
+int device_get_rpa_res_char_value(struct btd_device *device)
+{
+ return device->rpa_res_support;
+}
+
+/* Store the RPA Resolution Characteristic Value of remote device.
+ * This value would be checked before start directed advertising using RPA.
+ */
+void device_set_rpa_res_char_value(struct btd_device *device, uint8_t value)
+{
+ if (device->rpa_res_support == value)
+ return;
+
+ device->rpa_res_support = value;
+ store_device_info(device);
+}
+
void device_set_manufacturer_info(struct btd_device *device, struct eir_data *eir)
{
if (!device)
diff --git a/src/device.h b/src/device.h
index c8e6b093..92624d07 100644
--- a/src/device.h
+++ b/src/device.h
@@ -107,7 +107,7 @@ bool device_is_bonded(struct btd_device *device, uint8_t bdaddr_type);
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);
-void btd_device_set_temporary(struct btd_device *device, gboolean temporary);
+void btd_device_set_temporary(struct btd_device *device, bool 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_legacy(struct btd_device *device, bool legacy);
@@ -156,9 +156,10 @@ void device_set_adv_report_info(struct btd_device *device, void *data,
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);
-#ifdef IPSP_SUPPORT
+gboolean device_is_ipsp_connected(struct btd_device * device);
void device_set_ipsp_connected(struct btd_device *device, gboolean connected);
-#endif
+int device_get_rpa_res_char_value(struct btd_device *device);
+void device_set_rpa_res_char_value(struct btd_device *device, uint8_t value);
#endif
struct btd_device *btd_device_ref(struct btd_device *device);
diff --git a/src/eir.c b/src/eir.c
index cec119eb..1fe7ff7d 100644
--- a/src/eir.c
+++ b/src/eir.c
@@ -294,7 +294,6 @@ void eir_parse(struct eir_data *eir, const uint8_t *eir_data, uint8_t eir_len)
eir->manufacturer_data_len = data_len;
#endif
eir_parse_msd(eir, data, data_len);
-
break;
}
diff --git a/src/gatt-client.c b/src/gatt-client.c
index ab8081cc..08e50220 100644
--- a/src/gatt-client.c
+++ b/src/gatt-client.c
@@ -61,6 +61,9 @@ struct btd_gatt_client {
struct queue *services;
struct queue *all_notify_clients;
+#ifdef __TIZEN_PATCH__
+ guint wait_charcs_id;
+#endif
};
struct service {
@@ -381,20 +384,19 @@ static void desc_read_cb(bool success, uint8_t att_ecode,
struct async_dbus_op *op = user_data;
struct descriptor *desc = op->data;
struct service *service = desc->chrc->service;
+ DBusMessage *reply;
- if (!success) {
- DBusMessage *reply = create_gatt_dbus_error(op->msg, att_ecode);
-
- desc->read_id = 0;
- g_dbus_send_message(btd_get_dbus_connection(), reply);
- return;
- }
+ if (!success)
+ goto fail;
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 (!gatt_db_attribute_write(desc->attr, op->offset, value, length, 0,
+ NULL, write_descriptor_cb, desc)) {
+ error("Failed to store attribute");
+ goto fail;
+ }
/*
* If the value length is exactly MTU-1, then we may not have read the
@@ -414,10 +416,21 @@ static void desc_read_cb(bool success, uint8_t att_ecode,
return;
}
+ /* Read the stored data from db */
+ if (!gatt_db_attribute_read(desc->attr, 0, 0, NULL, read_op_cb, op)) {
+ error("Failed to read database");
+ goto fail;
+ }
+
desc->read_id = 0;
- /* Read the stored data from db */
- gatt_db_attribute_read(desc->attr, 0, 0, NULL, read_op_cb, op);
+ return;
+
+fail:
+ reply = create_gatt_dbus_error(op->msg, att_ecode);
+ desc->read_id = 0;
+ g_dbus_send_message(btd_get_dbus_connection(), reply);
+ return;
}
static DBusMessage *descriptor_read_value(DBusConnection *conn,
@@ -845,20 +858,19 @@ static void chrc_read_cb(bool success, uint8_t att_ecode, const uint8_t *value,
struct async_dbus_op *op = user_data;
struct characteristic *chrc = op->data;
struct service *service = chrc->service;
+ DBusMessage *reply;
- if (!success) {
- DBusMessage *reply = create_gatt_dbus_error(op->msg, att_ecode);
-
- chrc->read_id = 0;
- g_dbus_send_message(btd_get_dbus_connection(), reply);
- return ;
- }
+ if (!success)
+ goto fail;
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 (!gatt_db_attribute_write(chrc->attr, op->offset, value, length, 0,
+ NULL, write_characteristic_cb, chrc)) {
+ error("Failed to store attribute");
+ goto fail;
+ }
/*
* If the value length is exactly MTU-1, then we may not have read the
@@ -881,7 +893,17 @@ static void chrc_read_cb(bool success, uint8_t att_ecode, const uint8_t *value,
chrc->read_id = 0;
/* Read the stored data from db */
- gatt_db_attribute_read(chrc->attr, 0, 0, NULL, read_op_cb, op);
+ if (!gatt_db_attribute_read(chrc->attr, 0, 0, NULL, read_op_cb, op)) {
+ error("Failed to read database");
+ goto fail;
+ }
+
+ return;
+
+fail:
+ reply = create_gatt_dbus_error(op->msg, att_ecode);
+ chrc->read_id = 0;
+ g_dbus_send_message(btd_get_dbus_connection(), reply);
}
static DBusMessage *characteristic_read_value(DBusConnection *conn,
@@ -1062,7 +1084,18 @@ static DBusMessage *characteristic_write_value_by_type(DBusConnection *conn,
supported = true;
chrc->write_id = bt_gatt_client_write_without_response(gatt,
chrc->value_handle,
- chrc->props & BT_GATT_CHRC_PROP_AUTH,
+ false,
+ value, value_len);
+ if (chrc->write_id) {
+ chrc->write_id = 0;
+ return dbus_message_new_method_return(msg);
+ }
+ } else if ((write_type & chrc->props) == BT_GATT_CHRC_PROP_AUTH) {
+ DBG("BT_GATT_CHRC_PROP_AUTH");
+ supported = true;
+ chrc->write_id = bt_gatt_client_write_without_response(gatt,
+ chrc->value_handle,
+ true,
value, value_len);
if (chrc->write_id) {
chrc->write_id = 0;
@@ -1190,6 +1223,65 @@ static bool match_notify_sender(const void *a, const void *b)
return strcmp(client->owner, sender) == 0;
}
+#ifdef GATT_NO_RELAY
+struct char_value {
+ uint8_t *data;
+ uint8_t len;
+ char *chrc_path;
+};
+
+static void emit_value_changed_signal_to_dest(gpointer data, gpointer user_data)
+{
+ dbus_int32_t result = 0;
+ struct notify_client *notify_client = data;
+ struct char_value *value = user_data;
+
+ g_dbus_emit_signal_to_dest(btd_get_dbus_connection(),
+ notify_client->owner, value->chrc_path,
+ GATT_CHARACTERISTIC_IFACE, "GattValueChanged",
+ DBUS_TYPE_INT32, &result,
+ DBUS_TYPE_STRING, &value->chrc_path,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &value->data, value->len,
+ DBUS_TYPE_INVALID);
+}
+#endif
+
+#ifdef __TIZEN_PATCH__
+void gatt_characteristic_value_changed(void *data, uint8_t data_len, void *user_data)
+{
+ struct characteristic *chrc = user_data;
+ char *chrc_path = strdup(chrc->path);
+#ifdef GATT_NO_RELAY
+ struct char_value *value;
+
+ value = new0(struct char_value, 1);
+ value->len = data_len;
+ value->data = data;
+ value->chrc_path = chrc_path;
+
+ queue_foreach(chrc->notify_clients,
+ emit_value_changed_signal_to_dest, value);
+
+
+ if (value)
+ g_free(value);
+#else
+ dbus_int32_t result = 0;
+
+ g_dbus_emit_signal(btd_get_dbus_connection(), chrc->path,
+ GATT_CHARACTERISTIC_IFACE, "GattValueChanged",
+ DBUS_TYPE_INT32, &result,
+ DBUS_TYPE_STRING, &chrc_path,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &data, data_len,
+ DBUS_TYPE_INVALID);
+#endif
+
+ if (chrc_path)
+ free(chrc_path);
+
+}
+#endif
+
static void notify_cb(uint16_t value_handle, const uint8_t *value,
uint16_t length, void *user_data)
{
@@ -1206,6 +1298,8 @@ static void notify_cb(uint16_t value_handle, const uint8_t *value,
#ifdef __TIZEN_PATCH__
gatt_db_attribute_write(chrc->attr, 0, value, length, 0, NULL,
notify_characteristic_cb, chrc);
+
+ gatt_characteristic_value_changed(value, length, chrc);
#else
gatt_db_attribute_write(chrc->attr, 0, value, length, 0, NULL,
write_characteristic_cb, chrc);
@@ -1448,6 +1542,15 @@ static const GDBusMethodTable characteristic_methods[] = {
#endif
};
+#ifdef __TIZEN_PATCH__
+static const GDBusSignalTable characteristic_signals[] = {
+ { GDBUS_SIGNAL("GattValueChanged",
+ GDBUS_ARGS({ "Result", "i"},
+ { "Characteristic Path","s"},
+ { "GattData", "ay"})) },
+};
+#endif
+
static void characteristic_free(void *data)
{
struct characteristic *chrc = data;
@@ -1486,11 +1589,29 @@ static struct characteristic *characteristic_create(
chrc->service = service;
+#ifndef __TIZEN_PATCH__
gatt_db_attribute_get_char_data(attr, &chrc->handle,
&chrc->value_handle,
&chrc->props, &uuid);
+#else
+ if (!gatt_db_attribute_get_char_data(attr, &chrc->handle,
+ &chrc->value_handle,
+ &chrc->props, &uuid)) {
+ queue_destroy(chrc->descs, NULL);
+ queue_destroy(chrc->notify_clients, NULL);
+ free(chrc);
+ return NULL;
+ }
+#endif
+
chrc->attr = gatt_db_get_attribute(service->client->db,
chrc->value_handle);
+ if (!chrc->attr) {
+ error("Attribute 0x%04x not found", chrc->value_handle);
+ characteristic_free(chrc);
+ return NULL;
+ }
+
bt_uuid_to_uuid128(&uuid, &chrc->uuid);
chrc->path = g_strdup_printf("%s/char%04x", service->path,
@@ -1498,7 +1619,11 @@ static struct characteristic *characteristic_create(
if (!g_dbus_register_interface(btd_get_dbus_connection(), chrc->path,
GATT_CHARACTERISTIC_IFACE,
+#ifdef __TIZEN_PATCH__
+ characteristic_methods, characteristic_signals,
+#else
characteristic_methods, NULL,
+#endif
characteristic_properties,
chrc, characteristic_free)) {
error("Unable to register GATT characteristic with handle "
@@ -1933,6 +2058,10 @@ void btd_gatt_client_destroy(struct btd_gatt_client *client)
if (!client)
return;
+#ifdef __TIZEN_PATCH__
+ if (client->wait_charcs_id)
+ g_source_remove(client->wait_charcs_id);
+#endif
queue_destroy(client->services, unregister_service);
queue_destroy(client->all_notify_clients, NULL);
bt_gatt_client_unref(client->gatt);
@@ -1972,6 +2101,50 @@ fail:
notify_client_free(notify_client);
}
+#ifdef __TIZEN_PATCH__
+static void check_chrcs_ready(void *data, void *user_data)
+{
+ gboolean *chrcs_ready = user_data;
+ struct service *service = data;
+
+ /*
+ * Return FALSE if charcteristics are not ready or if there is
+ * any pending request to read char. extended properties exist.
+ */
+ if (!service->chrcs_ready ||
+ !queue_isempty(service->pending_ext_props))
+ *chrcs_ready = FALSE;
+}
+
+static gboolean check_all_chrcs_ready(gpointer user_data)
+{
+ struct btd_gatt_client *client = user_data;
+ gboolean all_chrcs_ready = TRUE;
+ static int count = 0;
+
+ queue_foreach(client->services, check_chrcs_ready, &all_chrcs_ready);
+
+ /*
+ * By adding below condition, forcing to call check_chrcs_ready()
+ * function to check whether all char./extended properties are ready.
+ * Above function would be called max. for 20 times (Assuming more
+ * no of services). Emit signal only when all characteristics are read.
+ */
+ if (all_chrcs_ready == FALSE && count < 20) {
+ count++;
+ return TRUE;
+ }
+
+ device_set_gatt_connected(client->device, TRUE);
+
+ client->wait_charcs_id = 0;
+
+ count = 0;
+
+ return FALSE;
+}
+#endif
+
void btd_gatt_client_ready(struct btd_gatt_client *client)
{
struct bt_gatt_client *gatt;
@@ -1995,6 +2168,20 @@ void btd_gatt_client_ready(struct btd_gatt_client *client)
if (queue_isempty(client->services)) {
DBG("Exporting services");
create_services(client);
+#ifdef __TIZEN_PATCH__
+ /*
+ * In case of more number of services and services having
+ * characteristics extended properties; GattConnected signal
+ * should be emitted only after all the characteristics are ready.
+ * This piece of code checks all the characteristics periodically and
+ * emit the signal if characteristics are ready.
+ */
+ if (client->wait_charcs_id > 0)
+ g_source_remove(client->wait_charcs_id);
+
+ client->wait_charcs_id = g_timeout_add(100,
+ check_all_chrcs_ready, client);
+#endif
return;
}
diff --git a/src/gatt-database.c b/src/gatt-database.c
index 95b5fc6b..8beb8c3f 100644
--- a/src/gatt-database.c
+++ b/src/gatt-database.c
@@ -23,6 +23,7 @@
#include <stdint.h>
#include <stdlib.h>
+#include <errno.h>
#include "lib/bluetooth.h"
#include "lib/sdp.h"
@@ -41,6 +42,7 @@
#include "device.h"
#include "gatt-database.h"
#include "dbus-common.h"
+#include "profile.h"
#ifndef ATT_CID
#define ATT_CID 4
@@ -75,6 +77,7 @@ struct btd_gatt_database {
struct gatt_db_attribute *svc_chngd;
struct gatt_db_attribute *svc_chngd_ccc;
struct queue *services;
+ struct queue *profiles;
};
struct external_service {
@@ -91,6 +94,14 @@ struct external_service {
struct queue *descs;
};
+struct external_profile {
+ struct btd_gatt_database *database;
+ char *owner;
+ char *path; /* Path to GattProfile1 */
+ unsigned int id;
+ struct queue *profiles; /* btd_profile list */
+};
+
struct external_chrc {
struct external_service *service;
char *path;
@@ -118,7 +129,11 @@ struct pending_op {
unsigned int id;
struct gatt_db_attribute *attrib;
struct queue *owner_queue;
- void *setup_data;
+ struct iovec data;
+#ifdef __TIZEN_PATCH__
+ bdaddr_t bdaddr;
+ uint8_t bdaddr_type;
+#endif
};
struct device_state {
@@ -197,6 +212,31 @@ find_device_state(struct btd_gatt_database *database, bdaddr_t *bdaddr,
return queue_find(database->device_states, dev_state_match, &dev_info);
}
+#ifdef __TIZEN_PATCH__
+static bool dev_addr_match(const void *a, const void *b)
+{
+ const struct device_state *dev_state = a;
+ const struct device_info *dev_info = b;
+
+ if (bacmp(&dev_state->bdaddr, &dev_info->bdaddr) == 0)
+ return TRUE;
+
+ return FALSE;
+}
+
+static struct device_state *
+find_device_state_from_address(struct btd_gatt_database *database, const bdaddr_t *bdaddr)
+{
+ struct device_info dev_info;
+
+ memset(&dev_info, 0, sizeof(dev_info));
+
+ bacpy(&dev_info.bdaddr, bdaddr);
+
+ return queue_find(database->device_states, dev_addr_match, &dev_info);
+}
+#endif
+
static bool ccc_state_match(const void *a, const void *b)
{
const struct ccc_state *ccc = a;
@@ -364,6 +404,52 @@ static void service_free(void *data)
free(service);
}
+static void profile_remove(void *data)
+{
+ struct btd_profile *p = data;
+
+ DBG("Removed \"%s\"", p->name);
+
+ adapter_foreach(adapter_remove_profile, p);
+
+ g_free((void *) p->name);
+ g_free((void *) p->remote_uuid);
+
+ free(p);
+}
+
+static void profile_release(struct external_profile *profile)
+{
+ DBusMessage *msg;
+
+ if (!profile->id)
+ return;
+
+ DBG("Releasing \"%s\"", profile->owner);
+
+ g_dbus_remove_watch(btd_get_dbus_connection(), profile->id);
+
+ msg = dbus_message_new_method_call(profile->owner, profile->path,
+ "org.bluez.GattProfile1",
+ "Release");
+ if (msg)
+ g_dbus_send_message(btd_get_dbus_connection(), msg);
+}
+
+static void profile_free(void *data)
+{
+ struct external_profile *profile = data;
+
+ queue_destroy(profile->profiles, profile_remove);
+
+ profile_release(profile);
+
+ g_free(profile->owner);
+ g_free(profile->path);
+
+ free(profile);
+}
+
static void gatt_database_free(void *data)
{
struct btd_gatt_database *database = data;
@@ -390,6 +476,7 @@ static void gatt_database_free(void *data)
queue_destroy(database->device_states, device_state_free);
queue_destroy(database->services, service_free);
+ queue_destroy(database->profiles, profile_free);
queue_destroy(database->ccc_callbacks, ccc_cb_free);
database->device_states = NULL;
database->ccc_callbacks = NULL;
@@ -494,6 +581,33 @@ done:
gatt_db_attribute_read_result(attrib, id, error, value, len);
}
+#ifdef __TIZEN_PATCH__
+static void gap_rpa_res_support_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 = 1;
+ const uint8_t *value = NULL;
+ uint8_t rpa_res_support = 0x00;
+
+ rpa_res_support = btd_adapter_get_rpa_res_support_value(database->adapter);
+
+ if (offset > 1) {
+ error = BT_ATT_ERROR_INVALID_OFFSET;
+ goto done;
+ }
+
+ len -= offset;
+ value = len ? &rpa_res_support : NULL;
+
+done:
+ gatt_db_attribute_read_result(attrib, id, error, value, len);
+}
+#endif
+
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;
@@ -589,7 +703,13 @@ static void populate_gap_service(struct btd_gatt_database *database)
/* Add the GAP service */
bt_uuid16_create(&uuid, UUID_GAP);
+
+#ifndef __TIZEN_PATCH__
service = gatt_db_add_service(database->db, &uuid, true, 5);
+#else
+ service = gatt_db_add_service(database->db, &uuid, true, 7);
+#endif
+
database->gap_handle = database_add_record(database, UUID_GAP, service,
"Generic Access Profile");
@@ -611,6 +731,15 @@ static void populate_gap_service(struct btd_gatt_database *database)
gap_appearance_read_cb,
NULL, database);
+#ifdef __TIZEN_PATCH__
+ /* Central address resolution characteristic */
+ bt_uuid16_create(&uuid, GATT_CHARAC_CENTRAL_RPA_RESOLUTION);
+ gatt_db_service_add_characteristic(service, &uuid, BT_ATT_PERM_READ,
+ BT_GATT_CHRC_PROP_READ,
+ gap_rpa_res_support_read_cb,
+ NULL, database);
+#endif
+
gatt_db_service_set_active(service, true);
}
@@ -836,11 +965,178 @@ struct notify {
bool indicate;
};
+#ifdef __TIZEN_PATCH__
+struct notify_indicate {
+ struct btd_gatt_database *database;
+ GDBusProxy *proxy;
+ uint16_t handle, ccc_handle;
+ const uint8_t *value;
+ uint16_t len;
+ bool indicate;
+};
+
+struct notify_indicate_cb {
+ GDBusProxy *proxy;
+ struct btd_device *device;
+};
+
+static void indicate_confirm_free(void *data)
+{
+ struct notify_indicate_cb *indicate = data;
+
+ if (indicate)
+ free(indicate);
+}
+
+static void indicate_confirm_setup_cb(DBusMessageIter *iter, void *user_data)
+{
+ struct btd_device *device = user_data;
+ char dstaddr[18] = { 0 };
+ char *addr_value = NULL;
+ gboolean complete = FALSE;
+
+ ba2str(device_get_address(device), dstaddr);
+ addr_value = g_strdup(dstaddr);
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
+ &addr_value);
+
+ complete = TRUE;
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN,
+ &complete);
+}
+
+static void indicate_confirm_reply_cb(DBusMessage *message, void *user_data)
+{
+ DBusError error;
+
+ dbus_error_init(&error);
+
+ if (dbus_set_error_from_message(&error, message) == TRUE) {
+ DBG("Failed to send indication/notification");
+ dbus_error_free(&error);
+ return;
+ }
+}
+#endif
+
static void conf_cb(void *user_data)
{
DBG("GATT server received confirmation");
+#ifdef __TIZEN_PATCH__
+ struct notify_indicate_cb *confirm = user_data;
+
+ if (confirm) {
+ /* Send confirmation to application */
+ if (g_dbus_proxy_method_call(confirm->proxy, "IndicateConfirm",
+ indicate_confirm_setup_cb,
+ indicate_confirm_reply_cb, confirm->device,
+ NULL) == TRUE)
+ return;
+ }
+#endif
+}
+
+#ifdef __TIZEN_PATCH__
+static void send_notification_indication_to_device(void *data, void *user_data)
+{
+ struct device_state *device_state = data;
+ struct notify_indicate *notify_indicate = user_data;
+ struct ccc_state *ccc;
+ struct btd_device *device;
+ struct notify_indicate_cb *confirm;
+
+ ccc = find_ccc_state(device_state, notify_indicate->ccc_handle);
+ if (!ccc)
+ return;
+
+ if (!ccc->value[0] || (notify_indicate->indicate && !(ccc->value[0] & 0x02)))
+ return;
+
+ device = btd_adapter_get_device(notify_indicate->database->adapter,
+ &device_state->bdaddr,
+ device_state->bdaddr_type);
+ if (!device)
+ return;
+
+ confirm = new0(struct notify_indicate_cb, 1);
+ confirm->proxy = notify_indicate->proxy;
+ confirm->device = device;
+ /*
+ * TODO: If the device is not connected but bonded, send the
+ * notification/indication when it becomes connected.
+ */
+ if (!notify_indicate->indicate) {
+ DBG("GATT server sending notification");
+ bt_gatt_server_send_notification(
+ btd_device_get_gatt_server(device),
+ notify_indicate->handle, notify_indicate->value,
+ notify_indicate->len);
+ /* In case of Notification, send response to application
+ * as remote device do not respond for notification */
+ conf_cb(confirm);
+ return;
+ }
+
+ DBG("GATT server sending indication");
+
+ bt_gatt_server_send_indication(btd_device_get_gatt_server(device),
+ notify_indicate->handle,
+ notify_indicate->value,
+ notify_indicate->len, conf_cb,
+ confirm, indicate_confirm_free);
+}
+
+static void send_notification_indication_to_devices(GDBusProxy *proxy,
+ struct btd_gatt_database *database,
+ uint16_t handle, const uint8_t *value,
+ uint16_t len, uint16_t ccc_handle,
+ bool indicate)
+{
+ struct notify_indicate notify_indicate;
+ DBG("");
+ memset(&notify_indicate, 0, sizeof(notify_indicate));
+
+ notify_indicate.database = database;
+ notify_indicate.proxy = proxy;
+ notify_indicate.handle = handle;
+ notify_indicate.ccc_handle = ccc_handle;
+ notify_indicate.value = value;
+ notify_indicate.len = len;
+ notify_indicate.indicate = indicate;
+
+ queue_foreach(database->device_states, send_notification_indication_to_device,
+ &notify_indicate);
}
+static void send_unicast_notification_indication_to_device(GDBusProxy *proxy,
+ struct btd_gatt_database *database,
+ uint16_t handle, const uint8_t *value,
+ uint16_t len, uint16_t ccc_handle,
+ bool indicate, const bdaddr_t *unicast_addr)
+{
+ struct device_state *dev_state;
+ struct notify_indicate notify_indicate;
+ DBG("");
+
+ memset(&notify_indicate, 0, sizeof(notify_indicate));
+
+ notify_indicate.database = database;
+ notify_indicate.proxy = proxy;
+ notify_indicate.handle = handle;
+ notify_indicate.ccc_handle = ccc_handle;
+ notify_indicate.value = value;
+ notify_indicate.len = len;
+ notify_indicate.indicate = indicate;
+
+ /* Find and return a device state. */
+ dev_state = find_device_state_from_address(database, unicast_addr);
+
+ if (dev_state)
+ send_notification_indication_to_device(dev_state, &notify_indicate);
+}
+#endif
+
static void send_notification_to_device(void *data, void *user_data)
{
struct device_state *device_state = data;
@@ -1366,6 +1662,7 @@ static bool parse_uuid(GDBusProxy *proxy, bt_uuid_t *uuid)
static bool parse_primary(GDBusProxy *proxy, bool *primary)
{
DBusMessageIter iter;
+ dbus_bool_t val;
if (!g_dbus_proxy_get_property(proxy, "Primary", &iter))
return false;
@@ -1373,12 +1670,16 @@ static bool parse_primary(GDBusProxy *proxy, bool *primary)
if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BOOLEAN)
return false;
- dbus_message_iter_get_basic(&iter, primary);
+ dbus_message_iter_get_basic(&iter, &val);
+
+ *primary = val;
+
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" */
@@ -1392,9 +1693,6 @@ static uint8_t dbus_error_to_att_ecode(const char *error_name)
if (strcmp(error_name, "org.bluez.Error.InvalidValueLength") == 0)
return BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN;
- if (strcmp(error_name, "org.bluez.Error.InProgress") == 0)
- return BT_ERROR_ALREADY_IN_PROGRESS;
-
return 0;
}
@@ -1462,17 +1760,41 @@ static void pending_op_free(void *data)
free(op);
}
+#ifdef __TIZEN_PATCH__
+static struct pending_op *pending_read_new(struct queue *owner_queue,
+ struct gatt_db_attribute *attrib,
+ struct bt_att *att,
+ unsigned int id)
+#else
static struct pending_op *pending_read_new(struct queue *owner_queue,
struct gatt_db_attribute *attrib,
unsigned int id)
+#endif
{
struct pending_op *op;
+#ifdef __TIZEN_PATCH__
+ bdaddr_t bdaddr;
+ uint8_t bdaddr_type;
+ char address[18];
+#endif
op = new0(struct pending_op, 1);
if (!op)
return NULL;
+#ifdef __TIZEN_PATCH__
+ if (!get_dst_info(att, &bdaddr, &bdaddr_type)) {
+ free(op);
+ return NULL;
+ }
+#endif
+
op->owner_queue = owner_queue;
+#ifdef __TIZEN_PATCH__
+ memcpy(&op->bdaddr, &bdaddr, sizeof(bdaddr_t));
+ op->bdaddr_type = bdaddr_type;
+#endif
+
op->attrib = attrib;
op->id = id;
queue_push_tail(owner_queue, op);
@@ -1480,22 +1802,59 @@ static struct pending_op *pending_read_new(struct queue *owner_queue,
return op;
}
+#ifdef __TIZEN_PATCH__
+static void read_setup_cb(DBusMessageIter *iter, void *user_data)
+{
+ struct pending_op *op = user_data;
+ char dstaddr[18] = { 0 };
+ char *addr_value = NULL;
+ uint16_t offset = 0;
+
+ ba2str(&op->bdaddr, dstaddr);
+ addr_value = g_strdup(dstaddr);
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
+ &addr_value);
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE,
+ &op->id);
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16,
+ &offset);
+}
+#endif
+
+#ifdef __TIZEN_PATCH__
+static void send_read(struct gatt_db_attribute *attrib, struct bt_att *att,
+ GDBusProxy *proxy, struct queue *owner_queue,
+ unsigned int id)
+#else
static void send_read(struct gatt_db_attribute *attrib, GDBusProxy *proxy,
struct queue *owner_queue,
unsigned int id)
+#endif
{
struct pending_op *op;
uint8_t ecode = BT_ATT_ERROR_UNLIKELY;
+#ifdef __TIZEN_PATCH__
+ op = pending_read_new(owner_queue, attrib, att, id);
+#else
op = pending_read_new(owner_queue, attrib, id);
+#endif
if (!op) {
error("Failed to allocate memory for pending read call");
ecode = BT_ATT_ERROR_INSUFFICIENT_RESOURCES;
goto error;
}
+#ifdef __TIZEN_PATCH__
+ if (g_dbus_proxy_method_call(proxy, "ReadValue", read_setup_cb, read_reply_cb,
+ op, pending_op_free) == TRUE)
+#else
if (g_dbus_proxy_method_call(proxy, "ReadValue", NULL, read_reply_cb,
op, pending_op_free) == TRUE)
+#endif
return;
pending_op_free(op);
@@ -1507,12 +1866,30 @@ error:
static void write_setup_cb(DBusMessageIter *iter, void *user_data)
{
struct pending_op *op = user_data;
- struct iovec *iov = op->setup_data;
DBusMessageIter array;
+#ifdef __TIZEN_PATCH__
+ char dstaddr[18] = { 0 };
+ char *addr_value = NULL;
+ uint16_t offset = 0;
+#endif
+
+#ifdef __TIZEN_PATCH__
+ ba2str(&op->bdaddr, dstaddr);
+ addr_value = g_strdup(dstaddr);
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
+ &addr_value);
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE,
+ &op->id);
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16,
+ &offset);
+#endif
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);
+ &op->data.iov_base, op->data.iov_len);
dbus_message_iter_close_container(iter, &array);
}
@@ -1552,40 +1929,71 @@ done:
gatt_db_attribute_write_result(op->attrib, op->id, ecode);
}
+#ifdef __TIZEN_PATCH__
+static struct pending_op *pending_write_new(struct queue *owner_queue,
+ struct gatt_db_attribute *attrib, struct bt_att *att,
+ unsigned int id,
+ const uint8_t *value,
+ size_t len)
+#else
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)
+#endif
{
struct pending_op *op;
- struct iovec iov;
+#ifdef __TIZEN_PATCH__
+ bdaddr_t bdaddr;
+ uint8_t bdaddr_type;
+ char address[18];
+#endif
op = new0(struct pending_op, 1);
if (!op)
return NULL;
+#ifdef __TIZEN_PATCH__
+ if (!get_dst_info(att, &bdaddr, &bdaddr_type)) {
+ free(op);
+ return NULL;
+ }
+#endif
- iov.iov_base = (uint8_t *) value;
- iov.iov_len = len;
+ op->data.iov_base = (uint8_t *) value;
+ op->data.iov_len = len;
+#ifdef __TIZEN_PATCH__
+ memcpy(&op->bdaddr, &bdaddr, sizeof(bdaddr_t));
+ op->bdaddr_type = bdaddr_type;
+#endif
op->owner_queue = owner_queue;
op->attrib = attrib;
op->id = id;
- op->setup_data = &iov;
queue_push_tail(owner_queue, op);
return op;
}
+#ifdef __TIZEN_PATCH__
+static void send_write(struct gatt_db_attribute *attrib, struct bt_att *att,
+ GDBusProxy *proxy, struct queue *owner_queue,
+ unsigned int id, const uint8_t *value, size_t len)
+#else
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)
+#endif
{
struct pending_op *op;
uint8_t ecode = BT_ATT_ERROR_UNLIKELY;
+#ifdef __TIZEN_PATCH__
+ op = pending_write_new(owner_queue, attrib, att, id, value, len);
+#else
op = pending_write_new(owner_queue, attrib, id, value, len);
+#endif
if (!op) {
error("Failed to allocate memory for pending read call");
ecode = BT_ATT_ERROR_INSUFFICIENT_RESOURCES;
@@ -1641,16 +2049,22 @@ static uint8_t ccc_write_cb(uint16_t value, void *user_data)
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_INSUFFICIENT_RESOURCES;
+ 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_ERROR_CCC_IMPROPERLY_CONFIGURED;
+ return BT_ATT_ERROR_REQUEST_NOT_SUPPORTED;
/*
* Always call StartNotify for an incoming enable and ignore the return
@@ -1659,7 +2073,7 @@ static uint8_t ccc_write_cb(uint16_t value, void *user_data)
if (g_dbus_proxy_method_call(chrc->proxy,
"StartNotify", NULL, NULL,
NULL, NULL) == FALSE)
- return BT_ATT_ERROR_UNLIKELY;
+ return BT_ATT_ERROR_REQUEST_NOT_SUPPORTED;
__sync_fetch_and_add(&chrc->ntfy_cnt, 1);
@@ -1673,7 +2087,87 @@ static void property_changed_cb(GDBusProxy *proxy, const char *name,
DBusMessageIter array;
uint8_t *value = NULL;
int len = 0;
+#ifdef __TIZEN_PATCH__
+ bool enable = FALSE;
+ const bdaddr_t *unicast_addr = NULL;
+#endif
+
+#ifdef __TIZEN_PATCH__
+ if (strcmp(name, "Value") == 0) {
+ 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;
+ } else if (strcmp(name, "Notifying") == 0) {
+ gboolean notify_indicate = FALSE;
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_BOOLEAN) {
+ DBG("Malformed \"Notifying\" property received");
+ return;
+ }
+
+ dbus_message_iter_get_basic(iter, &notify_indicate);
+
+ DBG("Set Notification %d", notify_indicate);
+ /* Set notification/indication */
+ set_ccc_notify_indicate(chrc->ccc, notify_indicate);
+ return;
+ } else if (strcmp(name, "Unicast") == 0) {
+ const char *address = NULL;
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) {
+ DBG("Malformed \"Value\" property received");
+ return;
+ }
+
+ dbus_message_iter_get_basic(iter, &address);
+
+ if (address) {
+ /* Set the address for unicast notification/indication */
+ set_ccc_unicast_address(chrc->ccc, address);
+ }
+ return;
+ } else
+ return;
+
+ enable = get_ccc_notify_indicate(chrc->ccc);
+
+ if (enable) {
+
+ unicast_addr = get_ccc_unicast_address(chrc->ccc);
+
+ if (unicast_addr && bacmp(unicast_addr, BDADDR_ANY)) {
+ send_unicast_notification_indication_to_device(proxy,
+ 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,
+ unicast_addr);
+ /* reset the unicast address */
+ set_ccc_unicast_address(chrc->ccc, NULL);
+ } else
+ send_notification_indication_to_devices(proxy,
+ 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);
+
+ set_ccc_notify_indicate(chrc->ccc, FALSE);
+ }
+#else
if (strcmp(name, "Value"))
return;
@@ -1699,6 +2193,7 @@ static void property_changed_cb(GDBusProxy *proxy, const char *name,
value, len,
gatt_db_attribute_get_handle(chrc->ccc),
chrc->props & BT_GATT_CHRC_PROP_INDICATE);
+#endif
}
static bool database_add_ccc(struct external_service *service,
@@ -1779,8 +2274,11 @@ static void desc_read_cb(struct gatt_db_attribute *attrib,
error("Read callback called with incorrect attribute");
return;
}
-
+#ifdef __TIZEN_PATCH__
+ send_read(attrib, att, desc->proxy, desc->pending_reads, id);
+#else
send_read(attrib, desc->proxy, desc->pending_reads, id);
+#endif
}
static void desc_write_cb(struct gatt_db_attribute *attrib,
@@ -1796,7 +2294,11 @@ static void desc_write_cb(struct gatt_db_attribute *attrib,
return;
}
+#ifdef __TIZEN_PATCH__
+ send_write(attrib, att, desc->proxy, desc->pending_writes, id, value, len);
+#else
send_write(attrib, desc->proxy, desc->pending_writes, id, value, len);
+#endif
}
static bool database_add_desc(struct external_service *service,
@@ -1838,7 +2340,11 @@ static void chrc_read_cb(struct gatt_db_attribute *attrib,
return;
}
+#ifdef __TIZEN_PATCH__
+ send_read(attrib, att, chrc->proxy, chrc->pending_reads, id);
+#else
send_read(attrib, chrc->proxy, chrc->pending_reads, id);
+#endif
}
static void chrc_write_cb(struct gatt_db_attribute *attrib,
@@ -1854,9 +2360,32 @@ static void chrc_write_cb(struct gatt_db_attribute *attrib,
return;
}
+#ifdef __TIZEN_PATCH__
+ send_write(attrib, att, chrc->proxy, chrc->pending_writes, id, value, len);
+#else
send_write(attrib, chrc->proxy, chrc->pending_writes, id, value, len);
+#endif
}
+#ifdef __TIZEN_PATCH__
+static bool database_check_ccc_desc(struct external_desc *desc)
+{
+ bt_uuid_t uuid, uuid_ccc;
+ char uuidstr[MAX_LEN_UUID_STR];
+
+ if (!parse_uuid(desc->proxy, &uuid)) {
+ error("Failed to read \"UUID\" property of descriptor");
+ return false;
+ }
+
+ bt_uuid16_create(&uuid_ccc, GATT_CLIENT_CHARAC_CFG_UUID);
+ if (bt_uuid_cmp(&uuid, &uuid_ccc) == 0)
+ return true;
+ else
+ return false;
+}
+#endif
+
static bool database_add_chrc(struct external_service *service,
struct external_chrc *chrc)
{
@@ -1889,8 +2418,13 @@ static bool database_add_chrc(struct external_service *service,
return false;
}
+#ifndef __TIZEN_PATCH__
+ /* Existing implementation adds CCC descriptor by default
+ * if notification and indication properties are set. But as per the requirment
+ * CCCD shall be added by the application */
if (!database_add_ccc(service, chrc))
return false;
+#endif
if (!database_add_cep(service, chrc))
return false;
@@ -1900,15 +2434,34 @@ static bool database_add_chrc(struct external_service *service,
while (entry) {
struct external_desc *desc = entry->data;
- if (desc->handled || g_strcmp0(desc->chrc_path, chrc->path))
+ if (desc->handled || g_strcmp0(desc->chrc_path, chrc->path)) {
+#ifdef __TIZEN_PATCH__
+ entry = entry->next;
+#endif
continue;
-
+ }
+#ifdef __TIZEN_PATCH__
+ /* Check if Application wants to add CCC and use existing
+ * implemenation to add CCC descriptors */
+ if (database_check_ccc_desc(desc)) {
+ if (!database_add_ccc(service, chrc)) {
+ chrc->attrib = NULL;
+ return false;
+ }
+ desc->attrib = chrc->ccc;
+ desc->handled = true;
+ } else if (!database_add_desc(service, desc)) {
+ chrc->attrib = NULL;
+ error("Failed to create descriptor entry");
+ return false;
+ }
+#else
if (!database_add_desc(service, desc)) {
chrc->attrib = NULL;
error("Failed to create descriptor entry");
return false;
}
-
+#endif
entry = entry->next;
}
@@ -2128,14 +2681,226 @@ static DBusMessage *manager_unregister_service(DBusConnection *conn,
return dbus_message_new_method_return(msg);
}
+static void profile_exited(DBusConnection *conn, void *user_data)
+{
+ struct external_profile *profile = user_data;
+
+ DBG("\"%s\" exited", profile->owner);
+
+ profile->id = 0;
+
+ queue_remove(profile->database->profiles, profile);
+
+ profile_free(profile);
+}
+
+static int profile_add(struct external_profile *profile, const char *uuid)
+{
+ struct btd_profile *p;
+
+ p = new0(struct btd_profile, 1);
+ if (!p)
+ goto fail;
+ /* Assign directly to avoid having extra fields */
+ p->name = (const void *) g_strdup_printf("%s%s/%s", profile->owner,
+ profile->path, uuid);
+ if (!p->name)
+ goto fail;
+
+ p->remote_uuid = (const void *) g_strdup(uuid);
+ if (!p->remote_uuid)
+ goto fail;
+
+ p->auto_connect = true;
+
+ queue_push_tail(profile->profiles, p);
+
+ DBG("Added \"%s\"", p->name);
+
+ return 0;
+fail:
+ error("Fail to add profile");
+
+ if (p) {
+ g_free(p->name);
+ g_free(p->remote_uuid);
+ free(p);
+ }
+
+ return -ENOMEM;
+}
+
+static void add_profile(void *data, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+
+ adapter_add_profile(adapter, data);
+}
+
+static int profile_create(DBusConnection *conn,
+ struct btd_gatt_database *database,
+ const char *sender, const char *path,
+ DBusMessageIter *iter)
+{
+ struct external_profile *profile;
+ DBusMessageIter uuids;
+
+ if (!path || !g_str_has_prefix(path, "/"))
+ return -EINVAL;
+
+ profile = new0(struct external_profile, 1);
+ if (!profile)
+ return -ENOMEM;
+
+ profile->owner = g_strdup(sender);
+ if (!profile->owner)
+ goto fail;
+
+ profile->path = g_strdup(path);
+ if (!profile->path)
+ goto fail;
+
+ profile->profiles = queue_new();
+ if (!profile->profiles)
+ goto fail;
+
+ profile->database = database;
+ profile->id = g_dbus_add_disconnect_watch(conn, sender, profile_exited,
+ profile, NULL);
+
+ dbus_message_iter_recurse(iter, &uuids);
+
+ while (dbus_message_iter_get_arg_type(&uuids) == DBUS_TYPE_STRING) {
+ const char *uuid;
+
+ dbus_message_iter_get_basic(&uuids, &uuid);
+
+ if (profile_add(profile, uuid) < 0)
+ goto fail;
+
+ dbus_message_iter_next(&uuids);
+ }
+
+ if (queue_isempty(profile->profiles))
+ goto fail;
+
+ queue_foreach(profile->profiles, add_profile, database->adapter);
+ queue_push_tail(database->profiles, profile);
+
+ return 0;
+
+fail:
+ profile_free(profile);
+ return -EINVAL;
+}
+
+static bool match_profile(const void *a, const void *b)
+{
+ const struct external_profile *profile = a;
+ const struct svc_match_data *data = b;
+
+ return g_strcmp0(profile->path, data->path) == 0 &&
+ g_strcmp0(profile->owner, data->sender) == 0;
+}
+
+static DBusMessage *manager_register_profile(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 svc_match_data match_data;
+
+ DBG("sender %s", sender);
+
+ 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->profiles, match_profile, &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);
+
+ if (profile_create(conn, database, sender, path, &args) < 0)
+ return btd_error_failed(msg, "Failed to register profile");
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *manager_unregister_profile(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_profile *profile;
+ 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;
+
+ profile = queue_remove_if(database->profiles, match_profile,
+ &match_data);
+ if (!profile)
+ return btd_error_does_not_exist(msg);
+
+ profile_free(profile);
+
+ return dbus_message_new_method_return(msg);
+}
+
static const GDBusMethodTable manager_methods[] = {
+#ifdef __TIZEN_PATCH__
+ { GDBUS_ASYNC_METHOD("RegisterService",
+ GDBUS_ARGS({ "service", "o" }, { "options", "a{sv}" }),
+ NULL, manager_register_service) },
+ { GDBUS_ASYNC_METHOD("UnregisterService",
+ GDBUS_ARGS({ "service", "o" }),
+ NULL, manager_unregister_service) },
+ { GDBUS_ASYNC_METHOD("RegisterProfile",
+ GDBUS_ARGS({ "profile", "o" }, { "UUIDs", "as" },
+ { "options", "a{sv}" }), NULL,
+ manager_register_profile) },
+ { GDBUS_ASYNC_METHOD("UnregisterProfile",
+ GDBUS_ARGS({ "profile", "o" }),
+ NULL, manager_unregister_profile) },
+ { }
+#else
{ 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) },
+ { GDBUS_EXPERIMENTAL_ASYNC_METHOD("RegisterProfile",
+ GDBUS_ARGS({ "profile", "o" }, { "UUIDs", "as" },
+ { "options", "a{sv}" }), NULL,
+ manager_register_profile) },
+ { GDBUS_EXPERIMENTAL_ASYNC_METHOD("UnregisterProfile",
+ GDBUS_ARGS({ "profile", "o" }),
+ NULL, manager_unregister_profile) },
{ }
+#endif
};
struct btd_gatt_database *btd_gatt_database_new(struct btd_adapter *adapter)
@@ -2164,6 +2929,10 @@ struct btd_gatt_database *btd_gatt_database_new(struct btd_adapter *adapter)
if (!database->services)
goto fail;
+ database->profiles = queue_new();
+ if (!database->profiles)
+ goto fail;
+
database->ccc_callbacks = queue_new();
if (!database->ccc_callbacks)
goto fail;
diff --git a/src/gatt.c b/src/gatt.c
index 536c0401..f4382166 100644
--- a/src/gatt.c
+++ b/src/gatt.c
@@ -551,6 +551,8 @@ bool btd_gatt_update_attr_db(void)
} else {
new_service_add = TRUE;
}
+ } else if (local_attr->type.value.u16 == GATT_CHARAC_UUID) {
+ continue;
}
/* Fix : RESOURCE_LEAK */
if (temp_att) {
@@ -746,6 +748,7 @@ void btd_gatt_set_notify_indicate_flag(struct btd_attribute *attrib,
attribute_cmp);
}
+#if 0
gboolean gatt_register_internet_protocol_service(struct btd_adapter *adapter)
{
bt_uuid_t uuid;
@@ -776,6 +779,7 @@ gboolean gatt_unregister_internet_protocol_service(struct btd_adapter *adapter)
return TRUE;
}
#endif
+#endif
#ifdef __TIZEN_PATCH__
struct btd_attribute *btd_gatt_add_service(const bt_uuid_t *uuid,
diff --git a/src/hcid.h b/src/hcid.h
index ec421bd7..251a2006 100644
--- a/src/hcid.h
+++ b/src/hcid.h
@@ -38,6 +38,7 @@ struct main_opts {
gboolean reverse_sdp;
gboolean name_resolv;
gboolean debug_keys;
+ gboolean fast_conn;
#ifdef __TIZEN_PATCH__
gboolean le_privacy;
#endif
diff --git a/src/main.c b/src/main.c
index 96cbae83..4adf008f 100644
--- a/src/main.c
+++ b/src/main.c
@@ -335,6 +335,13 @@ static void parse_config(GKeyFile *config)
g_free(str);
}
+
+ boolean = g_key_file_get_boolean(config, "General",
+ "FastConnectable", &err);
+ if (err)
+ g_clear_error(&err);
+ else
+ main_opts.fast_conn = boolean;
#ifdef __TIZEN_PATCH__
boolean = g_key_file_get_boolean(config, "General",
"EnableLEPrivacy", &err);
@@ -618,7 +625,7 @@ int main(int argc, char *argv[])
g_dbus_set_flags(gdbus_flags);
-#ifdef __TIZEN_PATCH__
+#if 0
gatt_init();
#endif
@@ -687,7 +694,7 @@ int main(int argc, char *argv[])
adapter_cleanup();
-#ifdef __TIZEN_PATCH__
+#if 0
gatt_cleanup();
#endif
diff --git a/src/main_m.conf b/src/main_m.conf
index 364a72c4..c748ed7a 100644
--- a/src/main_m.conf
+++ b/src/main_m.conf
@@ -63,6 +63,13 @@ Class = 0x00020C # Smart phone
# Possible values: "off", "single", "multiple"
#MultiProfile = off
+# Permanently enables the Fast Connectable setting for adapters that
+# support it. When enabled other devices can connect faster to us,
+# however the tradeoff is increased power consumptions. This feature
+# will fully work only on kernel version 4.1 and newer. Defaults to
+# 'false'.
+#FastConnectable = false
+
#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.
@@ -76,4 +83,5 @@ EnableLEPrivacy = false
# 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= \ No newline at end of file
+#ReconnectUUIDs=
+
diff --git a/src/main_w.conf b/src/main_w.conf
index 459d6160..27093553 100644
--- a/src/main_w.conf
+++ b/src/main_w.conf
@@ -56,6 +56,13 @@ Class = 0x000704 # Wearable, Wrist Watch
# Possible values: "dual", "bredr", "le"
#ControllerMode = dual
+# Permanently enables the Fast Connectable setting for adapters that
+# support it. When enabled other devices can connect faster to us,
+# however the tradeoff is increased power consumptions. This feature
+# will fully work only on kernel version 4.1 and newer. Defaults to
+# 'false'.
+#FastConnectable = false
+
#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.
diff --git a/src/profile.c b/src/profile.c
index 63ab2760..f174f7e3 100644
--- a/src/profile.c
+++ b/src/profile.c
@@ -549,9 +549,6 @@
<attribute id=\"0x0317\"> \
<uint32 value=\"0x00000003\"/> \
</attribute> \
- <attribute id=\"0x0200\"> \
- <uint16 value=\"%u\" name=\"psm\"/> \
- </attribute> \
</record>"
#define MAS_RECORD \
@@ -601,9 +598,6 @@
<attribute id=\"0x0317\"> \
<uint32 value=\"0x0000007f\"/> \
</attribute> \
- <attribute id=\"0x0200\"> \
- <uint16 value=\"%u\" name=\"psm\"/> \
- </attribute> \
</record>"
#define MNS_RECORD \
@@ -644,12 +638,12 @@
<attribute id=\"0x0100\"> \
<text value=\"%s\"/> \
</attribute> \
- <attribute id=\"0x0317\"> \
- <uint32 value=\"0x0000007f\"/> \
- </attribute> \
<attribute id=\"0x0200\"> \
<uint16 value=\"%u\" name=\"psm\"/> \
</attribute> \
+ <attribute id=\"0x0317\"> \
+ <uint32 value=\"0x0000007f\"/> \
+ </attribute> \
</record>"
#define SYNC_RECORD \
@@ -839,6 +833,7 @@ 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);
@@ -2185,29 +2180,15 @@ static char *get_pce_record(struct ext_profile *ext, struct ext_io *l2cap,
static char *get_pse_record(struct ext_profile *ext, struct ext_io *l2cap,
struct ext_io *rfcomm)
{
- uint16_t psm = 0;
- uint8_t chan = 0;
-
- if (l2cap)
- psm = l2cap->psm;
- if (rfcomm)
- chan = rfcomm->chan;
-
- return g_strdup_printf(PSE_RECORD, chan, ext->version, ext->name, psm);
+ return g_strdup_printf(PSE_RECORD, rfcomm->chan, ext->version,
+ ext->name);
}
static char *get_mas_record(struct ext_profile *ext, struct ext_io *l2cap,
struct ext_io *rfcomm)
{
- uint16_t psm = 0;
- uint8_t chan = 0;
-
- if (l2cap)
- psm = l2cap->psm;
- if (rfcomm)
- chan = rfcomm->chan;
-
- return g_strdup_printf(MAS_RECORD, chan, ext->version, ext->name, psm);
+ return g_strdup_printf(MAS_RECORD, rfcomm->chan, ext->version,
+ ext->name);
}
static char *get_mns_record(struct ext_profile *ext, struct ext_io *l2cap,
@@ -2430,8 +2411,6 @@ static struct default_settings {
.uuid = OBEX_PSE_UUID,
.name = "Phone Book Access",
.channel = PBAP_DEFAULT_CHANNEL,
- .psm = BTD_PROFILE_PSM_AUTO,
- .mode = BT_IO_MODE_ERTM,
.authorize = true,
.get_record = get_pse_record,
.version = 0x0101,
@@ -2446,8 +2425,6 @@ static struct default_settings {
.uuid = OBEX_MAS_UUID,
.name = "Message Access",
.channel = MAS_DEFAULT_CHANNEL,
- .psm = BTD_PROFILE_PSM_AUTO,
- .mode = BT_IO_MODE_ERTM,
.authorize = true,
.get_record = get_mas_record,
.version = 0x0100
diff --git a/src/shared/att-types.h b/src/shared/att-types.h
index 10a42f21..aa7f0da0 100644
--- a/src/shared/att-types.h
+++ b/src/shared/att-types.h
@@ -92,16 +92,6 @@ struct bt_att_pdu_error_rsp {
#define BT_ATT_ERROR_INSUFFICIENT_RESOURCES 0x11
/*
- * Common Profile and Service Error Code descriptions (see Supplement to the
- * Bluetooth Core Specification, sections 1.2 and 2). The error codes within
- * 0xE0-0xFC are reserved for future use. The remaining 3 are defined as the
- * following:
- */
-#define BT_ERROR_CCC_IMPROPERLY_CONFIGURED 0xfd
-#define BT_ERROR_ALREADY_IN_PROGRESS 0xfe
-#define BT_ERROR_OUT_OF_RANGE 0xff
-
-/*
* ATT attribute permission bitfield values. Permissions are grouped as
* "Access", "Encryption", "Authentication", and "Authorization". A bitmask of
* permissions is a byte that encodes a combination of these.
diff --git a/src/shared/att.c b/src/shared/att.c
index d7ee5a08..422cc2c8 100644
--- a/src/shared/att.c
+++ b/src/shared/att.c
@@ -43,6 +43,16 @@
#define ATT_OP_SIGNED_MASK 0x80
#define ATT_TIMEOUT_INTERVAL 30000 /* 30000 ms */
+/*
+ * Common Profile and Service Error Code descriptions (see Supplement to the
+ * Bluetooth Core Specification, sections 1.2 and 2). The error codes within
+ * 0xE0-0xFC are reserved for future use. The remaining 3 are defined as the
+ * following:
+ */
+#define BT_ERROR_CCC_IMPROPERLY_CONFIGURED 0xfd
+#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
@@ -679,13 +689,14 @@ static bool opcode_match(uint8_t opcode, uint8_t test_opcode)
static void respond_not_supported(struct bt_att *att, uint8_t opcode)
{
- struct bt_att_pdu_error_rsp pdu;
+ uint8_t pdu[4];
- pdu.opcode = opcode;
- pdu.handle = 0x0000;
- pdu.ecode = BT_ATT_ERROR_REQUEST_NOT_SUPPORTED;
+ pdu[0] = opcode;
+ pdu[1] = 0;
+ pdu[2] = 0;
+ pdu[3] = BT_ATT_ERROR_REQUEST_NOT_SUPPORTED;
- bt_att_send(att, BT_ATT_OP_ERROR_RSP, &pdu, sizeof(pdu), NULL, NULL,
+ bt_att_send(att, BT_ATT_OP_ERROR_RSP, pdu, sizeof(pdu), NULL, NULL,
NULL);
}
@@ -1415,3 +1426,11 @@ bool bt_att_set_remote_key(struct bt_att *att, uint8_t sign_key[16],
return sign_set_key(&att->remote_sign, sign_key, func, user_data);
}
+
+bool bt_att_has_crypto(struct bt_att *att)
+{
+ if (!att)
+ return false;
+
+ return att->crypto ? true : false;
+}
diff --git a/src/shared/att.h b/src/shared/att.h
index a440aafd..fb6247ec 100644
--- a/src/shared/att.h
+++ b/src/shared/att.h
@@ -90,3 +90,4 @@ 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);
+bool bt_att_has_crypto(struct bt_att *att);
diff --git a/src/shared/btsnoop.c b/src/shared/btsnoop.c
index 3592c2ed..d8be5eac 100644
--- a/src/shared/btsnoop.c
+++ b/src/shared/btsnoop.c
@@ -25,6 +25,9 @@
#include <config.h>
#endif
+#ifdef __TIZEN_PATCH__
+#include <stdio.h>
+#endif
#include <endian.h>
#include <fcntl.h>
#include <unistd.h>
@@ -72,6 +75,11 @@ struct btsnoop {
uint16_t index;
bool aborted;
bool pklg_format;
+#ifdef __TIZEN_PATCH__
+ char *path;
+ int16_t rotate_count;
+ ssize_t file_size;
+#endif
};
struct btsnoop *btsnoop_open(const char *path, unsigned long flags)
@@ -128,7 +136,12 @@ failed:
return NULL;
}
+#ifdef __TIZEN_PATCH__
+struct btsnoop *btsnoop_create(const char *path, uint32_t type,
+ int16_t rotate_count, ssize_t file_size)
+#else
struct btsnoop *btsnoop_create(const char *path, uint32_t type)
+#endif
{
struct btsnoop *btsnoop;
struct btsnoop_hdr hdr;
@@ -159,9 +172,99 @@ struct btsnoop *btsnoop_create(const char *path, uint32_t type)
return NULL;
}
+#ifdef __TIZEN_PATCH__
+ if (rotate_count > 0 && file_size > 0) {
+ btsnoop->path = strdup(path);
+ btsnoop->rotate_count = rotate_count;
+ btsnoop->file_size = file_size;
+ }
+#endif
+
return btsnoop_ref(btsnoop);
}
+#ifdef __TIZEN_PATCH__
+static int btsnoop_create_2(struct btsnoop *btsnoop)
+{
+ struct btsnoop_hdr hdr;
+ ssize_t written;
+
+ if (btsnoop->fd >= 0)
+ close(btsnoop->fd);
+
+ btsnoop->fd = open(btsnoop->path,
+ O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+ if (btsnoop->fd < 0) {
+ btsnoop_unref(btsnoop);
+ return -1;
+ }
+
+ memcpy(hdr.id, btsnoop_id, sizeof(btsnoop_id));
+ hdr.version = htobe32(btsnoop_version);
+ hdr.type = htobe32(btsnoop->type);
+
+ written = write(btsnoop->fd, &hdr, BTSNOOP_HDR_SIZE);
+ if (written < 0) {
+ btsnoop_unref(btsnoop);
+ return -1;
+ }
+
+ return btsnoop->fd;
+}
+
+static void btsnoop_rotate_files(struct btsnoop *btsnoop)
+{
+ char *filename = NULL;
+ char *new_filename = NULL;
+ int i;
+ int postfix_width = 0;
+ int err;
+
+ if (btsnoop->rotate_count <= 1)
+ return;
+
+ for (i = btsnoop->rotate_count / 10; i; i /= 10)
+ postfix_width++;
+
+ for (i = btsnoop->rotate_count - 2; i >= 0; i--) {
+ if (i == 0) {
+ filename = strdup(btsnoop->path);
+ err = (filename == NULL) ? -1 : 0;
+ } else {
+ err = asprintf(&filename, "%s.%0*d",
+ btsnoop->path, postfix_width, i);
+ }
+
+ if (err < 0 || access(filename, F_OK) < 0)
+ goto done;
+
+ err = asprintf(&new_filename, "%s.%0*d",
+ btsnoop->path, postfix_width, i + 1);
+ if (err < 0)
+ goto done;
+
+ err = rename(filename, new_filename);
+
+done:
+ if (new_filename) {
+ free(new_filename);
+ new_filename = NULL;
+ }
+
+ if (filename) {
+ free(filename);
+ filename = NULL;
+ }
+
+ if (err < 0)
+ break;
+ }
+
+ return;
+}
+#endif
+
struct btsnoop *btsnoop_ref(struct btsnoop *btsnoop)
{
if (!btsnoop)
@@ -180,6 +283,11 @@ void btsnoop_unref(struct btsnoop *btsnoop)
if (__sync_sub_and_fetch(&btsnoop->ref_count, 1))
return;
+#ifdef __TIZEN_PATCH__
+ if (btsnoop->path)
+ free(btsnoop->path);
+#endif
+
if (btsnoop->fd >= 0)
close(btsnoop->fd);
@@ -212,6 +320,16 @@ bool btsnoop_write(struct btsnoop *btsnoop, struct timeval *tv,
pkt.drops = htobe32(0);
pkt.ts = htobe64(ts + 0x00E03AB44A676000ll);
+#ifdef __TIZEN_PATCH__
+ if ((btsnoop->rotate_count > 0 && btsnoop->file_size > 0) &&
+ lseek(btsnoop->fd, 0x00, SEEK_CUR) +
+ BTSNOOP_PKT_SIZE + size > btsnoop->file_size) {
+ btsnoop_rotate_files(btsnoop);
+ if (btsnoop_create_2(btsnoop) < 0)
+ return false;
+ }
+#endif
+
written = write(btsnoop->fd, &pkt, BTSNOOP_PKT_SIZE);
if (written < 0)
return false;
diff --git a/src/shared/btsnoop.h b/src/shared/btsnoop.h
index 96759808..e5089a80 100644
--- a/src/shared/btsnoop.h
+++ b/src/shared/btsnoop.h
@@ -56,7 +56,12 @@ struct btsnoop_opcode_new_index {
struct btsnoop;
struct btsnoop *btsnoop_open(const char *path, unsigned long flags);
+#ifdef __TIZEN_PATCH__
+struct btsnoop *btsnoop_create(const char *path, uint32_t type,
+ int16_t rotate_count, ssize_t file_size);
+#else
struct btsnoop *btsnoop_create(const char *path, uint32_t type);
+#endif
struct btsnoop *btsnoop_ref(struct btsnoop *btsnoop);
void btsnoop_unref(struct btsnoop *btsnoop);
diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
index 28d7ee53..8f97f2be 100644
--- a/src/shared/gatt-client.c
+++ b/src/shared/gatt-client.c
@@ -81,6 +81,8 @@ struct bt_gatt_client {
struct queue *long_write_queue;
bool in_long_write;
+ unsigned int reliable_write_session_id;
+
/* List of registered disconnect/notification/indication callbacks */
struct queue *notify_list;
struct queue *notify_chrcs;
@@ -112,6 +114,7 @@ struct bt_gatt_client {
struct request {
struct bt_gatt_client *client;
bool long_write;
+ bool prep_write;
bool removed;
int ref_count;
unsigned int id;
@@ -1806,69 +1809,78 @@ static bool match_req_id(const void *a, const void *b)
static void cancel_long_write_cb(uint8_t opcode, const void *pdu, uint16_t len,
void *user_data)
{
- /* Do nothing */
+ struct bt_gatt_client *client = user_data;
+
+ if (queue_isempty(client->long_write_queue))
+ client->in_long_write = false;
}
-bool bt_gatt_client_cancel(struct bt_gatt_client *client, unsigned int id)
+static bool cancel_long_write_req(struct bt_gatt_client *client,
+ struct request *req)
{
- 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;
-
+ /*
+ * att_id == 0 means that request has been queued and no prepare write
+ * has been sent so far.Let's just remove if from the queue.
+ * Otherwise execute write needs to be send.
+ */
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),
+ return queue_remove(client->long_write_queue, req);
+
+ return !!bt_att_send(client->att, BT_ATT_OP_EXEC_WRITE_REQ, &pdu,
+ sizeof(pdu),
cancel_long_write_cb,
- NULL, NULL);
+ client, NULL);
- if (queue_isempty(client->long_write_queue))
- client->in_long_write = false;
+}
- return true;
+static void cancel_prep_write_cb(uint8_t opcode, const void *pdu, uint16_t len,
+ void *user_data)
+{
+ struct request *req = user_data;
+ struct bt_gatt_client *client = req->client;
+
+ client->reliable_write_session_id = 0;
}
-static void cancel_request(void *data)
+static bool cancel_prep_write_session(struct bt_gatt_client *client,
+ struct request *req)
{
- struct request *req = data;
uint8_t pdu = 0x00;
+ return !!bt_att_send(client->att, BT_ATT_OP_EXEC_WRITE_REQ, &pdu,
+ sizeof(pdu),
+ cancel_prep_write_cb,
+ req, request_unref);
+}
+
+static bool cancel_request(struct request *req)
+{
req->removed = true;
- if (!req->long_write) {
- bt_att_cancel(req->client->att, req->att_id);
- return;
- }
+ if (req->long_write)
+ return cancel_long_write_req(req->client, req);
- if (!req->att_id)
- queue_remove(req->client->long_write_queue, req);
+ if (req->prep_write)
+ return cancel_prep_write_session(req->client, req);
- if (queue_isempty(req->client->long_write_queue))
- req->client->in_long_write = false;
+ return bt_att_cancel(req->client->att, req->att_id);
+}
- bt_att_send(req->client->att, BT_ATT_OP_EXEC_WRITE_REQ,
- &pdu, sizeof(pdu),
- cancel_long_write_cb,
- NULL, NULL);
+bool bt_gatt_client_cancel(struct bt_gatt_client *client, unsigned int id)
+{
+ struct request *req;
+
+ if (!client || !id || !client->att)
+ return false;
- bt_att_cancel(req->client->att, req->att_id);
+ req = queue_remove_if(client->pending_requests, match_req_id,
+ UINT_TO_PTR(id));
+ if (!req)
+ return false;
+
+ return cancel_request(req);
}
bool bt_gatt_client_cancel_all(struct bt_gatt_client *client)
@@ -1876,7 +1888,8 @@ 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);
+ queue_remove_all(client->pending_requests, NULL, NULL,
+ (queue_destroy_func_t) cancel_request);
if (client->discovery_req) {
bt_gatt_request_cancel(client->discovery_req);
@@ -2254,6 +2267,7 @@ unsigned int bt_gatt_client_write_without_response(
}
struct write_op {
+ struct bt_gatt_client *client;
bt_gatt_client_callback_t callback;
void *user_data;
bt_gatt_destroy_func_t destroy;
@@ -2606,7 +2620,7 @@ unsigned int bt_gatt_client_write_long_value(struct bt_gatt_client *client,
req->destroy = long_write_op_free;
req->long_write = true;
- if (client->in_long_write) {
+ if (client->in_long_write || client->reliable_write_session_id > 0) {
queue_push_tail(client->long_write_queue, req);
return req->id;
}
@@ -2639,6 +2653,264 @@ unsigned int bt_gatt_client_write_long_value(struct bt_gatt_client *client,
return req->id;
}
+struct prep_write_op {
+ bt_gatt_client_write_long_callback_t callback;
+ void *user_data;
+ bt_gatt_destroy_func_t destroy;
+ uint8_t *pdu;
+ uint16_t pdu_len;
+};
+
+static void destroy_prep_write_op(void *data)
+{
+ struct prep_write_op *op = data;
+
+ if (op->destroy)
+ op->destroy(op->user_data);
+
+ free(op->pdu);
+ free(op);
+}
+
+static void prep_write_cb(uint8_t opcode, const void *pdu, uint16_t length,
+ void *user_data)
+{
+ struct request *req = user_data;
+ struct prep_write_op *op = req->data;
+ bool success;
+ uint8_t att_ecode;
+ bool reliable_error;
+
+ if (opcode == BT_ATT_OP_ERROR_RSP) {
+ success = false;
+ reliable_error = false;
+ att_ecode = process_error(pdu, length);
+ goto done;
+ }
+
+ if (opcode != BT_ATT_OP_PREP_WRITE_RSP) {
+ success = false;
+ reliable_error = false;
+ att_ecode = 0;
+ goto done;
+ }
+
+ if (!pdu || length != op->pdu_len ||
+ memcmp(pdu, op->pdu, op->pdu_len)) {
+ success = false;
+ reliable_error = true;
+ att_ecode = 0;
+ goto done;
+ }
+
+ success = true;
+ reliable_error = false;
+ att_ecode = 0;
+
+done:
+ if (op->callback)
+ op->callback(success, reliable_error, att_ecode, op->user_data);
+}
+
+static struct request *get_reliable_request(struct bt_gatt_client *client,
+ unsigned int id)
+{
+ struct request *req;
+ struct prep_write_op *op;
+
+ op = new0(struct prep_write_op, 1);
+ if (!op)
+ return NULL;
+
+ /* Following prepare writes */
+ if (id != 0)
+ req = queue_find(client->pending_requests, match_req_id,
+ UINT_TO_PTR(id));
+ else
+ req = request_create(client);
+
+ if (!req) {
+ free(op);
+ return NULL;
+ }
+
+ req->data = op;
+
+ return req;
+}
+
+unsigned int bt_gatt_client_prepare_write(struct bt_gatt_client *client,
+ unsigned int id, uint16_t value_handle,
+ uint16_t offset, const uint8_t *value,
+ uint16_t length,
+ bt_gatt_client_write_long_callback_t callback,
+ void *user_data,
+ bt_gatt_client_destroy_func_t destroy)
+{
+ struct request *req;
+ struct prep_write_op *op;
+ uint8_t pdu[4 + length];
+
+ if (!client)
+ return 0;
+
+ if (client->in_long_write)
+ return 0;
+
+ /*
+ * Make sure that client who owns reliable session continues with
+ * prepare writes or this is brand new reliable session (id == 0)
+ */
+ if (id != client->reliable_write_session_id) {
+ util_debug(client->debug_callback, client->debug_data,
+ "There is other reliable write session ongoing %u",
+ client->reliable_write_session_id);
+
+ return 0;
+ }
+
+ req = get_reliable_request(client, id);
+ if (!req)
+ return 0;
+
+ op = (struct prep_write_op *)req->data;
+
+ op->callback = callback;
+ op->user_data = user_data;
+ op->destroy = destroy;
+
+ req->destroy = destroy_prep_write_op;
+ req->prep_write = true;
+
+ put_le16(value_handle, pdu);
+ put_le16(offset, pdu + 2);
+ memcpy(pdu + 4, value, length);
+
+ /*
+ * Before sending command we need to remember pdu as we need to validate
+ * it in the response. Store handle, offset and value. Therefore
+ * increase length by 4 (handle + offset) as we need it in couple places
+ * below
+ */
+ length += 4;
+
+ op->pdu = malloc(length);
+ if (!op->pdu) {
+ op->destroy = NULL;
+ request_unref(req);
+ return 0;
+ }
+
+ memcpy(op->pdu, pdu, length);
+ op->pdu_len = length;
+
+ /*
+ * Now we are ready to send command
+ * Note that request_unref will be done on write execute
+ */
+ req->att_id = bt_att_send(client->att, BT_ATT_OP_PREP_WRITE_REQ, pdu,
+ sizeof(pdu), prep_write_cb, req,
+ NULL);
+ if (!req->att_id) {
+ op->destroy = NULL;
+ request_unref(req);
+ return 0;
+ }
+
+ /*
+ * Store first request id for prepare write and treat it as a session id
+ * valid until write execute is done
+ */
+ if (client->reliable_write_session_id == 0)
+ client->reliable_write_session_id = req->id;
+
+ return client->reliable_write_session_id;
+}
+
+static void exec_write_cb(uint8_t opcode, const void *pdu, uint16_t length,
+ void *user_data)
+{
+ struct request *req = user_data;
+ struct write_op *op = req->data;
+ bool success;
+ uint8_t att_ecode;
+
+ if (opcode == BT_ATT_OP_ERROR_RSP) {
+ success = false;
+ att_ecode = process_error(pdu, length);
+ goto done;
+ }
+
+ if (opcode != BT_ATT_OP_EXEC_WRITE_RSP || pdu || length) {
+ success = false;
+ att_ecode = 0;
+ goto done;
+ }
+
+ success = true;
+ att_ecode = 0;
+
+done:
+ if (op->callback)
+ op->callback(success, att_ecode, op->user_data);
+
+ op->client->reliable_write_session_id = 0;
+
+ start_next_long_write(op->client);
+}
+
+unsigned int bt_gatt_client_write_execute(struct bt_gatt_client *client,
+ unsigned int id,
+ 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;
+
+ if (!client)
+ return 0;
+
+ if (client->in_long_write)
+ return 0;
+
+ if (client->reliable_write_session_id != id)
+ return 0;
+
+ op = new0(struct write_op, 1);
+ if (!op)
+ return 0;
+
+ req = queue_find(client->pending_requests, match_req_id,
+ UINT_TO_PTR(id));
+ if (!req) {
+ free(op);
+ return 0;
+ }
+
+ op->client = client;
+ op->callback = callback;
+ op->user_data = user_data;
+ op->destroy = destroy;
+
+ pdu = 0x01;
+
+ req->data = op;
+ req->destroy = destroy_write_op;
+
+ req->att_id = bt_att_send(client->att, BT_ATT_OP_EXEC_WRITE_REQ, &pdu,
+ sizeof(pdu), exec_write_cb,
+ req, request_unref);
+ if (!req->att_id) {
+ op->destroy = NULL;
+ request_unref(req);
+ return 0;
+ }
+
+ return id;
+}
+
static bool match_notify_chrc_value_handle(const void *a, const void *b)
{
const struct notify_chrc *chrc = a;
diff --git a/src/shared/gatt-client.h b/src/shared/gatt-client.h
index 819019e0..13303c6f 100644
--- a/src/shared/gatt-client.h
+++ b/src/shared/gatt-client.h
@@ -108,6 +108,18 @@ unsigned int bt_gatt_client_write_long_value(struct bt_gatt_client *client,
bt_gatt_client_write_long_callback_t callback,
void *user_data,
bt_gatt_client_destroy_func_t destroy);
+unsigned int bt_gatt_client_prepare_write(struct bt_gatt_client *client,
+ unsigned int id,
+ uint16_t value_handle, uint16_t offset,
+ const uint8_t *value, uint16_t length,
+ bt_gatt_client_write_long_callback_t callback,
+ void *user_data,
+ bt_gatt_client_destroy_func_t destroy);
+unsigned int bt_gatt_client_write_execute(struct bt_gatt_client *client,
+ unsigned int id,
+ bt_gatt_client_callback_t callback,
+ void *user_data,
+ bt_gatt_client_destroy_func_t destroy);
unsigned int bt_gatt_client_register_notify(struct bt_gatt_client *client,
uint16_t chrc_value_handle,
diff --git a/src/shared/gatt-db.c b/src/shared/gatt-db.c
index 2b2090cb..fd5c95ef 100644
--- a/src/shared/gatt-db.c
+++ b/src/shared/gatt-db.c
@@ -89,6 +89,10 @@ struct gatt_db_attribute {
uint32_t permissions;
uint16_t value_len;
uint8_t *value;
+#ifdef __TIZEN_PATCH__
+ bool notify_indicate;
+ bdaddr_t unicast_addr;
+#endif
gatt_db_read_t read_func;
gatt_db_write_t write_func;
@@ -1721,3 +1725,34 @@ bool gatt_db_attribute_reset(struct gatt_db_attribute *attrib)
return true;
}
+
+#ifdef __TIZEN_PATCH__
+void set_ccc_notify_indicate(struct gatt_db_attribute *ccc,
+ bool enable)
+{
+ if (ccc)
+ ccc->notify_indicate = enable;
+}
+
+bool get_ccc_notify_indicate(const struct gatt_db_attribute *ccc)
+{
+ if (ccc)
+ return ccc->notify_indicate;
+
+ return false;
+}
+
+void set_ccc_unicast_address(const struct gatt_db_attribute *ccc,
+ const char *address)
+{
+ if (ccc)
+ str2ba(address, &ccc->unicast_addr);
+}
+
+bdaddr_t *get_ccc_unicast_address(const struct gatt_db_attribute *ccc)
+{
+ if (ccc)
+ return &ccc->unicast_addr;
+ return NULL;
+}
+#endif
diff --git a/src/shared/gatt-db.h b/src/shared/gatt-db.h
index 96cceb93..581b7d5b 100644
--- a/src/shared/gatt-db.h
+++ b/src/shared/gatt-db.h
@@ -234,3 +234,15 @@ 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);
+
+#ifdef __TIZEN_PATCH__
+void set_ccc_notify_indicate(struct gatt_db_attribute *ccc,
+ bool enable);
+
+bool get_ccc_notify_indicate(const struct gatt_db_attribute *ccc);
+
+void set_ccc_unicast_address(const struct gatt_db_attribute *ccc,
+ const char *address);
+
+bdaddr_t *get_ccc_unicast_address(const struct gatt_db_attribute *ccc);
+#endif
diff --git a/src/shared/gatt-helpers.c b/src/shared/gatt-helpers.c
index 2d6088e9..1076a6a4 100644
--- a/src/shared/gatt-helpers.c
+++ b/src/shared/gatt-helpers.c
@@ -212,6 +212,7 @@ static bool convert_uuid_le(const uint8_t *src, size_t len, uint8_t dst[16])
struct bt_gatt_request {
struct bt_att *att;
unsigned int id;
+ uint16_t start_handle;
uint16_t end_handle;
int ref_count;
bt_uuid_t uuid;
@@ -625,6 +626,10 @@ static void async_req_unref(void *data)
static void discovery_op_complete(struct bt_gatt_request *op, bool success,
uint8_t ecode)
{
+ /* Reset success if there is some result to report */
+ if (ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND && op->result_head)
+ success = true;
+
if (op->callback)
op->callback(success, ecode, success ? op->result_head : NULL,
op->user_data);
@@ -650,11 +655,6 @@ static void read_by_grp_type_cb(uint8_t opcode, const void *pdu,
if (opcode == BT_ATT_OP_ERROR_RSP) {
success = false;
att_ecode = process_error(pdu, length);
-
- if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND &&
- op->result_head)
- goto success;
-
goto done;
}
@@ -690,10 +690,22 @@ static void read_by_grp_type_cb(uint8_t opcode, const void *pdu,
}
last_end = get_le16(pdu + length - data_length + 2);
+
+ /*
+ * 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_end < op->start_handle) {
+ success = false;
+ goto done;
+ }
+
+ op->start_handle = last_end + 1;
+
if (last_end < op->end_handle) {
uint8_t pdu[6];
- put_le16(last_end + 1, pdu);
+ put_le16(op->start_handle, pdu);
put_le16(op->end_handle, pdu + 2);
put_le16(op->service_type, pdu + 4);
@@ -718,7 +730,6 @@ static void read_by_grp_type_cb(uint8_t opcode, const void *pdu,
put_le16(op->end_handle,
cur_result->pdu + length - data_length + 1);
-success:
success = true;
done:
@@ -736,11 +747,6 @@ static void find_by_type_val_cb(uint8_t opcode, const void *pdu,
if (opcode == BT_ATT_OP_ERROR_RSP) {
success = false;
att_ecode = process_error(pdu, length);
-
- if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND &&
- op->result_head)
- goto success;
-
goto done;
}
@@ -765,10 +771,22 @@ static void find_by_type_val_cb(uint8_t opcode, const void *pdu,
* last_end is end handle of last data set
*/
last_end = get_le16(pdu + length - 2);
+
+ /*
+ * 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_end < op->start_handle) {
+ success = false;
+ goto done;
+ }
+
+ op->start_handle = last_end + 1;
+
if (last_end < op->end_handle) {
uint8_t pdu[6 + get_uuid_len(&op->uuid)];
- put_le16(last_end + 1, pdu);
+ put_le16(op->start_handle, pdu);
put_le16(op->end_handle, pdu + 2);
put_le16(op->service_type, pdu + 4);
bt_uuid_to_le(&op->uuid, pdu + 6);
@@ -785,8 +803,7 @@ static void find_by_type_val_cb(uint8_t opcode, const void *pdu,
goto done;
}
-success:
- success = true;
+ success = false;
done:
discovery_op_complete(op, success, att_ecode);
@@ -810,6 +827,7 @@ static struct bt_gatt_request *discover_services(struct bt_att *att,
return NULL;
op->att = att;
+ op->start_handle = start;
op->end_handle = end;
op->callback = callback;
op->user_data = user_data;
@@ -1047,11 +1065,6 @@ static void discover_included_cb(uint8_t opcode, const void *pdu,
if (opcode == BT_ATT_OP_ERROR_RSP) {
att_ecode = process_error(pdu, length);
-
- if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND &&
- op->result_head)
- goto done;
-
success = false;
goto failed;
}
@@ -1099,10 +1112,21 @@ static void discover_included_cb(uint8_t opcode, const void *pdu,
}
last_handle = get_le16(pdu + length - data_length);
+
+ /*
+ * 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 < op->start_handle) {
+ success = false;
+ goto failed;
+ }
+
+ op->start_handle = last_handle + 1;
if (last_handle != op->end_handle) {
uint8_t pdu[6];
- put_le16(last_handle + 1, pdu);
+ put_le16(op->start_handle, pdu);
put_le16(op->end_handle, pdu + 2);
put_le16(GATT_INCLUDE_UUID, pdu + 4);
@@ -1118,7 +1142,6 @@ static void discover_included_cb(uint8_t opcode, const void *pdu,
goto failed;
}
-done:
success = true;
failed:
@@ -1145,6 +1168,7 @@ struct bt_gatt_request *bt_gatt_discover_included_services(struct bt_att *att,
op->callback = callback;
op->user_data = user_data;
op->destroy = destroy;
+ op->start_handle = start;
op->end_handle = end;
put_le16(start, pdu);
@@ -1174,11 +1198,6 @@ static void discover_chrcs_cb(uint8_t opcode, const void *pdu,
if (opcode == BT_ATT_OP_ERROR_RSP) {
success = false;
att_ecode = process_error(pdu, length);
-
- if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND &&
- op->result_head)
- goto success;
-
goto done;
}
@@ -1209,10 +1228,22 @@ static void discover_chrcs_cb(uint8_t opcode, const void *pdu,
goto done;
}
last_handle = get_le16(pdu + length - data_length);
+
+ /*
+ * 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 < op->start_handle) {
+ success = false;
+ goto done;
+ }
+
+ op->start_handle = last_handle + 1;
+
if (last_handle != op->end_handle) {
uint8_t pdu[6];
- put_le16(last_handle + 1, pdu);
+ put_le16(op->start_handle, pdu);
put_le16(op->end_handle, pdu + 2);
put_le16(GATT_CHARAC_UUID, pdu + 4);
@@ -1228,9 +1259,6 @@ static void discover_chrcs_cb(uint8_t opcode, const void *pdu,
goto done;
}
-success:
- success = true;
-
done:
discovery_op_complete(op, success, att_ecode);
}
@@ -1255,6 +1283,7 @@ struct bt_gatt_request *bt_gatt_discover_characteristics(struct bt_att *att,
op->callback = callback;
op->user_data = user_data;
op->destroy = destroy;
+ op->start_handle = start;
op->end_handle = end;
put_le16(start, pdu);
@@ -1283,13 +1312,7 @@ static void read_by_type_cb(uint8_t opcode, const void *pdu,
if (opcode == BT_ATT_OP_ERROR_RSP) {
att_ecode = process_error(pdu, length);
-
- if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND &&
- op->result_head)
- success = true;
- else
- success = false;
-
+ success = false;
goto done;
}
@@ -1313,10 +1336,22 @@ static void read_by_type_cb(uint8_t opcode, const void *pdu,
}
last_handle = get_le16(pdu + length - data_length);
+
+ /*
+ * 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 < op->start_handle) {
+ success = false;
+ goto done;
+ }
+
+ op->start_handle = last_handle + 1;
+
if (last_handle != op->end_handle) {
uint8_t pdu[4 + get_uuid_len(&op->uuid)];
- put_le16(last_handle + 1, pdu);
+ put_le16(op->start_handle, pdu);
put_le16(op->end_handle, pdu + 2);
bt_uuid_to_le(&op->uuid, pdu + 4);
@@ -1358,6 +1393,7 @@ bool bt_gatt_read_by_type(struct bt_att *att, uint16_t start, uint16_t end,
op->callback = callback;
op->user_data = user_data;
op->destroy = destroy;
+ op->start_handle = start;
op->end_handle = end;
op->uuid = *uuid;
@@ -1389,11 +1425,6 @@ static void discover_descs_cb(uint8_t opcode, const void *pdu,
if (opcode == BT_ATT_OP_ERROR_RSP) {
success = false;
att_ecode = process_error(pdu, length);
-
- if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND &&
- op->result_head)
- goto success;
-
goto done;
}
@@ -1430,10 +1461,22 @@ static void discover_descs_cb(uint8_t opcode, const void *pdu,
}
last_handle = get_le16(pdu + length - data_length);
+
+ /*
+ * 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 < op->start_handle) {
+ success = false;
+ goto done;
+ }
+
+ op->start_handle = last_handle + 1;
+
if (last_handle != op->end_handle) {
uint8_t pdu[4];
- put_le16(last_handle + 1, pdu);
+ put_le16(op->start_handle, pdu);
put_le16(op->end_handle, pdu + 2);
op->id = bt_att_send(op->att, BT_ATT_OP_FIND_INFO_REQ,
@@ -1445,10 +1488,8 @@ static void discover_descs_cb(uint8_t opcode, const void *pdu,
return;
success = false;
- goto done;
}
-success:
success = true;
done:
@@ -1475,6 +1516,7 @@ struct bt_gatt_request *bt_gatt_discover_descriptors(struct bt_att *att,
op->callback = callback;
op->user_data = user_data;
op->destroy = destroy;
+ op->start_handle = start;
op->end_handle = end;
put_le16(start, pdu);
diff --git a/src/shared/tester.c b/src/shared/tester.c
index a49fe041..3c3089f3 100644
--- a/src/shared/tester.c
+++ b/src/shared/tester.c
@@ -90,6 +90,7 @@ struct test_case {
gdouble end_time;
unsigned int timeout;
unsigned int timeout_id;
+ unsigned int teardown_id;
tester_destroy_func_t destroy;
void *user_data;
};
@@ -113,6 +114,9 @@ static void test_destroy(gpointer data)
if (test->timeout_id > 0)
g_source_remove(test->timeout_id);
+ if (test->teardown_id > 0)
+ g_source_remove(test->teardown_id);
+
if (test->destroy)
test->destroy(test->user_data);
@@ -329,6 +333,7 @@ static gboolean teardown_callback(gpointer user_data)
{
struct test_case *test = user_data;
+ test->teardown_id = 0;
test->stage = TEST_STAGE_TEARDOWN;
print_progress(test->name, COLOR_MAGENTA, "teardown");
@@ -496,7 +501,7 @@ void tester_setup_failed(void)
test->post_teardown_func(test->test_data);
}
-void tester_test_passed(void)
+static void test_result(enum test_result result)
{
struct test_case *test;
@@ -513,33 +518,41 @@ void tester_test_passed(void)
test->timeout_id = 0;
}
- test->result = TEST_RESULT_PASSED;
- print_progress(test->name, COLOR_GREEN, "test passed");
-
- g_idle_add(teardown_callback, test);
-}
-
-void tester_test_failed(void)
-{
- struct test_case *test;
+ test->result = result;
+ switch (result) {
+ case TEST_RESULT_PASSED:
+ print_progress(test->name, COLOR_GREEN, "test passed");
+ break;
+ case TEST_RESULT_FAILED:
+ print_progress(test->name, COLOR_RED, "test failed");
+ break;
+ case TEST_RESULT_NOT_RUN:
+ print_progress(test->name, COLOR_YELLOW, "test not run");
+ break;
+ case TEST_RESULT_TIMED_OUT:
+ print_progress(test->name, COLOR_RED, "test timed out");
+ break;
+ }
- if (!test_current)
+ if (test->teardown_id > 0)
return;
- test = test_current->data;
-
- if (test->stage != TEST_STAGE_RUN)
- return;
+ test->teardown_id = g_idle_add(teardown_callback, test);
+}
- if (test->timeout_id > 0) {
- g_source_remove(test->timeout_id);
- test->timeout_id = 0;
- }
+void tester_test_passed(void)
+{
+ test_result(TEST_RESULT_PASSED);
+}
- test->result = TEST_RESULT_FAILED;
- print_progress(test->name, COLOR_RED, "test failed");
+void tester_test_failed(void)
+{
+ test_result(TEST_RESULT_FAILED);
+}
- g_idle_add(teardown_callback, test);
+void tester_test_abort(void)
+{
+ test_result(TEST_RESULT_NOT_RUN);
}
void tester_teardown_complete(void)
diff --git a/src/shared/tester.h b/src/shared/tester.h
index 0231f193..83ef5de7 100644
--- a/src/shared/tester.h
+++ b/src/shared/tester.h
@@ -63,6 +63,7 @@ void tester_setup_failed(void);
void tester_test_passed(void);
void tester_test_failed(void);
+void tester_test_abort(void);
void tester_teardown_complete(void);
void tester_teardown_failed(void);
diff --git a/test/advertisement-example b/test/advertisement-example
new file mode 100644
index 00000000..98aeafa4
--- /dev/null
+++ b/test/advertisement-example
@@ -0,0 +1,170 @@
+#!/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'
+LE_ADVERTISING_MANAGER_IFACE = 'org.bluez.LEAdvertisingManager1'
+DBUS_OM_IFACE = 'org.freedesktop.DBus.ObjectManager'
+DBUS_PROP_IFACE = 'org.freedesktop.DBus.Properties'
+
+LE_ADVERTISEMENT_IFACE = 'org.bluez.LEAdvertisement1'
+
+
+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 Advertisement(dbus.service.Object):
+ PATH_BASE = '/org/bluez/example/advertisement'
+
+ def __init__(self, bus, index, advertising_type):
+ self.path = self.PATH_BASE + str(index)
+ self.bus = bus
+ self.ad_type = advertising_type
+ self.service_uuids = None
+ self.manufacturer_data = None
+ self.solicit_uuids = None
+ self.service_data = None
+ dbus.service.Object.__init__(self, bus, self.path)
+
+ def get_properties(self):
+ properties = dict()
+ properties['Type'] = self.ad_type
+ if self.service_uuids is not None:
+ properties['ServiceUUIDs'] = dbus.Array(self.service_uuids,
+ signature='s')
+ if self.solicit_uuids is not None:
+ properties['SolicitUUIDs'] = dbus.Array(self.solicit_uuids,
+ signature='s')
+ if self.manufacturer_data is not None:
+ properties['ManufacturerData'] = dbus.Dictionary(
+ self.manufacturer_data, signature='qay')
+ if self.service_data is not None:
+ properties['ServiceData'] = dbus.Dictionary(self.service_data,
+ signature='say')
+ return {LE_ADVERTISEMENT_IFACE: properties}
+
+ def get_path(self):
+ return dbus.ObjectPath(self.path)
+
+ def add_service_uuid(self, uuid):
+ if not self.service_uuids:
+ self.service_uuids = []
+ self.service_uuids.append(uuid)
+
+ def add_solicit_uuid(self, uuid):
+ if not self.solicit_uuids:
+ self.solicit_uuids = []
+ self.solicit_uuids.append(uuid)
+
+ def add_manufacturer_data(self, manuf_code, data):
+ if not self.manufacturer_data:
+ self.manufacturer_data = dict()
+ self.manufacturer_data[manuf_code] = data
+
+ def add_service_data(self, uuid, data):
+ if not self.service_data:
+ self.service_data = dict()
+ self.service_data[uuid] = data
+
+ @dbus.service.method(DBUS_PROP_IFACE,
+ in_signature='s',
+ out_signature='a{sv}')
+ def GetAll(self, interface):
+ print 'GetAll'
+ if interface != LE_ADVERTISEMENT_IFACE:
+ raise InvalidArgsException()
+ print 'returning props'
+ return self.get_properties()[LE_ADVERTISEMENT_IFACE]
+
+ @dbus.service.method(LE_ADVERTISEMENT_IFACE,
+ in_signature='',
+ out_signature='')
+ def Release(self):
+ print '%s: Released!' % self.path
+
+class TestAdvertisement(Advertisement):
+
+ def __init__(self, bus, index):
+ Advertisement.__init__(self, bus, index, 'broadcast')
+ self.add_service_uuid('0000180D-0000-1000-8000-00805F9B34FB')
+ self.add_service_uuid('0000180F-0000-1000-8000-00805F9B34FB')
+ self.add_manufacturer_data(0xffff, [0x00, 0x01, 0x02, 0x03, 0x04])
+ self.add_service_data('00009999-0000-1000-8000-00805F9B34FB',
+ [0x00, 0x01, 0x02, 0x03, 0x04])
+
+
+def register_ad_cb():
+ print 'Advertisement registered'
+
+
+def register_ad_error_cb(error):
+ print 'Failed to register advertisement: ' + 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 LE_ADVERTISING_MANAGER_IFACE in props:
+ 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 'LEAdvertisingManager1 interface not found'
+ return
+
+ ad_manager = dbus.Interface(bus.get_object(BLUEZ_SERVICE_NAME, adapter),
+ LE_ADVERTISING_MANAGER_IFACE)
+
+ test_advertisement = TestAdvertisement(bus, 0)
+
+ mainloop = gobject.MainLoop()
+
+ ad_manager.RegisterAdvertisement(test_advertisement.get_path(), {},
+ reply_handler=register_ad_cb,
+ error_handler=register_ad_error_cb)
+
+ mainloop.run()
+
+if __name__ == '__main__':
+ main()
diff --git a/tools/bneptest.c b/tools/bneptest.c
new file mode 100644
index 00000000..84319b9e
--- /dev/null
+++ b/tools/bneptest.c
@@ -0,0 +1,710 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2015 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 <signal.h>
+#include <stdlib.h>
+#include <getopt.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <linux/sockios.h>
+#include <netinet/in.h>
+#include <linux/if_bridge.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include <glib.h>
+
+#include "src/log.h"
+#include "src/shared/util.h"
+#include "btio/btio.h"
+#include "lib/bnep.h"
+#include "profiles/network/bnep.h"
+
+enum {
+ MODE_LISTEN,
+ MODE_CONNECT,
+};
+
+static GMainLoop *mloop;
+static GIOChannel *bnep_io;
+static struct bnep *session;
+
+static int mode;
+static bool no_close_after_disconn;
+static int send_frame_timeout;
+
+static bdaddr_t src_addr, dst_addr;
+static char iface[16];
+static char bridge[16];
+static bool send_ctrl_msg_type_set = false;
+static uint8_t ctrl_msg_type = 0x00;
+static bool send_bnep_msg_type_set = false;
+static uint8_t bnep_msg_type = 0x00;
+static int ctrl_msg_retransmition_nb = 0;
+static int bnep_msg_retransmission_nb = 0;
+static uint16_t local_role = BNEP_SVC_PANU;
+static uint16_t remote_role = BNEP_SVC_NAP;
+static uint16_t ntw_proto_down_range = 0x0000;
+static uint16_t ntw_proto_up_range = 0xdc05;
+static uint16_t ntw_proto_type = 0x0000;
+static uint8_t mcast_addr_down_range[6];
+static uint8_t mcast_addr_up_range[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+static uint8_t src_hw_addr[6];
+static uint8_t dst_hw_addr[6];
+static uint8_t general_frame_payload[] = "abcdef0123456789_bnep_test_data";
+
+static int set_forward_delay(int sk)
+{
+ unsigned long args[4] = { BRCTL_SET_BRIDGE_FORWARD_DELAY, 0, 0, 0 };
+ struct ifreq ifr;
+
+ memset(&ifr, 0, sizeof(ifr));
+ strncpy(ifr.ifr_name, bridge, IFNAMSIZ);
+ ifr.ifr_data = (char *) args;
+
+ if (ioctl(sk, SIOCDEVPRIVATE, &ifr) < 0) {
+ error("setting forward delay failed: %d (%s)",
+ errno, strerror(errno));
+ return -1;
+ }
+
+ return 0;
+}
+
+static int nap_create_bridge(void)
+{
+ int sk, err;
+
+ sk = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, 0);
+ if (sk < 0)
+ return -EOPNOTSUPP;
+
+ if (ioctl(sk, SIOCBRADDBR, bridge) < 0) {
+ if (errno != EEXIST) {
+ close(sk);
+ return -EOPNOTSUPP;
+ }
+ }
+
+ err = set_forward_delay(sk);
+ if (err < 0) {
+ printf("failed to set forward delay\n");
+ ioctl(sk, SIOCBRDELBR, bridge);
+ }
+
+ close(sk);
+
+ return err;
+}
+
+static int cleanup(void)
+{
+ bnep_cleanup();
+
+ if (mode == MODE_LISTEN)
+ bnep_server_delete(bridge, iface, &dst_addr);
+
+ if (bnep_io) {
+ g_io_channel_shutdown(bnep_io, TRUE, NULL);
+ g_io_channel_unref(bnep_io);
+ bnep_io = NULL;
+ }
+
+ return 0;
+}
+
+static gboolean bnep_watchdog_cb(GIOChannel *chan, GIOCondition cond,
+ gpointer user_data)
+{
+ printf("%s\n", __func__);
+
+ if (no_close_after_disconn)
+ return FALSE;
+
+ /* Cleanup since it's called when disconnected l2cap */
+ if (cleanup() < 0) {
+ printf("cleanup went wrong...\n");
+ return FALSE;
+ }
+
+ g_main_loop_quit(mloop);
+ return FALSE;
+}
+
+static ssize_t send_compressed_frame(int sk, uint8_t type)
+{
+ uint8_t frame[100];
+
+ printf("%s\n", __func__);
+
+ if (send_frame_timeout > 0) {
+ printf("waiting %d seconds before sending msg\n",
+ send_frame_timeout);
+ sleep(send_frame_timeout);
+ }
+
+ frame[0] = type;
+ memcpy(&frame[1], dst_hw_addr, sizeof(dst_hw_addr));
+ memcpy(&frame[7], src_hw_addr, sizeof(src_hw_addr));
+ frame[13] = ntw_proto_type & 0xff;
+ frame[14] = (ntw_proto_type >> 8);
+ memcpy(&frame[15], general_frame_payload,
+ sizeof(general_frame_payload));
+
+ /* TODO - set frame payload by user */
+ return send(sk, frame, 15 + sizeof(general_frame_payload), 0);
+}
+
+static ssize_t send_general_frame(int sk)
+{
+ uint8_t frame[100];
+
+ printf("%s\n", __func__);
+
+ if (send_frame_timeout > 0) {
+ printf("waiting %d seconds before sending msg\n",
+ send_frame_timeout);
+ sleep(send_frame_timeout);
+ }
+
+ frame[0] = BNEP_GENERAL;
+ memcpy(&frame[1], dst_hw_addr, sizeof(dst_hw_addr));
+ memcpy(&frame[7], src_hw_addr, sizeof(src_hw_addr));
+ frame[13] = ntw_proto_type & 0xff;
+ frame[14] = (ntw_proto_type >> 8);
+ memcpy(&frame[15], general_frame_payload,
+ sizeof(general_frame_payload));
+
+ /* TODO - set frame payload by user */
+ return send(sk, frame, 15 + sizeof(general_frame_payload), 0);
+}
+
+static ssize_t send_ctrl_frame(int sk)
+{
+ /*
+ * Max buff size = type(1byte) + ctrl(1byte) + len(2byte) +
+ * mcast_addr_down(6byte) + mcast_addr_up(6byte)
+ */
+ uint8_t buff[16];
+ struct bnep_set_filter_req *frame = (void *) buff;
+ int err;
+
+ printf("%s\n", __func__);
+
+ if (send_frame_timeout > 0) {
+ printf("waiting %d seconds before sending msg\n",
+ send_frame_timeout);
+ sleep(send_frame_timeout);
+ }
+
+ switch (ctrl_msg_type) {
+ case BNEP_FILTER_NET_TYPE_SET:
+ frame->type = BNEP_CONTROL;
+ frame->ctrl = ctrl_msg_type;
+ frame->len = htons(sizeof(ntw_proto_down_range) +
+ sizeof(ntw_proto_up_range));
+ memcpy(frame->list, &ntw_proto_down_range,
+ sizeof(ntw_proto_down_range));
+ memcpy(frame->list + sizeof(ntw_proto_down_range),
+ &ntw_proto_up_range, sizeof(ntw_proto_up_range));
+
+ err = send(sk, frame, sizeof(*frame) +
+ sizeof(ntw_proto_down_range) +
+ sizeof(ntw_proto_up_range), 0);
+ break;
+ case BNEP_FILTER_MULT_ADDR_SET:
+ frame->type = BNEP_CONTROL;
+ frame->ctrl = ctrl_msg_type;
+ frame->len = htons(sizeof(mcast_addr_down_range) +
+ sizeof(mcast_addr_up_range));
+ memcpy(frame->list, mcast_addr_down_range,
+ sizeof(mcast_addr_down_range));
+ memcpy(frame->list + sizeof(mcast_addr_down_range),
+ mcast_addr_up_range, sizeof(mcast_addr_up_range));
+
+ err = send(sk, frame, sizeof(*frame) +
+ sizeof(mcast_addr_down_range) +
+ sizeof(mcast_addr_up_range), 0);
+ break;
+ default:
+ err = -1;
+ break;
+ }
+
+ return err;
+}
+
+static int send_bnep_frame(int sk)
+{
+ int err;
+
+ switch (bnep_msg_type) {
+ case BNEP_GENERAL:
+ err = send_general_frame(sk);
+ break;
+ case BNEP_COMPRESSED:
+ err = send_compressed_frame(sk, BNEP_COMPRESSED);
+ break;
+ case BNEP_COMPRESSED_SRC_ONLY:
+ err = send_compressed_frame(sk,
+ BNEP_COMPRESSED_SRC_ONLY);
+ break;
+ case BNEP_COMPRESSED_DST_ONLY:
+ err = send_compressed_frame(sk,
+ BNEP_COMPRESSED_DST_ONLY);
+ break;
+ default:
+ printf("wrong bnep_msg_type 0x%02x\n", bnep_msg_type);
+ err = -EINVAL;
+ break;
+ }
+
+ return err;
+}
+
+static void handle_bnep_msg_send(int sk)
+{
+ if (send_ctrl_msg_type_set) {
+ do {
+ if (send_ctrl_frame(sk) < 0)
+ printf("sending ctrl frame error: %s (%d)\n",
+ strerror(errno), errno);
+ } while (ctrl_msg_retransmition_nb--);
+ }
+
+ if (send_bnep_msg_type_set) {
+ do {
+ if (send_bnep_frame(sk) < 0)
+ printf("sending bnep frame error: %s (%d)\n",
+ strerror(errno), errno);
+ } while (bnep_msg_retransmission_nb--);
+ }
+}
+
+static gboolean setup_bnep_cb(GIOChannel *chan, GIOCondition cond,
+ gpointer user_data)
+{
+ uint8_t packet[BNEP_MTU];
+ int sk, n, err;
+
+ printf("%s\n", __func__);
+
+ if (cond & (G_IO_ERR | G_IO_HUP | G_IO_NVAL)) {
+ error("hangup or error or inval on BNEP socket");
+ return FALSE;
+ }
+
+ sk = g_io_channel_unix_get_fd(chan);
+
+ /* Reading BNEP_SETUP_CONNECTION_REQUEST_MSG */
+#ifdef __TIZEN_PATCH__
+ n = recv(sk, packet, sizeof(packet), MSG_PEEK);
+#else
+ n = read(sk, packet, sizeof(packet));
+#endif
+ if (n < 0) {
+ error("read(): %s(%d)", strerror(errno), errno);
+ return FALSE;
+ }
+
+ err = nap_create_bridge();
+ if (err < 0) {
+ error("failed to create bridge: %s (%d)", strerror(-err), err);
+ return FALSE;
+ }
+
+ if (bnep_server_add(sk, (err < 0) ? NULL : bridge, iface, &dst_addr,
+ packet, n) < 0) {
+ printf("server_connadd failed\n");
+ cleanup();
+ return FALSE;
+ }
+
+ g_io_add_watch(chan, G_IO_HUP | G_IO_ERR | G_IO_NVAL, bnep_watchdog_cb,
+ NULL);
+
+ handle_bnep_msg_send(sk);
+
+ g_io_channel_unref(bnep_io);
+ bnep_io = NULL;
+
+ return FALSE;
+}
+
+static void connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+ printf("%s\n", __func__);
+
+ if (err) {
+ error("%s", err->message);
+ return;
+ }
+
+ g_io_add_watch(chan, G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ setup_bnep_cb, NULL);
+}
+
+static void connected_client_cb(char *iface, int err, void *data)
+{
+ int sk = PTR_TO_INT(data);
+
+ printf("%s\n", __func__);
+
+ handle_bnep_msg_send(sk);
+}
+
+static void disconnected_client_cb(void *data)
+{
+ printf("%s\n", __func__);
+
+ if (no_close_after_disconn)
+ return;
+
+ /* Cleanup since it's called when disconnected l2cap */
+ if (cleanup() < 0) {
+ printf("cleanup went wrong...\n");
+ return;
+ }
+
+ g_main_loop_quit(mloop);
+}
+
+static void connect_client_cb(GIOChannel *chan, GError *err, gpointer user_data)
+{
+ int perr;
+ int sk;
+
+ sk = g_io_channel_unix_get_fd(bnep_io);
+
+ session = bnep_new(sk, local_role, remote_role, bridge);
+ if (!session) {
+ printf("cannot create bnep session\n");
+ return;
+ }
+
+ perr = bnep_connect(session, connected_client_cb,
+ disconnected_client_cb, INT_TO_PTR(sk), NULL);
+ if (perr < 0)
+ printf("cannot initiate bnep connection\n");
+}
+
+static void confirm_cb(GIOChannel *chan, gpointer data)
+{
+ GError *err = NULL;
+ char address[18];
+
+ printf("%s\n", __func__);
+
+ bt_io_get(chan, &err, BT_IO_OPT_DEST_BDADDR, &dst_addr, BT_IO_OPT_DEST,
+ address, BT_IO_OPT_INVALID);
+ if (err) {
+ error("%s", err->message);
+ g_error_free(err);
+ return;
+ }
+
+ printf("incoming connection from: %s\n", address);
+
+ bnep_io = g_io_channel_ref(chan);
+ g_io_channel_set_close_on_unref(bnep_io, TRUE);
+
+ if (!bt_io_accept(bnep_io, connect_cb, NULL, NULL, &err)) {
+ error("bt_io_accept: %s", err->message);
+ g_error_free(err);
+ g_io_channel_unref(bnep_io);
+ }
+}
+
+static int bnep_server_listen(void)
+{
+ GError *gerr = NULL;
+
+ printf("%s\n", __func__);
+
+ bnep_io = bt_io_listen(NULL, confirm_cb, NULL, NULL, &gerr,
+ BT_IO_OPT_SOURCE_BDADDR, &src_addr,
+ BT_IO_OPT_PSM, BNEP_PSM,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+ BT_IO_OPT_OMTU, BNEP_MTU,
+ BT_IO_OPT_IMTU, BNEP_MTU,
+ BT_IO_OPT_INVALID);
+ if (!bnep_io) {
+ printf("can't start server listening: err %s\n", gerr->message);
+ g_error_free(gerr);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int bnep_client_connect(void)
+{
+ GError *gerr = NULL;
+ char bdastr[18];
+
+ printf("%s\n", __func__);
+
+ ba2str(&dst_addr, bdastr);
+ printf("connecting %s\n", bdastr);
+
+ bnep_io = bt_io_connect(connect_client_cb, NULL, NULL, &gerr,
+ BT_IO_OPT_SOURCE_BDADDR, &src_addr,
+ BT_IO_OPT_DEST_BDADDR, &dst_addr,
+ BT_IO_OPT_PSM, BNEP_PSM,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+ BT_IO_OPT_OMTU, BNEP_MTU,
+ BT_IO_OPT_IMTU, BNEP_MTU,
+ BT_IO_OPT_INVALID);
+ if (!bnep_io) {
+ printf("cannot connect: err %s\n", gerr->message);
+ g_error_free(gerr);
+ return -1;
+ }
+
+ return 0;
+}
+
+static void exit_handler(int sig)
+{
+ printf("got sig = %d, cleaning up...\n", sig);
+
+ if (cleanup() < 0)
+ printf("cleanup failure...\n");
+ else
+ printf("cleanup successful - exit\n");
+
+ exit(0);
+}
+
+static void usage(void)
+{
+ printf("bneptest - BNEP testing ver %s\n", VERSION);
+ printf("Usage:\n"
+ "\tbneptest [-i] -b <bridge name> -n <iface name>"
+ " <connection mode> [send_ctrl_cmd] [options]\n"
+ "\t-i hci dev number <hci number>, def. 0\n"
+ "\t-b bridge name <string>\n"
+ "\t-n interface name <string>\n");
+ printf("Connect Mode:\n"
+ "\t-c connect <dst_addr>\n"
+ "\t-r remote role <16 bit svc value>\n"
+ "\t-l local role <16 bit svc valu>\n");
+ printf("Listen Mode:\n"
+ "\t-s start server listening\n");
+ printf("Send control command:\n"
+ "\t-t send message type <control msg type>, def. 0\n"
+ "\t-e start network protocol type range <16 bit val>, def. 0\n"
+ "\t-d end network protocol type range <16 bit val>, def. 1500\n"
+ "\t-g start multicast addr range <xx:xx:xx:xx:xx:xx>, def. 0\n"
+ "\t-j end multicast addr range <xx:xx:xx:xx:xx:xx>, def. f\n"
+ "\t-y number of ctrl frame retransmission <integer>, def. 0\n"
+ "\t-u number of bnep frame retransmission <integer>, def. 0\n");
+ printf("Send bnep generic frame:\n"
+ "\t-w send bnep generic frame <bnep generic type>, def. 0\n"
+ "\t-k set src mac addr <xx:xx:xx:xx:xx:xx>, def. 0\n"
+ "\t-f set dst mac addr <xx:xx:xx:xx:xx:xx>, def. 0\n");
+ printf("Options:\n"
+ "\t-T send message timeout after setup <seconds>\n"
+ "\t-N don't close bneptest after disconnect\n");
+}
+
+static struct option main_options[] = {
+ { "device", 1, 0, 'i' },
+ { "listen", 0, 0, 's' },
+ { "connect", 1, 0, 'c' },
+ { "snd_ctrl_msg_type", 1, 0, 't' },
+ { "snd_bnep_msg_type", 1, 0, 'w' },
+ { "src_hw_addr", 1, 0, 'k' },
+ { "dst_hw_addr", 1, 0, 'f' },
+ { "send_timeout", 1, 0, 'T' },
+ { "ntw_proto_down_range", 1, 0, 'd' },
+ { "ntw_proto_up_range", 1, 0, 'e' },
+ { "mcast_addr_down_range", 1, 0, 'g' },
+ { "mcast_addr_up_range", 1, 0, 'j' },
+ { "local_role", 1, 0, 'l' },
+ { "remote_role", 1, 0, 'r' },
+ { "bridge name", 1, 0, 'b' },
+ { "iface name", 1, 0, 'n' },
+ { "no_close", 0, 0, 'N' },
+ { "retrans_ctrl_nb", 0, 0, 'y' },
+ { "retrans_bnep_nb", 0, 0, 'u' },
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+int main(int argc, char *argv[])
+{
+ int opt, i;
+ int err;
+ bool is_set_b_name = false, is_set_i_name = false;
+
+ DBG("");
+
+ signal(SIGINT, exit_handler);
+
+ hci_devba(0, &src_addr);
+ bacpy(&src_addr, BDADDR_ANY);
+
+ mloop = g_main_loop_new(NULL, FALSE);
+ if (!mloop) {
+ printf("cannot create main loop\n");
+
+ exit(1);
+ }
+
+ while ((opt = getopt_long(argc, argv,
+ "+i:c:b:n:t:T:d:e:g:j:k:f:w:l:r:y:u:Nsh",
+ main_options, NULL)) != EOF) {
+ switch (opt) {
+ case 'i':
+ if (!strncmp(optarg, "hci", 3))
+ hci_devba(atoi(optarg + 3), &src_addr);
+ else
+ str2ba(optarg, &src_addr);
+ break;
+ case 's':
+ mode = MODE_LISTEN;
+ break;
+ case 'c':
+ str2ba(optarg, &dst_addr);
+ mode = MODE_CONNECT;
+ break;
+ case 't':
+ send_ctrl_msg_type_set = true;
+ ctrl_msg_type = atoi(optarg);
+ break;
+ case 'w':
+ send_bnep_msg_type_set = true;
+ bnep_msg_type = atoi(optarg);
+ break;
+ case 'k':
+ for (i = 0; i <= 5; i++, optarg += 3)
+ src_hw_addr[i] = strtol(optarg, NULL, 16);
+ break;
+ case 'f':
+ for (i = 0; i <= 5; i++, optarg += 3)
+ dst_hw_addr[i] = strtol(optarg, NULL, 16);
+ break;
+ case 'T':
+ send_frame_timeout = atoi(optarg);
+ break;
+ case 'd':
+ ntw_proto_down_range = htons(atoi(optarg));
+ break;
+ case 'e':
+ ntw_proto_up_range = htons(atoi(optarg));
+ break;
+ case 'g':
+ for (i = 5; i >= 0; i--, optarg += 3)
+ mcast_addr_down_range[i] =
+ strtol(optarg, NULL, 16);
+ break;
+ case 'j':
+ for (i = 5; i >= 0; i--, optarg += 3)
+ mcast_addr_up_range[i] =
+ strtol(optarg, NULL, 16);
+ break;
+ case 'l':
+ local_role = atoi(optarg);
+ break;
+ case 'r':
+ remote_role = atoi(optarg);
+ break;
+ case 'b':
+ strncpy(bridge, optarg, 16);
+ bridge[15] = '\0';
+ is_set_b_name = true;
+ break;
+ case 'n':
+ strncpy(iface, optarg, 14);
+ strcat(iface, "\%d");
+ iface[15] = '\0';
+ is_set_i_name = true;
+ break;
+ case 'N':
+ no_close_after_disconn = true;
+ break;
+ case 'y':
+ ctrl_msg_retransmition_nb = atoi(optarg);
+ break;
+ case 'u':
+ bnep_msg_retransmission_nb = atoi(optarg);
+ break;
+ case 'h':
+ default:
+ usage();
+ exit(0);
+ }
+ }
+
+ if (!is_set_b_name || !is_set_i_name) {
+ printf("bridge, interface name must be set!\n");
+ exit(1);
+ }
+
+ switch (mode) {
+ case MODE_CONNECT:
+ err = bnep_init();
+ if (err < 0) {
+ printf("cannot initialize bnep\n");
+ exit(1);
+ }
+ err = bnep_client_connect();
+ if (err < 0)
+ exit(1);
+
+ break;
+ case MODE_LISTEN:
+ err = bnep_init();
+ if (err < 0) {
+ printf("cannot initialize bnep\n");
+ exit(1);
+ }
+ err = bnep_server_listen();
+ if (err < 0)
+ exit(1);
+
+ break;
+ default:
+ printf("connect/listen mode not set, exit...\n");
+ exit(1);
+ }
+
+ g_main_loop_run(mloop);
+
+ printf("Done\n");
+
+ g_main_loop_unref(mloop);
+
+ return 0;
+}
diff --git a/tools/btgatt-client.c b/tools/btgatt-client.c
index 02d9b57d..ee5315d4 100644
--- a/tools/btgatt-client.c
+++ b/tools/btgatt-client.c
@@ -67,6 +67,8 @@ struct client {
struct bt_att *att;
struct gatt_db *db;
struct bt_gatt_client *gatt;
+
+ unsigned int reliable_session_id;
};
static void print_prompt(void)
@@ -112,12 +114,6 @@ static const char *ecode_to_string(uint8_t ecode)
return "Group type Not Supported";
case BT_ATT_ERROR_INSUFFICIENT_RESOURCES:
return "Insufficient Resources";
- case BT_ERROR_CCC_IMPROPERLY_CONFIGURED:
- return "CCC Improperly Configured";
- case BT_ERROR_ALREADY_IN_PROGRESS:
- return "Procedure Already in Progress";
- case BT_ERROR_OUT_OF_RANGE:
- return "Out of Range";
default:
return "Unknown error type";
}
@@ -890,6 +886,208 @@ static void cmd_write_long_value(struct client *cli, char *cmd_str)
free(value);
}
+static void write_prepare_usage(void)
+{
+ printf("Usage: write-prepare [options] <value_handle> <offset> "
+ "<value>\n"
+ "Options:\n"
+ "\t-s, --session-id\tSession id\n"
+ "e.g.:\n"
+ "\twrite-prepare -s 1 0x0001 00 01 00\n");
+}
+
+static struct option write_prepare_options[] = {
+ { "session-id", 1, 0, 's' },
+ { }
+};
+
+static void cmd_write_prepare(struct client *cli, char *cmd_str)
+{
+ int opt, i;
+ char *argvbuf[516];
+ char **argv = argvbuf;
+ int argc = 0;
+ unsigned int id = 0;
+ uint16_t handle;
+ uint16_t offset;
+ char *endptr = NULL;
+ unsigned int length;
+ uint8_t *value = NULL;
+
+ if (!bt_gatt_client_is_ready(cli->gatt)) {
+ printf("GATT client not initialized\n");
+ return;
+ }
+
+ if (!parse_args(cmd_str, 514, argv + 1, &argc)) {
+ printf("Too many arguments\n");
+ write_value_usage();
+ return;
+ }
+
+ /* Add command name for getopt_long */
+ argc++;
+ argv[0] = "write-prepare";
+
+ optind = 0;
+ while ((opt = getopt_long(argc, argv , "s:", write_prepare_options,
+ NULL)) != -1) {
+ switch (opt) {
+ case 's':
+ if (!optarg) {
+ write_prepare_usage();
+ return;
+ }
+
+ id = atoi(optarg);
+
+ break;
+ default:
+ write_prepare_usage();
+ return;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 3) {
+ write_prepare_usage();
+ return;
+ }
+
+ if (cli->reliable_session_id != id) {
+ printf("Session id != Ongoing session id (%u!=%u)\n", id,
+ cli->reliable_session_id);
+ return;
+ }
+
+ handle = strtol(argv[0], &endptr, 0);
+ if (!endptr || *endptr != '\0' || !handle) {
+ printf("Invalid handle: %s\n", argv[0]);
+ return;
+ }
+
+ endptr = NULL;
+ offset = strtol(argv[1], &endptr, 0);
+ if (!endptr || *endptr != '\0' || errno == ERANGE) {
+ printf("Invalid offset: %s\n", argv[1]);
+ return;
+ }
+
+ /*
+ * First two arguments are handle and offset. What remains is the value
+ * length
+ */
+ length = argc - 2;
+
+ if (length == 0)
+ goto done;
+
+ if (length > UINT16_MAX) {
+ printf("Write value too long\n");
+ return;
+ }
+
+ value = malloc(length);
+ if (!value) {
+ printf("Failed to allocate memory for value\n");
+ return;
+ }
+
+ for (i = 2; i < argc; i++) {
+ if (strlen(argv[i]) != 2) {
+ printf("Invalid value byte: %s\n", argv[i]);
+ free(value);
+ return;
+ }
+
+ value[i-2] = strtol(argv[i], &endptr, 0);
+ if (endptr == argv[i] || *endptr != '\0' || errno == ERANGE) {
+ printf("Invalid value byte: %s\n", argv[i]);
+ free(value);
+ return;
+ }
+ }
+
+done:
+ cli->reliable_session_id =
+ bt_gatt_client_prepare_write(cli->gatt, id,
+ handle, offset,
+ value, length,
+ write_long_cb, NULL,
+ NULL);
+ if (!cli->reliable_session_id)
+ printf("Failed to proceed prepare write\n");
+ else
+ printf("Prepare write success.\n"
+ "Session id: %d to be used on next write\n",
+ cli->reliable_session_id);
+
+ free(value);
+}
+
+static void write_execute_usage(void)
+{
+ printf("Usage: write-execute <session_id> <execute>\n"
+ "e.g.:\n"
+ "\twrite-execute 1 0\n");
+}
+
+static void cmd_write_execute(struct client *cli, char *cmd_str)
+{
+ char *argvbuf[516];
+ char **argv = argvbuf;
+ int argc = 0;
+ char *endptr = NULL;
+ unsigned int session_id;
+ bool execute;
+
+ if (!bt_gatt_client_is_ready(cli->gatt)) {
+ printf("GATT client not initialized\n");
+ return;
+ }
+
+ if (!parse_args(cmd_str, 514, argv, &argc)) {
+ printf("Too many arguments\n");
+ write_value_usage();
+ return;
+ }
+
+ if (argc < 2) {
+ write_execute_usage();
+ return;
+ }
+
+ session_id = strtol(argv[0], &endptr, 0);
+ if (!endptr || *endptr != '\0') {
+ printf("Invalid session id: %s\n", argv[0]);
+ return;
+ }
+
+ if (session_id != cli->reliable_session_id) {
+ printf("Invalid session id: %u != %u\n", session_id,
+ cli->reliable_session_id);
+ return;
+ }
+
+ execute = !!strtol(argv[1], &endptr, 0);
+ if (!endptr || *endptr != '\0') {
+ printf("Invalid execute: %s\n", argv[1]);
+ return;
+ }
+
+ if (execute) {
+ if (!bt_gatt_client_write_execute(cli->gatt, session_id,
+ write_cb, NULL, NULL))
+ printf("Failed to proceed write execute\n");
+ } else {
+ bt_gatt_client_cancel(cli->gatt, session_id);
+ }
+
+ cli->reliable_session_id = 0;
+}
+
static void register_notify_usage(void)
{
printf("Usage: register-notify <chrc value handle>\n");
@@ -1136,14 +1334,18 @@ static struct {
"\tWrite a characteristic or descriptor value" },
{ "write-long-value", cmd_write_long_value,
"Write long characteristic or descriptor value" },
+ { "write-prepare", cmd_write_prepare,
+ "\tWrite prepare characteristic or descriptor value" },
+ { "write-execute", cmd_write_execute,
+ "\tExecute already prepared write" },
{ "register-notify", cmd_register_notify,
"\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"},
+ "\tSet security level on le connection"},
{ "get-sec-level", cmd_get_sec_level,
- "Get security level on le connection"},
+ "\tGet security level on le connection"},
{ "set-sign-key", cmd_set_sign_key,
"\tSet signing key for signed write command"},
{ }
diff --git a/tools/btgatt-server.c b/tools/btgatt-server.c
index b4fbe607..b30a958f 100644
--- a/tools/btgatt-server.c
+++ b/tools/btgatt-server.c
@@ -630,6 +630,7 @@ static void usage(void)
"\t-m, --mtu <mtu>\t\t\tThe ATT MTU to use\n"
"\t-s, --security-level <sec>\tSet security level (low|"
"medium|high)\n"
+ "\t-t, --type [random|public] \t The source address type\n"
"\t-v, --verbose\t\t\tEnable extra logging\n"
"\t-r, --heart-rate\t\tEnable Heart Rate service\n"
"\t-h, --help\t\t\tDisplay help\n");
@@ -639,13 +640,15 @@ static struct option main_options[] = {
{ "index", 1, 0, 'i' },
{ "mtu", 1, 0, 'm' },
{ "security-level", 1, 0, 's' },
+ { "type", 1, 0, 't' },
{ "verbose", 0, 0, 'v' },
{ "heart-rate", 0, 0, 'r' },
{ "help", 0, 0, 'h' },
{ }
};
-static int l2cap_le_att_listen_and_accept(bdaddr_t *src, int sec)
+static int l2cap_le_att_listen_and_accept(bdaddr_t *src, int sec,
+ uint8_t src_type)
{
int sk, nsk;
struct sockaddr_l2 srcaddr, addr;
@@ -663,7 +666,7 @@ static int l2cap_le_att_listen_and_accept(bdaddr_t *src, int sec)
memset(&srcaddr, 0, sizeof(srcaddr));
srcaddr.l2_family = AF_BLUETOOTH;
srcaddr.l2_cid = htobs(ATT_CID);
- srcaddr.l2_bdaddr_type = 0;
+ srcaddr.l2_bdaddr_type = src_type;
bacpy(&srcaddr.l2_bdaddr, src);
if (bind(sk, (struct sockaddr *) &srcaddr, sizeof(srcaddr)) < 0) {
@@ -1131,12 +1134,13 @@ int main(int argc, char *argv[])
int dev_id = -1;
int fd;
int sec = BT_SECURITY_LOW;
+ uint8_t src_type = BDADDR_LE_PUBLIC;
uint16_t mtu = 0;
sigset_t mask;
bool hr_visible = false;
struct server *server;
- while ((opt = getopt_long(argc, argv, "+hvrs:m:i:",
+ while ((opt = getopt_long(argc, argv, "+hvrs:t:m:i:",
main_options, NULL)) != -1) {
switch (opt) {
case 'h':
@@ -1160,6 +1164,17 @@ int main(int argc, char *argv[])
return EXIT_FAILURE;
}
break;
+ case 't':
+ if (strcmp(optarg, "random") == 0)
+ src_type = BDADDR_LE_RANDOM;
+ else if (strcmp(optarg, "public") == 0)
+ src_type = BDADDR_LE_PUBLIC;
+ else {
+ fprintf(stderr,
+ "Allowed types: random, public\n");
+ return EXIT_FAILURE;
+ }
+ break;
case 'm': {
int arg;
@@ -1207,7 +1222,7 @@ int main(int argc, char *argv[])
return EXIT_FAILURE;
}
- fd = l2cap_le_att_listen_and_accept(&src_addr, sec);
+ fd = l2cap_le_att_listen_and_accept(&src_addr, sec, src_type);
if (fd < 0) {
fprintf(stderr, "Failed to accept L2CAP ATT connection\n");
return EXIT_FAILURE;
diff --git a/tools/btmgmt.c b/tools/btmgmt.c
index 8eff56b8..d7af7f08 100644
--- a/tools/btmgmt.c
+++ b/tools/btmgmt.c
@@ -58,6 +58,10 @@
#include "src/shared/util.h"
#include "src/shared/mgmt.h"
+#define SCAN_TYPE_BREDR (1 << BDADDR_BREDR)
+#define SCAN_TYPE_LE ((1 << BDADDR_LE_PUBLIC) | (1 << BDADDR_LE_RANDOM))
+#define SCAN_TYPE_DUAL (SCAN_TYPE_BREDR | SCAN_TYPE_LE)
+
static struct mgmt *mgmt = NULL;
static uint16_t mgmt_index = MGMT_INDEX_NONE;
@@ -153,6 +157,56 @@ static size_t bin2hex(const uint8_t *buf, size_t buflen, char *str,
return i;
}
+static void print_eir(const uint8_t *eir, uint16_t eir_len)
+{
+ uint16_t parsed = 0;
+ char str[33];
+
+ while (parsed < eir_len - 1) {
+ uint8_t field_len = eir[0];
+
+ if (field_len == 0)
+ break;
+
+ parsed += field_len + 1;
+
+ if (parsed > eir_len)
+ break;
+
+ switch (eir[1]) {
+ case 0x01:
+ print("Flags: 0x%02x", eir[2]);
+ break;
+ case 0x0d:
+ print("Class of Device: 0x%02x%02x%02x",
+ eir[4], eir[3], eir[2]);
+ break;
+ case 0x1b:
+ ba2str((bdaddr_t *) (eir + 2), str);
+ print("LE Device Address: %s (%s)", str,
+ eir[8] ? "random" : "public");
+ break;
+ case 0x1c:
+ print("LE Role: 0x%02x", eir[2]);
+ break;
+ case 0x22:
+ bin2hex(eir + 2, 16, str, sizeof(str));
+ print("LE SC Confirmation Value: %s", str);
+ break;
+ case 0x23:
+ bin2hex(eir + 2, 16, str, sizeof(str));
+ print("LE SC Random Value: %s", str);
+ break;
+ default:
+ print("Type %u: %u byte%s", eir[1], field_len - 1,
+ (field_len - 1) == 1 ? "" : "s");
+ break;
+ }
+
+ eir += field_len + 1;
+ }
+}
+
static bool load_identity(uint16_t index, struct mgmt_irk_info *irk)
{
char identity_path[PATH_MAX];
@@ -235,6 +289,22 @@ static void unconf_index_removed(uint16_t index, uint16_t len,
print("hci%u removed (unconfigured)", index);
}
+static void ext_index_added(uint16_t index, uint16_t len,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_ext_index_added *ev = param;
+
+ print("hci%u added (type %u bus %u)", index, ev->type, ev->bus);
+}
+
+static void ext_index_removed(uint16_t index, uint16_t len,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_ext_index_removed *ev = param;
+
+ print("hci%u removed (type %u bus %u)", index, ev->type, ev->bus);
+}
+
static const char *options_str[] = {
"external",
"public-address",
@@ -472,6 +542,20 @@ static void auth_failed(uint16_t index, uint16_t len, const void *param,
index, addr, ev->status, mgmt_errstr(ev->status));
}
+static void class_of_dev_changed(uint16_t index, uint16_t len,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_class_of_dev_changed *ev = param;
+
+ if (len != sizeof(*ev)) {
+ error("Invalid class_of_dev_changed length (%u bytes)", len);
+ return;
+ }
+
+ print("hci%u class of device changed: 0x%02x%02x%02x", index,
+ ev->dev_class[2], ev->dev_class[1], ev->dev_class[0]);
+}
+
static void local_name_changed(uint16_t index, uint16_t len, const void *param,
void *user_data)
{
@@ -962,6 +1046,54 @@ static void passkey_notify(uint16_t index, uint16_t len, const void *param,
ev->entered);
}
+static void local_oob_data_updated(uint16_t index, uint16_t len,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_local_oob_data_updated *ev = param;
+ uint16_t eir_len;
+
+ if (len < sizeof(*ev)) {
+ error("Too small (%u bytes) local_oob_updated event", len);
+ return;
+ }
+
+ eir_len = le16_to_cpu(ev->eir_len);
+ if (len != sizeof(*ev) + eir_len) {
+ error("local_oob_updated: expected %zu bytes, got %u bytes",
+ sizeof(*ev) + eir_len, len);
+ return;
+ }
+
+ print("hci%u oob data updated: type %u len %u", index,
+ ev->type, eir_len);
+}
+
+static void advertising_added(uint16_t index, uint16_t len,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_advertising_added *ev = param;
+
+ if (len < sizeof(*ev)) {
+ error("Too small (%u bytes) advertising_added event", len);
+ return;
+ }
+
+ print("hci%u advertising_added: instance %u", index, ev->instance);
+}
+
+static void advertising_removed(uint16_t index, uint16_t len,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_advertising_removed *ev = param;
+
+ if (len < sizeof(*ev)) {
+ error("Too small (%u bytes) advertising_removed event", len);
+ return;
+ }
+
+ print("hci%u advertising_removed: instance %u", index, ev->instance);
+}
+
static void version_rsp(uint8_t status, uint16_t len, const void *param,
void *user_data)
{
@@ -1055,6 +1187,43 @@ static void cmd_commands(struct mgmt *mgmt, uint16_t index, int argc,
}
}
+static void config_info_rsp(uint8_t status, uint16_t len, const void *param,
+ void *user_data)
+{
+ const struct mgmt_rp_read_config_info *rp = param;
+ uint16_t index = PTR_TO_UINT(user_data);
+ uint32_t supported_options, missing_options;
+
+ if (status != 0) {
+ error("Reading hci%u config failed with status 0x%02x (%s)",
+ index, status, mgmt_errstr(status));
+ goto done;
+ }
+
+ if (len < sizeof(*rp)) {
+ error("Too small info reply (%u bytes)", len);
+ goto done;
+ }
+
+ print("hci%u:\tUnconfigured controller", index);
+
+ print("\tmanufacturer %u", le16_to_cpu(rp->manufacturer));
+
+ supported_options = le32_to_cpu(rp->supported_options);
+ print("\tsupported options: %s", options2str(supported_options));
+
+ missing_options = le32_to_cpu(rp->missing_options);
+ print("\tmissing options: %s", options2str(missing_options));
+
+done:
+ pending_index--;
+
+ if (pending_index > 0)
+ return;
+
+ noninteractive_quit(EXIT_SUCCESS);
+}
+
static void unconf_index_rsp(uint8_t status, uint16_t len, const void *param,
void *user_data)
{
@@ -1065,43 +1234,67 @@ static void unconf_index_rsp(uint8_t status, uint16_t len, const void *param,
if (status != 0) {
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)) {
error("Too small index list reply (%u bytes)", len);
- goto done;
+ return noninteractive_quit(EXIT_FAILURE);
}
- count = get_le16(&rp->num_controllers);
+ count = le16_to_cpu(rp->num_controllers);
if (len < sizeof(*rp) + count * sizeof(uint16_t)) {
error("Index count (%u) doesn't match reply length (%u)",
count, len);
- goto done;
+ return noninteractive_quit(EXIT_FAILURE);
}
print("Unconfigured index list with %u item%s",
- count, count != 1 ? "s" : "");
+ count, count != 1 ? "s" : "");
for (i = 0; i < count; i++) {
- uint16_t index;
+ uint16_t index = le16_to_cpu(rp->index[i]);
- index = get_le16(&rp->index[i]);
+ if (!mgmt_send(mgmt, MGMT_OP_READ_CONFIG_INFO, index, 0, NULL,
+ config_info_rsp, UINT_TO_PTR(index), NULL)) {
+ error("Unable to send read_config_info cmd");
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+
+ pending_index++;
+ }
+
+ if (!count)
+ noninteractive_quit(EXIT_SUCCESS);
+}
- print("\thci%u", index);
+static void cmd_config(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
+{
+ if (index == MGMT_INDEX_NONE) {
+ if (!mgmt_send(mgmt, MGMT_OP_READ_UNCONF_INDEX_LIST,
+ MGMT_INDEX_NONE, 0, NULL,
+ unconf_index_rsp, mgmt, NULL)) {
+ error("Unable to send unconf_index_list cmd");
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+ return;
}
-done:
- noninteractive_quit(EXIT_SUCCESS);
+ if (!mgmt_send(mgmt, MGMT_OP_READ_CONFIG_INFO, index, 0, NULL,
+ config_info_rsp, UINT_TO_PTR(index), NULL)) {
+ error("Unable to send read_config_info cmd");
+ return noninteractive_quit(EXIT_FAILURE);
+ }
}
-static void config_info_rsp(uint8_t status, uint16_t len, const void *param,
+static void config_options_rsp(uint8_t status, uint16_t len, const void *param,
void *user_data)
{
const struct mgmt_rp_read_config_info *rp = param;
uint16_t index = PTR_TO_UINT(user_data);
+ uint32_t supported_options, missing_options;
if (status != 0) {
error("Reading hci%u config failed with status 0x%02x (%s)",
@@ -1114,39 +1307,21 @@ static void config_info_rsp(uint8_t status, uint16_t len, const void *param,
goto done;
}
- print("hci%u:\tmanufacturer %u", index, get_le16(&rp->manufacturer));
-
- print("\tsupported options: %s",
- options2str(get_le32(&rp->supported_options)));
- print("\tmissing options: %s",
- options2str(get_le32(&rp->missing_options)));
+ print("hci%u:\tConfiguration options", index);
-done:
- noninteractive_quit(EXIT_SUCCESS);
-}
+ supported_options = le32_to_cpu(rp->supported_options);
+ print("\tsupported options: %s", options2str(supported_options));
-static void cmd_config(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
-{
- void *data;
+ missing_options = le32_to_cpu(rp->missing_options);
+ print("\tmissing options: %s", options2str(missing_options));
- if (index == MGMT_INDEX_NONE) {
- if (mgmt_send(mgmt, MGMT_OP_READ_UNCONF_INDEX_LIST,
- MGMT_INDEX_NONE, 0, NULL,
- unconf_index_rsp, mgmt, NULL) == 0) {
- error("Unable to send unconf_index_list cmd");
- return noninteractive_quit(EXIT_FAILURE);
- }
+done:
+ pending_index--;
+ if (pending_index > 0)
return;
- }
- data = UINT_TO_PTR(index);
-
- if (mgmt_send(mgmt, MGMT_OP_READ_CONFIG_INFO, index, 0, NULL,
- config_info_rsp, data, NULL) == 0) {
- error("Unable to send read_config_info cmd");
- return noninteractive_quit(EXIT_FAILURE);
- }
+ noninteractive_quit(EXIT_SUCCESS);
}
static void info_rsp(uint8_t status, uint16_t len, const void *param,
@@ -1154,10 +1329,9 @@ static void info_rsp(uint8_t status, uint16_t len, const void *param,
{
const struct mgmt_rp_read_info *rp = param;
uint16_t index = PTR_TO_UINT(user_data);
+ uint32_t supported_settings, current_settings;
char addr[18];
- pending_index--;
-
if (status != 0) {
error("Reading hci%u info failed with status 0x%02x (%s)",
index, status, mgmt_errstr(status));
@@ -1169,25 +1343,38 @@ static void info_rsp(uint8_t status, uint16_t len, const void *param,
goto done;
}
+ print("hci%u:\tPrimary controller", index);
+
ba2str(&rp->bdaddr, addr);
- print("hci%u:\taddr %s version %u manufacturer %u"
- " class 0x%02x%02x%02x", index,
- addr, rp->version, get_le16(&rp->manufacturer),
+ print("\taddr %s version %u manufacturer %u class 0x%02x%02x%02x",
+ addr, rp->version, le16_to_cpu(rp->manufacturer),
rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]);
- print("\tsupported settings: %s",
- settings2str(get_le32(&rp->supported_settings)));
+ supported_settings = le32_to_cpu(rp->supported_settings);
+ print("\tsupported settings: %s", settings2str(supported_settings));
- print("\tcurrent settings: %s",
- settings2str(get_le32(&rp->current_settings)));
+ current_settings = le32_to_cpu(rp->current_settings);
+ print("\tcurrent settings: %s", settings2str(current_settings));
print("\tname %s", rp->name);
print("\tshort name %s", rp->short_name);
- if (pending_index > 0)
+ if (supported_settings & MGMT_SETTING_CONFIGURATION) {
+ if (!mgmt_send(mgmt, MGMT_OP_READ_CONFIG_INFO,
+ index, 0, NULL, config_options_rsp,
+ UINT_TO_PTR(index), NULL)) {
+ error("Unable to send read_config cmd");
+ goto done;
+ }
return;
+ }
done:
+ pending_index--;
+
+ if (pending_index > 0)
+ return;
+
noninteractive_quit(EXIT_SUCCESS);
}
@@ -1210,7 +1397,7 @@ static void index_rsp(uint8_t status, uint16_t len, const void *param,
return noninteractive_quit(EXIT_FAILURE);
}
- count = get_le16(&rp->num_controllers);
+ count = le16_to_cpu(rp->num_controllers);
if (len < sizeof(*rp) + count * sizeof(uint16_t)) {
error("Index count (%u) doesn't match reply length (%u)",
@@ -1221,15 +1408,10 @@ static void index_rsp(uint8_t status, uint16_t len, const void *param,
print("Index list with %u item%s", count, count != 1 ? "s" : "");
for (i = 0; i < count; i++) {
- uint16_t index;
- void *data;
-
- index = get_le16(&rp->index[i]);
+ uint16_t index = le16_to_cpu(rp->index[i]);
- data = UINT_TO_PTR(index);
-
- if (mgmt_send(mgmt, MGMT_OP_READ_INFO, index, 0, NULL,
- info_rsp, data, NULL) == 0) {
+ if (!mgmt_send(mgmt, MGMT_OP_READ_INFO, index, 0, NULL,
+ info_rsp, UINT_TO_PTR(index), NULL)) {
error("Unable to send read_info cmd");
return noninteractive_quit(EXIT_FAILURE);
}
@@ -1243,12 +1425,10 @@ static void index_rsp(uint8_t status, uint16_t len, const void *param,
static void cmd_info(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
{
- void *data;
-
if (index == MGMT_INDEX_NONE) {
- if (mgmt_send(mgmt, MGMT_OP_READ_INDEX_LIST,
+ if (!mgmt_send(mgmt, MGMT_OP_READ_INDEX_LIST,
MGMT_INDEX_NONE, 0, NULL,
- index_rsp, mgmt, NULL) == 0) {
+ index_rsp, mgmt, NULL)) {
error("Unable to send index_list cmd");
return noninteractive_quit(EXIT_FAILURE);
}
@@ -1256,15 +1436,93 @@ static void cmd_info(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
return;
}
- data = UINT_TO_PTR(index);
-
- if (mgmt_send(mgmt, MGMT_OP_READ_INFO, index, 0, NULL, info_rsp,
- data, NULL) == 0) {
+ if (!mgmt_send(mgmt, MGMT_OP_READ_INFO, index, 0, NULL, info_rsp,
+ UINT_TO_PTR(index), NULL)) {
error("Unable to send read_info cmd");
return noninteractive_quit(EXIT_FAILURE);
}
}
+static void ext_index_rsp(uint8_t status, uint16_t len, const void *param,
+ void *user_data)
+{
+ const struct mgmt_rp_read_ext_index_list *rp = param;
+ uint16_t count, index_filter = PTR_TO_UINT(user_data);
+ unsigned int i;
+
+ if (status != 0) {
+ error("Reading ext index list failed with status 0x%02x (%s)",
+ status, mgmt_errstr(status));
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+
+ if (len < sizeof(*rp)) {
+ error("Too small ext 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) + sizeof(uint8_t))) {
+ error("Index count (%u) doesn't match reply length (%u)",
+ count, len);
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+
+ print("Extended index list with %u item%s",
+ count, count != 1 ? "s" : "");
+
+ for (i = 0; i < count; i++) {
+ uint16_t index = le16_to_cpu(rp->entry[i].index);
+
+ if (index_filter != MGMT_INDEX_NONE && index_filter != index)
+ continue;
+
+ switch (rp->entry[i].type) {
+ case 0x00:
+ if (!mgmt_send(mgmt, MGMT_OP_READ_INFO,
+ index, 0, NULL, info_rsp,
+ UINT_TO_PTR(index), NULL)) {
+ error("Unable to send read_info cmd");
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+ pending_index++;
+ break;
+ case 0x01:
+ if (!mgmt_send(mgmt, MGMT_OP_READ_CONFIG_INFO,
+ index, 0, NULL, config_info_rsp,
+ UINT_TO_PTR(index), NULL)) {
+ error("Unable to send read_config cmd");
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+ pending_index++;
+ break;
+ case 0x02:
+ print("hci%u:\tAMP controller (%u)", index,
+ rp->entry[i].bus);
+ break;
+ default:
+ print("hci%u:\tType %u controller (%u)", index,
+ rp->entry[i].type, rp->entry[i].bus);
+ break;
+ }
+ }
+
+ if (!count)
+ noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void cmd_extinfo(struct mgmt *mgmt, uint16_t index,
+ int argc, char **argv)
+{
+ if (!mgmt_send(mgmt, MGMT_OP_READ_EXT_INDEX_LIST,
+ MGMT_INDEX_NONE, 0, NULL,
+ ext_index_rsp, UINT_TO_PTR(index), NULL)) {
+ error("Unable to send ext_index_list cmd");
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+}
+
/* Wrapper to get the index and opcode to the response callback */
struct command_data {
uint16_t id;
@@ -1540,7 +1798,7 @@ static void class_rsp(uint16_t op, uint16_t id, uint8_t status, uint16_t len,
}
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]);
+ rp->dev_class[2], rp->dev_class[1], rp->dev_class[0]);
noninteractive_quit(EXIT_SUCCESS);
}
@@ -1621,9 +1879,11 @@ static void cmd_disconnect(struct mgmt *mgmt, uint16_t index, int argc,
break;
case 'h':
disconnect_usage();
+ optind = 0;
return noninteractive_quit(EXIT_SUCCESS);
default:
disconnect_usage();
+ optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
}
@@ -1739,7 +1999,7 @@ static void cmd_find_service(struct mgmt *mgmt, uint16_t index, int argc,
uuid_t uuid;
uint128_t uint128;
uuid_t uuid128;
- uint8_t type;
+ uint8_t type = SCAN_TYPE_DUAL;
int8_t rssi;
uint16_t count;
int opt;
@@ -1747,10 +2007,6 @@ static void cmd_find_service(struct mgmt *mgmt, uint16_t index, int argc,
if (index == MGMT_INDEX_NONE)
index = 0;
- type = 0;
- type |= (1 << BDADDR_BREDR);
- type |= (1 << BDADDR_LE_PUBLIC);
- type |= (1 << BDADDR_LE_RANDOM);
rssi = 127;
count = 0;
@@ -1763,23 +2019,23 @@ static void cmd_find_service(struct mgmt *mgmt, uint16_t index, int argc,
find_service_options, NULL)) != -1) {
switch (opt) {
case 'l':
- type &= ~(1 << BDADDR_BREDR);
- type |= (1 << BDADDR_LE_PUBLIC);
- type |= (1 << BDADDR_LE_RANDOM);
+ type &= ~SCAN_TYPE_BREDR;
+ type |= SCAN_TYPE_LE;
break;
case 'b':
- type |= (1 << BDADDR_BREDR);
- type &= ~(1 << BDADDR_LE_PUBLIC);
- type &= ~(1 << BDADDR_LE_RANDOM);
+ type |= SCAN_TYPE_BREDR;
+ type &= ~SCAN_TYPE_LE;
break;
case 'u':
if (count == MAX_UUIDS) {
print("Max %u UUIDs supported", MAX_UUIDS);
+ optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
if (bt_string2uuid(&uuid, optarg) < 0) {
print("Invalid UUID: %s", optarg);
+ optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
cp = (void *) buf;
@@ -1793,9 +2049,11 @@ static void cmd_find_service(struct mgmt *mgmt, uint16_t index, int argc,
break;
case 'h':
find_service_usage();
+ optind = 0;
return noninteractive_quit(EXIT_SUCCESS);
default:
find_service_usage();
+ optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
}
@@ -1850,35 +2108,30 @@ static struct option find_options[] = {
static void cmd_find(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
{
struct mgmt_cp_start_discovery cp;
- uint8_t type;
+ uint8_t type = SCAN_TYPE_DUAL;
int opt;
if (index == MGMT_INDEX_NONE)
index = 0;
- type = 0;
- 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':
- type &= ~(1 << BDADDR_BREDR);
- type |= (1 << BDADDR_LE_PUBLIC);
- type |= (1 << BDADDR_LE_RANDOM);
+ type &= ~SCAN_TYPE_BREDR;
+ type |= SCAN_TYPE_LE;
break;
case 'b':
- type |= (1 << BDADDR_BREDR);
- type &= ~(1 << BDADDR_LE_PUBLIC);
- type &= ~(1 << BDADDR_LE_RANDOM);
+ type |= SCAN_TYPE_BREDR;
+ type &= ~SCAN_TYPE_LE;
break;
case 'h':
find_usage();
+ optind = 0;
return noninteractive_quit(EXIT_SUCCESS);
default:
find_usage();
+ optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
}
@@ -1897,6 +2150,78 @@ static void cmd_find(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
}
}
+static void stop_find_rsp(uint8_t status, uint16_t len, const void *param,
+ void *user_data)
+{
+ if (status != 0) {
+ fprintf(stderr,
+ "Stop Discovery failed: status 0x%02x (%s)\n",
+ status, mgmt_errstr(status));
+ mainloop_quit();
+ return;
+ }
+
+ printf("Discovery stopped\n");
+ discovery = false;
+
+ noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void stop_find_usage(void)
+{
+ printf("Usage: btmgmt stop-find [-l|-b]>\n");
+}
+
+static struct option stop_find_options[] = {
+ { "help", 0, 0, 'h' },
+ { "le-only", 1, 0, 'l' },
+ { "bredr-only", 1, 0, 'b' },
+ { 0, 0, 0, 0 }
+};
+
+static void cmd_stop_find(struct mgmt *mgmt, uint16_t index, int argc,
+ char **argv)
+{
+ struct mgmt_cp_stop_discovery cp;
+ uint8_t type = SCAN_TYPE_DUAL;
+ int opt;
+
+ if (index == MGMT_INDEX_NONE)
+ index = 0;
+
+ while ((opt = getopt_long(argc, argv, "+lbh", stop_find_options,
+ NULL)) != -1) {
+ switch (opt) {
+ case 'l':
+ type &= ~SCAN_TYPE_BREDR;
+ type |= SCAN_TYPE_LE;
+ break;
+ case 'b':
+ type |= SCAN_TYPE_BREDR;
+ type &= ~SCAN_TYPE_LE;
+ break;
+ case 'h':
+ default:
+ stop_find_usage();
+ optind = 0;
+ exit(EXIT_SUCCESS);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ optind = 0;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.type = type;
+
+ if (mgmt_send(mgmt, MGMT_OP_STOP_DISCOVERY, index, sizeof(cp), &cp,
+ stop_find_rsp, NULL, NULL) == 0) {
+ fprintf(stderr, "Unable to send stop_discovery cmd\n");
+ exit(EXIT_FAILURE);
+ }
+}
+
static void name_rsp(uint8_t status, uint16_t len, const void *param,
void *user_data)
{
@@ -1995,9 +2320,11 @@ static void cmd_pair(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
break;
case 'h':
pair_usage();
+ optind = 0;
return noninteractive_quit(EXIT_SUCCESS);
default:
pair_usage();
+ optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
}
@@ -2084,9 +2411,11 @@ static void cmd_cancel_pair(struct mgmt *mgmt, uint16_t index, int argc,
break;
case 'h':
cancel_pair_usage();
+ optind = 0;
return noninteractive_quit(EXIT_SUCCESS);
default:
cancel_pair_usage();
+ optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
}
@@ -2168,9 +2497,11 @@ static void cmd_unpair(struct mgmt *mgmt, uint16_t index, int argc,
break;
case 'h':
unpair_usage();
+ optind = 0;
return noninteractive_quit(EXIT_SUCCESS);
default:
unpair_usage();
+ optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
}
@@ -2299,6 +2630,7 @@ static void cmd_irks(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
case 'l':
if (count >= MAX_IRKS) {
error("Number of IRKs exceeded");
+ optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
if (strlen(optarg) > 3 &&
@@ -2308,15 +2640,18 @@ static void cmd_irks(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
local_index = atoi(optarg);
if (!load_identity(local_index, &cp->irks[count])) {
error("Unable to load identity");
+ optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
count++;
break;
case 'h':
irks_usage();
+ optind = 0;
return noninteractive_quit(EXIT_SUCCESS);
default:
irks_usage();
+ optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
}
@@ -2394,9 +2729,11 @@ static void cmd_block(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
break;
case 'h':
block_usage();
+ optind = 0;
return noninteractive_quit(EXIT_SUCCESS);
default:
block_usage();
+ optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
}
@@ -2444,9 +2781,11 @@ static void cmd_unblock(struct mgmt *mgmt, uint16_t index, int argc,
break;
case 'h':
unblock_usage();
+ optind = 0;
return noninteractive_quit(EXIT_SUCCESS);
default:
unblock_usage();
+ optind = 0;
return noninteractive_quit(EXIT_FAILURE);
}
}
@@ -2555,7 +2894,6 @@ static void local_oob_rsp(uint8_t status, uint16_t len, const void *param,
void *user_data)
{
const struct mgmt_rp_read_local_oob_data *rp = param;
- const struct mgmt_rp_read_local_oob_ext_data *rp_ext = param;
char str[33];
if (status != 0) {
@@ -2569,19 +2907,19 @@ static void local_oob_rsp(uint8_t status, uint16_t len, const void *param,
return noninteractive_quit(EXIT_FAILURE);
}
- bin2hex(rp->hash, 16, str, sizeof(str));
+ bin2hex(rp->hash192, 16, str, sizeof(str));
print("Hash C from P-192: %s", str);
- bin2hex(rp->randomizer, 16, str, sizeof(str));
+ bin2hex(rp->rand192, 16, str, sizeof(str));
print("Randomizer R with P-192: %s", str);
- if (len < sizeof(*rp_ext))
+ if (len < sizeof(*rp))
return noninteractive_quit(EXIT_SUCCESS);
- bin2hex(rp_ext->hash256, 16, str, sizeof(str));
+ bin2hex(rp->hash256, 16, str, sizeof(str));
print("Hash C from P-256: %s", str);
- bin2hex(rp_ext->randomizer256, 16, str, sizeof(str));
+ bin2hex(rp->rand256, 16, str, sizeof(str));
print("Randomizer R with P-256: %s", str);
noninteractive_quit(EXIT_SUCCESS);
@@ -3236,6 +3574,456 @@ static void cmd_clr_devices(struct mgmt *mgmt, uint16_t index,
cmd_del_device(mgmt, index, 2, rm_argv);
}
+static void local_oob_ext_rsp(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 eir_len;
+
+ if (status != 0) {
+ error("Read Local OOB Ext Data failed with status 0x%02x (%s)",
+ status, mgmt_errstr(status));
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+
+ if (len < sizeof(*rp)) {
+ error("Too small (%u bytes) read_local_oob_ext rsp", len);
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+
+ eir_len = le16_to_cpu(rp->eir_len);
+ if (len != sizeof(*rp) + eir_len) {
+ error("local_oob_ext: expected %zu bytes, got %u bytes",
+ sizeof(*rp) + eir_len, len);
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+
+ print_eir(rp->eir, eir_len);
+
+ noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void cmd_bredr_oob(struct mgmt *mgmt, uint16_t index,
+ int argc, char **argv)
+{
+ struct mgmt_cp_read_local_oob_ext_data cp;
+
+ if (index == MGMT_INDEX_NONE)
+ index = 0;
+
+ cp.type = SCAN_TYPE_BREDR;
+
+ if (!mgmt_send(mgmt, MGMT_OP_READ_LOCAL_OOB_EXT_DATA,
+ index, sizeof(cp), &cp,
+ local_oob_ext_rsp, NULL, NULL)) {
+ error("Unable to send read_local_oob_ext cmd");
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+}
+
+static void cmd_le_oob(struct mgmt *mgmt, uint16_t index,
+ int argc, char **argv)
+{
+ struct mgmt_cp_read_local_oob_ext_data cp;
+
+ if (index == MGMT_INDEX_NONE)
+ index = 0;
+
+ cp.type = SCAN_TYPE_LE;
+
+ if (!mgmt_send(mgmt, MGMT_OP_READ_LOCAL_OOB_EXT_DATA,
+ index, sizeof(cp), &cp,
+ local_oob_ext_rsp, NULL, NULL)) {
+ error("Unable to send read_local_oob_ext cmd");
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+}
+
+static const char *adv_flags_str[] = {
+ "connectable",
+ "general-discoverable",
+ "limited-discoverable",
+ "managed-flags",
+ "tx-power",
+ "scan-rsp-appearance",
+ "scan-rsp-local-name",
+};
+
+static const char *adv_flags2str(uint32_t flags)
+{
+ static char str[256];
+ unsigned i;
+ int off;
+
+ off = 0;
+ str[0] = '\0';
+
+ for (i = 0; i < NELEM(adv_flags_str); i++) {
+ if ((flags & (1 << i)) != 0)
+ off += snprintf(str + off, sizeof(str) - off, "%s ",
+ adv_flags_str[i]);
+ }
+
+ return str;
+}
+
+static void adv_features_rsp(uint8_t status, uint16_t len, const void *param,
+ void *user_data)
+{
+ const struct mgmt_rp_read_adv_features *rp = param;
+ uint32_t supported_flags;
+
+ if (status != 0) {
+ error("Reading adv features failed with status 0x%02x (%s)",
+ status, mgmt_errstr(status));
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+
+ if (len < sizeof(*rp)) {
+ error("Too small adv features reply (%u bytes)", len);
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+
+ if (len < sizeof(*rp) + rp->num_instances * sizeof(uint8_t)) {
+ error("Instances count (%u) doesn't match reply length (%u)",
+ rp->num_instances, len);
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+
+ supported_flags = le32_to_cpu(rp->supported_flags);
+ print("Supported flags: %s", adv_flags2str(supported_flags));
+ print("Max advertising data len: %u", rp->max_adv_data_len);
+ print("Max scan response data len: %u", rp->max_scan_rsp_len);
+ print("Max instances: %u", rp->max_instances);
+
+ print("Instances list with %u item%s", rp->num_instances,
+ rp->num_instances != 1 ? "s" : "");
+
+ return noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void cmd_advinfo(struct mgmt *mgmt, uint16_t index,
+ int argc, char **argv)
+{
+ if (index == MGMT_INDEX_NONE)
+ index = 0;
+
+ if (!mgmt_send(mgmt, MGMT_OP_READ_ADV_FEATURES, index, 0, NULL,
+ adv_features_rsp, NULL, NULL)) {
+ error("Unable to send advertising features command");
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+}
+
+static void add_adv_rsp(uint8_t status, uint16_t len, const void *param,
+ void *user_data)
+{
+ const struct mgmt_rp_add_advertising *rp = param;
+
+ if (status != 0) {
+ error("Add Advertising failed with status 0x%02x (%s)",
+ status, mgmt_errstr(status));
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+
+ if (len != sizeof(*rp)) {
+ error("Invalid Add Advertising response length (%u)", len);
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+
+ print("Instance added: %u", rp->instance);
+
+ return noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void add_adv_usage(void)
+{
+ print("Usage: add-adv [options] <instance_id>\nOptions:\n"
+ "\t -u, --uuid <uuid> Service UUID\n"
+ "\t -d, --adv-data <data> Advertising Data bytes\n"
+ "\t -s, --scan-rsp <data> Scan Response Data bytes\n"
+ "\t -t, --timeout <timeout> Timeout in seconds\n"
+ "\t -c, --connectable \"connectable\" flag\n"
+ "\t -g, --general-discov \"general-discoverable\" flag\n"
+ "\t -l, --limited-discov \"limited-discoverable\" flag\n"
+ "\t -m, --managed-flags \"managed-flags\" flag\n"
+ "\t -p, --tx-power \"tx-power\" flag\n"
+ "e.g.:\n"
+ "\tadd-adv -u 180d -u 180f -d 080954657374204C45 1");
+}
+
+static struct option add_adv_options[] = {
+ { "help", 0, 0, 'h' },
+ { "uuid", 1, 0, 'u' },
+ { "adv-data", 1, 0, 'd' },
+ { "scan-rsp", 1, 0, 's' },
+ { "timeout", 1, 0, 't' },
+ { "connectable", 0, 0, 'c' },
+ { "general-discov", 0, 0, 'g' },
+ { "limited-discov", 0, 0, 'l' },
+ { "managed-flags", 0, 0, 'm' },
+ { "tx-power", 0, 0, 'p' },
+ { 0, 0, 0, 0}
+};
+
+static bool parse_bytes(char *optarg, uint8_t **bytes, size_t *len)
+{
+ unsigned i;
+
+ if (!optarg) {
+ add_adv_usage();
+ return false;
+ }
+
+ *len = strlen(optarg);
+
+ if (*len % 2) {
+ error("Malformed data");
+ return false;
+ }
+
+ *len /= 2;
+ if (*len > UINT8_MAX) {
+ error("Data too long");
+ return false;
+ }
+
+ *bytes = malloc(*len);
+ if (!*bytes) {
+ error("Failed to allocate memory");
+ return false;
+ }
+
+ for (i = 0; i < *len; i++) {
+ if (sscanf(optarg + (i * 2), "%2hhx", *bytes + i) != 1) {
+ error("Invalid data");
+ free(*bytes);
+ *bytes = NULL;
+ return false;
+ }
+ }
+
+ return true;
+}
+
+#define MAX_AD_UUID_BYTES 32
+
+static void cmd_add_adv(struct mgmt *mgmt, uint16_t index,
+ int argc, char **argv)
+{
+ struct mgmt_cp_add_advertising *cp = NULL;
+ int opt;
+ uint8_t *adv_data = NULL, *scan_rsp = NULL;
+ size_t adv_len = 0, scan_rsp_len = 0;
+ size_t cp_len;
+ uint8_t uuids[MAX_AD_UUID_BYTES];
+ size_t uuid_bytes = 0;
+ uint8_t uuid_type = 0;
+ uint16_t timeout = 0;
+ uint8_t instance;
+ uuid_t uuid;
+ bool success = false;
+ bool quit = true;
+ uint32_t flags = 0;
+
+ while ((opt = getopt_long(argc, argv, "+u:d:s:t:cglmph",
+ add_adv_options, NULL)) != -1) {
+ switch (opt) {
+ case 'u':
+ if (bt_string2uuid(&uuid, optarg) < 0) {
+ print("Invalid UUID: %s", optarg);
+ goto done;
+ }
+
+ if (uuid_type && uuid_type != uuid.type) {
+ print("UUID types must be consistent");
+ goto done;
+ }
+
+ if (uuid.type == SDP_UUID16) {
+ if (uuid_bytes + 2 >= MAX_AD_UUID_BYTES) {
+ print("Too many UUIDs");
+ goto done;
+ }
+
+ put_le16(uuid.value.uuid16, uuids + uuid_bytes);
+ uuid_bytes += 2;
+ } else if (uuid.type == SDP_UUID128) {
+ if (uuid_bytes + 16 >= MAX_AD_UUID_BYTES) {
+ print("Too many UUIDs");
+ goto done;
+ }
+
+ bswap_128(uuid.value.uuid128.data,
+ uuids + uuid_bytes);
+ uuid_bytes += 16;
+ } else {
+ printf("Unsupported UUID type");
+ goto done;
+ }
+
+ if (!uuid_type)
+ uuid_type = uuid.type;
+
+ break;
+ case 'd':
+ if (adv_len) {
+ print("Only one adv-data option allowed");
+ goto done;
+ }
+
+ if (!parse_bytes(optarg, &adv_data, &adv_len))
+ goto done;
+ break;
+ case 's':
+ if (scan_rsp_len) {
+ print("Only one scan-rsp option allowed");
+ goto done;
+ }
+
+ if (!parse_bytes(optarg, &scan_rsp, &scan_rsp_len))
+ goto done;
+ break;
+ case 't':
+ timeout = strtol(optarg, NULL, 0);
+ break;
+ case 'c':
+ flags |= MGMT_ADV_FLAG_CONNECTABLE;
+ break;
+ case 'g':
+ flags |= MGMT_ADV_FLAG_DISCOV;
+ break;
+ case 'l':
+ flags |= MGMT_ADV_FLAG_LIMITED_DISCOV;
+ break;
+ case 'm':
+ flags |= MGMT_ADV_FLAG_MANAGED_FLAGS;
+ break;
+ case 'p':
+ flags |= MGMT_ADV_FLAG_TX_POWER;
+ break;
+ case 'h':
+ success = true;
+ default:
+ add_adv_usage();
+ goto done;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ optind = 0;
+
+ if (argc != 1) {
+ add_adv_usage();
+ goto done;
+ }
+
+ if (uuid_bytes)
+ uuid_bytes += 2;
+
+ instance = strtol(argv[0], NULL, 0);
+
+ if (index == MGMT_INDEX_NONE)
+ index = 0;
+
+ cp_len = sizeof(*cp) + uuid_bytes + adv_len + scan_rsp_len;
+ cp = malloc0(cp_len);
+ if (!cp)
+ goto done;
+
+ cp->instance = instance;
+ put_le32(flags, &cp->flags);
+ put_le16(timeout, &cp->timeout);
+ cp->adv_data_len = adv_len + uuid_bytes;
+ cp->scan_rsp_len = scan_rsp_len;
+
+ if (uuid_bytes) {
+ cp->data[0] = uuid_bytes - 1;
+ cp->data[1] = uuid_type == SDP_UUID16 ? 0x03 : 0x07;
+ memcpy(cp->data + 2, uuids, uuid_bytes - 2);
+ }
+
+ memcpy(cp->data + uuid_bytes, adv_data, adv_len);
+ memcpy(cp->data + uuid_bytes + adv_len, scan_rsp, scan_rsp_len);
+
+ if (!mgmt_send(mgmt, MGMT_OP_ADD_ADVERTISING, index, cp_len, cp,
+ add_adv_rsp, NULL, NULL)) {
+ error("Unable to send \"Add Advertising\" command");
+ goto done;
+ }
+
+ quit = false;
+
+done:
+ free(adv_data);
+ free(scan_rsp);
+ free(cp);
+
+ if (quit)
+ noninteractive_quit(success ? EXIT_SUCCESS : EXIT_FAILURE);
+}
+
+static void rm_adv_rsp(uint8_t status, uint16_t len, const void *param,
+ void *user_data)
+{
+ const struct mgmt_rp_remove_advertising *rp = param;
+
+ if (status != 0) {
+ error("Remove Advertising failed with status 0x%02x (%s)",
+ status, mgmt_errstr(status));
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+
+ if (len != sizeof(*rp)) {
+ error("Invalid Remove Advertising response length (%u)", len);
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+
+ print("Instance removed: %u", rp->instance);
+
+ return noninteractive_quit(EXIT_SUCCESS);
+}
+
+static void rm_adv_usage(void)
+{
+ print("Usage: rm-adv <instance_id>");
+}
+
+static void cmd_rm_adv(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
+{
+ struct mgmt_cp_remove_advertising cp;
+ uint8_t instance;
+
+ if (argc != 2) {
+ rm_adv_usage();
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+
+ instance = strtol(argv[1], NULL, 0);
+
+ if (index == MGMT_INDEX_NONE)
+ index = 0;
+
+ memset(&cp, 0, sizeof(cp));
+
+ cp.instance = instance;
+
+ if (!mgmt_send(mgmt, MGMT_OP_REMOVE_ADVERTISING, index, sizeof(cp), &cp,
+ rm_adv_rsp, NULL, NULL)) {
+ error("Unable to send \"Remove Advertising\" command");
+ return noninteractive_quit(EXIT_FAILURE);
+ }
+}
+
+static void cmd_clr_adv(struct mgmt *mgmt, uint16_t index, int argc, char **argv)
+{
+ char *all_instances = "0";
+ char *rm_argv[] = { "rm-adv", all_instances, NULL };
+
+ cmd_rm_adv(mgmt, index, 2, rm_argv);
+}
+
struct cmd_info {
char *cmd;
void (*func)(struct mgmt *mgmt, uint16_t index, int argc, char **argv);
@@ -3249,6 +4037,7 @@ static struct cmd_info all_cmd[] = {
{ "commands", cmd_commands, "List supported commands" },
{ "config", cmd_config, "Show configuration info" },
{ "info", cmd_info, "Show controller info" },
+ { "extinfo", cmd_extinfo, "Show extended controller info" },
{ "power", cmd_power, "Toggle powered state" },
{ "discov", cmd_discov, "Toggle discoverable state" },
{ "connectable",cmd_connectable,"Toggle connectable state" },
@@ -3268,6 +4057,7 @@ static struct cmd_info all_cmd[] = {
{ "con", cmd_con, "List connections" },
{ "find", cmd_find, "Discover nearby devices" },
{ "find-service", cmd_find_service, "Discover nearby service" },
+ { "stop-find", cmd_stop_find, "Stop discovery" },
{ "name", cmd_name, "Set local name" },
{ "pair", cmd_pair, "Pair with a remote device" },
{ "cancelpair", cmd_cancel_pair,"Cancel pairing" },
@@ -3294,6 +4084,12 @@ static struct cmd_info all_cmd[] = {
{ "add-device", cmd_add_device, "Add Device" },
{ "del-device", cmd_del_device, "Remove Device" },
{ "clr-devices",cmd_clr_devices,"Clear Devices" },
+ { "bredr-oob", cmd_bredr_oob, "Local OOB data (BR/EDR)" },
+ { "le-oob", cmd_le_oob, "Local OOB data (LE)" },
+ { "advinfo", cmd_advinfo, "Show advertising features" },
+ { "add-adv", cmd_add_adv, "Add advertising instance" },
+ { "rm-adv", cmd_rm_adv, "Remove advertising instance" },
+ { "clr-adv", cmd_clr_adv, "Clear advertising instances" },
};
static void cmd_quit(struct mgmt *mgmt, uint16_t index,
@@ -3324,6 +4120,8 @@ static void register_mgmt_callbacks(struct mgmt *mgmt, uint16_t index)
NULL, NULL);
mgmt_register(mgmt, MGMT_EV_AUTH_FAILED, index, auth_failed,
NULL, NULL);
+ mgmt_register(mgmt, MGMT_EV_CLASS_OF_DEV_CHANGED, index,
+ class_of_dev_changed, 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,
@@ -3342,7 +4140,16 @@ static void register_mgmt_callbacks(struct mgmt *mgmt, uint16_t index)
unconf_index_removed, NULL, NULL);
mgmt_register(mgmt, MGMT_EV_NEW_CONFIG_OPTIONS, index,
new_config_options, NULL, NULL);
-
+ mgmt_register(mgmt, MGMT_EV_EXT_INDEX_ADDED, index,
+ ext_index_added, NULL, NULL);
+ mgmt_register(mgmt, MGMT_EV_EXT_INDEX_REMOVED, index,
+ ext_index_removed, NULL, NULL);
+ mgmt_register(mgmt, MGMT_EV_LOCAL_OOB_DATA_UPDATED, index,
+ local_oob_data_updated, NULL, NULL);
+ mgmt_register(mgmt, MGMT_EV_ADVERTISING_ADDED, index,
+ advertising_added, NULL, NULL);
+ mgmt_register(mgmt, MGMT_EV_ADVERTISING_REMOVED, index,
+ advertising_removed, NULL, NULL);
}
static void cmd_select(struct mgmt *mgmt, uint16_t index,
diff --git a/tools/btsnoop.c b/tools/btsnoop.c
index 3eb8082d..278e18ce 100644
--- a/tools/btsnoop.c
+++ b/tools/btsnoop.c
@@ -34,6 +34,8 @@
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
+#include <time.h>
+#include <sys/time.h>
#include <getopt.h>
#include <endian.h>
#include <arpa/inet.h>
@@ -272,6 +274,86 @@ close_input:
close(input_fd[i]);
}
+#define BT_SNOOP_TYPE_HCI_PREFIX "btsnoop_type_hci"
+
+static void command_split(const char *input)
+{
+ unsigned char buf[BTSNOOP_MAX_PACKET_SIZE];
+ uint16_t pktlen,opcode;
+ uint32_t type;
+ struct timeval tv;
+ uint16_t index, max_index = 0;
+ char write_file_name[255];
+ struct btsnoop *btsnoop_read_file = NULL;
+ struct btsnoop *btsnoop_write_file[16];
+ time_t t;
+ struct tm tm;
+ unsigned long num_packets = 0;
+
+ btsnoop_read_file = btsnoop_open(input, BTSNOOP_FLAG_PKLG_SUPPORT);
+ if (!btsnoop_read_file)
+ return;
+
+ type = btsnoop_get_type(btsnoop_read_file);
+ if (type != BTSNOOP_TYPE_MONITOR) {
+ fprintf(stderr, "unsupported link data type %u\n", type);
+ btsnoop_unref(btsnoop_read_file);
+ return;
+ }
+
+next_packet:
+ if (!btsnoop_read_hci(btsnoop_read_file, &tv, &index, &opcode, buf,
+ &pktlen))
+ goto close_files;
+
+ if (opcode == 0xffff)
+ goto next_packet;
+
+ switch (opcode) {
+ case BTSNOOP_OPCODE_NEW_INDEX:
+ t = tv.tv_sec;
+ localtime_r(&t, &tm);
+
+ if (max_index < index)
+ max_index = index;
+
+ sprintf(write_file_name, "%s%d_%02d:%02d:%02d.%06lu.log",
+ BT_SNOOP_TYPE_HCI_PREFIX, index, tm.tm_hour, tm.tm_min,
+ tm.tm_sec, tv.tv_usec);
+
+ printf("New Index %d would be saved in %s\n", index,
+ write_file_name);
+
+ btsnoop_write_file[index] = btsnoop_create(write_file_name,
+ BTSNOOP_TYPE_HCI, -1, -1);
+ if (!btsnoop_write_file[index])
+ goto close_files;
+
+ break;
+ case BTSNOOP_OPCODE_DEL_INDEX:
+ printf("Del Index %d\n", index);
+
+ btsnoop_unref(btsnoop_write_file[index]);
+ btsnoop_write_file[index] = NULL;
+ break;
+ default:
+ btsnoop_write_hci(btsnoop_write_file[index], &tv, index,
+ opcode, buf, pktlen);
+ }
+ num_packets++;
+
+ goto next_packet;
+
+close_files:
+ for (index = 0; index < max_index; index++)
+ btsnoop_unref(btsnoop_write_file[index]);
+
+ btsnoop_unref(btsnoop_read_file);
+
+ printf("BT Snoop data link transfer is completed for %lu packets\n",
+ num_packets);
+}
+
static void command_extract_eir(const char *input)
{
struct btsnoop_pkt pkt;
@@ -518,6 +600,7 @@ static void usage(void)
printf("commands:\n"
"\t-m, --merge <output> Merge multiple btsnoop files\n"
"\t-e, --extract <input> Extract data from btsnoop file\n"
+ "\t-s, --split <input> Split btmon file into legacy btsnoop file(s)\n"
"\t-h, --help Show help options\n");
}
@@ -525,12 +608,13 @@ static const struct option main_options[] = {
{ "merge", required_argument, NULL, 'm' },
{ "extract", required_argument, NULL, 'e' },
{ "type", required_argument, NULL, 't' },
+ { "split", required_argument, NULL, 's' },
{ "version", no_argument, NULL, 'v' },
{ "help", no_argument, NULL, 'h' },
{ }
};
-enum { INVALID, MERGE, EXTRACT };
+enum { INVALID, MERGE, EXTRACT, SPLIT };
int main(int argc, char *argv[])
{
@@ -542,7 +626,7 @@ int main(int argc, char *argv[])
for (;;) {
int opt;
- opt = getopt_long(argc, argv, "m:e:t:vh", main_options, NULL);
+ opt = getopt_long(argc, argv, "m:e:s:t:vh", main_options, NULL);
if (opt < 0)
break;
@@ -555,6 +639,9 @@ int main(int argc, char *argv[])
command = EXTRACT;
input_path = optarg;
break;
+ case 's':
+ command = SPLIT;
+ input_path = optarg;
case 't':
type = optarg;
break;
@@ -600,6 +687,15 @@ int main(int argc, char *argv[])
fprintf(stderr, "extract type not supported\n");
break;
+ case SPLIT:
+ if (argc - optind > 0) {
+ fprintf(stderr, "extra arguments not allowed\n");
+ return EXIT_FAILURE;
+ }
+
+ command_split(input_path);
+ break;
+
default:
usage();
return EXIT_FAILURE;
diff --git a/tools/mgmt-tester.c b/tools/mgmt-tester.c
index e5fb8894..d9970103 100644
--- a/tools/mgmt-tester.c
+++ b/tools/mgmt-tester.c
@@ -319,6 +319,25 @@ static void test_condition_complete(struct test_data *data)
test_post_teardown, 2, user, free); \
} while (0)
+#define test_bredr20(name, data, setup, func) \
+ do { \
+ struct test_data *user; \
+ user = malloc(sizeof(struct test_data)); \
+ if (!user) \
+ break; \
+ user->hciemu_type = HCIEMU_TYPE_LEGACY; \
+ user->test_setup = setup; \
+ user->test_data = data; \
+ user->expected_version = 0x04; \
+ user->expected_manufacturer = 0x003f; \
+ user->expected_supported_settings = 0x000010bf; \
+ user->initial_settings = 0x00000080; \
+ user->unmet_conditions = 0; \
+ tester_add_full(name, data, \
+ test_pre_setup, test_setup, func, NULL, \
+ test_post_teardown, 2, user, free); \
+ } while (0)
+
#define test_bredr(name, data, setup, func) \
do { \
struct test_data *user; \
@@ -378,6 +397,7 @@ struct generic_data {
uint16_t send_len;
const void * (*send_func)(uint16_t *len);
uint8_t expect_status;
+ bool expect_ignore_param;
const void *expect_param;
uint16_t expect_len;
const void * (*expect_func)(uint16_t *len);
@@ -477,6 +497,45 @@ static const struct generic_data read_info_invalid_index_test = {
.expect_status = MGMT_STATUS_INVALID_INDEX,
};
+static const struct generic_data read_unconf_index_list_invalid_param_test = {
+ .send_index_none = true,
+ .send_opcode = MGMT_OP_READ_UNCONF_INDEX_LIST,
+ .send_param = dummy_data,
+ .send_len = sizeof(dummy_data),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data read_unconf_index_list_invalid_index_test = {
+ .send_opcode = MGMT_OP_READ_UNCONF_INDEX_LIST,
+ .expect_status = MGMT_STATUS_INVALID_INDEX,
+};
+
+static const struct generic_data read_config_info_invalid_param_test = {
+ .send_opcode = MGMT_OP_READ_CONFIG_INFO,
+ .send_param = dummy_data,
+ .send_len = sizeof(dummy_data),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data read_config_info_invalid_index_test = {
+ .send_index_none = true,
+ .send_opcode = MGMT_OP_READ_CONFIG_INFO,
+ .expect_status = MGMT_STATUS_INVALID_INDEX,
+};
+
+static const struct generic_data read_ext_index_list_invalid_param_test = {
+ .send_index_none = true,
+ .send_opcode = MGMT_OP_READ_EXT_INDEX_LIST,
+ .send_param = dummy_data,
+ .send_len = sizeof(dummy_data),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data read_ext_index_list_invalid_index_test = {
+ .send_opcode = MGMT_OP_READ_EXT_INDEX_LIST,
+ .expect_status = MGMT_STATUS_INVALID_INDEX,
+};
+
static const char set_powered_on_param[] = { 0x01 };
static const char set_powered_invalid_param[] = { 0x02 };
static const char set_powered_garbage_param[] = { 0x01, 0x00 };
@@ -797,6 +856,12 @@ static const struct generic_data set_connectable_off_le_test_2 = {
.expect_hci_len = sizeof(set_connectable_off_adv_param),
};
+static uint16_t settings_powered_le_discoverable[] = {
+ MGMT_OP_SET_LE,
+ MGMT_OP_SET_CONNECTABLE,
+ MGMT_OP_SET_POWERED,
+ MGMT_OP_SET_DISCOVERABLE, 0 };
+
static uint16_t settings_powered_le_discoverable_advertising[] = {
MGMT_OP_SET_LE,
MGMT_OP_SET_CONNECTABLE,
@@ -835,6 +900,8 @@ static const struct generic_data set_connectable_off_le_test_4 = {
static const char set_fast_conn_on_param[] = { 0x01 };
static const char set_fast_conn_on_settings_1[] = { 0x87, 0x00, 0x00, 0x00 };
+static const char set_fast_conn_on_settings_2[] = { 0x85, 0x00, 0x00, 0x00 };
+static const char set_fast_conn_on_settings_3[] = { 0x84, 0x00, 0x00, 0x00 };
static const struct generic_data set_fast_conn_on_success_test_1 = {
.setup_settings = settings_powered_connectable,
@@ -847,6 +914,27 @@ static const struct generic_data set_fast_conn_on_success_test_1 = {
.expect_settings_set = MGMT_SETTING_FAST_CONNECTABLE,
};
+static const struct generic_data set_fast_conn_on_success_test_2 = {
+ .setup_settings = settings_powered,
+ .send_opcode = MGMT_OP_SET_FAST_CONNECTABLE,
+ .send_param = set_fast_conn_on_param,
+ .send_len = sizeof(set_fast_conn_on_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = set_fast_conn_on_settings_2,
+ .expect_len = sizeof(set_fast_conn_on_settings_2),
+ .expect_settings_set = MGMT_SETTING_FAST_CONNECTABLE,
+};
+
+static const struct generic_data set_fast_conn_on_success_test_3 = {
+ .send_opcode = MGMT_OP_SET_FAST_CONNECTABLE,
+ .send_param = set_fast_conn_on_param,
+ .send_len = sizeof(set_fast_conn_on_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = set_fast_conn_on_settings_3,
+ .expect_len = sizeof(set_fast_conn_on_settings_3),
+ .expect_settings_set = MGMT_SETTING_FAST_CONNECTABLE,
+};
+
static const struct generic_data set_fast_conn_on_not_supported_test_1 = {
.setup_settings = settings_powered_connectable,
.send_opcode = MGMT_OP_SET_FAST_CONNECTABLE,
@@ -855,6 +943,15 @@ static const struct generic_data set_fast_conn_on_not_supported_test_1 = {
.expect_status = MGMT_STATUS_NOT_SUPPORTED,
};
+static const char set_fast_conn_nval_param[] = { 0xff };
+
+static const struct generic_data set_fast_conn_nval_param_test_1 = {
+ .send_opcode = MGMT_OP_SET_FAST_CONNECTABLE,
+ .send_param = set_fast_conn_nval_param,
+ .send_len = sizeof(set_fast_conn_nval_param),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
static const char set_bondable_on_param[] = { 0x01 };
static const char set_bondable_invalid_param[] = { 0x02 };
static const char set_bondable_garbage_param[] = { 0x01, 0x00 };
@@ -1286,6 +1383,10 @@ static const struct generic_data set_ssp_on_invalid_index_test = {
static uint16_t settings_powered_ssp[] = { MGMT_OP_SET_SSP,
MGMT_OP_SET_POWERED, 0 };
+static uint16_t settings_powered_sc[] = { MGMT_OP_SET_SSP,
+ MGMT_OP_SET_SECURE_CONN,
+ MGMT_OP_SET_POWERED, 0 };
+
static const char set_sc_on_param[] = { 0x01 };
static const char set_sc_only_on_param[] = { 0x02 };
static const char set_sc_invalid_param[] = { 0x03 };
@@ -3748,6 +3849,650 @@ static const struct generic_data remove_device_success_5 = {
.expect_hci_len = sizeof(set_le_scan_off),
};
+static const struct generic_data read_adv_features_invalid_param_test = {
+ .send_opcode = MGMT_OP_READ_ADV_FEATURES,
+ .send_param = dummy_data,
+ .send_len = sizeof(dummy_data),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data read_adv_features_invalid_index_test = {
+ .send_index_none = true,
+ .send_opcode = MGMT_OP_READ_ADV_FEATURES,
+ .expect_status = MGMT_STATUS_INVALID_INDEX,
+};
+
+static const uint8_t add_advertising_param_1[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0x03, 0x02, 0x0d, 0x18,
+ 0x04, 0xff, 0x01, 0x02, 0x03,
+};
+
+static const uint8_t add_advertising_param_2[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x0a,
+ 0x03, 0x02, 0x0d, 0x18,
+ 0x04, 0xff, 0x01, 0x02, 0x03,
+ 0x03, 0x19, 0x40, 0x03,
+ 0x05, 0x03, 0x0d, 0x18, 0x0f, 0x18,
+};
+
+static const uint8_t add_advertising_param_3[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0x00, 0x09, 0x00,
+ 0x03, 0x02, 0x0d, 0x18,
+ 0x04, 0xff, 0x01, 0x02, 0x03,
+};
+
+static const uint8_t add_advertising_param_4[] = {
+ 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0x03, 0x02, 0x0d, 0x18,
+ 0x04, 0xff, 0x01, 0x02, 0x03,
+};
+
+static const uint8_t add_advertising_param_5[] = {
+ 0x01, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0x03, 0x02, 0x0d, 0x18,
+ 0x04, 0xff, 0x01, 0x02, 0x03,
+};
+
+static const uint8_t add_advertising_param_6[] = {
+ 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0x03, 0x02, 0x0d, 0x18,
+ 0x04, 0xff, 0x01, 0x02, 0x03,
+};
+
+static const uint8_t add_advertising_param_7[] = {
+ 0x01, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0x03, 0x02, 0x0d, 0x18,
+ 0x04, 0xff, 0x01, 0x02, 0x03,
+};
+
+static const uint8_t add_advertising_param_8[] = {
+ 0x01, 0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0x03, 0x02, 0x0d, 0x18,
+ 0x04, 0xff, 0x01, 0x02, 0x03,
+};
+
+static const uint8_t advertising_instance_param[] = {
+ 0x01,
+};
+
+static const uint8_t set_adv_data_1[] = {
+ 0x09, 0x03, 0x02, 0x0d, 0x18, 0x04, 0xff, 0x01, 0x02, 0x03,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+};
+
+static const uint8_t set_adv_data_2[] = {
+ 0x0c, 0x02, 0x01, 0x04, 0x03, 0x03, 0x0d, 0x18, 0x04, 0xff,
+ 0x01, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+};
+
+static const uint8_t set_adv_data_3[] = {
+ 0x06, 0x05, 0x08, 0x74, 0x65, 0x73, 0x74, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+};
+
+static const uint8_t set_adv_data_4[] = {
+ 0x03, 0x02, 0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+};
+
+static const uint8_t set_adv_data_5[] = {
+ 0x0c, 0x02, 0x01, 0x02, 0x03, 0x02, 0x0d, 0x18, 0x04, 0xff,
+ 0x01, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+};
+
+static const uint8_t set_adv_data_6[] = {
+ 0x0c, 0x02, 0x01, 0x01, 0x03, 0x02, 0x0d, 0x18, 0x04, 0xff,
+ 0x01, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+};
+
+static const uint8_t set_adv_data_7[] = {
+ 0x0c, 0x02, 0x01, 0x02, 0x03, 0x02, 0x0d, 0x18, 0x04, 0xff,
+ 0x01, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+};
+
+static const uint8_t set_adv_data_8[] = {
+ 0x0c, 0x02, 0x0a, 0x00, 0x03, 0x02, 0x0d, 0x18, 0x04, 0xff,
+ 0x01, 0x02, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+};
+
+static const uint8_t set_scan_rsp_1[] = {
+ 0x0a, 0x03, 0x19, 0x40, 0x03, 0x05, 0x03, 0x0d, 0x18, 0x0f,
+ 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00,
+};
+
+static const uint8_t add_advertising_invalid_param_1[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x00,
+ 0x03, 0x03, 0x0d, 0x18,
+ 0x19, 0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+ 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
+ 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e,
+};
+
+static const uint8_t add_advertising_invalid_param_2[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0x04, 0x03, 0x0d, 0x18,
+ 0x04, 0xff, 0x01, 0x02, 0x03,
+};
+
+static const uint8_t add_advertising_invalid_param_3[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0x03, 0x03, 0x0d, 0x18,
+ 0x02, 0xff, 0x01, 0x02, 0x03,
+};
+
+static const uint8_t add_advertising_invalid_param_4[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00,
+ 0x03, 0x03, 0x0d, 0x18,
+ 0x05, 0xff, 0x01, 0x02, 0x03,
+};
+
+static const uint8_t add_advertising_invalid_param_5[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1D, 0x00,
+ 0x03, 0x03, 0x0d, 0x18,
+ 0x19, 0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+ 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
+ 0x15, 0x16, 0x17, 0x18,
+};
+
+static const uint8_t add_advertising_invalid_param_6[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20,
+ 0x03, 0x03, 0x0d, 0x18,
+ 0x19, 0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+ 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
+ 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e,
+};
+
+static const uint8_t add_advertising_invalid_param_7[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09,
+ 0x04, 0x03, 0x0d, 0x18,
+ 0x04, 0xff, 0x01, 0x02, 0x03,
+};
+
+static const uint8_t add_advertising_invalid_param_8[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09,
+ 0x03, 0x03, 0x0d, 0x18,
+ 0x02, 0xff, 0x01, 0x02, 0x03,
+};
+
+static const uint8_t add_advertising_invalid_param_9[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09,
+ 0x03, 0x03, 0x0d, 0x18,
+ 0x05, 0xff, 0x01, 0x02, 0x03,
+};
+
+static const uint8_t add_advertising_invalid_param_10[] = {
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1D,
+ 0x03, 0x03, 0x0d, 0x18,
+ 0x19, 0xff, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
+ 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
+ 0x15, 0x16, 0x17, 0x18,
+};
+
+static const struct generic_data add_advertising_fail_1 = {
+ .setup_settings = settings_powered,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_1,
+ .send_len = sizeof(add_advertising_param_1),
+ .expect_status = MGMT_STATUS_REJECTED,
+};
+
+static const struct generic_data add_advertising_fail_2 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_invalid_param_1,
+ .send_len = sizeof(add_advertising_invalid_param_1),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data add_advertising_fail_3 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_invalid_param_2,
+ .send_len = sizeof(add_advertising_invalid_param_2),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data add_advertising_fail_4 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_invalid_param_3,
+ .send_len = sizeof(add_advertising_invalid_param_3),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data add_advertising_fail_5 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_invalid_param_4,
+ .send_len = sizeof(add_advertising_invalid_param_4),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data add_advertising_fail_6 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_invalid_param_5,
+ .send_len = sizeof(add_advertising_invalid_param_5),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data add_advertising_fail_7 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_invalid_param_6,
+ .send_len = sizeof(add_advertising_invalid_param_6),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data add_advertising_fail_8 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_invalid_param_7,
+ .send_len = sizeof(add_advertising_invalid_param_7),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data add_advertising_fail_9 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_invalid_param_8,
+ .send_len = sizeof(add_advertising_invalid_param_8),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data add_advertising_fail_10 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_invalid_param_9,
+ .send_len = sizeof(add_advertising_invalid_param_9),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data add_advertising_fail_11 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_invalid_param_10,
+ .send_len = sizeof(add_advertising_invalid_param_10),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data add_advertising_fail_12 = {
+ .setup_settings = settings_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_3,
+ .send_len = sizeof(add_advertising_param_3),
+ .expect_status = MGMT_STATUS_REJECTED,
+};
+
+static const struct generic_data add_advertising_success_1 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_1,
+ .send_len = sizeof(add_advertising_param_1),
+ .expect_param = advertising_instance_param,
+ .expect_len = sizeof(advertising_instance_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_alt_ev = MGMT_EV_ADVERTISING_ADDED,
+ .expect_alt_ev_param = advertising_instance_param,
+ .expect_alt_ev_len = sizeof(advertising_instance_param),
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA,
+ .expect_hci_param = set_adv_data_1,
+ .expect_hci_len = sizeof(set_adv_data_1),
+};
+
+static const char set_powered_adv_instance_settings_param[] = {
+ 0x81, 0x02, 0x00, 0x00,
+};
+
+static const struct generic_data add_advertising_success_2 = {
+ .send_opcode = MGMT_OP_SET_POWERED,
+ .send_param = set_powered_on_param,
+ .send_len = sizeof(set_powered_on_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = set_powered_adv_instance_settings_param,
+ .expect_len = sizeof(set_powered_adv_instance_settings_param),
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA,
+ .expect_hci_param = set_adv_data_3,
+ .expect_hci_len = sizeof(set_adv_data_3),
+};
+
+static const struct generic_data add_advertising_success_3 = {
+ .send_opcode = MGMT_OP_SET_POWERED,
+ .send_param = set_powered_on_param,
+ .send_len = sizeof(set_powered_on_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = set_powered_adv_instance_settings_param,
+ .expect_len = sizeof(set_powered_adv_instance_settings_param),
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_ENABLE,
+ .expect_hci_param = set_adv_on_set_adv_enable_param,
+ .expect_hci_len = sizeof(set_adv_on_set_adv_enable_param),
+};
+
+static const struct generic_data add_advertising_success_4 = {
+ .send_opcode = MGMT_OP_SET_ADVERTISING,
+ .send_param = set_adv_on_param,
+ .send_len = sizeof(set_adv_on_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = set_adv_settings_param_2,
+ .expect_len = sizeof(set_adv_settings_param_2),
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA,
+ .expect_hci_param = set_adv_data_4,
+ .expect_hci_len = sizeof(set_adv_data_4),
+};
+
+static const char set_adv_off_param[] = { 0x00 };
+
+static const struct generic_data add_advertising_success_5 = {
+ .send_opcode = MGMT_OP_SET_ADVERTISING,
+ .send_param = set_adv_off_param,
+ .send_len = sizeof(set_adv_off_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = set_powered_adv_instance_settings_param,
+ .expect_len = sizeof(set_powered_adv_instance_settings_param),
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA,
+ .expect_hci_param = set_adv_data_3,
+ .expect_hci_len = sizeof(set_adv_data_3),
+};
+
+static const struct generic_data add_advertising_success_6 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_2,
+ .send_len = sizeof(add_advertising_param_2),
+ .expect_param = advertising_instance_param,
+ .expect_len = sizeof(advertising_instance_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_alt_ev = MGMT_EV_ADVERTISING_ADDED,
+ .expect_alt_ev_param = advertising_instance_param,
+ .expect_alt_ev_len = sizeof(advertising_instance_param),
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA,
+ .expect_hci_param = set_adv_data_1,
+ .expect_hci_len = sizeof(set_adv_data_1),
+};
+
+static const struct generic_data add_advertising_success_7 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_2,
+ .send_len = sizeof(add_advertising_param_2),
+ .expect_param = advertising_instance_param,
+ .expect_len = sizeof(advertising_instance_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_alt_ev = MGMT_EV_ADVERTISING_ADDED,
+ .expect_alt_ev_param = advertising_instance_param,
+ .expect_alt_ev_len = sizeof(advertising_instance_param),
+ .expect_hci_command = BT_HCI_CMD_LE_SET_SCAN_RSP_DATA,
+ .expect_hci_param = set_scan_rsp_1,
+ .expect_hci_len = sizeof(set_scan_rsp_1),
+};
+
+static const struct generic_data add_advertising_success_8 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_4,
+ .send_len = sizeof(add_advertising_param_4),
+ .expect_param = advertising_instance_param,
+ .expect_len = sizeof(advertising_instance_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_PARAMETERS,
+ .expect_hci_param = set_connectable_on_adv_param,
+ .expect_hci_len = sizeof(set_connectable_on_adv_param),
+};
+
+static const struct generic_data add_advertising_success_9 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_5,
+ .send_len = sizeof(add_advertising_param_5),
+ .expect_param = advertising_instance_param,
+ .expect_len = sizeof(advertising_instance_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA,
+ .expect_hci_param = set_adv_data_5,
+ .expect_hci_len = sizeof(set_adv_data_5),
+};
+
+static const struct generic_data add_advertising_success_10 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_6,
+ .send_len = sizeof(add_advertising_param_6),
+ .expect_param = advertising_instance_param,
+ .expect_len = sizeof(advertising_instance_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA,
+ .expect_hci_param = set_adv_data_6,
+ .expect_hci_len = sizeof(set_adv_data_6),
+};
+
+static const struct generic_data add_advertising_success_11 = {
+ .setup_settings = settings_powered_le_discoverable,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_7,
+ .send_len = sizeof(add_advertising_param_7),
+ .expect_param = advertising_instance_param,
+ .expect_len = sizeof(advertising_instance_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA,
+ .expect_hci_param = set_adv_data_7,
+ .expect_hci_len = sizeof(set_adv_data_7),
+};
+
+static const struct generic_data add_advertising_success_12 = {
+ .setup_settings = settings_powered_le_discoverable,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_8,
+ .send_len = sizeof(add_advertising_param_8),
+ .expect_param = advertising_instance_param,
+ .expect_len = sizeof(advertising_instance_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_DATA,
+ .expect_hci_param = set_adv_data_8,
+ .expect_hci_len = sizeof(set_adv_data_8),
+};
+
+static uint16_t settings_powered_le_connectable[] = {
+ MGMT_OP_SET_POWERED,
+ MGMT_OP_SET_LE,
+ MGMT_OP_SET_CONNECTABLE, 0 };
+
+static uint8_t set_connectable_off_scan_adv_param[] = {
+ 0x00, 0x08, /* min_interval */
+ 0x00, 0x08, /* max_interval */
+ 0x02, /* type */
+ 0x01, /* own_addr_type */
+ 0x00, /* direct_addr_type */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* direct_addr */
+ 0x07, /* channel_map */
+ 0x00, /* filter_policy */
+};
+
+static const struct generic_data add_advertising_success_13 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_2,
+ .send_len = sizeof(add_advertising_param_2),
+ .expect_param = advertising_instance_param,
+ .expect_len = sizeof(advertising_instance_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_PARAMETERS,
+ .expect_hci_param = set_connectable_off_scan_adv_param,
+ .expect_hci_len = sizeof(set_connectable_off_scan_adv_param),
+};
+
+static const struct generic_data add_advertising_success_14 = {
+ .setup_settings = settings_powered_le,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_1,
+ .send_len = sizeof(add_advertising_param_1),
+ .expect_param = advertising_instance_param,
+ .expect_len = sizeof(advertising_instance_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_PARAMETERS,
+ .expect_hci_param = set_connectable_off_adv_param,
+ .expect_hci_len = sizeof(set_connectable_off_adv_param),
+};
+
+static const struct generic_data add_advertising_success_15 = {
+ .setup_settings = settings_powered_le_connectable,
+ .send_opcode = MGMT_OP_ADD_ADVERTISING,
+ .send_param = add_advertising_param_1,
+ .send_len = sizeof(add_advertising_param_1),
+ .expect_param = advertising_instance_param,
+ .expect_len = sizeof(advertising_instance_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_PARAMETERS,
+ .expect_hci_param = set_connectable_on_adv_param,
+ .expect_hci_len = sizeof(set_connectable_on_adv_param),
+};
+
+static const char set_connectable_settings_param_3[] = {
+ 0x83, 0x02, 0x00, 0x00 };
+
+static const struct generic_data add_advertising_success_16 = {
+ .send_opcode = MGMT_OP_SET_CONNECTABLE,
+ .send_param = set_connectable_on_param,
+ .send_len = sizeof(set_connectable_on_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = set_connectable_settings_param_3,
+ .expect_len = sizeof(set_connectable_settings_param_3),
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_PARAMETERS,
+ .expect_hci_param = set_connectable_on_adv_param,
+ .expect_hci_len = sizeof(set_connectable_on_adv_param),
+};
+
+static const struct generic_data add_advertising_success_17 = {
+ .send_opcode = MGMT_OP_SET_CONNECTABLE,
+ .send_param = set_connectable_off_param,
+ .send_len = sizeof(set_connectable_off_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = set_le_settings_param_2,
+ .expect_len = sizeof(set_le_settings_param_2),
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_PARAMETERS,
+ .expect_hci_param = set_connectable_off_adv_param,
+ .expect_hci_len = sizeof(set_connectable_off_adv_param),
+};
+
+static const char set_powered_off_le_settings_param[] = {
+ 0x80, 0x02, 0x00, 0x00
+};
+
+static const struct generic_data add_advertising_timeout_power_off = {
+ .send_opcode = MGMT_OP_SET_POWERED,
+ .send_param = set_powered_off_param,
+ .send_len = sizeof(set_powered_off_param),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = set_powered_off_le_settings_param,
+ .expect_len = sizeof(set_powered_off_le_settings_param),
+ .expect_alt_ev = MGMT_EV_ADVERTISING_REMOVED,
+ .expect_alt_ev_param = advertising_instance_param,
+ .expect_alt_ev_len = sizeof(advertising_instance_param),
+};
+
+static const uint8_t remove_advertising_param_1[] = {
+ 0x01,
+};
+
+static const uint8_t remove_advertising_param_2[] = {
+ 0x00,
+};
+
+static const struct generic_data remove_advertising_fail_1 = {
+ .send_opcode = MGMT_OP_REMOVE_ADVERTISING,
+ .send_param = remove_advertising_param_1,
+ .send_len = sizeof(remove_advertising_param_1),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data remove_advertising_success_1 = {
+ .send_opcode = MGMT_OP_REMOVE_ADVERTISING,
+ .send_param = remove_advertising_param_1,
+ .send_len = sizeof(remove_advertising_param_1),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = remove_advertising_param_1,
+ .expect_len = sizeof(remove_advertising_param_1),
+ .expect_alt_ev = MGMT_EV_ADVERTISING_REMOVED,
+ .expect_alt_ev_param = advertising_instance_param,
+ .expect_alt_ev_len = sizeof(advertising_instance_param),
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_ENABLE,
+ .expect_hci_param = set_adv_off_param,
+ .expect_hci_len = sizeof(set_adv_off_param),
+};
+
+static const struct generic_data remove_advertising_success_2 = {
+ .send_opcode = MGMT_OP_REMOVE_ADVERTISING,
+ .send_param = remove_advertising_param_2,
+ .send_len = sizeof(remove_advertising_param_2),
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_param = remove_advertising_param_1,
+ .expect_len = sizeof(remove_advertising_param_1),
+ .expect_alt_ev = MGMT_EV_ADVERTISING_REMOVED,
+ .expect_alt_ev_param = advertising_instance_param,
+ .expect_alt_ev_len = sizeof(advertising_instance_param),
+ .expect_hci_command = BT_HCI_CMD_LE_SET_ADV_ENABLE,
+ .expect_hci_param = set_adv_off_param,
+ .expect_hci_len = sizeof(set_adv_off_param),
+};
+
+static const struct generic_data read_local_oob_not_powered_test = {
+ .send_opcode = MGMT_OP_READ_LOCAL_OOB_DATA,
+ .expect_status = MGMT_STATUS_NOT_POWERED,
+};
+
+static const struct generic_data read_local_oob_invalid_param_test = {
+ .send_opcode = MGMT_OP_READ_LOCAL_OOB_DATA,
+ .send_param = dummy_data,
+ .send_len = sizeof(dummy_data),
+ .expect_status = MGMT_STATUS_INVALID_PARAMS,
+};
+
+static const struct generic_data read_local_oob_invalid_index_test = {
+ .send_index_none = true,
+ .send_opcode = MGMT_OP_READ_LOCAL_OOB_DATA,
+ .expect_status = MGMT_STATUS_INVALID_INDEX,
+};
+
+static const struct generic_data read_local_oob_legacy_pairing_test = {
+ .setup_settings = settings_powered,
+ .send_opcode = MGMT_OP_READ_LOCAL_OOB_DATA,
+ .expect_status = MGMT_STATUS_NOT_SUPPORTED,
+};
+
+static const struct generic_data read_local_oob_success_ssp_test = {
+ .setup_settings = settings_powered_ssp,
+ .send_opcode = MGMT_OP_READ_LOCAL_OOB_DATA,
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_ignore_param = true,
+ .expect_hci_command = BT_HCI_CMD_READ_LOCAL_OOB_DATA,
+};
+
+static const struct generic_data read_local_oob_success_sc_test = {
+ .setup_settings = settings_powered_sc,
+ .send_opcode = MGMT_OP_READ_LOCAL_OOB_DATA,
+ .expect_status = MGMT_STATUS_SUCCESS,
+ .expect_ignore_param = true,
+ .expect_hci_command = BT_HCI_CMD_READ_LOCAL_OOB_EXT_DATA,
+};
+
static void client_cmd_complete(uint16_t opcode, uint8_t status,
const void *param, uint8_t len,
void *user_data)
@@ -4098,6 +4843,199 @@ static void setup_add_device(const void *test_data)
setup_powered_callback, NULL, NULL);
}
+static void setup_add_advertising_callback(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ if (status != MGMT_STATUS_SUCCESS) {
+ tester_setup_failed();
+ return;
+ }
+
+ tester_print("Add Advertising setup complete");
+
+ setup_bthost();
+}
+
+static void setup_add_advertising_not_powered(const void *test_data)
+{
+ struct test_data *data = tester_get_data();
+ struct mgmt_cp_add_advertising *cp;
+ unsigned char adv_param[sizeof(*cp) + 6];
+ unsigned char param[] = { 0x01 };
+
+ tester_print("Adding advertising instance while unpowered");
+
+ cp = (struct mgmt_cp_add_advertising *) adv_param;
+ memset(cp, 0, sizeof(*cp));
+
+ cp->instance = 1;
+ cp->adv_data_len = 6;
+ cp->data[0] = 0x05;
+ cp->data[1] = 0x08;
+ cp->data[2] = 't';
+ cp->data[3] = 'e';
+ cp->data[4] = 's';
+ cp->data[5] = 't';
+
+ mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index,
+ sizeof(param), &param,
+ NULL, NULL, NULL);
+
+ mgmt_send(data->mgmt, MGMT_OP_ADD_ADVERTISING, data->mgmt_index,
+ sizeof(adv_param), adv_param,
+ setup_add_advertising_callback,
+ NULL, NULL);
+}
+
+static void setup_add_advertising(const void *test_data)
+{
+ struct test_data *data = tester_get_data();
+ struct mgmt_cp_add_advertising *cp;
+ unsigned char adv_param[sizeof(*cp) + 6];
+ unsigned char param[] = { 0x01 };
+
+ tester_print("Adding advertising instance while unpowered");
+
+ cp = (struct mgmt_cp_add_advertising *) adv_param;
+ memset(cp, 0, sizeof(*cp));
+
+ cp->instance = 1;
+ cp->adv_data_len = 6;
+ cp->data[0] = 0x05;
+ cp->data[1] = 0x08;
+ cp->data[2] = 't';
+ cp->data[3] = 'e';
+ cp->data[4] = 's';
+ cp->data[5] = 't';
+
+ mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index,
+ sizeof(param), &param,
+ NULL, NULL, NULL);
+
+ mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
+ sizeof(param), &param,
+ NULL, NULL, NULL);
+
+ mgmt_send(data->mgmt, MGMT_OP_ADD_ADVERTISING, data->mgmt_index,
+ sizeof(adv_param), adv_param,
+ setup_add_advertising_callback,
+ NULL, NULL);
+}
+
+static void setup_add_advertising_connectable(const void *test_data)
+{
+ struct test_data *data = tester_get_data();
+ struct mgmt_cp_add_advertising *cp;
+ unsigned char adv_param[sizeof(*cp) + 6];
+ unsigned char param[] = { 0x01 };
+
+ tester_print("Adding advertising instance while unpowered");
+
+ cp = (struct mgmt_cp_add_advertising *) adv_param;
+ memset(cp, 0, sizeof(*cp));
+
+ cp->instance = 1;
+ cp->adv_data_len = 6;
+ cp->data[0] = 0x05;
+ cp->data[1] = 0x08;
+ cp->data[2] = 't';
+ cp->data[3] = 'e';
+ cp->data[4] = 's';
+ cp->data[5] = 't';
+
+ mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index,
+ sizeof(param), &param,
+ NULL, NULL, NULL);
+
+ mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
+ sizeof(param), &param,
+ NULL, NULL, NULL);
+
+ mgmt_send(data->mgmt, MGMT_OP_SET_CONNECTABLE, data->mgmt_index,
+ sizeof(param), &param,
+ NULL, NULL, NULL);
+
+ mgmt_send(data->mgmt, MGMT_OP_ADD_ADVERTISING, data->mgmt_index,
+ sizeof(adv_param), adv_param,
+ setup_add_advertising_callback,
+ NULL, NULL);
+}
+
+static void setup_add_advertising_timeout(const void *test_data)
+{
+ struct test_data *data = tester_get_data();
+ struct mgmt_cp_add_advertising *cp;
+ unsigned char adv_param[sizeof(*cp) + 6];
+ unsigned char param[] = { 0x01 };
+
+ tester_print("Adding advertising instance while unpowered");
+
+ cp = (struct mgmt_cp_add_advertising *) adv_param;
+ memset(cp, 0, sizeof(*cp));
+
+ cp->instance = 1;
+ cp->timeout = 5;
+ cp->adv_data_len = 6;
+ cp->data[0] = 0x05;
+ cp->data[1] = 0x08;
+ cp->data[2] = 't';
+ cp->data[3] = 'e';
+ cp->data[4] = 's';
+ cp->data[5] = 't';
+
+ mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index,
+ sizeof(param), &param,
+ NULL, NULL, NULL);
+
+ mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
+ sizeof(param), &param,
+ NULL, NULL, NULL);
+
+ mgmt_send(data->mgmt, MGMT_OP_ADD_ADVERTISING, data->mgmt_index,
+ sizeof(adv_param), adv_param,
+ setup_add_advertising_callback,
+ NULL, NULL);
+}
+
+static void setup_set_and_add_advertising(const void *test_data)
+{
+ struct test_data *data = tester_get_data();
+ struct mgmt_cp_add_advertising *cp;
+ unsigned char adv_param[sizeof(*cp) + 6];
+ unsigned char param[] = { 0x01 };
+
+ tester_print("Adding advertising instance while unpowered");
+
+ cp = (struct mgmt_cp_add_advertising *) adv_param;
+ memset(cp, 0, sizeof(*cp));
+
+ cp->instance = 1;
+ cp->adv_data_len = 6;
+ cp->data[0] = 0x05;
+ cp->data[1] = 0x08;
+ cp->data[2] = 't';
+ cp->data[3] = 'e';
+ cp->data[4] = 's';
+ cp->data[5] = 't';
+
+ mgmt_send(data->mgmt, MGMT_OP_SET_LE, data->mgmt_index,
+ sizeof(param), &param,
+ NULL, NULL, NULL);
+
+ mgmt_send(data->mgmt, MGMT_OP_SET_POWERED, data->mgmt_index,
+ sizeof(param), &param,
+ NULL, NULL, NULL);
+
+ mgmt_send(data->mgmt, MGMT_OP_SET_ADVERTISING, data->mgmt_index,
+ sizeof(param), &param,
+ NULL, NULL, NULL);
+
+ mgmt_send(data->mgmt, MGMT_OP_ADD_ADVERTISING, data->mgmt_index,
+ sizeof(adv_param), adv_param,
+ setup_add_advertising_callback,
+ NULL, NULL);
+}
+
static void setup_complete(uint8_t status, uint16_t length,
const void *param, void *user_data)
{
@@ -4402,18 +5340,20 @@ static void command_generic_callback(uint8_t status, uint16_t length,
return;
}
- if (test->expect_func)
- expect_param = test->expect_func(&expect_len);
+ if (!test->expect_ignore_param) {
+ if (test->expect_func)
+ expect_param = test->expect_func(&expect_len);
- if (length != expect_len) {
- tester_test_failed();
- return;
- }
+ if (length != expect_len) {
+ tester_test_failed();
+ return;
+ }
- if (expect_param && expect_len > 0 &&
- memcmp(param, expect_param, length)) {
- tester_test_failed();
- return;
+ if (expect_param && expect_len > 0 &&
+ memcmp(param, expect_param, length)) {
+ tester_test_failed();
+ return;
+ }
}
test_condition_complete(data);
@@ -4679,6 +5619,24 @@ int main(int argc, char *argv[])
test_bredrle("Read info - Invalid index",
&read_info_invalid_index_test,
NULL, test_command_generic);
+ test_bredrle("Read unconfigured index list - Invalid parameters",
+ &read_unconf_index_list_invalid_param_test,
+ NULL, test_command_generic);
+ test_bredrle("Read unconfigured index list - Invalid index",
+ &read_unconf_index_list_invalid_index_test,
+ NULL, test_command_generic);
+ test_bredrle("Read configuration info - Invalid parameters",
+ &read_config_info_invalid_param_test,
+ NULL, test_command_generic);
+ test_bredrle("Read configuration info - Invalid index",
+ &read_config_info_invalid_index_test,
+ NULL, test_command_generic);
+ test_bredrle("Read extended index list - Invalid parameters",
+ &read_ext_index_list_invalid_param_test,
+ NULL, test_command_generic);
+ test_bredrle("Read extended index list - Invalid index",
+ &read_ext_index_list_invalid_index_test,
+ NULL, test_command_generic);
test_bredrle("Set powered on - Success",
&set_powered_on_success_test,
@@ -4770,6 +5728,15 @@ int main(int argc, char *argv[])
test_bredrle("Set fast connectable on - Success 1",
&set_fast_conn_on_success_test_1,
NULL, test_command_generic);
+ test_bredrle("Set fast connectable on - Success 2",
+ &set_fast_conn_on_success_test_2,
+ NULL, test_command_generic);
+ test_bredrle("Set fast connectable on - Success 3",
+ &set_fast_conn_on_success_test_3,
+ NULL, test_command_generic);
+ test_bredrle("Set fast connectable on - Invalid Params 1",
+ &set_fast_conn_nval_param_test_1,
+ NULL, test_command_generic);
test_le("Set fast connectable on - Not Supported 1",
&set_fast_conn_on_not_supported_test_1,
NULL, test_command_generic);
@@ -5411,5 +6378,141 @@ int main(int argc, char *argv[])
&remove_device_success_5,
setup_add_device, test_command_generic);
+ test_bredrle("Read Advertising Features - Invalid parameters",
+ &read_adv_features_invalid_param_test,
+ NULL, test_command_generic);
+ test_bredrle("Read Advertising Features - Invalid index",
+ &read_adv_features_invalid_index_test,
+ NULL, test_command_generic);
+
+ test_bredrle("Add Advertising - Failure: LE off",
+ &add_advertising_fail_1,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Invalid Params 1 (AD too long)",
+ &add_advertising_fail_2,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Invalid Params 2 (Malformed len)",
+ &add_advertising_fail_3,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Invalid Params 3 (Malformed len)",
+ &add_advertising_fail_4,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Invalid Params 4 (Malformed len)",
+ &add_advertising_fail_5,
+ NULL, test_command_generic);
+ test_le("Add Advertising - Invalid Params 5 (AD too long)",
+ &add_advertising_fail_6,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Invalid Params 6 (ScRsp too long)",
+ &add_advertising_fail_7,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Invalid Params 7 (Malformed len)",
+ &add_advertising_fail_8,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Invalid Params 8 (Malformed len)",
+ &add_advertising_fail_9,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Invalid Params 9 (Malformed len)",
+ &add_advertising_fail_10,
+ NULL, test_command_generic);
+ test_le("Add Advertising - Invalid Params 10 (ScRsp too long)",
+ &add_advertising_fail_11,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Timeout Not Powered",
+ &add_advertising_fail_12,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Timeout Power off",
+ &add_advertising_timeout_power_off,
+ setup_add_advertising_timeout,
+ test_command_generic);
+ test_bredrle("Add Advertising - Success 1",
+ &add_advertising_success_1,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Success 2",
+ &add_advertising_success_2,
+ setup_add_advertising_not_powered,
+ test_command_generic);
+ test_bredrle("Add Advertising - Success 3",
+ &add_advertising_success_3,
+ setup_add_advertising_not_powered,
+ test_command_generic);
+ test_bredrle("Add Advertising - Set Advertising on override 1",
+ &add_advertising_success_4,
+ setup_add_advertising,
+ test_command_generic);
+ test_bredrle("Add Advertising - Set Advertising off override 2",
+ &add_advertising_success_5,
+ setup_set_and_add_advertising,
+ test_command_generic);
+ test_bredrle("Add Advertising - Success 4",
+ &add_advertising_success_6,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Success 5",
+ &add_advertising_success_7,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Success 6 - Flag 0",
+ &add_advertising_success_8,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Success 7 - Flag 1",
+ &add_advertising_success_9,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Success 8 - Flag 2",
+ &add_advertising_success_10,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Success 8 - Flag 3",
+ &add_advertising_success_11,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Success 9 - Flag 4",
+ &add_advertising_success_12,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Success 10 - ADV_SCAN_IND",
+ &add_advertising_success_13,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Success 11 - ADV_NONCONN_IND",
+ &add_advertising_success_14,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Success 12 - ADV_IND",
+ &add_advertising_success_15,
+ NULL, test_command_generic);
+ test_bredrle("Add Advertising - Success 13 - connectable -> on",
+ &add_advertising_success_16,
+ setup_add_advertising,
+ test_command_generic);
+ test_bredrle("Add Advertising - Success 14 - connectable -> off",
+ &add_advertising_success_17,
+ setup_add_advertising_connectable,
+ test_command_generic);
+
+ test_bredrle("Remove Advertising - Invalid Params 1",
+ &remove_advertising_fail_1,
+ NULL, test_command_generic);
+ test_bredrle("Remove Advertising - Success 1",
+ &remove_advertising_success_1,
+ setup_add_advertising,
+ test_command_generic);
+ test_bredrle("Remove Advertising - Success 2",
+ &remove_advertising_success_2,
+ setup_add_advertising,
+ test_command_generic);
+
+ test_bredrle("Read Local OOB Data - Not powered",
+ &read_local_oob_not_powered_test,
+ NULL, test_command_generic);
+ test_bredrle("Read Local OOB Data - Invalid parameters",
+ &read_local_oob_invalid_param_test,
+ NULL, test_command_generic);
+ test_bredrle("Read Local OOB Data - Invalid index",
+ &read_local_oob_invalid_index_test,
+ NULL, test_command_generic);
+ test_bredr20("Read Local OOB Data - Legacy pairing",
+ &read_local_oob_legacy_pairing_test,
+ NULL, test_command_generic);
+ test_bredrle("Read Local OOB Data - Success SSP",
+ &read_local_oob_success_ssp_test,
+ NULL, test_command_generic);
+ test_bredrle("Read Local OOB Data - Success SC",
+ &read_local_oob_success_sc_test,
+ NULL, test_command_generic);
+
return tester_run();
}
diff --git a/tools/mpris-proxy.c b/tools/mpris-proxy.c
index bf8148fe..693055ed 100644
--- a/tools/mpris-proxy.c
+++ b/tools/mpris-proxy.c
@@ -406,7 +406,7 @@ static DBusHandlerResult player_message(DBusConnection *conn,
done:
dbus_message_unref(copy);
- return DBUS_HANDLER_RESULT_HANDLED;
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
}
static struct player *find_player_by_bus_name(const char *name)
diff --git a/tools/obexctl.c b/tools/obexctl.c
index 86c81d57..4faff6b6 100644
--- a/tools/obexctl.c
+++ b/tools/obexctl.c
@@ -758,7 +758,7 @@ static void send_reply(DBusMessage *message, void *user_data)
dbus_error_init(&error);
if (dbus_set_error_from_message(&error, message) == TRUE) {
- rl_printf("Failed to send/pull: %s\n", error.name);
+ rl_printf("Failed to send: %s\n", error.name);
dbus_error_free(&error);
return;
}
@@ -792,23 +792,6 @@ static void opp_send(GDBusProxy *proxy, int argc, char *argv[])
g_dbus_proxy_get_path(proxy));
}
-static void opp_pull(GDBusProxy *proxy, int argc, char *argv[])
-{
- if (argc < 2) {
- rl_printf("Missing file argument\n");
- return;
- }
-
- if (g_dbus_proxy_method_call(proxy, "PullBusinessCard", send_setup,
- send_reply, g_strdup(argv[1]), g_free) == FALSE) {
- rl_printf("Failed to pull\n");
- return;
- }
-
- rl_printf("Attempting to pull %s from %s\n", argv[1],
- g_dbus_proxy_get_path(proxy));
-}
-
static void push_reply(DBusMessage *message, void *user_data)
{
DBusMessageIter iter;
@@ -886,22 +869,6 @@ static void cmd_send(int argc, char *argv[])
rl_printf("Command not supported\n");
}
-static void cmd_pull(int argc, char *argv[])
-{
- GDBusProxy *proxy;
-
- if (!check_default_session())
- return;
-
- proxy = find_opp(g_dbus_proxy_get_path(default_session));
- if (proxy) {
- opp_pull(proxy, argc, argv);
- return;
- }
-
- rl_printf("Command not supported\n");
-}
-
static void change_folder_reply(DBusMessage *message, void *user_data)
{
DBusError error;
@@ -2012,8 +1979,6 @@ static const struct {
{ "suspend", "<transfer>", cmd_suspend, "Suspend transfer" },
{ "resume", "<transfer>", cmd_resume, "Resume transfer" },
{ "send", "<file>", cmd_send, "Send file" },
- { "pull", "<file>", cmd_pull,
- "Pull Vobject & stores in file" },
{ "cd", "<path>", cmd_cd, "Change current folder" },
{ "ls", "<options>", cmd_ls, "List current folder" },
{ "cp", "<source file> <destination file>", cmd_cp,
diff --git a/tools/oobtest.c b/tools/oobtest.c
index 9cc6c162..e77320ba 100644
--- a/tools/oobtest.c
+++ b/tools/oobtest.c
@@ -34,6 +34,10 @@
#include "src/shared/mainloop.h"
#include "src/shared/util.h"
#include "src/shared/mgmt.h"
+#include "src/shared/crypto.h"
+
+#define REMOTE_IRK "\x69\x30\xde\xc3\x8f\x84\x74\x14" \
+ "\xe1\x23\x99\xc1\xca\x9a\xc3\x31"
static bool use_bredr = false;
static bool use_le = false;
@@ -41,16 +45,21 @@ static bool use_sc = false;
static bool use_sconly = false;
static bool use_legacy = false;
static bool use_random = false;
+static bool use_privacy = false;
static bool use_debug = false;
static bool use_cross = false;
+static bool provide_tk = false;
static bool provide_p192 = false;
static bool provide_p256 = false;
+static bool provide_initiator = false;
+static bool provide_acceptor = 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 uint8_t oob_tk[16];
static void pin_code_request_event(uint16_t index, uint16_t len,
const void *param, void *user_data)
@@ -254,9 +263,12 @@ static void add_remote_oob_data(uint16_t index, const bdaddr_t *bdaddr,
cp.addr.type = BDADDR_LE_RANDOM;
else
cp.addr.type = BDADDR_LE_PUBLIC;
- if (hash192 && rand192) {
+ if (hash192) {
memcpy(cp.hash192, hash192, 16);
- memcpy(cp.rand192, rand192, 16);
+ if (rand192)
+ memcpy(cp.rand192, rand192, 16);
+ else
+ memset(cp.rand192, 0, 16);
} else {
memset(cp.hash192, 0, 16);
memset(cp.rand192, 0, 16);
@@ -277,7 +289,7 @@ static void add_remote_oob_data(uint16_t index, const bdaddr_t *bdaddr,
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;
+ const struct mgmt_rp_read_local_oob_data *rp = param;
uint16_t index = PTR_TO_UINT(user_data);
const uint8_t *hash192, *rand192, *hash256, *rand256;
int i;
@@ -291,12 +303,22 @@ static void read_oob_data_complete(uint8_t status, uint16_t len,
printf("[Index %u]\n", index);
+ hash192 = NULL;
+ rand192 = NULL;
+ hash256 = NULL;
+ rand256 = NULL;
+
+ if (index == index1 && !provide_initiator) {
+ printf(" Skipping initiator OOB data\n");
+ goto done;
+ } else if (index == index2 && !provide_acceptor) {
+ printf(" Skipping acceptor OOB data\n");
+ goto done;
+ }
+
if (provide_p192) {
hash192 = rp->hash192;
- rand192 = rp->randomizer192;
- } else {
- hash192 = NULL;
- rand192 = NULL;
+ rand192 = rp->rand192;
}
printf(" Hash C from P-192: ");
@@ -306,21 +328,15 @@ static void read_oob_data_complete(uint8_t status, uint16_t len,
printf(" Randomizer R with P-192: ");
for (i = 0; i < 16; i++)
- printf("%02x", rp->randomizer192[i]);
+ printf("%02x", rp->rand192[i]);
printf("\n");
- if (len < sizeof(*rp)) {
- hash256 = NULL;
- rand256 = NULL;
+ if (len < sizeof(*rp))
goto done;
- }
if (provide_p256) {
hash256 = rp->hash256;
- rand256 = rp->randomizer256;
- } else {
- hash256 = NULL;
- rand256 = NULL;
+ rand256 = rp->rand256;
}
printf(" Hash C from P-256: ");
@@ -330,7 +346,7 @@ static void read_oob_data_complete(uint8_t status, uint16_t len,
printf(" Randomizer R with P-256: ");
for (i = 0; i < 16; i++)
- printf("%02x", rp->randomizer256[i]);
+ printf("%02x", rp->rand256[i]);
printf("\n");
done:
@@ -342,6 +358,105 @@ done:
hash192, rand192, hash256, rand256);
}
+static void read_oob_ext_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);
+ uint16_t eir_len, parsed;
+ const uint8_t *eir, *tk, *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);
+
+ eir_len = le16_to_cpu(rp->eir_len);
+ printf(" OOB data len: %u\n", eir_len);
+
+ if (provide_tk)
+ tk = oob_tk;
+ else
+ tk = NULL;
+
+ hash256 = NULL;
+ rand256 = NULL;
+
+ if (index == index1 && !provide_initiator) {
+ printf(" Skipping initiator OOB data\n");
+ goto done;
+ } else if (index == index2 && !provide_acceptor) {
+ printf(" Skipping acceptor OOB data\n");
+ goto done;
+ }
+
+ if (eir_len < 2)
+ goto done;
+
+ eir = rp->eir;
+ parsed = 0;
+
+ while (parsed < eir_len - 1) {
+ uint8_t field_len = eir[0];
+
+ if (field_len == 0)
+ break;
+
+ parsed += field_len + 1;
+
+ if (parsed > eir_len)
+ break;
+
+ /* LE Bluetooth Device Address */
+ if (eir[1] == 0x1b) {
+ char str[18];
+
+ ba2str((bdaddr_t *) (eir + 2), str);
+ printf(" Device address: %s (%s)\n", str,
+ eir[8] ? "random" : "public");
+ }
+
+ /* LE Role */
+ if (eir[1] == 0x1c)
+ printf(" Role: 0x%02x\n", eir[2]);
+
+ /* LE Secure Connections Confirmation Value */
+ if (eir[1] == 0x22) {
+ hash256 = eir + 2;
+
+ printf(" Hash C from P-256: ");
+ for (i = 0; i < 16; i++)
+ printf("%02x", hash256[i]);
+ printf("\n");
+ }
+
+ /* LE Secure Connections Random Value */
+ if (eir[1] == 0x23) {
+ rand256 = eir + 2;
+
+ printf(" Randomizer R with P-256: ");
+ for (i = 0; i < 16; i++)
+ printf("%02x", rand256[i]);
+ printf("\n");
+ }
+
+ eir += field_len + 1;
+ }
+
+done:
+ if (index == index1)
+ add_remote_oob_data(index2, &bdaddr1,
+ tk, NULL, hash256, rand256);
+ else if (index == index2)
+ add_remote_oob_data(index1, &bdaddr2,
+ tk, NULL, hash256, rand256);
+}
+
static void set_powered_complete(uint8_t status, uint16_t len,
const void *param, void *user_data)
{
@@ -380,6 +495,23 @@ static void set_powered_complete(uint8_t status, uint16_t len,
mgmt_send(mgmt, MGMT_OP_READ_LOCAL_OOB_DATA, index, 0, NULL,
read_oob_data_complete,
UINT_TO_PTR(index), NULL);
+ } else if (use_le && provide_p256) {
+ uint8_t type = (1 << BDADDR_LE_PUBLIC) |
+ (1 << BDADDR_LE_RANDOM);
+
+ mgmt_send(mgmt, MGMT_OP_READ_LOCAL_OOB_EXT_DATA, index,
+ sizeof(type), &type,
+ read_oob_ext_data_complete,
+ UINT_TO_PTR(index), NULL);
+ } else if (use_le && provide_tk) {
+ const uint8_t *tk = oob_tk;
+
+ if (index == index1)
+ add_remote_oob_data(index2, &bdaddr1,
+ tk, NULL, NULL, NULL);
+ else if (index == index2)
+ add_remote_oob_data(index1, &bdaddr2,
+ tk, NULL, NULL, NULL);
} else {
if (index == index1)
add_remote_oob_data(index2, &bdaddr1,
@@ -413,6 +545,17 @@ static void clear_long_term_keys(uint16_t index)
sizeof(cp), &cp, NULL, NULL, NULL);
}
+static void clear_identity_resolving_keys(uint16_t index)
+{
+ struct mgmt_cp_load_irks cp;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.irk_count = cpu_to_le16(0);
+
+ mgmt_send(mgmt, MGMT_OP_LOAD_IRKS, index,
+ sizeof(cp), &cp, NULL, NULL, NULL);
+}
+
static void clear_remote_oob_data(uint16_t index)
{
struct mgmt_cp_remove_remote_oob_data cp;
@@ -425,6 +568,123 @@ static void clear_remote_oob_data(uint16_t index)
sizeof(cp), &cp, NULL, NULL, NULL);
}
+static void set_powered_down_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, "Power down for index %u failed: %s\n",
+ index, mgmt_errstr(status));
+ mainloop_quit();
+ return;
+ }
+}
+
+static void set_bredr_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, "Setting BR/EDR for index %u failed: %s\n",
+ index, mgmt_errstr(status));
+ mainloop_quit();
+ return;
+ }
+}
+
+static void set_le_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, "Setting LE for index %u failed: %s\n",
+ index, mgmt_errstr(status));
+ mainloop_quit();
+ return;
+ }
+}
+
+static void set_ssp_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, "Simple Pairing for index %u failed: %s\n",
+ index, mgmt_errstr(status));
+ mainloop_quit();
+ return;
+ }
+}
+
+static void set_static_address_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, "Static address for index %u failed: %s\n",
+ index, mgmt_errstr(status));
+ mainloop_quit();
+ return;
+ }
+}
+
+static void set_secure_conn_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, "Secure connections for index %u failed: %s\n",
+ index, mgmt_errstr(status));
+ mainloop_quit();
+ return;
+ }
+}
+
+static void set_privacy_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, "Setting privacy for index %u failed: %s\n",
+ index, mgmt_errstr(status));
+ mainloop_quit();
+ return;
+ }
+}
+
+static void set_debug_keys_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, "Setting debug keys for index %u failed: %s\n",
+ index, mgmt_errstr(status));
+ mainloop_quit();
+ return;
+ }
+}
+
+static void set_bondable_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, "Setting bondable for index %u failed: %s\n",
+ index, mgmt_errstr(status));
+ mainloop_quit();
+ return;
+ }
+}
+
static void read_info(uint8_t status, uint16_t len, const void *param,
void *user_data)
{
@@ -483,6 +743,12 @@ static void read_info(uint8_t status, uint16_t len, const void *param,
return;
}
+ if (use_privacy && !(supported_settings & MGMT_SETTING_PRIVACY)) {
+ fprintf(stderr, "Privacy support missing\n");
+ mainloop_quit();
+ return;
+ }
+
if (use_debug && !(supported_settings & MGMT_SETTING_DEBUG_KEYS)) {
fprintf(stderr, "Debug keys support missing\n");
mainloop_quit();
@@ -496,6 +762,16 @@ static void read_info(uint8_t status, uint16_t len, const void *param,
return;
}
+ if (provide_tk) {
+ const uint8_t *tk = oob_tk;
+ int i;
+
+ printf(" TK Value: ");
+ for (i = 0; i < 16; i++)
+ printf("%02x", tk[i]);
+ printf("\n");
+ }
+
mgmt_register(mgmt, MGMT_EV_PIN_CODE_REQUEST, index,
pin_code_request_event,
UINT_TO_PTR(index), NULL);
@@ -510,32 +786,46 @@ static void read_info(uint8_t status, uint16_t len, const void *param,
val = 0x00;
mgmt_send(mgmt, MGMT_OP_SET_POWERED, index, 1, &val,
- NULL, NULL, NULL);
+ set_powered_down_complete,
+ UINT_TO_PTR(index), NULL);
clear_link_keys(index);
clear_long_term_keys(index);
+ clear_identity_resolving_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);
+ set_bredr_complete,
+ UINT_TO_PTR(index), NULL);
val = use_cross ? 0x01 : 0x00;
mgmt_send(mgmt, MGMT_OP_SET_LE, index, 1, &val,
- NULL, NULL, NULL);
+ set_le_complete,
+ UINT_TO_PTR(index), NULL);
val = use_legacy ? 0x00 : 0x01;
mgmt_send(mgmt, MGMT_OP_SET_SSP, index, 1, &val,
- NULL, NULL, NULL);
+ set_ssp_complete,
+ UINT_TO_PTR(index), NULL);
} else if (use_le) {
val = 0x01;
mgmt_send(mgmt, MGMT_OP_SET_LE, index, 1, &val,
- NULL, NULL, NULL);
+ set_le_complete,
+ UINT_TO_PTR(index), NULL);
val = use_cross ? 0x01 : 0x00;
mgmt_send(mgmt, MGMT_OP_SET_BREDR, index, 1, &val,
- NULL, NULL, NULL);
+ set_bredr_complete,
+ UINT_TO_PTR(index), NULL);
+
+ if (use_cross) {
+ val = use_legacy ? 0x00 : 0x01;
+ mgmt_send(mgmt, MGMT_OP_SET_SSP, index, 1, &val,
+ set_ssp_complete,
+ UINT_TO_PTR(index), NULL);
+ }
} else {
fprintf(stderr, "Invalid transport for pairing\n");
mainloop_quit();
@@ -548,8 +838,9 @@ static void read_info(uint8_t status, uint16_t len, const void *param,
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);
+ mgmt_send(mgmt, MGMT_OP_SET_STATIC_ADDRESS, index, 6, &bdaddr,
+ set_static_address_complete,
+ UINT_TO_PTR(index), NULL);
if (index == index1)
bacpy(&bdaddr1, &bdaddr);
@@ -560,31 +851,62 @@ static void read_info(uint8_t status, uint16_t len, const void *param,
bacpy(&bdaddr, BDADDR_ANY);
- mgmt_send(mgmt, MGMT_OP_SET_STATIC_ADDRESS, index,
- 6, &bdaddr, NULL, NULL, NULL);
+ mgmt_send(mgmt, MGMT_OP_SET_STATIC_ADDRESS, index, 6, &bdaddr,
+ set_static_address_complete,
+ UINT_TO_PTR(index), NULL);
}
if (use_sc) {
val = 0x01;
mgmt_send(mgmt, MGMT_OP_SET_SECURE_CONN, index, 1, &val,
- NULL, NULL, NULL);
+ set_secure_conn_complete,
+ UINT_TO_PTR(index), NULL);
} else if (use_sconly) {
val = 0x02;
mgmt_send(mgmt, MGMT_OP_SET_SECURE_CONN, index, 1, &val,
- NULL, NULL, NULL);
+ set_secure_conn_complete,
+ UINT_TO_PTR(index), NULL);
} else {
val = 0x00;
mgmt_send(mgmt, MGMT_OP_SET_SECURE_CONN, index, 1, &val,
- NULL, NULL, NULL);
+ set_secure_conn_complete,
+ UINT_TO_PTR(index), NULL);
+ }
+
+ if (use_privacy) {
+ struct mgmt_cp_set_privacy cp;
+
+ if (index == index2) {
+ cp.privacy = 0x01;
+ memcpy(cp.irk, REMOTE_IRK, sizeof(cp.irk));
+ } else {
+ cp.privacy = 0x00;
+ memset(cp.irk, 0, sizeof(cp.irk));
+ }
+
+ mgmt_send(mgmt, MGMT_OP_SET_PRIVACY, index, sizeof(cp), &cp,
+ set_privacy_complete,
+ UINT_TO_PTR(index), NULL);
+ } else {
+ struct mgmt_cp_set_privacy cp;
+
+ cp.privacy = 0x00;
+ memset(cp.irk, 0, sizeof(cp.irk));
+
+ mgmt_send(mgmt, MGMT_OP_SET_PRIVACY, index, sizeof(cp), &cp,
+ set_privacy_complete,
+ UINT_TO_PTR(index), NULL);
}
val = 0x00;
mgmt_send(mgmt, MGMT_OP_SET_DEBUG_KEYS, index, 1, &val,
- NULL, NULL, NULL);
+ set_debug_keys_complete,
+ UINT_TO_PTR(index), NULL);
val = 0x01;
mgmt_send(mgmt, MGMT_OP_SET_BONDABLE, index, 1, &val,
- NULL, NULL, NULL);
+ set_bondable_complete,
+ UINT_TO_PTR(index), NULL);
val = 0x01;
mgmt_send(mgmt, MGMT_OP_SET_POWERED, index, 1, &val,
@@ -631,6 +953,16 @@ static void read_index_list(uint8_t status, uint16_t len, const void *param,
printf("Selecting index %u for initiator\n", index1);
printf("Selecting index %u for acceptor\n", index2);
+ if (provide_tk) {
+ struct bt_crypto *crypto;
+
+ printf("Generating Security Manager TK Value\n");
+
+ crypto = bt_crypto_new();
+ bt_crypto_random_bytes(crypto, oob_tk, 16);
+ bt_crypto_unref(crypto);
+ }
+
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,
@@ -659,28 +991,36 @@ static void usage(void)
"\t-O, --sconly Use Secure Connections Only\n"
"\t-P, --legacy Use Legacy Pairing\n"
"\t-R, --random Use Static random address\n"
+ "\t-Y, --privacy Use LE privacy feature\n"
"\t-D, --debug Use Pairing debug keys\n"
"\t-C, --cross Use cross-transport pairing\n"
+ "\t-0, --tk Provide LE legacy OOB data\n"
"\t-1, --p192 Provide P-192 OOB data\n"
"\t-2, --p256 Provide P-256 OOB data\n"
+ "\t-I, --initiator Initiator provides OOB data\n"
+ "\t-A, --acceptor Acceptor provides 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' },
+ { "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' },
+ { "privacy", no_argument, NULL, 'Y' },
+ { "debug", no_argument, NULL, 'D' },
+ { "cross", no_argument, NULL, 'C' },
+ { "dual", no_argument, NULL, 'C' },
+ { "tk", no_argument, NULL, '0' },
+ { "p192", no_argument, NULL, '1' },
+ { "p256", no_argument, NULL, '2' },
+ { "initiator", no_argument, NULL, 'I' },
+ { "acceptor", no_argument, NULL, 'A' },
+ { "version", no_argument, NULL, 'v' },
+ { "help", no_argument, NULL, 'h' },
{ }
};
@@ -692,7 +1032,7 @@ int main(int argc ,char *argv[])
for (;;) {
int opt;
- opt = getopt_long(argc, argv, "BLSOPRDC12vh",
+ opt = getopt_long(argc, argv, "BLSOPRYDC012IAvh",
main_options, NULL);
if (opt < 0)
break;
@@ -716,18 +1056,30 @@ int main(int argc ,char *argv[])
case 'R':
use_random = true;
break;
+ case 'Y':
+ use_privacy = true;
+ break;
case 'D':
use_debug = true;
break;
case 'C':
use_cross = true;
break;
+ case '0':
+ provide_tk = true;
+ break;
case '1':
provide_p192 = true;
break;
case '2':
provide_p256 = true;
break;
+ case 'I':
+ provide_initiator = true;
+ break;
+ case 'A':
+ provide_acceptor = true;
+ break;
case 'v':
printf("%s\n", VERSION);
return EXIT_SUCCESS;
@@ -754,6 +1106,11 @@ int main(int argc ,char *argv[])
return EXIT_FAILURE;
}
+ if (use_privacy && !use_le && !use_cross ) {
+ fprintf(stderr, "Specify --privacy with --le or --cross\n");
+ return EXIT_FAILURE;
+ }
+
if (use_random && !use_le) {
fprintf(stderr, "Specify --random with --le\n");
return EXIT_FAILURE;
diff --git a/unit/test-avctp.c b/unit/test-avctp.c
index 8f7d5ad1..3bc35696 100644
--- a/unit/test-avctp.c
+++ b/unit/test-avctp.c
@@ -36,6 +36,7 @@
#include <glib.h>
#include "src/shared/util.h"
+#include "src/shared/tester.h"
#include "src/log.h"
#include "android/avctp.h"
@@ -52,7 +53,6 @@ struct test_data {
};
struct context {
- GMainLoop *main_loop;
struct avctp *session;
guint source;
guint process;
@@ -77,16 +77,15 @@ struct context {
}; \
static struct test_data data; \
data.test_name = g_strdup(name); \
- data.pdu_list = g_malloc(sizeof(pdus)); \
- memcpy(data.pdu_list, pdus, sizeof(pdus)); \
- g_test_add_data_func(name, &data, function); \
+ data.pdu_list = g_memdup(pdus, sizeof(pdus)); \
+ tester_add(name, &data, NULL, function, NULL); \
} while (0)
static void test_debug(const char *str, void *user_data)
{
const char *prefix = user_data;
- g_print("%s%s\n", prefix, str);
+ tester_debug("%s%s", prefix, str);
}
static void test_free(gconstpointer user_data)
@@ -97,6 +96,17 @@ static void test_free(gconstpointer user_data)
g_free(data->pdu_list);
}
+static void destroy_context(struct context *context)
+{
+ if (context->source > 0)
+ g_source_remove(context->source);
+
+ avctp_shutdown(context->session);
+
+ test_free(context->data);
+ g_free(context);
+}
+
static gboolean context_quit(gpointer user_data)
{
struct context *context = user_data;
@@ -104,7 +114,9 @@ static gboolean context_quit(gpointer user_data)
if (context->process > 0)
g_source_remove(context->process);
- g_main_loop_quit(context->main_loop);
+ destroy_context(context);
+
+ tester_test_passed();
return FALSE;
}
@@ -119,8 +131,7 @@ static gboolean send_pdu(gpointer user_data)
len = write(context->fd, pdu->data, pdu->size);
- if (g_test_verbose())
- util_hexdump('<', pdu->data, len, test_debug, "AVCTP: ");
+ util_hexdump('<', pdu->data, len, test_debug, "AVCTP: ");
g_assert_cmpint(len, ==, pdu->size);
@@ -161,8 +172,7 @@ static gboolean test_handler(GIOChannel *channel, GIOCondition cond,
g_assert(len > 0);
- if (g_test_verbose())
- util_hexdump('>', buf, len, test_debug, "AVCTP: ");
+ util_hexdump('>', buf, len, test_debug, "AVCTP: ");
g_assert_cmpint(len, ==, pdu->size);
@@ -179,9 +189,6 @@ static struct context *create_context(uint16_t version, gconstpointer data)
GIOChannel *channel;
int err, sv[2];
- context->main_loop = g_main_loop_new(NULL, FALSE);
- g_assert(context->main_loop);
-
err = socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sv);
g_assert(err == 0);
@@ -207,34 +214,11 @@ static struct context *create_context(uint16_t version, gconstpointer data)
return context;
}
-static void destroy_context(struct context *context)
-{
- if (context->source > 0)
- g_source_remove(context->source);
-
- avctp_shutdown(context->session);
-
- 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 ssize_t handler(struct avctp *session,
uint8_t transaction, uint8_t *code,
uint8_t *subunit, uint8_t *operands,
size_t operand_count, void *user_data)
{
- DBG("transaction %d code %d subunit %d operand_count %zu",
- transaction, *code, *subunit, operand_count);
-
g_assert_cmpint(transaction, ==, 0);
g_assert_cmpint(*code, ==, 0);
g_assert_cmpint(*subunit, ==, 0);
@@ -250,9 +234,6 @@ static gboolean handler_response(struct avctp *session,
{
struct context *context = user_data;
- DBG("code 0x%02x subunit %d operand_count %zu", code, subunit,
- operand_count);
-
g_assert_cmpint(code, ==, 0x0a);
g_assert_cmpint(subunit, ==, 0);
g_assert_cmpint(operand_count, ==, 0);
@@ -266,8 +247,6 @@ static void test_client(gconstpointer data)
avctp_send_vendor_req(context->session, AVC_CTYPE_CONTROL, 0, NULL,
0, handler_response, context);
-
- execute_context(context);
}
static void test_server(gconstpointer data)
@@ -279,28 +258,24 @@ static void test_server(gconstpointer data)
ret = avctp_register_pdu_handler(context->session,
AVC_OP_VENDORDEP, handler, NULL);
- DBG("ret %d", ret);
g_assert_cmpint(ret, !=, 0);
}
g_idle_add(send_pdu, context);
-
- execute_context(context);
}
static void test_dummy(gconstpointer data)
{
struct context *context = create_context(0x0100, data);
- destroy_context(context);
+ context_quit(context);
}
int main(int argc, char *argv[])
{
- g_test_init(&argc, &argv, NULL);
+ tester_init(&argc, &argv);
- if (g_test_verbose())
- __btd_log_init("*", 0);
+ __btd_log_init("*", 0);
/* Connection Channel Management tests */
@@ -335,5 +310,5 @@ int main(int argc, char *argv[])
raw_pdu(0x00, 0xff, 0xff, 0x00, 0x00, 0x00),
raw_pdu(0x03, 0xff, 0xff));
- return g_test_run();
+ return tester_run();
}
diff --git a/unit/test-avdtp.c b/unit/test-avdtp.c
index 805f08d5..dd8aed73 100644
--- a/unit/test-avdtp.c
+++ b/unit/test-avdtp.c
@@ -37,7 +37,9 @@
#include "src/shared/util.h"
#include "src/shared/queue.h"
+#include "src/shared/tester.h"
#include "src/log.h"
+
#include "android/avdtp.h"
#define MAX_SEID 0x3E
@@ -78,13 +80,11 @@ struct test_data {
}; \
static struct test_data data; \
data.test_name = g_strdup(name); \
- data.pdu_list = g_malloc(sizeof(pdus)); \
- memcpy(data.pdu_list, pdus, sizeof(pdus)); \
- g_test_add_data_func(name, &data, function); \
+ data.pdu_list = g_memdup(pdus, sizeof(pdus)); \
+ tester_add(name, &data, NULL, function, NULL); \
} while (0)
struct context {
- GMainLoop *main_loop;
struct avdtp *session;
struct avdtp_local_sep *sep;
struct avdtp_stream *stream;
@@ -103,7 +103,7 @@ static void test_debug(const char *str, void *user_data)
{
const char *prefix = user_data;
- g_print("%s%s\n", prefix, str);
+ tester_debug("%s%s", prefix, str);
}
static void test_free(gconstpointer user_data)
@@ -114,6 +114,26 @@ static void test_free(gconstpointer user_data)
g_free(data->pdu_list);
}
+static void unregister_sep(void *data)
+{
+ 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);
+
+ test_free(context->data);
+ queue_destroy(context->lseps, unregister_sep);
+
+ g_free(context);
+}
+
static gboolean context_quit(gpointer user_data)
{
struct context *context = user_data;
@@ -121,7 +141,9 @@ static gboolean context_quit(gpointer user_data)
if (context->process > 0)
g_source_remove(context->process);
- g_main_loop_quit(context->main_loop);
+ destroy_context(context);
+
+ tester_test_passed();
return FALSE;
}
@@ -136,8 +158,7 @@ static gboolean send_pdu(gpointer user_data)
len = write(context->fd, pdu->data, pdu->size);
- if (g_test_verbose())
- util_hexdump('<', pdu->data, len, test_debug, "AVDTP: ");
+ util_hexdump('<', pdu->data, len, test_debug, "AVDTP: ");
g_assert_cmpint(len, ==, pdu->size);
@@ -194,8 +215,7 @@ static gboolean test_handler(GIOChannel *channel, GIOCondition cond,
g_assert(len > 0);
- if (g_test_verbose())
- util_hexdump('>', buf, len, test_debug, "AVDTP: ");
+ util_hexdump('>', buf, len, test_debug, "AVDTP: ");
g_assert_cmpint(len, ==, pdu->size);
@@ -227,9 +247,6 @@ static struct context *context_new(uint16_t version, uint16_t imtu,
GIOChannel *channel;
int err, sv[2];
- context->main_loop = g_main_loop_new(NULL, FALSE);
- g_assert(context->main_loop);
-
err = socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sv);
g_assert(err == 0);
@@ -264,35 +281,6 @@ static struct context *create_context(uint16_t version, gconstpointer data)
return context_new(version, 672, 672, data);
}
-static void unregister_sep(void *data)
-{
- 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,
@@ -424,6 +412,11 @@ static void sep_setconf_cfm(struct avdtp *session, struct avdtp_local_sep *sep,
struct context *context = user_data;
int ret;
+ if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BV-09-C")) {
+ context_quit(context);
+ return;
+ }
+
if (g_str_equal(context->data->test_name, "/TP/SIG/SMG/BI-07-C")) {
g_assert(err != NULL);
g_assert_cmpint(avdtp_error_error_code(err), ==, 0x13);
@@ -531,8 +524,6 @@ static void test_server(gconstpointer data)
g_assert(sep);
g_idle_add(send_pdu, context);
-
- execute_context(context);
}
static void test_server_1_3(gconstpointer data)
@@ -546,8 +537,6 @@ static void test_server_1_3(gconstpointer data)
g_assert(sep);
g_idle_add(send_pdu, context);
-
- execute_context(context);
}
static void test_server_1_3_sink(gconstpointer data)
@@ -561,8 +550,6 @@ static void test_server_1_3_sink(gconstpointer data)
g_assert(sep);
g_idle_add(send_pdu, context);
-
- execute_context(context);
}
static void test_server_0_sep(gconstpointer data)
@@ -570,8 +557,6 @@ static void test_server_0_sep(gconstpointer data)
struct context *context = create_context(0x0100, data);
g_idle_add(send_pdu, context);
-
- execute_context(context);
}
static void test_server_seid(gconstpointer data)
@@ -595,7 +580,7 @@ static void test_server_seid(gconstpointer data)
context);
g_assert(!sep);
- destroy_context(context);
+ context_quit(context);
}
static void test_server_seid_duplicate(gconstpointer data)
@@ -628,8 +613,6 @@ static void test_server_seid_duplicate(gconstpointer data)
/* Check SEID ids with DISCOVER */
g_idle_add(send_pdu, context);
-
- execute_context(context);
}
static gboolean sep_getcap_ind_frg(struct avdtp *session,
@@ -684,8 +667,6 @@ static void test_server_frg(gconstpointer data)
g_assert(sep);
g_idle_add(send_pdu, context);
-
- execute_context(context);
}
static void discover_cb(struct avdtp *session, GSList *seps,
@@ -767,8 +748,6 @@ static void test_client(gconstpointer data)
context->sep = sep;
avdtp_discover(context->session, discover_cb, context);
-
- execute_context(context);
}
static void test_client_1_3(gconstpointer data)
@@ -783,8 +762,6 @@ static void test_client_1_3(gconstpointer data)
context->sep = sep;
avdtp_discover(context->session, discover_cb, context);
-
- execute_context(context);
}
static void test_client_frg(gconstpointer data)
@@ -799,16 +776,13 @@ static void test_client_frg(gconstpointer data)
context->sep = sep;
avdtp_discover(context->session, discover_cb, context);
-
- execute_context(context);
}
int main(int argc, char *argv[])
{
- g_test_init(&argc, &argv, NULL);
+ tester_init(&argc, &argv);
- if (g_test_verbose())
- __btd_log_init("*", 0);
+ __btd_log_init("*", 0);
/*
* Stream Management Service
@@ -843,7 +817,8 @@ int main(int argc, char *argv[])
raw_pdu(0x42, 0x02, 0x01, 0x00, 0x07, 0x06, 0x00, 0x00,
0xff, 0xff, 0x02, 0x40),
raw_pdu(0x50, 0x03, 0x04, 0x04, 0x01, 0x00, 0x07, 0x06,
- 0x00, 0x00, 0x21, 0x02, 0x02, 0x20));
+ 0x00, 0x00, 0x21, 0x02, 0x02, 0x20),
+ raw_pdu(0x52, 0x03));
define_test("/TP/SIG/SMG/BV-10-C", test_server,
raw_pdu(0x00, 0x01),
raw_pdu(0x02, 0x01, 0x04, 0x00),
@@ -1413,5 +1388,5 @@ int main(int argc, char *argv[])
raw_pdu(0x50, 0x0d, 0x04, 0x00, 0x00),
raw_pdu(0x52, 0x0d));
- return g_test_run();
+ return tester_run();
}
diff --git a/unit/test-avrcp.c b/unit/test-avrcp.c
index cffba78f..2fc7906e 100644
--- a/unit/test-avrcp.c
+++ b/unit/test-avrcp.c
@@ -37,6 +37,7 @@
#include <glib.h>
#include "src/shared/util.h"
+#include "src/shared/tester.h"
#include "src/log.h"
#include "lib/bluetooth.h"
@@ -58,7 +59,6 @@ struct test_data {
};
struct context {
- GMainLoop *main_loop;
struct avrcp *session;
guint source;
guint browse_source;
@@ -109,16 +109,15 @@ struct context {
}; \
static struct test_data data; \
data.test_name = g_strdup(name); \
- data.pdu_list = g_malloc(sizeof(pdus)); \
- memcpy(data.pdu_list, pdus, sizeof(pdus)); \
- g_test_add_data_func(name, &data, function); \
+ data.pdu_list = g_memdup(pdus, sizeof(pdus)); \
+ tester_add(name, &data, NULL, function, NULL); \
} while (0)
static void test_debug(const char *str, void *user_data)
{
const char *prefix = user_data;
- g_print("%s%s\n", prefix, str);
+ tester_debug("%s%s", prefix, str);
}
static void test_free(gconstpointer user_data)
@@ -129,6 +128,20 @@ static void test_free(gconstpointer user_data)
g_free(data->pdu_list);
}
+static void destroy_context(struct context *context)
+{
+ if (context->source > 0)
+ g_source_remove(context->source);
+
+ if (context->browse_source > 0)
+ g_source_remove(context->browse_source);
+
+ avrcp_shutdown(context->session);
+
+ test_free(context->data);
+ g_free(context);
+}
+
static gboolean context_quit(gpointer user_data)
{
struct context *context = user_data;
@@ -136,7 +149,9 @@ static gboolean context_quit(gpointer user_data)
if (context->process > 0)
g_source_remove(context->process);
- g_main_loop_quit(context->main_loop);
+ destroy_context(context);
+
+ tester_test_passed();
return FALSE;
}
@@ -154,8 +169,7 @@ static gboolean send_pdu(gpointer user_data)
else
len = write(context->fd, pdu->data, pdu->size);
- if (g_test_verbose())
- util_hexdump('<', pdu->data, len, test_debug, "AVRCP: ");
+ util_hexdump('<', pdu->data, len, test_debug, "AVRCP: ");
g_assert_cmpint(len, ==, pdu->size);
@@ -185,15 +199,13 @@ static gboolean test_handler(GIOChannel *channel, GIOCondition cond,
ssize_t len;
int fd;
- DBG("");
-
pdu = &context->data->pdu_list[context->pdu_offset++];
g_assert(!pdu->browse);
if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
context->source = 0;
- g_print("%s: cond %x\n", __func__, cond);
+ tester_debug("%s: cond %x\n", __func__, cond);
return FALSE;
}
@@ -226,15 +238,13 @@ static gboolean browse_test_handler(GIOChannel *channel, GIOCondition cond,
ssize_t len;
int fd;
- DBG("");
-
pdu = &context->data->pdu_list[context->pdu_offset++];
g_assert(pdu->browse);
if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
context->browse_source = 0;
- g_print("%s: cond %x\n", __func__, cond);
+ tester_debug("%s: cond %x\n", __func__, cond);
return FALSE;
}
@@ -244,8 +254,7 @@ static gboolean browse_test_handler(GIOChannel *channel, GIOCondition cond,
g_assert(len > 0);
- if (g_test_verbose())
- util_hexdump('>', buf, len, test_debug, "AVRCP: ");
+ util_hexdump('>', buf, len, test_debug, "AVRCP: ");
g_assert_cmpint(len, ==, pdu->size);
@@ -263,11 +272,6 @@ static struct context *create_context(uint16_t version, gconstpointer data)
GIOChannel *channel;
int err, sv[2];
- DBG("");
-
- context->main_loop = g_main_loop_new(NULL, FALSE);
- g_assert(context->main_loop);
-
/* Control channel setup */
err = socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sv);
@@ -319,34 +323,11 @@ static struct context *create_context(uint16_t version, gconstpointer data)
return context;
}
-static void destroy_context(struct context *context)
-{
- if (context->source > 0)
- g_source_remove(context->source);
-
- avrcp_shutdown(context->session);
-
- if (context->browse_source > 0)
- g_source_remove(context->browse_source);
-
- g_main_loop_unref(context->main_loop);
-
- test_free(context->data);
- g_free(context);
-}
-
static void test_dummy(gconstpointer data)
{
struct context *context = create_context(0x0100, data);
- destroy_context(context);
-}
-
-static void execute_context(struct context *context)
-{
- g_main_loop_run(context->main_loop);
-
- destroy_context(context);
+ context_quit(context);
}
static bool handle_play(struct avrcp *session, bool pressed, void *user_data)
@@ -1083,16 +1064,13 @@ static void test_client(gconstpointer data)
if (g_str_equal(context->data->test_name, "/TP/PTH/BV-02-C"))
avrcp_send_passthrough(context->session, 0, AVC_FAST_FORWARD);
-
- execute_context(context);
}
int main(int argc, char *argv[])
{
- g_test_init(&argc, &argv, NULL);
+ tester_init(&argc, &argv);
- if (g_test_verbose())
- __btd_log_init("*", 0);
+ __btd_log_init("*", 0);
/* Media Player Selection Commands and Notifications */
@@ -2182,5 +2160,5 @@ int main(int argc, char *argv[])
0x00, 0x19, 0x58, AVRCP_ABORT_CONTINUING,
0x00, 0x00, 0x00));
- return g_test_run();
+ return tester_run();
}
diff --git a/unit/test-gatt.c b/unit/test-gatt.c
index 2edcacb0..95b42dd7 100644
--- a/unit/test-gatt.c
+++ b/unit/test-gatt.c
@@ -102,8 +102,7 @@ struct context {
data.uuid = bt_uuid; \
data.step = test_step; \
data.source_db = db; \
- data.pdu_list = g_malloc(sizeof(pdus)); \
- memcpy(data.pdu_list, pdus, sizeof(pdus)); \
+ data.pdu_list = g_memdup(pdus, sizeof(pdus)); \
tester_add(name, &data, NULL, function, NULL); \
} while (0)
@@ -149,6 +148,65 @@ struct context {
raw_pdu(0x04, 0x08, 0x00, 0x08, 0x00), \
raw_pdu(0x05, 0x01, 0x08, 0x00, 0x01, 0x29)
+#define SERVICE_DATA_2_PDUS \
+ MTU_EXCHANGE_CLIENT_PDUS, \
+ raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28), \
+ raw_pdu(0x11, 0x06, 0x01, 0x00, 0x04, 0x00, 0x01, 0x18),\
+ raw_pdu(0x10, 0x05, 0x00, 0xff, 0xff, 0x00, 0x28), \
+ raw_pdu(0x11, 0x06, 0x05, 0x00, 0x0a, 0x00, 0x0d, 0x18),\
+ raw_pdu(0x10, 0x0b, 0x00, 0xff, 0xff, 0x00, 0x28), \
+ raw_pdu(0x01, 0x10, 0x0b, 0x00, 0x0a), \
+ raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x01, 0x28), \
+ raw_pdu(0x01, 0x10, 0x01, 0x00, 0x0a), \
+ raw_pdu(0x08, 0x01, 0x00, 0x04, 0x00, 0x02, 0x28), \
+ raw_pdu(0x01, 0x08, 0x01, 0x00, 0x0a), \
+ raw_pdu(0x08, 0x05, 0x00, 0x0a, 0x00, 0x02, 0x28), \
+ raw_pdu(0x01, 0x08, 0x05, 0x00, 0x0a), \
+ raw_pdu(0x08, 0x01, 0x00, 0x04, 0x00, 0x03, 0x28), \
+ raw_pdu(0x09, 0x07, 0x02, 0x00, 0x02, 0x03, 0x00, 0x00, \
+ 0x2a), \
+ raw_pdu(0x08, 0x03, 0x00, 0x04, 0x00, 0x03, 0x28), \
+ raw_pdu(0x01, 0x08, 0x03, 0x00, 0x0a), \
+ raw_pdu(0x04, 0x04, 0x00, 0x04, 0x00), \
+ raw_pdu(0x05, 0x01, 0x04, 0x00, 0x01, 0x29), \
+ raw_pdu(0x08, 0x05, 0x00, 0x0a, 0x00, 0x03, 0x28), \
+ raw_pdu(0x09, 0x07, 0x07, 0x00, 0x0a, 0x08, 0x00, 0x29, \
+ 0x2a), \
+ raw_pdu(0x08, 0x08, 0x00, 0x0a, 0x00, 0x03, 0x28), \
+ raw_pdu(0x01, 0x08, 0x08, 0x00, 0x0a), \
+ raw_pdu(0x04, 0x09, 0x00, 0x0a, 0x00), \
+ raw_pdu(0x05, 0x01, 0x0a, 0x00, 0x01, 0x29)
+
+#define SERVICE_DATA_3_PDUS \
+ MTU_EXCHANGE_CLIENT_PDUS, \
+ raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28), \
+ raw_pdu(0x11, 0x06, 0x00, 0x01, 0x21, 0x01, 0x00, 0x18, \
+ 0x00, 0x02, 0x00, 0x02, 0x01, 0x18), \
+ raw_pdu(0x10, 0x01, 0x02, 0xff, 0xff, 0x00, 0x28), \
+ raw_pdu(0x11, 0x06, 0x00, 0x03, 0x20, 0x03, 0x0d, 0x18),\
+ raw_pdu(0x10, 0x21, 0x03, 0xff, 0xff, 0x00, 0x28), \
+ raw_pdu(0x01, 0x10, 0x21, 0x03, 0x0a), \
+ raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x01, 0x28), \
+ raw_pdu(0x01, 0x10, 0x01, 0x00, 0x0a), \
+ raw_pdu(0x08, 0x00, 0x01, 0x21, 0x01, 0x02, 0x28), \
+ raw_pdu(0x01, 0x08, 0x00, 0x01, 0x0a), \
+ raw_pdu(0x08, 0x00, 0x03, 0x20, 0x03, 0x02, 0x28), \
+ raw_pdu(0x01, 0x08, 0x00, 0x03, 0x0a), \
+ raw_pdu(0x08, 0x00, 0x01, 0x21, 0x01, 0x03, 0x28), \
+ raw_pdu(0x09, 0x07, 0x10, 0x01, 0x02, 0x11, 0x01, 0x00, \
+ 0x2a, 0x20, 0x01, 0x02, 0x21, 0x01, 0x01, 0x2a),\
+ raw_pdu(0x08, 0x21, 0x01, 0x21, 0x01, 0x03, 0x28), \
+ raw_pdu(0x01, 0x08, 0x21, 0x01, 0x0a), \
+ raw_pdu(0x04, 0x12, 0x01, 0x1f, 0x01), \
+ raw_pdu(0x01, 0x04, 0x12, 0x01, 0x0a), \
+ raw_pdu(0x08, 0x00, 0x03, 0x20, 0x03, 0x03, 0x28), \
+ raw_pdu(0x09, 0x07, 0x10, 0x03, 0x0a, 0x11, 0x03, 0x29, \
+ 0x2a), \
+ raw_pdu(0x08, 0x11, 0x03, 0x20, 0x03, 0x03, 0x28), \
+ raw_pdu(0x01, 0x08, 0x11, 0x03, 0x0a), \
+ raw_pdu(0x04, 0x12, 0x03, 0x20, 0x03), \
+ raw_pdu(0x05, 0x01, 0x20, 0x03, 0x02, 0x29)
+
#define PRIMARY_DISC_SMALL_DB \
raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28), \
raw_pdu(0x11, 0x06, 0x10, 0xF0, 0x17, 0xF0, 0x00, 0x18, \
@@ -304,9 +362,12 @@ static gboolean context_quit(gpointer user_data)
if (step && step->post_func)
step->post_func(context);
- destroy_context(context);
+ if (context->data->pdu_list[context->pdu_offset].valid)
+ tester_test_abort();
+ else
+ tester_test_passed();
- tester_test_passed();
+ destroy_context(context);
return FALSE;
}
@@ -910,6 +971,11 @@ static void test_signed_write(struct context *context)
uint8_t key[16] = {0xD8, 0x51, 0x59, 0x48, 0x45, 0x1F, 0xEA, 0x32, 0x0D,
0xC0, 0x5A, 0x2E, 0x88, 0x30, 0x81, 0x88 };
+ if (!bt_att_has_crypto(context->att)) {
+ context_quit(context);
+ return;
+ }
+
g_assert(bt_att_set_local_key(context->att, key, local_counter,
context));
@@ -952,22 +1018,101 @@ static const struct test_step test_signed_write_seclevel_1 = {
.length = 0x03
};
+static void test_long_write_cb(bool success, bool reliable_error,
+ uint8_t att_ecode, void *user_data)
+{
+ struct context *context = user_data;
+ const struct test_step *step = context->data->step;
+
+ g_assert(att_ecode == step->expected_att_ecode);
+
+ context_quit(context);
+}
+
+static void test_long_write(struct context *context)
+{
+ const struct test_step *step = context->data->step;
+
+ g_assert(bt_gatt_client_write_long_value(context->client, false,
+ step->handle, 0, step->value,
+ step->length, test_long_write_cb,
+ context, NULL));
+}
+
+/* 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_write_1 = {
+ .handle = 0x0007,
+ .func = test_long_write,
+ .expected_att_ecode = 0,
+ .value = long_data_2,
+ .length = sizeof(long_data_2)
+};
+
+static const struct test_step test_long_write_2 = {
+ .handle = 0x0000,
+ .func = test_long_write,
+ .expected_att_ecode = 0x01,
+ .value = write_data_1,
+ .length = 0x03
+};
+
+static const struct test_step test_long_write_3 = {
+ .handle = 0x0003,
+ .func = test_long_write,
+ .expected_att_ecode = 0x03,
+ .value = write_data_1,
+ .length = 0x03
+};
+
+static const struct test_step test_long_write_4 = {
+ .handle = 0x0007,
+ .func = test_long_write,
+ .expected_att_ecode = 0x08,
+ .value = write_data_1,
+ .length = 0x03
+};
+
+static const struct test_step test_long_write_5 = {
+ .handle = 0x0007,
+ .func = test_long_write,
+ .expected_att_ecode = 0x05,
+ .value = write_data_1,
+ .length = 0x03
+};
+
+static const struct test_step test_long_write_6 = {
+ .handle = 0x0007,
+ .func = test_long_write,
+ .expected_att_ecode = 0x0c,
+ .value = write_data_1,
+ .length = 0x03
+};
+
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)
+static struct gatt_db_attribute *
+add_char_with_value(struct gatt_db_attribute *service_att, uint16_t handle,
+ 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,
+ if (handle)
+ attrib = gatt_db_service_insert_characteristic(service_att,
+ handle, uuid,
+ att_permissions,
+ char_properties,
+ NULL, NULL,
+ NULL);
+ else
+ attrib = gatt_db_service_add_characteristic(service_att, uuid,
att_permissions,
char_properties,
NULL, NULL,
@@ -982,14 +1127,19 @@ static struct gatt_db_attribute *add_char_with_value(struct gatt_db *db,
}
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)
+add_desc_with_value(struct gatt_db_attribute *att, uint16_t handle,
+ 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);
+ if (handle)
+ desc_att = gatt_db_service_insert_descriptor(att, handle, uuid,
+ att_perms, NULL, NULL,
+ NULL);
+ else
+ 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);
@@ -1116,7 +1266,7 @@ static struct gatt_db *make_db(const struct att_handle_spec *spec)
case CHARACTERISTIC:
bt_string_to_uuid(&uuid, spec->uuid);
- add_char_with_value(db, att, &uuid,
+ add_char_with_value(att, spec->handle, &uuid,
spec->att_permissions,
spec->char_properties,
spec->value, spec->len);
@@ -1126,7 +1276,8 @@ static struct gatt_db *make_db(const struct att_handle_spec *spec)
case DESCRIPTOR:
bt_string_to_uuid(&uuid, spec->uuid);
- add_desc_with_value(att, &uuid, spec->att_permissions,
+ add_desc_with_value(att, spec->handle, &uuid,
+ spec->att_permissions,
spec->value, spec->len);
break;
@@ -1160,6 +1311,102 @@ static struct gatt_db *make_service_data_1_db(void)
return make_db(specs);
}
+#define CHARACTERISTIC_STR_AT(chr_handle, chr_uuid, permissions, properties, \
+ string) \
+ { \
+ .valid = true, \
+ .handle = chr_handle, \
+ .type = CHARACTERISTIC, \
+ .uuid = STR(chr_uuid), \
+ .att_permissions = permissions, \
+ .char_properties = properties, \
+ .value = (uint8_t *)string, \
+ .len = strlen(string), \
+ }
+
+#define DESCRIPTOR_STR_AT(desc_handle, desc_uuid, permissions, string) \
+ { \
+ .valid = true, \
+ .handle = desc_handle, \
+ .type = DESCRIPTOR, \
+ .uuid = STR(desc_uuid), \
+ .att_permissions = permissions, \
+ .value = (uint8_t *)string, \
+ .len = strlen(string), \
+ }
+
+static struct gatt_db *make_service_data_2_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, 6),
+ CHARACTERISTIC_STR_AT(0x0008,
+ GATT_CHARAC_MANUFACTURER_NAME_STRING,
+ BT_ATT_PERM_READ,
+ BT_GATT_CHRC_PROP_READ |
+ BT_GATT_CHRC_PROP_WRITE, ""),
+ DESCRIPTOR_STR_AT(0x000a, GATT_CHARAC_USER_DESC_UUID,
+ BT_ATT_PERM_READ, "Manufacturer Name"),
+ { }
+ };
+
+ return make_db(specs);
+}
+
+#define CHARACTERISTIC_AT(chr_handle, chr_uuid, permissions, properties, \
+ bytes...) \
+ { \
+ .valid = true, \
+ .handle = chr_handle, \
+ .type = CHARACTERISTIC, \
+ .uuid = STR(chr_uuid), \
+ .att_permissions = permissions, \
+ .char_properties = properties, \
+ .value = data(bytes), \
+ .len = sizeof(data(bytes)), \
+ }
+
+#define DESCRIPTOR_AT(desc_handle, desc_uuid, permissions, bytes...) \
+ { \
+ .valid = true, \
+ .handle = desc_handle, \
+ .type = DESCRIPTOR, \
+ .uuid = STR(desc_uuid), \
+ .att_permissions = permissions, \
+ .value = data(bytes), \
+ .len = sizeof(data(bytes)), \
+ }
+
+static struct gatt_db *make_service_data_3_db(void)
+{
+ const struct att_handle_spec specs[] = {
+ PRIMARY_SERVICE(0x0100, GAP_UUID, 0x0121 - 0x0100 + 1),
+ CHARACTERISTIC_STR_AT(0x0111, GATT_CHARAC_DEVICE_NAME,
+ BT_ATT_PERM_READ,
+ BT_GATT_CHRC_PROP_READ, "BlueZ"),
+ CHARACTERISTIC_AT(0x0121, GATT_CHARAC_APPEARANCE,
+ BT_ATT_PERM_READ,
+ BT_GATT_CHRC_PROP_READ, 0x00, 0x00),
+ PRIMARY_SERVICE(0x0200, GATT_UUID, 0x0200 - 0x0200 + 1),
+ PRIMARY_SERVICE(0x0300, HEART_RATE_UUID, 0x0320 - 0x0300 + 1),
+ CHARACTERISTIC_STR_AT(0x0311,
+ GATT_CHARAC_MANUFACTURER_NAME_STRING,
+ BT_ATT_PERM_READ,
+ BT_GATT_CHRC_PROP_READ |
+ BT_GATT_CHRC_PROP_WRITE, ""),
+ DESCRIPTOR_AT(0x0320, GATT_CLIENT_CHARAC_CFG_UUID,
+ BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+ 0x00, 0x00),
+ { }
+ };
+
+ return make_db(specs);
+}
+
/*
* Defined Test database 1:
* Tiny database fits into a single minimum sized-pdu.
@@ -1196,8 +1443,8 @@ static struct gatt_db *make_test_spec_small_db(void)
BT_GATT_CHRC_PROP_READ,
"BlueZ Unit Tester"),
CHARACTERISTIC(0000B009-0000-0000-0123-456789abcdef,
- BT_ATT_PERM_READ,
- BT_GATT_CHRC_PROP_READ, 0x09),
+ BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+ 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),
@@ -1740,9 +1987,6 @@ static const struct test_step test_long_read_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,
@@ -1953,11 +2197,14 @@ static const struct test_step test_indication_server_1 = {
int main(int argc, char *argv[])
{
- struct gatt_db *service_db_1, *ts_small_db, *ts_large_db_1;
+ struct gatt_db *service_db_1, *service_db_2, *service_db_3;
+ struct gatt_db *ts_small_db, *ts_large_db_1;
tester_init(&argc, &argv);
service_db_1 = make_service_data_1_db();
+ service_db_2 = make_service_data_2_db();
+ service_db_3 = make_service_data_3_db();
ts_small_db = make_test_spec_small_db();
ts_large_db_1 = make_test_spec_large_db_1();
@@ -2261,6 +2508,18 @@ int main(int argc, char *argv[])
raw_pdu(0x05, 0x01, 0x15, 0x00, 0x04, 0x29, 0x16, 0x00,
0x05, 0x29));
+ define_test_client("/TP/GAD/CL/BV-06-C/client-1", test_client,
+ service_db_1, NULL,
+ SERVICE_DATA_1_PDUS);
+
+ define_test_client("/TP/GAD/CL/BV-06-C/client-2", test_client,
+ service_db_2, NULL,
+ SERVICE_DATA_2_PDUS);
+
+ define_test_client("/TP/GAD/CL/BV-06-C/client-3", test_client,
+ service_db_3, NULL,
+ SERVICE_DATA_3_PDUS);
+
define_test_server("/TP/GAD/SR/BV-06-C/small", test_server,
ts_small_db, NULL,
raw_pdu(0x03, 0x00, 0x02),
@@ -3099,6 +3358,28 @@ int main(int argc, char *argv[])
raw_pdu(0x12, 0x07, 0x00, 0x01, 0x02, 0x03),
raw_pdu(0x01, 0x12, 0x07, 0x00, 0x0c));
+ define_test_server("/TP/GAW/SR/BV-07-C/small", test_server,
+ ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x16, 0x03, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x17, 0x03, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x18, 0x00),
+ raw_pdu(0x19),
+ raw_pdu(0x0a, 0x03, 0x00),
+ raw_pdu(0x0b, 0x42, 0x6c, 0x75, 0x65, 0x5a));
+
+ define_test_server("/TP/GAW/SR/BV-07-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x16, 0xc4, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x17, 0xc4, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x18, 0x00),
+ raw_pdu(0x19),
+ 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'));
+
define_test_server("/TP/GAW/SR/BV-03-C/small", test_server,
ts_small_db, NULL,
raw_pdu(0x03, 0x00, 0x02),
@@ -3135,6 +3416,390 @@ int main(int argc, char *argv[])
raw_pdu(0x12, 0x04, 0x00, 0x01, 0x02, 0x03),
raw_pdu(0x01, 0x12, 0x04, 0x00, 0x03));
+ define_test_client("/TP/GAW/CL/BV-05-C", test_client, service_db_1,
+ &test_long_write_1,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x16, 0x07, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 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(0x17, 0x07, 0x00, 0x00, 0x00,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 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(0x16, 0x07, 0x00, 0xfb, 0x01,
+ 0xff, 0xff, 0xff, 0xff, 0xff),
+ raw_pdu(0x17, 0x07, 0x00, 0xfb, 0x01,
+ 0xff, 0xff, 0xff, 0xff, 0xff),
+ raw_pdu(0x18, 0x01),
+ raw_pdu(0x19));
+
+ define_test_client("/TP/GAW/CL/BI-07-C", test_client, service_db_1,
+ &test_long_write_2,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x16, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x01, 0x16, 0x00, 0x00, 0x01),
+ raw_pdu(0x18, 0x00),
+ raw_pdu(0x19));
+
+ define_test_client("/TP/GAW/CL/BI-08-C", test_client, service_db_1,
+ &test_long_write_3,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x16, 0x03, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x01, 0x16, 0x03, 0x00, 0x03),
+ raw_pdu(0x18, 0x00),
+ raw_pdu(0x19));
+
+ define_test_client("/TP/GAW/CL/BI-11-C", test_client, service_db_1,
+ &test_long_write_4,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x16, 0x07, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x01, 0x16, 0x07, 0x00, 0x08),
+ raw_pdu(0x18, 0x00),
+ raw_pdu(0x19));
+
+ define_test_client("/TP/GAW/CL/BI-12-C", test_client, service_db_1,
+ &test_long_write_5,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x16, 0x07, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x01, 0x16, 0x07, 0x00, 0x05),
+ raw_pdu(0x18, 0x00),
+ raw_pdu(0x19));
+
+ define_test_client("/TP/GAW/CL/BI-13-C", test_client, service_db_1,
+ &test_long_write_6,
+ SERVICE_DATA_1_PDUS,
+ raw_pdu(0x16, 0x07, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x01, 0x16, 0x07, 0x00, 0x0c),
+ raw_pdu(0x18, 0x00),
+ raw_pdu(0x19));
+
+ define_test_server("/TP/GAW/SR/BV-05-C/small", test_server,
+ ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x16, 0x03, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+ raw_pdu(0x17, 0x03, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+ raw_pdu(0x16, 0x03, 0x00, 0x3f, 0x00, 0xff),
+ raw_pdu(0x17, 0x03, 0x00, 0x3f, 0x00, 0xff),
+ raw_pdu(0x18, 0x01),
+ raw_pdu(0x19));
+
+ define_test_server("/TP/GAW/SR/BV-05-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x16, 0x82, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+ raw_pdu(0x17, 0x82, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+ raw_pdu(0x16, 0x82, 0x00, 0x3f, 0x00, 0xff),
+ raw_pdu(0x17, 0x82, 0x00, 0x3f, 0x00, 0xff),
+ raw_pdu(0x18, 0x01),
+ raw_pdu(0x19));
+
+ define_test_server("/TP/GAW/SR/BI-07-C/small", test_server,
+ ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x16, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+ raw_pdu(0x01, 0x16, 0x00, 0x00, 0x01));
+
+ define_test_server("/TP/GAW/SR/BI-07-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x16, 0x0f, 0xf0, 0x00, 0x00, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+ raw_pdu(0x01, 0x16, 0x0f, 0xf0, 0x01));
+
+ define_test_server("/TP/GAW/SR/BI-08-C/small", test_server,
+ ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x16, 0x05, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+ raw_pdu(0x01, 0x16, 0x05, 0x00, 0x03));
+
+ define_test_server("/TP/GAW/SR/BI-08-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x16, 0x73, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+ raw_pdu(0x01, 0x16, 0x73, 0x00, 0x03));
+
+ define_test_server("/TP/GAW/SR/BV-06-C/small", test_server,
+ ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x16, 0x03, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x17, 0x03, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x18, 0x01),
+ raw_pdu(0x19));
+
+ define_test_server("/TP/GAW/SR/BV-06-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x16, 0x82, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x17, 0x82, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x18, 0x01),
+ raw_pdu(0x19));
+
+ define_test_server("/TP/GAW/SR/BV-10-C/small", test_server,
+ ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x16, 0x03, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x17, 0x03, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x16, 0x15, 0xf0, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x17, 0x15, 0xf0, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x16, 0x03, 0x00, 0x03, 0x00, 0x04, 0x05, 0x06),
+ raw_pdu(0x17, 0x03, 0x00, 0x03, 0x00, 0x04, 0x05, 0x06),
+ raw_pdu(0x16, 0x15, 0xf0, 0x03, 0x00, 0x04, 0x05, 0x06),
+ raw_pdu(0x17, 0x15, 0xf0, 0x03, 0x00, 0x04, 0x05, 0x06),
+ raw_pdu(0x18, 0x01),
+ raw_pdu(0x19));
+
+ define_test_server("/TP/GAW/SR/BV-10-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x16, 0x82, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x17, 0x82, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x16, 0x25, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x17, 0x25, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x16, 0x82, 0x00, 0x03, 0x00, 0x04, 0x05, 0x06),
+ raw_pdu(0x17, 0x82, 0x00, 0x03, 0x00, 0x04, 0x05, 0x06),
+ raw_pdu(0x16, 0x25, 0x00, 0x03, 0x00, 0x04, 0x05, 0x06),
+ raw_pdu(0x17, 0x25, 0x00, 0x03, 0x00, 0x04, 0x05, 0x06),
+ raw_pdu(0x18, 0x01),
+ raw_pdu(0x19));
+
+ define_test_server("/TP/GAW/SR/BI-14-C/small", test_server,
+ ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x16, 0x00, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x01, 0x16, 0x00, 0x00, 0x01));
+
+ define_test_server("/TP/GAW/SR/BI-14-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x16, 0x0f, 0xf0, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x01, 0x16, 0x0f, 0xf0, 0x01));
+
+ define_test_server("/TP/GAW/SR/BI-15-C/small", test_server,
+ ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x16, 0x05, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x01, 0x16, 0x05, 0x00, 0x03));
+
+ define_test_server("/TP/GAW/SR/BI-15-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x16, 0x73, 0x00, 0x00, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x01, 0x16, 0x73, 0x00, 0x03));
+
define_test_client("/TP/GAW/CL/BV-08-C", test_client, service_db_1,
&test_write_7,
SERVICE_DATA_1_PDUS,
@@ -3171,5 +3836,101 @@ int main(int argc, char *argv[])
raw_pdu(0x12, 0x08, 0x00, 0x01, 0x02, 0x03),
raw_pdu(0x01, 0x12, 0x08, 0x00, 0x0c));
+ define_test_server("/TP/GAW/SR/BV-08-C/small", test_server,
+ ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x12, 0x04, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x13));
+
+ define_test_server("/TP/GAW/SR/BV-08-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x12, 0x83, 0x00, 0x01, 0x02, 0x03),
+ raw_pdu(0x13));
+
+ define_test_server("/TP/GAW/SR/BI-20-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-20-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-21-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-21-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_server("/TP/GAW/SR/BV-09-C/small", test_server,
+ ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x16, 0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+ raw_pdu(0x17, 0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+ raw_pdu(0x16, 0x04, 0x00, 0x3f, 0x00, 0xff),
+ raw_pdu(0x17, 0x04, 0x00, 0x3f, 0x00, 0xff),
+ raw_pdu(0x18, 0x01),
+ raw_pdu(0x19));
+
+ define_test_server("/TP/GAW/SR/BV-09-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x16, 0x83, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+ raw_pdu(0x17, 0x83, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+ raw_pdu(0x16, 0x83, 0x00, 0x3f, 0x00, 0xff),
+ raw_pdu(0x17, 0x83, 0x00, 0x3f, 0x00, 0xff),
+ raw_pdu(0x18, 0x01),
+ raw_pdu(0x19));
+
+ define_test_server("/TP/GAW/SR/BI-25-C/small", test_server,
+ ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x16, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+ raw_pdu(0x01, 0x16, 0x00, 0x00, 0x01));
+
+ define_test_server("/TP/GAW/SR/BI-25-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x16, 0x0f, 0xf0, 0x00, 0x00, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+ raw_pdu(0x01, 0x16, 0x0f, 0xf0, 0x01));
+
+ define_test_server("/TP/GAW/SR/BI-26-C/small", test_server,
+ ts_small_db, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x16, 0x13, 0xf0, 0x00, 0x00, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+ raw_pdu(0x01, 0x16, 0x13, 0xf0, 0x03));
+
+ define_test_server("/TP/GAW/SR/BI-26-C/large-1", test_server,
+ ts_large_db_1, NULL,
+ raw_pdu(0x03, 0x00, 0x02),
+ raw_pdu(0x16, 0x04, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff),
+ raw_pdu(0x01, 0x16, 0x04, 0x00, 0x03));
+
return tester_run();
}
diff --git a/unit/test-gdbus-client.c b/unit/test-gdbus-client.c
index 11ad1f75..ad426fe2 100644
--- a/unit/test-gdbus-client.c
+++ b/unit/test-gdbus-client.c
@@ -353,7 +353,7 @@ static void proxy_get_string(GDBusProxy *proxy, void *user_data)
g_assert(dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_STRING);
dbus_message_iter_get_basic(&iter, &string);
- g_assert(g_strcmp0(string, "value") == 0);
+ g_assert_cmpstr(string, ==, "value");
g_dbus_client_unref(context->dbus_client);
}
@@ -908,6 +908,56 @@ static void client_proxy_removed(void)
destroy_context(context);
}
+static void client_no_object_manager(void)
+{
+ struct context *context = create_context();
+ DBusConnection *conn;
+ DBusMessageIter iter;
+ static const GDBusPropertyTable string_properties[] = {
+ { "String", "s", get_string, set_string, string_exists },
+ { },
+ };
+
+ if (context == NULL)
+ return;
+
+ conn = g_dbus_setup_private(DBUS_BUS_SESSION, SERVICE_NAME1, NULL);
+ g_assert(conn != NULL);
+
+ context->data = g_strdup("value");
+
+ g_dbus_register_interface(conn,
+ SERVICE_PATH, SERVICE_NAME1,
+ methods, signals, string_properties,
+ context, NULL);
+
+ context->dbus_client = g_dbus_client_new_full(context->dbus_conn,
+ SERVICE_NAME1, SERVICE_PATH,
+ NULL);
+
+ g_dbus_client_set_disconnect_watch(context->dbus_client,
+ disconnect_handler, context);
+
+ context->proxy = g_dbus_proxy_new(context->dbus_client, SERVICE_PATH,
+ SERVICE_NAME1);
+
+ g_dbus_client_set_proxy_handlers(context->dbus_client, proxy_get_string,
+ NULL, NULL, context);
+
+ g_assert(!g_dbus_proxy_get_property(context->proxy, "String", &iter));
+
+ g_main_loop_run(context->main_loop);
+
+ g_dbus_proxy_unref(context->proxy);
+ g_dbus_unregister_interface(conn, SERVICE_PATH, SERVICE_NAME1);
+
+ dbus_connection_flush(conn);
+ dbus_connection_close(conn);
+ dbus_connection_unref(conn);
+
+ destroy_context(context);
+}
+
static void proxy_force_disconnect(GDBusProxy *proxy, void *user_data)
{
struct context *context = user_data;
@@ -1051,6 +1101,9 @@ int main(int argc, char *argv[])
g_test_add_func("/gdbus/client_proxy_removed", client_proxy_removed);
+ g_test_add_func("/gdbus/client_no_object_manager",
+ client_no_object_manager);
+
g_test_add_func("/gdbus/client_force_disconnect",
client_force_disconnect);
diff --git a/unit/test-hfp.c b/unit/test-hfp.c
index face9a4c..66966ce5 100644
--- a/unit/test-hfp.c
+++ b/unit/test-hfp.c
@@ -95,9 +95,8 @@ struct test_data {
}; \
static struct test_data data; \
data.test_name = g_strdup(name); \
- data.pdu_list = g_malloc(sizeof(pdus)); \
+ data.pdu_list = g_memdup(pdus, sizeof(pdus)); \
data.result_func = result_function; \
- memcpy(data.pdu_list, pdus, sizeof(pdus)); \
g_test_add_data_func(name, &data, function); \
data.test_handler = test_handler; \
} while (0)
@@ -110,10 +109,9 @@ struct test_data {
}; \
static struct test_data data; \
data.test_name = g_strdup(name); \
- data.pdu_list = g_malloc(sizeof(pdus)); \
+ data.pdu_list = g_memdup(pdus, sizeof(pdus)); \
data.hf_result_func = result_func; \
data.response_func = response_function; \
- memcpy(data.pdu_list, pdus, sizeof(pdus)); \
g_test_add_data_func(name, &data, function); \
data.test_handler = test_hf_handler; \
} while (0)
diff --git a/unit/test-hog.c b/unit/test-hog.c
new file mode 100644
index 00000000..778f0879
--- /dev/null
+++ b/unit/test-hog.c
@@ -0,0 +1,440 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2015 Intel Corporation. All rights reserved.
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <unistd.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <fcntl.h>
+
+#include <glib.h>
+
+#include "src/shared/util.h"
+#include "src/shared/tester.h"
+
+#include "attrib/gattrib.h"
+
+#include "android/hog.h"
+
+struct test_pdu {
+ bool valid;
+ const uint8_t *data;
+ size_t size;
+};
+
+struct test_data {
+ char *test_name;
+ struct test_pdu *pdu_list;
+};
+
+struct context {
+ GAttrib *attrib;
+ struct bt_hog *hog;
+ guint source;
+ guint process;
+ int fd;
+ unsigned int pdu_offset;
+ const struct test_data *data;
+};
+
+#define data(args...) ((const unsigned char[]) { args })
+
+#define raw_pdu(args...) \
+{ \
+ .valid = true, \
+ .data = data(args), \
+ .size = sizeof(data(args)),\
+}
+
+#define false_pdu() \
+{ \
+ .valid = false, \
+}
+
+#define define_test(name, function, args...) \
+ do { \
+ const struct test_pdu pdus[] = { \
+ args, { } \
+ }; \
+ static struct test_data data; \
+ data.test_name = g_strdup(name); \
+ data.pdu_list = g_memdup(pdus, sizeof(pdus)); \
+ tester_add(name, &data, NULL, function, NULL); \
+ } while (0)
+
+static void test_debug(const char *str, void *user_data)
+{
+ const char *prefix = user_data;
+
+ tester_debug("%s%s", prefix, str);
+}
+
+static gboolean context_quit(gpointer user_data)
+{
+ struct context *context = user_data;
+
+ if (context->process > 0)
+ g_source_remove(context->process);
+
+ if (context->source > 0)
+ g_source_remove(context->source);
+
+ bt_hog_unref(context->hog);
+
+ g_attrib_unref(context->attrib);
+
+ g_free(context);
+
+ tester_test_passed();
+
+ return FALSE;
+}
+
+static gboolean send_pdu(gpointer user_data)
+{
+ struct context *context = user_data;
+ const struct test_pdu *pdu;
+ ssize_t len;
+
+ pdu = &context->data->pdu_list[context->pdu_offset++];
+
+ len = write(context->fd, pdu->data, pdu->size);
+
+ util_hexdump('<', pdu->data, len, test_debug, "hog: ");
+
+ g_assert_cmpint(len, ==, pdu->size);
+
+ context->process = 0;
+
+ if (!context->data->pdu_list[context->pdu_offset].valid)
+ context_quit(context);
+
+ return FALSE;
+}
+
+static gboolean test_handler(GIOChannel *channel, GIOCondition cond,
+ gpointer user_data)
+{
+ struct context *context = user_data;
+ unsigned char buf[512];
+ const struct test_pdu *pdu;
+ ssize_t len;
+ int fd;
+
+ pdu = &context->data->pdu_list[context->pdu_offset++];
+
+ if (cond & (G_IO_NVAL | G_IO_ERR | G_IO_HUP)) {
+ context->source = 0;
+ g_print("%s: cond %x\n", __func__, cond);
+ return FALSE;
+ }
+
+ fd = g_io_channel_unix_get_fd(channel);
+
+ len = read(fd, buf, sizeof(buf));
+
+ g_assert(len > 0);
+
+ util_hexdump('>', buf, len, test_debug, "hog: ");
+
+ g_assert_cmpint(len, ==, pdu->size);
+
+ g_assert(memcmp(buf, pdu->data, pdu->size) == 0);
+
+ context->process = g_idle_add(send_pdu, context);
+
+ return TRUE;
+}
+
+static struct context *create_context(gconstpointer data)
+{
+ struct context *context;
+ GIOChannel *channel, *att_io;
+ int err, sv[2], fd;
+ char name[] = "bluez-hog";
+ uint16_t vendor = 0x0002;
+ uint16_t product = 0x0001;
+ uint16_t version = 0x0001;
+
+ context = g_new0(struct context, 1);
+ err = socketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CLOEXEC, 0, sv);
+ g_assert(err == 0);
+
+ att_io = g_io_channel_unix_new(sv[0]);
+
+ g_io_channel_set_close_on_unref(att_io, TRUE);
+
+ context->attrib = g_attrib_new(att_io, 23);
+ g_assert(context->attrib);
+
+ g_io_channel_unref(att_io);
+
+ fd = open("/dev/null", O_WRONLY | O_CLOEXEC);
+ g_assert(fd > 0);
+
+ context->hog = bt_hog_new(fd, name, vendor, product, version, NULL);
+ g_assert(context->hog);
+
+ channel = g_io_channel_unix_new(sv[1]);
+
+ g_io_channel_set_close_on_unref(channel, TRUE);
+ g_io_channel_set_encoding(channel, NULL, NULL);
+ g_io_channel_set_buffered(channel, FALSE);
+
+ context->source = g_io_add_watch(channel,
+ G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ test_handler, context);
+ g_assert(context->source > 0);
+
+ g_io_channel_unref(channel);
+
+ context->fd = sv[1];
+ context->data = data;
+
+ return context;
+}
+
+static void test_hog(gconstpointer data)
+{
+ struct context *context = create_context(data);
+
+ g_assert(bt_hog_attach(context->hog, context->attrib));
+}
+
+int main(int argc, char *argv[])
+{
+ tester_init(&argc, &argv);
+
+ define_test("/TP/HGRF/RH/BV-01-I", test_hog,
+ raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28),
+ raw_pdu(0x11, 0x06, 0x01, 0x00, 0x04, 0x00, 0x12,
+ 0x18, 0x05, 0x00, 0x08, 0x00, 0x12, 0x18),
+ raw_pdu(0x10, 0x09, 0x00, 0xff, 0xff, 0x00, 0x28),
+ raw_pdu(0x01, 0x10, 0x09, 0x00, 0x0a),
+ raw_pdu(0x08, 0x01, 0x00, 0x04, 0x00, 0x03, 0x28),
+ raw_pdu(0x09, 0x07, 0x03, 0x00, 0x02, 0x04, 0x00,
+ 0x4b, 0x2a),
+ raw_pdu(0x08, 0x01, 0x00, 0x04, 0x00, 0x02, 0x28),
+ raw_pdu(0x01, 0x08, 0x01, 0x00, 0x0a),
+ raw_pdu(0x08, 0x05, 0x00, 0x08, 0x00, 0x02, 0x28),
+ raw_pdu(0x01, 0x08, 0x05, 0x00, 0x0a),
+ raw_pdu(0x08, 0x05, 0x00, 0x08, 0x00, 0x03, 0x28),
+ raw_pdu(0x09, 0x07, 0x07, 0x00, 0x02, 0x08, 0x00,
+ 0x4b, 0x2a),
+ raw_pdu(0x0a, 0x04, 0x00),
+ raw_pdu(0x0b, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
+ 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
+ 0x15, 0x16),
+ raw_pdu(0x0a, 0x08, 0x00),
+ raw_pdu(0x0b, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
+ 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
+ 0x15, 0x16),
+ raw_pdu(0x0c, 0x04, 0x00, 0x16, 0x00),
+ raw_pdu(0x0d, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
+ 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
+ 0x15, 0x16),
+ raw_pdu(0x0c, 0x08, 0x00, 0x16, 0x00),
+ raw_pdu(0x0d, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
+ 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14,
+ 0x15, 0x16),
+ raw_pdu(0x0c, 0x04, 0x00, 0x2c, 0x00),
+ raw_pdu(0x0d, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
+ 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13),
+ raw_pdu(0x0c, 0x08, 0x00, 0x2c, 0x00),
+ raw_pdu(0x0d, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
+ 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13));
+
+ define_test("/TP/HGRF/RH/BV-08-I", test_hog,
+ raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28),
+ raw_pdu(0x11, 0x06, 0x01, 0x00, 0x05, 0x00, 0x12,
+ 0x18, 0x06, 0x00, 0x0a, 0x00, 0x12, 0x18),
+ raw_pdu(0x10, 0x0b, 0x00, 0xff, 0xff, 0x00, 0x28),
+ raw_pdu(0x01, 0x10, 0x0b, 0x00, 0x0a),
+ raw_pdu(0x08, 0x01, 0x00, 0x05, 0x00, 0x03, 0x28),
+ raw_pdu(0x09, 0x07, 0x03, 0x00, 0x0a, 0x04, 0x00,
+ 0x4d, 0x2a),
+ raw_pdu(0x08, 0x01, 0x00, 0x05, 0x00, 0x02, 0x28),
+ raw_pdu(0x01, 0x08, 0x01, 0x00, 0x0a),
+ raw_pdu(0x08, 0x06, 0x00, 0x0a, 0x00, 0x02, 0x28),
+ raw_pdu(0x01, 0x08, 0x06, 0x00, 0x0a),
+ raw_pdu(0x08, 0x06, 0x00, 0x0a, 0x00, 0x03, 0x28),
+ raw_pdu(0x09, 0x07, 0x08, 0x00, 0x0a, 0x09, 0x00,
+ 0x4d, 0x2a),
+ raw_pdu(0x08, 0x04, 0x00, 0x05, 0x00, 0x03, 0x28),
+ raw_pdu(0x01, 0x08, 0x04, 0x00, 0x0a),
+ raw_pdu(0x08, 0x09, 0x00, 0x0a, 0x00, 0x03, 0x28),
+ raw_pdu(0x01, 0x08, 0x09, 0x00, 0x0a),
+ raw_pdu(0x0a, 0x04, 0x00),
+ raw_pdu(0x0b, 0xee, 0xee, 0xff, 0xff),
+ raw_pdu(0x04, 0x05, 0x00, 0x05, 0x00),
+ raw_pdu(0x05, 0x01, 0x05, 0x00, 0x08, 0x29),
+ raw_pdu(0x0a, 0x09, 0x00),
+ raw_pdu(0x0b, 0xff, 0xff, 0xee, 0xee),
+ raw_pdu(0x04, 0x0a, 0x00, 0x0a, 0x00),
+ raw_pdu(0x05, 0x01, 0x0a, 0x00, 0x08, 0x29),
+ raw_pdu(0x0a, 0x05, 0x00),
+ raw_pdu(0x0b, 0x01, 0x03),
+ raw_pdu(0x0a, 0x0a, 0x00),
+ raw_pdu(0x0b, 0x02, 0x03));
+
+ define_test("/TP/HGRF/RH/BV-09-I", test_hog,
+ raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28),
+ raw_pdu(0x11, 0x06, 0x01, 0x00, 0x04, 0x00, 0x12,
+ 0x18, 0x05, 0x00, 0x08, 0x00, 0x12, 0x18),
+ raw_pdu(0x10, 0x09, 0x00, 0xff, 0xff, 0x00, 0x28),
+ raw_pdu(0x01, 0x10, 0x09, 0x00, 0x0a),
+ raw_pdu(0x08, 0x01, 0x00, 0x04, 0x00, 0x03, 0x28),
+ raw_pdu(0x09, 0x07, 0x03, 0x00, 0x02, 0x04, 0x00,
+ 0x4a, 0x2a),
+ raw_pdu(0x08, 0x01, 0x00, 0x04, 0x00, 0x02, 0x28),
+ raw_pdu(0x01, 0x08, 0x01, 0x00, 0x0a),
+ raw_pdu(0x08, 0x05, 0x00, 0x08, 0x00, 0x02, 0x28),
+ raw_pdu(0x01, 0x08, 0x05, 0x00, 0x0a),
+ raw_pdu(0x08, 0x05, 0x00, 0x08, 0x00, 0x03, 0x28),
+ raw_pdu(0x09, 0x07, 0x07, 0x00, 0x02, 0x08, 0x00,
+ 0x4a, 0x2a),
+ raw_pdu(0x0a, 0x04, 0x00),
+ raw_pdu(0x0b, 0x01, 0x11, 0x00, 0x01),
+ raw_pdu(0x0a, 0x08, 0x00),
+ raw_pdu(0x0b, 0x01, 0x11, 0x00, 0x01));
+
+ define_test("/TP/HGRF/RH/BV-06-I", test_hog,
+ raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28),
+ raw_pdu(0x11, 0x06, 0x01, 0x00, 0x05, 0x00, 0x12,
+ 0x18, 0x06, 0x00, 0x0a, 0x00, 0x12, 0x18),
+ raw_pdu(0x10, 0x0b, 0x00, 0xff, 0xff, 0x00, 0x28),
+ raw_pdu(0x01, 0x10, 0x0b, 0x00, 0x0a),
+ raw_pdu(0x08, 0x01, 0x00, 0x05, 0x00, 0x03, 0x28),
+ raw_pdu(0x09, 0x07, 0x03, 0x00, 0x0a, 0x04, 0x00,
+ 0x4d, 0x2a),
+ raw_pdu(0x08, 0x01, 0x00, 0x05, 0x00, 0x02, 0x28),
+ raw_pdu(0x01, 0x08, 0x01, 0x00, 0x0a),
+ raw_pdu(0x08, 0x06, 0x00, 0x0a, 0x00, 0x02, 0x28),
+ raw_pdu(0x01, 0x08, 0x06, 0x00, 0x0a),
+ raw_pdu(0x08, 0x06, 0x00, 0x0a, 0x00, 0x03, 0x28),
+ raw_pdu(0x09, 0x07, 0x08, 0x00, 0x0a, 0x09, 0x00,
+ 0x4d, 0x2a),
+ raw_pdu(0x08, 0x04, 0x00, 0x05, 0x00, 0x03, 0x28),
+ raw_pdu(0x01, 0x08, 0x05, 0x00, 0x0a),
+ raw_pdu(0x08, 0x09, 0x00, 0x0a, 0x00, 0x03, 0x28),
+ raw_pdu(0x01, 0x08, 0x09, 0x00, 0x0a),
+ raw_pdu(0x0a, 0x04, 0x00),
+ raw_pdu(0x0b, 0xee, 0xee, 0xff, 0xff),
+ raw_pdu(0x04, 0x05, 0x00, 0x05, 0x00),
+ raw_pdu(0x05, 0x01, 0x05, 0x00, 0x08, 0x29),
+ raw_pdu(0x0a, 0x09, 0x00),
+ raw_pdu(0x0b, 0xff, 0xff, 0xee, 0xee),
+ raw_pdu(0x04, 0x0a, 0x00, 0x0a, 0x00),
+ raw_pdu(0x05, 0x01, 0x0a, 0x00, 0x08, 0x29),
+ raw_pdu(0x0a, 0x05, 0x00),
+ raw_pdu(0x0b, 0x01, 0x02),
+ raw_pdu(0x0a, 0x0a, 0x00),
+ raw_pdu(0x0b, 0x02, 0x02));
+
+ define_test("/TP/HGCF/RH/BV-01-I", test_hog,
+ raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28),
+ raw_pdu(0x11, 0x06, 0x01, 0x00, 0x06, 0x00, 0x12,
+ 0x18, 0x07, 0x00, 0x0c, 0x00, 0x12, 0x18),
+ raw_pdu(0x10, 0x0d, 0x00, 0xff, 0xff, 0x00, 0x28),
+ raw_pdu(0x01, 0x10, 0x0d, 0x00, 0x0a),
+ raw_pdu(0x08, 0x01, 0x00, 0x06, 0x00, 0x03, 0x28),
+ raw_pdu(0x09, 0x07, 0x03, 0x00, 0x1a, 0x04, 0x00,
+ 0x4d, 0x2a),
+ raw_pdu(0x08, 0x01, 0x00, 0x06, 0x00, 0x02, 0x28),
+ raw_pdu(0x01, 0x08, 0x01, 0x00, 0x0a),
+ raw_pdu(0x08, 0x07, 0x00, 0x0c, 0x00, 0x02, 0x28),
+ raw_pdu(0x01, 0x08, 0x07, 0x00, 0x0a),
+ raw_pdu(0x08, 0x07, 0x00, 0x0c, 0x00, 0x03, 0x28),
+ raw_pdu(0x09, 0x07, 0x09, 0x00, 0x1a, 0x0a, 0x00,
+ 0x4d, 0x2a),
+ raw_pdu(0x08, 0x04, 0x00, 0x06, 0x00, 0x03, 0x28),
+ raw_pdu(0x01, 0x08, 0x04, 0x00, 0x0a),
+ raw_pdu(0x08, 0x0a, 0x00, 0x0c, 0x00, 0x03, 0x28),
+ raw_pdu(0x01, 0x08, 0x0a, 0x00, 0x0a),
+ raw_pdu(0x0a, 0x04, 0x00),
+ raw_pdu(0x0b, 0xed, 0x00),
+ raw_pdu(0x04, 0x05, 0x00, 0x06, 0x00),
+ raw_pdu(0x05, 0x01, 0x05, 0x00, 0x02, 0x29,
+ 0x06, 0x00, 0x08, 0x29),
+ raw_pdu(0x0a, 0x0a, 0x00),
+ raw_pdu(0x0b, 0xed, 0x00),
+ raw_pdu(0x04, 0x0b, 0x00, 0x0c, 0x00),
+ raw_pdu(0x05, 0x01, 0x0b, 0x00, 0x02, 0x29,
+ 0x0c, 0x00, 0x08, 0x29),
+ raw_pdu(0x0a, 0x06, 0x00),
+ raw_pdu(0x0b, 0x01, 0x01),
+ raw_pdu(0x0a, 0x0c, 0x00),
+ raw_pdu(0x0b, 0x02, 0x01),
+ raw_pdu(0x0a, 0x05, 0x00),
+ raw_pdu(0x0b, 0x00, 0x00),
+ raw_pdu(0x0a, 0x0b, 0x00),
+ raw_pdu(0x0b, 0x00, 0x00),
+ raw_pdu(0x12, 0x05, 0x00, 0x01, 0x00),
+ raw_pdu(0x13),
+ raw_pdu(0x12, 0x0b, 0x00, 0x01, 0x00),
+ raw_pdu(0x13));
+
+ define_test("/TP/HGRF/RH/BV-02-I", test_hog,
+ raw_pdu(0x10, 0x01, 0x00, 0xff, 0xff, 0x00, 0x28),
+ raw_pdu(0x11, 0x06, 0x01, 0x00, 0x05, 0x00, 0x12,
+ 0x18, 0x06, 0x00, 0x0a, 0x00, 0x12, 0x18),
+ raw_pdu(0x10, 0x0b, 0x00, 0xff, 0xff, 0x00, 0x28),
+ raw_pdu(0x01, 0x10, 0x0b, 0x00, 0x0a),
+ raw_pdu(0x08, 0x01, 0x00, 0x05, 0x00, 0x03, 0x28),
+ raw_pdu(0x09, 0x07, 0x03, 0x00, 0x02, 0x04, 0x00,
+ 0x4b, 0x2a),
+ raw_pdu(0x08, 0x01, 0x00, 0x05, 0x00, 0x02, 0x28),
+ raw_pdu(0x01, 0x08, 0x01, 0x00, 0x0a),
+ raw_pdu(0x08, 0x06, 0x00, 0x0a, 0x00, 0x02, 0x28),
+ raw_pdu(0x01, 0x08, 0x06, 0x00, 0x0a),
+ raw_pdu(0x08, 0x06, 0x00, 0x0a, 0x00, 0x03, 0x28),
+ raw_pdu(0x09, 0x07, 0x08, 0x00, 0x02, 0x09, 0x00,
+ 0x4b, 0x2a),
+ raw_pdu(0x08, 0x04, 0x00, 0x05, 0x00, 0x03, 0x28),
+ raw_pdu(0x01, 0x08, 0x04, 0x00, 0x0a),
+ raw_pdu(0x08, 0x09, 0x00, 0x0a, 0x00, 0x03, 0x28),
+ raw_pdu(0x01, 0x08, 0x09, 0x00, 0x0a),
+ raw_pdu(0x0a, 0x04, 0x00),
+ raw_pdu(0x0b, 0x01, 0x02, 0x03),
+ raw_pdu(0x04, 0x05, 0x00, 0x05, 0x00),
+ raw_pdu(0x05, 0x01, 0x05, 0x00, 0x07, 0x29),
+ raw_pdu(0x0a, 0x09, 0x00),
+ raw_pdu(0x0b, 0x01, 0x02, 0x03),
+ raw_pdu(0x04, 0x0a, 0x00, 0x0a, 0x00),
+ raw_pdu(0x05, 0x01, 0x0a, 0x00, 0x07, 0x29),
+ raw_pdu(0x0a, 0x05, 0x00),
+ raw_pdu(0x0b, 0x19, 0x2a),
+ raw_pdu(0x0a, 0x0a, 0x00),
+ raw_pdu(0x0b, 0x19, 0x2a));
+
+ return tester_run();
+}
diff --git a/unit/test-sdp.c b/unit/test-sdp.c
index a89dbfca..b4ef4d1e 100644
--- a/unit/test-sdp.c
+++ b/unit/test-sdp.c
@@ -77,8 +77,7 @@ struct test_data {
}; \
static struct test_data data; \
data.mtu = _mtu; \
- data.pdu_list = g_malloc(sizeof(pdus)); \
- memcpy(data.pdu_list, pdus, sizeof(pdus)); \
+ data.pdu_list = g_memdup(pdus, sizeof(pdus)); \
g_test_add_data_func(name, &data, test_sdp); \
} while (0)
diff --git a/unit/test-uhid.c b/unit/test-uhid.c
index 85e13567..b48e0faa 100644
--- a/unit/test-uhid.c
+++ b/unit/test-uhid.c
@@ -73,8 +73,7 @@ struct context {
}; \
static struct test_data data; \
data.test_name = g_strdup(name); \
- data.pdu_list = g_malloc(sizeof(pdus)); \
- memcpy(data.pdu_list, pdus, sizeof(pdus)); \
+ data.pdu_list = g_memdup(pdus, sizeof(pdus)); \
g_test_add_data_func(name, &data, function); \
} while (0)