summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorh.sandeep <h.sandeep@samsung.com>2017-02-02 16:49:57 +0530
committerh.sandeep <h.sandeep@samsung.com>2017-03-28 16:33:31 +0530
commit2fe71a1f4ab196fe6f852e05794a187b2d3ec81b (patch)
tree557e8b03309bbcdc6edfe7828281182557273e80
parente1364a95f5eb5595a9f01064d8cb3dd10a9d0f40 (diff)
downloadbluez-accepted/tizen_common.tar.gz
bluez-accepted/tizen_common.tar.bz2
bluez-accepted/tizen_common.zip
Change-Id: I5d5a9d9e2e7811af3dd3878b4f740e1f1a019d51 Signed-off-by: h.sandeep <h.sandeep@samsung.com>
-rwxr-xr-xMakefile.am17
-rwxr-xr-xMakefile.obexd4
-rwxr-xr-xMakefile.plugins75
-rwxr-xr-xMakefile.tools2
-rwxr-xr-xandroid/bas.c379
-rwxr-xr-xandroid/bas.h32
-rwxr-xr-xandroid/dis.c304
-rwxr-xr-xandroid/dis.h39
-rwxr-xr-xandroid/hog.c1511
-rwxr-xr-xandroid/hog.h41
-rwxr-xr-xandroid/sco-ipc-api.txt37
-rwxr-xr-xandroid/scpp.c371
-rwxr-xr-xandroid/scpp.h35
-rwxr-xr-xandroid/socket-api.txt61
-rwxr-xr-xattrib/att.c17
-rwxr-xr-xattrib/att.h20
-rwxr-xr-xattrib/gatt-service.c22
-rwxr-xr-xattrib/gatt-service.h12
-rwxr-xr-xattrib/gatt.c50
-rwxr-xr-xattrib/gatt.h5
-rwxr-xr-xattrib/interactive.c21
-rwxr-xr-xclient/main.c6
-rwxr-xr-xconfigure.ac58
-rwxr-xr-xdoc/adapter-api.txt162
-rwxr-xr-xdoc/coding-style.txt279
-rwxr-xr-xdoc/device-api.txt47
-rwxr-xr-xdoc/gatt-api.txt44
-rwxr-xr-xdoc/maintainer-guidelines.txt114
-rwxr-xr-xdoc/mgmt-api.txt62
-rwxr-xr-xdoc/network-api.txt14
-rwxr-xr-xdoc/oob-api.txt38
-rwxr-xr-xdoc/profile-api.txt93
-rwxr-xr-xdoc/settings-storage.txt22
-rwxr-xr-xgdbus/gdbus.h6
-rwxr-xr-xgdbus/object.c128
-rwxr-xr-xgobex/gobex-apparam.c9
-rwxr-xr-xgobex/gobex-apparam.h4
-rwxr-xr-xgobex/gobex-header.c13
-rwxr-xr-xgobex/gobex.c5
-rwxr-xr-xlib/bluetooth.h10
-rwxr-xr-xlib/hci.c175
-rwxr-xr-xlib/hci.h57
-rwxr-xr-xlib/hci_lib.h16
-rwxr-xr-xlib/l2cap.h12
-rwxr-xr-xlib/mgmt.h340
-rwxr-xr-xlib/uuid.h20
-rwxr-xr-xmonitor/broadcom.c758
-rwxr-xr-xmonitor/control.c12
-rwxr-xr-xmonitor/control.h4
-rwxr-xr-xmonitor/display.h4
-rwxr-xr-xmonitor/main.c33
-rwxr-xr-xmonitor/packet.c39
-rwxr-xr-xmonitor/packet.h8
-rwxr-xr-xobexd/client/bluetooth.c11
-rwxr-xr-xobexd/client/manager.c29
-rwxr-xr-xobexd/client/mns-tizen.c271
-rwxr-xr-xobexd/client/mns-tizen.h24
-rwxr-xr-xobexd/client/opp.c14
-rwxr-xr-xobexd/client/pbap.c60
-rwxr-xr-xobexd/client/session.c35
-rwxr-xr-xobexd/client/session.h5
-rwxr-xr-xobexd/client/transfer.c19
-rwxr-xr-xobexd/plugins/ftp.c56
-rwxr-xr-xobexd/plugins/messages-tizen.c1547
-rwxr-xr-xobexd/plugins/messages-tracker.c345
-rwxr-xr-xobexd/plugins/pbap.c161
-rwxr-xr-xobexd/plugins/phonebook-dummy.c5
-rwxr-xr-xobexd/plugins/phonebook-ebook.c708
-rwxr-xr-xobexd/plugins/phonebook-tizen.c813
-rwxr-xr-xobexd/plugins/phonebook-tracker.c1718
-rwxr-xr-xobexd/plugins/syncevolution.c483
-rwxr-xr-xobexd/src/main.c8
-rwxr-xr-xobexd/src/manager.c129
-rwxr-xr-xobexd/src/obex.c16
-rwxr-xr-xobexd/src/obexd.h3
-rwxr-xr-xobexd/src/org.bluez.obex.service3
-rwxr-xr-xpackaging/bluez.spec184
-rwxr-xr-xplugins/dbusoob.c273
-rwxr-xr-xplugins/neard.c7
-rwxr-xr-xplugins/policy.c28
-rwxr-xr-xprofile.h164
-rwxr-xr-xprofiles/alert/server.c1044
-rwxr-xr-xprofiles/audio/a2dp.c135
-rwxr-xr-xprofiles/audio/avctp.c77
-rwxr-xr-xprofiles/audio/avctp.h3
-rwxr-xr-xprofiles/audio/avdtp.c450
-rwxr-xr-xprofiles/audio/avdtp.h4
-rwxr-xr-xprofiles/audio/avrcp.c384
-rwxr-xr-xprofiles/audio/media.c419
-rwxr-xr-xprofiles/audio/player.c106
-rwxr-xr-xprofiles/audio/player.h7
-rwxr-xr-xprofiles/audio/sink.c30
-rwxr-xr-xprofiles/cyclingspeed/cyclingspeed.c1266
-rwxr-xr-xprofiles/gap/gas.c38
-rwxr-xr-xprofiles/heartrate/heartrate.c870
-rwxr-xr-xprofiles/input/device.c227
-rwxr-xr-xprofiles/input/device.h9
-rwxr-xr-xprofiles/input/manager.c38
-rwxr-xr-xprofiles/input/server.c84
-rwxr-xr-xprofiles/network/bnep.c78
-rwxr-xr-xprofiles/network/bnep.h4
-rwxr-xr-xprofiles/network/connection.c18
-rwxr-xr-xprofiles/network/server.c235
-rwxr-xr-xprofiles/proximity/immalert.c458
-rwxr-xr-xprofiles/proximity/immalert.h26
-rwxr-xr-xprofiles/proximity/linkloss.c547
-rwxr-xr-xprofiles/proximity/linkloss.h26
-rwxr-xr-xprofiles/proximity/main.c81
-rwxr-xr-xprofiles/proximity/manager.c196
-rwxr-xr-xprofiles/proximity/manager.h26
-rwxr-xr-xprofiles/proximity/monitor.c822
-rwxr-xr-xprofiles/proximity/monitor.h43
-rwxr-xr-xprofiles/proximity/reporter.c330
-rwxr-xr-xprofiles/proximity/reporter.h46
-rwxr-xr-xprofiles/tds/manager.c68
-rwxr-xr-xprofiles/tds/tds.c785
-rwxr-xr-xprofiles/tds/tds.h34
-rwxr-xr-xprofiles/thermometer/thermometer.c1321
-rwxr-xr-xprofiles/time/server.c283
-rwxr-xr-xsrc/adapter.c5477
-rwxr-xr-xsrc/adapter.h127
-rwxr-xr-xsrc/adapter_le_vsc_features.c769
-rwxr-xr-xsrc/adapter_le_vsc_features.h503
-rwxr-xr-xsrc/attio.h33
-rwxr-xr-xsrc/attrib-server.c197
-rwxr-xr-xsrc/attrib-server.h12
-rwxr-xr-xsrc/bluetooth.conf109
-rwxr-xr-xsrc/bluetooth.service.in7
-rwxr-xr-xsrc/device.c2378
-rwxr-xr-xsrc/device.h72
-rwxr-xr-xsrc/eir.c33
-rwxr-xr-xsrc/eir.h4
-rwxr-xr-xsrc/gatt-client.c425
-rwxr-xr-xsrc/gatt-database.c492
-rwxr-xr-xsrc/hcid.h3
-rwxr-xr-xsrc/log.c16
-rwxr-xr-xsrc/log.h3
-rwxr-xr-xsrc/main.c17
-rwxr-xr-xsrc/main_hive.conf87
-rwxr-xr-xsrc/main_ivi.conf87
-rwxr-xr-xsrc/main_m.conf87
-rwxr-xr-xsrc/main_w.conf84
-rwxr-xr-xsrc/oob.c46
-rwxr-xr-xsrc/oob.h36
-rwxr-xr-xsrc/profile.c579
-rwxr-xr-xsrc/profile.h4
-rwxr-xr-xsrc/sdp-xml.c5
-rwxr-xr-xsrc/service.c14
-rwxr-xr-xsrc/shared/att.c79
-rwxr-xr-xsrc/shared/att.h11
-rwxr-xr-xsrc/shared/btsnoop.c118
-rwxr-xr-xsrc/shared/btsnoop.h5
-rwxr-xr-xsrc/shared/crypto.c4
-rwxr-xr-xsrc/shared/gatt-client.c381
-rwxr-xr-xsrc/shared/gatt-client.h22
-rwxr-xr-xsrc/shared/gatt-db.c38
-rwxr-xr-xsrc/shared/gatt-db.h12
-rwxr-xr-xsrc/shared/gatt-server.c75
-rwxr-xr-xsrc/shared/gatt-server.h11
-rwxr-xr-xsrc/storage.c47
-rwxr-xr-xsrc/storage.h10
-rwxr-xr-xtest/advertisement-example170
-rwxr-xr-xtest/exchange-business-cards19
-rwxr-xr-xtest/get-managed-objects29
-rwxr-xr-xtest/get-obex-capabilities19
-rwxr-xr-xtest/list-folders39
-rwxr-xr-xtest/simple-obex-agent87
-rwxr-xr-xtools/bdaddr.168
-rwxr-xr-xtools/bneptest.c4
-rwxr-xr-xtools/btgatt-client.c15
-rwxr-xr-xtools/btsnoop.c146
-rwxr-xr-xtools/example.psr12
-rwxr-xr-xtools/gatt-example533
-rwxr-xr-xtools/hciattach.c336
-rwxr-xr-xtools/hciattach.h3
-rwxr-xr-xtools/hciattach_bcm43xx.c4
-rwxr-xr-xtools/hciattach_sprd.c592
-rwxr-xr-xtools/hciattach_sprd.h82
-rwxr-xr-xtools/hciattach_ti.c23
-rwxr-xr-xtools/hciconfig.c7
-rwxr-xr-xtools/hcidump.c124
-rwxr-xr-xtools/hcitool.c63
-rwxr-xr-xtools/ibeacon.c1
-rwxr-xr-xtools/l2test.c182
-rwxr-xr-xtools/parse_companies.pl59
-rwxr-xr-xtools/parser/l2cap.h12
-rwxr-xr-xtools/pskey_get.c384
-rwxr-xr-xtools/sdptool.c4
-rwxr-xr-xtools/test-runner.c4
-rwxr-xr-xtools/update_compids.sh57
-rwxr-xr-xtools/valgrind.supp27
191 files changed, 38812 insertions, 196 deletions
diff --git a/Makefile.am b/Makefile.am
index ce7fd0fb..7a8ad8d9 100755
--- a/Makefile.am
+++ b/Makefile.am
@@ -182,6 +182,7 @@ src_bluetoothd_SOURCES = $(builtin_sources) \
src/agent.h src/agent.c \
src/error.h src/error.c \
src/adapter.h src/adapter.c \
+ src/adapter_le_vsc_features.h src/adapter_le_vsc_features.c \
src/profile.h src/profile.c \
src/service.h src/service.c \
src/gatt-client.h src/gatt-client.c \
@@ -191,7 +192,7 @@ src_bluetoothd_SOURCES = $(builtin_sources) \
src_bluetoothd_LDADD = lib/libbluetooth-internal.la \
gdbus/libgdbus-internal.la \
src/libshared-glib.la \
- @BACKTRACE_LIBS@ @GLIB_LIBS@ @DBUS_LIBS@ -ldl -lrt
+ @BACKTRACE_LIBS@ @GLIB_LIBS@ @DBUS_LIBS@ @LIBXML_LIBS@ @INIPARSER_LIBS@ -ldl -lrt
src_bluetoothd_LDFLAGS = $(AM_LDFLAGS) -Wl,--export-dynamic \
-Wl,--version-script=$(srcdir)/src/bluetooth.ver
@@ -213,7 +214,7 @@ CLEANFILES += $(builtin_files) src/bluetooth.service
man_MANS = src/bluetoothd.8
EXTRA_DIST += src/genbuiltin src/bluetooth.conf \
- src/main.conf profiles/network/network.conf \
+ src/main_m.conf src/main_w.conf profiles/network/network.conf \
profiles/input/input.conf profiles/proximity/proximity.conf
test_scripts =
@@ -266,7 +267,7 @@ EXTRA_DIST += doc/btsnoop.txt
EXTRA_DIST += tools/magic.btsnoop
-AM_CFLAGS += @DBUS_CFLAGS@ @GLIB_CFLAGS@
+AM_CFLAGS += @DBUS_CFLAGS@ @GLIB_CFLAGS@ @LIBXML_CFLAGS@ @INIPARSER_CFLAGS@
AM_CPPFLAGS = -I$(builddir)/lib
@@ -421,6 +422,16 @@ unit_test_gattrib_LDADD = lib/libbluetooth-internal.la \
src/libshared-glib.la \
@GLIB_LIBS@ @DBUS_LIBS@ -ldl -lrt
+if MIDI
+unit_tests += unit/test-midi
+unit_test_midi_CFLAGS = $(AM_CFLAGS) @ALSA_CFLAGS@ -DMIDI_TEST
+unit_test_midi_SOURCES = unit/test-midi.c \
+ profiles/midi/libmidi.h \
+ profiles/midi/libmidi.c
+unit_test_midi_LDADD = src/libshared-glib.la \
+ @GLIB_LIBS@ @ALSA_LIBS@
+endif
+
if MAINTAINER_MODE
noinst_PROGRAMS += $(unit_tests)
endif
diff --git a/Makefile.obexd b/Makefile.obexd
index 42135a97..05a3b6b6 100755
--- a/Makefile.obexd
+++ b/Makefile.obexd
@@ -82,11 +82,13 @@ obexd_src_obexd_SOURCES = $(btio_sources) $(gobex_sources) \
obexd/src/map_ap.h
obexd_src_obexd_LDADD = lib/libbluetooth-internal.la \
gdbus/libgdbus-internal.la \
- @ICAL_LIBS@ @DBUS_LIBS@ @GLIB_LIBS@ -ldl
+ @ICAL_LIBS@ @DBUS_LIBS@ @GLIB_LIBS@ \
+ @LIBXML_LIBS@ @INIPARSER_LIBS@ -ldl
obexd_src_obexd_LDFLAGS = -Wl,--export-dynamic
obexd_src_obexd_CFLAGS = $(AM_CFLAGS) @GLIB_CFLAGS@ @DBUS_CFLAGS@ \
+ @LIBXML_CFLAGS@ @INIPARSER_CFLAGS@ \
@ICAL_CFLAGS@ -DOBEX_PLUGIN_BUILTIN \
-DPLUGINDIR=\""$(obex_plugindir)"\" \
-fPIC -D_FILE_OFFSET_BITS=64 -pie
diff --git a/Makefile.plugins b/Makefile.plugins
index 53e80fef..d0b81d07 100755
--- a/Makefile.plugins
+++ b/Makefile.plugins
@@ -2,32 +2,46 @@
builtin_modules += hostname
builtin_sources += plugins/hostname.c
+if TIZEN_UNUSED_PLUGIN
builtin_modules += wiimote
builtin_sources += plugins/wiimote.c
+endif
+if AUTOPAIR
builtin_modules += autopair
builtin_sources += plugins/autopair.c
+endif
builtin_modules += policy
builtin_sources += plugins/policy.c
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+builtin_modules += dbusoob
+builtin_sources += plugins/dbusoob.c \
+ src/oob.h src/oob.c
+#endif
+
if MAINTAINER_MODE
builtin_modules += gatt_example
builtin_sources += plugins/gatt-example.c
endif
if EXPERIMENTAL
+if TIZEN_UNUSED_PLUGIN
builtin_modules += neard
builtin_sources += plugins/neard.c
+endif
+if TIZEN_SAP_PLUGIN
builtin_modules += sap
builtin_sources += profiles/sap/main.c profiles/sap/manager.h \
profiles/sap/manager.c profiles/sap/server.h \
profiles/sap/server.c profiles/sap/sap.h \
profiles/sap/sap-u8500.c
-noinst_LIBRARIES += profiles/sap/libsap.a
-profiles_sap_libsap_a_SOURCES = profiles/sap/sap.h profiles/sap/sap-u8500.c
+#noinst_LIBRARIES += profiles/sap/libsap.a
+#profiles_sap_libsap_a_SOURCES = profiles/sap/sap.h profiles/sap/sap-u8500.c
+endif
endif
builtin_modules += a2dp
@@ -52,12 +66,18 @@ builtin_sources += profiles/network/manager.c \
profiles/network/connection.h \
profiles/network/connection.c
+#if WEARABLE
+#builtin_modules +=
+#builtin_sources +=
+#else
+if TIZEN_HID_PLUGIN
builtin_modules += input
builtin_sources += profiles/input/manager.c \
profiles/input/server.h profiles/input/server.c \
profiles/input/device.h profiles/input/device.c \
profiles/input/hidp_defs.h
+if TIZEN_UNUSED_PLUGIN
builtin_modules += hog
builtin_sources += profiles/input/hog.c profiles/input/uhid_copy.h \
profiles/input/hog-lib.c profiles/input/hog-lib.h \
@@ -67,8 +87,12 @@ builtin_sources += profiles/input/hog.c profiles/input/uhid_copy.h \
profiles/input/suspend.h profiles/input/suspend-none.c
EXTRA_DIST += profiles/input/suspend-dummy.c
+endif
+endif
+#endif
if EXPERIMENTAL
+if TIZEN_HEALTH_PLUGIN
builtin_modules += health
builtin_sources += profiles/health/mcap.h profiles/health/mcap.c \
profiles/health/hdp_main.c profiles/health/hdp_types.h \
@@ -77,7 +101,9 @@ builtin_sources += profiles/health/mcap.h profiles/health/mcap.c \
profiles/health/hdp.h profiles/health/hdp.c \
profiles/health/hdp_util.h profiles/health/hdp_util.c
endif
+endif
+if TIZEN_HEALTH_PLUGIN
builtin_modules += gap
builtin_sources += profiles/gap/gas.c
@@ -86,11 +112,52 @@ builtin_sources += profiles/scanparam/scan.c
builtin_modules += deviceinfo
builtin_sources += profiles/deviceinfo/deviceinfo.c
+endif
+
+if EXPERIMENTAL
+if TIZEN_UNUSED_PLUGIN
+if TIZEN_PROXIMITY_PLUGIN
+builtin_modules += proximity
+builtin_sources += profiles/proximity/main.c profiles/proximity/manager.h \
+ profiles/proximity/manager.c \
+ profiles/proximity/monitor.h \
+ profiles/proximity/monitor.c \
+ profiles/proximity/reporter.h \
+ profiles/proximity/reporter.c \
+ profiles/proximity/linkloss.h \
+ profiles/proximity/linkloss.c \
+ profiles/proximity/immalert.h \
+ profiles/proximity/immalert.c
+endif
+
+
+if TIZEN_TDS_PLUGIN
+builtin_modules += tds
+builtin_sources += profiles/tds/manager.c profiles/tds/tds.h \
+ profiles/tds/tds.c
+endif
+
+builtin_modules += alert
+builtin_sources += profiles/alert/server.c
+
+builtin_modules += time
+builtin_sources += profiles/time/server.c
+
+builtin_modules += thermometer
+builtin_sources += profiles/thermometer/thermometer.c
+
+builtin_modules += heartrate
+builtin_sources += profiles/heartrate/heartrate.c
+
+builtin_modules += cyclingspeed
+builtin_sources += profiles/cyclingspeed/cyclingspeed.c
+endif
+endif
if SIXAXIS
plugin_LTLIBRARIES += plugins/sixaxis.la
plugins_sixaxis_la_SOURCES = plugins/sixaxis.c
plugins_sixaxis_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \
- -no-undefined @UDEV_LIBS@
-plugins_sixaxis_la_CFLAGS = $(AM_CFLAGS) -fvisibility=hidden @UDEV_CFLAGS@
+ -no-undefined @UDEV_LIBS@ @LIBXML_LIBS@ @INIPARSER_LIBS@
+plugins_sixaxis_la_CFLAGS = $(AM_CFLAGS) -fvisibility=hidden @UDEV_CFLAGS@ @LIBXML_CFLAGS@ @INIPARSER_CFLAGS@
endif
diff --git a/Makefile.tools b/Makefile.tools
index 42f17fc1..fe6220fb 100755
--- a/Makefile.tools
+++ b/Makefile.tools
@@ -169,7 +169,7 @@ tools_hciattach_SOURCES = tools/hciattach.c tools/hciattach.h \
tools/hciattach_sprd.c \
tools/pskey_get.c \
tools/hciattach_bcm43xx.c
-tools_hciattach_LDADD = lib/libbluetooth-internal.la
+tools_hciattach_LDADD = lib/libbluetooth-internal.la @LIBXML_LIBS@ @INIPARSER_LIBS@
tools_hciconfig_SOURCES = tools/hciconfig.c tools/csr.h tools/csr.c
tools_hciconfig_LDADD = lib/libbluetooth-internal.la
diff --git a/android/bas.c b/android/bas.c
new file mode 100755
index 00000000..dcbf9de7
--- /dev/null
+++ b/android/bas.c
@@ -0,0 +1,379 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2014 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can rebastribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is bastributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include "src/log.h"
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+
+#include "attrib/gattrib.h"
+#include "attrib/att.h"
+#include "attrib/gatt.h"
+
+#include "android/bas.h"
+
+#define ATT_NOTIFICATION_HEADER_SIZE 3
+#define ATT_READ_RESPONSE_HEADER_SIZE 1
+
+struct bt_bas {
+ int ref_count;
+ GAttrib *attrib;
+ struct gatt_primary *primary;
+ uint16_t handle;
+ uint16_t ccc_handle;
+ guint id;
+ struct queue *gatt_op;
+};
+
+struct gatt_request {
+ unsigned int id;
+ struct bt_bas *bas;
+ void *user_data;
+};
+
+static void destroy_gatt_req(struct gatt_request *req)
+{
+ queue_remove(req->bas->gatt_op, req);
+ bt_bas_unref(req->bas);
+ free(req);
+}
+
+static void bas_free(struct bt_bas *bas)
+{
+ bt_bas_detach(bas);
+
+ g_free(bas->primary);
+ queue_destroy(bas->gatt_op, (void *) destroy_gatt_req);
+ g_free(bas);
+}
+
+struct bt_bas *bt_bas_new(void *primary)
+{
+ struct bt_bas *bas;
+
+ bas = g_try_new0(struct bt_bas, 1);
+ if (!bas)
+ return NULL;
+
+ bas->gatt_op = queue_new();
+ if (!bas->gatt_op) {
+ bas_free(bas);
+ return NULL;
+ }
+
+ if (primary)
+ bas->primary = g_memdup(primary, sizeof(*bas->primary));
+
+ return bt_bas_ref(bas);
+}
+
+struct bt_bas *bt_bas_ref(struct bt_bas *bas)
+{
+ if (!bas)
+ return NULL;
+
+ __sync_fetch_and_add(&bas->ref_count, 1);
+
+ return bas;
+}
+
+void bt_bas_unref(struct bt_bas *bas)
+{
+ if (!bas)
+ return;
+
+ if (__sync_sub_and_fetch(&bas->ref_count, 1))
+ return;
+
+ bas_free(bas);
+}
+
+static struct gatt_request *create_request(struct bt_bas *bas,
+ void *user_data)
+{
+ struct gatt_request *req;
+
+ req = new0(struct gatt_request, 1);
+ if (!req)
+ return NULL;
+
+ req->user_data = user_data;
+ req->bas = bt_bas_ref(bas);
+
+ return req;
+}
+
+static bool set_and_store_gatt_req(struct bt_bas *bas,
+ struct gatt_request *req,
+ unsigned int id)
+{
+ req->id = id;
+ return queue_push_head(bas->gatt_op, req);
+}
+
+static void write_char(struct bt_bas *bas, GAttrib *attrib, uint16_t handle,
+ const uint8_t *value, size_t vlen,
+ GAttribResultFunc func,
+ gpointer user_data)
+{
+ struct gatt_request *req;
+ unsigned int id;
+
+ req = create_request(bas, user_data);
+ if (!req)
+ return;
+
+ id = gatt_write_char(attrib, handle, value, vlen, func, req);
+
+ if (set_and_store_gatt_req(bas, req, id))
+ return;
+
+ error("bas: Could not write characteristic");
+ g_attrib_cancel(attrib, id);
+ free(req);
+
+}
+
+static void read_char(struct bt_bas *bas, GAttrib *attrib, uint16_t handle,
+ GAttribResultFunc func, gpointer user_data)
+{
+ struct gatt_request *req;
+ unsigned int id;
+
+ req = create_request(bas, user_data);
+ if (!req)
+ return;
+
+ id = gatt_read_char(attrib, handle, func, req);
+
+ if (set_and_store_gatt_req(bas, req, id))
+ return;
+
+ error("bas: Could not read characteristic");
+ g_attrib_cancel(attrib, id);
+ free(req);
+}
+
+static void discover_char(struct bt_bas *bas, GAttrib *attrib,
+ uint16_t start, uint16_t end,
+ bt_uuid_t *uuid, gatt_cb_t func,
+ gpointer user_data)
+{
+ struct gatt_request *req;
+ unsigned int id;
+
+ req = create_request(bas, user_data);
+ if (!req)
+ return;
+
+ id = gatt_discover_char(attrib, start, end, uuid, func, req);
+
+ if (set_and_store_gatt_req(bas, req, id))
+ return;
+
+ error("bas: Could not discover characteristic");
+ g_attrib_cancel(attrib, id);
+ free(req);
+}
+
+static void discover_desc(struct bt_bas *bas, GAttrib *attrib,
+ uint16_t start, uint16_t end, bt_uuid_t *uuid,
+ gatt_cb_t func, gpointer user_data)
+{
+ struct gatt_request *req;
+ unsigned int id;
+
+ req = create_request(bas, user_data);
+ if (!req)
+ return;
+
+ id = gatt_discover_desc(attrib, start, end, uuid, func, req);
+ if (set_and_store_gatt_req(bas, req, id))
+ return;
+
+ error("bas: Could not discover descriptor");
+ g_attrib_cancel(attrib, id);
+ free(req);
+}
+
+static void notification_cb(const guint8 *pdu, guint16 len, gpointer user_data)
+{
+ DBG("Battery Level at %u", pdu[ATT_NOTIFICATION_HEADER_SIZE]);
+}
+
+static void read_value_cb(guint8 status, const guint8 *pdu, guint16 len,
+ gpointer user_data)
+{
+ DBG("Battery Level at %u", pdu[ATT_READ_RESPONSE_HEADER_SIZE]);
+}
+
+static void ccc_written_cb(guint8 status, const guint8 *pdu,
+ guint16 plen, gpointer user_data)
+{
+ struct gatt_request *req = user_data;
+ struct bt_bas *bas = req->user_data;
+
+ destroy_gatt_req(req);
+
+ if (status != 0) {
+ error("Write Scan Refresh CCC failed: %s",
+ att_ecode2str(status));
+ return;
+ }
+
+ DBG("Battery Level: notification enabled");
+
+ bas->id = g_attrib_register(bas->attrib, ATT_OP_HANDLE_NOTIFY,
+ bas->handle, notification_cb, bas,
+ NULL);
+}
+
+static void write_ccc(struct bt_bas *bas, GAttrib *attrib, uint16_t handle,
+ void *user_data)
+{
+ uint8_t value[2];
+
+ put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value);
+
+ write_char(bas, attrib, handle, value, sizeof(value), ccc_written_cb,
+ user_data);
+}
+
+static void ccc_read_cb(guint8 status, const guint8 *pdu, guint16 len,
+ gpointer user_data)
+{
+ struct gatt_request *req = user_data;
+ struct bt_bas *bas = req->user_data;
+
+ destroy_gatt_req(req);
+
+ if (status != 0) {
+ error("Error reading CCC value: %s", att_ecode2str(status));
+ return;
+ }
+
+ write_ccc(bas, bas->attrib, bas->ccc_handle, bas);
+}
+
+static void discover_descriptor_cb(uint8_t status, GSList *descs,
+ void *user_data)
+{
+ struct gatt_request *req = user_data;
+ struct bt_bas *bas = req->user_data;
+ struct gatt_desc *desc;
+
+ destroy_gatt_req(req);
+
+ if (status != 0) {
+ error("Discover descriptors failed: %s", att_ecode2str(status));
+ return;
+ }
+
+ /* There will be only one descriptor on list and it will be CCC */
+ desc = descs->data;
+ bas->ccc_handle = desc->handle;
+
+ read_char(bas, bas->attrib, desc->handle, ccc_read_cb, bas);
+}
+
+static void bas_discovered_cb(uint8_t status, GSList *chars, void *user_data)
+{
+ struct gatt_request *req = user_data;
+ struct bt_bas *bas = req->user_data;
+ struct gatt_char *chr;
+ uint16_t start, end;
+ bt_uuid_t uuid;
+
+ destroy_gatt_req(req);
+
+ if (status) {
+ error("Battery: %s", att_ecode2str(status));
+ return;
+ }
+
+ chr = chars->data;
+ bas->handle = chr->value_handle;
+
+ DBG("Battery handle: 0x%04x", bas->handle);
+
+ read_char(bas, bas->attrib, bas->handle, read_value_cb, bas);
+
+ start = chr->value_handle + 1;
+ end = bas->primary->range.end;
+
+ bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+
+ discover_desc(bas, bas->attrib, start, end, &uuid,
+ discover_descriptor_cb, bas);
+}
+
+bool bt_bas_attach(struct bt_bas *bas, void *attrib)
+{
+ if (!bas || bas->attrib || !bas->primary)
+ return false;
+
+ bas->attrib = g_attrib_ref(attrib);
+
+ if (bas->handle > 0)
+ return true;
+
+ discover_char(bas, bas->attrib, bas->primary->range.start,
+ bas->primary->range.end, NULL,
+ bas_discovered_cb, bas);
+
+ return true;
+}
+
+static void cancel_gatt_req(struct gatt_request *req)
+{
+ if (g_attrib_cancel(req->bas->attrib, req->id))
+ destroy_gatt_req(req);
+}
+
+void bt_bas_detach(struct bt_bas *bas)
+{
+ if (!bas || !bas->attrib)
+ return;
+
+ if (bas->id > 0) {
+ g_attrib_unregister(bas->attrib, bas->id);
+ bas->id = 0;
+ }
+
+ queue_foreach(bas->gatt_op, (void *) cancel_gatt_req, NULL);
+ g_attrib_unref(bas->attrib);
+ bas->attrib = NULL;
+}
diff --git a/android/bas.h b/android/bas.h
new file mode 100755
index 00000000..3e175b5b
--- /dev/null
+++ b/android/bas.h
@@ -0,0 +1,32 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2014 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can rebastribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is bastributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+struct bt_bas;
+
+struct bt_bas *bt_bas_new(void *primary);
+
+struct bt_bas *bt_bas_ref(struct bt_bas *bas);
+void bt_bas_unref(struct bt_bas *bas);
+
+bool bt_bas_attach(struct bt_bas *bas, void *gatt);
+void bt_bas_detach(struct bt_bas *bas);
diff --git a/android/dis.c b/android/dis.c
new file mode 100755
index 00000000..75dbe3d3
--- /dev/null
+++ b/android/dis.c
@@ -0,0 +1,304 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012 Texas Instruments, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include "src/log.h"
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+
+#include "attrib/gattrib.h"
+#include "attrib/att.h"
+#include "attrib/gatt.h"
+
+#include "android/dis.h"
+
+#define PNP_ID_SIZE 7
+
+struct bt_dis {
+ int ref_count;
+ uint16_t handle;
+ uint8_t source;
+ uint16_t vendor;
+ uint16_t product;
+ uint16_t version;
+ GAttrib *attrib; /* GATT connection */
+ struct gatt_primary *primary; /* Primary details */
+ bt_dis_notify notify;
+ void *notify_data;
+ struct queue *gatt_op;
+};
+
+struct characteristic {
+ struct gatt_char attr; /* Characteristic */
+ struct bt_dis *d; /* deviceinfo where the char belongs */
+};
+
+struct gatt_request {
+ unsigned int id;
+ struct bt_dis *dis;
+ void *user_data;
+};
+
+static void destroy_gatt_req(struct gatt_request *req)
+{
+ queue_remove(req->dis->gatt_op, req);
+ bt_dis_unref(req->dis);
+ free(req);
+}
+
+static void dis_free(struct bt_dis *dis)
+{
+ bt_dis_detach(dis);
+
+ g_free(dis->primary);
+ queue_destroy(dis->gatt_op, (void *) destroy_gatt_req);
+ g_free(dis);
+}
+
+struct bt_dis *bt_dis_new(void *primary)
+{
+ struct bt_dis *dis;
+
+ dis = g_try_new0(struct bt_dis, 1);
+ if (!dis)
+ return NULL;
+
+ dis->gatt_op = queue_new();
+ if (!dis->gatt_op) {
+ dis_free(dis);
+ return NULL;
+ }
+
+ if (primary)
+ dis->primary = g_memdup(primary, sizeof(*dis->primary));
+
+ return bt_dis_ref(dis);
+}
+
+struct bt_dis *bt_dis_ref(struct bt_dis *dis)
+{
+ if (!dis)
+ return NULL;
+
+ __sync_fetch_and_add(&dis->ref_count, 1);
+
+ return dis;
+}
+
+void bt_dis_unref(struct bt_dis *dis)
+{
+ if (!dis)
+ return;
+
+ if (__sync_sub_and_fetch(&dis->ref_count, 1))
+ return;
+
+ dis_free(dis);
+}
+
+static struct gatt_request *create_request(struct bt_dis *dis,
+ void *user_data)
+{
+ struct gatt_request *req;
+
+ req = new0(struct gatt_request, 1);
+ if (!req)
+ return NULL;
+
+ req->user_data = user_data;
+ req->dis = bt_dis_ref(dis);
+
+ return req;
+}
+
+static bool set_and_store_gatt_req(struct bt_dis *dis,
+ struct gatt_request *req,
+ unsigned int id)
+{
+ req->id = id;
+ return queue_push_head(dis->gatt_op, req);
+}
+
+static void read_pnpid_cb(guint8 status, const guint8 *pdu, guint16 len,
+ gpointer user_data)
+{
+ struct gatt_request *req = user_data;
+ struct bt_dis *dis = req->user_data;
+ uint8_t value[PNP_ID_SIZE];
+ ssize_t vlen;
+
+ destroy_gatt_req(req);
+
+ if (status != 0) {
+ error("Error reading PNP_ID value: %s", att_ecode2str(status));
+ return;
+ }
+
+ vlen = dec_read_resp(pdu, len, value, sizeof(value));
+ if (vlen < 0) {
+ error("Error reading PNP_ID: Protocol error");
+ return;
+ }
+
+ if (vlen < 7) {
+ error("Error reading PNP_ID: Invalid pdu length received");
+ return;
+ }
+
+ dis->source = value[0];
+ dis->vendor = get_le16(&value[1]);
+ dis->product = get_le16(&value[3]);
+ dis->version = get_le16(&value[5]);
+
+ DBG("source: 0x%02X vendor: 0x%04X product: 0x%04X version: 0x%04X",
+ dis->source, dis->vendor, dis->product, dis->version);
+
+ if (dis->notify)
+ dis->notify(dis->source, dis->vendor, dis->product,
+ dis->version, dis->notify_data);
+}
+
+static void read_char(struct bt_dis *dis, GAttrib *attrib, uint16_t handle,
+ GAttribResultFunc func, gpointer user_data)
+{
+ struct gatt_request *req;
+ unsigned int id;
+
+ req = create_request(dis, user_data);
+ if (!req)
+ return;
+
+ id = gatt_read_char(attrib, handle, func, req);
+
+ if (set_and_store_gatt_req(dis, req, id))
+ return;
+
+ error("dis: Could not read characteristic");
+ g_attrib_cancel(attrib, id);
+ free(req);
+}
+
+static void discover_char(struct bt_dis *dis, GAttrib *attrib,
+ uint16_t start, uint16_t end,
+ bt_uuid_t *uuid, gatt_cb_t func,
+ gpointer user_data)
+{
+ struct gatt_request *req;
+ unsigned int id;
+
+ req = create_request(dis, user_data);
+ if (!req)
+ return;
+
+ id = gatt_discover_char(attrib, start, end, uuid, func, req);
+
+ if (set_and_store_gatt_req(dis, req, id))
+ return;
+
+ error("dis: Could not send discover characteristic");
+ g_attrib_cancel(attrib, id);
+ free(req);
+}
+
+static void configure_deviceinfo_cb(uint8_t status, GSList *characteristics,
+ void *user_data)
+{
+ struct gatt_request *req = user_data;
+ struct bt_dis *d = req->user_data;
+ GSList *l;
+
+ destroy_gatt_req(req);
+
+ if (status != 0) {
+ error("Discover deviceinfo characteristics: %s",
+ att_ecode2str(status));
+ return;
+ }
+
+ for (l = characteristics; l; l = l->next) {
+ struct gatt_char *c = l->data;
+
+ if (strcmp(c->uuid, PNPID_UUID) == 0) {
+ d->handle = c->value_handle;
+ read_char(d, d->attrib, d->handle, read_pnpid_cb, d);
+ break;
+ }
+ }
+}
+
+bool bt_dis_attach(struct bt_dis *dis, void *attrib)
+{
+ struct gatt_primary *primary = dis->primary;
+
+ if (dis->attrib || !primary)
+ return false;
+
+ dis->attrib = g_attrib_ref(attrib);
+
+ if (!dis->handle)
+ discover_char(dis, dis->attrib, primary->range.start,
+ primary->range.end, NULL,
+ configure_deviceinfo_cb, dis);
+
+ return true;
+}
+
+static void cancel_gatt_req(struct gatt_request *req)
+{
+ if (g_attrib_cancel(req->dis->attrib, req->id))
+ destroy_gatt_req(req);
+}
+
+void bt_dis_detach(struct bt_dis *dis)
+{
+ if (!dis->attrib)
+ return;
+
+ queue_foreach(dis->gatt_op, (void *) cancel_gatt_req, NULL);
+ g_attrib_unref(dis->attrib);
+ dis->attrib = NULL;
+}
+
+bool bt_dis_set_notification(struct bt_dis *dis, bt_dis_notify func,
+ void *user_data)
+{
+ if (!dis)
+ return false;
+
+ dis->notify = func;
+ dis->notify_data = user_data;
+
+ return true;
+}
diff --git a/android/dis.h b/android/dis.h
new file mode 100755
index 00000000..faf27b3d
--- /dev/null
+++ b/android/dis.h
@@ -0,0 +1,39 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2014 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+struct bt_dis;
+
+struct bt_dis *bt_dis_new(void *primary);
+
+struct bt_dis *bt_dis_ref(struct bt_dis *dis);
+void bt_dis_unref(struct bt_dis *dis);
+
+bool bt_dis_attach(struct bt_dis *dis, void *gatt);
+void bt_dis_detach(struct bt_dis *dis);
+
+typedef void (*bt_dis_notify) (uint8_t source, uint16_t vendor,
+ uint16_t product, uint16_t version,
+ void *user_data);
+
+bool bt_dis_set_notification(struct bt_dis *dis, bt_dis_notify func,
+ void *user_data);
diff --git a/android/hog.c b/android/hog.c
new file mode 100755
index 00000000..ff77bb37
--- /dev/null
+++ b/android/hog.c
@@ -0,0 +1,1511 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2014 Intel Corporation.
+ * Copyright (C) 2012 Marcel Holtmann <marcel@holtmann.org>
+ * Copyright (C) 2012 Nordic Semiconductor Inc.
+ * Copyright (C) 2012 Instituto Nokia de Tecnologia - INdT
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdlib.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "src/shared/util.h"
+#include "src/shared/uhid.h"
+#include "src/shared/queue.h"
+#include "src/log.h"
+
+#include "attrib/att.h"
+#include "attrib/gattrib.h"
+#include "attrib/gatt.h"
+
+#include "btio/btio.h"
+
+#include "android/scpp.h"
+#include "android/dis.h"
+#include "android/bas.h"
+#include "android/hog.h"
+
+#define HOG_UUID "00001812-0000-1000-8000-00805f9b34fb"
+
+#define HOG_INFO_UUID 0x2A4A
+#define HOG_REPORT_MAP_UUID 0x2A4B
+#define HOG_REPORT_UUID 0x2A4D
+#define HOG_PROTO_MODE_UUID 0x2A4E
+#define HOG_CONTROL_POINT_UUID 0x2A4C
+
+#define HOG_REPORT_TYPE_INPUT 1
+#define HOG_REPORT_TYPE_OUTPUT 2
+#define HOG_REPORT_TYPE_FEATURE 3
+
+#define HOG_PROTO_MODE_BOOT 0
+#define HOG_PROTO_MODE_REPORT 1
+
+#define HOG_REPORT_MAP_MAX_SIZE 512
+#define HID_INFO_SIZE 4
+#define ATT_NOTIFICATION_HEADER_SIZE 3
+
+struct bt_hog {
+ int ref_count;
+ char *name;
+ uint16_t vendor;
+ uint16_t product;
+ uint16_t version;
+ struct gatt_primary *primary;
+ GAttrib *attrib;
+ GSList *reports;
+ struct bt_uhid *uhid;
+ int uhid_fd;
+ gboolean has_report_id;
+ uint16_t bcdhid;
+ uint8_t bcountrycode;
+ uint16_t proto_mode_handle;
+ uint16_t ctrlpt_handle;
+ uint8_t flags;
+ unsigned int getrep_att;
+ uint16_t getrep_id;
+ unsigned int setrep_att;
+ uint16_t setrep_id;
+ struct bt_scpp *scpp;
+ struct bt_dis *dis;
+ struct queue *bas;
+ GSList *instances;
+ struct queue *gatt_op;
+};
+
+struct report {
+ struct bt_hog *hog;
+ uint8_t id;
+ uint8_t type;
+ uint16_t ccc_handle;
+ guint notifyid;
+ struct gatt_char *decl;
+ uint16_t len;
+ uint8_t *value;
+};
+
+struct gatt_request {
+ unsigned int id;
+ struct bt_hog *hog;
+ void *user_data;
+};
+
+static struct gatt_request *create_request(struct bt_hog *hog,
+ void *user_data)
+{
+ struct gatt_request *req;
+
+ req = new0(struct gatt_request, 1);
+ if (!req)
+ return NULL;
+
+ req->user_data = user_data;
+ req->hog = bt_hog_ref(hog);
+
+ return req;
+}
+
+static bool set_and_store_gatt_req(struct bt_hog *hog,
+ struct gatt_request *req,
+ unsigned int id)
+{
+ req->id = id;
+ return queue_push_head(hog->gatt_op, req);
+}
+
+static void destroy_gatt_req(struct gatt_request *req)
+{
+ queue_remove(req->hog->gatt_op, req);
+ bt_hog_unref(req->hog);
+ free(req);
+}
+
+static void write_char(struct bt_hog *hog, GAttrib *attrib, uint16_t handle,
+ const uint8_t *value, size_t vlen,
+ GAttribResultFunc func,
+ gpointer user_data)
+{
+ struct gatt_request *req;
+ unsigned int id;
+
+ req = create_request(hog, user_data);
+ if (!req)
+ return;
+
+ id = gatt_write_char(attrib, handle, value, vlen, func, req);
+
+ if (set_and_store_gatt_req(hog, req, id))
+ return;
+
+ error("hog: Could not read char");
+ g_attrib_cancel(attrib, id);
+ free(req);
+}
+
+static void read_char(struct bt_hog *hog, GAttrib *attrib, uint16_t handle,
+ GAttribResultFunc func, gpointer user_data)
+{
+ struct gatt_request *req;
+ unsigned int id;
+
+ req = create_request(hog, user_data);
+ if (!req)
+ return;
+
+ id = gatt_read_char(attrib, handle, func, req);
+
+ if (set_and_store_gatt_req(hog, req, id))
+ return;
+
+ error("hog: Could not read char");
+ g_attrib_cancel(attrib, id);
+ free(req);
+}
+
+static void discover_desc(struct bt_hog *hog, GAttrib *attrib,
+ uint16_t start, uint16_t end, gatt_cb_t func,
+ gpointer user_data)
+{
+ struct gatt_request *req;
+ unsigned int id;
+
+ req = create_request(hog, user_data);
+ if (!req)
+ return;
+
+ id = gatt_discover_desc(attrib, start, end, NULL, func, req);
+ if (set_and_store_gatt_req(hog, req, id))
+ return;
+
+ error("hog: Could not discover descriptors");
+ g_attrib_cancel(attrib, id);
+ free(req);
+}
+
+static void discover_char(struct bt_hog *hog, GAttrib *attrib,
+ uint16_t start, uint16_t end,
+ bt_uuid_t *uuid, gatt_cb_t func,
+ gpointer user_data)
+{
+ struct gatt_request *req;
+ unsigned int id;
+
+ req = create_request(hog, user_data);
+ if (!req)
+ return;
+
+ id = gatt_discover_char(attrib, start, end, uuid, func, req);
+
+ if (set_and_store_gatt_req(hog, req, id))
+ return;
+
+ error("hog: Could not discover characteristic");
+ g_attrib_cancel(attrib, id);
+ free(req);
+}
+
+static void discover_primary(struct bt_hog *hog, GAttrib *attrib,
+ bt_uuid_t *uuid, gatt_cb_t func,
+ gpointer user_data)
+{
+ struct gatt_request *req;
+ unsigned int id;
+
+ req = create_request(hog, user_data);
+ if (!req)
+ return;
+
+ id = gatt_discover_primary(attrib, uuid, func, req);
+
+ if (set_and_store_gatt_req(hog, req, id))
+ return;
+
+ error("hog: Could not send discover primary");
+ g_attrib_cancel(attrib, id);
+ free(req);
+}
+
+static void find_included(struct bt_hog *hog, GAttrib *attrib,
+ uint16_t start, uint16_t end,
+ gatt_cb_t func, gpointer user_data)
+{
+ struct gatt_request *req;
+ unsigned int id;
+
+ req = create_request(hog, user_data);
+ if (!req)
+ return;
+
+ id = gatt_find_included(attrib, start, end, func, req);
+
+ if (set_and_store_gatt_req(hog, req, id))
+ return;
+
+ error("Could not find included");
+ g_attrib_cancel(attrib, id);
+ free(req);
+}
+
+static void report_value_cb(const guint8 *pdu, guint16 len, gpointer user_data)
+{
+ struct report *report = user_data;
+ struct bt_hog *hog = report->hog;
+ struct uhid_event ev;
+ uint8_t *buf;
+ int err;
+
+ if (len < ATT_NOTIFICATION_HEADER_SIZE) {
+ error("Malformed ATT notification");
+ return;
+ }
+
+ pdu += ATT_NOTIFICATION_HEADER_SIZE;
+ len -= ATT_NOTIFICATION_HEADER_SIZE;
+
+ memset(&ev, 0, sizeof(ev));
+ ev.type = UHID_INPUT;
+ buf = ev.u.input.data;
+
+ if (hog->has_report_id) {
+ buf[0] = report->id;
+ len = MIN(len, sizeof(ev.u.input.data) - 1);
+ memcpy(buf + 1, pdu, len);
+ ev.u.input.size = ++len;
+ } else {
+ len = MIN(len, sizeof(ev.u.input.data));
+ memcpy(buf, pdu, len);
+ ev.u.input.size = len;
+ }
+
+ err = bt_uhid_send(hog->uhid, &ev);
+ if (err < 0) {
+ error("bt_uhid_send: %s (%d)", strerror(-err), -err);
+ return;
+ }
+
+ DBG("HoG report (%u bytes)", ev.u.input.size);
+}
+
+static void report_ccc_written_cb(guint8 status, const guint8 *pdu,
+ guint16 plen, gpointer user_data)
+{
+ struct gatt_request *req = user_data;
+ struct report *report = req->user_data;
+ struct bt_hog *hog = report->hog;
+
+ destroy_gatt_req(req);
+
+ if (status != 0) {
+ error("Write report characteristic descriptor failed: %s",
+ att_ecode2str(status));
+ return;
+ }
+
+ report->notifyid = g_attrib_register(hog->attrib,
+ ATT_OP_HANDLE_NOTIFY,
+ report->decl->value_handle,
+ report_value_cb, report, NULL);
+
+ DBG("Report characteristic descriptor written: notifications enabled");
+}
+
+static void write_ccc(struct bt_hog *hog, GAttrib *attrib, uint16_t handle,
+ void *user_data)
+{
+ uint8_t value[2];
+
+ put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value);
+
+ write_char(hog, attrib, handle, value, sizeof(value),
+ report_ccc_written_cb, user_data);
+}
+
+static void ccc_read_cb(guint8 status, const guint8 *pdu, guint16 len,
+ gpointer user_data)
+{
+ struct gatt_request *req = user_data;
+ struct report *report = req->user_data;
+
+ destroy_gatt_req(req);
+
+ if (status != 0) {
+ error("Error reading CCC value: %s", att_ecode2str(status));
+ return;
+ }
+
+ write_ccc(report->hog, report->hog->attrib, report->ccc_handle, report);
+}
+
+static void report_reference_cb(guint8 status, const guint8 *pdu,
+ guint16 plen, gpointer user_data)
+{
+ struct gatt_request *req = user_data;
+ struct report *report = req->user_data;
+
+ destroy_gatt_req(req);
+
+ if (status != 0) {
+ error("Read Report Reference descriptor failed: %s",
+ att_ecode2str(status));
+ return;
+ }
+
+ if (plen != 3) {
+ error("Malformed ATT read response");
+ return;
+ }
+
+ report->id = pdu[1];
+ report->type = pdu[2];
+ DBG("Report ID: 0x%02x Report type: 0x%02x", pdu[1], pdu[2]);
+
+ /* Enable notifications only for Input Reports */
+ if (report->type == HOG_REPORT_TYPE_INPUT)
+ read_char(report->hog, report->hog->attrib, report->ccc_handle,
+ ccc_read_cb, report);
+}
+
+static void external_report_reference_cb(guint8 status, const guint8 *pdu,
+ guint16 plen, gpointer user_data);
+
+static void discover_external_cb(uint8_t status, GSList *descs, void *user_data)
+{
+ struct gatt_request *req = user_data;
+ struct bt_hog *hog = req->user_data;
+
+ destroy_gatt_req(req);
+
+ if (status != 0) {
+ error("Discover external descriptors failed: %s",
+ att_ecode2str(status));
+ return;
+ }
+
+ for ( ; descs; descs = descs->next) {
+ struct gatt_desc *desc = descs->data;
+
+ read_char(hog, hog->attrib, desc->handle,
+ external_report_reference_cb,
+ hog);
+ }
+}
+
+static void discover_external(struct bt_hog *hog, GAttrib *attrib,
+ uint16_t start, uint16_t end,
+ gpointer user_data)
+{
+ bt_uuid_t uuid;
+
+ if (start > end)
+ return;
+
+ bt_uuid16_create(&uuid, GATT_EXTERNAL_REPORT_REFERENCE);
+
+ discover_desc(hog, attrib, start, end, discover_external_cb,
+ user_data);
+}
+
+static void discover_report_cb(uint8_t status, GSList *descs, void *user_data)
+{
+ struct gatt_request *req = user_data;
+ struct report *report = req->user_data;
+ struct bt_hog *hog = report->hog;
+
+ destroy_gatt_req(req);
+
+ if (status != 0) {
+ error("Discover report descriptors failed: %s",
+ att_ecode2str(status));
+ return;
+ }
+
+ for ( ; descs; descs = descs->next) {
+ struct gatt_desc *desc = descs->data;
+
+ switch (desc->uuid16) {
+ case GATT_CLIENT_CHARAC_CFG_UUID:
+ report->ccc_handle = desc->handle;
+ break;
+ case GATT_REPORT_REFERENCE:
+ read_char(hog, hog->attrib, desc->handle,
+ report_reference_cb, report);
+ break;
+ }
+ }
+}
+
+static void discover_report(struct bt_hog *hog, GAttrib *attrib,
+ uint16_t start, uint16_t end,
+ gpointer user_data)
+{
+ if (start > end)
+ return;
+
+ discover_desc(hog, attrib, start, end, discover_report_cb, user_data);
+}
+
+static void report_read_cb(guint8 status, const guint8 *pdu, guint16 len,
+ gpointer user_data)
+{
+ struct gatt_request *req = user_data;
+ struct report *report = req->user_data;
+
+ destroy_gatt_req(req);
+
+ if (status != 0) {
+ error("Error reading Report value: %s", att_ecode2str(status));
+ return;
+ }
+
+ if (report->value)
+ g_free(report->value);
+
+ report->value = g_memdup(pdu, len);
+ report->len = len;
+}
+
+static struct report *report_new(struct bt_hog *hog, struct gatt_char *chr)
+{
+ struct report *report;
+
+ report = g_new0(struct report, 1);
+ report->hog = hog;
+ report->decl = g_memdup(chr, sizeof(*chr));
+ hog->reports = g_slist_append(hog->reports, report);
+
+ read_char(hog, hog->attrib, chr->value_handle, report_read_cb, report);
+
+ return report;
+}
+
+static void external_service_char_cb(uint8_t status, GSList *chars,
+ void *user_data)
+{
+ struct gatt_request *req = user_data;
+ struct bt_hog *hog = req->user_data;
+ struct gatt_primary *primary = hog->primary;
+ struct report *report;
+ GSList *l;
+
+ destroy_gatt_req(req);
+
+ if (status != 0) {
+ const char *str = att_ecode2str(status);
+ DBG("Discover external service characteristic failed: %s", str);
+ return;
+ }
+
+ for (l = chars; l; l = g_slist_next(l)) {
+ struct gatt_char *chr, *next;
+ uint16_t start, end;
+
+ chr = l->data;
+ next = l->next ? l->next->data : NULL;
+
+ DBG("0x%04x UUID: %s properties: %02x",
+ chr->handle, chr->uuid, chr->properties);
+
+ report = report_new(hog, chr);
+ start = chr->value_handle + 1;
+ end = (next ? next->handle - 1 : primary->range.end);
+ discover_report(hog, hog->attrib, start, end, report);
+ }
+}
+
+static void external_report_reference_cb(guint8 status, const guint8 *pdu,
+ guint16 plen, gpointer user_data)
+{
+ struct gatt_request *req = user_data;
+ struct bt_hog *hog = req->user_data;
+ uint16_t uuid16;
+ bt_uuid_t uuid;
+
+ destroy_gatt_req(req);
+
+ if (status != 0) {
+ error("Read External Report Reference descriptor failed: %s",
+ att_ecode2str(status));
+ return;
+ }
+
+ if (plen != 3) {
+ error("Malformed ATT read response");
+ return;
+ }
+
+ uuid16 = get_le16(&pdu[1]);
+ DBG("External report reference read, external report characteristic "
+ "UUID: 0x%04x", uuid16);
+
+ /* Do not discover if is not a Report */
+ if (uuid16 != HOG_REPORT_UUID)
+ return;
+
+ bt_uuid16_create(&uuid, uuid16);
+ discover_char(hog, hog->attrib, 0x0001, 0xffff, &uuid,
+ external_service_char_cb, hog);
+}
+
+static int report_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct report *ra = a, *rb = b;
+
+ /* sort by type first.. */
+ if (ra->type != rb->type)
+ return ra->type - rb->type;
+
+ /* skip id check in case of report id 0 */
+ if (!rb->id)
+ return 0;
+
+ /* ..then by id */
+ return ra->id - rb->id;
+}
+
+static struct report *find_report(struct bt_hog *hog, uint8_t type, uint8_t id)
+{
+ struct report cmp;
+ GSList *l;
+
+ cmp.type = type;
+ cmp.id = hog->has_report_id ? id : 0;
+
+ l = g_slist_find_custom(hog->reports, &cmp, report_cmp);
+
+ return l ? l->data : NULL;
+}
+
+static struct report *find_report_by_rtype(struct bt_hog *hog, uint8_t rtype,
+ uint8_t id)
+{
+ uint8_t type;
+
+ switch (rtype) {
+ case UHID_FEATURE_REPORT:
+ type = HOG_REPORT_TYPE_FEATURE;
+ break;
+ case UHID_OUTPUT_REPORT:
+ type = HOG_REPORT_TYPE_OUTPUT;
+ break;
+ case UHID_INPUT_REPORT:
+ type = HOG_REPORT_TYPE_INPUT;
+ break;
+ default:
+ return NULL;
+ }
+
+ return find_report(hog, type, id);
+}
+
+static void output_written_cb(guint8 status, const guint8 *pdu,
+ guint16 plen, gpointer user_data)
+{
+ struct gatt_request *req = user_data;
+
+ destroy_gatt_req(req);
+
+ if (status != 0) {
+ error("Write output report failed: %s", att_ecode2str(status));
+ return;
+ }
+}
+
+static void forward_report(struct uhid_event *ev, void *user_data)
+{
+ struct bt_hog *hog = user_data;
+ struct report *report;
+ void *data;
+ int size;
+
+ report = find_report_by_rtype(hog, ev->u.output.rtype,
+ ev->u.output.data[0]);
+ if (!report)
+ return;
+
+ data = ev->u.output.data;
+ size = ev->u.output.size;
+ if (hog->has_report_id && size > 0) {
+ data++;
+ --size;
+ }
+
+ DBG("Sending report type %d ID %d to handle 0x%X", report->type,
+ report->id, report->decl->value_handle);
+
+ if (hog->attrib == NULL)
+ return;
+
+ if (report->decl->properties & GATT_CHR_PROP_WRITE)
+ write_char(hog, hog->attrib, report->decl->value_handle,
+ data, size, output_written_cb, hog);
+ else if (report->decl->properties & GATT_CHR_PROP_WRITE_WITHOUT_RESP)
+ gatt_write_cmd(hog->attrib, report->decl->value_handle,
+ data, size, NULL, NULL);
+}
+
+static void get_feature(struct uhid_event *ev, void *user_data)
+{
+ struct bt_hog *hog = user_data;
+ struct report *report;
+ struct uhid_event rsp;
+ int err;
+
+ memset(&rsp, 0, sizeof(rsp));
+ rsp.type = UHID_FEATURE_ANSWER;
+ rsp.u.feature_answer.id = ev->u.feature.id;
+
+ report = find_report_by_rtype(hog, ev->u.feature.rtype,
+ ev->u.feature.rnum);
+ if (!report) {
+ rsp.u.feature_answer.err = ENOTSUP;
+ goto done;
+ }
+
+ if (!report->value) {
+ rsp.u.feature_answer.err = EIO;
+ goto done;
+ }
+
+ rsp.u.feature_answer.size = report->len;
+ memcpy(rsp.u.feature_answer.data, report->value, report->len);
+
+done:
+ err = bt_uhid_send(hog->uhid, &rsp);
+ if (err < 0)
+ error("bt_uhid_send: %s", strerror(-err));
+}
+
+static void set_report_cb(guint8 status, const guint8 *pdu,
+ guint16 plen, gpointer user_data)
+{
+ struct bt_hog *hog = user_data;
+ struct uhid_event rsp;
+ int err;
+
+ hog->setrep_att = 0;
+
+ memset(&rsp, 0, sizeof(rsp));
+ rsp.type = UHID_SET_REPORT_REPLY;
+ rsp.u.set_report_reply.id = hog->setrep_id;
+ rsp.u.set_report_reply.err = status;
+
+ if (status != 0)
+ error("Error setting Report value: %s", att_ecode2str(status));
+
+ err = bt_uhid_send(hog->uhid, &rsp);
+ if (err < 0)
+ error("bt_uhid_send: %s", strerror(-err));
+}
+
+static void set_report(struct uhid_event *ev, void *user_data)
+{
+ struct bt_hog *hog = user_data;
+ struct report *report;
+ void *data;
+ int size;
+ int err;
+
+ /* uhid never sends reqs in parallel; if there's a req, it timed out */
+ if (hog->setrep_att) {
+ g_attrib_cancel(hog->attrib, hog->setrep_att);
+ hog->setrep_att = 0;
+ }
+
+ hog->setrep_id = ev->u.set_report.id;
+
+ report = find_report_by_rtype(hog, ev->u.set_report.rtype,
+ ev->u.set_report.rnum);
+ if (!report) {
+ err = ENOTSUP;
+ goto fail;
+ }
+
+ data = ev->u.set_report.data;
+ size = ev->u.set_report.size;
+ if (hog->has_report_id && size > 0) {
+ data++;
+ --size;
+ }
+
+ DBG("Sending report type %d ID %d to handle 0x%X", report->type,
+ report->id, report->decl->value_handle);
+
+ if (hog->attrib == NULL)
+ return;
+
+ hog->setrep_att = gatt_write_char(hog->attrib,
+ report->decl->value_handle,
+ data, size, set_report_cb,
+ hog);
+ if (!hog->setrep_att) {
+ err = ENOMEM;
+ goto fail;
+ }
+
+ return;
+fail:
+ /* cancel the request on failure */
+ set_report_cb(err, NULL, 0, hog);
+}
+
+static void get_report_cb(guint8 status, const guint8 *pdu, guint16 len,
+ gpointer user_data)
+{
+ struct bt_hog *hog = user_data;
+ struct uhid_event rsp;
+ int err;
+
+ hog->getrep_att = 0;
+
+ memset(&rsp, 0, sizeof(rsp));
+ rsp.type = UHID_GET_REPORT_REPLY;
+ rsp.u.get_report_reply.id = hog->getrep_id;
+
+ if (status != 0) {
+ error("Error reading Report value: %s", att_ecode2str(status));
+ goto exit;
+ }
+
+ if (len == 0) {
+ error("Error reading Report, length %d", len);
+ status = EIO;
+ goto exit;
+ }
+
+ if (pdu[0] != 0x0b) {
+ error("Error reading Report, invalid response: %02x", pdu[0]);
+ status = EPROTO;
+ goto exit;
+ }
+
+ --len;
+ ++pdu;
+ if (hog->has_report_id && len > 0) {
+ --len;
+ ++pdu;
+ }
+
+ rsp.u.get_report_reply.size = len;
+ memcpy(rsp.u.get_report_reply.data, pdu, len);
+
+exit:
+ rsp.u.get_report_reply.err = status;
+ err = bt_uhid_send(hog->uhid, &rsp);
+ if (err < 0)
+ error("bt_uhid_send: %s", strerror(-err));
+}
+
+static void get_report(struct uhid_event *ev, void *user_data)
+{
+ struct bt_hog *hog = user_data;
+ struct report *report;
+ guint8 err;
+
+ /* uhid never sends reqs in parallel; if there's a req, it timed out */
+ if (hog->getrep_att) {
+ g_attrib_cancel(hog->attrib, hog->getrep_att);
+ hog->getrep_att = 0;
+ }
+
+ hog->getrep_id = ev->u.get_report.id;
+
+ report = find_report_by_rtype(hog, ev->u.get_report.rtype,
+ ev->u.get_report.rnum);
+ if (!report) {
+ err = ENOTSUP;
+ goto fail;
+ }
+
+ hog->getrep_att = gatt_read_char(hog->attrib,
+ report->decl->value_handle,
+ get_report_cb, hog);
+ if (!hog->getrep_att) {
+ err = ENOMEM;
+ goto fail;
+ }
+
+ return;
+
+fail:
+ /* cancel the request on failure */
+ get_report_cb(err, NULL, 0, hog);
+}
+
+static bool get_descriptor_item_info(uint8_t *buf, ssize_t blen, ssize_t *len,
+ bool *is_long)
+{
+ if (!blen)
+ return false;
+
+ *is_long = (buf[0] == 0xfe);
+
+ if (*is_long) {
+ if (blen < 3)
+ return false;
+
+ /*
+ * long item:
+ * byte 0 -> 0xFE
+ * byte 1 -> data size
+ * byte 2 -> tag
+ * + data
+ */
+
+ *len = buf[1] + 3;
+ } else {
+ uint8_t b_size;
+
+ /*
+ * short item:
+ * byte 0[1..0] -> data size (=0, 1, 2, 4)
+ * byte 0[3..2] -> type
+ * byte 0[7..4] -> tag
+ * + data
+ */
+
+ b_size = buf[0] & 0x03;
+ *len = (b_size ? 1 << (b_size - 1) : 0) + 1;
+ }
+
+ /* item length should be no more than input buffer length */
+ return *len <= blen;
+}
+
+static char *item2string(char *str, uint8_t *buf, uint8_t len)
+{
+ char *p = str;
+ int i;
+
+ /*
+ * Since long item tags are not defined except for vendor ones, we
+ * just ensure that short items are printed properly (up to 5 bytes).
+ */
+ for (i = 0; i < 6 && i < len; i++)
+ p += sprintf(p, " %02x", buf[i]);
+
+ /*
+ * If there are some data left, just add continuation mark to indicate
+ * this.
+ */
+ if (i < len)
+ sprintf(p, " ...");
+
+ return str;
+}
+
+static void report_map_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
+ gpointer user_data)
+{
+ struct gatt_request *req = user_data;
+ struct bt_hog *hog = req->user_data;
+ uint8_t value[HOG_REPORT_MAP_MAX_SIZE];
+ struct uhid_event ev;
+ ssize_t vlen;
+ char itemstr[20]; /* 5x3 (data) + 4 (continuation) + 1 (null) */
+ int i, err;
+ GError *gerr = NULL;
+
+ destroy_gatt_req(req);
+
+ if (status != 0) {
+ error("Report Map read failed: %s", att_ecode2str(status));
+ return;
+ }
+
+ vlen = dec_read_resp(pdu, plen, value, sizeof(value));
+ if (vlen < 0) {
+ error("ATT protocol error");
+ return;
+ }
+
+ DBG("Report MAP:");
+ for (i = 0; i < vlen;) {
+ ssize_t ilen = 0;
+ bool long_item = false;
+
+ if (get_descriptor_item_info(&value[i], vlen - i, &ilen,
+ &long_item)) {
+ /* Report ID is short item with prefix 100001xx */
+ if (!long_item && (value[i] & 0xfc) == 0x84)
+ hog->has_report_id = TRUE;
+
+ DBG("\t%s", item2string(itemstr, &value[i], ilen));
+
+ i += ilen;
+ } else {
+ error("Report Map parsing failed at %d", i);
+
+ /* Just print remaining items at once and break */
+ DBG("\t%s", item2string(itemstr, &value[i], vlen - i));
+ break;
+ }
+ }
+
+ /* create uHID device */
+ memset(&ev, 0, sizeof(ev));
+ ev.type = UHID_CREATE;
+
+ bt_io_get(g_attrib_get_channel(hog->attrib), &gerr,
+ BT_IO_OPT_SOURCE, ev.u.create.phys,
+ BT_IO_OPT_DEST, ev.u.create.uniq,
+ BT_IO_OPT_INVALID);
+ if (gerr) {
+ error("Failed to connection details: %s", gerr->message);
+ g_error_free(gerr);
+ return;
+ }
+
+ strcpy((char *) ev.u.create.name, hog->name);
+ ev.u.create.vendor = hog->vendor;
+ ev.u.create.product = hog->product;
+ ev.u.create.version = hog->version;
+ ev.u.create.country = hog->bcountrycode;
+ ev.u.create.bus = BUS_BLUETOOTH;
+ ev.u.create.rd_data = value;
+ ev.u.create.rd_size = vlen;
+
+ err = bt_uhid_send(hog->uhid, &ev);
+ if (err < 0) {
+ error("bt_uhid_send: %s", strerror(-err));
+ return;
+ }
+
+ bt_uhid_register(hog->uhid, UHID_OUTPUT, forward_report, hog);
+ bt_uhid_register(hog->uhid, UHID_FEATURE, get_feature, hog);
+ bt_uhid_register(hog->uhid, UHID_GET_REPORT, get_report, hog);
+ bt_uhid_register(hog->uhid, UHID_SET_REPORT, set_report, hog);
+}
+
+static void info_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
+ gpointer user_data)
+{
+ struct gatt_request *req = user_data;
+ struct bt_hog *hog = req->user_data;
+ uint8_t value[HID_INFO_SIZE];
+ ssize_t vlen;
+
+ destroy_gatt_req(req);
+
+ if (status != 0) {
+ error("HID Information read failed: %s",
+ att_ecode2str(status));
+ return;
+ }
+
+ vlen = dec_read_resp(pdu, plen, value, sizeof(value));
+ if (vlen != 4) {
+ error("ATT protocol error");
+ return;
+ }
+
+ hog->bcdhid = get_le16(&value[0]);
+ hog->bcountrycode = value[2];
+ hog->flags = value[3];
+
+ DBG("bcdHID: 0x%04X bCountryCode: 0x%02X Flags: 0x%02X",
+ hog->bcdhid, hog->bcountrycode, hog->flags);
+}
+
+static void proto_mode_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
+ gpointer user_data)
+{
+ struct gatt_request *req = user_data;
+ struct bt_hog *hog = req->user_data;
+ uint8_t value;
+ ssize_t vlen;
+
+ destroy_gatt_req(req);
+
+ if (status != 0) {
+ error("Protocol Mode characteristic read failed: %s",
+ att_ecode2str(status));
+ return;
+ }
+
+ vlen = dec_read_resp(pdu, plen, &value, sizeof(value));
+ if (vlen < 0) {
+ error("ATT protocol error");
+ return;
+ }
+
+ if (value == HOG_PROTO_MODE_BOOT) {
+ uint8_t nval = HOG_PROTO_MODE_REPORT;
+
+ DBG("HoG is operating in Boot Procotol Mode");
+
+ gatt_write_cmd(hog->attrib, hog->proto_mode_handle, &nval,
+ sizeof(nval), NULL, NULL);
+ } else if (value == HOG_PROTO_MODE_REPORT)
+ DBG("HoG is operating in Report Protocol Mode");
+}
+
+static void char_discovered_cb(uint8_t status, GSList *chars, void *user_data)
+{
+ struct gatt_request *req = user_data;
+ struct bt_hog *hog = req->user_data;
+ struct gatt_primary *primary = hog->primary;
+ bt_uuid_t report_uuid, report_map_uuid, info_uuid;
+ bt_uuid_t proto_mode_uuid, ctrlpt_uuid;
+ struct report *report;
+ GSList *l;
+ uint16_t info_handle = 0, proto_mode_handle = 0;
+
+ destroy_gatt_req(req);
+
+ if (status != 0) {
+ const char *str = att_ecode2str(status);
+ DBG("Discover all characteristics failed: %s", str);
+ return;
+ }
+
+ bt_uuid16_create(&report_uuid, HOG_REPORT_UUID);
+ bt_uuid16_create(&report_map_uuid, HOG_REPORT_MAP_UUID);
+ bt_uuid16_create(&info_uuid, HOG_INFO_UUID);
+ bt_uuid16_create(&proto_mode_uuid, HOG_PROTO_MODE_UUID);
+ bt_uuid16_create(&ctrlpt_uuid, HOG_CONTROL_POINT_UUID);
+
+ for (l = chars; l; l = g_slist_next(l)) {
+ struct gatt_char *chr, *next;
+ bt_uuid_t uuid;
+ uint16_t start, end;
+
+ chr = l->data;
+ next = l->next ? l->next->data : NULL;
+
+ DBG("0x%04x UUID: %s properties: %02x",
+ chr->handle, chr->uuid, chr->properties);
+
+ bt_string_to_uuid(&uuid, chr->uuid);
+
+ start = chr->value_handle + 1;
+ end = (next ? next->handle - 1 : primary->range.end);
+
+ if (bt_uuid_cmp(&uuid, &report_uuid) == 0) {
+ report = report_new(hog, chr);
+ discover_report(hog, hog->attrib, start, end, report);
+ } else if (bt_uuid_cmp(&uuid, &report_map_uuid) == 0) {
+ read_char(hog, hog->attrib, chr->value_handle,
+ report_map_read_cb, hog);
+ discover_external(hog, hog->attrib, start, end, hog);
+ } else if (bt_uuid_cmp(&uuid, &info_uuid) == 0)
+ info_handle = chr->value_handle;
+ else if (bt_uuid_cmp(&uuid, &proto_mode_uuid) == 0)
+ proto_mode_handle = chr->value_handle;
+ else if (bt_uuid_cmp(&uuid, &ctrlpt_uuid) == 0)
+ hog->ctrlpt_handle = chr->value_handle;
+ }
+
+ if (proto_mode_handle) {
+ hog->proto_mode_handle = proto_mode_handle;
+ read_char(hog, hog->attrib, proto_mode_handle,
+ proto_mode_read_cb, hog);
+ }
+
+ if (info_handle)
+ read_char(hog, hog->attrib, info_handle, info_read_cb, hog);
+}
+
+static void report_free(void *data)
+{
+ struct report *report = data;
+
+ g_free(report->value);
+ g_free(report->decl);
+ g_free(report);
+}
+
+static void cancel_gatt_req(struct gatt_request *req)
+{
+ if (g_attrib_cancel(req->hog->attrib, req->id))
+ destroy_gatt_req(req);
+}
+
+static void hog_free(void *data)
+{
+ struct bt_hog *hog = data;
+
+ bt_hog_detach(hog);
+
+ queue_destroy(hog->bas, (void *) bt_bas_unref);
+ g_slist_free_full(hog->instances, hog_free);
+
+ bt_scpp_unref(hog->scpp);
+ bt_dis_unref(hog->dis);
+ bt_uhid_unref(hog->uhid);
+ g_slist_free_full(hog->reports, report_free);
+ g_free(hog->name);
+ g_free(hog->primary);
+ queue_destroy(hog->gatt_op, (void *) destroy_gatt_req);
+ g_free(hog);
+}
+
+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;
+
+ hog = g_try_new0(struct bt_hog, 1);
+ if (!hog)
+ return NULL;
+
+ hog->gatt_op = queue_new();
+ hog->bas = queue_new();
+
+ 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);
+ return NULL;
+ }
+
+ hog->name = g_strdup(name);
+ hog->vendor = vendor;
+ hog->product = product;
+ hog->version = version;
+
+ if (primary)
+ hog->primary = g_memdup(primary, sizeof(*hog->primary));
+
+ return bt_hog_ref(hog);
+}
+
+struct bt_hog *bt_hog_ref(struct bt_hog *hog)
+{
+ if (!hog)
+ return NULL;
+
+ __sync_fetch_and_add(&hog->ref_count, 1);
+
+ return hog;
+}
+
+void bt_hog_unref(struct bt_hog *hog)
+{
+ if (!hog)
+ return;
+
+ if (__sync_sub_and_fetch(&hog->ref_count, 1))
+ return;
+
+ hog_free(hog);
+}
+
+static void find_included_cb(uint8_t status, GSList *services, void *user_data)
+{
+ struct gatt_request *req = user_data;
+ GSList *l;
+
+ DBG("");
+
+ destroy_gatt_req(req);
+
+ if (status) {
+ const char *str = att_ecode2str(status);
+ DBG("Find included failed: %s", str);
+ return;
+ }
+
+ for (l = services; l; l = l->next) {
+ struct gatt_included *include = l->data;
+
+ DBG("included: handle %x, uuid %s",
+ include->handle, include->uuid);
+ }
+}
+
+static void hog_attach_scpp(struct bt_hog *hog, struct gatt_primary *primary)
+{
+ if (hog->scpp) {
+ bt_scpp_attach(hog->scpp, hog->attrib);
+ return;
+ }
+
+ hog->scpp = bt_scpp_new(primary);
+ if (hog->scpp)
+ bt_scpp_attach(hog->scpp, hog->attrib);
+}
+
+static void dis_notify(uint8_t source, uint16_t vendor, uint16_t product,
+ uint16_t version, void *user_data)
+{
+ struct bt_hog *hog = user_data;
+
+ hog->vendor = vendor;
+ hog->product = product;
+ hog->version = version;
+}
+
+static void hog_attach_dis(struct bt_hog *hog, struct gatt_primary *primary)
+{
+ if (hog->dis) {
+ bt_dis_attach(hog->dis, hog->attrib);
+ return;
+ }
+
+ hog->dis = bt_dis_new(primary);
+ if (hog->dis) {
+ bt_dis_set_notification(hog->dis, dis_notify, hog);
+ bt_dis_attach(hog->dis, hog->attrib);
+ }
+}
+
+static void hog_attach_bas(struct bt_hog *hog, struct gatt_primary *primary)
+{
+ struct bt_bas *instance;
+
+ instance = bt_bas_new(primary);
+ if (!instance)
+ return;
+
+ bt_bas_attach(instance, hog->attrib);
+ queue_push_head(hog->bas, instance);
+}
+
+static void hog_attach_hog(struct bt_hog *hog, struct gatt_primary *primary)
+{
+ struct bt_hog *instance;
+
+ if (!hog->primary) {
+ hog->primary = g_memdup(primary, sizeof(*primary));
+ discover_char(hog, hog->attrib, primary->range.start,
+ primary->range.end, NULL,
+ char_discovered_cb, hog);
+ find_included(hog, hog->attrib, primary->range.start,
+ primary->range.end, find_included_cb, hog);
+ return;
+ }
+
+ instance = bt_hog_new(hog->uhid_fd, hog->name, hog->vendor,
+ hog->product, hog->version, primary);
+ if (!instance)
+ return;
+
+ find_included(instance, hog->attrib, primary->range.start,
+ primary->range.end, find_included_cb, instance);
+
+ bt_hog_attach(instance, hog->attrib);
+ hog->instances = g_slist_append(hog->instances, instance);
+}
+
+static void primary_cb(uint8_t status, GSList *services, void *user_data)
+{
+ struct gatt_request *req = user_data;
+ struct bt_hog *hog = req->user_data;
+ struct gatt_primary *primary;
+ GSList *l;
+
+ DBG("");
+
+ destroy_gatt_req(req);
+
+ if (status) {
+ const char *str = att_ecode2str(status);
+ DBG("Discover primary failed: %s", str);
+ return;
+ }
+
+ if (!services) {
+ DBG("No primary service found");
+ return;
+ }
+
+ for (l = services; l; l = l->next) {
+ primary = l->data;
+
+ if (strcmp(primary->uuid, SCAN_PARAMETERS_UUID) == 0) {
+ hog_attach_scpp(hog, primary);
+ continue;
+ }
+
+ if (strcmp(primary->uuid, DEVICE_INFORMATION_UUID) == 0) {
+ hog_attach_dis(hog, primary);
+ continue;
+ }
+
+ if (strcmp(primary->uuid, BATTERY_UUID) == 0) {
+ hog_attach_bas(hog, primary);
+ continue;
+ }
+
+ if (strcmp(primary->uuid, HOG_UUID) == 0)
+ hog_attach_hog(hog, primary);
+ }
+}
+
+bool bt_hog_attach(struct bt_hog *hog, void *gatt)
+{
+ struct gatt_primary *primary = hog->primary;
+ GSList *l;
+
+ if (hog->attrib)
+ return false;
+
+ hog->attrib = g_attrib_ref(gatt);
+
+ if (!primary) {
+ discover_primary(hog, hog->attrib, NULL, primary_cb, hog);
+ return true;
+ }
+
+ if (hog->scpp)
+ bt_scpp_attach(hog->scpp, gatt);
+
+ if (hog->dis)
+ bt_dis_attach(hog->dis, gatt);
+
+ queue_foreach(hog->bas, (void *) bt_bas_attach, gatt);
+
+ for (l = hog->instances; l; l = l->next) {
+ struct bt_hog *instance = l->data;
+
+ bt_hog_attach(instance, gatt);
+ }
+
+ if (hog->reports == NULL) {
+ discover_char(hog, hog->attrib, primary->range.start,
+ primary->range.end, NULL,
+ char_discovered_cb, hog);
+ return true;
+ }
+
+ for (l = hog->reports; l; l = l->next) {
+ struct report *r = l->data;
+
+ r->notifyid = g_attrib_register(hog->attrib,
+ ATT_OP_HANDLE_NOTIFY,
+ r->decl->value_handle,
+ report_value_cb, r, NULL);
+ }
+
+ return true;
+}
+
+void bt_hog_detach(struct bt_hog *hog)
+{
+ GSList *l;
+
+ if (!hog->attrib)
+ return;
+
+ queue_foreach(hog->bas, (void *) bt_bas_detach, NULL);
+
+ for (l = hog->instances; l; l = l->next) {
+ struct bt_hog *instance = l->data;
+
+ bt_hog_detach(instance);
+ }
+
+ for (l = hog->reports; l; l = l->next) {
+ struct report *r = l->data;
+
+ if (r->notifyid > 0) {
+ g_attrib_unregister(hog->attrib, r->notifyid);
+ r->notifyid = 0;
+ }
+ }
+
+ if (hog->scpp)
+ bt_scpp_detach(hog->scpp);
+
+ if (hog->dis)
+ bt_dis_detach(hog->dis);
+
+ queue_foreach(hog->gatt_op, (void *) cancel_gatt_req, NULL);
+ g_attrib_unref(hog->attrib);
+ hog->attrib = NULL;
+}
+
+int bt_hog_set_control_point(struct bt_hog *hog, bool suspend)
+{
+ uint8_t value = suspend ? 0x00 : 0x01;
+
+ if (hog->attrib == NULL)
+ return -ENOTCONN;
+
+ if (hog->ctrlpt_handle == 0)
+ return -ENOTSUP;
+
+ gatt_write_cmd(hog->attrib, hog->ctrlpt_handle, &value,
+ sizeof(value), NULL, NULL);
+
+ return 0;
+}
+
+int bt_hog_send_report(struct bt_hog *hog, void *data, size_t size, int type)
+{
+ struct report *report;
+ GSList *l;
+
+ if (!hog)
+ return -EINVAL;
+
+ if (!hog->attrib)
+ return -ENOTCONN;
+
+ report = find_report(hog, type, 0);
+ if (!report)
+ return -ENOTSUP;
+
+ DBG("hog: Write report, handle 0x%X", report->decl->value_handle);
+
+ if (report->decl->properties & GATT_CHR_PROP_WRITE)
+ write_char(hog, hog->attrib, report->decl->value_handle,
+ data, size, output_written_cb, hog);
+
+ if (report->decl->properties & GATT_CHR_PROP_WRITE_WITHOUT_RESP)
+ gatt_write_cmd(hog->attrib, report->decl->value_handle,
+ data, size, NULL, NULL);
+
+ for (l = hog->instances; l; l = l->next) {
+ struct bt_hog *instance = l->data;
+
+ bt_hog_send_report(instance, data, size, type);
+ }
+
+ return 0;
+}
diff --git a/android/hog.h b/android/hog.h
new file mode 100755
index 00000000..2a9b899c
--- /dev/null
+++ b/android/hog.h
@@ -0,0 +1,41 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2014 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+struct bt_hog;
+
+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);
+
+bool bt_hog_attach(struct bt_hog *hog, void *gatt);
+void bt_hog_detach(struct bt_hog *hog);
+
+int bt_hog_set_control_point(struct bt_hog *hog, bool suspend);
+int bt_hog_send_report(struct bt_hog *hog, void *data, size_t size, int type);
diff --git a/android/sco-ipc-api.txt b/android/sco-ipc-api.txt
new file mode 100755
index 00000000..27d5ef21
--- /dev/null
+++ b/android/sco-ipc-api.txt
@@ -0,0 +1,37 @@
+Bluetooth SCO Audio Plugin
+==========================
+
+The SCO Audio Plugin communicate through abstract socket name
+"\0bluez_sco_socket".
+
+ .----SCO----. .--Android--.
+ | Plugin | | Daemon |
+ | | Command | |
+ | | --------------------------> | |
+ | | | |
+ | | <-------------------------- | |
+ | | Response | |
+ | | | |
+ | | | |
+ | | | |
+ '-----------' '-----------'
+
+
+ SCO HAL Daemon
+ ----------------------------------------------------
+
+ call get_fd() --> Get SCO socket fd
+ return get_fd() <-- Return SCO socket fd and mtu
+
+SCO Audio Service (ID 0)
+========================
+
+ Opcode 0x00 - Error response
+
+ Response parameters: Status (1 octet)
+
+ Opcode 0x01 - Get SCO fd command
+
+ Command parameters: Remote address (6 octets)
+ Response parameters: MTU (2 octets)
+ File descriptor (inline)
diff --git a/android/scpp.c b/android/scpp.c
new file mode 100755
index 00000000..f8f81f37
--- /dev/null
+++ b/android/scpp.c
@@ -0,0 +1,371 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012 Nordic Semiconductor Inc.
+ * Copyright (C) 2012 Instituto Nokia de Tecnologia - INdT
+ *
+ *
+ * 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 <stdbool.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include "src/log.h"
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "src/shared/util.h"
+#include "src/shared/queue.h"
+
+#include "attrib/att.h"
+#include "attrib/gattrib.h"
+#include "attrib/gatt.h"
+
+#include "android/scpp.h"
+
+#define SCAN_INTERVAL_WIN_UUID 0x2A4F
+#define SCAN_REFRESH_UUID 0x2A31
+
+#define SCAN_INTERVAL 0x0060
+#define SCAN_WINDOW 0x0030
+#define SERVER_REQUIRES_REFRESH 0x00
+
+struct bt_scpp {
+ int ref_count;
+ GAttrib *attrib;
+ struct gatt_primary *primary;
+ uint16_t interval;
+ uint16_t window;
+ uint16_t iwhandle;
+ uint16_t refresh_handle;
+ guint refresh_cb_id;
+ struct queue *gatt_op;
+};
+
+static void discover_char(struct bt_scpp *scpp, GAttrib *attrib,
+ uint16_t start, uint16_t end,
+ bt_uuid_t *uuid, gatt_cb_t func,
+ gpointer user_data)
+{
+ unsigned int id;
+
+ id = gatt_discover_char(attrib, start, end, uuid, func, user_data);
+
+ if (queue_push_head(scpp->gatt_op, UINT_TO_PTR(id)))
+ return;
+
+ error("scpp: Could not discover characteristic");
+ g_attrib_cancel(attrib, id);
+}
+
+static void discover_desc(struct bt_scpp *scpp, GAttrib *attrib,
+ uint16_t start, uint16_t end, bt_uuid_t *uuid,
+ gatt_cb_t func, gpointer user_data)
+{
+ unsigned int id;
+
+ id = gatt_discover_desc(attrib, start, end, uuid, func, user_data);
+
+ if (queue_push_head(scpp->gatt_op, UINT_TO_PTR(id)))
+ return;
+
+ error("scpp: Could not discover descriptor");
+ g_attrib_cancel(attrib, id);
+}
+
+static void write_char(struct bt_scpp *scan, GAttrib *attrib, uint16_t handle,
+ const uint8_t *value, size_t vlen,
+ GAttribResultFunc func,
+ gpointer user_data)
+{
+ unsigned int id;
+
+ id = gatt_write_char(attrib, handle, value, vlen, func, user_data);
+
+ if (queue_push_head(scan->gatt_op, UINT_TO_PTR(id)))
+ return;
+
+ error("scpp: Could not read char");
+ g_attrib_cancel(attrib, id);
+}
+
+static void scpp_free(struct bt_scpp *scan)
+{
+ bt_scpp_detach(scan);
+
+ g_free(scan->primary);
+ queue_destroy(scan->gatt_op, NULL); /* cleared in bt_scpp_detach */
+ g_free(scan);
+}
+
+struct bt_scpp *bt_scpp_new(void *primary)
+{
+ struct bt_scpp *scan;
+
+ scan = g_try_new0(struct bt_scpp, 1);
+ if (!scan)
+ return NULL;
+
+ scan->interval = SCAN_INTERVAL;
+ scan->window = SCAN_WINDOW;
+
+ scan->gatt_op = queue_new();
+ if (!scan->gatt_op) {
+ scpp_free(scan);
+ return NULL;
+ }
+
+ if (primary)
+ scan->primary = g_memdup(primary, sizeof(*scan->primary));
+
+ return bt_scpp_ref(scan);
+}
+
+struct bt_scpp *bt_scpp_ref(struct bt_scpp *scan)
+{
+ if (!scan)
+ return NULL;
+
+ __sync_fetch_and_add(&scan->ref_count, 1);
+
+ return scan;
+}
+
+void bt_scpp_unref(struct bt_scpp *scan)
+{
+ if (!scan)
+ return;
+
+ if (__sync_sub_and_fetch(&scan->ref_count, 1))
+ return;
+
+ scpp_free(scan);
+}
+
+static void write_scan_params(GAttrib *attrib, uint16_t handle,
+ uint16_t interval, uint16_t window)
+{
+ uint8_t value[4];
+
+ put_le16(interval, &value[0]);
+ put_le16(window, &value[2]);
+
+ gatt_write_cmd(attrib, handle, value, sizeof(value), NULL, NULL);
+}
+
+static void refresh_value_cb(const uint8_t *pdu, uint16_t len,
+ gpointer user_data)
+{
+ struct bt_scpp *scan = user_data;
+
+ DBG("Server requires refresh: %d", pdu[3]);
+
+ if (pdu[3] == SERVER_REQUIRES_REFRESH)
+ write_scan_params(scan->attrib, scan->iwhandle, scan->interval,
+ scan->window);
+}
+
+static void ccc_written_cb(guint8 status, const guint8 *pdu,
+ guint16 plen, gpointer user_data)
+{
+ struct bt_scpp *scan = user_data;
+
+ if (status != 0) {
+ error("Write Scan Refresh CCC failed: %s",
+ att_ecode2str(status));
+ return;
+ }
+
+ DBG("Scan Refresh: notification enabled");
+
+ scan->refresh_cb_id = g_attrib_register(scan->attrib,
+ ATT_OP_HANDLE_NOTIFY, scan->refresh_handle,
+ refresh_value_cb, scan, NULL);
+}
+
+static void write_ccc(struct bt_scpp *scan, GAttrib *attrib, uint16_t handle,
+ void *user_data)
+{
+ uint8_t value[2];
+
+ put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value);
+
+ write_char(scan, attrib, handle, value, sizeof(value), ccc_written_cb,
+ user_data);
+}
+
+static void discover_descriptor_cb(uint8_t status, GSList *descs,
+ void *user_data)
+{
+ struct bt_scpp *scan = user_data;
+ struct gatt_desc *desc;
+
+ if (status != 0) {
+ error("Discover descriptors failed: %s", att_ecode2str(status));
+ return;
+ }
+
+ /* There will be only one descriptor on list and it will be CCC */
+ desc = descs->data;
+
+ write_ccc(scan, scan->attrib, desc->handle, scan);
+}
+
+static void refresh_discovered_cb(uint8_t status, GSList *chars,
+ void *user_data)
+{
+ struct bt_scpp *scan = user_data;
+ struct gatt_char *chr;
+ uint16_t start, end;
+ bt_uuid_t uuid;
+
+ if (status) {
+ error("Scan Refresh %s", att_ecode2str(status));
+ return;
+ }
+
+ if (!chars) {
+ DBG("Scan Refresh not supported");
+ return;
+ }
+
+ chr = chars->data;
+
+ DBG("Scan Refresh handle: 0x%04x", chr->value_handle);
+
+ start = chr->value_handle + 1;
+ end = scan->primary->range.end;
+
+ if (start > end)
+ return;
+
+ scan->refresh_handle = chr->value_handle;
+
+ bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+
+ discover_desc(scan, scan->attrib, start, end, &uuid,
+ discover_descriptor_cb, user_data);
+}
+
+static void iwin_discovered_cb(uint8_t status, GSList *chars, void *user_data)
+{
+ struct bt_scpp *scan = user_data;
+ struct gatt_char *chr;
+
+ if (status) {
+ error("Discover Scan Interval Window: %s",
+ att_ecode2str(status));
+ return;
+ }
+
+ chr = chars->data;
+ scan->iwhandle = chr->value_handle;
+
+ DBG("Scan Interval Window handle: 0x%04x", scan->iwhandle);
+
+ write_scan_params(scan->attrib, scan->iwhandle, scan->interval,
+ scan->window);
+}
+
+bool bt_scpp_attach(struct bt_scpp *scan, void *attrib)
+{
+ bt_uuid_t iwin_uuid, refresh_uuid;
+
+ if (!scan || scan->attrib || !scan->primary)
+ return false;
+
+ scan->attrib = g_attrib_ref(attrib);
+
+ if (scan->iwhandle)
+ write_scan_params(scan->attrib, scan->iwhandle, scan->interval,
+ scan->window);
+ else {
+ bt_uuid16_create(&iwin_uuid, SCAN_INTERVAL_WIN_UUID);
+ discover_char(scan, scan->attrib, scan->primary->range.start,
+ scan->primary->range.end, &iwin_uuid,
+ iwin_discovered_cb, scan);
+ }
+
+ if (scan->refresh_handle)
+ scan->refresh_cb_id = g_attrib_register(scan->attrib,
+ ATT_OP_HANDLE_NOTIFY, scan->refresh_handle,
+ refresh_value_cb, scan, NULL);
+ else {
+ bt_uuid16_create(&refresh_uuid, SCAN_REFRESH_UUID);
+ discover_char(scan, scan->attrib, scan->primary->range.start,
+ scan->primary->range.end, &refresh_uuid,
+ refresh_discovered_cb, scan);
+ }
+
+ return true;
+}
+
+static void cancel_gatt_req(void *data, void *user_data)
+{
+ unsigned int id = PTR_TO_UINT(data);
+ struct bt_scpp *scan = user_data;
+
+ g_attrib_cancel(scan->attrib, id);
+}
+
+void bt_scpp_detach(struct bt_scpp *scan)
+{
+ if (!scan || !scan->attrib)
+ return;
+
+ if (scan->refresh_cb_id > 0) {
+ g_attrib_unregister(scan->attrib, scan->refresh_cb_id);
+ scan->refresh_cb_id = 0;
+ }
+
+ queue_foreach(scan->gatt_op, cancel_gatt_req, scan);
+ g_attrib_unref(scan->attrib);
+ scan->attrib = NULL;
+}
+
+bool bt_scpp_set_interval(struct bt_scpp *scan, uint16_t value)
+{
+ if (!scan)
+ return false;
+
+ /* TODO: Check valid range */
+
+ scan->interval = value;
+
+ return true;
+}
+
+bool bt_scpp_set_window(struct bt_scpp *scan, uint16_t value)
+{
+ if (!scan)
+ return false;
+
+ /* TODO: Check valid range */
+
+ scan->window = value;
+
+ return true;
+}
diff --git a/android/scpp.h b/android/scpp.h
new file mode 100755
index 00000000..048fb9f2
--- /dev/null
+++ b/android/scpp.h
@@ -0,0 +1,35 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2014 Intel Corporation. All rights reserved.
+ *
+ *
+ * This library is free software; you can rescpptribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is scpptributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+struct bt_scpp;
+
+struct bt_scpp *bt_scpp_new(void *primary);
+
+struct bt_scpp *bt_scpp_ref(struct bt_scpp *scan);
+void bt_scpp_unref(struct bt_scpp *scan);
+
+bool bt_scpp_attach(struct bt_scpp *scan, void *gatt);
+void bt_scpp_detach(struct bt_scpp *scan);
+
+bool bt_scpp_set_interval(struct bt_scpp *scan, uint16_t value);
+bool bt_scpp_set_window(struct bt_scpp *scan, uint16_t value);
diff --git a/android/socket-api.txt b/android/socket-api.txt
new file mode 100755
index 00000000..9f622f98
--- /dev/null
+++ b/android/socket-api.txt
@@ -0,0 +1,61 @@
+Android Socket protocol for Bluetooth
+=====================================
+
+Since Android switched from BlueZ (where sockets where nicely implemented) to
+Bluedroid user space stack there is a need to emulate bluetooth sockets.
+
+Android Bluetooth Socket Hardware Abstraction Layer (HAL) bt_sock.h has
+only 2 functions:
+
+static btsock_interface_t sock_if = {
+ sizeof(sock_if),
+ sock_listen,
+ sock_connect
+};
+
+with following parameters:
+
+sock_listen(btsock_type_t type, const char *service_name,
+ const uint8_t *uuid, int chan, int *sock_fd, int flags)
+sock_connect(const bt_bdaddr_t *bdaddr, btsock_type_t type,
+ const uint8_t *uuid, int chan, int *sock_fd, int flags)
+
+socket type RFCOMM is only supported at the moment. uuid and channel used
+to decide where to connect.
+
+sockfd is used to return socket fd to Android framework. It is used to inform
+framework when remote device is connected.
+
+listen()
+========
+
+Listens on RFCOMM socket, socket channel is either found based on uuid or
+channel parameter used directly. Returns sock_fd to Android framework.
+
+Through this sock_fd channel number as (int) needs to be written right after
+listen() succeeds.
+
+When remote device is connected to this socket we shall send accept signal
+through sock_fd
+
+connect()
+=========
+
+Connects to remote device specified in bd_addr parameter. Socket channel is
+found by SDP search of remote device by supplied uuid. Returns sock_fd to
+Android framework.
+
+Through this sock_fd channel number as (int) needs to be written right after
+connects() succeeds.
+
+When remote device is connected to this socket we shall send connect signal
+through sock_fd
+
+The format of connect/accept signal is shown below:
+
+struct hal_sock_connect_signal {
+ short size;
+ uint8_t bdaddr[6];
+ int channel;
+ int status;
+} __attribute__((packed));
diff --git a/attrib/att.c b/attrib/att.c
index 826b3c18..fef602c3 100755
--- a/attrib/att.c
+++ b/attrib/att.c
@@ -129,11 +129,18 @@ struct att_data_list *att_data_list_alloc(uint16_t num, uint16_t len)
return list;
}
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void get_uuid(uint8_t type, const void *val, bt_uuid_t *uuid)
+#else
static void get_uuid(uint8_t type, const void *val, bt_uuid_t *uuid)
+#endif
{
if (type == BT_UUID16)
bt_uuid16_create(uuid, get_le16(val));
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ else if (type == BT_UUID32)
+ bt_uuid32_create(uuid, get_le32(val));
+#endif
else {
uint128_t u128;
@@ -153,6 +160,10 @@ uint16_t enc_read_by_grp_req(uint16_t start, uint16_t end, bt_uuid_t *uuid,
if (uuid->type == BT_UUID16)
uuid_len = 2;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ else if (uuid->type == BT_UUID32)
+ uuid_len = 4;
+#endif
else if (uuid->type == BT_UUID128)
uuid_len = 16;
else
@@ -187,6 +198,10 @@ uint16_t dec_read_by_grp_req(const uint8_t *pdu, size_t len, uint16_t *start,
if (len == (min_len + 2))
type = BT_UUID16;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ else if (len == (min_len + 4))
+ type = BT_UUID32;
+#endif
else if (len == (min_len + 16))
type = BT_UUID128;
else
diff --git a/attrib/att.h b/attrib/att.h
index 2311aafb..c32f9ffe 100755
--- a/attrib/att.h
+++ b/attrib/att.h
@@ -106,6 +106,26 @@ struct att_range {
uint16_t end;
};
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void get_uuid(uint8_t type, const void *val, bt_uuid_t *uuid);
+
+static inline bt_uuid_t att_get_uuid(const void *ptr, uint8_t len)
+{
+ bt_uuid_t uuid;
+ if (len == 2) {
+ //return att_get_uuid16(ptr);
+ get_uuid(BT_UUID16, ptr, &uuid);
+ } else if (len == 4) {
+ //return att_get_uuid32(ptr);
+ get_uuid(BT_UUID32, ptr, &uuid);
+ } else {
+ //return att_get_uuid128(ptr);
+ get_uuid(BT_UUID128, ptr, &uuid);
+ }
+ return uuid;
+}
+#endif
+
struct att_data_list *att_data_list_alloc(uint16_t num, uint16_t len);
void att_data_list_free(struct att_data_list *list);
diff --git a/attrib/gatt-service.c b/attrib/gatt-service.c
index 629d9cfd..e434d3b5 100755
--- a/attrib/gatt-service.c
+++ b/attrib/gatt-service.c
@@ -81,17 +81,26 @@ static GSList *parse_opts(gatt_option opt1, va_list args)
while (opt != GATT_OPT_INVALID) {
switch (opt) {
case GATT_OPT_CHR_UUID16:
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ case GATT_OPT_DESC_UUID16:
+#endif
bt_uuid16_create(&info->uuid, va_arg(args, int));
/* characteristic declaration and value */
info->num_attrs += 2;
break;
case GATT_OPT_CHR_UUID:
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ case GATT_OPT_DESC_UUID:
+#endif
memcpy(&info->uuid, va_arg(args, bt_uuid_t *),
sizeof(bt_uuid_t));
/* characteristic declaration and value */
info->num_attrs += 2;
break;
case GATT_OPT_CHR_PROPS:
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ case GATT_OPT_DESC_PROPS:
+#endif
info->props = va_arg(args, int);
if (info->props & (GATT_CHR_PROP_NOTIFY |
@@ -103,6 +112,9 @@ static GSList *parse_opts(gatt_option opt1, va_list args)
* descriptor, but it is not supported yet. */
break;
case GATT_OPT_CHR_VALUE_CB:
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ case GATT_OPT_DESC_VALUE_CB:
+#endif
cb = g_new0(struct attrib_cb, 1);
cb->event = va_arg(args, attrib_event_t);
cb->fn = va_arg(args, void *);
@@ -130,7 +142,12 @@ static GSList *parse_opts(gatt_option opt1, va_list args)
}
opt = va_arg(args, gatt_option);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (opt == GATT_OPT_CHR_UUID16 || opt == GATT_OPT_CHR_UUID ||
+ opt == GATT_OPT_DESC_UUID16 || opt == GATT_OPT_DESC_UUID) {
+#else
if (opt == GATT_OPT_CHR_UUID16 || opt == GATT_OPT_CHR_UUID) {
+#endif
info = g_new0(struct gatt_info, 1);
l = g_slist_append(l, info);
}
@@ -268,6 +285,10 @@ static gboolean add_characteristic(struct btd_adapter *adapter,
if (info->value_handle != NULL)
*info->value_handle = a->handle;
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
+/* Since old attrib service implementation add descriptor by default
+ * if notification and indication properties are set, As per new gatt server implemenation
+ * CCCD are added by the application*/
/* client characteristic configuration descriptor */
if (info->props & (GATT_CHR_PROP_NOTIFY | GATT_CHR_PROP_INDICATE)) {
uint8_t cfg_val[2];
@@ -283,6 +304,7 @@ static gboolean add_characteristic(struct btd_adapter *adapter,
if (info->ccc_handle != NULL)
*info->ccc_handle = a->handle;
}
+#endif
*handle = h;
diff --git a/attrib/gatt-service.h b/attrib/gatt-service.h
index 728d3a8e..5a8ac38e 100755
--- a/attrib/gatt-service.h
+++ b/attrib/gatt-service.h
@@ -31,8 +31,20 @@ typedef enum {
/* a uint16 value */
GATT_OPT_CHR_UUID16,
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ /* bt_uuid_t* value */
+ GATT_OPT_DESC_UUID,
+
+ /* a uint16 value */
+ GATT_OPT_DESC_UUID16,
+#endif
+
GATT_OPT_CHR_PROPS,
GATT_OPT_CHR_VALUE_CB,
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ GATT_OPT_DESC_PROPS,
+ GATT_OPT_DESC_VALUE_CB,
+#endif
GATT_OPT_CHR_AUTHENTICATION,
GATT_OPT_CHR_AUTHORIZATION,
diff --git a/attrib/gatt.c b/attrib/gatt.c
index 480f8742..72457cee 100755
--- a/attrib/gatt.c
+++ b/attrib/gatt.c
@@ -185,6 +185,10 @@ static void put_uuid_le(const bt_uuid_t *uuid, void *dst)
{
if (uuid->type == BT_UUID16)
put_le16(uuid->value.u16, dst);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ else if (uuid->type == BT_UUID32)
+ put_le32(uuid->value.u32, dst);
+#endif
else
/* Convert from 128-bit BE to LE */
bswap_128(&uuid->value.u128, dst);
@@ -197,6 +201,13 @@ static void get_uuid128(uint8_t type, const void *val, bt_uuid_t *uuid)
bt_uuid16_create(&uuid16, get_le16(val));
bt_uuid_to_uuid128(&uuid16, uuid);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ } else if (type == BT_UUID32) {
+ bt_uuid_t uuid32;
+
+ bt_uuid32_create(&uuid32, get_le32(val));
+ bt_uuid_to_uuid128(&uuid32, uuid);
+#endif
} else {
uint128_t u128;
@@ -841,6 +852,45 @@ done:
long_read->func(status, rpdu, rlen, long_read->user_data);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+guint gatt_read_char_by_offset(GAttrib *attrib, uint16_t handle, uint16_t offset,
+ GAttribResultFunc func, gpointer user_data)
+{
+ uint8_t *buf;
+ size_t buflen;
+ guint16 plen;
+ guint id;
+ struct read_long_data *long_read;
+
+ long_read = g_try_new0(struct read_long_data, 1);
+
+ if (long_read == NULL)
+ return 0;
+
+ long_read->attrib = g_attrib_ref(attrib);
+ long_read->func = func;
+ long_read->user_data = user_data;
+ long_read->handle = handle;
+
+ buf = g_attrib_get_buffer(attrib, &buflen);
+ if (offset > 0)
+ plen = enc_read_blob_req(handle, offset, buf, buflen);
+ else
+ plen = enc_read_req(handle, buf, buflen);
+
+ id = g_attrib_send(attrib, 0, buf, plen, read_char_helper,
+ long_read, read_long_destroy);
+ if (id == 0)
+ g_free(long_read);
+ else {
+ __sync_fetch_and_add(&long_read->ref, 1);
+ long_read->id = id;
+ }
+
+ return id;
+}
+#endif
+
guint gatt_read_char(GAttrib *attrib, uint16_t handle, GAttribResultFunc func,
gpointer user_data)
{
diff --git a/attrib/gatt.h b/attrib/gatt.h
index 63b2940d..0d998c98 100755
--- a/attrib/gatt.h
+++ b/attrib/gatt.h
@@ -120,3 +120,8 @@ guint gatt_exchange_mtu(GAttrib *attrib, uint16_t mtu, GAttribResultFunc func,
gboolean gatt_parse_record(const sdp_record_t *rec,
uuid_t *prim_uuid, uint16_t *psm,
uint16_t *start, uint16_t *end);
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+guint gatt_read_char_by_offset(GAttrib *attrib, uint16_t handle, uint16_t offset,
+ GAttribResultFunc func, gpointer user_data);
+#endif
diff --git a/attrib/interactive.c b/attrib/interactive.c
index 7d4786ac..62a25fab 100755
--- a/attrib/interactive.c
+++ b/attrib/interactive.c
@@ -559,7 +559,9 @@ static void cmd_char_desc(int argcp, char **argvp)
static void cmd_read_hnd(int argcp, char **argvp)
{
int handle;
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ int offset = 0;
+#endif
if (conn_state != STATE_CONNECTED) {
failed("Disconnected\n");
return;
@@ -575,8 +577,18 @@ static void cmd_read_hnd(int argcp, char **argvp)
error("Invalid handle: %s\n", argvp[1]);
return;
}
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (argcp > 2) {
+ offset = strtohandle(argvp[2]);
+ if (offset < 0) {
+ error("Invalid Offset: %s\n", argvp[2]);
+ return;
+ }
+ }
+ gatt_read_char_by_offset(attrib, handle, offset, char_read_cb, attrib);
+#else
gatt_read_char(attrib, handle, char_read_cb, attrib);
+#endif
}
static void cmd_read_uuid(int argcp, char **argvp)
@@ -796,8 +808,13 @@ static struct {
"Characteristics Discovery" },
{ "char-desc", cmd_char_desc, "[start hnd] [end hnd]",
"Characteristics Descriptor Discovery" },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ { "char-read-hnd", cmd_read_hnd, "<handle> [offset]",
+ "Characteristics Value/Descriptor Read by handle" },
+#else
{ "char-read-hnd", cmd_read_hnd, "<handle>",
"Characteristics Value/Descriptor Read by handle" },
+#endif
{ "char-read-uuid", cmd_read_uuid, "<UUID> [start hnd] [end hnd]",
"Characteristics Value/Descriptor Read by UUID" },
{ "char-write-req", cmd_char_write, "<handle> <new value>",
diff --git a/client/main.c b/client/main.c
index e1198a83..bbf4cd33 100755
--- a/client/main.c
+++ b/client/main.c
@@ -808,6 +808,9 @@ static void cmd_show(const char *arg)
print_uuids(proxy);
print_property(proxy, "Modalias");
print_property(proxy, "Discovering");
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ print_property(proxy, "Advertising");
+#endif
}
static void cmd_select(const char *arg)
@@ -1368,6 +1371,9 @@ static void cmd_info(const char *arg)
print_property(proxy, "LegacyPairing");
print_uuids(proxy);
print_property(proxy, "Modalias");
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ print_property(proxy, "ManufacturerDataLen");
+#endif
print_property(proxy, "ManufacturerData");
print_property(proxy, "ServiceData");
print_property(proxy, "RSSI");
diff --git a/configure.ac b/configure.ac
index d4695001..af589abb 100755
--- a/configure.ac
+++ b/configure.ac
@@ -34,13 +34,8 @@ AC_PROG_LIBTOOL
if (test "$USE_MAINTAINER_MODE" = "yes"); then
AC_CHECK_PROG(enable_coverage, [lcov], [yes], [no])
- AC_CHECK_PROG(enable_dbus_run_session, [dbus-run-session], [yes])
- AC_CHECK_PROG(enable_valgrind, [valgrind], [yes])
- AC_CHECK_HEADERS(valgrind/memcheck.h)
fi
AM_CONDITIONAL(COVERAGE, test "${enable_coverage}" = "yes")
-AM_CONDITIONAL(DBUS_RUN_SESSION, test "${enable_dbus_run_session}" = "yes")
-AM_CONDITIONAL(VALGRIND, test "${enable_valgrind}" = "yes")
MISC_FLAGS
@@ -79,6 +74,14 @@ PKG_CHECK_MODULES(DBUS, dbus-1 >= 1.6, dummy=yes,
AC_SUBST(DBUS_CFLAGS)
AC_SUBST(DBUS_LIBS)
+PKG_CHECK_MODULES(LIBXML, libxml-2.0)
+AC_SUBST(LIBXML_CFLAGS)
+AC_SUBST(LIBXML_LIBS)
+
+PKG_CHECK_MODULES(INIPARSER, iniparser)
+AC_SUBST(INIPARSER_CFLAGS)
+AC_SUBST(INIPARSER_LIBS)
+
AC_ARG_WITH([dbusconfdir], AC_HELP_STRING([--with-dbusconfdir=DIR],
[path to D-Bus configuration directory]),
[path_dbusconfdir=${withval}])
@@ -180,6 +183,18 @@ AC_ARG_ENABLE(cups, AC_HELP_STRING([--disable-cups],
[disable CUPS printer support]), [enable_cups=${enableval}])
AM_CONDITIONAL(CUPS, test "${enable_cups}" != "no")
+
+AC_ARG_ENABLE(midi, AC_HELP_STRING([--enable-midi],
+ [enable MIDI support]), [enable_midi=${enableval}])
+AM_CONDITIONAL(MIDI, test "${enable_midi}" = "no")
+
+if (test "${enable_midi}" = "yes"); then
+ PKG_CHECK_MODULES(ALSA, alsa, dummy=no,
+ AC_MSG_ERROR(ALSA lib is required for MIDI support))
+ AC_SUBST(ALSA_CFLAGS)
+ AC_SUBST(ALSA_LIBS)
+fi
+
AC_ARG_ENABLE(obex, AC_HELP_STRING([--disable-obex],
[disable OBEX profile support]), [enable_obex=${enableval}])
@@ -245,6 +260,39 @@ AC_ARG_ENABLE(experimental, AC_HELP_STRING([--enable-experimental],
[enable_experimental=${enableval}])
AM_CONDITIONAL(EXPERIMENTAL, test "${enable_experimental}" = "yes")
+# Start of TIZEN_FEATURE_BLUEZ_MODIFY
+AC_ARG_ENABLE(wearable, AC_HELP_STRING([--enable-wearable],
+ [enable wearable profile]), [enable_wearable=${enableval}])
+AM_CONDITIONAL(WEARABLE, test "${enable_wearable}" = "yes")
+
+AC_ARG_ENABLE(autopair, AC_HELP_STRING([--enable-autopair],
+ [Enable Autopair Plugin]), [enable_autopair=${enableval}])
+AM_CONDITIONAL(AUTOPAIR, test "${enable_autopair}" = "yes")
+
+AC_ARG_ENABLE(hid, AC_HELP_STRING([--enable-hid],
+ [Enable HID Plugin]), [enable_hid=${enableval}])
+AM_CONDITIONAL(TIZEN_HID_PLUGIN, test "${enable_hid}" = "yes")
+
+AM_CONDITIONAL(TIZEN_HEALTH_PLUGIN, test "${enable_health}" = "yes")
+
+AC_ARG_ENABLE(proximity, AC_HELP_STRING([--enable-proximity],
+ [Enable PROXIMITY Plugin]), [enable_proximity=${enableval}])
+AM_CONDITIONAL(TIZEN_PROXIMITY_PLUGIN, test "${enable_proximity}" = "yes")
+
+AC_ARG_ENABLE(tds, AC_HELP_STRING([--enable-tds],
+ [Enable TDS Plugin]), [enable_tds=${enableval}])
+AM_CONDITIONAL(TIZEN_TDS_PLUGIN, test "${enable_tds}" = "yes")
+
+AC_ARG_ENABLE(tizenunusedplugin, AC_HELP_STRING([--enable-tizenunusedplugin],
+ [Enable Unused Plugin]), [enable_tizenunusedplugin=${enableval}])
+AM_CONDITIONAL(TIZEN_UNUSED_PLUGIN, test "${enable_tizenunusedplugin}" = "yes")
+
+AC_ARG_ENABLE(sap, AC_HELP_STRING([--enable-sap],
+ [Enable SAP Plugin]), [enable_sap=${enableval}])
+AM_CONDITIONAL(TIZEN_SAP_PLUGIN, test "${enable_sap}" = "yes")
+
+# End of TIZEN_FEATURE_BLUEZ_MODIFY
+
AC_ARG_ENABLE(deprecated, AC_HELP_STRING([--enable-deprecated],
[enable deprecated plugins (BLE services, ...)]),
[enable_deprecated=${enableval}])
diff --git a/doc/adapter-api.txt b/doc/adapter-api.txt
index 08de6cd8..e37781eb 100755
--- a/doc/adapter-api.txt
+++ b/doc/adapter-api.txt
@@ -109,6 +109,159 @@ Methods void StartDiscovery()
org.bluez.Error.NotSupported
org.bluez.Error.Failed
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ void StartCustomDiscovery(string pattern)
+
+ This method starts the device discovery session with
+ parameter. The valid paramter strings are "BREDR",
+ "LE" or "LE_BREDR" which will perform the inquiry for
+ appropriate types. This includes an inquiry procedure
+ and remote device name resolving. Use StopDiscovery
+ to release the sessions acquired.
+
+ This process will start creating Device objects as
+ new devices are discovered.
+
+ Possible errors: org.bluez.Error.NotReady
+ org.bluez.Error.Failed
+
+ void StartLEDiscovery()
+
+ This method starts the LE device discovery session.
+ General discovery and active scan is default.
+ Use StopLEDiscovery to release the sessions
+ acquired.
+
+ This process will start emitting DeviceFound and
+ PropertyChanged "LEDiscovering" signals.
+
+ Possible errors: org.bluez.Error.NotReady
+ org.bluez.Error.Failed
+
+ void StopLEDiscovery()
+
+ This method will cancel any previous StartLEDiscovery
+ transaction.
+
+ Note that a discovery procedure is shared between all
+ discovery sessions thus calling StopLEDiscovery will only
+ release a single session.
+
+ Possible errors: org.bluez.Error.NotReady
+ org.bluez.Error.Failed
+ org.bluez.Error.NotAuthorized
+
+ void SetAdvertising(boolean enable)
+
+ This method is used to set LE advertising on a
+ controller that supports it.
+
+ This process will emit PropertyChanged "Advertising"
+ signal.
+
+ Possible errors: org.bluez.Error.NotReady
+ org.bluez.Error.InvalidArguments
+ org.bluez.Error.Failed
+
+ void SetAdvertisingParameters(uint32 interval_min,
+ uint32 interval_max, uint32 filter_policy,
+ uint32 type)
+
+ This method allows for setting the Low Energy
+ advertising interval and advertising filter policy.
+ It is only supported on controller with LE support.
+
+ Possible errors: org.bluez.Error.NotReady
+ org.bluez.Error.Failed
+
+ void SetAdvertisingData(array{byte} value)
+
+ This method is used to set LE advertising data on a
+ controller that supports it.
+
+ Possible errors: org.bluez.Error.NotReady
+ org.bluez.Error.InvalidArguments
+ org.bluez.Error.Failed
+
+ void SetScanParameters(uint32 type, uint32 interval, uint32 window)
+
+ This method allows for setting the Low Energy
+ scan interval and window.
+ It is only supported on controller with LE support.
+
+ Possible errors: org.bluez.Error.NotReady
+ org.bluez.Error.Failed
+
+ void SetScanRespData(array{byte} value)
+
+ This method is used to set LE scan response data on
+ a controller that supports it.
+
+ Possible errors: org.bluez.Error.NotReady
+ org.bluez.Error.InvalidArguments
+ org.bluez.Error.Failed
+
+ void AddDeviceWhiteList(string address, uint32 address_type)
+
+ This method is used to add LE device to White List for given
+ address.
+
+ Possible errors: org.bluez.Error.NotReady
+ org.bluez.Error.InvalidArguments
+ org.bluez.Error.Failed
+
+ void RemoveDeviceWhiteList(string address, uint32 address_type)
+
+ This method is used to remove LE device to White List for given
+ address.
+
+ Possible errors: org.bluez.Error.NotReady
+ org.bluez.Error.InvalidArguments
+ org.bluez.Error.Failed
+
+ void ClearDeviceWhiteList()
+
+ This method is used to clear LE device to White List
+
+ Possible errors: org.bluez.Error.NotReady
+ org.bluez.Error.Failed
+
+ void SetLePrivacy(boolean enable_privacy)
+
+ This method is used to set/reset LE privacy feature for the local
+ adapter when it supports the feature.
+
+ Possible errors: org.bluez.Error.NotReady
+ org.bluez.Error.InvalidArguments
+ org.bluez.Error.Failed
+
+ void SetLeStaticRandomAddress(boolean enable_random_address)
+
+ This method is used to set/reset LE static random address for the local
+ adapter when it supports the feature.
+
+ Possible errors: org.bluez.Error.NotReady
+ org.bluez.Error.InvalidArguments
+ org.bluez.Error.Failed
+
+ void SetManufacturerData(array{byte} value)
+
+ This method is used to set Manufacturer data on a
+ controller that supports it.
+
+ Possible errors: org.bluez.Error.NotReady
+ org.bluez.Error.InvalidArguments
+ org.bluez.Error.Failed
+
+ void CreateDevice(string address)
+
+ Creates a new object path for a remote device. This
+ method will connect to the remote device and retrieve
+ all SDP records.
+
+ Possible errors: org.bluez.Error.InvalidArguments
+#endif
+
Properties string Address [readonly]
The Bluetooth device address.
@@ -219,3 +372,12 @@ Properties string Address [readonly]
Local Device ID information in modalias format
used by the kernel and udev.
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ boolean LEDiscovering [readonly]
+
+ Indicates that a device LE discovery procedure is active.
+
+ string Version [readonly]
+
+ The Bluetooth version.
+#endif
diff --git a/doc/coding-style.txt b/doc/coding-style.txt
new file mode 100755
index 00000000..f0bf880e
--- /dev/null
+++ b/doc/coding-style.txt
@@ -0,0 +1,279 @@
+BlueZ coding style
+******************
+
+Every project has its coding style, and BlueZ is not an exception. This
+document describes the preferred coding style for BlueZ code, in order to keep
+some level of consistency among developers so that code can be easily
+understood and maintained.
+
+First of all, BlueZ coding style must follow every rule for Linux kernel
+(http://www.kernel.org/doc/Documentation/CodingStyle). There also exists a tool
+named checkpatch.pl to help you check the compliance with it. Just type
+"checkpatch.pl --no-tree patch_name" to check your patch. In theory, you need
+to clean up all the warnings and errors except this one: "ERROR: Missing
+Signed-off-by: line(s)". BlueZ does not used Signed-Off lines, so including
+them is actually an error. In certain circumstances one can ignore the 80
+character per line limit. This is generally only allowed if the alternative
+would make the code even less readable.
+
+Besides the kernel coding style above, BlueZ has special flavors for its own.
+Some of them are mandatory (marked as 'M'), while some others are optional
+(marked as 'O'), but generally preferred.
+
+M1: Blank line before and after an if/while/do/for statement
+============================================================
+
+There should be a blank line before if statement unless the if is nested and
+not preceded by an expression or variable declaration.
+
+Example:
+1)
+a = 1;
+if (b) { // wrong
+
+2)
+a = 1
+
+if (b) {
+}
+a = 2; // wrong
+
+3)
+if (a) {
+ if (b) // correct
+
+4)
+b = 2;
+
+if (a) { // correct
+
+}
+
+b = 3;
+
+The only exception to this rule applies when a variable is being checked for
+errors as such:
+
+err = stat(filename, &st);
+if (err || !S_ISDIR(st.st_mode))
+ return;
+
+M2: Multiple line comment
+=========================
+
+If your comment has more than one line, please start it from the second line.
+
+Example:
+/*
+ * first line comment // correct
+ * ...
+ * last line comment
+ */
+
+
+M3: Space before and after operator
+===================================
+
+There should be a space before and after each operator.
+
+Example:
+a + b; // correct
+
+
+M4: Wrap long lines
+===================
+
+If your condition in if, while, for statement or a function declaration is too
+long to fit in one line, the new line needs to be indented not aligned with the
+body.
+
+Example:
+1)
+if ((adapter->supported_settings & MGMT_SETTING_SSP) &&
+ !(adapter->current_settings & MGMT_SETTING_SSP)) // wrong
+
+2)
+if ((adapter->supported_settings & MGMT_SETTING_SSP) &&
+ !(adapter->current_settings & MGMT_SETTING_SSP))
+
+3)
+void btd_adapter_register_pin_cb(struct btd_adapter *adapter,
+ btd_adapter_pin_cb_t cb) // wrong
+
+4)
+void btd_adapter_register_pin_cb(struct btd_adapter *adapter,
+ btd_adapter_pin_cb_t cb)
+
+The referred style for line wrapping is to indent as far as possible to the
+right without hitting the 80 columns limit.
+
+M5: Space when doing type casting
+=================================
+
+There should be a space between new type and variable.
+
+Example:
+1)
+a = (int *)b; // wrong
+2)
+a = (int *) b; // correct
+
+
+M6: Don't initialize variable unnecessarily
+===========================================
+
+When declaring a variable, try not to initialize it unless necessary.
+
+Example:
+int i = 1; // wrong
+
+for (i = 0; i < 3; i++) {
+}
+
+M7: Follow the order of include header elements
+===============================================
+
+When writing an include header the various elements should be in the following
+order:
+ - #includes
+ - forward declarations
+ - #defines
+ - enums
+ - typedefs
+ - function declarations and inline function definitions
+
+M8: Internal headers must not use include guards
+================================================
+
+Any time when creating a new header file with non-public API, that header
+must not contain include guards.
+
+M9: Naming of enums
+===================
+
+Enums must have a descriptive name. The enum type should be small caps and
+it should not be typedef-ed. Enum contents should be in CAPITAL letters and
+prefixed by the enum type name.
+
+Example:
+
+enum animal_type {
+ ANIMAL_TYPE_FOUR_LEGS,
+ ANIMAL_TYPE_EIGHT_LEGS,
+ ANIMAL_TYPE_TWO_LEGS,
+};
+
+If the enum contents have values (e.g. from specification) the formatting
+should be as follows:
+
+enum animal_type {
+ ANIMAL_TYPE_FOUR_LEGS = 4,
+ ANIMAL_TYPE_EIGHT_LEGS = 8,
+ ANIMAL_TYPE_TWO_LEGS = 2,
+};
+
+M10: Enum as switch variable
+============================
+
+If the variable of a switch is an enum, you must include all values in
+switch body even if providing default. This is enforced by compiler option
+enabling extra warning in such case. The reason for this is to ensure that if
+later on enum is modified and one forget to change the switch accordingly, the
+compiler will complain the new added type hasn't been handled.
+
+Example:
+
+enum animal_type {
+ ANIMAL_TYPE_FOUR_LEGS = 4,
+ ANIMAL_TYPE_EIGHT_LEGS = 8,
+ ANIMAL_TYPE_TWO_LEGS = 2,
+};
+
+enum animal_type t;
+
+switch (t) { // OK
+case ANIMAL_TYPE_FOUR_LEGS:
+ ...
+ break;
+case ANIMAL_TYPE_EIGHT_LEGS:
+ ...
+ break;
+case ANIMAL_TYPE_TWO_LEGS:
+ ...
+ break;
+default:
+ break;
+}
+
+switch (t) { // Wrong
+case ANIMAL_TYPE_FOUR_LEGS:
+ ...
+ break;
+case ANIMAL_TYPE_TWO_LEGS:
+ ...
+ break;
+default:
+ break;
+}
+
+However if the enum comes from an external header file outside BlueZ, such as
+Android headers, we cannot make any assumption of how the enum is defined and
+this rule might not apply.
+
+M11: Always use parenthesis with sizeof
+=======================================
+
+The expression argument to the sizeof operator should always be in
+parenthesis, too.
+
+Example:
+1)
+memset(stuff, 0, sizeof(*stuff));
+
+2)
+memset(stuff, 0, sizeof *stuff); // Wrong
+
+M12: Use void if function has no parameters
+===========================================
+
+A function with no parameters must use void in the parameter list.
+
+Example:
+1)
+void foo(void)
+{
+}
+
+2)
+void foo() // Wrong
+{
+}
+
+O1: Try to avoid complex if body
+================================
+
+It's better not to have a complicated statement for if. You may judge its
+contrary condition and return | break | continue | goto ASAP.
+
+Example:
+1)
+if (device) { // worse
+ memset(&eir_data, 0, sizeof(eir_data));
+ if (eir_len > 0)
+ eir_parse(&eir_data, ev->eir, eir_len);
+ ...
+} else {
+ error("Unable to get device object for %s", addr);
+ return;
+}
+
+2)
+if (!device) {
+ error("Unable to get device object for %s", addr);
+ return;
+}
+
+memset(&eir_data, 0, sizeof(eir_data));
+if (eir_len > 0)
+ eir_parse(&eir_data, ev->eir, eir_len);
+...
diff --git a/doc/device-api.txt b/doc/device-api.txt
index 13b28818..d03906c4 100755
--- a/doc/device-api.txt
+++ b/doc/device-api.txt
@@ -81,7 +81,11 @@ Methods void Connect()
org.bluez.Error.InvalidArguments
org.bluez.Error.NotSupported
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ void Pair(uint8 conn_type)
+#else
void Pair()
+#endif
This method will connect to the remote device,
initiate pairing and then retrieve all SDP records
@@ -99,6 +103,14 @@ Methods void Connect()
In case there is no application agent and also
no default agent present, this method will fail.
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ conn_type represents the type of the connection to be
+ used for pairing.
+ 0xFF -> BR/EDR and LE
+ 0x00 -> BR/EDR only
+ 0x01 -> LE only
+#endif
+
Possible errors: org.bluez.Error.InvalidArguments
org.bluez.Error.Failed
org.bluez.Error.AlreadyExists
@@ -116,6 +128,29 @@ Methods void Connect()
Possible errors: org.bluez.Error.DoesNotExist
org.bluez.Error.Failed
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ void ConnectLe()
+
+ This is a generic method to connect GATT of
+ the remote device supporting LE and have been
+ flagged as auto-connectable on our side.
+
+ Possible errors: org.bluez.Error.NotReady
+ org.bluez.Error.Failed
+ org.bluez.Error.InProgress
+ org.bluez.Error.AlreadyConnected
+
+
+ void DisconnectLe()
+
+ This method disconnects a specific remote device LE
+ connection by terminating the low-level ACL connection.
+ The use of this method should be restricted to administrator
+ use.
+
+ Possible errors: org.bluez.Error.NotConnected
+#endif
+
Properties string Address [readonly]
The Bluetooth device address of the remote device.
@@ -154,7 +189,11 @@ Properties string Address [readonly]
Indicates if the remote device is paired.
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ byte Connected [readonly]
+#else
boolean Connected [readonly]
+#endif
Indicates if the remote device is currently connected.
A PropertiesChanged signal indicate changes to this
@@ -234,3 +273,11 @@ Properties string Address [readonly]
array{byte} AdvertisingFlags [readonly, experimental]
The Advertising Data Flags of the remote device.
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ boolean GattConnected [readonly]
+
+ Indicates if the remote LE device is currently connected.
+ A PropertiesChanged signal indicate changes to this
+ status.
+#endif \ No newline at end of file
diff --git a/doc/gatt-api.txt b/doc/gatt-api.txt
index 6c98b871..b29f5993 100755
--- a/doc/gatt-api.txt
+++ b/doc/gatt-api.txt
@@ -45,6 +45,16 @@ Properties string UUID [read-only]
belongs to. Only present on services from remote
devices.
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ array{object} Characteristics [read-only]
+
+ Array of object paths representing the characteristics
+ of this service. This property is set only when the
+ characteristic discovery has been completed, however the
+ characteristic objects will become available via
+ ObjectManager as soon as they get discovered.
+#endif
+
array{object} Includes [read-only]: Not implemented
Array of object paths representing the included
@@ -153,6 +163,22 @@ Properties string UUID [read-only]
"secure-read" (Server only)
"secure-write" (Server only)
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ array{object} Descriptors [read-only]
+
+ Array of object paths representing the descriptors
+ of this service. This property is set only when the
+ descriptor discovery has been completed, however the
+ descriptor objects will become available via
+ ObjectManager as soon as they get discovered.
+
+ 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
====================================
@@ -342,3 +368,21 @@ Methods void RegisterApplication(object application, dict options)
Possible errors: org.bluez.Error.InvalidArguments
org.bluez.Error.DoesNotExist
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ GetService(string uuid)
+
+ This Reads the service, characterstics and descriptors
+ that has been previously registered. The string uuid parameter
+ must match the same value that has been used
+ on registration.
+
+ The return values includes,
+ Key objectpath
+ ---- -----------
+ Service /serviceX
+ CharacteristicsX /serviceX/CharacterisitcX
+ DescriptorX /serviceX/CharacterisitcX/DescriptorX
+
+ Possible errors: org.bluez.Error.InvalidArguments
+#endif
diff --git a/doc/maintainer-guidelines.txt b/doc/maintainer-guidelines.txt
new file mode 100755
index 00000000..21162d4e
--- /dev/null
+++ b/doc/maintainer-guidelines.txt
@@ -0,0 +1,114 @@
+Maintainer guidelines
+*********************
+
+This document is intended for the maintainers of the BlueZ project. It
+serves as basic guidelines for handling patch review and commit access.
+
+
+Rule 1: Keep the GIT tree clean and linear
+==========================================
+
+The bluetooth.git, bluetooth-next.git and bluez.git trees are not your
+private playground. The history is meant to be clean and linear.
+
+ - NO merges
+ - NO branches
+ - NO tags
+
+If anyone needs testing or work on a feature, clone the tree and do
+it in your own copy. The master trees are off limits.
+
+One advise to avoid any accidental errors in this area to set proper
+options in global ~/.gitconfig or local .git/config files.
+
+ [merge]
+ ff = only
+
+Violations of this rule are not acceptable. This rule is enforced. If
+in doubt ask one of the seasoned maintainers.
+
+
+Rule 2: Enforce clean commit messages
+=====================================
+
+The commit messages are required to be clean and follow style guidelines
+to be consistent.
+
+Commit messages should adhere to a 72 characters by line limit. That
+makes it easy to read them via git log in a terminal window. Exceptions
+to this rule are logs, trace or other verbatim copied information.
+
+Every commit requires full names and email addresses. No synonyms or
+nicknames are allowed. It is also important that the Outlook style
+names with lastname, firstname are not allowed. It is the maintainers
+job to ensure we get proper firstname lastname <email> authorship.
+
+It is also important that the committer itself uses a valid name and
+email address when committing patches. So ensure that either the
+global ~/.gitconfig or local .git/config provides proper values.
+
+ [user]
+ name = Peter Mustermann
+ email = peter@mustermann.de
+
+Commit messages for bluez.git shall not contain Signed-off-by
+signatures. They are not used in userspace and with that it is the
+maintainers job to ensure they do not get committed to the repository.
+
+For bluetooth.git and bluetooth-next.git The Signed-off-by process is
+used and the signatures are required.
+
+Tags like Change-Id generated from Gerrit are never acceptable. It is
+the maintainers job to ensure that these are not committed into the
+repositories.
+
+Violations of this rule create a mess in the tree that can not be
+reversed. If in doubt ask one of the seasoned maintainers.
+
+
+Rule 3: Enforce correct coding style
+====================================
+
+The coding style follows roughly the kernel coding style with any
+exceptions documented in doc/coding-style.txt.
+
+To ensure trivial white-space errors don't get committed, have the
+following in your .gitconfig:
+
+ [apply]
+ whitespace = error
+
+It can also be helpful to use the checkpatch.pl script coming with the
+Linux kernel to do some automated checking. Adding the following to your
+.git/hooks/pre-commit and .git/hooks/pre-applypatch is a simple way to
+do this:
+
+ exec git diff --cached | ~/src/linux/scripts/checkpatch.pl -q \
+ --no-tree --no-signoff --show-types \
+ --ignore CAMELCASE,NEW_TYPEDEFS,INITIALISED_STATIC -
+
+The above assumes that a kernel tree resides in ~/src/linux/.
+
+
+Rule 4: Pay extra attention to adding new files to the tree
+===========================================================
+
+New files that are added to the tree require several things to be
+verified first:
+
+ - Check that the names are acceptible with other maintainers
+ - Ensure that the file modes are correct
+ - Verify that the license & copyright headers are correct
+ - If the file is supposed to be part of the release tarball,
+ make sure that it gets picked up by 'make dist' (particularly
+ important for documentation or other files that are not code)
+
+
+Rule 5: Keep the mailing list in sync with the commit process
+=============================================================
+
+When applying patches, be sure to send a response to the mailing list as
+soon as the code has been pushed to the upstream tree. Usually this
+means one email per patch, however patch-sets may only have one response
+covering the entire set. If applying a subset of a patch-set clearly
+state what was applied in your response.
diff --git a/doc/mgmt-api.txt b/doc/mgmt-api.txt
index 34270dd2..66907527 100755
--- a/doc/mgmt-api.txt
+++ b/doc/mgmt-api.txt
@@ -1907,6 +1907,68 @@ Load Identity Resolving Keys Command
Possible errors: Invalid Parameters
Invalid Index
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+Generate Identity Resolving Key Command
+=======================================
+
+ Command Code: 0x00F6
+ Controller Index: <controller id>
+ Command Parameters:
+ Return Parameters:
+
+ This command is used to generate identity resolving key for the
+ local device, which will be used at the time of pairing for key
+ distribution and to generate resolvable private address for local device.
+
+ This command generates a Command Complete event on success
+ or a Command Status event on failure.
+
+ Possible errors: Failed
+ Invalid Index
+
+Generate Resolvable Private Address Command
+=======================================
+
+ Command Code: 0x00F5
+ Controller Index: <controller id>
+ Command Parameters:
+ Return Parameters: Resolvable Private Address (6 Octets)
+
+ This command is used to generate resolvable private address for the
+ local device when LE privacy is supported and device is having its IRK.
+
+ This command returns generated private address which is resolvable
+ by remote device on success.
+
+ This command generates a Command Complete event on success
+ or a Command Status event on failure.
+
+ Possible errors: Failed
+ Invalid Index
+
+Set Random Address Command
+==========================
+
+ Command Code: 0x00F4
+ Controller Index: <controller id>
+ Command Parameters: Resolvable Private Address (6 Octets)
+ Return Parameters:
+
+ This command is used to set the random address to local controller.
+ If local device supports LE Privacy then this command will be called to set
+ its Random address while active scanning remote devices and in the case of
+ advertising itself.
+
+ This command intern calls the HCI Set Random address command from kernel
+ space to set the RPA to controller.
+
+ This command generates a Command Complete event on success
+ or a Command Status event on failure.
+
+ Possible errors: Failed
+ Invalid Index
+
+#endif
Get Connection Information Command
==================================
diff --git a/doc/network-api.txt b/doc/network-api.txt
index 109da28b..0fb633c5 100755
--- a/doc/network-api.txt
+++ b/doc/network-api.txt
@@ -74,3 +74,17 @@ Methods void Register(string uuid, string bridge)
All servers will be automatically unregistered when
the calling application terminates.
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ void Disconnect(string address)
+
+ Disconnect the device from the network device.
+
+ Possible errors: org.bluez.Error.Failed
+ org.bluez.Error.NotConnected
+
+ dict GetProperties(string address)
+
+ Returns all properties of the specified device.
+ See the properties section for available properties.
+#endif
diff --git a/doc/oob-api.txt b/doc/oob-api.txt
new file mode 100755
index 00000000..d8387123
--- /dev/null
+++ b/doc/oob-api.txt
@@ -0,0 +1,38 @@
+BlueZ D-Bus Out Of Band Pairing API description
+===============================================
+
+Copyright (C) 2011 Szymon Janc <szymon.janc@tieto.com> for ST-Ericsson
+
+Service org.bluez
+Interface org.bluez.OutOfBand
+Object path [variable prefix]/{hci0,hci1,...}
+
+Methods array{byte} hash, array{byte} randomizer ReadLocalData()
+
+ This method reads local OOB data from adapter. Return
+ value is pair of arrays 16 bytes each.
+
+ Note: This method will generate and return new local
+ OOB data.
+
+ Possible errors: org.bluez.Error.Failed
+ org.bluez.Error.InProgress
+
+ void AddRemoteData(string address, array{byte} hash,
+ array{byte} randomizer)
+
+ This method adds new Out Of Band data for
+ specified address. If data for specified address
+ already exists it will be overwritten with new one.
+
+ Possible errors: org.bluez.Error.Failed
+ org.bluez.Error.InvalidArguments
+
+ void RemoveRemoteData(string address)
+
+ This method removes Out Of Band data for specified
+ address. If data for specified address does not exist
+ nothing is removed.
+
+ Possible errors: org.bluez.Error.Failed
+ org.bluez.Error.InvalidArguments
diff --git a/doc/profile-api.txt b/doc/profile-api.txt
index ec18034a..60eee2b8 100755
--- a/doc/profile-api.txt
+++ b/doc/profile-api.txt
@@ -91,7 +91,100 @@ Object path /org/bluez
Possible errors: org.bluez.Error.InvalidArguments
org.bluez.Error.AlreadyExists
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ void RegisterProfile2(object profile, string uuid, string destination,
+ string path,dict options)
+ This registers a profile implementation.
+
+ If an application disconects/exits, its registered profile
+ will not be removed and bluez launches an application through
+ dbus activation when profile is connected.
+
+ HFP HS UUID: 0000111e-0000-1000-8000-00805f9b34fb
+
+ Default RFCOMM channel is 6. And this requires
+ authentication.
+
+ string Destination
+
+ Application bus name
+
+ string Path
+
+ Applicatoin path name
+
+ Available options:
+
+ string Name
+
+ Human readable name for the profile
+
+ string Service
+
+ The primary service class UUID
+ (if different from the actual
+ profile UUID)
+
+ string Role
+
+ For asymmetric profiles that do not
+ have UUIDs available to uniquely
+ identify each side this
+ parameter allows specifying the
+ precise local role.
+
+ Possible values: "client", "server"
+
+ uint16 Channel
+
+ RFCOMM channel number that is used
+ for client and server UUIDs.
+
+ If applicable it will be used in the
+ SDP record as well.
+
+ uint16 PSM
+
+ PSM number that is used for client
+ and server UUIDs.
+
+ If applicable it will be used in the
+ SDP record as well.
+
+ boolean RequireAuthentication
+
+ Pairing is required before connections
+ will be established. No devices will
+ be connected if not paired.
+
+ boolean RequireAuthorization
+
+ Request authorization before any
+ connection will be established.
+
+ boolean AutoConnect
+
+ In case of a client UUID this will
+ force connection of the RFCOMM or
+ L2CAP channels when a remote device
+ is connected.
+
+ string ServiceRecord
+
+ Provide a manual SDP record.
+
+ uint16 Version
+
+ Profile version (for SDP record)
+
+ uint16 Features
+
+ Profile features (for SDP record)
+
+ Possible errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.AlreadyExists
+#endif
void UnregisterProfile(object profile)
This unregisters the profile that has been previously
diff --git a/doc/settings-storage.txt b/doc/settings-storage.txt
index 6a708b45..bbe525a5 100755
--- a/doc/settings-storage.txt
+++ b/doc/settings-storage.txt
@@ -81,13 +81,23 @@ Settings file contains one [General] group with adapter info like:
0 = disable timer, i.e. stay
discoverable forever
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ LocalIrk String Identity resolving key for local
+ adapter. This key value is used to
+ generate the RPA to suport privacy feature.
+ If value is NULL, i.e IRK to be generated
+ for this adapter.
+#endif
+
Sample:
[General]
Name=My PC
Discoverable=false
Pairable=true
DiscoverableTimeout=0
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ LocalIrk=""
+#endif
Identity file format
====================
@@ -294,3 +304,13 @@ Long term key) related to a remote device.
Counter Integer Signing counter
Authenticated Boolean True if the key is authenticated
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+[IdentityResolvingKey] group contains:
+
+ Key String Identity Resolving key in hexadecimal format
+
+ IdentityAddress String Identity Address of the device
+
+ IdentityAddressType String Type of Identity Address of the device
+#endif
diff --git a/gdbus/gdbus.h b/gdbus/gdbus.h
index e37385fa..e40dd44b 100755
--- a/gdbus/gdbus.h
+++ b/gdbus/gdbus.h
@@ -277,6 +277,12 @@ gboolean g_dbus_emit_signal_valist(DBusConnection *connection,
const char *path, const char *interface,
const char *name, int type, va_list args);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+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 afb45876..bd800606 100755
--- a/gdbus/object.c
+++ b/gdbus/object.c
@@ -33,7 +33,24 @@
#include "gdbus.h"
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#if 0
+#include <syslog.h>
+static void gdbus_dbg(const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+
+ vsyslog(LOG_DEBUG, format, ap);
+
+ va_end(ap);
+}
+#endif
+#else
#define info(fmt...)
+#endif
+
#define error(fmt...)
#define debug(fmt...)
@@ -88,6 +105,12 @@ static int global_flags = 0;
static struct generic_data *root;
static GSList *pending = NULL;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define ADAPTER_INTERFACE "org.bluez.Adapter1"
+
+static char *adapter_path = NULL;
+#endif
+
static gboolean process_changes(gpointer user_data);
static void process_properties_from_interface(struct generic_data *data,
struct interface_data *iface);
@@ -663,6 +686,13 @@ static gboolean remove_interface(struct generic_data *data, const char *name)
data->interfaces = g_slist_remove(data->interfaces, iface);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (g_strcmp0(iface->name, ADAPTER_INTERFACE) == 0) {
+ g_free(adapter_path);
+ adapter_path = NULL;
+ }
+#endif
+
if (iface->destroy) {
iface->destroy(iface->user_data);
iface->user_data = NULL;
@@ -1075,7 +1105,12 @@ static DBusHandlerResult generic_message(DBusConnection *connection,
if (check_privilege(connection, message, method,
iface->user_data) == TRUE)
return DBUS_HANDLER_RESULT_HANDLED;
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#if 0
+ gdbus_dbg("%s: %s.%s()", dbus_message_get_path(message),
+ iface->name, method->name);
+#endif
+#endif
return process_message(connection, message, method,
iface->user_data);
}
@@ -1165,9 +1200,36 @@ static DBusMessage *get_objects(DBusConnection *connection,
return reply;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static DBusMessage *default_adapter(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ DBusMessage *reply;
+
+ if (!adapter_path)
+ return g_dbus_create_error(msg,
+ "org.bluez.Error" ".NoSuchAdapter",
+ "No such adapter");
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &adapter_path,
+ DBUS_TYPE_INVALID);
+
+ return reply;
+}
+#endif
+
static const GDBusMethodTable manager_methods[] = {
{ GDBUS_METHOD("GetManagedObjects", NULL,
GDBUS_ARGS({ "objects", "a{oa{sa{sv}}}" }), get_objects) },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ { GDBUS_METHOD("DefaultAdapter",
+ NULL, GDBUS_ARGS({ "adapter", "o" }),
+ default_adapter) },
+#endif
{ }
};
@@ -1355,6 +1417,11 @@ gboolean g_dbus_register_interface(DBusConnection *connection,
return FALSE;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (g_strcmp0(name, ADAPTER_INTERFACE) == 0)
+ adapter_path = g_strdup(path);
+#endif
+
if (!add_interface(data, name, methods, signals, properties, user_data,
destroy)) {
object_path_unref(connection, path);
@@ -1615,6 +1682,65 @@ gboolean g_dbus_emit_signal(DBusConnection *connection,
return result;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+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/gobex/gobex-apparam.c b/gobex/gobex-apparam.c
index 43281720..6f7a952d 100755
--- a/gobex/gobex-apparam.c
+++ b/gobex/gobex-apparam.c
@@ -104,8 +104,10 @@ GObexApparam *g_obex_apparam_decode(const void *data, gsize size)
GHashTable *tags;
gsize count = 0;
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
if (size < 2)
return NULL;
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
apparam = g_obex_apparam_new();
@@ -166,6 +168,13 @@ gssize g_obex_apparam_encode(GObexApparam *apparam, void *buf, gsize len)
return count;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void g_obex_apparam_remove_all(GObexApparam *apparam)
+{
+ g_hash_table_remove_all(apparam->tags);
+}
+#endif
+
GObexApparam *g_obex_apparam_set_bytes(GObexApparam *apparam, guint8 id,
const void *value, gsize len)
{
diff --git a/gobex/gobex-apparam.h b/gobex/gobex-apparam.h
index 6c086092..701fd431 100755
--- a/gobex/gobex-apparam.h
+++ b/gobex/gobex-apparam.h
@@ -30,6 +30,10 @@ typedef struct _GObexApparam GObexApparam;
GObexApparam *g_obex_apparam_decode(const void *data, gsize size);
gssize g_obex_apparam_encode(GObexApparam *apparam, void *buf, gsize size);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void g_obex_apparam_remove_all(GObexApparam *apparam);
+#endif
+
GObexApparam *g_obex_apparam_set_bytes(GObexApparam *apparam, guint8 id,
const void *value, gsize size);
GObexApparam *g_obex_apparam_set_uint8(GObexApparam *apparam, guint8 id,
diff --git a/gobex/gobex-header.c b/gobex/gobex-header.c
index c594999a..c6634c7b 100755
--- a/gobex/gobex-header.c
+++ b/gobex/gobex-header.c
@@ -389,6 +389,11 @@ GObexHeader *g_obex_header_new_unicode(guint8 id, const char *str)
GObexHeader *header;
gsize len;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ gunichar2 *utf16;
+ glong utf16_len;
+#endif
+
g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x", G_OBEX_HDR_ENC(id));
if (G_OBEX_HDR_ENC(id) != G_OBEX_HDR_ENC_UNICODE)
@@ -401,9 +406,15 @@ GObexHeader *g_obex_header_new_unicode(guint8 id, const char *str)
len = g_utf8_strlen(str, -1);
header->vlen = len;
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
header->hlen = len == 0 ? 3 : 3 + ((len + 1) * 2);
header->v.string = g_strdup(str);
-
+#else
+ header->v.string = g_strdup(str);
+ utf16_len = utf8_to_utf16(&utf16, header->v.string);
+ header->hlen = len == 0 ? 3 : 3 + utf16_len;
+ g_free(utf16);
+#endif
g_obex_debug(G_OBEX_DEBUG_HEADER, "%s", header->v.string);
return header;
diff --git a/gobex/gobex.c b/gobex/gobex.c
index 42175fcc..61ddc66d 100755
--- a/gobex/gobex.c
+++ b/gobex/gobex.c
@@ -571,7 +571,12 @@ static guint8 *digest_response(const guint8 *nonce)
return result;
g_checksum_update(md5, nonce, NONCE_LEN);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ g_checksum_update(md5, (guint8 *) ":0000", 5);
+#else
g_checksum_update(md5, (guint8 *) ":BlueZ", 6);
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
+
size = NONCE_LEN;
g_checksum_get_digest(md5, result, &size);
diff --git a/lib/bluetooth.h b/lib/bluetooth.h
index eb279260..f70ff09d 100755
--- a/lib/bluetooth.h
+++ b/lib/bluetooth.h
@@ -120,6 +120,16 @@ struct bt_voice {
#define BT_SNDMTU 12
#define BT_RCVMTU 13
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define BT_LE_CONN_PARAM 14
+struct le_conn_param {
+ uint16_t min;
+ uint16_t max;
+ uint16_t latency;
+ uint16_t to_multiplier;
+};
+#endif
+
#define BT_VOICE_TRANSPARENT 0x0003
#define BT_VOICE_CVSD_16BIT 0x0060
diff --git a/lib/hci.c b/lib/hci.c
index a3f5d969..4dd92b84 100755
--- a/lib/hci.c
+++ b/lib/hci.c
@@ -160,8 +160,13 @@ char *hci_bustostr(int bus)
return "SPI";
case HCI_I2C:
return "I2C";
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ case HCI_SMD:
+ return "QC_SMD";
+#else
case HCI_SMD:
return "SMD";
+#endif
default:
return "Unknown";
}
@@ -594,7 +599,12 @@ static hci_map commands_map[] = {
{ "LE Receiver Test", 228 },
{ "LE Transmitter Test", 229 },
{ "LE Test End", 230 },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ { "LE Read Maximum Data Length", 231 },
+ { "Reserved", 232 },
+#else
{ "Reserved", 231 },
+#endif
{ NULL }
};
@@ -1086,6 +1096,38 @@ int hci_close_dev(int dd)
/* HCI functions that require open device
* dd - Device descriptor returned by hci_open_dev. */
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+int hci_send_data(int dd, uint16_t handle, uint8_t len, void *data)
+{
+ uint8_t type = HCI_ACLDATA_PKT;
+ hci_acl_hdr ac;
+ struct iovec iv[3];
+ int ivn;
+
+ ac.handle = htobs(handle);
+ ac.dlen= htobs(len);
+
+ iv[0].iov_base = &type;
+ iv[0].iov_len = 1;
+ iv[1].iov_base = &ac;
+ iv[1].iov_len = HCI_ACL_HDR_SIZE;
+ ivn = 2;
+
+ if (len) {
+ iv[2].iov_base = data;
+ iv[2].iov_len = len;
+ ivn = 3;
+ }
+
+ while (writev(dd, iv, ivn) < 0) {
+ if (errno == EAGAIN || errno == EINTR)
+ continue;
+ return -1;
+ }
+ return 0;
+}
+#endif
+
int hci_send_cmd(int dd, uint16_t ogf, uint16_t ocf, uint8_t plen, void *param)
{
uint8_t type = HCI_COMMAND_PKT;
@@ -1604,7 +1646,11 @@ int hci_write_local_name(int dd, const char *name, int to)
struct hci_request rq;
memset(&cp, 0, sizeof(cp));
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ strncpy((char *) cp.name, name, sizeof(cp.name) - 1);
+#else
strncpy((char *) cp.name, name, sizeof(cp.name));
+#endif
memset(&rq, 0, sizeof(rq));
rq.ogf = OGF_HOST_CTL;
@@ -3119,3 +3165,132 @@ int hci_le_read_remote_features(int dd, uint16_t handle, uint8_t *features, int
return 0;
}
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+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)
+{
+ le_read_maximum_data_length_rp rp;
+ struct hci_request rq;
+
+ memset(&rq, 0, sizeof(rq));
+ memset(&rp, 0, sizeof(rp));
+
+ rq.ogf = OGF_LE_CTL;
+ rq.ocf = OCF_LE_READ_MAXIMUM_DATA_LENGTH;
+ rq.rparam = &rp;
+ rq.rlen = LE_READ_MAXIMUM_DATA_LENGTH_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ *tx_octets = rp.max_tx_octets;
+ *tx_time = rp.max_tx_time;
+ *rx_octets = rp.max_rx_octets;
+ *rx_time = rp.max_rx_time;
+ *status = rp.status;
+ return 0;
+}
+
+int hci_le_write_host_suggested_data_length(
+ int dd, uint16_t *def_tx_octets,
+ uint16_t *def_tx_time, int to)
+{
+ le_write_host_suggested_data_length_cp cp;
+ struct hci_request rq;
+ uint8_t status;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.def_tx_octets = *def_tx_octets;
+ cp.def_tx_time = *def_tx_time;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_LE_CTL;
+ rq.ocf = OCF_LE_WRITE_HOST_SUGGESTED_DATA_LENGTH;
+ rq.cparam = &cp;
+ rq.clen = LE_WRITE_HOST_SUGGESTED_DATA_LENGTH_CP_SIZE;
+ rq.rparam = &status;
+ rq.rlen = 1;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (status) {
+ errno = EIO;
+ return -1;
+ }
+
+ return 0;
+}
+
+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)
+{
+ le_read_host_suggested_data_length_rp rp;
+ struct hci_request rq;
+
+ memset(&rp, 0, sizeof(rp));
+ memset(&rq, 0, sizeof(rq));
+
+ rq.ogf = OGF_LE_CTL;
+ rq.ocf = OCF_LE_READ_HOST_SUGGESTED_DATA_LENGTH;
+ rq.rparam = &rp;
+ rq.rlen = LE_READ_HOST_SUGGESTED_DATA_LENGTH_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ *def_tx_octets = rp.def_tx_octets;
+ *def_tx_time = rp.def_tx_time;
+ *status = rp.status;
+ return 0;
+}
+
+int hci_le_set_data_length(
+ int dd, const bdaddr_t *bdaddr, uint16_t *max_tx_octets,
+ uint16_t *max_tx_time, int to)
+{
+ le_set_data_length_cp cp;
+ le_set_data_length_rp rp;
+ struct hci_request rq;
+
+ memset(&cp, 0, sizeof(cp));
+ memset(&rp, 0, sizeof(rp));
+
+ bacpy(&cp.bdaddr, bdaddr);
+ cp.max_tx_octets = *max_tx_octets;
+ cp.max_tx_time = *max_tx_time;
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_LE_CTL;
+ rq.ocf = OCF_LE_SET_DATA_LENGTH;
+ rq.cparam = &cp;
+ rq.clen = LE_SET_DATA_LENGTH_CP_SIZE;
+ rq.rparam = &rp;
+ rq.rlen = LE_SET_DATA_LENGTH_RP_SIZE;
+
+ if (hci_send_req(dd, &rq, to) < 0)
+ return -1;
+
+ if (rp.status) {
+ errno = EIO;
+ return -1;
+ }
+
+ return 0;
+}
+
+#endif
diff --git a/lib/hci.h b/lib/hci.h
index 794333b2..061580bb 100755
--- a/lib/hci.h
+++ b/lib/hci.h
@@ -1710,6 +1710,52 @@ typedef struct {
} __attribute__ ((packed)) le_test_end_rp;
#define LE_TEST_END_RP_SIZE 3
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define OCF_LE_READ_MAXIMUM_DATA_LENGTH 0x002F
+typedef struct {
+ uint8_t status;
+ uint16_t max_tx_octets;
+ uint16_t max_tx_time;
+ uint16_t max_rx_octets;
+ uint16_t max_rx_time;
+} __attribute__ ((packed))
+le_read_maximum_data_length_rp;
+#define LE_READ_MAXIMUM_DATA_LENGTH_SIZE 9
+
+#define OCF_LE_WRITE_HOST_SUGGESTED_DATA_LENGTH 0x0030
+typedef struct {
+ uint16_t def_tx_octets;
+ uint16_t def_tx_time;
+} __attribute__ ((packed))
+le_write_host_suggested_data_length_cp;
+#define LE_WRITE_HOST_SUGGESTED_DATA_LENGTH_CP_SIZE 4
+
+#define OCF_LE_READ_HOST_SUGGESTED_DATA_LENGTH 0x0024
+typedef struct {
+ uint8_t status;
+ uint16_t def_tx_octets;
+ uint16_t def_tx_time;
+} __attribute__ ((packed))
+le_read_host_suggested_data_length_rp;
+#define LE_READ_HOST_SUGGESTED_DATA_LENGTH_SIZE 5
+
+#define OCF_LE_SET_DATA_LENGTH 0x0022
+typedef struct {
+ bdaddr_t bdaddr;
+ uint16_t max_tx_octets;
+ uint16_t max_tx_time;
+} __attribute__ ((packed))
+le_set_data_length_cp;
+#define LE_SET_DATA_LENGTH_CP_SIZE 10
+
+typedef struct {
+ uint16_t handle;
+ uint8_t status;
+} __attribute__ ((packed))
+le_set_data_length_rp;
+#define LE_SET_DATA_LENGTH_RP_SIZE 3
+#endif
+
#define OCF_LE_ADD_DEVICE_TO_RESOLV_LIST 0x0027
typedef struct {
uint8_t bdaddr_type;
@@ -1744,6 +1790,17 @@ typedef struct {
/* Vendor specific commands */
#define OGF_VENDOR_CMD 0x3f
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define BRCM_QOS_PRIORITY_NORMAL 0x00
+#define BRCM_QOS_PRIORITY_HIGH 0xFF
+#define BROADCOM_QOS_CMD 0xFC57 /* Only for bcm4329/bcm4330/bcm4334 chipsets */
+typedef struct {
+ uint16_t handle;
+ uint8_t priority;
+} __attribute__ ((__packed__)) broadcom_qos_cp;
+#define BROADCOM_QOS_CP_SIZE 3
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
+
/* ---- HCI Events ---- */
#define EVT_INQUIRY_COMPLETE 0x01
diff --git a/lib/hci_lib.h b/lib/hci_lib.h
index 55aeb176..ff79599a 100755
--- a/lib/hci_lib.h
+++ b/lib/hci_lib.h
@@ -50,6 +50,9 @@ struct hci_version {
int hci_open_dev(int dev_id);
int hci_close_dev(int dd);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+int hci_send_data(int dd, uint16_t handle, uint8_t len, void *data);
+#endif
int hci_send_cmd(int dd, uint16_t ogf, uint16_t ocf, uint8_t plen, void *param);
int hci_send_req(int dd, struct hci_request *req, int timeout);
@@ -141,7 +144,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_FEATURE_BLUEZ_MODIFY
+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/l2cap.h b/lib/l2cap.h
index 5ce94c4e..62b8c334 100755
--- a/lib/l2cap.h
+++ b/lib/l2cap.h
@@ -182,6 +182,18 @@ typedef struct {
} __attribute__ ((packed)) l2cap_conf_opt;
#define L2CAP_CONF_OPT_SIZE 2
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+typedef struct {
+ uint8_t mode;
+ uint8_t txwin_size;
+ uint8_t max_transmit;
+ uint16_t retrans_timeout;
+ uint16_t monitor_timeout;
+ uint16_t max_pdu_size;
+} __attribute__ ((packed)) l2cap_conf_rfc ;
+#define L2CAP_CONF_RFC_SIZE 9
+#endif
+
#define L2CAP_CONF_MTU 0x01
#define L2CAP_CONF_FLUSH_TO 0x02
#define L2CAP_CONF_QOS 0x03
diff --git a/lib/mgmt.h b/lib/mgmt.h
index 798a05e4..2a7b787a 100755
--- a/lib/mgmt.h
+++ b/lib/mgmt.h
@@ -900,21 +900,361 @@ static const char *mgmt_status[] = {
"Permission Denied",
};
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+
+#define TIZEN_OP_CODE_BASE 0xff00
+#define TIZEN_EV_BASE 0xff00
+
+#define MGMT_MAX_ADVERTISING_LENGTH 31
+
+#define MGMT_MAX_EIR_MANUFACTURER_DATA_LENGTH 100
+
+#define MGMT_IRK_SIZE 16
+
+#define MGMT_OP_SET_ADVERTISING_PARAMS (TIZEN_OP_CODE_BASE + 0x01)
+struct mgmt_cp_set_advertising_params {
+ uint16_t interval_min;
+ uint16_t interval_max;
+ uint8_t filter_policy;
+ uint8_t type;
+} __packed;
+
+#define MGMT_OP_SET_ADVERTISING_DATA (TIZEN_OP_CODE_BASE + 0x02)
+struct mgmt_cp_set_advertising_data {
+ uint8_t data[MGMT_MAX_ADVERTISING_LENGTH];
+} __packed;
+
+#define MGMT_OP_SET_SCAN_RSP_DATA (TIZEN_OP_CODE_BASE + 0x03)
+struct mgmt_cp_set_scan_rsp_data {
+ uint8_t data[MGMT_MAX_ADVERTISING_LENGTH];
+} __packed;
+
+#define MGMT_OP_ADD_DEV_WHITE_LIST (TIZEN_OP_CODE_BASE + 0x04)
+struct mgmt_cp_add_dev_white_list {
+ uint8_t bdaddr_type;
+ bdaddr_t bdaddr;
+} __packed;
+
+#define MGMT_OP_REMOVE_DEV_FROM_WHITE_LIST (TIZEN_OP_CODE_BASE + 0x05)
+struct mgmt_cp_remove_dev_white_list {
+ uint8_t bdaddr_type;
+ bdaddr_t bdaddr;
+} __packed;
+
+#define MGMT_OP_CLEAR_DEV_WHITE_LIST (TIZEN_OP_CODE_BASE + 0x06)
+
+/* BEGIN TIZEN_Bluetooth :: RSSI monitoring */
+#define MGMT_OP_SET_RSSI_ENABLE (TIZEN_OP_CODE_BASE + 0x07)
+struct mgmt_cp_set_enable_rssi {
+ int8_t low_th;
+ int8_t in_range_th;
+ int8_t high_th;
+ bdaddr_t bdaddr;
+ int8_t link_type;
+} __packed;
+
+struct mgmt_cc_rsp_enable_rssi {
+ uint8_t status;
+ uint8_t le_ext_opcode;
+ bdaddr_t bt_address;
+ int8_t link_type;
+} __packed;
+
+#define MGMT_OP_GET_RAW_RSSI (TIZEN_OP_CODE_BASE + 0x08)
+struct mgmt_cp_get_raw_rssi {
+ bdaddr_t bt_address;
+ uint8_t link_type;
+} __packed;
+struct mgmt_cc_rp_get_raw_rssi {
+ uint8_t status;
+ int8_t rssi_dbm;
+ uint8_t link_type;
+ bdaddr_t bt_address;
+} __packed;
+
+#define MGMT_OP_SET_RSSI_DISABLE (TIZEN_OP_CODE_BASE + 0x09)
+struct mgmt_cp_disable_rssi {
+ bdaddr_t bdaddr;
+ int8_t link_type;
+} __packed;
+struct mgmt_cc_rp_disable_rssi {
+ uint8_t status;
+ uint8_t le_ext_opcode;
+ bdaddr_t bt_address;
+ int8_t link_type;
+} __packed;
+/* END TIZEN_Bluetooth :: RSSI monitoring */
+
+#define MGMT_OP_START_LE_DISCOVERY (TIZEN_OP_CODE_BASE + 0x0a)
+struct mgmt_cp_start_le_discovery {
+ uint8_t type;
+} __packed;
+
+#define MGMT_OP_STOP_LE_DISCOVERY (TIZEN_OP_CODE_BASE + 0x0b)
+struct mgmt_cp_stop_le_discovery {
+ uint8_t type;
+} __packed;
+
+#define MGMT_OP_LE_CONN_UPDATE (TIZEN_OP_CODE_BASE + 0x0d)
+struct mgmt_cp_le_conn_update {
+ uint16_t interval_min;
+ uint16_t interval_max;
+ uint16_t latency;
+ uint16_t supervision_time_out;
+ bdaddr_t bdaddr;
+} __packed;
+
+#define MGMT_OP_SET_MANUFACTURER_DATA (TIZEN_OP_CODE_BASE + 0x0e)
+struct mgmt_cp_set_manufacturer_data {
+ uint8_t data[MGMT_MAX_EIR_MANUFACTURER_DATA_LENGTH];
+} __packed;
+
+#define MGMT_OP_LE_SET_SCAN_PARAMS (TIZEN_OP_CODE_BASE + 0x0f)
+struct mgmt_cp_le_set_scan_params {
+ uint8_t type; /* le scan type */
+ uint16_t interval;
+ uint16_t window;
+} __packed;
+
+#define MGMT_SCO_ROLE_HANDSFREE 0x00
+#define MGMT_SCO_ROLE_AUDIO_GATEWAY 0x01
+#define MGMT_OP_SET_VOICE_SETTING (TIZEN_OP_CODE_BASE + 0x10)
+struct mgmt_cp_set_voice_setting {
+ bdaddr_t bdaddr;
+ uint8_t sco_role;
+ uint16_t voice_setting;
+} __packed;
+
+#define MGMT_OP_GET_ADV_TX_POWER (TIZEN_OP_CODE_BASE + 0x11)
+struct mgmt_rp_get_adv_tx_power {
+ int8_t adv_tx_power;
+} __packed;
+
+#define MGMT_OP_ENABLE_6LOWPAN (TIZEN_OP_CODE_BASE + 0x12)
+struct mgmt_cp_enable_6lowpan {
+ uint8_t enable_6lowpan;
+} __packed;
+
+#define MGMT_OP_CONNECT_6LOWPAN (TIZEN_OP_CODE_BASE + 0x13)
+struct mgmt_cp_connect_6lowpan {
+ struct mgmt_addr_info addr;
+} __packed;
+
+#define MGMT_OP_DISCONNECT_6LOWPAN (TIZEN_OP_CODE_BASE + 0x14)
+struct mgmt_cp_disconnect_6lowpan {
+ struct mgmt_addr_info addr;
+} __packed;
+
+#define MGMT_OP_LE_READ_MAXIMUM_DATA_LENGTH (TIZEN_OP_CODE_BASE + 0x15)
+struct mgmt_rp_le_read_maximum_data_length {
+ uint8_t status;
+ uint16_t max_tx_octets;
+ uint16_t max_tx_time;
+ uint16_t max_rx_octets;
+ uint16_t max_rx_time;
+} __packed;
+
+#define MGMT_OP_LE_WRITE_HOST_SUGGESTED_DATA_LENGTH (TIZEN_OP_CODE_BASE + 0x16)
+struct mgmt_cp_le_write_host_suggested_data_length {
+ uint16_t def_tx_octets;
+ uint16_t def_tx_time;
+} __packed;
+
+#define MGMT_OP_LE_READ_HOST_SUGGESTED_DATA_LENGTH (TIZEN_OP_CODE_BASE + 0x17)
+struct mgmt_rp_le_read_host_suggested_data_length {
+ uint8_t status;
+ uint16_t def_tx_octets;
+ uint16_t def_tx_time;
+} __packed;
+
+#define MGMT_OP_LE_SET_DATA_LENGTH (TIZEN_OP_CODE_BASE + 0x18)
+struct mgmt_cp_le_set_data_length {
+ bdaddr_t bdaddr;
+ uint16_t max_tx_octets;
+ uint16_t max_tx_time;
+} __packed;
+#define MGMT_LE_SET_DATA_LENGTH_SIZE 10
+
+#define MGMT_OP_SET_IRK (TIZEN_OP_CODE_BASE + 0x19)
+struct mgmt_cp_set_irk {
+ uint8_t irk[16];
+} __packed;
+
+#define MGMT_OP_SET_DEV_RPA_RES_SUPPORT (TIZEN_OP_CODE_BASE + 0x1a)
+struct mgmt_cp_set_dev_rpa_res_support {
+ struct mgmt_addr_info addr;
+ uint8_t res_support;
+} __packed;
+
+/* BEGIN TIZEN_Bluetooth :: name update changes */
+#define MGMT_EV_DEVICE_NAME_UPDATE (TIZEN_EV_BASE + 0x01)
+struct mgmt_ev_device_name_update {
+ struct mgmt_addr_info addr;
+ uint16_t eir_len;
+ uint8_t eir[0];
+} __packed;
+/* END TIZEN_Bluetooth :: name update changes */
+
+/* BEGIN TIZEN_Bluetooth :: Add handling of hardware error event */
+#define MGMT_EV_HARDWARE_ERROR (TIZEN_EV_BASE + 0x02)
+struct mgmt_ev_hardware_error{
+ uint8_t error_code;
+} __packed;
+/* END TIZEN_Bluetooth */
+
+/* BEGIN TIZEN_Bluetooth :: HCI TX Timeout Error */
+#define MGMT_EV_TX_TIMEOUT_ERROR (TIZEN_EV_BASE + 0x03)
+/* END TIZEN_Bluetooth */
+
+/* BEGIN TIZEN_Bluetooth :: Add handling of RSSI Events */
+#define MGMT_EV_RSSI_ALERT (TIZEN_EV_BASE + 0x04)
+struct mgmt_ev_vendor_specific_rssi_alert {
+ bdaddr_t bdaddr;
+ int8_t link_type;
+ int8_t alert_type;
+ int8_t rssi_dbm;
+} __packed;
+
+#define MGMT_EV_RAW_RSSI (TIZEN_EV_BASE + 0x05)
+
+#define MGMT_EV_RSSI_ENABLED (TIZEN_EV_BASE + 0x06)
+
+#define MGMT_EV_RSSI_DISABLED (TIZEN_EV_BASE + 0x07)
+/* END TIZEN_Bluetooth :: Handling of RSSI Events */
+
+/* BEGIN TIZEN_Bluetooth :: Add LE connection update Events */
+#define MGMT_EV_CONN_UPDATED (TIZEN_EV_BASE + 0x08)
+struct mgmt_ev_conn_updated {
+ struct mgmt_addr_info addr;
+ uint16_t conn_interval;
+ uint16_t conn_latency;
+ uint16_t supervision_timeout;
+} __packed;
+
+#define MGMT_EV_CONN_UPDATE_FAILED (TIZEN_EV_BASE + 0x09)
+struct mgmt_ev_conn_update_failed {
+ struct mgmt_addr_info addr;
+ uint8_t status;
+} __packed;
+/* END TIZEN_Bluetooth :: Add LE connection update Events */
+
+#define MGMT_EV_LE_DEVICE_FOUND (TIZEN_EV_BASE + 0x0a)
+struct mgmt_ev_le_device_found {
+ struct mgmt_addr_info addr;
+ int8_t rssi;
+ uint32_t flags;
+ int8_t adv_type;
+ uint16_t eir_len;
+ uint8_t eir[0];
+} __packed;
+
+#define MGMT_EV_MULTI_ADV_STATE_CHANGED (TIZEN_EV_BASE + 0x0b)
+struct mgmt_ev_vendor_specific_multi_adv_state_changed {
+ uint8_t adv_instance;
+ uint8_t state_change_reason;
+ int16_t connection_handle;
+} __packed;
+
+#define MGMT_EV_6LOWPAN_CONN_STATE_CHANGED (TIZEN_EV_BASE + 0x0c)
+struct mgmt_ev_6lowpan_conn_state_changed {
+ struct mgmt_addr_info addr;
+ uint8_t connected;
+ uint8_t ifname[16];
+} __packed;
+
+
+#define MGMT_EV_LE_DATA_LENGTH_CHANGED (TIZEN_EV_BASE + 0x0d)
+struct mgmt_ev_le_data_length_changed {
+ struct mgmt_addr_info addr;
+ int16_t max_tx_octets;
+ int16_t max_tx_time;
+ int16_t max_rx_octets;
+ int16_t max_rx_time;
+} __packed;
+
+static const char *mgmt_tizen_op[] = {
+ "<0x0000>",
+ "Set Advertising Parameters",
+ "Set Advertising Data",
+ "Set Scan Response Data",
+ "Add Device White List",
+ "Remove Device White List",
+ "Clear Device White List",
+ "Set RSSI Enable",
+ "Get Raw RSSI",
+ "Set RSSI Disable",
+ "Start LE Discovery",
+ "Stop LE Discovery",
+ "Disable LE Auto Connect",
+ "LE Connection Update",
+ "Set Manufacturer Data",
+ "LE Set Scan Parameters",
+ "Set Voice Setting",
+ "Get Adv Tx Power",
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ "Enable BT 6LOWPAN",
+ "Connect BT 6LOWPAN",
+ "Disconnect BT 6LOWPAN"
+#endif
+};
+
+static const char *mgmt_tizen_ev[] = {
+ "<0x0000>",
+ "Device Name Update",
+ "Hardware Error",
+ "Tx TimeOut Error",
+ "RSSI Alert",
+ "Raw RSSI",
+ "RSSI Enabled",
+ "RSSI Disabled",
+ "LE Connection Updated",
+ "LE Connection Update Failed",
+ "LE Device Found",
+ "Multi Adv State Change",
+};
+#endif /* End of TIZEN_FEATURE_BLUEZ_MODIFY */
+
#ifndef NELEM
#define NELEM(x) (sizeof(x) / sizeof((x)[0]))
#endif
static inline const char *mgmt_opstr(uint16_t op)
{
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (op >= NELEM(mgmt_op)) {
+ uint16_t tizen_op = op - TIZEN_OP_CODE_BASE;
+
+ if (tizen_op > 0 &&
+ tizen_op < NELEM(mgmt_tizen_op))
+ return mgmt_tizen_op[tizen_op];
+
+ return "<unknown opcode>";
+ }
+#else
if (op >= NELEM(mgmt_op))
return "<unknown opcode>";
+#endif
+
return mgmt_op[op];
}
static inline const char *mgmt_evstr(uint16_t ev)
{
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (ev >= NELEM(mgmt_ev)) {
+ uint16_t tizen_ev = ev - TIZEN_EV_BASE;
+
+ if (tizen_ev > 0 &&
+ tizen_ev < NELEM(mgmt_tizen_ev))
+ return mgmt_tizen_ev[tizen_ev];
+
+ return "<unknown event>";
+ }
+#else
if (ev >= NELEM(mgmt_ev))
return "<unknown event>";
+#endif
+
return mgmt_ev[ev];
}
diff --git a/lib/uuid.h b/lib/uuid.h
index 2dcfe9e4..393fbe81 100755
--- a/lib/uuid.h
+++ b/lib/uuid.h
@@ -30,6 +30,9 @@ extern "C" {
#endif
#include <stdint.h>
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#include <bluetooth/bluetooth.h>
+#endif
#define GENERIC_AUDIO_UUID "00001203-0000-1000-8000-00805f9b34fb"
@@ -88,6 +91,9 @@ extern "C" {
#define HDP_SINK_UUID "00001402-0000-1000-8000-00805f9b34fb"
#define HID_UUID "00001124-0000-1000-8000-00805f9b34fb"
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+#define HID_DEVICE_UUID "00001124-0000-1000-8000-00805f9b43bf"
+#endif
#define DUN_GW_UUID "00001103-0000-1000-8000-00805f9b34fb"
@@ -106,6 +112,12 @@ extern "C" {
#define OBEX_MNS_UUID "00001133-0000-1000-8000-00805f9b34fb"
#define OBEX_MAP_UUID "00001134-0000-1000-8000-00805f9b34fb"
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+/* Samsung Accessary Protocol UUIDs */
+#define WEARABLE_OLD_SAP_UUID "a49eb41e-cb06-495c-9f4f-aa80a90cdf4a"
+#define WEARABLE_NEW_SAP_UUID "a49eb41e-cb06-495c-9f4f-bb80a90cdf00"
+#endif
+
/* GATT UUIDs section */
#define GATT_PRIM_SVC_UUID 0x2800
#define GATT_SND_SVC_UUID 0x2801
@@ -127,6 +139,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_FEATURE_BLUEZ_MODIFY
+#define GATT_CHARAC_CENTRAL_RPA_RESOLUTION 0x2AA6
+#endif
/* GATT Characteristic Descriptors */
#define GATT_CHARAC_EXT_PROPER_UUID 0x2900
@@ -139,6 +154,11 @@ extern "C" {
#define GATT_EXTERNAL_REPORT_REFERENCE 0x2907
#define GATT_REPORT_REFERENCE 0x2908
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+/* GATT Service UUIDs : Defined by SIG */
+#define GATT_IPSP_UUID 0x1820
+#endif
+
typedef struct {
enum {
BT_UUID_UNSPEC = 0,
diff --git a/monitor/broadcom.c b/monitor/broadcom.c
index a3c34439..80edbebd 100755
--- a/monitor/broadcom.c
+++ b/monitor/broadcom.c
@@ -35,6 +35,9 @@
#include "ll.h"
#include "vendor.h"
#include "broadcom.h"
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#include "uuid.h"
+#endif
static void print_status(uint8_t status)
{
@@ -93,6 +96,599 @@ static void launch_ram_cmd(const void *data, uint8_t size)
print_field("Address: 0x%8.8x", addr);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void set_advt_param_multi_subcmd(const void *data, uint8_t size)
+{
+ uint8_t adv_instance = get_u8(data + size - 2);
+ int8_t tx_power = *((int8_t *)(data + size - 1));
+
+ print_le_set_adv_parameters_cmd(data, size - 2);
+
+ print_field("Advertising Instance: %u", adv_instance);
+ print_field("TX Power: %d", tx_power);
+}
+
+static void set_advt_data_subcmd(const void *data, uint8_t size)
+{
+ uint8_t adv_instance = get_u8(data + size - 1);
+
+ print_le_set_adv_data_cmd(data, size - 1);
+
+ print_field("Advertising Instance: %u", adv_instance);
+}
+
+static void set_scan_rsp_data_multi_subcmd(const void *data, uint8_t size)
+{
+ uint8_t adv_instance = get_u8(data + size - 1);
+
+ print_le_set_scan_rsp_data_cmd(data, size - 1);
+
+ print_field("Advertising Instance: %u", adv_instance);
+}
+
+static void set_random_addr_multi_subcmd(const void *data, uint8_t size)
+{
+ uint8_t adv_instance = get_u8(data + size - 1);
+
+ print_le_set_random_address_cmd(data, size - 1);
+
+ print_field("Advertising Instance: %u", adv_instance);
+}
+
+static void set_adv_enable_multi_subcmd(const void *data, uint8_t size)
+{
+ uint8_t adv_instance = get_u8(data + size - 1);
+
+ print_le_set_adv_enable_cmd(data, size - 1);
+
+ print_field("Advertising Instance: %u", adv_instance);
+}
+
+static void enable_custom_feature_subcmd(const void *data, uint8_t size)
+{
+ uint8_t enable = get_u8(data);
+ const char *str;
+
+ switch (enable) {
+ case 0x00:
+ str = "Disable";
+ break;
+ case 0x01:
+ str = "Enable";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("%s offloaded RPA feature (0x%2.2x)", str, enable);
+}
+
+static void add_irk_to_list_subcmd(const void *data, uint8_t size)
+{
+ uint8_t addr_type = get_u8(data + 16);
+ const uint8_t *addr = data + 17;
+ const char *str;
+
+ print_field("LE IRK (1st byte LSB)");
+ packet_hexdump(data, 16);
+
+ switch (addr_type) {
+ case 0x00:
+ str = "Public Address";
+ break;
+ case 0x01:
+ str = "Random Address";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("Address type : %s (0x%2.2x)", str, addr_type);
+ print_field("Address : %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X",
+ addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
+}
+
+static void remove_irk_from_list_subcmd(const void *data, uint8_t size)
+{
+ uint8_t addr_type = get_u8(data);
+ const uint8_t *addr = data + 1;
+ const char *str;
+
+ switch (addr_type) {
+ case 0x00:
+ str = "Public Address";
+ break;
+ case 0x01:
+ str = "Random Address";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("Address type : %s (0x%2.2x)", str, addr_type);
+ print_field("Address : %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X",
+ addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
+}
+
+static void read_irk_list_entry_subcmd(const void *data, uint8_t size)
+{
+ uint8_t index = get_u8(data);
+
+ print_field("LE Read IRK List entry index : %u", index);
+}
+
+static void apcf_enable_subcmd(const void *data, uint8_t size)
+{
+ uint8_t enable = get_u8(data);
+ const char *str;
+
+ switch (enable) {
+ case 0x00:
+ str = "Disable";
+ break;
+ case 0x01:
+ str = "Enable";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("%s APCF feature (0x%2.2x)", str, enable);
+}
+
+static const struct {
+ uint8_t bit;
+ const char *str;
+} apcf_feature_table[] = {
+ { 0, "Broadcast Address filter" },
+ { 1, "Service Data Change filter" },
+ { 2, "Service UUID check" },
+ { 3, "Service Solicitation UUID check" },
+ { 4, "Local Name check" },
+ { 5, "Manufacturer Data check" },
+ { 6, "Service Data check" },
+ { }
+};
+
+static void print_apcf_feature(const char *label, uint16_t feature)
+{
+ int i;
+ uint16_t mask;
+
+ mask = feature;
+
+ print_field("%s", label);
+
+ for (i = 0; apcf_feature_table[i].str; i++) {
+ if (feature & (1 << apcf_feature_table[i].bit)) {
+ print_field(" %s", apcf_feature_table[i].str);
+ mask &= ~(1 << apcf_feature_table[i].bit);
+ }
+ }
+
+ if (mask)
+ print_field(" Unknown features (0x%4.4x)", mask);
+}
+
+static void apcf_set_filtering_param_subcmd(const void *data, uint8_t size)
+{
+ uint8_t add = get_u8(data);
+ uint8_t index = get_u8(data + 1);
+ uint16_t feature_selection = get_le16(data + 2);
+ uint16_t list_logic = get_le16(data + 4);
+ uint16_t filter_logic = get_u8(data + 6);
+ uint8_t rssi_high = get_u8(data + 8);
+ uint8_t delivery_mode = get_u8(data + 9);
+ uint16_t onfound_timeout = get_le16(data + 10);
+ uint8_t onfound_timeout_cnt = get_u8(data + 12);
+ uint8_t rssi_low = get_u8(data + 13);
+ uint16_t onlost_timeout = get_le16(data + 14);
+ uint16_t no_of_tracking_entries;
+ const char *str;
+
+ switch (add) {
+ case 0x00:
+ str = "Add";
+ break;
+ case 0x01:
+ str = "Delete";
+ break;
+ case 0x02:
+ str = "Clear";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("Action : %s for filter [%d]", str, index);
+
+ print_apcf_feature("Feature Selection", feature_selection);
+ print_apcf_feature("List Logic Type (OR)", ~list_logic);
+ print_apcf_feature("List Logic Type (AND)", list_logic);
+ print_apcf_feature("Filter Logic Type (OR)", ~(filter_logic << 3));
+ print_apcf_feature("Filter Logic Type (AND)", filter_logic << 3);
+ print_field("RSSI High Threshold : %d dBm", rssi_high);
+
+ switch (delivery_mode) {
+ case 0x00:
+ str = "Immediate";
+ break;
+ case 0x01:
+ str = "On Found";
+ break;
+ case 0x02:
+ str = "Batched";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("Delivery_mode : %s", str);
+ print_field("On Found Timeout : %d miliseconds", onfound_timeout);
+ print_field("On Found Timeout Count : %d", onfound_timeout_cnt);
+ print_field("RSSI Low Threshold : %d dBm", rssi_low);
+ print_field("On Lost Timeout : %d miliseconds", onlost_timeout);
+
+ if (size >= 18) {
+ no_of_tracking_entries = get_le16(data + 16);
+ print_field("Number of Tracking Entries : %d",
+ no_of_tracking_entries);
+ }
+}
+
+static void apcf_broadcaster_addr_subcmd(const void *data, uint8_t size)
+{
+ uint8_t add = get_u8(data);
+ uint8_t index = get_u8(data + 1);
+ uint8_t type = get_u8(data + 7);
+ char *str;
+
+ switch (add) {
+ case 0x00:
+ str = "Add";
+ break;
+ case 0x01:
+ str = "Delete";
+ break;
+ case 0x02:
+ str = "Clear";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("Action : %s for filter [%d]", str, index);
+ packet_print_addr("Address", data + 2, type == 0x00 ? false : true);
+}
+
+static void apcf_service_uuid_subcmd(const void *data, uint8_t size)
+{
+ uint8_t add = get_u8(data);
+ uint8_t index = get_u8(data + 1);
+ char *str;
+ const uint8_t *uuid;
+ uint16_t uuid16;
+ uint32_t uuid32;
+
+
+ switch (add) {
+ case 0x00:
+ str = "Add";
+ break;
+ case 0x01:
+ str = "Delete";
+ break;
+ case 0x02:
+ str = "Clear";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("Action : %s for filter [%d]", str, index);
+
+ switch ((size - 2) / 2) {
+ case 2:
+ uuid16 = get_le16(data + 2);
+ print_field(" UUID : %s (0x%4.4x)",
+ uuid16_to_str(uuid16), uuid16);
+
+ uuid16 = get_le16(data + 4);
+ print_field(" UUID Mask: %s (0x%4.4x)",
+ uuid16_to_str(uuid16), uuid16);
+ break;
+ case 4:
+ uuid32 = get_le32(data + 2);
+ print_field(" UUID :%s (0x%8.8x)",
+ uuid32_to_str(uuid32), uuid32);
+
+ uuid32 = get_le32(data + 6);
+ print_field(" UUID Mask:%s (0x%8.8x)",
+ uuid32_to_str(uuid32), uuid32);
+ break;
+ case 16:
+ uuid = data + 2;
+ print_field(" UUID :%8.8x-%4.4x-%4.4x-%4.4x-%8.8x%4.4x",
+ get_le32(&uuid[12]), get_le16(&uuid[10]),
+ get_le16(&uuid[8]), get_le16(&uuid[6]),
+ get_le32(&uuid[2]), get_le16(&uuid[0]));
+ uuid = data + 18;
+ print_field(" UUID :%8.8x-%4.4x-%4.4x-%4.4x-%8.8x%4.4x",
+ get_le32(&uuid[12]), get_le16(&uuid[10]),
+ get_le16(&uuid[8]), get_le16(&uuid[6]),
+ get_le32(&uuid[2]), get_le16(&uuid[0]));
+ break;
+ default:
+ print_field("Invalid UUIDs");
+ packet_hexdump(data + 2, size - 2);
+ break;
+ }
+
+ return;
+}
+
+static void apcf_service_solicitation_uuid_subcmd(const void *data, uint8_t size)
+{
+ apcf_service_uuid_subcmd(data, size);
+}
+
+static void apcf_local_name_subcmd(const void *data, uint8_t size)
+{
+ uint8_t add = get_u8(data);
+ uint8_t index = get_u8(data + 1);
+ char *str;
+ char name[30] = { 0 };
+
+ switch (add) {
+ case 0x00:
+ str = "Add";
+ break;
+ case 0x01:
+ str = "Delete";
+ break;
+ case 0x02:
+ str = "Clear";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("Action : %s for filter [%d]", str, index);
+
+ memcpy(name, data + 2, size - 2 < 29 ? size - 2 : 29);
+ print_field("Local Name : %s", name);
+}
+
+static void apcf_manufacturer_data_subcmd(const void *data, uint8_t size)
+{
+ uint8_t add = get_u8(data);
+ uint8_t index = get_u8(data + 1);
+ char *str;
+
+ switch (add) {
+ case 0x00:
+ str = "Add";
+ break;
+ case 0x01:
+ str = "Delete";
+ break;
+ case 0x02:
+ str = "Clear";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("Action : %s for filter [%d]", str, index);
+
+ print_field("Manufacturer data");
+ packet_hexdump(data + 2, (size - 2 ) / 2);
+
+ print_field("Manufacturer data Mask");
+ packet_hexdump(data + 2 + (size - 2) / 2, (size - 2 ) / 2);
+}
+
+static void apcf_service_data_subcmd(const void *data, uint8_t size)
+{
+ uint8_t add = get_u8(data);
+ uint8_t index = get_u8(data + 1);
+ char *str;
+
+ switch (add) {
+ case 0x00:
+ str = "Add";
+ break;
+ case 0x01:
+ str = "Delete";
+ break;
+ case 0x02:
+ str = "Clear";
+ break;
+ default:
+ str = "Reserved";
+ break;
+ }
+
+ print_field("Action : %s for filter [%d]", str, index);
+
+ print_field("Service data");
+ packet_hexdump(data + 2, (size - 2 ) / 2);
+
+ print_field("Service data Mask");
+ packet_hexdump(data + 2 + (size - 2) / 2, (size - 2 ) / 2);
+}
+
+struct subcmd_data {
+ uint8_t subcmd;
+ const char *str;
+ void (*cmd_func) (const void *data, uint8_t size);
+ uint8_t cmd_size;
+ bool cmd_fixed;
+};
+
+static void print_subcmd(const struct subcmd_data *subcmd_data,
+ const void *data, uint8_t size)
+{
+ const char *subcmd_color;
+
+ if (subcmd_data->cmd_func)
+ subcmd_color = COLOR_BLUE;
+ else
+ subcmd_color = COLOR_WHITE_BG;
+
+ print_indent(6, subcmd_color, "", subcmd_data->str, COLOR_OFF,
+ " (0x%2.2x)", subcmd_data->subcmd);
+
+ if (!subcmd_data->cmd_func) {
+ packet_hexdump(data, size);
+ return;
+ }
+
+ if (subcmd_data->cmd_fixed) {
+ if (size != subcmd_data->cmd_size) {
+ print_text(COLOR_ERROR, "invalid packet size");
+ packet_hexdump(data, size);
+ return;
+ }
+ } else {
+ if (size < subcmd_data->cmd_size) {
+ print_text(COLOR_ERROR, "too short packet");
+ packet_hexdump(data, size);
+ return;
+ }
+ }
+
+ subcmd_data->cmd_func(data, size);
+}
+
+static const struct subcmd_data le_multi_advt_table[] = {
+ { 0x01, "LE Set Advertising Parameters Multi Sub Command",
+ set_advt_param_multi_subcmd, 23, true },
+ { 0x02, "LE Set Advertising Data Multi Sub Command",
+ set_advt_data_subcmd, 33, false },
+ { 0x03, "LE Set Scan Response Data Multi Sub Command",
+ set_scan_rsp_data_multi_subcmd, 33, false },
+ { 0x04, "LE Set Random Address Multi Sub Command",
+ set_random_addr_multi_subcmd, 7, true },
+ { 0x05, "LE Set Advertise Enable Multi Sub Command",
+ set_adv_enable_multi_subcmd, 2, true },
+ { }
+};
+
+static void le_multi_advt_cmd(const void *data, uint8_t size)
+{
+ uint8_t subcmd = *((const uint8_t *)data);
+ struct subcmd_data unknown;
+ const struct subcmd_data *subcmd_data = &unknown;
+ int i;
+
+ unknown.subcmd = subcmd;
+ unknown.str = "Unknown Sub Command";
+ unknown.cmd_func = NULL;
+ unknown.cmd_size = 0;
+ unknown.cmd_fixed = true;
+
+ for (i = 0; le_multi_advt_table[i].str; i++) {
+ if (le_multi_advt_table[i].subcmd == subcmd) {
+ subcmd_data = &le_multi_advt_table[i];
+ break;
+ }
+ }
+
+ print_subcmd(subcmd_data, data + 1, size - 1);
+}
+
+static const struct subcmd_data le_rpa_offload_table[] = {
+ { 0x01, "Enable customer specific feature",
+ enable_custom_feature_subcmd, 1, true },
+ { 0x02, "Add IRK to the list",
+ add_irk_to_list_subcmd, 23, true },
+ { 0x03, "Remove IRK from the list",
+ remove_irk_from_list_subcmd, 7, true },
+ { 0x04, "Clear IRK list",
+ null_cmd, 0, true },
+ { 0x05, "Read IRK list entry",
+ read_irk_list_entry_subcmd, 1, true },
+ { }
+};
+
+static void le_rpa_offload_cmd(const void *data, uint8_t size)
+{
+ uint8_t subcmd = *((const uint8_t *)data);
+ struct subcmd_data unknown;
+ const struct subcmd_data *subcmd_data = &unknown;
+ int i;
+
+ unknown.subcmd = subcmd;
+ unknown.str = "Unknown Sub Command";
+ unknown.cmd_func = NULL;
+ unknown.cmd_size = 0;
+ unknown.cmd_fixed = true;
+
+ for (i = 0; le_rpa_offload_table[i].str; i++) {
+ if (le_rpa_offload_table[i].subcmd == subcmd) {
+ subcmd_data = &le_rpa_offload_table[i];
+ break;
+ }
+ }
+
+ print_subcmd(subcmd_data, data + 1, size - 1);
+}
+
+static const struct subcmd_data le_apcf_table[] = {
+ { 0x00, "APCF Enable",
+ apcf_enable_subcmd, 1, true },
+ { 0x01, "APCF Set Filtering Parameters",
+ apcf_set_filtering_param_subcmd, 15, false },
+ { 0x02, "APCF Broadcaster Address",
+ apcf_broadcaster_addr_subcmd, 9, true },
+ { 0x03, "APCF Service UUID",
+ apcf_service_uuid_subcmd, 2, false },
+ { 0x04, "APCF Service Solicitation UUID",
+ apcf_service_solicitation_uuid_subcmd, 2, false },
+ { 0x05, "APCF Local Name",
+ apcf_local_name_subcmd, 2, false },
+ { 0x06, "APCF Manufacturer Data",
+ apcf_manufacturer_data_subcmd, 2, false },
+ { 0x07, "APCF Service Data",
+ apcf_service_data_subcmd, 2, false },
+ { }
+};
+
+static void le_apcf_cmd(const void *data, uint8_t size)
+{
+ uint8_t subcmd = *((const uint8_t *)data);
+ struct subcmd_data unknown;
+ const struct subcmd_data *subcmd_data = &unknown;
+ int i;
+
+ unknown.subcmd = subcmd;
+ unknown.str = "Unknown Sub Command";
+ unknown.cmd_func = NULL;
+ unknown.cmd_size = 0;
+ unknown.cmd_fixed = true;
+
+ for (i = 0; le_apcf_table[i].str; i++) {
+ if (le_apcf_table[i].subcmd == subcmd) {
+ subcmd_data = &le_apcf_table[i];
+ break;
+ }
+ }
+
+ print_subcmd(subcmd_data, data + 1, size - 1);
+}
+#endif
+
static void read_vid_pid_rsp(const void *data, uint8_t size)
{
uint8_t status = get_u8(data);
@@ -132,6 +728,97 @@ static void read_verbose_version_info_rsp(const void *data, uint8_t size)
print_field("Build number: %u (0x%4.4x)", build_num, build_num);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void get_vendor_capabilities_rsp(const void *data, uint8_t size)
+{
+ uint8_t status = get_u8(data);
+ uint8_t max_advt_instances = get_u8(data + 1);
+ uint8_t offloaded_resolution_of_private_address = get_u8(data + 2);
+ uint16_t total_scan_results_storage = get_le16(data + 3);
+ uint8_t max_irk_list_sz = get_u8(data + 5);
+ uint8_t filtering_support = get_u8(data + 6);
+ uint8_t max_filter = get_u8(data + 7);
+ uint8_t activity_energy_info_support = get_u8(data + 8);
+ uint8_t onlost_follow_per_filter = get_u8(data + 9);
+
+ print_status(status);
+ print_field("The Number of advertisement instances supported: %u",
+ max_advt_instances);
+ print_field("BT chip capability of RPA: %s",
+ offloaded_resolution_of_private_address ?
+ "Capable" : "Not Capable");
+ print_field("Storage for scan results: %u bytes",
+ total_scan_results_storage);
+ print_field("The Number of IRK entries supported: %u", max_irk_list_sz);
+ print_field("Support Filtering in BT chip: %s",
+ filtering_support ? "Supported" : "Not Supported");
+ print_field("The Number of filters supported: %u", max_filter);
+ print_field("Supports reporting of activity and energy info: %s",
+ activity_energy_info_support ?
+ "Capable" : "Not Capable");
+ print_field("The Number of advertisers that can be analysed for "
+ "onlost per filter: %u", onlost_follow_per_filter);
+}
+
+static void le_multi_advt_rsp(const void *data, uint8_t size)
+{
+ uint8_t status = get_u8(data);
+ uint8_t subcmd = get_u8(data + 1);
+ int i;
+ const char *str = "Unknown Sub Command";
+
+ print_status(status);
+
+ for (i = 0; le_multi_advt_table[i].str; i++) {
+ if (le_multi_advt_table[i].subcmd == subcmd) {
+ str = le_multi_advt_table[i].str;
+ break;
+ }
+ }
+
+ print_field("Multi Advertise OPcode: %s (%u)", str, subcmd);
+}
+
+static void le_rpa_offload_rsp(const void *data, uint8_t size)
+{
+ uint8_t status = get_u8(data);
+ uint8_t subcmd = get_u8(data + 1);
+ int i;
+ const char *str = "Unknown Sub Command";
+
+ print_status(status);
+
+ for (i = 0; le_rpa_offload_table[i].str; i++) {
+ if (le_rpa_offload_table[i].subcmd == subcmd) {
+ str = le_rpa_offload_table[i].str;
+ break;
+ }
+ }
+
+ print_field("RPA Offload OPcode: %s (%u)", str, subcmd);
+}
+
+static void le_apcf_rsp(const void *data, uint8_t size)
+{
+ uint8_t status = get_u8(data);
+ uint8_t subcmd = get_u8(data + 1);
+ int i;
+ const char *str = "Unknown Sub Command";
+
+ print_status(status);
+
+ for (i = 0; le_apcf_table[i].str; i++) {
+ if (le_apcf_table[i].subcmd == subcmd) {
+ str = le_apcf_table[i].str;
+ break;
+ }
+ }
+
+ print_field("Advertising Packet Content Filter OPcode: %s (%u)",
+ str, subcmd);
+}
+#endif
+
static const struct vendor_ocf vendor_ocf_table[] = {
{ 0x001, "Write BD ADDR",
write_bd_addr_cmd, 6, true,
@@ -157,6 +844,25 @@ static const struct vendor_ocf vendor_ocf_table[] = {
{ 0x079, "Read Verbose Config Version Info",
null_cmd, 0, true,
read_verbose_version_info_rsp, 7, true },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ { 0x0153, "LE Get Vendor Capabilities",
+ null_cmd, 0, true,
+ get_vendor_capabilities_rsp, 10, false },
+ { 0x0154, "LE Multi Advertise",
+ le_multi_advt_cmd, 1, false,
+ le_multi_advt_rsp, 2, true },
+ { 0x0155, "LE RPA Offload",
+ le_rpa_offload_cmd, 1, false,
+ le_rpa_offload_rsp, 2, false },
+#if 0
+ { 0x0156, "LE Batch Scan",
+ le_batch_scan_cmd, 1, false,
+ le_batch_scan_rsp, 2, true },
+#endif
+ { 0x0157, "LE APCF",
+ le_apcf_cmd, 1, false,
+ le_apcf_rsp, 2, false },
+#endif
{ }
};
@@ -234,7 +940,59 @@ void broadcom_lm_diag(const void *data, uint8_t size)
}
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+struct hci_vse_sec_brcm_link_loss_dbg_info{
+ unsigned char linklost_status;
+ unsigned char conn_handle;
+ char trans_pwr;
+ char rssi;
+ unsigned char ch_map[10];
+ unsigned char lmp_cmd[4];
+} __packed;
+
+static void linkloss_evt(const void *data, uint8_t size)
+{
+
+ struct hci_vse_sec_brcm_link_loss_dbg_info *ev = (void *) data;
+ char *status = NULL;
+ switch (ev->linklost_status) {
+ case 0:
+ status = "BT_Link_Supervision_Timeout";
+ break;
+ case 1:
+ status = "LE_Link_Supervision_Timeout";
+ break;
+ case 2:
+ status = "BT_LMP_Timeout_Local";
+ break;
+ case 3:
+ status = "BT_LMP_Timeout_Remote";
+ break;
+ case 4:
+ status = "LE_LMP_Timeout";
+ break;
+ case 5:
+ status = "Page_Timeout";
+ break;
+ default :
+ break;
+ }
+
+ print_field("Status:%s,Handle:%02x,Trans_Pwr:%d,RSSI:%d"
+ " Ch_map:%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x"
+ " LMP_cmd:0x%x%x%x%x",
+ status, ev->conn_handle, ev->trans_pwr, ev->rssi,
+ ev->ch_map[0], ev->ch_map[1], ev->ch_map[2], ev->ch_map[3],
+ ev->ch_map[4], ev->ch_map[5], ev->ch_map[6], ev->ch_map[7],
+ ev->ch_map[8], ev->ch_map[9], ev->lmp_cmd[0], ev->lmp_cmd[1],
+ ev->lmp_cmd[2], ev->lmp_cmd[3]);
+}
+#endif
+
static const struct vendor_evt vendor_evt_table[] = {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ { 0x76, "SEC Link Loss", linkloss_evt, 18, true },
+#endif
{ }
};
diff --git a/monitor/control.c b/monitor/control.c
index 9bbdc37d..4262e43b 100755
--- a/monitor/control.c
+++ b/monitor/control.c
@@ -1364,9 +1364,18 @@ int control_tty(const char *path, unsigned int speed)
return 0;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+bool control_writer(const char *path, int16_t rotate_count, ssize_t file_size)
+#else
bool control_writer(const char *path)
+#endif
{
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ btsnoop_file = btsnoop_create(path, BTSNOOP_FORMAT_MONITOR,
+ rotate_count, file_size);
+#else
btsnoop_file = btsnoop_create(path, BTSNOOP_FORMAT_MONITOR);
+#endif
return !!btsnoop_file;
}
@@ -1396,6 +1405,9 @@ void control_reader(const char *path)
break;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ setenv("PAGER", "cat", 0);
+#endif
open_pager();
switch (format) {
diff --git a/monitor/control.h b/monitor/control.h
index 630a852e..cd66441e 100755
--- a/monitor/control.h
+++ b/monitor/control.h
@@ -24,7 +24,11 @@
#include <stdint.h>
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+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_tty(const char *path, unsigned int speed);
diff --git a/monitor/display.h b/monitor/display.h
index b85f37b9..1f54c6c1 100755
--- a/monitor/display.h
+++ b/monitor/display.h
@@ -48,7 +48,11 @@ bool use_color(void);
#define COLOR_INFO COLOR_OFF
#define COLOR_DEBUG COLOR_WHITE
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define FALLBACK_TERMINAL_WIDTH 130
+#else
#define FALLBACK_TERMINAL_WIDTH 80
+#endif
#define print_indent(indent, color1, prefix, title, color2, fmt, args...) \
do { \
diff --git a/monitor/main.c b/monitor/main.c
index f9bca221..57830ada 100755
--- a/monitor/main.c
+++ b/monitor/main.c
@@ -70,6 +70,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_FEATURE_BLUEZ_MODIFY
+ "\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");
}
@@ -86,6 +90,10 @@ static const struct option main_options[] = {
{ "date", no_argument, NULL, 'T' },
{ "sco", no_argument, NULL, 'S' },
{ "ellisys", required_argument, NULL, 'E' },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ { "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' },
@@ -102,6 +110,10 @@ int main(int argc, char *argv[])
const char *tty = NULL;
unsigned int tty_speed = B115200;
unsigned short ellisys_port = 0;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ int16_t rotate_count = -1;
+ ssize_t file_size = -1;
+#endif
const char *str;
int exit_status;
sigset_t mask;
@@ -113,8 +125,13 @@ int main(int argc, char *argv[])
for (;;) {
int opt;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ opt = getopt_long(argc, argv, "d:r:w:a:s:p:i:tTSE:C:W:vh",
+ main_options, NULL);
+#else
opt = getopt_long(argc, argv, "d:r:w:a:s:p:i:tTSE:vh",
main_options, NULL);
+#endif
if (opt < 0)
break;
@@ -171,6 +188,14 @@ int main(int argc, char *argv[])
ellisys_server = optarg;
ellisys_port = 24352;
break;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ case 'C':
+ rotate_count = atoi(optarg);
+ break;
+ case 'W':
+ file_size = atoll(optarg);
+ break;
+#endif
case '#':
packet_todo();
lmp_todo();
@@ -221,10 +246,18 @@ int main(int argc, char *argv[])
return EXIT_SUCCESS;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ 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 6272562d..259b805b 100755
--- a/monitor/packet.c
+++ b/monitor/packet.c
@@ -7413,6 +7413,10 @@ static const char *current_vendor_str(void)
return "Intel";
case 15:
return "Broadcom";
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ default:
+ return "Unknown";
+#endif
}
return NULL;
@@ -7432,6 +7436,10 @@ static const struct vendor_ocf *current_vendor_ocf(uint16_t ocf)
return intel_vendor_ocf(ocf);
case 15:
return broadcom_vendor_ocf(ocf);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ default:
+ return broadcom_vendor_ocf(ocf);
+#endif
}
return NULL;
@@ -7451,6 +7459,10 @@ static const struct vendor_evt *current_vendor_evt(uint8_t evt)
return intel_vendor_evt(evt);
case 15:
return broadcom_vendor_evt(evt);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ default:
+ return broadcom_vendor_evt(evt);
+#endif
}
return NULL;
@@ -11695,3 +11707,30 @@ void packet_todo(void)
printf("\t%s\n", le_meta_event_table[i].str);
}
}
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void print_le_set_adv_parameters_cmd(const void *data, uint8_t size)
+{
+ le_set_adv_parameters_cmd(data, size);
+}
+
+void print_le_set_random_address_cmd(const void *data, uint8_t size)
+{
+ le_set_random_address_cmd(data, size);
+}
+
+void print_le_set_adv_data_cmd(const void *data, uint8_t size)
+{
+ le_set_adv_data_cmd(data, size);
+}
+
+void print_le_set_scan_rsp_data_cmd(const void *data, uint8_t size)
+{
+ le_set_scan_rsp_data_cmd(data, size);
+}
+
+void print_le_set_adv_enable_cmd(const void *data, uint8_t size)
+{
+ le_set_adv_enable_cmd(data, size);
+}
+#endif
diff --git a/monitor/packet.h b/monitor/packet.h
index 354f4fee..20e17182 100755
--- a/monitor/packet.h
+++ b/monitor/packet.h
@@ -99,3 +99,11 @@ void packet_ctrl_event(struct timeval *tv, struct ucred *cred, uint16_t index,
const void *data, uint16_t size);
void packet_todo(void);
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void print_le_set_adv_parameters_cmd(const void *data, uint8_t size);
+void print_le_set_random_address_cmd(const void *data, uint8_t size);
+void print_le_set_adv_data_cmd(const void *data, uint8_t size);
+void print_le_set_scan_rsp_data_cmd(const void *data, uint8_t size);
+void print_le_set_adv_enable_cmd(const void *data, uint8_t size);
+#endif
diff --git a/obexd/client/bluetooth.c b/obexd/client/bluetooth.c
index e35124a0..a755b8c4 100755
--- a/obexd/client/bluetooth.c
+++ b/obexd/client/bluetooth.c
@@ -186,10 +186,21 @@ static void search_callback(uint8_t type, uint16_t status,
protos = NULL;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ /* Use obex over l2cap only if obex over rfcomm is not there */
+ if (ch == -1) {
+ data = sdp_data_get(rec, 0x0200);
+ /* PSM must be odd and lsb of upper byte must be 0 */
+ if (data != NULL && (data->val.uint16 & 0x0101) ==
+ 0x0001)
+ ch = data->val.uint16;
+ }
+#else
data = sdp_data_get(rec, 0x0200);
/* PSM must be odd and lsb of upper byte must be 0 */
if (data != NULL && (data->val.uint16 & 0x0101) == 0x0001)
ch = data->val.uint16;
+#endif
/* Cache the sdp record associated with the service that we
* attempt to connect. This allows reading its application
diff --git a/obexd/client/manager.c b/obexd/client/manager.c
index fbcad6da..cc472e47 100755
--- a/obexd/client/manager.c
+++ b/obexd/client/manager.c
@@ -46,7 +46,9 @@
#include "sync.h"
#include "map.h"
#include "manager.h"
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#include "mns-tizen.h"
+#endif
#define CLIENT_INTERFACE "org.bluez.obex.Client1"
#define ERROR_INTERFACE "org.bluez.obex.Error"
#define CLIENT_PATH "/org/bluez/obex"
@@ -64,11 +66,21 @@ static void shutdown_session(struct obc_session *session)
obc_session_unref(session);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void release_session(struct obc_session *session)
+{
+ DBG("+");
+ sessions = g_slist_remove(sessions, session);
+ shutdown_session(session);
+ DBG("-");
+}
+#else
static void release_session(struct obc_session *session)
{
sessions = g_slist_remove(sessions, session);
shutdown_session(session);
}
+#endif
static void unregister_session(void *data)
{
@@ -154,7 +166,10 @@ static int parse_device_dict(DBusMessageIter *iter,
static struct obc_session *find_session(const char *path)
{
GSList *l;
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if(!path)
+ return NULL;
+#endif
for (l = sessions; l; l = l->next) {
struct obc_session *session = l->data;
@@ -238,9 +253,16 @@ static DBusMessage *remove_session(DBusConnection *connection,
ERROR_INTERFACE ".NotAuthorized",
"Not Authorized");
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ obc_session_update(session, message, connection);
+#endif
release_session(session);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ return NULL;
+#else
return dbus_message_new_method_return(message);
+#endif
}
static const GDBusMethodTable client_methods[] = {
@@ -265,6 +287,9 @@ static struct obc_module {
{ "pbap", pbap_init, pbap_exit },
{ "sync", sync_init, sync_exit },
{ "map", map_init, map_exit },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ { "mns", mns_init, mns_exit },
+#endif
{ }
};
diff --git a/obexd/client/mns-tizen.c b/obexd/client/mns-tizen.c
new file mode 100755
index 00000000..c6da1eb1
--- /dev/null
+++ b/obexd/client/mns-tizen.c
@@ -0,0 +1,271 @@
+/*
+ *
+ * OBEX Client
+ *
+ * Copyright (C) 2012 Samsung Electronics Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <glib.h>
+#include <gdbus.h>
+#include <string.h>
+
+#include "log.h"
+
+#include "transfer.h"
+#include "session.h"
+#include "driver.h"
+#include "map_ap.h"
+#include "mns-tizen.h"
+#include "gobex/gobex-apparam.h"
+
+#define OBEX_MNS_UUID \
+ "\xBB\x58\x2B\x41\x42\x0C\x11\xDB\xB0\xDE\x08\x00\x20\x0C\x9A\x66"
+#define OBEX_MNS_UUID_LEN 16
+
+#define MNS_INTERFACE "org.openobex.MessageNotification"
+#define ERROR_INF MNS_INTERFACE ".Error"
+#define MNS_UUID "00001133-0000-1000-8000-00805f9b34fb"
+
+enum msg_event_type {
+ EVENT_TYPE_NEW_MESSAGE,
+ EVENT_TYPE_DELIVERY_SUCCESS,
+ EVENT_TYPE_SENDING_SUCCESS,
+ EVENT_TYPE_DELIVERY_FAILURE,
+ EVENT_TYPE_SENDING_FAILURE,
+ EVENT_TYPE_MEMORY_FULL,
+ EVENT_TYPE_MEMORY_AVAILABLE,
+ EVENT_TYPE_MESSAGE_DELETED,
+ EVENT_TYPE_MESSAGE_SHIFT,
+ EVENT_TYPE_UNKNOWN,
+};
+
+struct mns_data {
+ struct obc_session *session;
+ DBusMessage *msg;
+};
+
+static DBusConnection *conn = NULL;
+
+static int get_event_type(gchar *event_type)
+{
+ DBG("event_type = %s\n", event_type);
+
+ if (!g_strcmp0(event_type, "NewMessage"))
+ return EVENT_TYPE_NEW_MESSAGE;
+ else if (!g_strcmp0(event_type, "DeliverySuccess"))
+ return EVENT_TYPE_DELIVERY_SUCCESS;
+ else if (!g_strcmp0(event_type, "SendingSuccess"))
+ return EVENT_TYPE_SENDING_SUCCESS;
+ else if (!g_strcmp0(event_type, "DeliveryFailure"))
+ return EVENT_TYPE_DELIVERY_FAILURE;
+ else if (!g_strcmp0(event_type, "SendingFailure"))
+ return EVENT_TYPE_SENDING_FAILURE;
+ else if (!g_strcmp0(event_type, "MemoryFull"))
+ return EVENT_TYPE_MEMORY_FULL;
+ else if (!g_strcmp0(event_type, "MemoryAvailable"))
+ return EVENT_TYPE_MEMORY_AVAILABLE;
+ else if (!g_strcmp0(event_type, "MessageDeleted"))
+ return EVENT_TYPE_MESSAGE_DELETED;
+ else if (!g_strcmp0(event_type, "MessageShift"))
+ return EVENT_TYPE_MESSAGE_SHIFT;
+ else
+ return EVENT_TYPE_UNKNOWN;
+
+}
+
+static gchar *generate_event_report(gchar *event_type,
+ guint64 handle, gchar *folder,
+ gchar *old_folder, gchar *msg_type)
+{
+ GString *buf;
+ int event;
+
+ event = get_event_type(event_type);
+ if (event == EVENT_TYPE_UNKNOWN)
+ return NULL;
+
+ buf = g_string_new("<MAP-event-report version=\"1.0\">");
+ g_string_append_printf(buf, "<event type=\"%s\" ", event_type);
+
+ if (event == EVENT_TYPE_MEMORY_FULL ||
+ event == EVENT_TYPE_MEMORY_AVAILABLE)
+ goto done;
+
+ g_string_append_printf(buf, "handle=\"%llx\" ", handle);
+ g_string_append_printf(buf, "folder=\"%s\" ", folder);
+
+ if (event == EVENT_TYPE_MESSAGE_SHIFT)
+ g_string_append_printf(buf, " old_folder=\"%s\" ", old_folder);
+
+ g_string_append_printf(buf, "msg_type=\"%s\" ", msg_type);
+
+done:
+ g_string_append(buf, "/></MAP-event-report>");
+
+ return g_string_free(buf, FALSE);
+}
+
+static DBusMessage *send_event(DBusConnection *connection,
+ DBusMessage *message, void *user_data)
+{
+ struct mns_data *mns = user_data;
+ struct obc_transfer *transfer;
+ GObexApparam *apparam;
+ gchar *event_type;
+ gchar *folder;
+ gchar *old_folder;
+ gchar *msg_type;
+ gchar *buf;
+ guint64 handle;
+ GError *err;
+ DBusMessage *reply;
+
+ if (dbus_message_get_args(message, NULL,
+ DBUS_TYPE_STRING, &event_type,
+ DBUS_TYPE_UINT64, &handle,
+ DBUS_TYPE_STRING, &folder,
+ DBUS_TYPE_STRING, &old_folder,
+ DBUS_TYPE_STRING, &msg_type,
+ DBUS_TYPE_INVALID) == FALSE)
+ return g_dbus_create_error(message,
+ "org.openobex.Error.InvalidArguments",
+ NULL);
+
+ buf = generate_event_report(event_type, handle, folder,
+ old_folder, msg_type);
+ if (!buf)
+ return g_dbus_create_error(message,
+ "org.openobex.Error.InvalidArguments", NULL);
+
+ transfer = obc_transfer_put("x-bt/MAP-event-report", NULL, NULL,
+ buf, strlen(buf), &err);
+
+ g_free(buf);
+
+ if (transfer == NULL)
+ goto fail;
+
+ /* Obexd currently supports single SDP for MAS */
+ apparam = g_obex_apparam_set_uint8(NULL, MAP_AP_MASINSTANCEID, 0);
+
+ obc_transfer_set_apparam(transfer, apparam);
+
+ if (obc_session_queue(mns->session, transfer, NULL, NULL, &err))
+ return dbus_message_new_method_return(message);
+
+fail:
+ reply = g_dbus_create_error(message, ERROR_INF ".Failed", "%s",
+ err->message);
+ g_error_free(err);
+ return reply;
+}
+
+static GDBusMethodTable mns_methods[] = {
+ { GDBUS_ASYNC_METHOD("SendEvent",
+ GDBUS_ARGS({ "event_type", "s" }, { "handle", "t" },
+ { "folder", "s" }, { "old_folder", "s" },
+ { "msg_type", "s" }),
+ NULL,
+ send_event) },
+ { }
+};
+
+static void mns_free(void *data)
+{
+ struct mns_data *mns = data;
+
+ obc_session_unref(mns->session);
+ g_free(mns);
+}
+
+static int mns_probe(struct obc_session *session)
+{
+ struct mns_data *mns;
+ const char *path;
+
+ path = obc_session_get_path(session);
+
+ DBG("%s", path);
+
+ mns = g_try_new0(struct mns_data, 1);
+ if (!mns)
+ return -ENOMEM;
+
+ mns->session = obc_session_ref(session);
+
+ if (!g_dbus_register_interface(conn, path, MNS_INTERFACE, mns_methods,
+ NULL, NULL, mns, mns_free)) {
+ mns_free(mns);
+ return -ENOMEM;
+ }
+
+ return 0;
+}
+
+static void mns_remove(struct obc_session *session)
+{
+ const char *path = obc_session_get_path(session);
+
+ DBG("%s", path);
+
+ g_dbus_unregister_interface(conn, path, MNS_INTERFACE);
+}
+
+static struct obc_driver mns = {
+ .service = "MNS",
+ .uuid = MNS_UUID,
+ .target = OBEX_MNS_UUID,
+ .target_len = OBEX_MNS_UUID_LEN,
+ .probe = mns_probe,
+ .remove = mns_remove
+};
+
+int mns_init(void)
+{
+ int err;
+
+ DBG("");
+
+ conn = dbus_bus_get(DBUS_BUS_SESSION, NULL);
+ if (!conn)
+ return -EIO;
+
+ err = obc_driver_register(&mns);
+ if (err < 0) {
+ dbus_connection_unref(conn);
+ conn = NULL;
+ return err;
+ }
+
+ return 0;
+}
+
+void mns_exit(void)
+{
+ DBG("");
+
+ dbus_connection_unref(conn);
+ conn = NULL;
+
+ obc_driver_unregister(&mns);
+}
diff --git a/obexd/client/mns-tizen.h b/obexd/client/mns-tizen.h
new file mode 100755
index 00000000..e4ee5e54
--- /dev/null
+++ b/obexd/client/mns-tizen.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * OBEX Client
+ *
+ * Copyright (C) 2012 Samsung Electronics Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+int mns_init(void);
+void mns_exit(void);
diff --git a/obexd/client/opp.c b/obexd/client/opp.c
index 92785f66..9a6cf34c 100755
--- a/obexd/client/opp.c
+++ b/obexd/client/opp.c
@@ -55,16 +55,26 @@ static DBusMessage *opp_send_file(DBusConnection *connection,
char *filename;
char *basename;
GError *err = NULL;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ char *mimetype = NULL;
+#endif
if (dbus_message_get_args(message, NULL,
DBUS_TYPE_STRING, &filename,
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ DBUS_TYPE_STRING, &mimetype,
+#endif
DBUS_TYPE_INVALID) == FALSE)
return g_dbus_create_error(message,
ERROR_INTERFACE ".InvalidArguments", NULL);
basename = g_path_get_basename(filename);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ transfer = obc_transfer_put(mimetype, basename, filename, NULL, 0, &err);
+#else
transfer = obc_transfer_put(NULL, basename, filename, NULL, 0, &err);
+#endif
g_free(basename);
@@ -123,7 +133,11 @@ static DBusMessage *opp_exchange_business_cards(DBusConnection *connection,
static const GDBusMethodTable opp_methods[] = {
{ GDBUS_METHOD("SendFile",
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ GDBUS_ARGS({ "sourcefile", "s" }, { "mimetype", "s" }),
+#else
GDBUS_ARGS({ "sourcefile", "s" }),
+#endif
GDBUS_ARGS({ "transfer", "o" }, { "properties", "a{sv}" }),
opp_send_file) },
{ GDBUS_METHOD("PullBusinessCard",
diff --git a/obexd/client/pbap.c b/obexd/client/pbap.c
index 1ab34a7d..db46a803 100755
--- 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_FEATURE_BLUEZ_MODIFY
+ tmp = g_strdup("SIM1");
+#else
tmp = g_strdup("sim1");
+#endif
else
tmp = g_ascii_strup(location, 4);
@@ -227,6 +231,11 @@ static char *build_phonebook_path(const char *location, const char *item)
} else
return NULL;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (!g_ascii_strcasecmp(item, "nil"))
+ return path;
+#endif
+
if (!g_ascii_strcasecmp(item, "pb") ||
!g_ascii_strcasecmp(item, "ich") ||
!g_ascii_strcasecmp(item, "och") ||
@@ -358,7 +367,11 @@ static void read_return_apparam(struct obc_transfer *transfer,
g_obex_apparam_get_uint16(apparam, PHONEBOOKSIZE_TAG,
phone_book_size);
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
+ g_obex_apparam_get_uint16(apparam, NEWMISSEDCALLS_TAG,
+#else
g_obex_apparam_get_uint8(apparam, NEWMISSEDCALLS_TAG,
+#endif
new_missed_calls);
read_version(pbap, apparam);
@@ -493,8 +506,14 @@ static GObexApparam *parse_offset(GObexApparam *apparam, DBusMessageIter *iter)
{
guint16 num;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16 &&
+ dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT32)
+ return NULL;
+#else
if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16)
return NULL;
+#endif
dbus_message_iter_get_basic(iter, &num);
@@ -506,8 +525,14 @@ static GObexApparam *parse_max_count(GObexApparam *apparam,
{
guint16 num;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16 &&
+ dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT32)
+ return NULL;
+#else
if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16)
return NULL;
+#endif
dbus_message_iter_get_basic(iter, &num);
@@ -737,9 +762,16 @@ static DBusMessage *pbap_select(DBusConnection *connection,
}
request = pending_request_new(pbap, message);
-
- obc_session_setpath(pbap->session, path, pbap_setpath_cb, request,
- &err);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (pbap->path == NULL || strlen(pbap->path) == 0)
+ obc_session_setpath(pbap->session, path + 1, pbap_setpath_cb, request,
+ &err);
+ else
+ obc_session_setpath(pbap->session, path, pbap_setpath_cb, request,
+ &err);
+#else
+ obc_session_setpath(pbap->session, path, pbap_setpath_cb, request, &err);
+#endif
if (err != NULL) {
DBusMessage *reply;
reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed",
@@ -870,6 +902,9 @@ static DBusMessage *pbap_list(DBusConnection *connection,
struct pbap_data *pbap = user_data;
GObexApparam *apparam;
DBusMessageIter args;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ const char *pb_folder;
+#endif
if (!pbap->path)
return g_dbus_create_error(message,
@@ -878,6 +913,15 @@ static DBusMessage *pbap_list(DBusConnection *connection,
dbus_message_iter_init(message, &args);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING)
+ return g_dbus_create_error(message,
+ ERROR_INTERFACE ".InvalidArguments", NULL);
+
+ dbus_message_iter_get_basic(&args, &pb_folder);
+ dbus_message_iter_next(&args);
+#endif
+
apparam = g_obex_apparam_set_uint16(NULL, MAXLISTCOUNT_TAG,
DEFAULT_COUNT);
apparam = g_obex_apparam_set_uint16(apparam, LISTSTARTOFFSET_TAG,
@@ -889,7 +933,11 @@ static DBusMessage *pbap_list(DBusConnection *connection,
ERROR_INTERFACE ".InvalidArguments", NULL);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ return pull_vcard_listing(pbap, message, pb_folder, apparam);
+#else
return pull_vcard_listing(pbap, message, "", apparam);
+#endif
}
static GObexApparam *parse_attribute(GObexApparam *apparam, const char *field)
@@ -1049,7 +1097,11 @@ static const GDBusMethodTable pbap_methods[] = {
{ "properties", "a{sv}" }),
pbap_pull_vcard) },
{ GDBUS_ASYNC_METHOD("List",
- GDBUS_ARGS({ "filters", "a{sv}" }),
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ GDBUS_ARGS({ "folder", "s" }, {"filters", "a{sv}" }),
+#else
+ GDBUS_ARGS({"filters", "a{sv}" }),
+#endif
GDBUS_ARGS({ "vcard_listing", "a(ss)" }),
pbap_list) },
{ GDBUS_ASYNC_METHOD("Search",
diff --git a/obexd/client/session.c b/obexd/client/session.c
index 5f981bf5..6c7c3374 100755
--- a/obexd/client/session.c
+++ b/obexd/client/session.c
@@ -43,6 +43,9 @@
#include "dbus.h"
#include "transfer.h"
#include "session.h"
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#include "manager.h"
+#endif
#include "driver.h"
#include "transport.h"
@@ -114,6 +117,10 @@ struct obc_session {
guint process_id;
char *folder;
struct callback_data *callback;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ DBusMessage *message;
+ DBusConnection *connection;
+#endif
};
static GSList *sessions = NULL;
@@ -139,6 +146,17 @@ struct obc_session *obc_session_ref(struct obc_session *session)
return session;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void obc_session_update(struct obc_session *session, DBusMessage *message,
+ DBusConnection *connection)
+{
+ DBG("+");
+ session->message = dbus_message_ref(message);
+ session->connection = dbus_connection_ref(connection);
+ DBG("-");
+}
+#endif
+
static void session_unregistered(struct obc_session *session)
{
char *path;
@@ -263,6 +281,18 @@ static void disconnect_complete(GObex *obex, GError *err, GObexPacket *rsp,
if (err)
error("%s", err->message);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (session->message) {
+ /* Dbus reply need to be done */
+ DBG("Dbus reply for remove_session");
+ g_dbus_send_reply(session->connection, session->message,
+ DBUS_TYPE_INVALID);
+ dbus_message_unref(session->message);
+ dbus_connection_unref(session->connection);
+ session->message = NULL;
+ session->connection = NULL;
+ }
+#endif
/* Disconnect transport */
if (session->id > 0 && session->transport != NULL) {
session->transport->disconnect(session->id);
@@ -350,8 +380,11 @@ static void session_disconnected(GObex *obex, GError *err, gpointer user_data)
if (err)
error("%s", err->message);
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ release_session(session);
+#else
obc_session_shutdown(session);
+#endif
}
static void transport_func(GIOChannel *io, GError *err, gpointer user_data)
diff --git a/obexd/client/session.h b/obexd/client/session.h
index b561b7e5..2ce9f3ff 100755
--- a/obexd/client/session.h
+++ b/obexd/client/session.h
@@ -80,3 +80,8 @@ guint obc_session_delete(struct obc_session *session, const char *file,
GError **err);
void obc_session_cancel(struct obc_session *session, guint id,
gboolean remove);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void release_session(struct obc_session *session);
+void obc_session_update(struct obc_session *session, DBusMessage *message,
+ DBusConnection *connection);
+#endif
diff --git a/obexd/client/transfer.c b/obexd/client/transfer.c
index 092e72fe..a5ecd13e 100755
--- a/obexd/client/transfer.c
+++ b/obexd/client/transfer.c
@@ -787,12 +787,26 @@ static gboolean report_progress(gpointer data)
if (transfer->transferred == transfer->progress)
return TRUE;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (transfer->transferred == transfer->size) {
+ transfer->progress_id = 0;
+ if(transfer->progress == 0) {
+ transfer->progress = transfer->transferred;
+ transfer_set_status(transfer, TRANSFER_STATUS_ACTIVE);
+ }
+
+ return FALSE;
+ }
+
+ transfer->progress = transfer->transferred;
+#else
transfer->progress = transfer->transferred;
if (transfer->transferred == transfer->size) {
transfer->progress_id = 0;
return FALSE;
}
+#endif
if (transfer->status != TRANSFER_STATUS_ACTIVE &&
transfer->status != TRANSFER_STATUS_SUSPENDED)
@@ -884,8 +898,13 @@ static gboolean transfer_start_put(struct obc_transfer *transfer, GError **err)
if (transfer->path == NULL)
return TRUE;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ transfer->progress_id = g_timeout_add(10, report_progress,
+ transfer);
+#else
transfer->progress_id = g_timeout_add_seconds(1, report_progress,
transfer);
+#endif
return TRUE;
}
diff --git a/obexd/plugins/ftp.c b/obexd/plugins/ftp.c
index 3ee18a61..85756a08 100755
--- a/obexd/plugins/ftp.c
+++ b/obexd/plugins/ftp.c
@@ -170,6 +170,13 @@ int ftp_chkput(struct obex_session *os, void *user_data)
{
struct ftp_session *ftp = user_data;
const char *name = obex_get_name(os);
+#if 0
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ char *folder;
+ int32_t time;
+ int err;
+#endif
+#endif
char *path;
int ret;
@@ -184,6 +191,15 @@ int ftp_chkput(struct obex_session *os, void *user_data)
if (obex_get_size(os) == OBJECT_SIZE_DELETE)
return 0;
+#if 0
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ time = 0;
+ err = manager_request_authorization(os, time, &folder, &name);
+ if (err < 0)
+ return -EPERM;
+#endif
+#endif
+
path = g_build_filename(ftp->folder, name, NULL);
ret = obex_put_stream_start(os, path);
@@ -219,6 +235,36 @@ int ftp_put(struct obex_session *os, void *user_data)
return 0;
}
+#if 0
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static gboolean is_valid_name(const char *name)
+{
+ char *forbid_chars = "[*\"<>;?|\\^:/]";
+ int exp;
+ regex_t reg;
+ regmatch_t match[1];
+ size_t size = 1;
+
+ if (name[0] == '.')
+ return FALSE;
+
+ exp = regcomp(&reg, forbid_chars, 0);
+
+ if (exp != 0)
+ return FALSE;
+
+ exp = regexec(&reg, name, size, match, 0);
+
+ regfree(&reg);
+
+ if (exp != REG_NOMATCH)
+ return FALSE;
+
+ return TRUE;
+}
+#endif
+#endif
+
int ftp_setpath(struct obex_session *os, void *user_data)
{
struct ftp_session *ftp = user_data;
@@ -273,6 +319,16 @@ int ftp_setpath(struct obex_session *os, void *user_data)
return -EPERM;
}
+#if 0
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ /* Check if the folder name is valid or not */
+ if (!is_valid_name(name)) {
+ error("Set path failed: Invalid folder name!");
+ return -EINVAL;
+ }
+#endif
+#endif
+
fullname = g_build_filename(ftp->folder, name, NULL);
DBG("Fullname: %s", fullname);
diff --git a/obexd/plugins/messages-tizen.c b/obexd/plugins/messages-tizen.c
new file mode 100755
index 00000000..e59c37b1
--- /dev/null
+++ b/obexd/plugins/messages-tizen.c
@@ -0,0 +1,1547 @@
+/*
+ *
+ * OBEX Server
+ *
+ * Copyright (C) 2012 Samsung Electronics Co., Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <glib.h>
+#include <string.h>
+#include <stdio.h>
+#include "log.h"
+#include "messages.h"
+#include "../../profile.h"
+
+#include <dbus/dbus.h>
+
+#define QUERY_GET_FOLDER_TREE "GetFolderTree"
+#define QUERY_GET_MSG_LIST "GetMessageList"
+#define QUERY_GET_MESSAGE "GetMessage"
+#define QUERY_PUSH_MESSAGE "PushMessage"
+#define QUERY_PUSH_MESSAGE_DATA "PushMessageData"
+#define QUERY_UPDATE_MESSAGE "UpdateMessage"
+#define QUERY_SET_READ_STATUS "SetReadStatus"
+#define QUERY_SET_DELETE_STATUS "SetDeleteStatus"
+#define QUERY_NOTI_REGISTRATION "NotiRegistration"
+#define QUERY_DESTROY_AGENT "DestroyAgent"
+
+#define BT_MAP_SERVICE_OBJECT_PATH "/org/bluez/map_agent"
+#define BT_MAP_SERVICE_NAME "org.bluez.map_agent"
+#define BT_MAP_SERVICE_INTERFACE "org.bluez.MapAgent"
+
+/* Added as per MAP specification */
+#define BT_MAP_LIST_ITEM_MAX_LEN 256
+
+static DBusConnection *g_conn = NULL;
+
+struct mns_reg_data {
+ uint8_t notification_status;
+ char *remote_addr;
+};
+
+struct message_folder {
+ char *name;
+ GSList *subfolders;
+};
+
+struct session {
+ char *cwd;
+ struct message_folder *folder;
+ char *name;
+ uint16_t max;
+ uint16_t offset;
+ void *user_data;
+ struct messages_filter *filter;
+ struct messages_message *msg;
+ void (*folder_list_cb)(void *session, int err, uint16_t size,
+ const char *name, void *user_data);
+ void (*msg_list_cb)(void *session, int err, int size, gboolean newmsg,
+ const struct messages_message *entry,
+ void *user_data);
+ void (*push_msg_cb)(void *session, int err, guint64 handle,
+ void *user_data);
+ void (*get_msg_cb)(void *session, int err, gboolean fmore,
+ const char *chunk, void *user_data);
+ void (*msg_update_cb)(void *session, int err, void *user_data);
+ void (*msg_status_cb)(void *session, int err, void *user_data);
+};
+
+static struct message_folder *folder_tree = NULL;
+
+static void message_list_item_free(struct messages_message *data)
+{
+ DBG("+");
+ g_free(data->handle);
+ data->handle = NULL;
+
+ g_free(data->subject);
+ data->subject = NULL;
+
+ g_free(data->datetime);
+ data->datetime = NULL;
+
+ g_free(data->sender_name);
+ data->sender_name = NULL;
+
+ g_free(data->sender_addressing);
+ data->sender_addressing = NULL;
+
+ g_free(data->replyto_addressing);
+ data->replyto_addressing = NULL;
+
+ g_free(data->recipient_name);
+ data->recipient_name = NULL;
+
+ g_free(data->recipient_addressing);
+ data->recipient_addressing = NULL;
+
+ g_free(data->type);
+ data->type = NULL;
+
+ g_free(data->reception_status);
+ data->reception_status = NULL;
+
+ g_free(data->size);
+ data->size = NULL;
+
+ g_free(data->attachment_size);
+ data->attachment_size = NULL;
+ DBG("-");
+}
+
+static void session_filter_free(struct messages_filter *data)
+{
+ DBG("+");
+ if (NULL == data)
+ return;
+
+ g_free((gpointer)data->period_begin);
+ g_free((gpointer)data->period_end);
+ g_free((gpointer)data->recipient);
+ g_free((gpointer)data->originator);
+ g_free(data);
+ DBG("-");
+}
+
+static gboolean is_time_in_period(char *ref_time, char *period)
+{
+ guint64 ref_date_val;
+ guint64 date_val;
+
+ guint64 ref_time_val;
+ guint64 time_val;
+
+ char *start;
+ char *end = NULL;
+
+ char temp[20];
+
+ start = strtok_r(ref_time, "T", &end);
+ if (NULL == start || NULL == end)
+ return FALSE;
+
+ snprintf(temp, sizeof(temp), "%s", start);
+ ref_date_val = g_ascii_strtoull(temp, NULL, 16);
+ snprintf(temp, sizeof(temp), "%s", end);
+ ref_time_val = g_ascii_strtoull(temp, NULL, 16);
+
+ start = strtok_r(period, "T", &end);
+ if (NULL == start || NULL == end)
+ return FALSE;
+
+ snprintf(temp, sizeof(temp), "%s", start);
+ date_val = g_ascii_strtoull(temp, NULL, 16);
+ snprintf(temp, sizeof(temp), "%s", end);
+ time_val = g_ascii_strtoull(temp, NULL, 16);
+
+ if (ref_date_val < date_val) {
+ return TRUE;
+ } else if (ref_date_val > date_val) {
+ return FALSE;
+ } else {
+ if (ref_time_val <= time_val)
+ return TRUE;
+ else
+ return FALSE;
+ }
+}
+
+static gboolean __filter_timebased(char *begin, char *end, char *time)
+{
+ gboolean ret = 0;
+
+ if (!begin && !end) {
+ /* If start and stop are not specified
+ do not filter. */
+
+ return TRUE;
+ } else if (!end && begin) {
+ /* If "FilterPeriodEnd" is not specified the returned
+ message listing shall include the messages from
+ "FilterPeriodBegin" to current time */
+
+ return is_time_in_period(begin, time);
+ } else if (!begin && end) {
+ /* If "FilterPeriodBegin" is not specified the returned
+ message listing shall include the messages older than
+ "FilterPeriodEnd" */
+
+ return is_time_in_period(time, end);
+ } else {
+
+ if (TRUE == is_time_in_period(end, begin))
+ return FALSE;
+
+ ret = is_time_in_period(begin, time);
+ if (ret == TRUE)
+ return is_time_in_period(time, end);
+ else
+ return FALSE;
+ }
+}
+
+static uint8_t get_type_val(const char *type)
+{
+ if (!g_strcmp0(type, "SMS_GSM"))
+ return 0x01;
+ else if (!g_strcmp0(type, "SMS_CDMA"))
+ return 0x02;
+ else if (!g_strcmp0(type, "EMAIL"))
+ return 0x04;
+ else if (!g_strcmp0(type, "MMS"))
+ return 0x08;
+ else
+ return 0x00;
+}
+
+static uint8_t get_read_status_val(gboolean read)
+{
+ if (read)
+ return 0x02; /* Read messages */
+ else
+ return 0x01; /* Unread messages */
+}
+
+static uint8_t get_priority_val(gboolean priority)
+{
+ if (priority)
+ return 0x01; /* High priority */
+ else
+ return 0x02; /* Low priority */
+}
+
+static struct message_folder *get_folder(const char *folder)
+{
+ GSList *folders = folder_tree->subfolders;
+ struct message_folder *last = NULL;
+ char **path;
+ int i;
+
+ if (g_strcmp0(folder, "/") == 0)
+ return folder_tree;
+
+ path = g_strsplit(folder, "/", 0);
+
+ for (i = 1; path[i] != NULL; i++) {
+ gboolean match_found = FALSE;
+ GSList *l;
+
+ for (l = folders; l != NULL; l = g_slist_next(l)) {
+ struct message_folder *folder = l->data;
+
+ if (g_ascii_strncasecmp(folder->name, path[i],
+ strlen(folder->name)) == 0) {
+ match_found = TRUE;
+ last = l->data;
+ folders = folder->subfolders;
+ break;
+ }
+ }
+
+ if (!match_found) {
+ g_strfreev(path);
+ return NULL;
+ }
+ }
+
+ g_strfreev(path);
+
+ return last;
+}
+
+static void destroy_folder_tree(void *root)
+{
+ struct message_folder *folder = root;
+ GSList *tmp, *next;
+
+ if (folder == NULL)
+ return;
+
+ g_free(folder->name);
+
+ tmp = folder->subfolders;
+ while (tmp != NULL) {
+ next = g_slist_next(tmp);
+ destroy_folder_tree(tmp->data);
+ tmp = next;
+ }
+ g_slist_free(folder->subfolders);
+ g_free(folder);
+}
+
+static struct message_folder *create_folder(const char *name)
+{
+ struct message_folder *folder = g_new0(struct message_folder, 1);
+
+ folder->name = g_strdup(name);
+ return folder;
+}
+
+static void create_folder_tree()
+{
+
+ struct message_folder *parent, *child;
+
+ folder_tree = create_folder("/");
+
+ parent = create_folder("telecom");
+ folder_tree->subfolders = g_slist_append(folder_tree->subfolders,
+ parent);
+
+ child = create_folder("msg");
+ parent->subfolders = g_slist_append(parent->subfolders, child);
+}
+
+int messages_init(void)
+{
+ g_conn = dbus_bus_get(DBUS_BUS_SESSION, NULL);
+ if (!g_conn) {
+ error("Can't get on session bus");
+ return -1;
+ }
+
+ return 0;
+}
+
+void messages_exit(void)
+{
+ if (g_conn) {
+ dbus_connection_unref(g_conn);
+ g_conn = NULL;
+ }
+}
+
+static void message_get_folder_list(DBusMessage *reply, void *user_data)
+{
+ DBusMessageIter iter;
+ DBusMessageIter iter_struct;
+ DBusMessageIter entry;
+ DBusError derr;
+ const char *name = NULL;
+ struct message_folder *parent = {0,}, *child = {0,};
+ GSList *l;
+
+ DBG("+\n");
+
+ for (l = folder_tree->subfolders; l != NULL; l = parent->subfolders)
+ parent = l->data;
+
+ DBG("Last child folder = %s \n", parent->name);
+ dbus_error_init(&derr);
+
+ if (dbus_set_error_from_message(&derr, reply)) {
+ error("Replied with an error: %s, %s", derr.name, derr.message);
+ dbus_error_free(&derr);
+ } else {
+ dbus_message_iter_init(reply, &iter);
+ dbus_message_iter_recurse(&iter, &iter_struct);
+
+ while (dbus_message_iter_get_arg_type(&iter_struct) ==
+ DBUS_TYPE_STRUCT) {
+ dbus_message_iter_recurse(&iter_struct, &entry);
+
+ dbus_message_iter_get_basic(&entry, &name);
+ DBG("Folder name = %s \n", name);
+ child = create_folder(name);
+ parent->subfolders = g_slist_append(parent->subfolders,
+ child);
+ dbus_message_iter_next(&iter_struct);
+ }
+ }
+ dbus_message_unref(reply);
+ DBG("-\n");
+}
+
+static void message_get_msg_list(DBusPendingCall *call, void *user_data)
+{
+ DBusMessage *reply = dbus_pending_call_steal_reply(call);
+ DBusMessageIter iter;
+ DBusMessageIter iter_struct;
+ DBusMessageIter entry;
+ DBusError derr;
+ const char *msg_handle;
+ const char *subject;
+ const char *datetime;
+ const char *sender_name;
+ const char *sender_addressing;
+ const char *replyto_addressing;
+ const char *recipient_name;
+ const char *recipient_addressing;
+ const char *type;
+ const char *reception_status;
+ const char *size;
+ const char *attachment_size;
+ gboolean text;
+ gboolean read;
+ gboolean sent;
+ gboolean protect;
+ gboolean priority;
+ gboolean newmessage;
+ guint64 count;
+ uint8_t type_val;
+ uint8_t read_val;
+ uint8_t priority_val;
+ uint32_t mask;
+
+ struct session *session = user_data;
+ struct messages_message *data = g_new0(struct messages_message, 1);
+
+ DBG("+\n");
+ DBG("parameter_mask = %x; type = %d; period_begin = %s;"
+ "period_end = %s; read_status = %d; recipient = %s;"
+ "originator = %s; priority = %d",
+ session->filter->parameter_mask, session->filter->type,
+ session->filter->period_begin, session->filter->period_end,
+ session->filter->read_status, session->filter->recipient,
+ session->filter->originator, session->filter->priority);
+
+ dbus_error_init(&derr);
+
+ if (dbus_set_error_from_message(&derr, reply)) {
+ error("Replied with an error: %s, %s", derr.name, derr.message);
+ dbus_error_free(&derr);
+ session->msg_list_cb(session, -ENOENT, 0,
+ FALSE, data, session->user_data);
+
+ g_free(data);
+ g_free(session->name);
+ session_filter_free(session->filter);
+ dbus_message_unref(reply);
+
+ return;
+ }
+
+ dbus_message_iter_init(reply, &iter);
+ dbus_message_iter_get_basic(&iter, &newmessage);
+ dbus_message_iter_next(&iter);
+ dbus_message_iter_get_basic(&iter, &count);
+ dbus_message_iter_next(&iter);
+
+ if (session->max == 0)
+ goto done;
+
+ dbus_message_iter_recurse(&iter, &iter_struct);
+
+ if (session->filter->parameter_mask == 0)
+ mask = ~session->filter->parameter_mask;
+ else
+ mask = session->filter->parameter_mask;
+
+ while (dbus_message_iter_get_arg_type(&iter_struct) ==
+ DBUS_TYPE_STRUCT) {
+ dbus_message_iter_recurse(&iter_struct, &entry);
+ dbus_message_iter_get_basic(&entry, &msg_handle);
+
+ if (msg_handle == NULL) {
+ dbus_message_iter_next(&iter_struct);
+ continue;
+ }
+
+ DBG("Msg handle = %s \n", msg_handle);
+ data->handle = g_strdup(msg_handle);
+
+ dbus_message_iter_next(&entry);
+ dbus_message_iter_get_basic(&entry, &subject);
+
+ if (mask & PMASK_SUBJECT) {
+ DBG("subject = %s\n", subject);
+ data->subject = g_strndup(subject,
+ BT_MAP_LIST_ITEM_MAX_LEN);
+ data->mask |= PMASK_SUBJECT;
+ }
+
+ dbus_message_iter_next(&entry);
+ dbus_message_iter_get_basic(&entry, &datetime);
+
+ if ((mask & PMASK_DATETIME) && (NULL != datetime)) {
+ DBG("datetime = %s\n", datetime);
+ char *begin = g_strdup(session->filter->period_begin);
+ char *end = g_strdup(session->filter->period_end);
+ char *time = g_strdup(datetime);
+ gboolean filter;
+
+ filter = __filter_timebased(begin, end, time);
+
+ g_free(begin);
+ g_free(end);
+ g_free(time);
+
+ if (TRUE == filter) {
+ data->datetime = g_strdup(datetime);
+ data->mask |= PMASK_DATETIME;
+ } else {
+ message_list_item_free(data);
+ dbus_message_iter_next(&iter_struct);
+ continue;
+ }
+ }
+
+ dbus_message_iter_next(&entry);
+ dbus_message_iter_get_basic(&entry, &sender_name);
+
+ if ((mask & PMASK_SENDER_NAME) &&
+ (NULL != session->filter->originator)) {
+ DBG("sender_name = %s \n", sender_name);
+
+ if (g_strstr_len(sender_name, -1,
+ session->filter->originator)) {
+ data->sender_name = g_strndup(sender_name,
+ BT_MAP_LIST_ITEM_MAX_LEN);
+ data->mask |= PMASK_SENDER_NAME;
+ } else {
+ message_list_item_free(data);
+ dbus_message_iter_next(&iter_struct);
+ continue;
+ }
+ }
+
+ dbus_message_iter_next(&entry);
+ dbus_message_iter_get_basic(&entry, &sender_addressing);
+
+ if ((mask & PMASK_SENDER_ADDRESSING) &&
+ (NULL != sender_addressing)) {
+ DBG("sender_addressing = %s \n", sender_addressing);
+
+ data->sender_addressing = g_strndup(sender_addressing,
+ BT_MAP_LIST_ITEM_MAX_LEN);
+ data->mask |= PMASK_SENDER_ADDRESSING;
+ }
+
+ dbus_message_iter_next(&entry);
+ dbus_message_iter_get_basic(&entry, &recipient_name);
+
+ if ((mask & PMASK_RECIPIENT_NAME) &&
+ (NULL != session->filter->recipient)) {
+ DBG("recipient_name = %s \n", recipient_name);
+
+ if (g_strstr_len(recipient_name, -1,
+ session->filter->recipient)) {
+ data->recipient_name =
+ g_strndup(recipient_name,
+ BT_MAP_LIST_ITEM_MAX_LEN);
+ data->mask |= PMASK_RECIPIENT_NAME;
+ } else {
+ message_list_item_free(data);
+ dbus_message_iter_next(&iter_struct);
+ continue;
+ }
+ }
+
+ dbus_message_iter_next(&entry);
+ dbus_message_iter_get_basic(&entry, &recipient_addressing);
+
+ if ((mask & PMASK_RECIPIENT_ADDRESSING) &&
+ (NULL != recipient_addressing)) {
+ DBG("recipient_addressing=%s\n", recipient_addressing);
+
+ data->recipient_addressing =
+ g_strndup(recipient_addressing,
+ BT_MAP_LIST_ITEM_MAX_LEN);
+ data->mask |= PMASK_RECIPIENT_ADDRESSING;
+ }
+
+ dbus_message_iter_next(&entry);
+ dbus_message_iter_get_basic(&entry, &type);
+
+ if ((mask & PMASK_TYPE) && (NULL != type)) {
+ DBG("type = %s \n", type);
+
+ type_val = get_type_val(type);
+ if (!(session->filter->type & type_val)) {
+ data->type = g_strdup(type);
+ data->mask |= PMASK_TYPE;
+ }
+ }
+
+ dbus_message_iter_next(&entry);
+ dbus_message_iter_get_basic(&entry, &size);
+
+ if ((mask & PMASK_SIZE) && (NULL != size)) {
+ DBG("size = %s \n", size);
+
+ data->size = g_strdup(size);
+ data->mask |= PMASK_SIZE;
+ }
+
+ dbus_message_iter_next(&entry);
+ dbus_message_iter_get_basic(&entry, &reception_status);
+
+ if (mask & PMASK_RECEPTION_STATUS) {
+ DBG("reception_status = %s \n", reception_status);
+
+ data->reception_status = g_strdup(reception_status);
+ data->mask |= PMASK_RECEPTION_STATUS;
+ }
+
+ dbus_message_iter_next(&entry);
+ dbus_message_iter_get_basic(&entry, &text);
+
+ if (mask & PMASK_TEXT) {
+ DBG("text = %d \n", text);
+ data->text = text;
+ data->mask |= PMASK_TEXT;
+ }
+
+ dbus_message_iter_next(&entry);
+ dbus_message_iter_get_basic(&entry, &attachment_size);
+
+ if (mask & PMASK_ATTACHMENT_SIZE) {
+ DBG("attachment_size = %s\n", attachment_size);
+
+ data->attachment_size = g_strdup(attachment_size);
+ data->mask |= PMASK_ATTACHMENT_SIZE;
+ }
+
+ dbus_message_iter_next(&entry);
+ dbus_message_iter_get_basic(&entry, &priority);
+
+ if (mask & PMASK_PRIORITY) {
+ DBG("priority = %d \n", priority);
+
+ priority_val = get_priority_val(priority);
+ if ((session->filter->priority == 0) ||
+ (session->filter->priority & priority_val)) {
+ data->priority = priority;
+ data->mask |= PMASK_PRIORITY;
+ } else {
+ message_list_item_free(data);
+ dbus_message_iter_next(&iter_struct);
+ continue;
+ }
+ }
+
+ dbus_message_iter_next(&entry);
+ dbus_message_iter_get_basic(&entry, &read);
+
+ if (mask & PMASK_READ) {
+ DBG("read = %d \n", read);
+
+ read_val = get_read_status_val(read);
+
+ if ((session->filter->read_status == 0) ||
+ (session->filter->read_status & read_val)) {
+ data->read = read;
+ data->mask |= PMASK_READ;
+ } else {
+ message_list_item_free(data);
+ dbus_message_iter_next(&iter_struct);
+ continue;
+ }
+ }
+
+ dbus_message_iter_next(&entry);
+ dbus_message_iter_get_basic(&entry, &sent);
+
+ if (mask & PMASK_SENT) {
+ DBG("sent = %d \n", sent);
+ data->sent = sent;
+ data->mask |= PMASK_SENT;
+ }
+
+ dbus_message_iter_next(&entry);
+ dbus_message_iter_get_basic(&entry, &protect);
+
+ if (mask & PMASK_PROTECTED) {
+ DBG("protect = %d \n", protect);
+ data->protect = protect;
+ data->mask |= PMASK_PROTECTED;
+ }
+
+ dbus_message_iter_next(&entry);
+ dbus_message_iter_get_basic(&entry, &replyto_addressing);
+
+ if ((mask & PMASK_REPLYTO_ADDRESSING) &&
+ (0x04 == get_type_val(type))) {
+
+ DBG("replyto_addressing = %s \n", replyto_addressing);
+ if (replyto_addressing)
+ data->replyto_addressing =
+ g_strdup(replyto_addressing);
+ else
+ data->replyto_addressing = g_strdup("");
+
+ data->mask |= PMASK_REPLYTO_ADDRESSING;
+ }
+
+ session->msg_list_cb(session, -EAGAIN, 1, newmessage, data,
+ session->user_data);
+
+ message_list_item_free(data);
+ dbus_message_iter_next(&iter_struct);
+ }
+
+done:
+ session->msg_list_cb(session, 0, count, newmessage, NULL,
+ session->user_data);
+
+ g_free(data);
+ g_free(session->name);
+ session_filter_free(session->filter);
+ dbus_message_unref(reply);
+ DBG("-\n");
+}
+
+static void message_get_msg(DBusPendingCall *call, void *user_data)
+{
+ DBusMessage *reply = dbus_pending_call_steal_reply(call);
+ DBusMessageIter iter;
+ DBusError derr;
+ struct session *session = user_data;
+ char *msg_body;
+ gboolean fraction_deliver;
+
+ DBG("+\n");
+
+ dbus_error_init(&derr);
+ if (dbus_set_error_from_message(&derr, reply)) {
+ error("Replied with an error: %s, %s", derr.name, derr.message);
+ dbus_error_free(&derr);
+ } else {
+ dbus_message_iter_init(reply, &iter);
+ dbus_message_iter_get_basic(&iter, &fraction_deliver);
+ dbus_message_iter_next(&iter);
+ dbus_message_iter_get_basic(&iter, &msg_body);
+ DBG("msg_body %s\n", msg_body);
+
+ session->get_msg_cb(session, -EAGAIN, fraction_deliver,
+ msg_body, session->user_data);
+ session->get_msg_cb(session, 0, fraction_deliver,
+ NULL, session->user_data);
+ }
+ dbus_message_unref(reply);
+ DBG("-\n");
+}
+
+int messages_connect(void **s)
+{
+ DBusMessage *message;
+ DBusMessage *reply;
+ DBusError err;
+ DBG("+\n");
+
+ struct session *session = g_new0(struct session, 1);
+
+ create_folder_tree();
+
+ session->cwd = g_strdup("/");
+ session->folder = folder_tree;
+
+ *s = session;
+
+ message = dbus_message_new_method_call(BT_MAP_SERVICE_NAME,
+ BT_MAP_SERVICE_OBJECT_PATH,
+ BT_MAP_SERVICE_INTERFACE,
+ QUERY_GET_FOLDER_TREE);
+ if (!message) {
+ error("Can't allocate new message");
+ return -1;
+ }
+
+ dbus_error_init(&err);
+
+ reply = dbus_connection_send_with_reply_and_block(g_conn, message,
+ DBUS_TIMEOUT_USE_DEFAULT, &err);
+ if (!reply) {
+ DBG(" Reply failed");
+ if (dbus_error_is_set(&err)) {
+ DBG("%s", err.message);
+ dbus_error_free(&err);
+ }
+
+ dbus_message_unref(message);
+ return -1;
+ }
+
+ message_get_folder_list(reply, session);
+
+ dbus_message_unref(message);
+ DBG("-\n");
+ return 0;
+}
+
+void messages_disconnect(void *s)
+{
+ DBusMessage *message;
+ struct session *session = s;
+ DBG("+\n");
+
+ destroy_folder_tree(folder_tree);
+ folder_tree = NULL;
+ g_free(session->cwd);
+ g_free(session);
+
+ message = dbus_message_new_method_call(BT_MAP_SERVICE_NAME,
+ BT_MAP_SERVICE_OBJECT_PATH,
+ BT_MAP_SERVICE_INTERFACE,
+ QUERY_DESTROY_AGENT);
+
+ if (!message) {
+ error("Can't allocate new message");
+ return;
+ }
+
+ if (dbus_connection_send(g_conn, message, NULL) == FALSE)
+ error("Could not send dbus message");
+
+ dbus_message_unref(message);
+
+ DBG("-\n");
+}
+
+static gboolean notification_registration(gpointer user_data)
+{
+ DBG("+\n");
+ DBusMessage *message = NULL;
+ gboolean reg;
+ struct mns_reg_data *data = (struct mns_reg_data *)user_data;
+
+ message = dbus_message_new_method_call(BT_MAP_SERVICE_NAME,
+ BT_MAP_SERVICE_OBJECT_PATH,
+ BT_MAP_SERVICE_INTERFACE,
+ QUERY_NOTI_REGISTRATION);
+ if (!message) {
+ error("Can't allocate new message");
+ goto done;
+ }
+
+ DBG("data->notification_status = %d\n", data->notification_status);
+
+ if (data->notification_status == 1)
+ reg = TRUE;
+ else
+ reg = FALSE;
+
+ dbus_message_append_args(message, DBUS_TYPE_STRING, &data->remote_addr,
+ DBUS_TYPE_BOOLEAN, &reg,
+ DBUS_TYPE_INVALID);
+
+ if (dbus_connection_send(g_conn, message, NULL) == FALSE)
+ error("Could not send dbus message");
+
+ dbus_message_unref(message);
+
+done:
+ g_free(data->remote_addr);
+ g_free(data);
+
+ DBG("-\n");
+ return FALSE;
+}
+
+int messages_set_notification_registration(void *session,
+ char *address, uint8_t status,
+ void *user_data)
+{
+ DBG("+\n");
+ struct mns_reg_data *data = g_new0(struct mns_reg_data, 1);
+ data->notification_status = status;
+ data->remote_addr = g_strdup(address);
+
+ DBG("status = %d\n", status);
+
+ g_idle_add(notification_registration, data);
+ DBG("-\n");
+ return 1;
+}
+
+int messages_set_folder(void *s, const char *name, gboolean cdup)
+{
+ struct session *session = s;
+ char *newrel = NULL;
+ char *newabs;
+ char *tmp;
+
+ if (name && (strchr(name, '/') || strcmp(name, "..") == 0))
+ return -EBADR;
+
+ if (cdup) {
+ if (session->cwd[0] == 0)
+ return -ENOENT;
+
+ newrel = g_path_get_dirname(session->cwd);
+
+ /* We use empty string for indication of the root directory */
+ if (newrel[0] == '.' && newrel[1] == 0)
+ newrel[0] = 0;
+ }
+
+ tmp = newrel;
+ if (!cdup && (!name || name[0] == 0))
+ newrel = g_strdup("");
+ else
+ newrel = g_build_filename(newrel ? newrel : session->cwd, name,
+ NULL);
+ g_free(tmp);
+
+ if (newrel[0] != '/')
+ newabs = g_build_filename("/", newrel, NULL);
+ else
+ newabs = g_strdup(newrel);
+
+ session->folder = get_folder(newabs);
+ if (session->folder == NULL) {
+ g_free(newrel);
+ g_free(newabs);
+
+ return -ENOENT;
+ }
+
+ g_free(newrel);
+ g_free(session->cwd);
+ session->cwd = newabs;
+
+ return 0;
+}
+
+static gboolean async_get_folder_listing(void *s)
+{
+ struct session *session = s;
+ int i;
+ uint16_t folder_list_size = 0;
+ char *path = NULL;
+ struct message_folder *folder;
+ GSList *dir;
+
+ if (session->name && strchr(session->name, '/') != NULL)
+ goto done;
+
+ path = g_build_filename(session->cwd, session->name, NULL);
+
+ if (path == NULL || strlen(path) == 0)
+ goto done;
+
+ folder = get_folder(path);
+
+ if (folder == NULL)
+ goto done;
+
+ if (session->max == 0) {
+ folder_list_size = g_slist_length(folder->subfolders);
+ goto done;
+ }
+
+ dir = folder->subfolders;
+
+ /* move to offset */
+ for (i = 0; i < session->offset; i++) {
+ if (dir == NULL)
+ goto done;
+
+ dir = g_slist_next(dir);
+ }
+
+ for (i = 0; i < session->max; i++) {
+ struct message_folder *dir_data;
+
+ if (dir == NULL)
+ goto done;
+
+ dir_data = dir->data;
+ session->folder_list_cb(session, -EAGAIN, 0,
+ dir_data->name, session->user_data);
+
+ dir = g_slist_next(dir);
+ }
+
+ done:
+ session->folder_list_cb(session, 0, folder_list_size,
+ NULL, session->user_data);
+
+ g_free(path);
+ g_free(session->name);
+
+ return FALSE;
+}
+
+int messages_get_folder_listing(void *s, const char *name,
+ uint16_t max, uint16_t offset,
+ messages_folder_listing_cb callback,
+ void *user_data)
+{
+ DBG("+\n");
+ struct session *session = s;
+ session->name = g_strdup(name);
+ session->max = max;
+ session->offset = offset;
+ session->folder_list_cb = callback;
+ session->user_data = user_data;
+
+ g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, async_get_folder_listing,
+ session, NULL);
+
+ DBG("-\n");
+ return 0;
+}
+
+
+static void append_variant(DBusMessageIter *iter, int type, void *val)
+{
+ DBusMessageIter value;
+ char sig[2] = { type, '\0' };
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_VARIANT, sig, &value);
+ dbus_message_iter_append_basic(&value, type, val);
+ dbus_message_iter_close_container(iter, &value);
+}
+
+static void dict_append_entry(DBusMessageIter *dict, const char *key,
+ int type, void *val)
+{
+ DBusMessageIter entry;
+
+ dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry);
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+ append_variant(&entry, type, val);
+ dbus_message_iter_close_container(dict, &entry);
+}
+
+int messages_get_messages_listing(void *session, const char *name,
+ uint16_t max, uint16_t offset,
+ uint8_t subject_len,
+ const struct messages_filter *filter,
+ messages_get_messages_listing_cb callback,
+ void *user_data)
+{
+ DBusPendingCall *call;
+ DBusMessage *message;
+ struct session *s = session;
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+
+ if (name != NULL && strlen(name))
+ s->name = g_strdup(name);
+ else
+ s->name = g_strdup(s->cwd);
+
+ s->max = max;
+ s->offset = offset;
+
+ s->filter = g_new0(struct messages_filter, 1);
+ s->filter->parameter_mask = filter->parameter_mask;
+ s->filter->type = filter->type;
+ s->filter->period_begin = g_strdup(filter->period_begin);
+ s->filter->period_end = g_strdup(filter->period_end);
+ s->filter->read_status = filter->read_status;
+ s->filter->recipient = g_strdup(filter->recipient);
+ s->filter->originator = g_strdup(filter->originator);
+ s->filter->priority = filter->priority;
+
+ s->msg_list_cb = (void *)callback;
+ s->user_data = user_data;
+
+ message = dbus_message_new_method_call(BT_MAP_SERVICE_NAME,
+ BT_MAP_SERVICE_OBJECT_PATH,
+ BT_MAP_SERVICE_INTERFACE,
+ QUERY_GET_MSG_LIST);
+ if (!message) {
+ error("Can't allocate new message");
+ g_free(s->name);
+ session_filter_free(s->filter);
+ return -1;
+ }
+
+ dbus_message_append_args(message, DBUS_TYPE_STRING, &s->name,
+ DBUS_TYPE_UINT16, &s->max,
+ DBUS_TYPE_UINT16, &s->offset,
+ DBUS_TYPE_BYTE, &subject_len,
+ DBUS_TYPE_INVALID);
+
+ dbus_message_iter_init_append(message, &iter);
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+ if (filter->parameter_mask)
+ dict_append_entry(&dict, "ParameterMask", DBUS_TYPE_UINT32,
+ &filter->parameter_mask);
+ if (filter->type)
+ dict_append_entry(&dict, "FilterMessageType", DBUS_TYPE_BYTE,
+ &filter->type);
+ if (filter->period_begin)
+ dict_append_entry(&dict, "FilterPeriodBegin", DBUS_TYPE_STRING,
+ &filter->period_begin);
+ if (filter->period_end)
+ dict_append_entry(&dict, "FilterPeriodEnd", DBUS_TYPE_STRING,
+ &filter->period_end);
+ if (filter->read_status)
+ dict_append_entry(&dict, "FilterReadStatus", DBUS_TYPE_BYTE,
+ &filter->read_status);
+ if (filter->recipient)
+ dict_append_entry(&dict, "FilterRecipient", DBUS_TYPE_STRING,
+ &filter->recipient);
+ if (filter->originator)
+ dict_append_entry(&dict, "FilterOriginator", DBUS_TYPE_STRING,
+ &filter->originator);
+ if (filter->priority)
+ dict_append_entry(&dict, "FilterPriority", DBUS_TYPE_BYTE,
+ &filter->priority);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ if (dbus_connection_send_with_reply(g_conn, message, &call,
+ DBUS_TIMEOUT_INFINITE) == FALSE) {
+ error("Could not send dbus message");
+ dbus_message_unref(message);
+ g_free(s->name);
+ session_filter_free(s->filter);
+ return -1;
+ }
+ dbus_pending_call_set_notify(call, message_get_msg_list, s, NULL);
+ dbus_message_unref(message);
+ DBG("-\n");
+ return 1;
+}
+
+int messages_push_message(void *session, const char *folder,
+ uint8_t transparent, uint8_t retry,
+ uint8_t charset,
+ messages_push_message_cb callback,
+ void *user_data)
+{
+ DBusMessage *message;
+ DBusMessage *reply;
+ DBusError err;
+ struct session *s = session;
+
+ gboolean save_copy = FALSE; /* As per specs default value */
+ gboolean retry_send = TRUE; /* As per specs default value */
+ gboolean native = FALSE;
+ gchar *folder_path = NULL;
+ guint64 handle = 0;
+
+ DBG("+\n");
+
+ DBG("session->cwd %s +\n", s->cwd);
+
+ if (g_ascii_strncasecmp(s->cwd, "/telecom/msg",
+ strlen("/telecom/msg")) != 0) {
+ DBG("Path Not Set properly");
+ return -1;
+ }
+
+ if ((folder[0] == '\0') && (g_strcmp0(s->cwd, "/telecom/msg") == 0)) {
+ DBG("Invalid Folders");
+ return -1;
+ }
+
+ if ((folder[0] != '\0'))
+ folder_path = g_strconcat("/telecom/msg/", folder, NULL);
+ else
+ folder_path = g_strdup(s->cwd);
+
+ s->push_msg_cb = callback;
+ s->user_data = user_data;
+
+ if (transparent & 0x1)
+ save_copy = TRUE;
+
+ if (!(retry & 0x1)) {
+ retry_send = FALSE;
+ DBG("Retry send %d\n", retry_send);
+ }
+
+ if (charset & 0x1) {
+ native = TRUE;
+ DBG("native send %d\n", native);
+ }
+
+ DBG("save_copy %d\n", save_copy);
+ DBG("retry_send %d\n", retry_send);
+ DBG("native %d\n", native);
+
+ message = dbus_message_new_method_call(BT_MAP_SERVICE_NAME,
+ BT_MAP_SERVICE_OBJECT_PATH,
+ BT_MAP_SERVICE_INTERFACE,
+ QUERY_PUSH_MESSAGE);
+ if (!message) {
+ error("Can't allocate new message");
+ g_free(folder_path);
+ return -1;
+ }
+
+ dbus_message_append_args(message, DBUS_TYPE_BOOLEAN, &save_copy,
+ DBUS_TYPE_BOOLEAN, &retry_send,
+ DBUS_TYPE_BOOLEAN, &native,
+ DBUS_TYPE_STRING, &folder_path,
+ DBUS_TYPE_INVALID);
+
+ dbus_error_init(&err);
+
+ reply = dbus_connection_send_with_reply_and_block(
+ g_conn, message,
+ DBUS_TIMEOUT_USE_DEFAULT, &err);
+ if (!reply) {
+ DBG(" Reply failed");
+
+ if (dbus_error_is_set(&err)) {
+ DBG("%s", err.message);
+ dbus_error_free(&err);
+ }
+ g_free(folder_path);
+ dbus_message_unref(message);
+ return -1;
+ }
+
+ if (!dbus_message_get_args(reply, &err, DBUS_TYPE_UINT64,
+ &handle, DBUS_TYPE_INVALID)) {
+ if (dbus_error_is_set(&err)) {
+ error("err %s\n", err.message);
+ dbus_error_free(&err);
+ }
+ g_free(folder_path);
+ dbus_message_unref(message);
+ dbus_message_unref(reply);
+ return -1;
+ }
+
+ DBG("uint64 handle %"G_GUINT64_FORMAT"\n", handle);
+ s->push_msg_cb(s, 0, handle, s->user_data);
+
+ g_free(folder_path);
+ dbus_message_unref(message);
+ dbus_message_unref(reply);
+
+ DBG("-\n");
+ return 1;
+}
+
+int messages_push_message_data(void *session, const char *bmsg, void *user_data)
+{
+ DBusMessage *message;
+
+ DBG("+\n");
+
+ message = dbus_message_new_method_call(BT_MAP_SERVICE_NAME,
+ BT_MAP_SERVICE_OBJECT_PATH,
+ BT_MAP_SERVICE_INTERFACE,
+ QUERY_PUSH_MESSAGE_DATA);
+ if (!message) {
+ error("Can't allocate new message");
+ return -1;
+ }
+
+ dbus_message_append_args(message, DBUS_TYPE_STRING, &bmsg,
+ DBUS_TYPE_INVALID);
+
+ if (dbus_connection_send(g_conn, message, NULL) == FALSE) {
+ error("Could not send dbus message");
+ dbus_message_unref(message);
+ return -1;
+ }
+
+ dbus_message_unref(message);
+ DBG("-\n");
+ return 1;
+}
+
+int messages_get_message(void *session,
+ const char *handle,
+ uint8_t attachment, uint8_t charset,
+ uint8_t fraction_request,
+ messages_get_message_cb callback,
+ void *user_data)
+{
+ DBusPendingCall *call;
+ DBusMessage *message;
+ struct session *s = session;
+ char *message_name;
+ gboolean attach = FALSE;
+ gboolean transcode = FALSE;
+ gboolean first_request = TRUE;
+
+ DBG("+\n");
+
+ if (NULL != handle) {
+ message_name = g_strdup(handle);
+ DBG("Message handle = %s\n", handle);
+ } else {
+ return -1;
+ }
+ s->get_msg_cb = callback;
+ s->user_data = user_data;
+
+ message = dbus_message_new_method_call(BT_MAP_SERVICE_NAME,
+ BT_MAP_SERVICE_OBJECT_PATH,
+ BT_MAP_SERVICE_INTERFACE,
+ QUERY_GET_MESSAGE);
+ if (!message) {
+ error("Can't allocate new message");
+ g_free(message_name);
+ return -1;
+ }
+
+ if (attachment & 0x1)
+ attach = TRUE;
+
+ if (charset & 0x1)
+ transcode = TRUE;
+
+ if (fraction_request & 0x1)
+ first_request = FALSE;
+
+ dbus_message_append_args(message, DBUS_TYPE_STRING, &message_name,
+ DBUS_TYPE_BOOLEAN, &attach,
+ DBUS_TYPE_BOOLEAN, &transcode,
+ DBUS_TYPE_BOOLEAN, &first_request,
+ DBUS_TYPE_INVALID);
+
+ if (dbus_connection_send_with_reply(g_conn, message, &call, -1) ==
+ FALSE) {
+ error("Could not send dbus message");
+ dbus_message_unref(message);
+ g_free(message_name);
+ return -1;
+ }
+ dbus_pending_call_set_notify(call, message_get_msg, s, NULL);
+ dbus_message_unref(message);
+ g_free(message_name);
+ DBG("-\n");
+ return 1;
+}
+
+static void message_update_msg(DBusPendingCall *call, void *user_data)
+{
+ DBusMessage *reply = dbus_pending_call_steal_reply(call);
+ DBusMessageIter iter;
+ DBusError derr;
+ struct session *session = user_data;
+ int err;
+ DBG("+\n");
+
+ dbus_error_init(&derr);
+ if (dbus_set_error_from_message(&derr, reply)) {
+ error("Replied with an error: %s, %s", derr.name, derr.message);
+ dbus_error_free(&derr);
+ } else {
+ dbus_message_iter_init(reply, &iter);
+ if (dbus_message_iter_get_arg_type(&iter) ==
+ DBUS_TYPE_INT32) {
+ dbus_message_iter_get_basic(&iter, &err);
+ DBG("Error : %d\n", err);
+ session->msg_update_cb(session, err,
+ session->user_data);
+ }
+ }
+ dbus_message_unref(reply);
+ DBG("-\n");
+}
+
+int messages_update_inbox(void *session,
+ messages_status_cb callback,
+ void *user_data)
+{
+ if (TIZEN_FEATURE_BLUEZ_SMS_ONLY) {
+ /* MAP.TS.1.0.3 : TP/MMB/BV-16-I
+ Currently support is only for SMS, Since SMS service does not
+ allow the polling of its mailbox, it must return Not implemented */
+
+ return -ENOSYS;
+ }
+
+ DBusPendingCall *call;
+ DBusMessage *message;
+ struct session *s = session;
+
+ DBG("+\n");
+
+ s->msg_update_cb = callback;
+ s->user_data = user_data;
+
+ message = dbus_message_new_method_call(BT_MAP_SERVICE_NAME,
+ BT_MAP_SERVICE_OBJECT_PATH,
+ BT_MAP_SERVICE_INTERFACE,
+ QUERY_UPDATE_MESSAGE);
+ if (!message) {
+ error("Can't allocate new message");
+ return -1;
+ }
+
+ if (dbus_connection_send_with_reply(g_conn, message, &call, -1) ==
+ FALSE) {
+ error("Could not send dbus message");
+ dbus_message_unref(message);
+ return -1;
+ }
+ dbus_pending_call_set_notify(call, message_update_msg, s, NULL);
+ dbus_message_unref(message);
+ DBG("-\n");
+ return 1;
+}
+
+static void message_status_msg(DBusPendingCall *call, void *user_data)
+{
+ DBusMessage *reply = dbus_pending_call_steal_reply(call);
+ DBusMessageIter iter;
+ DBusError derr;
+ struct session *session = user_data;
+ int err;
+
+ DBG("+\n");
+
+ dbus_error_init(&derr);
+ if (dbus_set_error_from_message(&derr, reply)) {
+ error("Replied with an error: %s, %s", derr.name, derr.message);
+ dbus_error_free(&derr);
+ } else {
+ dbus_message_iter_init(reply, &iter);
+ if (dbus_message_iter_get_arg_type(&iter) ==
+ DBUS_TYPE_INT32) {
+ dbus_message_iter_get_basic(&iter, &err);
+ DBG("Error : %d\n", err);
+ session->msg_status_cb(session, err,
+ session->user_data);
+ }
+ }
+ dbus_message_unref(reply);
+ DBG("-\n");
+}
+
+int messages_set_read(void *session, const char *handle, uint8_t value,
+ messages_status_cb callback, void *user_data)
+{
+ DBusPendingCall *call;
+ DBusMessage *message;
+ struct session *s = session;
+ char *message_name;
+ gboolean read;
+
+ DBG("+\n");
+
+ if (NULL == handle)
+ return -1;
+
+ DBG("Message handle = %s\n", handle);
+ message_name = g_strdup(handle);
+
+ s->msg_status_cb = callback;
+ s->user_data = user_data;
+
+ message = dbus_message_new_method_call(BT_MAP_SERVICE_NAME,
+ BT_MAP_SERVICE_OBJECT_PATH,
+ BT_MAP_SERVICE_INTERFACE,
+ QUERY_SET_READ_STATUS);
+ if (!message) {
+ error("Can't allocate new message");
+ g_free(message_name);
+ return -1;
+ }
+
+ read = value ? TRUE : FALSE;
+
+ dbus_message_append_args(message, DBUS_TYPE_STRING, &message_name,
+ DBUS_TYPE_BOOLEAN, &read,
+ DBUS_TYPE_INVALID);
+
+ if (dbus_connection_send_with_reply(g_conn, message, &call, -1) ==
+ FALSE) {
+ error("Could not send dbus message");
+ g_free(message_name);
+ dbus_message_unref(message);
+ return -1;
+ }
+
+ dbus_pending_call_set_notify(call, message_status_msg, s, NULL);
+ dbus_message_unref(message);
+ g_free(message_name);
+ DBG("-\n");
+ return 1;
+}
+
+int messages_set_delete(void *session, const char *handle,
+ uint8_t value,
+ messages_status_cb callback,
+ void *user_data)
+{
+ DBusPendingCall *call;
+ DBusMessage *message;
+ struct session *s = session;
+ char *message_name;
+ gboolean del;
+
+ DBG("+\n");
+
+ if (NULL == handle)
+ return -1;
+
+ DBG("Message handle = %s\n", handle);
+ message_name = g_strdup(handle);
+
+ s->msg_status_cb = callback;
+ s->user_data = user_data;
+
+ message = dbus_message_new_method_call(BT_MAP_SERVICE_NAME,
+ BT_MAP_SERVICE_OBJECT_PATH,
+ BT_MAP_SERVICE_INTERFACE,
+ QUERY_SET_DELETE_STATUS);
+ if (!message) {
+ error("Can't allocate new message");
+ g_free(message_name);
+ return -1;
+ }
+
+ del = value ? TRUE : FALSE;
+
+ dbus_message_append_args(message, DBUS_TYPE_STRING, &message_name,
+ DBUS_TYPE_BOOLEAN, &del,
+ DBUS_TYPE_INVALID);
+
+ if (dbus_connection_send_with_reply(g_conn, message, &call, -1) ==
+ FALSE) {
+ error("Could not send dbus message");
+ g_free(message_name);
+ dbus_message_unref(message);
+ return -1;
+ }
+
+ dbus_pending_call_set_notify(call, message_status_msg, s, NULL);
+ dbus_message_unref(message);
+ g_free(message_name);
+ DBG("-\n");
+ return 1;
+}
+
+void messages_abort(void *session)
+{
+}
diff --git a/obexd/plugins/messages-tracker.c b/obexd/plugins/messages-tracker.c
new file mode 100755
index 00000000..60f3a807
--- /dev/null
+++ b/obexd/plugins/messages-tracker.c
@@ -0,0 +1,345 @@
+/*
+ *
+ * OBEX Server
+ *
+ * Copyright (C) 2010-2011 Nokia 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 <errno.h>
+#include <glib.h>
+#include <string.h>
+
+#include "messages.h"
+
+struct message_folder {
+ char *name;
+ GSList *subfolders;
+ char *query;
+};
+
+struct session {
+ char *cwd;
+ struct message_folder *folder;
+ char *name;
+ uint16_t max;
+ uint16_t offset;
+ void *user_data;
+ void (*folder_list_cb)(void *session, int err, uint16_t size,
+ const char *name, void *user_data);
+};
+
+static struct message_folder *folder_tree = NULL;
+
+static struct message_folder *get_folder(const char *folder)
+{
+ GSList *folders = folder_tree->subfolders;
+ struct message_folder *last = NULL;
+ char **path;
+ int i;
+
+ if (g_strcmp0(folder, "/") == 0)
+ return folder_tree;
+
+ path = g_strsplit(folder, "/", 0);
+
+ for (i = 1; path[i] != NULL; i++) {
+ gboolean match_found = FALSE;
+ GSList *l;
+
+ for (l = folders; l != NULL; l = g_slist_next(l)) {
+ struct message_folder *folder = l->data;
+
+ if (g_strcmp0(folder->name, path[i]) == 0) {
+ match_found = TRUE;
+ last = l->data;
+ folders = folder->subfolders;
+ break;
+ }
+ }
+
+ if (!match_found) {
+ g_strfreev(path);
+ return NULL;
+ }
+ }
+
+ g_strfreev(path);
+
+ return last;
+}
+
+static struct message_folder *create_folder(const char *name, const char *query)
+{
+ struct message_folder *folder = g_new0(struct message_folder, 1);
+
+ folder->name = g_strdup(name);
+ folder->query = g_strdup(query);
+
+ return folder;
+}
+
+static void destroy_folder_tree(void *root)
+{
+ struct message_folder *folder = root;
+ GSList *tmp, *next;
+
+ if (folder == NULL)
+ return;
+
+ g_free(folder->name);
+ g_free(folder->query);
+
+ tmp = folder->subfolders;
+ while (tmp != NULL) {
+ next = g_slist_next(tmp);
+ destroy_folder_tree(tmp->data);
+ tmp = next;
+ }
+
+ g_slist_free(folder->subfolders);
+ g_free(folder);
+}
+
+static void create_folder_tree(void)
+{
+ struct message_folder *parent, *child;
+
+ folder_tree = create_folder("/", "FILTER (!BOUND(?msg))");
+
+ parent = create_folder("telecom", "FILTER (!BOUND(?msg))");
+ folder_tree->subfolders = g_slist_append(folder_tree->subfolders,
+ parent);
+
+ child = create_folder("msg", "FILTER (!BOUND(?msg))");
+ parent->subfolders = g_slist_append(parent->subfolders, child);
+
+ parent = child;
+
+ child = create_folder("inbox", "?msg nmo:isSent \"false\" ; "
+ "nmo:isDeleted \"false\" ; "
+ "nmo:isDraft \"false\". ");
+ parent->subfolders = g_slist_append(parent->subfolders, child);
+
+ child = create_folder("sent", "?msg nmo:isDeleted \"false\" ; "
+ "nmo:isSent \"true\" . ");
+ parent->subfolders = g_slist_append(parent->subfolders, child);
+
+ child = create_folder("deleted", "?msg nmo:isDeleted \"true\" . ");
+ parent->subfolders = g_slist_append(parent->subfolders, child);
+}
+
+int messages_init(void)
+{
+ create_folder_tree();
+
+ return 0;
+}
+
+void messages_exit(void)
+{
+ destroy_folder_tree(folder_tree);
+}
+
+int messages_connect(void **s)
+{
+ struct session *session = g_new0(struct session, 1);
+
+ session->cwd = g_strdup("/");
+ session->folder = folder_tree;
+
+ *s = session;
+
+ return 0;
+}
+
+void messages_disconnect(void *s)
+{
+ struct session *session = s;
+
+ g_free(session->cwd);
+ g_free(session);
+}
+
+int messages_set_notification_registration(void *session,
+ void (*send_event)(void *session,
+ const struct messages_event *event, void *user_data),
+ void *user_data)
+{
+ return -ENOSYS;
+}
+
+int messages_set_folder(void *s, const char *name, gboolean cdup)
+{
+ struct session *session = s;
+ char *newrel = NULL;
+ char *newabs;
+ char *tmp;
+
+ if (name && (strchr(name, '/') || strcmp(name, "..") == 0))
+ return -EBADR;
+
+ if (cdup) {
+ if (session->cwd[0] == 0)
+ return -ENOENT;
+
+ newrel = g_path_get_dirname(session->cwd);
+
+ /* We use empty string for indication of the root directory */
+ if (newrel[0] == '.' && newrel[1] == 0)
+ newrel[0] = 0;
+ }
+
+ tmp = newrel;
+ if (!cdup && (!name || name[0] == 0))
+ newrel = g_strdup("");
+ else
+ newrel = g_build_filename(newrel ? newrel : session->cwd, name,
+ NULL);
+ g_free(tmp);
+
+ if (newrel[0] != '/')
+ newabs = g_build_filename("/", newrel, NULL);
+ else
+ newabs = g_strdup(newrel);
+
+ session->folder = get_folder(newabs);
+ if (session->folder == NULL) {
+ g_free(newrel);
+ g_free(newabs);
+
+ return -ENOENT;
+ }
+
+ g_free(newrel);
+ g_free(session->cwd);
+ session->cwd = newabs;
+
+ return 0;
+}
+
+static gboolean async_get_folder_listing(void *s)
+{
+ struct session *session = s;
+ gboolean count = FALSE;
+ int folder_count = 0;
+ char *path = NULL;
+ struct message_folder *folder;
+ GSList *dir;
+
+ if (session->name && strchr(session->name, '/') != NULL)
+ goto done;
+
+ path = g_build_filename(session->cwd, session->name, NULL);
+
+ if (path == NULL || strlen(path) == 0)
+ goto done;
+
+ folder = get_folder(path);
+
+ if (folder == NULL)
+ goto done;
+
+ if (session->max == 0) {
+ session->max = 0xffff;
+ session->offset = 0;
+ count = TRUE;
+ }
+
+ for (dir = folder->subfolders; dir &&
+ (folder_count - session->offset) < session->max;
+ folder_count++, dir = g_slist_next(dir)) {
+ struct message_folder *dir_data = dir->data;
+
+ if (count == FALSE && session->offset <= folder_count)
+ session->folder_list_cb(session, -EAGAIN, 0,
+ dir_data->name, session->user_data);
+ }
+
+ done:
+ session->folder_list_cb(session, 0, folder_count, NULL,
+ session->user_data);
+
+ g_free(path);
+ g_free(session->name);
+
+ return FALSE;
+}
+
+int messages_get_folder_listing(void *s, const char *name,
+ uint16_t max, uint16_t offset,
+ messages_folder_listing_cb callback,
+ void *user_data)
+{
+ struct session *session = s;
+ session->name = g_strdup(name);
+ session->max = max;
+ session->offset = offset;
+ session->folder_list_cb = callback;
+ session->user_data = user_data;
+
+ g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, async_get_folder_listing,
+ session, NULL);
+
+ return 0;
+}
+
+int messages_get_messages_listing(void *session, const char *name,
+ uint16_t max, uint16_t offset,
+ uint8_t subject_len,
+ const struct messages_filter *filter,
+ messages_get_messages_listing_cb callback,
+ void *user_data)
+{
+ return -ENOSYS;
+}
+
+int messages_get_message(void *session, const char *handle,
+ unsigned long flags,
+ messages_get_message_cb callback,
+ void *user_data)
+{
+ return -ENOSYS;
+}
+
+int messages_update_inbox(void *session, messages_status_cb callback,
+ void *user_data)
+{
+ return -ENOSYS;
+}
+
+int messages_set_read(void *session, const char *handle, uint8_t value,
+ messages_status_cb callback, void *user_data)
+{
+ return -ENOSYS;
+}
+
+int messages_set_delete(void *session, const char *handle, uint8_t value,
+ messages_status_cb callback,
+ void *user_data)
+{
+ return -ENOSYS;
+}
+
+void messages_abort(void *session)
+{
+}
diff --git a/obexd/plugins/pbap.c b/obexd/plugins/pbap.c
index ad932087..68a21393 100755
--- a/obexd/plugins/pbap.c
+++ b/obexd/plugins/pbap.c
@@ -65,6 +65,10 @@
#define PHONEBOOKSIZE_TAG 0X08
#define NEWMISSEDCALLS_TAG 0X09
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define PBAP_MAXLISTCOUNT_MAX_VALUE 65535
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
+
struct cache {
gboolean valid;
uint32_t index;
@@ -85,6 +89,9 @@ struct pbap_session {
uint32_t find_handle;
struct cache cache;
struct pbap_object *obj;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ void *backend_data;
+#endif
};
struct pbap_object {
@@ -194,11 +201,18 @@ static void phonebook_size_result(const char *buffer, size_t bufsize,
phonebooksize);
pbap->obj->firstpacket = TRUE;
-
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
if (missed > 0) {
+#else
+ if (pbap->params->required_missedcall_call_header == TRUE) {
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
DBG("missed %d", missed);
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
pbap->obj->apparam = g_obex_apparam_set_uint16(
+#else
+ pbap->obj->apparam = g_obex_apparam_set_uint8(
+#endif
pbap->obj->apparam,
NEWMISSEDCALLS_TAG,
missed);
@@ -231,13 +245,20 @@ static void query_result(const char *buffer, size_t bufsize, int vcards,
else
pbap->obj->buffer = g_string_append_len(pbap->obj->buffer,
buffer, bufsize);
-
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
if (missed > 0) {
+#else
+ if (pbap->params->required_missedcall_call_header == TRUE) {
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
DBG("missed %d", missed);
pbap->obj->firstpacket = TRUE;
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
pbap->obj->apparam = g_obex_apparam_set_uint16(
+#else
+ pbap->obj->apparam = g_obex_apparam_set_uint8(
+#endif
pbap->obj->apparam,
NEWMISSEDCALLS_TAG,
missed);
@@ -373,9 +394,27 @@ static int generate_response(void *user_data)
pbap->obj->apparam,
PHONEBOOKSIZE_TAG,
size);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (pbap->params->required_missedcall_call_header == TRUE) {
+ //DBG("missed %d", missed);
+ pbap->obj->apparam = g_obex_apparam_set_uint8(
+ pbap->obj->apparam,
+ NEWMISSEDCALLS_TAG,
+ pbap->params->new_missed_calls);
+ }
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
return 0;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (pbap->params->required_missedcall_call_header == TRUE) {
+ pbap->obj->firstpacket = TRUE;
+ pbap->obj->apparam = g_obex_apparam_set_uint8(
+ pbap->obj->apparam,
+ NEWMISSEDCALLS_TAG,
+ pbap->params->new_missed_calls);
+ }
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
/*
* Don't free the sorted list content: this list contains
@@ -406,12 +445,18 @@ static int generate_response(void *user_data)
return 0;
}
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
static void cache_ready_notify(void *user_data)
+#else
+static void cache_ready_notify(void *user_data, unsigned int new_missed_call)
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
{
struct pbap_session *pbap = user_data;
DBG("");
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ pbap->params->new_missed_calls = new_missed_call;
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
phonebook_req_finalize(pbap->obj->request);
pbap->obj->request = NULL;
@@ -445,10 +490,27 @@ static void cache_entry_done(void *user_data)
obex_object_set_io_flags(pbap->obj, G_IO_ERR, ret);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void cache_clear_notify(void *user_data)
+{
+ struct pbap_session *pbap = user_data;
+
+ if (pbap == NULL)
+ return;
+
+ pbap->cache.valid = FALSE;
+ pbap->cache.index = 0;
+ cache_clear(&pbap->cache);
+}
+#endif
+
static struct apparam_field *parse_aparam(const uint8_t *buffer, uint32_t hlen)
{
GObexApparam *apparam;
struct apparam_field *param;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ gboolean bmaxlistCount = FALSE;
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
apparam = g_obex_apparam_decode(buffer, hlen);
if (apparam == NULL)
@@ -466,13 +528,27 @@ static struct apparam_field *parse_aparam(const uint8_t *buffer, uint32_t hlen)
g_obex_apparam_get_uint8(apparam, SEARCHATTRIB_TAG,
&param->searchattrib);
g_obex_apparam_get_uint8(apparam, FORMAT_TAG, &param->format);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ bmaxlistCount = g_obex_apparam_get_uint16(apparam, MAXLISTCOUNT_TAG,
+ &param->maxlistcount);
+#else
g_obex_apparam_get_uint16(apparam, MAXLISTCOUNT_TAG,
&param->maxlistcount);
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
g_obex_apparam_get_uint16(apparam, LISTSTARTOFFSET_TAG,
&param->liststartoffset);
g_obex_apparam_get_uint64(apparam, FILTER_TAG, &param->filter);
param->searchval = g_obex_apparam_get_string(apparam, SEARCHVALUE_TAG);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if(bmaxlistCount == FALSE) {
+ param->maxlistcount = PBAP_MAXLISTCOUNT_MAX_VALUE;
+ }
+ else {
+ /* Keep the MaxlistCount value as send in request from client */
+ }
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
+
DBG("o %x sa %x sv %s fil %" G_GINT64_MODIFIER "x for %x max %x off %x",
param->order, param->searchattrib, param->searchval,
param->filter, param->format, param->maxlistcount,
@@ -493,10 +569,28 @@ static void *pbap_connect(struct obex_session *os, int *err)
pbap->folder = g_strdup("/");
pbap->find_handle = PHONEBOOK_INVALID_HANDLE;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ int error;
+
+ error = phonebook_connect(&pbap->backend_data);
+ if (error < 0) {
+ if (err)
+ *err = error;
+ goto failed;
+ }
+#endif
+
if (err)
*err = 0;
return pbap;
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+failed:
+ g_free(pbap);
+
+ return NULL;
+#endif
}
static int pbap_get(struct obex_session *os, void *user_data)
@@ -612,6 +706,13 @@ static void pbap_disconnect(struct obex_session *os, void *user_data)
manager_unregister_session(os);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (!pbap)
+ return;
+
+ phonebook_disconnect(pbap->backend_data);
+#endif
+
if (pbap->obj)
pbap->obj->session = NULL;
@@ -676,6 +777,13 @@ static void *vobject_pull_open(const char *name, int oflag, mode_t mode,
ret = -EBADR;
goto fail;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (strcmp(name, "/telecom/mch.vcf") == 0)
+ pbap->params->required_missedcall_call_header = TRUE;
+
+ DBG("[%s] - required_missedcall_call_header [%d] ",
+ name,pbap->params->required_missedcall_call_header);
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
if (pbap->params->maxlistcount == 0)
cb = phonebook_size_result;
@@ -746,6 +854,13 @@ static void *vobject_list_open(const char *name, int oflag, mode_t mode,
ret = -EPERM;
goto fail;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (strcmp(name, "/telecom/mch") == 0)
+ pbap->params->required_missedcall_call_header = TRUE;
+
+ DBG("[%s] - required_missedcall_call_header [%d] ",
+ name,pbap->params->required_missedcall_call_header);
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
/* PullvCardListing always get the contacts from the cache */
@@ -757,6 +872,10 @@ static void *vobject_list_open(const char *name, int oflag, mode_t mode,
cache_ready_notify, pbap, &ret);
if (ret == 0)
obj = vobject_create(pbap, request);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ phonebook_set_cache_notification(pbap->backend_data,
+ cache_clear_notify, pbap);
+#endif
}
if (ret < 0)
goto fail;
@@ -782,7 +901,11 @@ static void *vobject_vcard_open(const char *name, int oflag, mode_t mode,
struct pbap_session *pbap = context;
const char *id;
uint32_t handle;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ int ret = 0;
+#else
int ret;
+#endif
void *request;
DBG("name %s context %p valid %d", name, context, pbap->cache.valid);
@@ -799,8 +922,18 @@ static void *vobject_vcard_open(const char *name, int oflag, mode_t mode,
if (pbap->cache.valid == FALSE) {
pbap->find_handle = handle;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ request = phonebook_create_cache(pbap->folder,
+ (phonebook_entry_cb)cache_entry_notify,
+ (phonebook_cache_ready_cb)cache_entry_done, pbap, &ret);
+#else
request = phonebook_create_cache(pbap->folder,
cache_entry_notify, cache_entry_done, pbap, &ret);
+#endif
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ phonebook_set_cache_notification(pbap->backend_data,
+ cache_clear_notify, pbap);
+#endif
goto done;
}
@@ -841,8 +974,15 @@ static ssize_t vobject_pull_get_next_header(void *object, void *buf, size_t mtu,
if (obj->firstpacket) {
obj->firstpacket = FALSE;
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ gsize count = 0;
+ count = g_obex_apparam_encode(obj->apparam, buf, mtu);
+ DBG("APPARAM Processed remove tags");
+ g_obex_apparam_remove_all(obj->apparam);
+ return count;
+#else
return g_obex_apparam_encode(obj->apparam, buf, mtu);
+#endif
}
return 0;
@@ -891,11 +1031,22 @@ static ssize_t vobject_list_get_next_header(void *object, void *buf, size_t mtu,
return -EAGAIN;
*hi = G_OBEX_HDR_APPARAM;
-
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
if (pbap->params->maxlistcount == 0)
return g_obex_apparam_encode(obj->apparam, buf, mtu);
return 0;
+#else
+ if (obj->apparam != NULL) {
+ gsize count = 0;
+ count = g_obex_apparam_encode(obj->apparam, buf, mtu);
+ DBG("APPARAM Processed remove tags");
+ g_obex_apparam_remove_all(obj->apparam);
+ return count;
+ }
+ else
+ return 0;
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
}
static ssize_t vobject_list_read(void *object, void *buf, size_t count)
diff --git a/obexd/plugins/phonebook-dummy.c b/obexd/plugins/phonebook-dummy.c
index 29ae8893..322be4de 100755
--- a/obexd/plugins/phonebook-dummy.c
+++ b/obexd/plugins/phonebook-dummy.c
@@ -524,6 +524,11 @@ void *phonebook_get_entry(const char *folder, const char *id,
filename = g_build_filename(root_folder, folder, id, NULL);
fd = open(filename, O_RDONLY);
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ g_free(filename);
+#endif
+
if (fd < 0) {
DBG("open(): %s(%d)", strerror(errno), errno);
if (err)
diff --git a/obexd/plugins/phonebook-ebook.c b/obexd/plugins/phonebook-ebook.c
new file mode 100755
index 00000000..c422585d
--- /dev/null
+++ b/obexd/plugins/phonebook-ebook.c
@@ -0,0 +1,708 @@
+/*
+ *
+ * OBEX Server
+ *
+ * Copyright (C) 2009-2010 Intel Corporation
+ * Copyright (C) 2007-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <errno.h>
+
+#include <glib.h>
+#include <libebook/e-book.h>
+
+#include "lib/bluetooth.h"
+
+#include "obexd/src/log.h"
+#include "obexd/src/obex.h"
+#include "obexd/src/service.h"
+#include "phonebook.h"
+
+#define QUERY_FN "(contains \"family_name\" \"%s\")"
+#define QUERY_NAME "(contains \"given_name\" \"%s\")"
+#define QUERY_PHONE "(contains \"phone\" \"%s\")"
+
+struct query_context {
+ const struct apparam_field *params;
+ phonebook_cb contacts_cb;
+ phonebook_entry_cb entry_cb;
+ phonebook_cache_ready_cb ready_cb;
+ EBookQuery *query;
+ unsigned int count;
+ GString *buf;
+ char *id;
+ unsigned queued_calls;
+ void *user_data;
+ GSList *ebooks;
+ gboolean canceled;
+};
+
+static char *attribute_mask[] = {
+/* 0 */ "VERSION",
+ "FN",
+ "N",
+ "PHOTO",
+ "BDAY",
+ "ADR",
+ "LABEL",
+ "TEL",
+/* 8 */ "EMAIL",
+ "MAILER",
+ "TZ",
+ "GEO",
+ "TITLE",
+ "ROLE",
+ "LOGO",
+ "AGENT",
+/* 16 */ "ORG",
+ "NOTE",
+ "REV",
+ "SOUND",
+ "URL",
+ "UID",
+ "KEY",
+ "NICKNAME",
+/* 24 */ "CATEGORIES",
+ "PROID",
+ "CLASS",
+ "SORT-STRING",
+/* 28 */ "X-IRMC-CALL-DATETIME",
+ NULL
+
+};
+
+static void close_ebooks(GSList *ebooks)
+{
+ g_slist_free_full(ebooks, g_object_unref);
+}
+
+static void free_query_context(struct query_context *data)
+{
+ g_free(data->id);
+
+ if (data->buf != NULL)
+ g_string_free(data->buf, TRUE);
+
+ if (data->query != NULL)
+ e_book_query_unref(data->query);
+
+ close_ebooks(data->ebooks);
+
+ g_free(data);
+}
+
+static char *evcard_to_string(EVCard *evcard, unsigned int format,
+ uint64_t filter)
+{
+ EVCard *evcard2;
+ GList *l;
+ char *vcard;
+
+ if (!filter)
+ return e_vcard_to_string(evcard, EVC_FORMAT_VCARD_30);
+ /* XXX There is no support for VCARD 2.1 at this time */
+
+ /*
+ * Mandatory attributes for vCard 2.1 are VERSION ,N and TEL.
+ * Mandatory attributes for vCard 3.0 are VERSION, N, FN and TEL
+ */
+ filter = format == EVC_FORMAT_VCARD_30 ? filter | 0x87: filter | 0x85;
+
+ l = e_vcard_get_attributes(evcard);
+ evcard2 = e_vcard_new();
+ for (; l; l = g_list_next(l)) {
+ EVCardAttribute *attrib = l->data;
+ const char *name;
+ int i;
+
+ if (!attrib)
+ continue;
+
+ name = e_vcard_attribute_get_name(attrib);
+
+ for (i = 0; attribute_mask[i] != NULL; i++) {
+ if (!(filter & (1 << i)))
+ continue;
+ if (g_strcmp0(name, attribute_mask[i]) != 0)
+ continue;
+
+ e_vcard_add_attribute(evcard2,
+ e_vcard_attribute_copy(attrib));
+ }
+ }
+
+ vcard = e_vcard_to_string(evcard2, format);
+ g_object_unref(evcard2);
+
+ return vcard;
+}
+
+static void ebookpull_cb(EBook *book, const GError *gerr, GList *contacts,
+ void *user_data)
+{
+ struct query_context *data = user_data;
+ GList *l;
+ unsigned int count, maxcount;
+
+ data->queued_calls--;
+
+ if (data->canceled)
+ goto canceled;
+
+ if (gerr != NULL) {
+ error("E-Book query failed: %s", gerr->message);
+ goto done;
+ }
+
+ DBG("");
+
+ /*
+ * When MaxListCount is zero, PCE wants to know the number of used
+ * indexes in the phonebook of interest. All other parameters that
+ * may be present in the request shall be ignored.
+ */
+ maxcount = data->params->maxlistcount;
+ if (maxcount == 0) {
+ data->count += g_list_length(contacts);
+ goto done;
+ }
+
+ l = g_list_nth(contacts, data->params->liststartoffset);
+
+ for (count = 0; l && count + data->count < maxcount; l = g_list_next(l),
+ count++) {
+ EContact *contact = E_CONTACT(l->data);
+ EVCard *evcard = E_VCARD(contact);
+ char *vcard;
+
+ vcard = evcard_to_string(evcard, EVC_FORMAT_VCARD_30,
+ data->params->filter);
+
+ data->buf = g_string_append(data->buf, vcard);
+ data->buf = g_string_append(data->buf, "\r\n");
+ g_free(vcard);
+ }
+
+ DBG("collected %d vcards", count);
+
+ data->count += count;
+
+ g_list_free_full(contacts, g_object_unref);
+
+done:
+ if (data->queued_calls == 0) {
+ GString *buf = data->buf;
+ data->buf = NULL;
+
+ data->contacts_cb(buf->str, buf->len, data->count,
+ 0, TRUE, data->user_data);
+
+ g_string_free(buf, TRUE);
+
+ }
+
+ return;
+
+canceled:
+ if (data->queued_calls == 0)
+ free_query_context(data);
+}
+
+static void ebook_entry_cb(EBook *book, const GError *gerr,
+ EContact *contact, void *user_data)
+{
+ struct query_context *data = user_data;
+ EVCard *evcard;
+ char *vcard;
+ size_t len;
+
+ data->queued_calls--;
+
+ if (data->canceled)
+ goto done;
+
+ if (gerr != NULL) {
+ error("E-Book query failed: %s", gerr->message);
+ goto done;
+ }
+
+ DBG("");
+
+ evcard = E_VCARD(contact);
+
+ vcard = evcard_to_string(evcard, EVC_FORMAT_VCARD_30,
+ data->params->filter);
+
+ len = vcard ? strlen(vcard) : 0;
+
+ data->count++;
+ data->contacts_cb(vcard, len, 1, 0, TRUE, data->user_data);
+
+ g_free(vcard);
+ g_object_unref(contact);
+
+ return;
+
+done:
+ if (data->queued_calls == 0) {
+ if (data->count == 0)
+ data->contacts_cb(NULL, 0, 1, 0, TRUE,
+ data->user_data);
+ else if (data->canceled)
+ free_query_context(data);
+ }
+}
+
+static char *evcard_name_attribute_to_string(EVCard *evcard)
+{
+ EVCardAttribute *attrib;
+ GList *l;
+ GString *name = NULL;
+
+ attrib = e_vcard_get_attribute(evcard, EVC_N);
+ if (!attrib)
+ return NULL;
+
+ for (l = e_vcard_attribute_get_values(attrib); l; l = l->next) {
+ const char *value = l->data;
+
+ if (!strlen(value))
+ continue;
+
+ if (!name)
+ name = g_string_new(value);
+ else {
+ name = g_string_append(name, ";");
+ name = g_string_append(name, l->data);
+ }
+ }
+
+ if (!name)
+ return NULL;
+
+ return g_string_free(name, FALSE);
+}
+
+static void cache_cb(EBook *book, const GError *gerr, GList *contacts,
+ void *user_data)
+{
+ struct query_context *data = user_data;
+ GList *l;
+
+ data->queued_calls--;
+
+ if (data->canceled)
+ goto canceled;
+
+ if (gerr != NULL) {
+ error("E-Book operation failed: %s", gerr->message);
+ goto done;
+ }
+
+ DBG("");
+
+ for (l = contacts; l; l = g_list_next(l)) {
+ EContact *contact = E_CONTACT(l->data);
+ EVCard *evcard = E_VCARD(contact);
+ EVCardAttribute *attrib;
+ char *uid, *tel, *name;
+
+ name = evcard_name_attribute_to_string(evcard);
+ if (!name)
+ continue;
+
+ attrib = e_vcard_get_attribute(evcard, EVC_UID);
+ if (!attrib)
+ continue;
+
+ uid = e_vcard_attribute_get_value(attrib);
+ if (!uid)
+ continue;
+
+ attrib = e_vcard_get_attribute(evcard, EVC_TEL);
+ if (attrib)
+ tel = e_vcard_attribute_get_value(attrib);
+ else
+ tel = g_strdup("");
+
+ data->entry_cb(uid, PHONEBOOK_INVALID_HANDLE, name, NULL,
+ tel, data->user_data);
+
+ g_free(name);
+ g_free(uid);
+ g_free(tel);
+ }
+
+ g_list_free_full(contacts, g_object_unref);
+
+done:
+ if (data->queued_calls == 0)
+ data->ready_cb(data->user_data);
+
+ return;
+
+canceled:
+ if (data->queued_calls == 0)
+ free_query_context(data);
+}
+
+static GSList *traverse_sources(GSList *ebooks, GSList *sources,
+ char **default_src) {
+ GError *gerr = NULL;
+
+ for (; sources != NULL; sources = g_slist_next(sources)) {
+ char *uri;
+ ESource *source = E_SOURCE(sources->data);
+ EBook *ebook = e_book_new(source, &gerr);
+
+ if (ebook == NULL) {
+ error("Can't create user's address book: %s",
+ gerr->message);
+ g_clear_error(&gerr);
+ continue;
+ }
+
+ uri = e_source_get_uri(source);
+ if (g_strcmp0(*default_src, uri) == 0) {
+ g_free(uri);
+ continue;
+ }
+ g_free(uri);
+
+ if (e_book_open(ebook, FALSE, &gerr) == FALSE) {
+ error("Can't open e-book address book: %s",
+ gerr->message);
+ g_object_unref(ebook);
+ g_clear_error(&gerr);
+ continue;
+ }
+
+ if (*default_src == NULL)
+ *default_src = e_source_get_uri(source);
+
+ DBG("%s address book opened", e_source_peek_name(source));
+
+ ebooks = g_slist_append(ebooks, ebook);
+ }
+
+ return ebooks;
+}
+
+int phonebook_init(void)
+{
+ g_type_init();
+
+ return 0;
+}
+
+static GSList *open_ebooks(void)
+{
+ GError *gerr = NULL;
+ ESourceList *src_list;
+ GSList *list;
+ char *default_src = NULL;
+ GSList *ebooks = NULL;
+
+ if (e_book_get_addressbooks(&src_list, &gerr) == FALSE) {
+ error("Can't list user's address books: %s", gerr->message);
+ g_error_free(gerr);
+ return NULL;
+ }
+
+ list = e_source_list_peek_groups(src_list);
+ while (list != NULL) {
+ ESourceGroup *group = E_SOURCE_GROUP(list->data);
+ GSList *sources = e_source_group_peek_sources(group);
+
+ ebooks = traverse_sources(ebooks, sources, &default_src);
+
+ list = list->next;
+ }
+
+ g_free(default_src);
+ g_object_unref(src_list);
+
+ return ebooks;
+}
+
+void phonebook_exit(void)
+{
+}
+
+char *phonebook_set_folder(const char *current_folder,
+ const char *new_folder, uint8_t flags, int *err)
+{
+ gboolean root, child;
+ char *fullname = NULL, *tmp1, *tmp2, *base;
+ int ret = 0, len;
+
+ root = (g_strcmp0("/", current_folder) == 0);
+ child = (new_folder && strlen(new_folder) != 0);
+
+ /* Evolution back-end will support /telecom/pb folder only */
+
+ switch (flags) {
+ case 0x02:
+ /* Go back to root */
+ if (!child) {
+ fullname = g_strdup("/");
+ goto done;
+ }
+
+ /* Go down 1 level */
+ fullname = g_build_filename(current_folder, new_folder, NULL);
+ if (strcmp(PB_TELECOM_FOLDER, fullname) != 0 &&
+ strcmp(PB_CONTACTS_FOLDER, fullname) != 0) {
+ g_free(fullname);
+ fullname = NULL;
+ ret = -ENOENT;
+ }
+
+ break;
+ case 0x03:
+ /* Go up 1 level */
+ if (root) {
+ /* Already root */
+ ret = -EBADR;
+ goto done;
+ }
+
+ /*
+ * Removing one level of the current folder. Current folder
+ * contains AT LEAST one level since it is not at root folder.
+ * Use glib utility functions to handle invalid chars in the
+ * folder path properly.
+ */
+ tmp1 = g_path_get_basename(current_folder);
+ tmp2 = g_strrstr(current_folder, tmp1);
+ len = tmp2 - (current_folder + 1);
+
+ g_free(tmp1);
+
+ if (len == 0)
+ base = g_strdup("/");
+ else
+ base = g_strndup(current_folder, len);
+
+ /* Return one level only */
+ if (!child) {
+ fullname = base;
+ goto done;
+ }
+
+ fullname = g_build_filename(base, new_folder, NULL);
+ if (strcmp(fullname, PB_TELECOM_FOLDER) != 0 &&
+ strcmp(fullname, PB_CONTACTS_FOLDER) != 0) {
+ g_free(fullname);
+ fullname = NULL;
+ ret = -ENOENT;
+ }
+
+ g_free(base);
+
+ break;
+ default:
+ ret = -EBADR;
+ break;
+ }
+
+done:
+ if (err)
+ *err = ret;
+
+ return fullname;
+}
+
+void phonebook_req_finalize(void *request)
+{
+ struct query_context *data = request;
+
+ if (data->queued_calls == 0)
+ free_query_context(data);
+ else
+ data->canceled = TRUE;
+}
+
+void *phonebook_pull(const char *name, const struct apparam_field *params,
+ phonebook_cb cb, void *user_data, int *err)
+{
+ struct query_context *data;
+
+ if (g_strcmp0(PB_CONTACTS, name) != 0) {
+ if (err)
+ *err = -ENOENT;
+
+ return NULL;
+ }
+
+ data = g_new0(struct query_context, 1);
+ data->contacts_cb = cb;
+ data->params = params;
+ data->user_data = user_data;
+ data->buf = g_string_new("");
+ data->query = e_book_query_any_field_contains("");
+ data->ebooks = open_ebooks();
+
+ if (err)
+ *err = data->ebooks == NULL ? -EIO : 0;
+
+ return data;
+}
+
+int phonebook_pull_read(void *request)
+{
+ struct query_context *data = request;
+ GSList *l;
+
+ if (!data)
+ return -ENOENT;
+
+ for (l = data->ebooks; l != NULL; l = g_slist_next(l)) {
+ EBook *ebook = l->data;
+
+ if (e_book_is_opened(ebook) == FALSE)
+ continue;
+
+ if (e_book_get_contacts_async(ebook, data->query,
+ ebookpull_cb, data) == TRUE)
+ data->queued_calls++;
+ }
+
+ if (data->queued_calls == 0)
+ return -ENOENT;
+
+ return 0;
+}
+
+void *phonebook_get_entry(const char *folder, const char *id,
+ const struct apparam_field *params,
+ phonebook_cb cb, void *user_data, int *err)
+{
+ struct query_context *data;
+ GSList *l;
+
+ data = g_new0(struct query_context, 1);
+ data->contacts_cb = cb;
+ data->params = params;
+ data->user_data = user_data;
+ data->id = g_strdup(id);
+ data->ebooks = open_ebooks();
+
+ for (l = data->ebooks; l != NULL; l = g_slist_next(l)) {
+ EBook *ebook = l->data;
+
+ if (e_book_is_opened(ebook) == FALSE)
+ continue;
+
+ if (e_book_get_contact_async(ebook, data->id,
+ ebook_entry_cb, data) == TRUE)
+ data->queued_calls++;
+ }
+
+ if (err)
+ *err = (data->queued_calls == 0 ? -ENOENT : 0);
+
+ return data;
+}
+
+void *phonebook_create_cache(const char *name, phonebook_entry_cb entry_cb,
+ phonebook_cache_ready_cb ready_cb, void *user_data, int *err)
+{
+ struct query_context *data;
+ EBookQuery *query;
+ GSList *l;
+ EContact *me;
+ EVCard *evcard;
+ GError *gerr = NULL;
+ EBook *eb;
+ EVCardAttribute *attrib;
+ char *uid, *tel, *cname;
+
+ if (g_strcmp0(PB_CONTACTS_FOLDER, name) != 0) {
+ if (err)
+ *err = -ENOENT;
+
+ return NULL;
+ }
+
+ DBG("");
+
+ query = e_book_query_any_field_contains("");
+
+ data = g_new0(struct query_context, 1);
+ data->entry_cb = entry_cb;
+ data->ready_cb = ready_cb;
+ data->user_data = user_data;
+ data->query = query;
+ data->ebooks = open_ebooks();
+
+ /* Add 0.vcf */
+ if (e_book_get_self(&me, &eb, &gerr) == FALSE) {
+ g_error_free(gerr);
+ goto next;
+ }
+
+ evcard = E_VCARD(me);
+
+ cname = evcard_name_attribute_to_string(evcard);
+ if (!cname)
+ cname = g_strdup("");
+
+ attrib = e_vcard_get_attribute(evcard, EVC_UID);
+ uid = e_vcard_attribute_get_value(attrib);
+ if (!uid)
+ uid = g_strdup("");
+
+ attrib = e_vcard_get_attribute(evcard, EVC_TEL);
+ if (attrib)
+ tel = e_vcard_attribute_get_value(attrib);
+ else
+ tel = g_strdup("");
+
+ data->entry_cb(uid, 0, cname, NULL, tel, data->user_data);
+
+ data->count++;
+
+ g_free(cname);
+ g_free(uid);
+ g_free(tel);
+ g_object_unref(eb);
+
+next:
+ for (l = data->ebooks; l != NULL; l = g_slist_next(l)) {
+ EBook *ebook = l->data;
+
+ if (e_book_is_opened(ebook) == FALSE)
+ continue;
+
+ if (e_book_get_contacts_async(ebook, query,
+ cache_cb, data) == TRUE)
+ data->queued_calls++;
+ }
+
+ if (err)
+ *err = (data->queued_calls == 0 ? -ENOENT : 0);
+
+ return data;
+}
diff --git a/obexd/plugins/phonebook-tizen.c b/obexd/plugins/phonebook-tizen.c
new file mode 100755
index 00000000..dcb61796
--- /dev/null
+++ b/obexd/plugins/phonebook-tizen.c
@@ -0,0 +1,813 @@
+/*
+ *
+ * OBEX Server
+ *
+ * Copyright (c) 2000-2016 Samsung Electronics Co., Ltd. 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 Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <dirent.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdint.h>
+#include <string.h>
+#include <stdarg.h>
+#include <glib.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "log.h"
+#include "phonebook.h"
+
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#define PHONEBOOK_DESTINATION "org.bluez.pb_agent"
+#define PHONEBOOK_PATH "/org/bluez/pb_agent"
+#define PHONEBOOK_INTERFACE "org.bluez.PbAgent"
+
+#define QUERY_GET_PHONEBOOK_FOLDER_LIST "GetPhonebookFolderList"
+#define QUERY_GET_PHONEBOOK_SIZE "GetPhonebookSize"
+#define QUERY_GET_PHONEBOOK "GetPhonebook"
+#define QUERY_GET_PHONEBOOK_LIST "GetPhonebookList"
+#define QUERY_GET_PHONEBOOK_ENTRY "GetPhonebookEntry"
+#define QUERY_DESTROY_AGENT "DestroyAgent"
+
+static GPtrArray *folder_list = NULL;
+
+struct phonebook_data {
+ phonebook_cb cb;
+ DBusPendingCallNotifyFunction reply_cb;
+ DBusPendingCall *call;
+ void *user_data;
+ const struct apparam_field *params;
+
+ phonebook_entry_cb entry_cb;
+ phonebook_cache_ready_cb ready_cb;
+
+ char *req_name;
+};
+
+struct phonebook_session {
+ DBusConnection *connection;
+ phonebook_cache_clear_cb clear_cb;
+ unsigned int clear_id;
+
+ void *user_data;
+};
+
+static gboolean folder_is_valid(const gchar *folder,
+ gboolean leaf_only)
+{
+ int i;
+ int folder_len;
+
+ if (folder_list == NULL || folder == NULL)
+ return FALSE;
+
+ folder_len = strlen(folder);
+
+ for (i = 0 ; i < folder_list->len; i++) {
+ char *str = (char *)g_ptr_array_index(folder_list, i);
+
+ if (folder_len > strlen(str))
+ continue;
+
+ if (g_strcmp0(str, folder) == 0)
+ return TRUE;
+
+ if (leaf_only == TRUE)
+ continue;
+
+ if (strncmp(str, folder, folder_len) == 0) {
+
+ if (*(folder + folder_len - 1) == '/')
+ return TRUE; /* folder is ended with '/' */
+
+ if (*(str + folder_len) == '/')
+ return TRUE; /* folder is matched */
+ }
+ }
+
+ return FALSE;
+}
+
+static DBusPendingCall* phonebook_request(struct phonebook_data *data,
+ const gchar *method,
+ DBusPendingCallNotifyFunction function,
+ int first_arg_type,
+ ...)
+{
+ DBusConnection *connection;
+ DBusPendingCall *call;
+ DBusMessage *message;
+
+ va_list args;
+
+ DBG("%s\n", method);
+
+ if (!method) {
+ error("Can't get method name");
+ return NULL;
+ }
+
+ connection = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL);
+ if (!connection) {
+ error("Can't get on session bus");
+ return NULL;
+ }
+
+ message = dbus_message_new_method_call(PHONEBOOK_DESTINATION,
+ PHONEBOOK_PATH,
+ PHONEBOOK_INTERFACE,
+ method);
+ if (!message) {
+ dbus_connection_unref(connection);
+ error("Can't Allocate new message");
+ return NULL;
+ }
+
+ va_start(args, first_arg_type);
+ dbus_message_append_args_valist(message, first_arg_type, args);
+ va_end(args);
+
+ if (dbus_connection_send_with_reply(connection, message, &call, -1) == FALSE) {
+ dbus_message_unref(message);
+ dbus_connection_unref(connection);
+ DBG("-");
+ return NULL;
+ }
+ dbus_pending_call_set_notify(call, function, data, NULL);
+
+ dbus_message_unref(message);
+ dbus_connection_unref(connection);
+
+ DBG("-");
+ return call;
+}
+
+static void get_phonebook_size_reply(DBusPendingCall *call, void *user_data)
+{
+ DBusMessage *reply = dbus_pending_call_steal_reply(call);
+ struct phonebook_data *s_data = user_data;
+ DBusError derr;
+ uint32_t phonebook_size;
+
+ unsigned int new_missed_call = 0;
+
+ DBG("");
+ dbus_pending_call_unref(s_data->call);
+ s_data->call = NULL;
+
+ if (!reply) {
+ DBG("Reply Error\n");
+ return;
+ }
+
+ dbus_error_init(&derr);
+ if (dbus_set_error_from_message(&derr, reply)) {
+ error("Replied with an error: %s, %s", derr.name, derr.message);
+ dbus_error_free(&derr);
+ phonebook_size = 0;
+ } else {
+ dbus_message_get_args(reply, NULL,
+ DBUS_TYPE_UINT32, &phonebook_size,
+ DBUS_TYPE_UINT32, &new_missed_call,
+ DBUS_TYPE_INVALID);
+ DBG("phonebooksize:%d\n", phonebook_size);
+ }
+
+ s_data->cb(NULL, 0, phonebook_size, new_missed_call, TRUE, s_data->user_data);
+
+ dbus_message_unref(reply);
+}
+
+static void get_phonebook_reply(DBusPendingCall *call, void *user_data)
+{
+ DBusMessage *reply = dbus_pending_call_steal_reply(call);
+ struct phonebook_data *s_data = user_data;
+ DBusError derr;
+ GString *buffer;
+
+ unsigned int new_missed_call = 0;
+
+ uint32_t count = 0;
+
+ DBG("");
+ dbus_pending_call_unref(s_data->call);
+ s_data->call = NULL;
+
+ if (!reply) {
+ DBG("Reply Error\n");
+ return;
+ }
+
+ buffer = g_string_new("");
+
+ dbus_error_init(&derr);
+ if (dbus_set_error_from_message(&derr, reply)) {
+ error("Replied with an error: %s, %s", derr.name, derr.message);
+ dbus_error_free(&derr);
+ } else {
+ DBusMessageIter iter;
+
+ dbus_message_iter_init(reply, &iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_ARRAY) {
+ DBusMessageIter recurse_iter;
+
+ dbus_message_iter_recurse(&iter, &recurse_iter);
+ while (dbus_message_iter_get_arg_type(&recurse_iter) == DBUS_TYPE_STRING) {
+ gchar *str;
+
+ dbus_message_iter_get_basic(&recurse_iter, &str);
+ dbus_message_iter_next(&recurse_iter);
+
+ g_string_append(buffer, str);
+
+ DBG("str:\n%s\n", str);
+
+ count++;
+ }
+ dbus_message_iter_next(&iter);
+ }
+
+ if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_UINT32)
+ dbus_message_iter_get_basic(&iter, &new_missed_call);
+
+ DBG("new_missed_call %d\n", new_missed_call);
+ }
+
+ s_data->cb(buffer->str, buffer->len, count, new_missed_call, 1, s_data->user_data);
+
+ g_string_free(buffer, TRUE);
+ dbus_message_unref(reply);
+}
+
+
+static void get_phonebook_list_reply(DBusPendingCall *call, void *user_data)
+{
+ DBusMessage *reply = dbus_pending_call_steal_reply(call);
+ DBusMessageIter iter, iter_struct, entry;
+ struct phonebook_data *data = user_data;
+ DBusError derr;
+ uint32_t handle = 0;
+ const char *name = NULL;
+ const char *tel = NULL;
+ char id[10];
+ unsigned int new_missed_call = 0;
+
+ dbus_pending_call_unref(data->call);
+ data->call = NULL;
+ if (!reply) {
+ DBG("Reply Error\n");
+ return;
+ }
+
+ dbus_error_init(&derr);
+ if (dbus_set_error_from_message(&derr, reply)) {
+ error("Replied with an error: %s, %s", derr.name, derr.message);
+ dbus_error_free(&derr);
+ } else {
+
+ dbus_message_iter_init(reply, &iter);
+
+ dbus_message_iter_recurse(&iter, &iter_struct);
+
+ while (dbus_message_iter_get_arg_type(&iter_struct) ==
+ DBUS_TYPE_STRUCT) {
+ dbus_message_iter_recurse(&iter_struct, &entry);
+
+ dbus_message_iter_get_basic(&entry, &name);
+ dbus_message_iter_next(&entry);
+ dbus_message_iter_get_basic(&entry, &tel);
+ dbus_message_iter_next(&entry);
+ dbus_message_iter_get_basic(&entry, &handle);
+
+ /*
+ DBG("[handle:%d name:%s tel:%s]\n", handle, name, tel);
+ */
+
+ snprintf(id, sizeof(id), "%d.vcf", handle);
+
+ data->entry_cb(id, handle, name, NULL, tel,
+ data->user_data);
+
+ dbus_message_iter_next(&iter_struct);
+ }
+
+ dbus_message_iter_next(&iter);
+ if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_UINT32) {
+ dbus_message_iter_get_basic(&iter, &new_missed_call);
+ }
+
+ DBG("new_missed_call %d\n", new_missed_call);
+ }
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ data->ready_cb(data->user_data, new_missed_call);
+#else
+ data->ready_cb(data->user_data);
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
+
+ dbus_message_unref(reply);
+}
+
+static void get_phonebook_entry_reply(DBusPendingCall *call, void *user_data)
+{
+ DBusMessage *reply = dbus_pending_call_steal_reply(call);
+ struct phonebook_data *s_data = user_data;
+ DBusError derr;
+ const char *phonebook_entry;
+
+ DBG("");
+ dbus_pending_call_unref(s_data->call);
+ s_data->call = NULL;
+
+ if (!reply) {
+ DBG("Reply Error\n");
+ return;
+ }
+
+ dbus_error_init(&derr);
+ if (dbus_set_error_from_message(&derr, reply)) {
+ error("Replied with an error: %s, %s", derr.name, derr.message);
+ dbus_error_free(&derr);
+ } else {
+ dbus_message_get_args(reply, NULL, DBUS_TYPE_STRING,
+ &phonebook_entry, DBUS_TYPE_INVALID);
+ DBG("phonebook_entry:[%s]\n", phonebook_entry);
+ }
+
+ s_data->cb(phonebook_entry, strlen(phonebook_entry), 1, 0, TRUE,
+ s_data->user_data);
+
+ dbus_message_unref(reply);
+}
+
+static gboolean clear_signal(DBusConnection *conn, DBusMessage *msg,
+ void *user_data)
+{
+ struct phonebook_session *session;
+
+ if (user_data == NULL)
+ return FALSE;
+
+ DBG("");
+ session = user_data;
+
+ session->clear_cb(session->user_data);
+
+ g_dbus_remove_watch(session->connection, session->clear_id);
+ session->clear_id = 0;
+
+ return TRUE;
+}
+
+static gboolean phonebook_folder_list_init(struct phonebook_session *session)
+{
+ DBusMessage *message;
+ DBusMessage *reply;
+
+ DBusMessageIter iter;
+ DBusMessageIter recurse_iter;
+
+ DBusError error;
+
+ if (session->connection == NULL) {
+ session->connection = g_dbus_setup_bus(DBUS_BUS_SYSTEM,
+ NULL, NULL);
+ }
+
+ if (session->connection == NULL) {
+ DBG("Can't get on s bus");
+ return FALSE;
+ }
+
+ message = dbus_message_new_method_call(PHONEBOOK_DESTINATION,
+ PHONEBOOK_PATH,
+ PHONEBOOK_INTERFACE,
+ QUERY_GET_PHONEBOOK_FOLDER_LIST);
+
+ if (message == NULL) {
+ DBG("Can't allocate dbus message");
+ return FALSE;
+ }
+
+ dbus_error_init(&error);
+
+ reply = dbus_connection_send_with_reply_and_block(session->connection,
+ message, -1, &error);
+
+ if (!reply) {
+ if (dbus_error_is_set(&error) == TRUE) {
+ DBG("%s", error.message);
+ dbus_error_free(&error);
+ } else {
+ DBG("Failed to get contacts");
+ }
+ dbus_message_unref(message);
+ return FALSE;
+ }
+
+ dbus_message_iter_init(reply, &iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) {
+ dbus_message_unref(message);
+ dbus_message_unref(reply);
+ return FALSE;
+ }
+
+ if (folder_list) {
+ g_ptr_array_free(folder_list, TRUE);
+ folder_list = NULL;
+ }
+
+ folder_list = g_ptr_array_new();
+
+ dbus_message_iter_recurse(&iter, &recurse_iter);
+
+ while (dbus_message_iter_get_arg_type(&recurse_iter)
+ == DBUS_TYPE_STRING) {
+ gchar *str;
+
+ dbus_message_iter_get_basic(&recurse_iter, &str);
+ DBG("folder list %s\n", str);
+ g_ptr_array_add(folder_list, g_strdup(str));
+
+ dbus_message_iter_next(&recurse_iter);
+ }
+
+ dbus_message_unref(message);
+ dbus_message_unref(reply);
+ return TRUE;
+}
+
+int phonebook_init(void)
+{
+ return 0;
+}
+
+void phonebook_exit(void)
+{
+}
+
+int phonebook_connect(void **user_data)
+{
+ struct phonebook_session *session;
+ gboolean ret;
+
+ DBG("");
+
+ session = g_new0(struct phonebook_session, 1);
+
+ *user_data = session;
+
+ ret = phonebook_folder_list_init(session);
+
+ if (ret)
+ return 0;
+
+ return -1;
+}
+
+void phonebook_disconnect(void *user_data)
+{
+ struct phonebook_session *session;
+ DBusMessage *message;
+ DBG("");
+ session = user_data;
+
+ if (folder_list) {
+ g_ptr_array_free(folder_list, TRUE);
+ folder_list = NULL;
+ }
+
+ if (!session->connection) {
+ g_free(session);
+ return;
+ }
+
+
+ if (session->clear_id)
+ g_dbus_remove_watch(session->connection, session->clear_id);
+
+ message = dbus_message_new_method_call(PHONEBOOK_DESTINATION,
+ PHONEBOOK_PATH,
+ PHONEBOOK_INTERFACE,
+ QUERY_DESTROY_AGENT);
+
+ if (message) {
+ if (dbus_connection_send(session->connection, message,
+ NULL) == FALSE)
+ error("Could not send dbus message");
+
+ dbus_message_unref(message);
+ }
+
+ dbus_connection_unref(session->connection);
+ g_free(session);
+}
+
+char *phonebook_set_folder(const char *current_folder,
+ const char *new_folder, uint8_t flags, int *err)
+{
+ char *tmp1, *tmp2, *base, *path = NULL;
+ gboolean root, child;
+ int ret = 0;
+ int len;
+
+ root = (g_strcmp0("/", current_folder) == 0);
+ child = (new_folder && strlen(new_folder) != 0);
+
+ switch (flags) {
+ case 0x02:
+ /* Go back to root */
+ if (!child) {
+ path = g_strdup("/");
+ goto done;
+ }
+
+ path = g_build_filename(current_folder, new_folder, NULL);
+ break;
+ case 0x03:
+ /* Go up 1 level */
+ if (root) {
+ /* Already root */
+ path = g_strdup("/");
+ goto done;
+ }
+
+ /*
+ * Removing one level of the current folder. Current folder
+ * contains AT LEAST one level since it is not at root folder.
+ * Use glib utility functions to handle invalid chars in the
+ * folder path properly.
+ */
+ tmp1 = g_path_get_basename(current_folder);
+ tmp2 = g_strrstr(current_folder, tmp1);
+ len = tmp2 - (current_folder + 1);
+
+ g_free(tmp1);
+
+ if (len == 0)
+ base = g_strdup("/");
+ else
+ base = g_strndup(current_folder, len);
+
+ /* Return: one level only */
+ if (!child) {
+ path = base;
+ goto done;
+ }
+
+ path = g_build_filename(base, new_folder, NULL);
+ g_free(base);
+
+ break;
+ default:
+ ret = -EBADR;
+ break;
+ }
+
+done:
+ if (path && !folder_is_valid(path, FALSE))
+ ret = -ENOENT;
+
+ if (ret < 0) {
+ g_free(path);
+ path = NULL;
+ }
+
+ if (err)
+ *err = ret;
+
+ DBG("path : %s\n", path);
+
+ return path;
+}
+
+void phonebook_req_finalize(void *request)
+{
+ struct phonebook_data *data = request;
+
+ DBG("");
+
+ if (!data)
+ return;
+
+ if (data->call) {
+ dbus_pending_call_cancel(data->call);
+ dbus_pending_call_unref(data->call);
+ }
+ g_free(data->req_name);
+ g_free(data);
+}
+
+void *phonebook_pull(const char *name, const struct apparam_field *params,
+ phonebook_cb cb, void *user_data, int *err)
+{
+ struct phonebook_data *data;
+ char *folder;
+
+ DBG("name %s", name);
+
+ if (!g_str_has_suffix(name, ".vcf")) {
+ if (err)
+ *err = -ENOENT;
+
+ return NULL;
+ }
+
+ folder = g_strndup(name, strlen(name) - 4);
+
+ if (!folder_is_valid(folder, TRUE)) {
+ if (err)
+ *err = -ENOENT;
+
+ g_free(folder);
+ return NULL;
+ }
+
+ g_free(folder);
+
+ data = g_new0(struct phonebook_data, 1);
+ data->params = params;
+ data->user_data = user_data;
+ data->cb = cb;
+ data->req_name = g_strdup(name);
+
+ if (err)
+ *err = 0;
+
+ return data;
+}
+
+int phonebook_pull_read(void *request)
+{
+ struct phonebook_data *data = request;
+
+ DBG("name %s", data->req_name);
+
+ if (data->params->maxlistcount == 0) {
+ data->call = phonebook_request(data,
+ QUERY_GET_PHONEBOOK_SIZE,
+ get_phonebook_size_reply,
+ DBUS_TYPE_STRING, &data->req_name,
+ DBUS_TYPE_INVALID);
+ return 0;
+ }
+
+ data->call = phonebook_request(data,
+ QUERY_GET_PHONEBOOK,
+ get_phonebook_reply,
+ DBUS_TYPE_STRING, &data->req_name,
+ DBUS_TYPE_UINT64, &data->params->filter,
+ DBUS_TYPE_BYTE, &data->params->format,
+ DBUS_TYPE_UINT16, &data->params->maxlistcount,
+ DBUS_TYPE_UINT16, &data->params->liststartoffset,
+ DBUS_TYPE_INVALID);
+
+ return 0;
+}
+
+void *phonebook_get_entry(const char *folder, const char *id,
+ const struct apparam_field *params, phonebook_cb cb,
+ void *user_data, int *err)
+{
+ struct phonebook_data *data;
+
+ DBG("");
+ if (!g_str_has_suffix(id, ".vcf")) {
+ if (err)
+ *err = -ENOENT;
+
+ return NULL;
+ }
+
+ if (!folder_is_valid(folder, TRUE)) {
+ if (err)
+ *err = - ENOENT;
+
+ return NULL;
+ }
+
+ DBG("folder %s id %s", folder, id);
+
+ data = g_new0(struct phonebook_data, 1);
+ data->params = params;
+ data->user_data = user_data;
+ data->cb = cb;
+
+ data->call = phonebook_request(data,
+ QUERY_GET_PHONEBOOK_ENTRY,
+ get_phonebook_entry_reply,
+ DBUS_TYPE_STRING, &folder,
+ DBUS_TYPE_STRING, &id,
+ DBUS_TYPE_UINT64, &data->params->filter,
+ DBUS_TYPE_BYTE, &data->params->format,
+ DBUS_TYPE_INVALID);
+
+ if (*err) {
+ if (data->call)
+ *err = 0;
+ else {
+ *err = -ENOENT;
+ g_free(data);
+ data = NULL;
+ }
+ }
+
+ return data;
+}
+
+void *phonebook_create_cache(const char *name, phonebook_entry_cb entry_cb,
+ phonebook_cache_ready_cb ready_cb, void *user_data, int *err)
+{
+ struct phonebook_data *data;
+
+ if (!folder_is_valid(name, TRUE)) {
+ if (err)
+ *err = - ENOENT;
+
+ return NULL;
+ }
+
+ DBG("name %s", name);
+
+ data = g_new0(struct phonebook_data, 1);
+ data->user_data = user_data;
+ data->entry_cb = entry_cb;
+ data->ready_cb = ready_cb;
+
+ data->call = phonebook_request(data,
+ QUERY_GET_PHONEBOOK_LIST,
+ get_phonebook_list_reply,
+ DBUS_TYPE_STRING, &name,
+ DBUS_TYPE_INVALID);
+
+ if (*err) {
+ if (data->call)
+ *err = 0;
+ else {
+ *err = -ENOENT;
+ g_free(data);
+ data = NULL;
+ }
+ }
+
+ return data;
+}
+
+void phonebook_set_cache_notification(void *session,
+ phonebook_cache_clear_cb clear_cb,
+ void *user_data)
+{
+ struct phonebook_session *s = session;
+
+ DBG("");
+ s->clear_cb = clear_cb;
+
+ if (s->connection == NULL) {
+ s->connection = g_dbus_setup_bus(DBUS_BUS_SYSTEM,
+ NULL, NULL);
+
+ if (s->connection == NULL) {
+ error("Can't get on s bus");
+ return;
+ }
+ }
+
+ s->user_data = user_data;
+
+ if (s->clear_id) {
+ g_dbus_remove_watch(s->connection, s->clear_id);
+ s->clear_id = 0;
+ }
+
+ s->clear_id = g_dbus_add_signal_watch(s->connection,
+ NULL, PHONEBOOK_PATH, PHONEBOOK_INTERFACE,
+ "clear", clear_signal,
+ s, NULL);
+}
diff --git a/obexd/plugins/phonebook-tracker.c b/obexd/plugins/phonebook-tracker.c
new file mode 100755
index 00000000..0743629c
--- /dev/null
+++ b/obexd/plugins/phonebook-tracker.c
@@ -0,0 +1,1718 @@
+/*
+ * Phonebook access through D-Bus vCard and call history service
+ *
+ * Copyright (C) 2010 Nokia 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 <string.h>
+#include <stdlib.h>
+#include <time.h>
+#include <stdio.h>
+#include <errno.h>
+#include <glib.h>
+#include <dbus/dbus.h>
+#include <libtracker-sparql/tracker-sparql.h>
+
+#include "obexd/src/log.h"
+#include "obexd/src/obex.h"
+#include "obexd/src/service.h"
+#include "obexd/src/mimetype.h"
+#include "phonebook.h"
+#include "vcard.h"
+
+#define TRACKER_SERVICE "org.freedesktop.Tracker1"
+#define TRACKER_RESOURCES_PATH "/org/freedesktop/Tracker1/Resources"
+#define TRACKER_RESOURCES_INTERFACE "org.freedesktop.Tracker1.Resources"
+
+#define TRACKER_DEFAULT_CONTACT_ME "http://www.semanticdesktop.org/ontologies/2007/03/22/nco#default-contact-me"
+#define AFFILATION_HOME "Home"
+#define AFFILATION_WORK "Work"
+#define ADDR_FIELD_AMOUNT 7
+#define PULL_QUERY_COL_AMOUNT 23
+#define COUNT_QUERY_COL_AMOUNT 1
+
+#define COL_PHONE_AFF 0 /* work/home phone numbers */
+#define COL_FULL_NAME 1
+#define COL_FAMILY_NAME 2
+#define COL_GIVEN_NAME 3
+#define COL_ADDITIONAL_NAME 4
+#define COL_NAME_PREFIX 5
+#define COL_NAME_SUFFIX 6
+#define COL_ADDR_AFF 7 /* addresses from affilation */
+#define COL_BIRTH_DATE 8
+#define COL_NICKNAME 9
+#define COL_URL 10
+#define COL_PHOTO 11
+#define COL_ORG_ROLE 12
+#define COL_UID 13
+#define COL_TITLE 14
+#define COL_AFF_TYPE 15
+#define COL_ORG_NAME 16
+#define COL_ORG_DEPARTMENT 17
+#define COL_EMAIL_AFF 18 /* email's from affilation (work/home) */
+#define COL_DATE 19
+#define COL_SENT 20
+#define COL_ANSWERED 21
+#define CONTACTS_ID_COL 22
+#define CONTACT_ID_PREFIX "urn:uuid:"
+#define CALL_ID_PREFIX "message:"
+
+#define FAX_NUM_TYPE "http://www.semanticdesktop.org/ontologies/2007/03/22/nco#FaxNumber"
+#define MOBILE_NUM_TYPE "http://www.semanticdesktop.org/ontologies/2007/03/22/nco#CellPhoneNumber"
+
+#define MAIN_DELIM "\30" /* Main delimiter between phones, addresses, emails*/
+#define SUB_DELIM "\31" /* Delimiter used in telephone number strings*/
+#define ADDR_DELIM "\37" /* Delimiter used for address data fields */
+#define MAX_FIELDS 100 /* Max amount of fields to be concatenated at once*/
+#define VCARDS_PART_COUNT 50 /* amount of vcards sent at once to PBAP core */
+#define QUERY_OFFSET_FORMAT "%s OFFSET %d"
+
+#define CONTACTS_QUERY_ALL \
+"SELECT " \
+"(SELECT GROUP_CONCAT(fn:concat(rdf:type(?aff_number)," \
+"\"\31\", nco:phoneNumber(?aff_number)), \"\30\")" \
+"WHERE {" \
+" ?_role nco:hasPhoneNumber ?aff_number" \
+"}) " \
+"nco:fullname(?_contact) " \
+"nco:nameFamily(?_contact) " \
+"nco:nameGiven(?_contact) " \
+"nco:nameAdditional(?_contact) " \
+"nco:nameHonorificPrefix(?_contact) " \
+"nco:nameHonorificSuffix(?_contact) " \
+"(SELECT GROUP_CONCAT(fn:concat(" \
+"tracker:coalesce(nco:pobox(?aff_addr), \"\"), \"\37\"," \
+"tracker:coalesce(nco:extendedAddress(?aff_addr), \"\"), \"\37\"," \
+"tracker:coalesce(nco:streetAddress(?aff_addr), \"\"), \"\37\"," \
+"tracker:coalesce(nco:locality(?aff_addr), \"\"), \"\37\"," \
+"tracker:coalesce(nco:region(?aff_addr), \"\"), \"\37\"," \
+"tracker:coalesce(nco:postalcode(?aff_addr), \"\"), \"\37\"," \
+"tracker:coalesce(nco:country(?aff_addr), \"\"), " \
+"\"\31\", rdfs:label(?_role) ), " \
+"\"\30\") " \
+"WHERE {" \
+"?_role nco:hasPostalAddress ?aff_addr" \
+"}) " \
+"nco:birthDate(?_contact) " \
+"(SELECT " \
+" ?nick " \
+" WHERE { " \
+" { " \
+" ?_contact nco:nickname ?nick " \
+" } UNION { " \
+" ?_contact nco:hasAffiliation ?role . " \
+" ?role nco:hasIMAddress ?im . " \
+" ?im nco:imNickname ?nick " \
+" } " \
+" } " \
+") " \
+"(SELECT GROUP_CONCAT(fn:concat( " \
+ "?url_val, \"\31\", tracker:coalesce(rdfs:label(?_role), \"\") "\
+ "), \"\30\") " \
+ "WHERE {" \
+ "?_role nco:url ?url_val . " \
+"})" \
+"nie:url(nco:photo(?_contact)) " \
+"nco:role(?_role) " \
+"nco:contactUID(?_contact) " \
+"nco:title(?_role) " \
+"rdfs:label(?_role) " \
+"nco:fullname(nco:org(?_role))" \
+"nco:department(?_role) " \
+"(SELECT GROUP_CONCAT(fn:concat(?emailaddress,\"\31\"," \
+ "tracker:coalesce(rdfs:label(?_role), \"\"))," \
+ "\"\30\") " \
+ "WHERE { " \
+ "?_role nco:hasEmailAddress " \
+ " [ nco:emailAddress ?emailaddress ] " \
+ "}) " \
+"\"NOTACALL\" \"false\" \"false\" " \
+"?_contact " \
+"WHERE {" \
+" ?_contact a nco:PersonContact ." \
+" OPTIONAL {?_contact nco:hasAffiliation ?_role .}" \
+"}" \
+"ORDER BY tracker:id(?_contact)"
+
+#define CONTACTS_QUERY_ALL_LIST \
+ "SELECT ?c nco:nameFamily(?c) " \
+ "nco:nameGiven(?c) nco:nameAdditional(?c) " \
+ "nco:nameHonorificPrefix(?c) nco:nameHonorificSuffix(?c) " \
+ "(SELECT " \
+ "?nick " \
+ "WHERE { " \
+ "{ " \
+ "?c nco:nickname ?nick " \
+ "} UNION { " \
+ "?c nco:hasAffiliation ?role . " \
+ "?role nco:hasIMAddress ?im . " \
+ "?im nco:imNickname ?nick " \
+ "} " \
+ "} " \
+ ") " \
+ "nco:phoneNumber(?h) " \
+ "WHERE { " \
+ "?c a nco:PersonContact . " \
+ "OPTIONAL { ?c nco:hasPhoneNumber ?h . } " \
+ "OPTIONAL { " \
+ "?c nco:hasAffiliation ?a . " \
+ "?a nco:hasPhoneNumber ?h . " \
+ "} " \
+ "} GROUP BY ?c"
+
+#define CALLS_CONSTRAINTS(CONSTRAINT) \
+" WHERE { " \
+ "?_call a nmo:Call . " \
+ "?_unb_contact a nco:Contact . " \
+ "?_unb_contact nco:hasPhoneNumber ?_cpn . " \
+CONSTRAINT \
+ "OPTIONAL { " \
+ "{ SELECT ?_contact ?_no ?_role ?_number " \
+ "count(?_contact) as ?cnt " \
+ "WHERE { " \
+ "?_contact a nco:PersonContact . " \
+ "{ " \
+ "?_contact nco:hasAffiliation ?_role . "\
+ "?_role nco:hasPhoneNumber ?_number . " \
+ "} UNION { " \
+ "?_contact nco:hasPhoneNumber ?_number" \
+ "} " \
+ "?_number maemo:localPhoneNumber ?_no . " \
+ "} GROUP BY ?_no } " \
+ "FILTER(?cnt = 1) " \
+ "?_cpn maemo:localPhoneNumber ?_no . " \
+ "} " \
+"} "
+
+#define CALLS_LIST(CONSTRAINT) \
+"SELECT ?_call nco:nameFamily(?_contact) " \
+ "nco:nameGiven(?_contact) nco:nameAdditional(?_contact) " \
+ "nco:nameHonorificPrefix(?_contact) " \
+ "nco:nameHonorificSuffix(?_contact) " \
+ "(SELECT " \
+ "?nick " \
+ "WHERE { " \
+ "{ " \
+ "?_contact nco:nickname ?nick " \
+ "} UNION { " \
+ "?_contact nco:hasAffiliation ?role . " \
+ "?role nco:hasIMAddress ?im . " \
+ "?im nco:imNickname ?nick " \
+ "} " \
+ "} " \
+ ") " \
+ "nco:phoneNumber(?_cpn) " \
+CALLS_CONSTRAINTS(CONSTRAINT) \
+"ORDER BY DESC(nmo:sentDate(?_call)) "
+
+#define CALLS_QUERY(CONSTRAINT) \
+"SELECT " \
+"(SELECT fn:concat(rdf:type(?role_number)," \
+ "\"\31\", nco:phoneNumber(?role_number))" \
+ "WHERE {" \
+ "{" \
+ " ?_role nco:hasPhoneNumber ?role_number " \
+ " FILTER (?role_number = ?_number)" \
+ "} UNION { " \
+ "?_unb_contact nco:hasPhoneNumber ?role_number . " \
+ " FILTER (!bound(?_role)) " \
+ "}" \
+"} GROUP BY nco:phoneNumber(?role_number) ) " \
+ "nco:fullname(?_contact) " \
+ "nco:nameFamily(?_contact) " \
+ "nco:nameGiven(?_contact) " \
+ "nco:nameAdditional(?_contact) " \
+ "nco:nameHonorificPrefix(?_contact) " \
+ "nco:nameHonorificSuffix(?_contact) " \
+"(SELECT GROUP_CONCAT(fn:concat(" \
+ "tracker:coalesce(nco:pobox(?aff_addr), \"\"), \"\37\"," \
+ "tracker:coalesce(nco:extendedAddress(?aff_addr), \"\"), \"\37\","\
+ "tracker:coalesce(nco:streetAddress(?aff_addr), \"\"), \"\37\","\
+ "tracker:coalesce(nco:locality(?aff_addr), \"\"), \"\37\"," \
+ "tracker:coalesce(nco:region(?aff_addr), \"\"), \"\37\"," \
+ "tracker:coalesce(nco:postalcode(?aff_addr), \"\"), \"\37\"," \
+ "tracker:coalesce(nco:country(?aff_addr), \"\"), " \
+ "\"\31\", rdfs:label(?c_role) ), " \
+ "\"\30\") " \
+ "WHERE {" \
+ "?_contact nco:hasAffiliation ?c_role . " \
+ "?c_role nco:hasPostalAddress ?aff_addr" \
+ "}) " \
+ "nco:birthDate(?_contact) " \
+"(SELECT " \
+ "?nick " \
+ "WHERE { " \
+ " { " \
+ " ?_contact nco:nickname ?nick " \
+ " } UNION { " \
+ " ?_contact nco:hasAffiliation ?role . " \
+ " ?role nco:hasIMAddress ?im . " \
+ " ?im nco:imNickname ?nick " \
+ " } " \
+ " } " \
+ ") " \
+"(SELECT GROUP_CONCAT(fn:concat(?url_value, \"\31\", " \
+ "tracker:coalesce(rdfs:label(?c_role), \"\")), \"\30\") " \
+ "WHERE {" \
+ "?_contact nco:hasAffiliation ?c_role . " \
+ "?c_role nco:url ?url_value . " \
+"})" \
+ "nie:url(nco:photo(?_contact)) " \
+ "nco:role(?_role) " \
+ "nco:contactUID(?_contact) " \
+ "nco:title(?_role) " \
+ "rdfs:label(?_role) " \
+ "nco:fullname(nco:org(?_role)) " \
+ "nco:department(?_role) " \
+"(SELECT GROUP_CONCAT(fn:concat(?emailaddress,\"\31\"," \
+ "tracker:coalesce(rdfs:label(?c_role), \"\"))," \
+ "\"\30\") " \
+ "WHERE { " \
+ "?_contact nco:hasAffiliation ?c_role . " \
+ "?c_role nco:hasEmailAddress " \
+ " [ nco:emailAddress ?emailaddress ] " \
+ "}) " \
+ "nmo:receivedDate(?_call) " \
+ "nmo:isSent(?_call) " \
+ "nmo:isAnswered(?_call) " \
+ "?_call " \
+CALLS_CONSTRAINTS(CONSTRAINT) \
+"ORDER BY DESC(nmo:sentDate(?_call)) "
+
+#define MISSED_CONSTRAINT \
+"?_call nmo:from ?_unb_contact . " \
+"?_call nmo:isSent false . " \
+"?_call nmo:isAnswered false . "
+
+#define INCOMING_CONSTRAINT \
+"?_call nmo:from ?_unb_contact . " \
+"?_call nmo:isSent false . " \
+"?_call nmo:isAnswered true . "
+
+#define OUTGOING_CONSTRAINT \
+"?_call nmo:to ?_unb_contact . " \
+"?_call nmo:isSent true . "
+
+#define COMBINED_CONSTRAINT \
+"{ " \
+" ?_call nmo:from ?_unb_contact . " \
+" ?_call nmo:isSent false " \
+"} UNION { " \
+" ?_call nmo:to ?_unb_contact . " \
+" ?_call nmo:isSent true " \
+"} "
+
+#define CALL_URI_CONSTRAINT \
+COMBINED_CONSTRAINT \
+"FILTER (?_call = <%s>) "
+
+#define MISSED_CALLS_QUERY CALLS_QUERY(MISSED_CONSTRAINT)
+#define MISSED_CALLS_LIST CALLS_LIST(MISSED_CONSTRAINT)
+#define INCOMING_CALLS_QUERY CALLS_QUERY(INCOMING_CONSTRAINT)
+#define INCOMING_CALLS_LIST CALLS_LIST(INCOMING_CONSTRAINT)
+#define OUTGOING_CALLS_QUERY CALLS_QUERY(OUTGOING_CONSTRAINT)
+#define OUTGOING_CALLS_LIST CALLS_LIST(OUTGOING_CONSTRAINT)
+#define COMBINED_CALLS_QUERY CALLS_QUERY(COMBINED_CONSTRAINT)
+#define COMBINED_CALLS_LIST CALLS_LIST(COMBINED_CONSTRAINT)
+#define CONTACT_FROM_CALL_QUERY CALLS_QUERY(CALL_URI_CONSTRAINT)
+
+#define CONTACTS_QUERY_FROM_URI \
+"SELECT " \
+"(SELECT GROUP_CONCAT(fn:concat(rdf:type(?aff_number)," \
+"\"\31\", nco:phoneNumber(?aff_number)), \"\30\")" \
+"WHERE {" \
+" ?_role nco:hasPhoneNumber ?aff_number" \
+"}) " \
+"nco:fullname(<%s>) " \
+"nco:nameFamily(<%s>) " \
+"nco:nameGiven(<%s>) " \
+"nco:nameAdditional(<%s>) " \
+"nco:nameHonorificPrefix(<%s>) " \
+"nco:nameHonorificSuffix(<%s>) " \
+"(SELECT GROUP_CONCAT(fn:concat(" \
+"tracker:coalesce(nco:pobox(?aff_addr), \"\"), \"\37\"," \
+"tracker:coalesce(nco:extendedAddress(?aff_addr), \"\"), \"\37\"," \
+"tracker:coalesce(nco:streetAddress(?aff_addr), \"\"), \"\37\"," \
+"tracker:coalesce(nco:locality(?aff_addr), \"\"), \"\37\"," \
+"tracker:coalesce(nco:region(?aff_addr), \"\"), \"\37\"," \
+"tracker:coalesce(nco:postalcode(?aff_addr), \"\"), \"\37\"," \
+"tracker:coalesce(nco:country(?aff_addr), \"\"), " \
+"\"\31\", rdfs:label(?_role) ), " \
+"\"\30\") " \
+"WHERE {" \
+"?_role nco:hasPostalAddress ?aff_addr" \
+"}) " \
+"nco:birthDate(<%s>) " \
+"(SELECT " \
+" ?nick " \
+" WHERE { " \
+" { " \
+" ?_contact nco:nickname ?nick " \
+" } UNION { " \
+" ?_contact nco:hasAffiliation ?role . " \
+" ?role nco:hasIMAddress ?im . " \
+" ?im nco:imNickname ?nick " \
+" } " \
+" FILTER (?_contact = <%s>)" \
+" } " \
+") " \
+"(SELECT GROUP_CONCAT(fn:concat( " \
+ "?url_val, \"\31\", tracker:coalesce(rdfs:label(?_role), \"\") "\
+ "), \"\30\") " \
+ "WHERE {" \
+ "?_role nco:url ?url_val . " \
+"})" \
+"nie:url(nco:photo(<%s>)) " \
+"nco:role(?_role) " \
+"nco:contactUID(<%s>) " \
+"nco:title(?_role) " \
+"rdfs:label(?_role) " \
+"nco:fullname(nco:org(?_role))" \
+"nco:department(?_role) " \
+"(SELECT GROUP_CONCAT(fn:concat(?emailaddress,\"\31\"," \
+ "tracker:coalesce(rdfs:label(?_role), \"\"))," \
+ "\"\30\") " \
+ "WHERE { " \
+ "?_role nco:hasEmailAddress " \
+ " [ nco:emailAddress ?emailaddress ] " \
+ "}) " \
+"\"NOTACALL\" \"false\" \"false\" " \
+"<%s> " \
+"WHERE {" \
+" <%s> a nco:PersonContact ." \
+" OPTIONAL {<%s> nco:hasAffiliation ?_role .}" \
+"}"
+
+#define CONTACTS_OTHER_QUERY_FROM_URI \
+ "SELECT fn:concat(\"TYPE_OTHER\", \"\31\", nco:phoneNumber(?t))"\
+ "\"\" \"\" \"\" \"\" \"\" \"\" \"\" \"\" " \
+ "\"\" \"\" \"\" \"\" \"\" \"\" \"\" \"\" \"\" \"\" " \
+ " \"NOTACALL\" \"false\" \"false\" <%s> " \
+ "WHERE { " \
+ "<%s> a nco:Contact . " \
+ "OPTIONAL { <%s> nco:hasPhoneNumber ?t . } " \
+ "} "
+
+#define CONTACTS_COUNT_QUERY \
+ "SELECT COUNT(?c) " \
+ "WHERE {" \
+ "?c a nco:PersonContact ." \
+ "}"
+
+#define MISSED_CALLS_COUNT_QUERY \
+ "SELECT COUNT(?call) WHERE {" \
+ "?c a nco:Contact ;" \
+ "nco:hasPhoneNumber ?h ." \
+ "?call a nmo:Call ;" \
+ "nmo:isSent false ;" \
+ "nmo:from ?c ;" \
+ "nmo:isAnswered false ." \
+ "}"
+
+#define INCOMING_CALLS_COUNT_QUERY \
+ "SELECT COUNT(?call) WHERE {" \
+ "?c a nco:Contact ;" \
+ "nco:hasPhoneNumber ?h ." \
+ "?call a nmo:Call ;" \
+ "nmo:isSent false ;" \
+ "nmo:from ?c ;" \
+ "nmo:isAnswered true ." \
+ "}"
+
+#define OUTGOING_CALLS_COUNT_QUERY \
+ "SELECT COUNT(?call) WHERE {" \
+ "?c a nco:Contact ;" \
+ "nco:hasPhoneNumber ?h ." \
+ "?call a nmo:Call ;" \
+ "nmo:isSent true ;" \
+ "nmo:to ?c ." \
+ "}"
+
+#define COMBINED_CALLS_COUNT_QUERY \
+ "SELECT COUNT(?call) WHERE {" \
+ "{" \
+ "?c a nco:Contact ;" \
+ "nco:hasPhoneNumber ?h ." \
+ "?call a nmo:Call ;" \
+ "nmo:isSent true ;" \
+ "nmo:to ?c ." \
+ "}UNION {" \
+ "?c a nco:Contact ;" \
+ "nco:hasPhoneNumber ?h ." \
+ "?call a nmo:Call ;" \
+ "nmo:from ?c ." \
+ "}" \
+ "}"
+
+#define NEW_MISSED_CALLS_COUNT_QUERY \
+ "SELECT COUNT(?call) WHERE {" \
+ "?c a nco:Contact ;" \
+ "nco:hasPhoneNumber ?h ." \
+ "?call a nmo:Call ;" \
+ "nmo:isSent false ;" \
+ "nmo:from ?c ;" \
+ "nmo:isAnswered false ;" \
+ "nmo:isRead false ." \
+ "}"
+
+typedef int (*reply_list_foreach_t) (const char **reply, int num_fields,
+ void *user_data);
+
+typedef void (*add_field_t) (struct phonebook_contact *contact,
+ const char *value, int type);
+
+struct pending_reply {
+ reply_list_foreach_t callback;
+ void *user_data;
+ int num_fields;
+};
+
+struct contact_data {
+ char *id;
+ struct phonebook_contact *contact;
+};
+
+struct phonebook_data {
+ phonebook_cb cb;
+ void *user_data;
+ int index;
+ gboolean vcardentry;
+ const struct apparam_field *params;
+ GSList *contacts;
+ phonebook_cache_ready_cb ready_cb;
+ phonebook_entry_cb entry_cb;
+ int newmissedcalls;
+ GCancellable *query_canc;
+ char *req_name;
+ int vcard_part_count;
+ int tracker_index;
+};
+
+struct phonebook_index {
+ GArray *phonebook;
+ int index;
+};
+
+static TrackerSparqlConnection *connection = NULL;
+
+static const char *name2query(const char *name)
+{
+ if (g_str_equal(name, PB_CONTACTS))
+ return CONTACTS_QUERY_ALL;
+ else if (g_str_equal(name, PB_CALLS_INCOMING))
+ return INCOMING_CALLS_QUERY;
+ else if (g_str_equal(name, PB_CALLS_OUTGOING))
+ return OUTGOING_CALLS_QUERY;
+ else if (g_str_equal(name, PB_CALLS_MISSED))
+ return MISSED_CALLS_QUERY;
+ else if (g_str_equal(name, PB_CALLS_COMBINED))
+ return COMBINED_CALLS_QUERY;
+
+ return NULL;
+}
+
+static const char *name2count_query(const char *name)
+{
+ if (g_str_equal(name, PB_CONTACTS))
+ return CONTACTS_COUNT_QUERY;
+ else if (g_str_equal(name, PB_CALLS_INCOMING))
+ return INCOMING_CALLS_COUNT_QUERY;
+ else if (g_str_equal(name, PB_CALLS_OUTGOING))
+ return OUTGOING_CALLS_COUNT_QUERY;
+ else if (g_str_equal(name, PB_CALLS_MISSED))
+ return MISSED_CALLS_COUNT_QUERY;
+ else if (g_str_equal(name, PB_CALLS_COMBINED))
+ return COMBINED_CALLS_COUNT_QUERY;
+
+ return NULL;
+}
+
+static gboolean folder_is_valid(const char *folder)
+{
+ if (folder == NULL)
+ return FALSE;
+
+ if (g_str_equal(folder, "/"))
+ return TRUE;
+ else if (g_str_equal(folder, PB_TELECOM_FOLDER))
+ return TRUE;
+ else if (g_str_equal(folder, PB_CONTACTS_FOLDER))
+ return TRUE;
+ else if (g_str_equal(folder, PB_CALLS_INCOMING_FOLDER))
+ return TRUE;
+ else if (g_str_equal(folder, PB_CALLS_OUTGOING_FOLDER))
+ return TRUE;
+ else if (g_str_equal(folder, PB_CALLS_MISSED_FOLDER))
+ return TRUE;
+ else if (g_str_equal(folder, PB_CALLS_COMBINED_FOLDER))
+ return TRUE;
+
+ return FALSE;
+}
+
+static const char *folder2query(const char *folder)
+{
+ if (g_str_equal(folder, PB_CONTACTS_FOLDER))
+ return CONTACTS_QUERY_ALL_LIST;
+ else if (g_str_equal(folder, PB_CALLS_INCOMING_FOLDER))
+ return INCOMING_CALLS_LIST;
+ else if (g_str_equal(folder, PB_CALLS_OUTGOING_FOLDER))
+ return OUTGOING_CALLS_LIST;
+ else if (g_str_equal(folder, PB_CALLS_MISSED_FOLDER))
+ return MISSED_CALLS_LIST;
+ else if (g_str_equal(folder, PB_CALLS_COMBINED_FOLDER))
+ return COMBINED_CALLS_LIST;
+
+ return NULL;
+}
+
+static const char **string_array_from_cursor(TrackerSparqlCursor *cursor,
+ int array_len)
+{
+ const char **result;
+ int i;
+
+ result = g_new0(const char *, array_len);
+
+ for (i = 0; i < array_len; ++i) {
+ TrackerSparqlValueType type;
+
+ type = tracker_sparql_cursor_get_value_type(cursor, i);
+
+ if (type == TRACKER_SPARQL_VALUE_TYPE_BLANK_NODE ||
+ type == TRACKER_SPARQL_VALUE_TYPE_UNBOUND)
+ /* For null/unbound type filling result part with ""*/
+ result[i] = "";
+ else
+ /* Filling with string representation of content*/
+ result[i] = tracker_sparql_cursor_get_string(cursor, i,
+ NULL);
+ }
+
+ return result;
+}
+
+static void update_cancellable(struct phonebook_data *pdata,
+ GCancellable *canc)
+{
+ if (pdata->query_canc)
+ g_object_unref(pdata->query_canc);
+
+ pdata->query_canc = canc;
+}
+
+static void async_query_cursor_next_cb(GObject *source, GAsyncResult *result,
+ gpointer user_data)
+{
+ struct pending_reply *pending = user_data;
+ TrackerSparqlCursor *cursor = TRACKER_SPARQL_CURSOR(source);
+ GCancellable *cancellable;
+ GError *error = NULL;
+ gboolean success;
+ const char **node;
+ int err;
+
+ success = tracker_sparql_cursor_next_finish(
+ TRACKER_SPARQL_CURSOR(source),
+ result, &error);
+
+ if (!success) {
+ if (error) {
+ DBG("cursor_next error: %s", error->message);
+ g_error_free(error);
+ } else
+ /* When tracker_sparql_cursor_next_finish ends with
+ * failure and no error is set, that means end of
+ * results returned by query */
+ pending->callback(NULL, 0, pending->user_data);
+
+ goto failed;
+ }
+
+ node = string_array_from_cursor(cursor, pending->num_fields);
+ err = pending->callback(node, pending->num_fields, pending->user_data);
+ g_free(node);
+
+ /* Fetch next result only if processing current chunk ended with
+ * success. Sometimes during processing data, we are able to determine
+ * if there is no need to get more data from tracker - by example
+ * stored amount of data parts is big enough for sending and we might
+ * want to suspend processing or just some error occurred. */
+ if (!err) {
+ cancellable = g_cancellable_new();
+ update_cancellable(pending->user_data, cancellable);
+ tracker_sparql_cursor_next_async(cursor, cancellable,
+ async_query_cursor_next_cb,
+ pending);
+ return;
+ }
+
+failed:
+ g_object_unref(cursor);
+ g_free(pending);
+}
+
+static int query_tracker(const char *query, int num_fields,
+ reply_list_foreach_t callback, void *user_data)
+{
+ struct pending_reply *pending;
+ GCancellable *cancellable;
+ TrackerSparqlCursor *cursor;
+ GError *error = NULL;
+
+ DBG("");
+
+ if (connection == NULL)
+ connection = tracker_sparql_connection_get_direct(
+ NULL, &error);
+
+ if (!connection) {
+ if (error) {
+ DBG("direct-connection error: %s", error->message);
+ g_error_free(error);
+ }
+
+ return -EINTR;
+ }
+
+ cancellable = g_cancellable_new();
+ update_cancellable(user_data, cancellable);
+ cursor = tracker_sparql_connection_query(connection, query,
+ cancellable, &error);
+
+ if (cursor == NULL) {
+ if (error) {
+ DBG("connection_query error: %s", error->message);
+ g_error_free(error);
+ }
+
+ g_object_unref(cancellable);
+
+ return -EINTR;
+ }
+
+ pending = g_new0(struct pending_reply, 1);
+ pending->callback = callback;
+ pending->user_data = user_data;
+ pending->num_fields = num_fields;
+
+ /* Now asynchronously going through each row of results - callback
+ * async_query_cursor_next_cb will be called ALWAYS, even if async
+ * request was canceled */
+ tracker_sparql_cursor_next_async(cursor, cancellable,
+ async_query_cursor_next_cb,
+ pending);
+
+ return 0;
+}
+
+static char *iso8601_utc_to_localtime(const char *datetime)
+{
+ time_t time;
+ struct tm tm, *local;
+ char localdate[32];
+ int nr;
+
+ memset(&tm, 0, sizeof(tm));
+
+ nr = sscanf(datetime, "%04u-%02u-%02uT%02u:%02u:%02u",
+ &tm.tm_year, &tm.tm_mon, &tm.tm_mday,
+ &tm.tm_hour, &tm.tm_min, &tm.tm_sec);
+ if (nr < 6) {
+ /* Invalid time format */
+ error("sscanf(): %s (%d)", strerror(errno), errno);
+ return g_strdup("");
+ }
+
+ /* Time already in localtime */
+ if (!g_str_has_suffix(datetime, "Z")) {
+ strftime(localdate, sizeof(localdate), "%Y%m%dT%H%M%S", &tm);
+ return g_strdup(localdate);
+ }
+
+ tm.tm_year -= 1900; /* Year since 1900 */
+ tm.tm_mon--; /* Months since January, values 0-11 */
+
+ time = mktime(&tm);
+ time -= timezone;
+
+ local = localtime(&time);
+
+ strftime(localdate, sizeof(localdate), "%Y%m%dT%H%M%S", local);
+
+ return g_strdup(localdate);
+}
+
+static void set_call_type(struct phonebook_contact *contact,
+ const char *datetime, const char *is_sent,
+ const char *is_answered)
+{
+ gboolean sent, answered;
+
+ if (g_strcmp0(datetime, "NOTACALL") == 0) {
+ contact->calltype = CALL_TYPE_NOT_A_CALL;
+ return;
+ }
+
+ sent = g_str_equal(is_sent, "true");
+ answered = g_str_equal(is_answered, "true");
+
+ if (sent == FALSE) {
+ if (answered == FALSE)
+ contact->calltype = CALL_TYPE_MISSED;
+ else
+ contact->calltype = CALL_TYPE_INCOMING;
+ } else
+ contact->calltype = CALL_TYPE_OUTGOING;
+
+ /* Tracker gives time in the ISO 8601 format, UTC time */
+ contact->datetime = iso8601_utc_to_localtime(datetime);
+}
+
+static gboolean contact_matches(struct contact_data *c_data, const char *id,
+ const char *datetime)
+{
+ char *localtime;
+ int cmp_ret;
+
+ if (g_strcmp0(c_data->id, id) != 0)
+ return FALSE;
+
+ /* id is equal and not call history entry => contact matches */
+ if (c_data->contact->calltype == CALL_TYPE_NOT_A_CALL)
+ return TRUE;
+
+ /* for call history entries have to compare also timestamps of calls */
+ localtime = iso8601_utc_to_localtime(datetime);
+ cmp_ret = g_strcmp0(c_data->contact->datetime, localtime);
+ g_free(localtime);
+
+ return (cmp_ret == 0) ? TRUE : FALSE;
+}
+
+static struct phonebook_contact *find_contact(GSList *contacts, const char *id,
+ const char *datetime)
+{
+ GSList *l;
+
+ for (l = contacts; l; l = l->next) {
+ struct contact_data *c_data = l->data;
+
+ if (contact_matches(c_data, id, datetime))
+ return c_data->contact;
+ }
+
+ return NULL;
+}
+
+static struct phonebook_field *find_field(GSList *fields, const char *value,
+ int type)
+{
+ GSList *l;
+
+ for (l = fields; l; l = l->next) {
+ struct phonebook_field *field = l->data;
+ /* Returning phonebook number if phone values and type values
+ * are equal */
+ if (g_strcmp0(field->text, value) == 0 && field->type == type)
+ return field;
+ }
+
+ return NULL;
+}
+
+static void add_phone_number(struct phonebook_contact *contact,
+ const char *phone, int type)
+{
+ struct phonebook_field *number;
+
+ if (phone == NULL || strlen(phone) == 0)
+ return;
+
+ /* Not adding number if there is already added with the same value */
+ if (find_field(contact->numbers, phone, type))
+ return;
+
+ number = g_new0(struct phonebook_field, 1);
+ number->text = g_strdup(phone);
+ number->type = type;
+
+ contact->numbers = g_slist_append(contact->numbers, number);
+}
+
+static void add_email(struct phonebook_contact *contact, const char *address,
+ int type)
+{
+ struct phonebook_field *email;
+
+ if (address == NULL || strlen(address) == 0)
+ return;
+
+ /* Not adding email if there is already added with the same value */
+ if (find_field(contact->emails, address, type))
+ return;
+
+ email = g_new0(struct phonebook_field, 1);
+ email->text = g_strdup(address);
+ email->type = type;
+
+ contact->emails = g_slist_append(contact->emails, email);
+}
+
+static gboolean addr_matches(struct phonebook_addr *a, struct phonebook_addr *b)
+{
+ GSList *la, *lb;
+
+ if (a->type != b->type)
+ return FALSE;
+
+ for (la = a->fields, lb = b->fields; la && lb;
+ la = la->next, lb = lb->next) {
+ char *field_a = la->data;
+ char *field_b = lb->data;
+
+ if (g_strcmp0(field_a, field_b) != 0)
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* generates phonebook_addr struct from tracker address data string. */
+static struct phonebook_addr *gen_addr(const char *address, int type)
+{
+ struct phonebook_addr *addr;
+ GSList *fields = NULL;
+ char **addr_parts;
+ int i;
+
+ /* This test handles cases when address points to empty string
+ * (or address is NULL pointer) or string containing only six
+ * separators. It indicates that none of address fields is present
+ * and there is no sense to create dummy phonebook_addr struct */
+ if (address == NULL || strlen(address) < ADDR_FIELD_AMOUNT)
+ return NULL;
+
+ addr_parts = g_strsplit(address, ADDR_DELIM, ADDR_FIELD_AMOUNT);
+
+ for (i = 0; i < ADDR_FIELD_AMOUNT; ++i)
+ fields = g_slist_append(fields, g_strdup(addr_parts[i]));
+
+ g_strfreev(addr_parts);
+
+ addr = g_new0(struct phonebook_addr, 1);
+ addr->fields = fields;
+ addr->type = type;
+
+ return addr;
+}
+
+static void add_address(struct phonebook_contact *contact,
+ const char *address, int type)
+{
+ struct phonebook_addr *addr;
+ GSList *l;
+
+ addr = gen_addr(address, type);
+ if (addr == NULL)
+ return;
+
+ /* Not adding address if there is already added with the same value.
+ * These type of checks have to be done because sometimes tracker
+ * returns results for contact data in more than 1 row - then the same
+ * address may be returned more than once in query results */
+ for (l = contact->addresses; l; l = l->next) {
+ struct phonebook_addr *tmp = l->data;
+
+ if (addr_matches(tmp, addr)) {
+ phonebook_addr_free(addr);
+ return;
+ }
+ }
+
+ contact->addresses = g_slist_append(contact->addresses, addr);
+}
+
+static void add_url(struct phonebook_contact *contact, const char *url_val,
+ int type)
+{
+ struct phonebook_field *url;
+
+ if (url_val == NULL || strlen(url_val) == 0)
+ return;
+
+ /* Not adding url if there is already added with the same value */
+ if (find_field(contact->urls, url_val, type))
+ return;
+
+ url = g_new0(struct phonebook_field, 1);
+
+ url->text = g_strdup(url_val);
+ url->type = type;
+
+ contact->urls = g_slist_append(contact->urls, url);
+}
+
+static GString *gen_vcards(GSList *contacts,
+ const struct apparam_field *params)
+{
+ GSList *l;
+ GString *vcards;
+
+ vcards = g_string_new(NULL);
+
+ /* Generating VCARD string from contacts and freeing used contacts */
+ for (l = contacts; l; l = l->next) {
+ struct contact_data *c_data = l->data;
+ phonebook_add_contact(vcards, c_data->contact,
+ params->filter, params->format);
+ }
+
+ return vcards;
+}
+
+static int pull_contacts_size(const char **reply, int num_fields,
+ void *user_data)
+{
+ struct phonebook_data *data = user_data;
+
+ if (num_fields < 0) {
+ data->cb(NULL, 0, num_fields, 0, TRUE, data->user_data);
+ return -EINTR;
+ }
+
+ if (reply != NULL) {
+ data->index = atoi(reply[0]);
+ return 0;
+ }
+
+ data->cb(NULL, 0, data->index, data->newmissedcalls, TRUE,
+ data->user_data);
+
+ return 0;
+ /*
+ * phonebook_data is freed in phonebook_req_finalize. Useful in
+ * cases when call is terminated.
+ */
+}
+
+static void add_affiliation(char **field, const char *value)
+{
+ if (strlen(*field) > 0 || value == NULL || strlen(value) == 0)
+ return;
+
+ g_free(*field);
+
+ *field = g_strdup(value);
+}
+
+static void contact_init(struct phonebook_contact *contact,
+ const char **reply)
+{
+ if (reply[COL_FAMILY_NAME][0] == '\0' &&
+ reply[COL_GIVEN_NAME][0] == '\0' &&
+ reply[COL_ADDITIONAL_NAME][0] == '\0' &&
+ reply[COL_NAME_PREFIX][0] == '\0' &&
+ reply[COL_NAME_SUFFIX][0] == '\0') {
+ if (reply[COL_FULL_NAME][0] != '\0')
+ contact->family = g_strdup(reply[COL_FULL_NAME]);
+ else
+ contact->family = g_strdup(reply[COL_NICKNAME]);
+ } else {
+ contact->family = g_strdup(reply[COL_FAMILY_NAME]);
+ contact->given = g_strdup(reply[COL_GIVEN_NAME]);
+ contact->additional = g_strdup(reply[COL_ADDITIONAL_NAME]);
+ contact->prefix = g_strdup(reply[COL_NAME_PREFIX]);
+ contact->suffix = g_strdup(reply[COL_NAME_SUFFIX]);
+ }
+ contact->fullname = g_strdup(reply[COL_FULL_NAME]);
+ contact->birthday = g_strdup(reply[COL_BIRTH_DATE]);
+ contact->nickname = g_strdup(reply[COL_NICKNAME]);
+ contact->photo = g_strdup(reply[COL_PHOTO]);
+ contact->company = g_strdup(reply[COL_ORG_NAME]);
+ contact->department = g_strdup(reply[COL_ORG_DEPARTMENT]);
+ contact->role = g_strdup(reply[COL_ORG_ROLE]);
+ contact->uid = g_strdup(reply[COL_UID]);
+ contact->title = g_strdup(reply[COL_TITLE]);
+
+ set_call_type(contact, reply[COL_DATE], reply[COL_SENT],
+ reply[COL_ANSWERED]);
+}
+
+static enum phonebook_number_type get_phone_type(const char *affilation)
+{
+ if (g_strcmp0(AFFILATION_HOME, affilation) == 0)
+ return TEL_TYPE_HOME;
+ else if (g_strcmp0(AFFILATION_WORK, affilation) == 0)
+ return TEL_TYPE_WORK;
+
+ return TEL_TYPE_OTHER;
+}
+
+static void add_aff_number(struct phonebook_contact *contact,
+ const char *pnumber, const char *aff_type)
+{
+ char **num_parts;
+ char *type, *number;
+
+ /* For phone taken directly from contacts data, phone number string
+ * is represented as number type and number string - those strings are
+ * separated by SUB_DELIM string */
+ num_parts = g_strsplit(pnumber, SUB_DELIM, 2);
+
+ if (!num_parts)
+ return;
+
+ if (num_parts[0])
+ type = num_parts[0];
+ else
+ goto failed;
+
+ if (num_parts[1])
+ number = num_parts[1];
+ else
+ goto failed;
+
+ if (g_strrstr(type, FAX_NUM_TYPE))
+ add_phone_number(contact, number, TEL_TYPE_FAX);
+ else if (g_strrstr(type, MOBILE_NUM_TYPE))
+ add_phone_number(contact, number, TEL_TYPE_MOBILE);
+ else
+ /* if this is no fax/mobile phone, then adding phone number
+ * type based on type of the affilation field */
+ add_phone_number(contact, number, get_phone_type(aff_type));
+
+failed:
+ g_strfreev(num_parts);
+}
+
+static void contact_add_numbers(struct phonebook_contact *contact,
+ const char **reply)
+{
+ char **aff_numbers;
+ int i;
+
+ /* Filling phone numbers from contact's affilation */
+ aff_numbers = g_strsplit(reply[COL_PHONE_AFF], MAIN_DELIM, MAX_FIELDS);
+
+ if (aff_numbers)
+ for (i = 0; aff_numbers[i]; ++i)
+ add_aff_number(contact, aff_numbers[i],
+ reply[COL_AFF_TYPE]);
+
+ g_strfreev(aff_numbers);
+}
+
+static enum phonebook_field_type get_field_type(const char *affilation)
+{
+ if (g_strcmp0(AFFILATION_HOME, affilation) == 0)
+ return FIELD_TYPE_HOME;
+ else if (g_strcmp0(AFFILATION_WORK, affilation) == 0)
+ return FIELD_TYPE_WORK;
+
+ return FIELD_TYPE_OTHER;
+}
+
+static void add_aff_field(struct phonebook_contact *contact,
+ const char *aff_email, add_field_t add_field_cb)
+{
+ char **email_parts;
+ char *type, *email;
+
+ /* Emails from affilation data, are represented as real email
+ * string and affilation type - those strings are separated by
+ * SUB_DELIM string */
+ email_parts = g_strsplit(aff_email, SUB_DELIM, 2);
+
+ if (!email_parts)
+ return;
+
+ if (email_parts[0])
+ email = email_parts[0];
+ else
+ goto failed;
+
+ if (email_parts[1])
+ type = email_parts[1];
+ else
+ goto failed;
+
+ add_field_cb(contact, email, get_field_type(type));
+
+failed:
+ g_strfreev(email_parts);
+}
+
+static void contact_add_emails(struct phonebook_contact *contact,
+ const char **reply)
+{
+ char **aff_emails;
+ int i;
+
+ /* Emails from affilation */
+ aff_emails = g_strsplit(reply[COL_EMAIL_AFF], MAIN_DELIM, MAX_FIELDS);
+
+ if (aff_emails)
+ for (i = 0; aff_emails[i] != NULL; ++i)
+ add_aff_field(contact, aff_emails[i], add_email);
+
+ g_strfreev(aff_emails);
+}
+
+static void contact_add_addresses(struct phonebook_contact *contact,
+ const char **reply)
+{
+ char **aff_addr;
+ int i;
+
+ /* Addresses from affilation */
+ aff_addr = g_strsplit(reply[COL_ADDR_AFF], MAIN_DELIM, MAX_FIELDS);
+
+ if (aff_addr)
+ for (i = 0; aff_addr[i] != NULL; ++i)
+ add_aff_field(contact, aff_addr[i], add_address);
+
+ g_strfreev(aff_addr);
+}
+
+static void contact_add_urls(struct phonebook_contact *contact,
+ const char **reply)
+{
+ char **aff_url;
+ int i;
+
+ /* Addresses from affilation */
+ aff_url = g_strsplit(reply[COL_URL], MAIN_DELIM, MAX_FIELDS);
+
+ if (aff_url)
+ for (i = 0; aff_url[i] != NULL; ++i)
+ add_aff_field(contact, aff_url[i], add_url);
+
+ g_strfreev(aff_url);
+}
+
+static void contact_add_organization(struct phonebook_contact *contact,
+ const char **reply)
+{
+ /* Adding fields connected by nco:hasAffiliation - they may be in
+ * separate replies */
+ add_affiliation(&contact->title, reply[COL_TITLE]);
+ add_affiliation(&contact->company, reply[COL_ORG_NAME]);
+ add_affiliation(&contact->department, reply[COL_ORG_DEPARTMENT]);
+ add_affiliation(&contact->role, reply[COL_ORG_ROLE]);
+}
+
+static void free_data_contacts(struct phonebook_data *data)
+{
+ GSList *l;
+
+ /* freeing contacts */
+ for (l = data->contacts; l; l = l->next) {
+ struct contact_data *c_data = l->data;
+
+ g_free(c_data->id);
+ phonebook_contact_free(c_data->contact);
+ g_free(c_data);
+ }
+
+ g_slist_free(data->contacts);
+ data->contacts = NULL;
+}
+
+static void send_pull_part(struct phonebook_data *data,
+ const struct apparam_field *params, gboolean lastpart)
+{
+ GString *vcards;
+
+ DBG("");
+ vcards = gen_vcards(data->contacts, params);
+ data->cb(vcards->str, vcards->len, g_slist_length(data->contacts),
+ data->newmissedcalls, lastpart, data->user_data);
+
+ if (!lastpart)
+ free_data_contacts(data);
+ g_string_free(vcards, TRUE);
+}
+
+static int pull_contacts(const char **reply, int num_fields, void *user_data)
+{
+ struct phonebook_data *data = user_data;
+ const struct apparam_field *params = data->params;
+ struct phonebook_contact *contact;
+ struct contact_data *contact_data;
+ int last_index, i;
+ gboolean cdata_present = FALSE, part_sent = FALSE;
+ static char *temp_id = NULL;
+
+ if (num_fields < 0) {
+ data->cb(NULL, 0, num_fields, 0, TRUE, data->user_data);
+ goto fail;
+ }
+
+ DBG("reply %p", reply);
+ data->tracker_index++;
+
+ if (reply == NULL)
+ goto done;
+
+ /* Trying to find contact in recently added contacts. It is needed for
+ * contacts that have more than one telephone number filled */
+ contact = find_contact(data->contacts, reply[CONTACTS_ID_COL],
+ reply[COL_DATE]);
+
+ /* If contact is already created then adding only new phone numbers */
+ if (contact) {
+ cdata_present = TRUE;
+ goto add_numbers;
+ }
+
+ /* We are doing a PullvCardEntry, no need for those checks */
+ if (data->vcardentry)
+ goto add_entry;
+
+ /* Last four fields are always present, ignoring them */
+ for (i = 0; i < num_fields - 4; i++) {
+ if (reply[i][0] != '\0')
+ break;
+ }
+
+ if (i == num_fields - 4 && !g_str_equal(reply[CONTACTS_ID_COL],
+ TRACKER_DEFAULT_CONTACT_ME))
+ return 0;
+
+ if (g_strcmp0(temp_id, reply[CONTACTS_ID_COL])) {
+ data->index++;
+ g_free(temp_id);
+ temp_id = g_strdup(reply[CONTACTS_ID_COL]);
+
+ /* Incrementing counter for vcards in current part of data,
+ * but only if liststartoffset has been already reached */
+ if (data->index > params->liststartoffset)
+ data->vcard_part_count++;
+ }
+
+ if (data->vcard_part_count > VCARDS_PART_COUNT) {
+ DBG("Part of vcard data ready for sending...");
+ data->vcard_part_count = 0;
+ /* Sending part of data to PBAP core - more data can be still
+ * fetched, so marking lastpart as FALSE */
+ send_pull_part(data, params, FALSE);
+
+ /* Later, after adding contact data, need to return -EINTR to
+ * stop fetching more data for this request. Data will be
+ * downloaded again from this point, when phonebook_pull_read
+ * will be called again with current request as a parameter*/
+ part_sent = TRUE;
+ }
+
+ last_index = params->liststartoffset + params->maxlistcount;
+
+ if (data->index <= params->liststartoffset)
+ return 0;
+
+ /* max number of results achieved - need send vcards data that was
+ * already collected and stop further data processing (these operations
+ * will be invoked in "done" section) */
+ if (data->index > last_index && params->maxlistcount > 0) {
+ DBG("Maxlistcount achieved");
+ goto done;
+ }
+
+add_entry:
+ contact = g_new0(struct phonebook_contact, 1);
+ contact_init(contact, reply);
+
+add_numbers:
+ contact_add_numbers(contact, reply);
+ contact_add_emails(contact, reply);
+ contact_add_addresses(contact, reply);
+ contact_add_urls(contact, reply);
+ contact_add_organization(contact, reply);
+
+ DBG("contact %p", contact);
+
+ /* Adding contacts data to wrapper struct - this data will be used to
+ * generate vcard list */
+ if (!cdata_present) {
+ contact_data = g_new0(struct contact_data, 1);
+ contact_data->contact = contact;
+ contact_data->id = g_strdup(reply[CONTACTS_ID_COL]);
+ data->contacts = g_slist_append(data->contacts, contact_data);
+ }
+
+ if (part_sent)
+ return -EINTR;
+
+ return 0;
+
+done:
+ /* Processing is end, this is definitely last part of transmission
+ * (marking lastpart as TRUE) */
+ send_pull_part(data, params, TRUE);
+
+fail:
+ g_free(temp_id);
+ temp_id = NULL;
+
+ return -EINTR;
+ /*
+ * phonebook_data is freed in phonebook_req_finalize. Useful in
+ * cases when call is terminated.
+ */
+}
+
+static int add_to_cache(const char **reply, int num_fields, void *user_data)
+{
+ struct phonebook_data *data = user_data;
+ char *formatted;
+ int i;
+
+ if (reply == NULL || num_fields < 0)
+ goto done;
+
+ /* the first element is the URI, always not empty */
+ for (i = 1; i < num_fields; i++) {
+ if (reply[i][0] != '\0')
+ break;
+ }
+
+ if (i == num_fields &&
+ !g_str_equal(reply[0], TRACKER_DEFAULT_CONTACT_ME))
+ return 0;
+
+ if (i == 7)
+ formatted = g_strdup(reply[7]);
+ else if (i == 6)
+ formatted = g_strdup(reply[6]);
+ else
+ formatted = g_strdup_printf("%s;%s;%s;%s;%s",
+ reply[1], reply[2], reply[3], reply[4],
+ reply[5]);
+
+ /* The owner vCard must have the 0 handle */
+ if (strcmp(reply[0], TRACKER_DEFAULT_CONTACT_ME) == 0)
+ data->entry_cb(reply[0], 0, formatted, "",
+ reply[6], data->user_data);
+ else
+ data->entry_cb(reply[0], PHONEBOOK_INVALID_HANDLE, formatted,
+ "", reply[6], data->user_data);
+
+ g_free(formatted);
+
+ return 0;
+
+done:
+ if (num_fields <= 0)
+ data->ready_cb(data->user_data);
+
+ return -EINTR;
+ /*
+ * phonebook_data is freed in phonebook_req_finalize. Useful in
+ * cases when call is terminated.
+ */
+}
+
+int phonebook_init(void)
+{
+ g_thread_init(NULL);
+ g_type_init();
+
+ return 0;
+}
+
+void phonebook_exit(void)
+{
+}
+
+char *phonebook_set_folder(const char *current_folder, const char *new_folder,
+ uint8_t flags, int *err)
+{
+ char *tmp1, *tmp2, *base, *path = NULL;
+ gboolean root, child;
+ int ret = 0;
+ int len;
+
+ root = (g_strcmp0("/", current_folder) == 0);
+ child = (new_folder && strlen(new_folder) != 0);
+
+ switch (flags) {
+ case 0x02:
+ /* Go back to root */
+ if (!child) {
+ path = g_strdup("/");
+ goto done;
+ }
+
+ path = g_build_filename(current_folder, new_folder, NULL);
+ break;
+ case 0x03:
+ /* Go up 1 level */
+ if (root) {
+ /* Already root */
+ path = g_strdup("/");
+ goto done;
+ }
+
+ /*
+ * Removing one level of the current folder. Current folder
+ * contains AT LEAST one level since it is not at root folder.
+ * Use glib utility functions to handle invalid chars in the
+ * folder path properly.
+ */
+ tmp1 = g_path_get_basename(current_folder);
+ tmp2 = g_strrstr(current_folder, tmp1);
+ len = tmp2 - (current_folder + 1);
+
+ g_free(tmp1);
+
+ if (len == 0)
+ base = g_strdup("/");
+ else
+ base = g_strndup(current_folder, len);
+
+ /* Return: one level only */
+ if (!child) {
+ path = base;
+ goto done;
+ }
+
+ path = g_build_filename(base, new_folder, NULL);
+ g_free(base);
+
+ break;
+ default:
+ ret = -EBADR;
+ break;
+ }
+
+done:
+ if (path && !folder_is_valid(path))
+ ret = -ENOENT;
+
+ if (ret < 0) {
+ g_free(path);
+ path = NULL;
+ }
+
+ if (err)
+ *err = ret;
+
+ return path;
+}
+
+static int pull_newmissedcalls(const char **reply, int num_fields,
+ void *user_data)
+{
+ struct phonebook_data *data = user_data;
+ reply_list_foreach_t pull_cb;
+ int col_amount, err;
+ const char *query;
+ int nmissed;
+
+ if (num_fields < 0) {
+ data->cb(NULL, 0, num_fields, 0, TRUE, data->user_data);
+
+ return -EINTR;
+ }
+
+ if (reply != NULL) {
+ nmissed = atoi(reply[0]);
+ data->newmissedcalls =
+ nmissed <= UINT8_MAX ? nmissed : UINT8_MAX;
+ DBG("newmissedcalls %d", data->newmissedcalls);
+
+ return 0;
+ }
+
+ if (data->params->maxlistcount == 0) {
+ query = name2count_query(PB_CALLS_MISSED);
+ col_amount = COUNT_QUERY_COL_AMOUNT;
+ pull_cb = pull_contacts_size;
+ } else {
+ query = name2query(PB_CALLS_MISSED);
+ col_amount = PULL_QUERY_COL_AMOUNT;
+ pull_cb = pull_contacts;
+ }
+
+ err = query_tracker(query, col_amount, pull_cb, data);
+ if (err < 0) {
+ data->cb(NULL, 0, err, 0, TRUE, data->user_data);
+
+ return -EINTR;
+ }
+
+ return 0;
+}
+
+void phonebook_req_finalize(void *request)
+{
+ struct phonebook_data *data = request;
+
+ DBG("");
+
+ if (!data)
+ return;
+
+ /* canceling asynchronous operation on tracker if any is active */
+ if (data->query_canc) {
+ g_cancellable_cancel(data->query_canc);
+ g_object_unref(data->query_canc);
+ }
+
+ free_data_contacts(data);
+ g_free(data->req_name);
+ g_free(data);
+}
+
+void *phonebook_pull(const char *name, const struct apparam_field *params,
+ phonebook_cb cb, void *user_data, int *err)
+{
+ struct phonebook_data *data;
+
+ DBG("name %s", name);
+
+ data = g_new0(struct phonebook_data, 1);
+ data->params = params;
+ data->user_data = user_data;
+ data->cb = cb;
+ data->req_name = g_strdup(name);
+
+ if (err)
+ *err = 0;
+
+ return data;
+}
+
+int phonebook_pull_read(void *request)
+{
+ struct phonebook_data *data = request;
+ reply_list_foreach_t pull_cb;
+ const char *query;
+ char *offset_query;
+ int col_amount;
+ int ret;
+
+ if (!data)
+ return -ENOENT;
+
+ data->newmissedcalls = 0;
+
+ if (g_strcmp0(data->req_name, PB_CALLS_MISSED) == 0 &&
+ data->tracker_index == 0) {
+ /* new missed calls amount should be counted only once - it
+ * will be done during generating first part of results of
+ * missed calls history */
+ query = NEW_MISSED_CALLS_COUNT_QUERY;
+ col_amount = COUNT_QUERY_COL_AMOUNT;
+ pull_cb = pull_newmissedcalls;
+ } else if (data->params->maxlistcount == 0) {
+ query = name2count_query(data->req_name);
+ col_amount = COUNT_QUERY_COL_AMOUNT;
+ pull_cb = pull_contacts_size;
+ } else {
+ query = name2query(data->req_name);
+ col_amount = PULL_QUERY_COL_AMOUNT;
+ pull_cb = pull_contacts;
+ }
+
+ if (query == NULL)
+ return -ENOENT;
+
+ if (pull_cb == pull_contacts && data->tracker_index > 0) {
+ /* Adding offset to pull query to download next parts of data
+ * from tracker (phonebook_pull_read may be called many times
+ * from PBAP core to fetch data partially) */
+ offset_query = g_strdup_printf(QUERY_OFFSET_FORMAT, query,
+ data->tracker_index);
+ ret = query_tracker(offset_query, col_amount, pull_cb, data);
+
+ g_free(offset_query);
+
+ return ret;
+ }
+
+ return query_tracker(query, col_amount, pull_cb, data);
+}
+
+void *phonebook_get_entry(const char *folder, const char *id,
+ const struct apparam_field *params,
+ phonebook_cb cb, void *user_data, int *err)
+{
+ struct phonebook_data *data;
+ char *query;
+ int ret;
+
+ DBG("folder %s id %s", folder, id);
+
+ data = g_new0(struct phonebook_data, 1);
+ data->user_data = user_data;
+ data->params = params;
+ data->cb = cb;
+ data->vcardentry = TRUE;
+
+ if (g_str_has_prefix(id, CONTACT_ID_PREFIX) == TRUE ||
+ g_strcmp0(id, TRACKER_DEFAULT_CONTACT_ME) == 0)
+ query = g_strdup_printf(CONTACTS_QUERY_FROM_URI, id, id, id, id,
+ id, id, id, id, id, id, id, id, id);
+ else if (g_str_has_prefix(id, CALL_ID_PREFIX) == TRUE)
+ query = g_strdup_printf(CONTACT_FROM_CALL_QUERY, id);
+ else
+ query = g_strdup_printf(CONTACTS_OTHER_QUERY_FROM_URI,
+ id, id, id);
+
+ ret = query_tracker(query, PULL_QUERY_COL_AMOUNT, pull_contacts, data);
+ if (err)
+ *err = ret;
+
+ g_free(query);
+
+ return data;
+}
+
+void *phonebook_create_cache(const char *name, phonebook_entry_cb entry_cb,
+ phonebook_cache_ready_cb ready_cb, void *user_data, int *err)
+{
+ struct phonebook_data *data;
+ const char *query;
+ int ret;
+
+ DBG("name %s", name);
+
+ query = folder2query(name);
+ if (query == NULL) {
+ if (err)
+ *err = -ENOENT;
+ return NULL;
+ }
+
+ data = g_new0(struct phonebook_data, 1);
+ data->entry_cb = entry_cb;
+ data->ready_cb = ready_cb;
+ data->user_data = user_data;
+
+ ret = query_tracker(query, 8, add_to_cache, data);
+ if (err)
+ *err = ret;
+
+ return data;
+}
diff --git a/obexd/plugins/syncevolution.c b/obexd/plugins/syncevolution.c
new file mode 100755
index 00000000..854505a5
--- /dev/null
+++ b/obexd/plugins/syncevolution.c
@@ -0,0 +1,483 @@
+/*
+ *
+ * OBEX Server
+ *
+ * Copyright (C) 2007-2010 Intel Corporation
+ * Copyright (C) 2007-2010 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/types.h>
+
+#include <glib.h>
+#include <dbus/dbus.h>
+
+#include "lib/bluetooth.h"
+
+#include "gdbus/gdbus.h"
+
+#include "btio/btio.h"
+#include "obexd/src/plugin.h"
+#include "obexd/src/obex.h"
+#include "obexd/src/service.h"
+#include "obexd/src/mimetype.h"
+#include "obexd/src/log.h"
+#include "obexd/src/manager.h"
+#include "obexd/src/obexd.h"
+#include "filesystem.h"
+
+#define SYNCML_TARGET_SIZE 11
+
+static const uint8_t SYNCML_TARGET[SYNCML_TARGET_SIZE] = {
+ 0x53, 0x59, 0x4E, 0x43, 0x4D, 0x4C, 0x2D, 0x53,
+ 0x59, 0x4E, 0x43 };
+
+#define SYNCEVOLUTION_CHANNEL 19
+
+#define SYNCEVOLUTION_RECORD "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\
+<record> \
+ <attribute id=\"0x0001\"> \
+ <sequence> \
+ <uuid value=\"00000002-0000-1000-8000-0002ee000002\"/> \
+ </sequence> \
+ </attribute> \
+ \
+ <attribute id=\"0x0004\"> \
+ <sequence> \
+ <sequence> \
+ <uuid value=\"0x0100\"/> \
+ </sequence> \
+ <sequence> \
+ <uuid value=\"0x0003\"/> \
+ <uint8 value=\"%u\" name=\"channel\"/> \
+ </sequence> \
+ <sequence> \
+ <uuid value=\"0x0008\"/> \
+ </sequence> \
+ </sequence> \
+ </attribute> \
+ \
+ <attribute id=\"0x0100\"> \
+ <text value=\"%s\" name=\"name\"/> \
+ </attribute> \
+</record>"
+
+#define SYNCE_BUS_NAME "org.syncevolution"
+#define SYNCE_PATH "/org/syncevolution/Server"
+#define SYNCE_SERVER_INTERFACE "org.syncevolution.Server"
+#define SYNCE_CONN_INTERFACE "org.syncevolution.Connection"
+
+struct synce_context {
+ struct obex_session *os;
+ DBusConnection *dbus_conn;
+ char *conn_obj;
+ unsigned int reply_watch;
+ unsigned int abort_watch;
+ GString *buffer;
+ int lasterr;
+ char *id;
+};
+
+static void append_dict_entry(DBusMessageIter *dict, const char *key,
+ int type, void *val)
+{
+ DBusMessageIter entry;
+
+ dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+ NULL, &entry);
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key);
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &val);
+ dbus_message_iter_close_container(dict, &entry);
+}
+
+static gboolean reply_signal(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct synce_context *context = data;
+ const char *path = dbus_message_get_path(msg);
+ DBusMessageIter iter, array_iter;
+ char *value;
+ int length;
+
+ if (strcmp(context->conn_obj, path) != 0) {
+ obex_object_set_io_flags(context, G_IO_ERR, -EPERM);
+ context->lasterr = -EPERM;
+ return FALSE;
+ }
+
+ dbus_message_iter_init(msg, &iter);
+
+ dbus_message_iter_recurse(&iter, &array_iter);
+ dbus_message_iter_get_fixed_array(&array_iter, &value, &length);
+
+ context->buffer = g_string_new_len(value, length);
+ obex_object_set_io_flags(context, G_IO_IN, 0);
+ context->lasterr = 0;
+
+ return TRUE;
+}
+
+static gboolean abort_signal(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct synce_context *context = data;
+
+ obex_object_set_io_flags(context, G_IO_ERR, -EPERM);
+ context->lasterr = -EPERM;
+
+ return TRUE;
+}
+
+static void connect_cb(DBusPendingCall *call, void *user_data)
+{
+ struct synce_context *context = user_data;
+ DBusConnection *conn;
+ DBusMessage *reply;
+ DBusError err;
+ char *path;
+
+ conn = context->dbus_conn;
+
+ reply = dbus_pending_call_steal_reply(call);
+
+ dbus_error_init(&err);
+ if (dbus_message_get_args(reply, &err, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID) == FALSE) {
+ error("%s", err.message);
+ dbus_error_free(&err);
+ goto failed;
+ }
+
+ DBG("Got conn object %s from syncevolution", path);
+ context->conn_obj = g_strdup(path);
+
+ context->reply_watch = g_dbus_add_signal_watch(conn, NULL, path,
+ SYNCE_CONN_INTERFACE, "Reply",
+ reply_signal, context, NULL);
+
+ context->abort_watch = g_dbus_add_signal_watch(conn, NULL, path,
+ SYNCE_CONN_INTERFACE, "Abort",
+ abort_signal, context, NULL);
+
+ dbus_message_unref(reply);
+
+ return;
+
+failed:
+ obex_object_set_io_flags(context, G_IO_ERR, -EPERM);
+ context->lasterr = -EPERM;
+}
+
+static void process_cb(DBusPendingCall *call, void *user_data)
+{
+ struct synce_context *context = user_data;
+ DBusMessage *reply;
+ DBusError derr;
+
+ reply = dbus_pending_call_steal_reply(call);
+ dbus_error_init(&derr);
+ if (dbus_set_error_from_message(&derr, reply)) {
+ error("process_cb(): syncevolution replied with an error:"
+ " %s, %s", derr.name, derr.message);
+ dbus_error_free(&derr);
+
+ obex_object_set_io_flags(context, G_IO_ERR, -EPERM);
+ context->lasterr = -EPERM;
+ goto done;
+ }
+
+ obex_object_set_io_flags(context, G_IO_OUT, 0);
+ context->lasterr = 0;
+
+done:
+ dbus_message_unref(reply);
+}
+
+static void *synce_connect(struct obex_session *os, int *err)
+{
+ DBusConnection *conn;
+ struct synce_context *context;
+ char *address;
+
+ manager_register_session(os);
+
+ conn = manager_dbus_get_connection();
+ if (!conn)
+ goto failed;
+
+ context = g_new0(struct synce_context, 1);
+ context->dbus_conn = conn;
+ context->lasterr = -EAGAIN;
+ context->os = os;
+
+ if (obex_getpeername(os, &address) == 0) {
+ context->id = g_strdup_printf("%s+%d", address,
+ SYNCEVOLUTION_CHANNEL);
+ g_free(address);
+ }
+
+ if (err)
+ *err = 0;
+
+ return context;
+
+failed:
+ if (err)
+ *err = -EPERM;
+
+ return NULL;
+}
+
+static int synce_put(struct obex_session *os, void *user_data)
+{
+ return 0;
+}
+
+static int synce_get(struct obex_session *os, void *user_data)
+{
+ return obex_get_stream_start(os, NULL);
+}
+
+static void close_cb(DBusPendingCall *call, void *user_data)
+{
+ DBusMessage *reply;
+ DBusError derr;
+
+ reply = dbus_pending_call_steal_reply(call);
+ dbus_error_init(&derr);
+ if (dbus_set_error_from_message(&derr, reply)) {
+ error("close_cb(): syncevolution replied with an error:"
+ " %s, %s", derr.name, derr.message);
+ dbus_error_free(&derr);
+ }
+
+ dbus_message_unref(reply);
+}
+
+static void synce_disconnect(struct obex_session *os, void *user_data)
+{
+ struct synce_context *context = user_data;
+
+ g_free(context);
+}
+
+static void *synce_open(const char *name, int oflag, mode_t mode,
+ void *user_data, size_t *size, int *err)
+{
+ struct synce_context *context = user_data;
+
+ if (err)
+ *err = context ? 0 : -EFAULT;
+
+ return user_data;
+}
+
+static int synce_close(void *object)
+{
+ struct synce_context *context = object;
+ DBusMessage *msg;
+ const char *error;
+ gboolean normal;
+ DBusPendingCall *call;
+
+ if (!context->conn_obj)
+ goto done;
+
+ msg = dbus_message_new_method_call(SYNCE_BUS_NAME, context->conn_obj,
+ SYNCE_CONN_INTERFACE, "Close");
+ if (!msg)
+ goto failed;
+
+ normal = TRUE;
+ error = "none";
+ dbus_message_append_args(msg, DBUS_TYPE_BOOLEAN, &normal,
+ DBUS_TYPE_STRING, &error, DBUS_TYPE_INVALID);
+
+ g_dbus_send_message_with_reply(context->dbus_conn, msg, &call, -1);
+ dbus_pending_call_set_notify(call, close_cb, NULL, NULL);
+ dbus_message_unref(msg);
+ dbus_pending_call_unref(call);
+
+failed:
+ g_dbus_remove_watch(context->dbus_conn, context->reply_watch);
+ context->reply_watch = 0;
+ g_dbus_remove_watch(context->dbus_conn, context->abort_watch);
+ context->abort_watch = 0;
+
+ g_free(context->conn_obj);
+ context->conn_obj = NULL;
+
+done:
+ dbus_connection_unref(context->dbus_conn);
+ g_free(context);
+ return 0;
+}
+
+static ssize_t synce_read(void *object, void *buf, size_t count)
+{
+ struct synce_context *context = object;
+ DBusConnection *conn;
+ char transport[36], transport_description[24];
+ const char *session;
+ DBusMessage *msg;
+ DBusMessageIter iter, dict;
+ gboolean authenticate;
+ DBusPendingCall *call;
+
+ if (context->buffer)
+ return string_read(context->buffer, buf, count);
+
+ conn = manager_dbus_get_connection();
+ if (conn == NULL)
+ return -EPERM;
+
+ msg = dbus_message_new_method_call(SYNCE_BUS_NAME, SYNCE_PATH,
+ SYNCE_SERVER_INTERFACE, "Connect");
+ if (!msg)
+ return -EPERM;
+
+ dbus_message_iter_init_append(msg, &iter);
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_STRING_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+ append_dict_entry(&dict, "id", DBUS_TYPE_STRING, context->id);
+
+ snprintf(transport, sizeof(transport), "%s.obexd", OBEXD_SERVICE);
+ append_dict_entry(&dict, "transport", DBUS_TYPE_STRING, transport);
+
+ snprintf(transport_description, sizeof(transport_description),
+ "version %s", VERSION);
+ append_dict_entry(&dict, "transport_description", DBUS_TYPE_STRING,
+ transport_description);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ authenticate = FALSE;
+ session = "";
+ dbus_message_append_args(msg, DBUS_TYPE_BOOLEAN, &authenticate,
+ DBUS_TYPE_STRING, &session, DBUS_TYPE_INVALID);
+
+ if (!g_dbus_send_message_with_reply(conn, msg, &call, -1)) {
+ error("D-Bus call to %s failed.", SYNCE_SERVER_INTERFACE);
+ dbus_message_unref(msg);
+ return -EPERM;
+ }
+
+ dbus_pending_call_set_notify(call, connect_cb, context, NULL);
+
+ dbus_pending_call_unref(call);
+ dbus_message_unref(msg);
+
+ return -EAGAIN;
+}
+
+static ssize_t synce_write(void *object, const void *buf, size_t count)
+{
+ struct synce_context *context = object;
+ DBusMessage *msg;
+ DBusMessageIter iter, array_iter;
+ DBusPendingCall *call;
+ const char *type = obex_get_type(context->os);
+
+ if (context->lasterr == 0)
+ return count;
+
+ if (!context->conn_obj)
+ return -EFAULT;
+
+ msg = dbus_message_new_method_call(SYNCE_BUS_NAME, context->conn_obj,
+ SYNCE_CONN_INTERFACE, "Process");
+ if (!msg)
+ return -EFAULT;
+
+ dbus_message_iter_init_append(msg, &iter);
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_BYTE_AS_STRING, &array_iter);
+
+ dbus_message_iter_append_fixed_array(&array_iter, DBUS_TYPE_BYTE,
+ &buf, count);
+ dbus_message_iter_close_container(&iter, &array_iter);
+
+ dbus_message_append_args(msg, DBUS_TYPE_STRING, &type,
+ DBUS_TYPE_INVALID);
+
+ if (!g_dbus_send_message_with_reply(context->dbus_conn, msg,
+ &call, -1)) {
+ error("D-Bus call to %s failed.", SYNCE_CONN_INTERFACE);
+ dbus_message_unref(msg);
+ return -EPERM;
+ }
+
+ dbus_pending_call_set_notify(call, process_cb, context, NULL);
+
+ dbus_message_unref(msg);
+ dbus_pending_call_unref(call);
+
+ return -EAGAIN;
+}
+
+static struct obex_mime_type_driver synce_driver = {
+ .target = SYNCML_TARGET,
+ .target_size = SYNCML_TARGET_SIZE,
+ .open = synce_open,
+ .close = synce_close,
+ .read = synce_read,
+ .write = synce_write,
+};
+
+static struct obex_service_driver synce = {
+ .name = "OBEX server for SyncML, using SyncEvolution",
+ .service = OBEX_SYNCEVOLUTION,
+ .channel = SYNCEVOLUTION_CHANNEL,
+ .secure = TRUE,
+ .record = SYNCEVOLUTION_RECORD,
+ .target = SYNCML_TARGET,
+ .target_size = SYNCML_TARGET_SIZE,
+ .get = synce_get,
+ .put = synce_put,
+ .connect = synce_connect,
+ .disconnect = synce_disconnect,
+};
+
+static int synce_init(void)
+{
+ int err;
+
+ err = obex_mime_type_driver_register(&synce_driver);
+ if (err < 0)
+ return err;
+
+ return obex_service_driver_register(&synce);
+}
+
+static void synce_exit(void)
+{
+ obex_service_driver_unregister(&synce);
+ obex_mime_type_driver_unregister(&synce_driver);
+}
+
+OBEX_PLUGIN_DEFINE(syncevolution, synce_init, synce_exit)
diff --git a/obexd/src/main.c b/obexd/src/main.c
index c774cda5..e2df4eff 100755
--- a/obexd/src/main.c
+++ b/obexd/src/main.c
@@ -179,6 +179,14 @@ static GOptionEntry options[] = {
{ NULL },
};
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void obex_option_set_root_folder(const char *root)
+{
+ g_free(option_root);
+ option_root = g_strdup(root);
+}
+#endif
+
gboolean obex_option_auto_accept(void)
{
return option_autoaccept;
diff --git a/obexd/src/manager.c b/obexd/src/manager.c
index f84384ae..90c51147 100755
--- a/obexd/src/manager.c
+++ b/obexd/src/manager.c
@@ -190,6 +190,52 @@ static DBusMessage *unregister_agent(DBusConnection *conn,
return dbus_message_new_method_return(msg);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static DBusMessage *set_root(DBusConnection *conn, DBusMessage *msg,
+ const char *root, void *data)
+{
+ DBG("new_root: %s", root);
+
+ /* Change the option root path (using in filesystem) */
+ obex_option_set_root_folder(root);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *set_property(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ DBusMessageIter iter;
+ DBusMessageIter sub;
+ const char *property;
+
+ if (!dbus_message_iter_init(msg, &iter))
+ return invalid_args(msg);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING)
+ return invalid_args(msg);
+
+ dbus_message_iter_get_basic(&iter, &property);
+ dbus_message_iter_next(&iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+ return invalid_args(msg);
+ dbus_message_iter_recurse(&iter, &sub);
+
+ if (g_str_equal("Root", property)) {
+ const char *root;
+
+ if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING)
+ return invalid_args(msg);
+ dbus_message_iter_get_basic(&sub, &root);
+
+ return set_root(conn, msg, root, data);
+ }
+
+ return invalid_args(msg);
+}
+#endif
+
static gboolean get_source(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *data)
{
@@ -440,6 +486,79 @@ static gboolean transfer_get_filename(const GDBusPropertyTable *property,
return TRUE;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static gboolean transfer_operation_exists(const GDBusPropertyTable *property,
+ void *data)
+{
+ struct obex_transfer *transfer = data;
+ struct obex_session *session = transfer->session;
+
+ if (session->cmd == G_OBEX_OP_PUT &&
+ session->size != OBJECT_SIZE_DELETE)
+ return TRUE;
+ else if (session->cmd == G_OBEX_OP_GET)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+static gboolean transfer_get_operation(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct obex_transfer *transfer = data;
+ struct obex_session *session = transfer->session;
+ const char *operation;
+
+ if (session->cmd == G_OBEX_OP_PUT &&
+ session->size != OBJECT_SIZE_DELETE)
+ operation = "PUT";
+ else if (session->cmd == G_OBEX_OP_GET)
+ operation = "GET";
+ else
+ return FALSE;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &operation);
+
+ return TRUE;
+}
+
+static gboolean transfer_address_exists(const GDBusPropertyTable *property,
+ void *data)
+{
+ struct obex_transfer *transfer = data;
+ struct obex_session *session = transfer->session;
+ char *address;
+ int err;
+
+ err = obex_getpeername(session, &address);
+ if (err < 0)
+ return FALSE;
+
+ g_free(address);
+
+ return TRUE;
+}
+
+static gboolean transfer_get_address(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct obex_transfer *transfer = data;
+ struct obex_session *session = transfer->session;
+ char *address;
+ int err;
+
+ err = obex_getpeername(session, &address);
+ if (err < 0)
+ return FALSE;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &address);
+ g_free(address);
+
+ return TRUE;
+}
+
+#endif
+
static gboolean transfer_get_transferred(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *data)
{
@@ -457,6 +576,10 @@ static const GDBusMethodTable manager_methods[] = {
GDBUS_ARGS({ "agent", "o" }), NULL, register_agent) },
{ GDBUS_METHOD("UnregisterAgent",
GDBUS_ARGS({ "agent", "o" }), NULL, unregister_agent) },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ { GDBUS_METHOD("SetProperty",
+ GDBUS_ARGS({ "property", "sv" }), NULL, set_property) },
+#endif
{ }
};
@@ -474,6 +597,12 @@ static const GDBusPropertyTable transfer_properties[] = {
{ "Time", "t", transfer_get_time, NULL, transfer_time_exists },
{ "Filename", "s", transfer_get_filename, NULL,
transfer_filename_exists },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ { "Operation", "s", transfer_get_operation, NULL,
+ transfer_operation_exists },
+ { "Address", "s", transfer_get_address, NULL,
+ transfer_address_exists },
+#endif
{ "Transferred", "t", transfer_get_transferred },
{ }
};
diff --git a/obexd/src/obex.c b/obexd/src/obex.c
index 788bffc6..fee2461b 100755
--- a/obexd/src/obex.c
+++ b/obexd/src/obex.c
@@ -642,7 +642,17 @@ static void parse_name(struct obex_session *os, GObexPacket *req)
if (!g_obex_header_get_unicode(hdr, &name))
return;
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ DBG("Obex Session For: %s", os->service->name);
+ if (name && g_strcmp0(os->service->name, "Object Push server") == 0) {
+ char *new_name;
+ new_name = strrchr(name, '/');
+ if (new_name) {
+ name = new_name + 1;
+ DBG("FileName %s", name);
+ }
+ }
+#endif
os->name = g_strdup(name);
DBG("NAME: %s", os->name);
}
@@ -773,7 +783,11 @@ int obex_put_stream_start(struct obex_session *os, const char *filename)
int err;
os->object = os->driver->open(filename, O_WRONLY | O_CREAT | O_TRUNC,
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ 0644, os->service_data,
+#else
0600, os->service_data,
+#endif
os->size != OBJECT_SIZE_UNKNOWN ?
(size_t *) &os->size : NULL, &err);
if (os->object == NULL) {
diff --git a/obexd/src/obexd.h b/obexd/src/obexd.h
index 42c3c4d9..ddf5ba03 100755
--- a/obexd/src/obexd.h
+++ b/obexd/src/obexd.h
@@ -41,3 +41,6 @@ gboolean obex_option_auto_accept(void);
const char *obex_option_root_folder(void);
gboolean obex_option_symlinks(void);
const char *obex_option_capability(void);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void obex_option_set_root_folder(const char *root);
+#endif
diff --git a/obexd/src/org.bluez.obex.service b/obexd/src/org.bluez.obex.service
index a5380888..3abb5551 100755
--- a/obexd/src/org.bluez.obex.service
+++ b/obexd/src/org.bluez.obex.service
@@ -1,4 +1,3 @@
[D-BUS Service]
Name=org.bluez.obex
-Exec=/bin/false
-SystemdService=dbus-org.bluez.obex.service
+Exec=/bin/sh -c 'exec /usr/libexec/bluetooth/obexd -d --noplugin=ftp,syncevolution,pcsuite,irmc --symlinks -r /opt/usr/home/owner/media/'
diff --git a/packaging/bluez.spec b/packaging/bluez.spec
index ff125945..42d5729c 100755
--- a/packaging/bluez.spec
+++ b/packaging/bluez.spec
@@ -1,10 +1,14 @@
+# Do not create provides fro extension-tm1 because the main package
+# should anchor any reverse-dependencies
+%global __provides_exclude_from ^(.*\\.tm1)$
+
#%define with_libcapng --enable-capng
%define _libpath /usr/lib
%define upgrade_script_path /usr/share/upgrade/scripts
Name: bluez
Summary: Bluetooth Stack for Linux
-Version: 5.37
-Release: 2
+Version: 5.43
+Release: 1
Group: Network & Connectivity/Bluetooth
License: GPL-2.0+ and LGPL-2.1+ and Apache-2.0
URL: http://www.bluez.org/
@@ -23,6 +27,8 @@ Source1001: bluez.manifest
#Requires: dbus >= 0.60
#BuildRequires: pkgconfig(libudev)
BuildRequires: pkgconfig(dbus-1)
+BuildRequires: pkgconfig(iniparser)
+BuildRequires: pkgconfig(libxml-2.0)
#BuildRequires: pkgconfig(glib-2.0)
#BuildRequires: pkgconfig(ncurses)
#BuildRequires: flex
@@ -45,6 +51,8 @@ BuildRequires: pkg-config
BuildRequires: readline-devel
BuildRequires: udev
BuildRequires: pkgconfig(libtzplatform-config)
+Requires: %{name}-compat = %{version}-%{release}
+Recommends: %{name}-profile_common = %{version}-%{release}
%description
The Bluetooth stack for Linux.
@@ -87,6 +95,43 @@ Requires: python-gobject
Contains a few tools for testing various bluetooth functions. The
BLUETOOTH trademarks are owned by Bluetooth SIG, Inc., U.S.A.
+%package profile_common
+Summary: Modified bluez for mobile/common profile
+Provides: %{name}-compat = %{version}-%{release}
+Provides: %{name}-profile_mobile = %{version}-%{release}
+Provides: %{name}-profile_ivi = %{version}-%{release}
+Conflicts: %{name}-profile_wearable
+Conflicts: %{name}-profile_tv
+%description profile_common
+Bluez default service script for Tizen (mobile/common profile)
+
+%package profile_wearable
+Summary: Modified bluez for wearable profile
+Provides: %{name}-compat = %{version}-%{release}
+Conflicts: %{name}-profile_common
+Conflicts: %{name}-profile_tv
+%description profile_wearable
+Bluez modified service script for Tizen wearable
+
+%package profile_tv
+Summary: Modified bluez for wearable profile
+Provides: %{name}-compat = %{version}-%{release}
+Conflicts: %{name}-profile_common
+Conflicts: %{name}-profile_wearable
+%description profile_tv
+Bluez modified service script for Tizen TV
+
+%ifarch %{ix86} || %{arm}
+%package -n libbluetooth-extension-TM1
+Summary: Extension for mobile TM1
+Requires: libbluetooth = %{version}-%{release}
+%description -n libbluetooth-extension-TM1
+Bluez default service script for Tizen mobile TM1
+When you want to uninstall this while keeping libbluetooth, you need
+to reinstall libbluetooth after uninstalling this because this package
+overwrites some contents of libbluetooth.
+%endif
+
%prep
%setup -q
cp %{SOURCE1001} .
@@ -94,20 +139,72 @@ cp %{SOURCE1001} .
%build
autoreconf -fiv
-%if "%{?tizen_target_name}" == "TM1" || "%{?profile}" == "ivi"
-%if "%{?tizen_target_name}" == "TM1"
-export CFLAGS="${CFLAGS} -DTIZEN_FEATURE_BLUEZ_SPRD_QOS -DTIZEN_FEATURE_BLUEZ_SPRD_PAGE_SCAN"
-%endif
-%else
-export CFLAGS="${CFLAGS} -DTIZEN_FEATURE_BLUEZ_BRCM_CHIP"
-%endif
+#if "{?profile}" == "ivi"
+# TIZEN_FEATURE_BLUEZ_BRCM_CHIP: only in tools/hciattach.c ==> RUNTIME! (profile.h)
+#endif
-%if "%{?profile}" == "wearable"
-export CFLAGS="${CFLAGS} -DTIZEN_FEATURE_BLUEZ_SMS_ONLY -DTIZEN_FEATURE_BLUEZ_BRCM_QOS -DTIZEN_FEATURE_BLUEZ_ROLE_CHANGE -DTIZEN_FEATURE_BLUEZ_CONFIRM_ONLY"
-%endif
+#if "{?profile}" == "wearable"
+# TIZEN_FEATURE_BLUEZ_SMS_ONLY: only in obexd/plugins/messages-tizen.c ==> RUNTIME! (profile.h)
+# TIZEN_FEATURE_BLUEZ_BRCM_QOS: only in profiles/audio/avdtp.c ==> RUNTIME! (profile.h)
+# TIZEN_FEATURE_BLUEZ_ROLE_CHANGE: only in profiles/audio/avdtp.c ==> RUNTIME! (profile.h)
+# TIZEN_FEATURE_BLUEZ_CONFIRM_ONLY: only in src/device.c ==> RUNTIME! (profile.h)
+#endif
export LDFLAGS=" -lncurses -Wl,--as-needed "
export CFLAGS+=" -DTIZEN_FEATURE_BLUEZ_MODIFY -DTIZEN_FEATURE_BLUEZ_PBAP_SIM -DTIZEN_FEATURE_BLUEZ_AVRCP_TARGET"
+export CFLAGS_DEFAULT="$CFLAGS"
+
+%ifarch %{ix86} || %{arm}
+# extension-TM1
+export CFLAGS="$CFLAGS_DEFAULT -DTIZEN_FEATURE_BLUEZ_SPRD_QOS -DTIZEN_FEATURE_BLUEZ_SPRD_PAGE_SCAN"
+# TIZEN_FEATURE_BLUEZ_SPRD_QOS: only in profiles/audio/avdtp.c
+# TIZEN_FEATURE_BLUEZ_SPRD_PAGE_SCAN: src/adapter.c
+
+%reconfigure --disable-static \
+ --sysconfdir=%{_sysconfdir} \
+ --localstatedir=%{_localstatedir} \
+ --with-systemdsystemunitdir=%{_libpath}/systemd/system \
+ --with-systemduserunitdir=%{_libpath}/systemd/user \
+ --libexecdir=%{_libexecdir} \
+ --enable-debug \
+ --enable-pie \
+ --enable-serial \
+ --enable-input \
+ --enable-usb=no \
+ --enable-tools \
+ --disable-bccmd \
+ --enable-pcmcia=no \
+ --enable-hid2hci=no \
+ --enable-alsa=no \
+ --enable-gstreamer=no \
+ --disable-dfutool \
+ --disable-cups \
+ --enable-health=yes \
+ --enable-proximity=yes \
+ --enable-tds=yes \
+ --enable-dbusoob \
+ --enable-test \
+ --with-telephony=tizen \
+ --enable-obex \
+ --enable-library \
+ --enable-gatt \
+ --enable-experimental \
+ --enable-autopair=no \
+ --enable-hid=yes \
+ --enable-tizenunusedplugin=no
+
+
+make %{?_smp_mflags} all V=1
+
+mkdir -p tm1
+
+%make_install
+cp -a %{buildroot}%{_libdir}/libbluetooth.so* tm1/
+%endif
+
+# non-extension-TM1
+export CFLAGS="$CFLAGS_DEFAULT"
+
%reconfigure --disable-static \
--sysconfdir=%{_sysconfdir} \
--localstatedir=%{_localstatedir} \
@@ -140,12 +237,15 @@ export CFLAGS+=" -DTIZEN_FEATURE_BLUEZ_MODIFY -DTIZEN_FEATURE_BLUEZ_PBAP_SIM -DT
--enable-autopair=no \
%if "%{?profile}" == "wearable"
--enable-wearable \
-%else
- --enable-network \
%endif
--enable-hid=yes \
--enable-tizenunusedplugin=no
+# The if/endif for wearable above if only for PRODUCT optimization
+# enable-wearable disables "TIZEN_HID_PLUGIN / TIZEN_UNUSED_PLUGIN" (input / hog)
+# TIZEN_UNUSED_PLUGIN is "no" regardless of enable-wearable.
+# TIZEN_HID_PLUGIN only matters.
+# enable-network is not used in configure.
make %{?_smp_mflags} all V=1
@@ -154,6 +254,11 @@ make check
%install
%make_install
+%ifarch %{ix86} || %{arm}
+pushd tm1
+for FILE in libbluetooth.so*; do mv "$FILE" "%{buildroot}%{_libdir}/$FILE.tm1"; done
+popd
+%endif
# bluez-test
rm -rvf $RPM_BUILD_ROOT/%{_libdir}/gstreamer-*
@@ -163,11 +268,9 @@ install --mode=0644 -D %{S:7} $RPM_BUILD_ROOT/%{_sysconfdir}/modprobe.d/50-bluet
# no idea why this is suddenly necessary...
install --mode 0755 -d $RPM_BUILD_ROOT/var/lib/bluetooth
-%if "%{?profile}" == "wearable"
-install -D -m 0644 src/main_w.conf %{buildroot}%{_sysconfdir}/bluetooth/main.conf
-%else
+install -D -m 0644 src/main_w.conf %{buildroot}%{_sysconfdir}/bluetooth/main.conf.wearable
install -D -m 0644 src/main_m.conf %{buildroot}%{_sysconfdir}/bluetooth/main.conf
-%endif
+
#install -D -m 0644 src/bluetooth.conf %{buildroot}%{_sysconfdir}/dbus-1/system.d/bluetooth.conf
#install -D -m 0644 profiles/audio/audio.conf %{buildroot}%{_sysconfdir}/bluetooth/audio.conf
@@ -192,11 +295,11 @@ install -D -m 0755 attrib/gatttool $RPM_BUILD_ROOT/%{_bindir}/
install -D -m 0755 tools/obexctl %{buildroot}%{_bindir}/obexctl
#test
-%if "%{?profile}" == "tv"
+#if "tv"
mkdir -p %{buildroot}%{_libpath}/systemd/system/multi-user.target.wants/
ln -sf bluetooth.service %{buildroot}%{_libpath}/systemd/system/dbus-org.bluez.service
ln -sf ../bluetooth-frwk.service %{buildroot}%{_libpath}/systemd/system/multi-user.target.wants/bluetooth.service
-%endif
+#endif
mkdir -p %{buildroot}%{upgrade_script_path}
cp -f packaging/500.bluez_upgrade.sh %{buildroot}%{upgrade_script_path}
@@ -205,12 +308,19 @@ cp -f packaging/500.bluez_upgrade.sh %{buildroot}%{upgrade_script_path}
%postun -n libbluetooth -p /sbin/ldconfig
+%ifarch %{ix86} || %{arm}
+%post -n libbluetooth-extension-TM1
+pushd %{_libdir}
+for FILE in libbluetooth.so*.tm1; do mv "$FILE" "${FILE%.tm1}"; done
+popd
+/sbin/ldconfig
+%endif
+
%files
%manifest %{name}.manifest
%defattr(-, root, root)
%license COPYING
#%{_sysconfdir}/bluetooth/audio.conf
-%{_sysconfdir}/bluetooth/main.conf
#%{_sysconfdir}/bluetooth/network.conf
#%{_sysconfdir}/bluetooth/rfcomm.conf
#%{_sysconfdir}/dbus-1/system.d/bluetooth.conf
@@ -234,16 +344,9 @@ cp -f packaging/500.bluez_upgrade.sh %{buildroot}%{upgrade_script_path}
%{upgrade_script_path}/500.bluez_upgrade.sh
#test -2
-%if "%{?profile}" == "tv"
-%{_libpath}/systemd/system/bluetooth.service
-%{_libpath}/systemd/system/multi-user.target.wants/bluetooth.service
-%{_libpath}/systemd/system/dbus-org.bluez.service
-%{_datadir}/dbus-1/system-services/org.bluez.service
-%else
%exclude /%{_libpath}/systemd/system/bluetooth.service
%exclude %{_libpath}/systemd/system/multi-user.target.wants/bluetooth.service
%exclude /%{_datadir}/dbus-1/system-services/org.bluez.service
-%endif
%config %{_sysconfdir}/dbus-1/system.d/bluetooth.conf
%dir /var/lib/bluetooth
%dir %{_sysconfdir}/modprobe.d
@@ -263,8 +366,16 @@ cp -f packaging/500.bluez_upgrade.sh %{buildroot}%{upgrade_script_path}
%manifest %{name}.manifest
%defattr(-, root, root)
%{_libdir}/libbluetooth.so.*
+%exclude %{_libdir}/libbluetooth.so*.tm1
%license COPYING
+%ifarch %{ix86} || %{arm}
+%files -n libbluetooth-extension-TM1
+%manifest %{name}.manifest
+%defattr(-, root, root)
+%{_libdir}/libbluetooth.so*.tm1
+%endif
+
%files -n obexd
%manifest %{name}.manifest
%defattr(-,root,root,-)
@@ -298,4 +409,21 @@ cp -f packaging/500.bluez_upgrade.sh %{buildroot}%{upgrade_script_path}
%docs_package
+%post profile_wearable
+ln -sf main.conf.wearable %{_sysconfdir}/bluetooth/main.conf
+%preun profile_wearable
+rm %{_sysconfdir}/bluetooth/main.conf
+%files profile_wearable
+%{_sysconfdir}/bluetooth/main.conf.wearable
+
+%files profile_tv
+%{_sysconfdir}/bluetooth/main.conf
+%{_libpath}/systemd/system/bluetooth.service
+%{_libpath}/systemd/system/multi-user.target.wants/bluetooth.service
+%{_libpath}/systemd/system/dbus-org.bluez.service
+%{_datadir}/dbus-1/system-services/org.bluez.service
+
+%files profile_common
+%{_sysconfdir}/bluetooth/main.conf
+
%changelog
diff --git a/plugins/dbusoob.c b/plugins/dbusoob.c
new file mode 100755
index 00000000..8948d5d1
--- /dev/null
+++ b/plugins/dbusoob.c
@@ -0,0 +1,273 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 ST-Ericsson SA
+ *
+ * Author: Szymon Janc <szymon.janc@tieto.com> for ST-Ericsson
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include "gdbus/gdbus.h"
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/sdp.h>
+
+#include "src/plugin.h"
+#include "src/log.h"
+#include "src/dbus-common.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/eir.h"
+#include "src/agent.h"
+#include "src/hcid.h"
+#include "src/error.h"
+
+#define OOB_INTERFACE "org.bluez.OutOfBand"
+
+struct oob_request {
+ struct btd_adapter *adapter;
+ DBusMessage *msg;
+};
+
+static GSList *oob_requests = NULL;
+static DBusConnection *connection = NULL;
+
+static gint oob_request_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct oob_request *data = a;
+ const struct btd_adapter *adapter = b;
+
+ return data->adapter != adapter;
+}
+
+static struct oob_request *find_oob_request(struct btd_adapter *adapter)
+{
+ GSList *match;
+
+ match = g_slist_find_custom(oob_requests, adapter, oob_request_cmp);
+
+ if (match)
+ return match->data;
+
+ return NULL;
+}
+
+static void read_local_data_complete(struct btd_adapter *adapter,
+ const uint8_t *hash192, const uint8_t *randomizer192,
+ const uint8_t *hash256, const uint8_t *randomizer256,
+ void *user_data)
+{
+ struct DBusMessage *reply;
+ struct oob_request *oob_request;
+
+ oob_request = find_oob_request(adapter);
+ if (!oob_request)
+ return;
+
+ if ((hash192 && randomizer192) || (hash256 && randomizer256))
+ reply = g_dbus_create_reply(oob_request->msg,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &hash192, 16,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &randomizer192, 16,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
+ &hash256, hash256 ? 16 : 0,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
+ &randomizer256, randomizer256 ? 16 : 0,
+ DBUS_TYPE_INVALID);
+ else
+ reply = btd_error_failed(oob_request->msg,
+ "Failed to read local OOB data.");
+
+ oob_requests = g_slist_remove(oob_requests, oob_request);
+ dbus_message_unref(oob_request->msg);
+ g_free(oob_request);
+
+ if (!reply) {
+ error("Couldn't allocate D-Bus message");
+ return;
+ }
+
+ if (!g_dbus_send_message(connection, reply))
+ error("D-Bus send failed");
+}
+
+static DBusMessage *read_local_data(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct btd_adapter *adapter = data;
+ struct oob_request *oob_request;
+ struct oob_handler *handler;
+
+ if (find_oob_request(adapter))
+ return btd_error_in_progress(msg);
+
+ if (btd_adapter_read_local_oob_data(adapter))
+ return btd_error_failed(msg, "Request failed.");
+
+ oob_request = g_new(struct oob_request, 1);
+ oob_request->adapter = adapter;
+ oob_requests = g_slist_append(oob_requests, oob_request);
+ oob_request->msg = dbus_message_ref(msg);
+
+ handler = g_new0(struct oob_handler, 1);
+ handler->read_local_cb = read_local_data_complete;
+
+ btd_adapter_set_oob_handler(oob_request->adapter, handler);
+
+ return NULL;
+}
+
+static DBusMessage *add_remote_data(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct btd_adapter *adapter = data;
+ const char *addr = NULL;
+ uint8_t *hash192 = NULL;
+ uint8_t *randomizer192 = NULL;
+ int32_t h192_len = 0;
+ int32_t r192_len = 0;
+ uint8_t *hash256 = NULL;
+ uint8_t *randomizer256 = NULL;
+ int32_t h256_len = 0;
+ int32_t r256_len = 0;
+ bdaddr_t bdaddr;
+ uint8_t addr_type = 0;
+ bool valid_len;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &addr,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &hash192, &h192_len,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &randomizer192, &r192_len,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &hash256, &h256_len,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &randomizer256, &r256_len,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ valid_len = (h192_len == 16 && r192_len == 16) ||
+ (h256_len == 16 && r256_len == 16);
+
+ if (!valid_len || bachk(addr))
+ return btd_error_invalid_args(msg);
+
+ str2ba(addr, &bdaddr);
+
+ if (btd_adapter_add_remote_oob_ext_data(adapter, &bdaddr, addr_type,
+ hash192, randomizer192,
+ hash256, randomizer256))
+ return btd_error_failed(msg, "Request failed");
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *remove_remote_data(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct btd_adapter *adapter = data;
+ const char *addr;
+ bdaddr_t bdaddr;
+ uint8_t addr_type = 0;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &addr,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ if (bachk(addr))
+ return btd_error_invalid_args(msg);
+
+ str2ba(addr, &bdaddr);
+
+ if (btd_adapter_remove_remote_oob_ext_data(adapter, &bdaddr, addr_type))
+ return btd_error_failed(msg, "Request failed");
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static const GDBusMethodTable oob_methods[] = {
+ { GDBUS_METHOD("AddRemoteData",
+ GDBUS_ARGS({ "address", "s" },
+ { "hash192", "ay" }, { "randomizer192", "ay" },
+ { "hash256", "ay" }, { "randomizer256", "ay" }),
+ NULL,
+ add_remote_data) },
+ { GDBUS_METHOD("RemoveRemoteData",
+ GDBUS_ARGS({ "address", "s" }), NULL,
+ remove_remote_data) },
+ { GDBUS_ASYNC_METHOD("ReadLocalData",
+ NULL, GDBUS_ARGS(
+ {"hash192", "ay" }, { "randomizer192", "ay" },
+ {"hash256", "ay" }, { "randomizer256", "ay" }),
+ read_local_data) },
+ { }
+};
+
+static int oob_probe(struct btd_adapter *adapter)
+{
+ const char *path = adapter_get_path(adapter);
+
+ DBG("dbusoob probe");
+ DBG("adapter path: %s", path);
+
+ if (!g_dbus_register_interface(connection, path, OOB_INTERFACE,
+ oob_methods, NULL, NULL, adapter, NULL)) {
+ error("OOB interface init failed on path %s", path);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void oob_remove(struct btd_adapter *adapter)
+{
+ read_local_data_complete(adapter, NULL, NULL, NULL, NULL, NULL);
+
+ g_dbus_unregister_interface(connection, adapter_get_path(adapter),
+ OOB_INTERFACE);
+}
+
+static struct btd_adapter_driver oob_driver = {
+ .name = "oob",
+ .probe = oob_probe,
+ .remove = oob_remove,
+};
+
+static int dbusoob_init(void)
+{
+ DBG("Setup dbusoob plugin");
+
+ connection = btd_get_dbus_connection();
+
+ return btd_register_adapter_driver(&oob_driver);
+}
+
+static void dbusoob_exit(void)
+{
+ DBG("Cleanup dbusoob plugin");
+
+ btd_unregister_adapter_driver(&oob_driver);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(dbusoob, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+ dbusoob_init, dbusoob_exit)
+#endif
diff --git a/plugins/neard.c b/plugins/neard.c
index cabcf340..51e276e4 100755
--- a/plugins/neard.c
+++ b/plugins/neard.c
@@ -259,9 +259,16 @@ static DBusMessage *create_request_oob_reply(struct btd_adapter *adapter,
return reply;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void read_local_complete(struct btd_adapter *adapter,
+ const uint8_t *hash, const uint8_t *randomizer,
+ const uint8_t *hash256, const uint8_t *randomizer256,
+ void *user_data)
+#else
static void read_local_complete(struct btd_adapter *adapter,
const uint8_t *hash, const uint8_t *randomizer,
void *user_data)
+#endif
{
DBusMessage *msg = user_data;
DBusMessage *reply;
diff --git a/plugins/policy.c b/plugins/policy.c
index c9a7c84b..9fdee00b 100755
--- a/plugins/policy.c
+++ b/plugins/policy.c
@@ -45,12 +45,21 @@
#include "src/profile.h"
#include "src/hcid.h"
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define CONTROL_CONNECT_TIMEOUT 4
+#define TARGET_CONNECT_TIMEOUT 1
+#else
#define CONTROL_CONNECT_TIMEOUT 2
+#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
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define SOURCE_RETRIES 0
+#else
#define SOURCE_RETRIES 1
+#endif
#define SINK_RETRIES SOURCE_RETRIES
#define CT_RETRIES 1
#define TG_RETRIES CT_RETRIES
@@ -287,7 +296,15 @@ static void sink_cb(struct btd_service *service, btd_service_state_t old_state,
* immediatelly otherwise set timer
*/
if (old_state == BTD_SERVICE_STATE_CONNECTING)
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ /* Set timer as most of the devices initiate
+ * avrcp connection immediately; irrespective of local
+ * or remote initiated a2dp connection
+ */
+ 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, CONTROL_CONNECT_TIMEOUT);
@@ -317,8 +334,15 @@ 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_FEATURE_BLUEZ_MODIFY
+ data->tg_timer = g_timeout_add_seconds(TARGET_CONNECT_TIMEOUT,
+ policy_connect_tg,
+ data);
+#else
data->tg_timer = g_timeout_add_seconds(timeout, policy_connect_tg,
data);
+#endif
}
static gboolean policy_connect_source(gpointer user_data)
@@ -386,9 +410,13 @@ static void source_cb(struct btd_service *service,
if (data->tg_timer > 0) {
g_source_remove(data->tg_timer);
data->tg_timer = 0;
+#if defined TIZEN_FEATURE_BLUEZ_MODIFY && defined BT_QUALIFICATION
+ }
+#else
} else if (btd_service_get_state(target) !=
BTD_SERVICE_STATE_DISCONNECTED)
policy_disconnect(data, target);
+#endif
break;
case BTD_SERVICE_STATE_CONNECTING:
break;
diff --git a/profile.h b/profile.h
new file mode 100755
index 00000000..697303d6
--- /dev/null
+++ b/profile.h
@@ -0,0 +1,164 @@
+/*
+ * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifndef __TIZEN_PROFILE_H__
+#define __TIZEN_PROFILE_H__
+
+#include <stdlib.h>
+
+#include <iniparser.h>
+#include <libxml/xmlmemory.h>
+#include <libxml/parser.h>
+#include <libxml/tree.h>
+
+#define MODEL_CONFIG_FILE "/etc/config/model-config.xml"
+#define TYPE_FIELD "string"
+#define FEATURE_TAG "platform"
+#define MODEL_CONFIG_TAG "model-config"
+
+typedef enum {
+ TIZEN_PROFILE_UNKNOWN = 0,
+ TIZEN_PROFILE_MOBILE = 0x1,
+ TIZEN_PROFILE_WEARABLE = 0x2,
+ TIZEN_PROFILE_TV = 0x4,
+ TIZEN_PROFILE_IVI = 0x8,
+ TIZEN_PROFILE_COMMON = 0x10,
+} tizen_profile_t;
+
+static tizen_profile_t profile = TIZEN_PROFILE_UNKNOWN;
+
+static inline int __get_profile_from_model_config_xml(const char *field, char **value)
+{
+ char *node_name = NULL;
+ char *node_value = NULL;
+ xmlNode *cur_node = NULL;
+ xmlNodePtr cur_ptr = NULL;
+ xmlNodePtr model_ptr = NULL;
+ xmlDocPtr xml_doc = NULL;
+
+ xml_doc = xmlParseFile(MODEL_CONFIG_FILE);
+ if (xml_doc == NULL)
+ return -1;
+
+ cur_ptr = xmlDocGetRootElement(xml_doc);
+ if (cur_ptr == NULL) {
+ xmlFreeDoc(xml_doc);
+ return -1;
+ }
+
+ for (cur_node = cur_ptr; cur_node; cur_node = cur_node->next) {
+ if (!xmlStrcmp(cur_ptr->name, (const xmlChar*)MODEL_CONFIG_TAG))
+ break;
+ }
+
+ if (cur_ptr == NULL) {
+ xmlFreeDoc(xml_doc);
+ return -1;
+ }
+
+ cur_ptr = cur_ptr->xmlChildrenNode;
+ for (cur_node = cur_ptr; cur_node; cur_node = cur_node->next) {
+ if (!xmlStrcmp(cur_node->name, (const xmlChar*)FEATURE_TAG)) {
+ model_ptr = cur_node;
+ break;
+ }
+ }
+
+ if (model_ptr == NULL) {
+ xmlFreeDoc(xml_doc);
+ return -1;
+ }
+
+ if (model_ptr) {
+ cur_ptr = model_ptr->xmlChildrenNode;
+
+ for (cur_node = cur_ptr; cur_node; cur_node = cur_node->next) {
+ if (cur_node->type == XML_ELEMENT_NODE) {
+ node_name = (char *)xmlGetProp(cur_node, (const xmlChar*)"name");
+
+ if (!strncmp(node_name, field, strlen(node_name))) {
+ node_value = (char *)xmlNodeListGetString(xml_doc, cur_node->xmlChildrenNode, 1);
+ if (node_value) {
+ *value = strdup(node_value);
+ free(node_name);
+ free(node_value);
+ break;
+ }
+ }
+ free(node_name);
+ }
+ }
+ }
+
+ xmlFreeDoc(xml_doc);
+ return 0;
+}
+
+static inline tizen_profile_t _get_tizen_profile(void)
+{
+ char *profile_name = NULL;
+
+ if (__builtin_expect(profile != TIZEN_PROFILE_UNKNOWN, 1))
+ return profile;
+
+ if (__get_profile_from_model_config_xml("tizen.org/feature/profile",
+ &profile_name) < 0) {
+ profile = TIZEN_PROFILE_MOBILE;
+ return profile;
+ }
+
+ if (profile_name == NULL) {
+ profile = TIZEN_PROFILE_MOBILE;
+ return profile;
+ }
+
+ switch (*profile_name) {
+ case 'm':
+ case 'M':
+ profile = TIZEN_PROFILE_MOBILE;
+ break;
+ case 'w':
+ case 'W':
+ profile = TIZEN_PROFILE_WEARABLE;
+ break;
+ case 't':
+ case 'T':
+ profile = TIZEN_PROFILE_TV;
+ break;
+ case 'i':
+ case 'I':
+ profile = TIZEN_PROFILE_IVI;
+ break;
+ default: /* common or unknown ==> ALL ARE COMMON */
+ profile = TIZEN_PROFILE_COMMON;
+ }
+ free(profile_name);
+
+ return profile;
+}
+
+#define TIZEN_FEATURE_BLUEZ_BRCM_CHIP ((_get_tizen_profile()) == TIZEN_PROFILE_IVI)
+#define TIZEN_FEATURE_BLUEZ_SMS_ONLY ((_get_tizen_profile()) == TIZEN_PROFILE_WEARABLE)
+#define TIZEN_FEATURE_BLUEZ_BRCM_QOS ((_get_tizen_profile()) == TIZEN_PROFILE_WEARABLE)
+#define TIZEN_FEATURE_BLUEZ_ROLE_CHANGE ((_get_tizen_profile()) == TIZEN_PROFILE_WEARABLE)
+#define TIZEN_FEATURE_BLUEZ_CONFIRM_ONLY ((_get_tizen_profile()) == TIZEN_PROFILE_WEARABLE)
+
+
+#endif /* __TIZEN_PROFILE_H__ */
+
diff --git a/profiles/alert/server.c b/profiles/alert/server.c
new file mode 100755
index 00000000..2f6e3cde
--- /dev/null
+++ b/profiles/alert/server.c
@@ -0,0 +1,1044 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ * Copyright (C) 2011 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/hci.h"
+#include "lib/hci_lib.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
+#include "src/plugin.h"
+#include "src/dbus-common.h"
+#include "attrib/att.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "attrib/att-database.h"
+#include "src/log.h"
+#include "attrib/gatt-service.h"
+#include "attrib/gattrib.h"
+#include "src/attrib-server.h"
+#include "attrib/gatt.h"
+#include "src/profile.h"
+#include "src/error.h"
+#include "src/textfile.h"
+#include "src/attio.h"
+
+#define PHONE_ALERT_STATUS_SVC_UUID 0x180E
+#define ALERT_NOTIF_SVC_UUID 0x1811
+
+#define ALERT_STATUS_CHR_UUID 0x2A3F
+#define RINGER_CP_CHR_UUID 0x2A40
+#define RINGER_SETTING_CHR_UUID 0x2A41
+
+#define ALERT_NOTIF_CP_CHR_UUID 0x2A44
+#define UNREAD_ALERT_CHR_UUID 0x2A45
+#define NEW_ALERT_CHR_UUID 0x2A46
+#define SUPP_NEW_ALERT_CAT_CHR_UUID 0x2A47
+#define SUPP_UNREAD_ALERT_CAT_CHR_UUID 0x2A48
+
+#define ALERT_OBJECT_PATH "/org/bluez"
+#define ALERT_INTERFACE "org.bluez.Alert1"
+#define ALERT_AGENT_INTERFACE "org.bluez.AlertAgent1"
+
+/* Maximum length for "Text String Information" */
+#define NEW_ALERT_MAX_INFO_SIZE 18
+/* Maximum length for New Alert Characteristic Value */
+#define NEW_ALERT_CHR_MAX_VALUE_SIZE (NEW_ALERT_MAX_INFO_SIZE + 2)
+
+enum {
+ ENABLE_NEW_INCOMING,
+ ENABLE_UNREAD_CAT,
+ DISABLE_NEW_INCOMING,
+ DISABLE_UNREAD_CAT,
+ NOTIFY_NEW_INCOMING,
+ NOTIFY_UNREAD_CAT,
+};
+
+enum {
+ RINGER_SILENT_MODE = 1,
+ RINGER_MUTE_ONCE,
+ RINGER_CANCEL_SILENT_MODE,
+};
+
+/* Ringer Setting characteristic values */
+enum {
+ RINGER_SILENT,
+ RINGER_NORMAL,
+};
+
+enum notify_type {
+ NOTIFY_RINGER_SETTING = 0,
+ NOTIFY_ALERT_STATUS,
+ NOTIFY_NEW_ALERT,
+ NOTIFY_UNREAD_ALERT,
+ NOTIFY_SIZE,
+};
+
+struct alert_data {
+ const char *category;
+ char *srv;
+ char *path;
+ guint watcher;
+};
+
+struct alert_adapter {
+ struct btd_adapter *adapter;
+ uint16_t supp_new_alert_cat_handle;
+ uint16_t supp_unread_alert_cat_handle;
+ uint16_t hnd_ccc[NOTIFY_SIZE];
+ uint16_t hnd_value[NOTIFY_SIZE];
+};
+
+struct notify_data {
+ struct alert_adapter *al_adapter;
+ enum notify_type type;
+ uint8_t *value;
+ size_t len;
+};
+
+struct notify_callback {
+ struct notify_data *notify_data;
+ struct btd_device *device;
+ guint id;
+};
+
+static GSList *registered_alerts = NULL;
+static GSList *alert_adapters = NULL;
+static uint8_t ringer_setting = RINGER_NORMAL;
+static uint8_t alert_status = 0;
+
+static const char * const anp_categories[] = {
+ "simple",
+ "email",
+ "news",
+ "call",
+ "missed-call",
+ "sms-mms",
+ "voice-mail",
+ "schedule",
+ "high-priority",
+ "instant-message",
+};
+
+static const char * const pasp_categories[] = {
+ "ringer",
+ "vibrate",
+ "display",
+};
+
+static int adapter_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct alert_adapter *al_adapter = a;
+ const struct btd_adapter *adapter = b;
+
+ return al_adapter->adapter == adapter ? 0 : -1;
+}
+
+static struct alert_adapter *find_alert_adapter(struct btd_adapter *adapter)
+{
+ GSList *l = g_slist_find_custom(alert_adapters, adapter, adapter_cmp);
+
+ return l ? l->data : NULL;
+}
+
+static void alert_data_destroy(gpointer user_data)
+{
+ struct alert_data *alert = user_data;
+
+ if (alert->watcher)
+ g_dbus_remove_watch(btd_get_dbus_connection(), alert->watcher);
+
+ g_free(alert->srv);
+ g_free(alert->path);
+ g_free(alert);
+}
+
+static void alert_release(gpointer user_data)
+{
+ struct alert_data *alert = user_data;
+ DBusMessage *msg;
+
+ msg = dbus_message_new_method_call(alert->srv, alert->path,
+ ALERT_AGENT_INTERFACE,
+ "Release");
+ if (msg)
+ g_dbus_send_message(btd_get_dbus_connection(), msg);
+
+ alert_data_destroy(alert);
+}
+
+static void alert_destroy(gpointer user_data)
+{
+ DBG("");
+
+ g_slist_free_full(registered_alerts, alert_release);
+ registered_alerts = NULL;
+}
+
+static const char *valid_category(const char *category)
+{
+ unsigned i;
+
+ for (i = 0; i < G_N_ELEMENTS(anp_categories); i++) {
+ if (g_str_equal(anp_categories[i], category))
+ return anp_categories[i];
+ }
+
+ for (i = 0; i < G_N_ELEMENTS(pasp_categories); i++) {
+ if (g_str_equal(pasp_categories[i], category))
+ return pasp_categories[i];
+ }
+
+ return NULL;
+}
+
+static struct alert_data *get_alert_data_by_category(const char *category)
+{
+ GSList *l;
+ struct alert_data *alert;
+
+ for (l = registered_alerts; l; l = g_slist_next(l)) {
+ alert = l->data;
+ if (g_str_equal(alert->category, category))
+ return alert;
+ }
+
+ return NULL;
+}
+
+static gboolean registered_category(const char *category)
+{
+ struct alert_data *alert;
+
+ alert = get_alert_data_by_category(category);
+ if (alert)
+ return TRUE;
+
+ return FALSE;
+}
+
+static gboolean pasp_category(const char *category)
+{
+ unsigned i;
+
+ for (i = 0; i < G_N_ELEMENTS(pasp_categories); i++)
+ if (g_str_equal(category, pasp_categories[i]))
+ return TRUE;
+
+ return FALSE;
+}
+
+static gboolean valid_description(const char *category,
+ const char *description)
+{
+ if (!pasp_category(category)) {
+ if (strlen(description) >= NEW_ALERT_MAX_INFO_SIZE)
+ return FALSE;
+
+ return TRUE;
+ }
+
+ if (g_str_equal(description, "active") ||
+ g_str_equal(description, "not active"))
+ return TRUE;
+
+ if (g_str_equal(category, "ringer"))
+ if (g_str_equal(description, "enabled") ||
+ g_str_equal(description, "disabled"))
+ return TRUE;
+
+ return FALSE;
+}
+
+static gboolean valid_count(const char *category, uint16_t count)
+{
+ if (!pasp_category(category) && count > 0 && count <= 255)
+ return TRUE;
+
+ if (pasp_category(category) && count == 1)
+ return TRUE;
+
+ return FALSE;
+}
+
+static void update_supported_categories(gpointer data, gpointer user_data)
+{
+ struct alert_adapter *al_adapter = data;
+ struct btd_adapter *adapter = al_adapter->adapter;
+ uint8_t value[2];
+ unsigned int i;
+
+ memset(value, 0, sizeof(value));
+
+ for (i = 0; i < G_N_ELEMENTS(anp_categories); i++) {
+ if (registered_category(anp_categories[i]))
+ hci_set_bit(i, value);
+ }
+
+ attrib_db_update(adapter, al_adapter->supp_new_alert_cat_handle, NULL,
+ value, sizeof(value), NULL);
+
+ /* FIXME: For now report all registered categories as supporting unread
+ * status, until it is known which ones should be supported */
+ attrib_db_update(adapter, al_adapter->supp_unread_alert_cat_handle,
+ NULL, value, sizeof(value), NULL);
+}
+
+static void watcher_disconnect(DBusConnection *conn, void *user_data)
+{
+ struct alert_data *alert = user_data;
+
+ DBG("Category %s was disconnected", alert->category);
+
+ registered_alerts = g_slist_remove(registered_alerts, alert);
+ alert_data_destroy(alert);
+
+ g_slist_foreach(alert_adapters, update_supported_categories, NULL);
+}
+
+static gboolean is_notifiable_device(struct btd_device *device, uint16_t ccc)
+{
+ char *filename;
+ GKeyFile *key_file;
+ char handle[6];
+ char *str;
+ uint16_t val;
+ gboolean result;
+
+ sprintf(handle, "%hu", ccc);
+
+ filename = btd_device_get_storage_path(device, "ccc");
+ if (!filename) {
+ warn("Unable to get ccc storage path for device");
+ return FALSE;
+ }
+
+ key_file = g_key_file_new();
+ g_key_file_load_from_file(key_file, filename, 0, NULL);
+
+ str = g_key_file_get_string(key_file, handle, "Value", NULL);
+ if (!str) {
+ result = FALSE;
+ goto end;
+ }
+
+ val = strtol(str, NULL, 16);
+ if (!(val & 0x0001)) {
+ result = FALSE;
+ goto end;
+ }
+
+ result = TRUE;
+end:
+ g_free(str);
+ g_free(filename);
+ g_key_file_free(key_file);
+
+ return result;
+}
+
+static void destroy_notify_callback(guint8 status, const guint8 *pdu, guint16 len,
+ gpointer user_data)
+{
+ struct notify_callback *cb = user_data;
+
+ DBG("status=%#x", status);
+
+ btd_device_remove_attio_callback(cb->device, cb->id);
+ btd_device_unref(cb->device);
+ g_free(cb->notify_data->value);
+ g_free(cb->notify_data);
+ g_free(cb);
+}
+
+static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
+{
+ struct notify_callback *cb = user_data;
+ struct notify_data *nd = cb->notify_data;
+ enum notify_type type = nd->type;
+ struct alert_adapter *al_adapter = nd->al_adapter;
+ size_t len;
+ uint8_t *pdu = g_attrib_get_buffer(attrib, &len);
+
+
+ switch (type) {
+ case NOTIFY_RINGER_SETTING:
+ len = enc_notification(al_adapter->hnd_value[type],
+ &ringer_setting, sizeof(ringer_setting),
+ pdu, len);
+ break;
+ case NOTIFY_ALERT_STATUS:
+ len = enc_notification(al_adapter->hnd_value[type],
+ &alert_status, sizeof(alert_status),
+ pdu, len);
+ break;
+ case NOTIFY_NEW_ALERT:
+ case NOTIFY_UNREAD_ALERT:
+ len = enc_notification(al_adapter->hnd_value[type],
+ nd->value, nd->len, pdu, len);
+ break;
+ case NOTIFY_SIZE:
+ default:
+ DBG("Unknown type, could not send notification");
+ goto end;
+ }
+
+ DBG("Send notification for handle: 0x%04x, ccc: 0x%04x",
+ al_adapter->hnd_value[type],
+ al_adapter->hnd_ccc[type]);
+
+ g_attrib_send(attrib, 0, pdu, len, destroy_notify_callback, cb, NULL);
+
+ return;
+
+end:
+ btd_device_remove_attio_callback(cb->device, cb->id);
+ btd_device_unref(cb->device);
+ g_free(cb->notify_data->value);
+ g_free(cb->notify_data);
+ g_free(cb);
+}
+
+static void filter_devices_notify(struct btd_device *device, void *user_data)
+{
+ struct notify_data *notify_data = user_data;
+ struct alert_adapter *al_adapter = notify_data->al_adapter;
+ enum notify_type type = notify_data->type;
+ struct notify_callback *cb;
+
+ if (!is_notifiable_device(device, al_adapter->hnd_ccc[type]))
+ return;
+
+ cb = g_new0(struct notify_callback, 1);
+ cb->notify_data = notify_data;
+ cb->device = btd_device_ref(device);
+ cb->id = btd_device_add_attio_callback(device,
+ attio_connected_cb, NULL, cb);
+}
+
+static void notify_devices(struct alert_adapter *al_adapter,
+ enum notify_type type, uint8_t *value, size_t len)
+{
+ struct notify_data *notify_data;
+
+ notify_data = g_new0(struct notify_data, 1);
+ notify_data->al_adapter = al_adapter;
+ notify_data->type = type;
+ notify_data->value = g_memdup(value, len);
+ notify_data->len = len;
+
+ btd_adapter_for_each_device(al_adapter->adapter, filter_devices_notify,
+ notify_data);
+}
+
+static void pasp_notification(enum notify_type type)
+{
+ GSList *it;
+ struct alert_adapter *al_adapter;
+
+ for (it = alert_adapters; it; it = g_slist_next(it)) {
+ al_adapter = it->data;
+
+ notify_devices(al_adapter, type, NULL, 0);
+ }
+}
+
+static DBusMessage *register_alert(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ const char *sender = dbus_message_get_sender(msg);
+ char *path;
+ const char *category;
+ const char *c;
+ struct alert_data *alert;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &c,
+ DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ category = valid_category(c);
+ if (!category) {
+ DBG("Invalid category: %s", c);
+ return btd_error_invalid_args(msg);
+ }
+
+ if (registered_category(category)) {
+ DBG("Category %s already registered", category);
+ return dbus_message_new_method_return(msg);
+ }
+
+ alert = g_new0(struct alert_data, 1);
+ alert->srv = g_strdup(sender);
+ alert->path = g_strdup(path);
+ alert->category = category;
+ alert->watcher = g_dbus_add_disconnect_watch(conn, alert->srv,
+ watcher_disconnect, alert, NULL);
+
+ if (alert->watcher == 0) {
+ alert_data_destroy(alert);
+ DBG("Could not register disconnect watcher");
+ return btd_error_failed(msg,
+ "Could not register disconnect watcher");
+ }
+
+ registered_alerts = g_slist_append(registered_alerts, alert);
+
+ g_slist_foreach(alert_adapters, update_supported_categories, NULL);
+
+ DBG("RegisterAlert(\"%s\", \"%s\")", alert->category, alert->path);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static void update_new_alert(gpointer data, gpointer user_data)
+{
+ struct alert_adapter *al_adapter = data;
+ struct btd_adapter *adapter = al_adapter->adapter;
+ uint8_t *value = user_data;
+
+ attrib_db_update(adapter, al_adapter->hnd_value[NOTIFY_NEW_ALERT], NULL,
+ &value[1], value[0], NULL);
+
+ notify_devices(al_adapter, NOTIFY_NEW_ALERT, &value[1], value[0]);
+}
+
+static void update_phone_alerts(const char *category, const char *description)
+{
+ unsigned int i;
+
+ if (g_str_equal(category, "ringer")) {
+ if (g_str_equal(description, "enabled")) {
+ ringer_setting = RINGER_NORMAL;
+ pasp_notification(NOTIFY_RINGER_SETTING);
+ return;
+ } else if (g_str_equal(description, "disabled")) {
+ ringer_setting = RINGER_SILENT;
+ pasp_notification(NOTIFY_RINGER_SETTING);
+ return;
+ }
+ }
+
+ for (i = 0; i < G_N_ELEMENTS(pasp_categories); i++) {
+ if (g_str_equal(pasp_categories[i], category)) {
+ if (g_str_equal(description, "active")) {
+ alert_status |= (1 << i);
+ pasp_notification(NOTIFY_ALERT_STATUS);
+ } else if (g_str_equal(description, "not active")) {
+ alert_status &= ~(1 << i);
+ pasp_notification(NOTIFY_ALERT_STATUS);
+ }
+ break;
+ }
+ }
+}
+
+static DBusMessage *new_alert(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ const char *sender = dbus_message_get_sender(msg);
+ const char *category, *description;
+ struct alert_data *alert;
+ uint16_t count;
+ unsigned int i;
+ size_t dlen;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &category,
+ DBUS_TYPE_UINT16, &count, DBUS_TYPE_STRING,
+ &description, DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ alert = get_alert_data_by_category(category);
+ if (!alert) {
+ DBG("Category %s not registered", category);
+ return btd_error_invalid_args(msg);
+ }
+
+ if (!g_str_equal(alert->srv, sender)) {
+ DBG("Sender %s is not registered in category %s", sender,
+ category);
+ return btd_error_invalid_args(msg);
+ }
+
+ if (!valid_description(category, description)) {
+ DBG("Description %s is invalid for %s category",
+ description, category);
+ return btd_error_invalid_args(msg);
+ }
+
+ if (!valid_count(category, count)) {
+ DBG("Count %d is invalid for %s category", count, category);
+ return btd_error_invalid_args(msg);
+ }
+
+ dlen = strlen(description);
+
+ for (i = 0; i < G_N_ELEMENTS(anp_categories); i++) {
+ uint8_t value[NEW_ALERT_CHR_MAX_VALUE_SIZE + 1];
+ uint8_t *ptr = value;
+
+ if (!g_str_equal(anp_categories[i], category))
+ continue;
+
+ memset(value, 0, sizeof(value));
+
+ *ptr++ = 2; /* Attribute value size */
+ *ptr++ = i; /* Category ID (mandatory) */
+ *ptr++ = count; /* Number of New Alert (mandatory) */
+ /* Text String Information (optional) */
+ strncpy((char *) ptr, description,
+ NEW_ALERT_MAX_INFO_SIZE - 1);
+
+ if (dlen > 0)
+ *value += dlen + 1;
+
+ g_slist_foreach(alert_adapters, update_new_alert, value);
+ }
+
+ if (pasp_category(category))
+ update_phone_alerts(category, description);
+
+ DBG("NewAlert(\"%s\", %d, \"%s\")", category, count, description);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static int agent_ringer_mute_once(void)
+{
+ struct alert_data *alert;
+ DBusMessage *msg;
+
+ alert = get_alert_data_by_category("ringer");
+ if (!alert) {
+ DBG("Category ringer is not registered");
+ return -EINVAL;
+ }
+
+ msg = dbus_message_new_method_call(alert->srv, alert->path,
+ ALERT_AGENT_INTERFACE, "MuteOnce");
+ if (!msg)
+ return -ENOMEM;
+
+ dbus_message_set_no_reply(msg, TRUE);
+ g_dbus_send_message(btd_get_dbus_connection(), msg);
+
+ return 0;
+}
+
+static int agent_ringer_set_ringer(const char *mode)
+{
+ struct alert_data *alert;
+ DBusMessage *msg;
+
+ alert = get_alert_data_by_category("ringer");
+ if (!alert) {
+ DBG("Category ringer is not registered");
+ return -EINVAL;
+ }
+
+ msg = dbus_message_new_method_call(alert->srv, alert->path,
+ ALERT_AGENT_INTERFACE, "SetRinger");
+ if (!msg)
+ return -ENOMEM;
+
+ dbus_message_append_args(msg, DBUS_TYPE_STRING, &mode,
+ DBUS_TYPE_INVALID);
+
+ dbus_message_set_no_reply(msg, TRUE);
+ g_dbus_send_message(btd_get_dbus_connection(), msg);
+
+ return 0;
+}
+
+static void update_unread_alert(gpointer data, gpointer user_data)
+{
+ struct alert_adapter *al_adapter = data;
+ struct btd_adapter *adapter = al_adapter->adapter;
+ uint8_t *value = user_data;
+
+ attrib_db_update(adapter,
+ al_adapter->hnd_value[NOTIFY_UNREAD_ALERT], NULL, value,
+ 2, NULL);
+
+ notify_devices(al_adapter, NOTIFY_UNREAD_ALERT, value, 2);
+}
+
+static DBusMessage *unread_alert(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ const char *sender = dbus_message_get_sender(msg);
+ struct alert_data *alert;
+ const char *category;
+ unsigned int i;
+ uint16_t count;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &category,
+ DBUS_TYPE_UINT16, &count,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ alert = get_alert_data_by_category(category);
+ if (!alert) {
+ DBG("Category %s not registered", category);
+ return btd_error_invalid_args(msg);
+ }
+
+ if (!valid_count(category, count)) {
+ DBG("Count %d is invalid for %s category", count, category);
+ return btd_error_invalid_args(msg);
+ }
+
+ if (!g_str_equal(alert->srv, sender)) {
+ DBG("Sender %s is not registered in category %s", sender,
+ category);
+ return btd_error_invalid_args(msg);
+ }
+
+ for (i = 0; i < G_N_ELEMENTS(anp_categories); i++) {
+ if (g_str_equal(anp_categories[i], category)) {
+ uint8_t value[2];
+
+ value[0] = i; /* Category ID */
+ value[1] = count; /* Unread count */
+
+ g_slist_foreach(alert_adapters, update_unread_alert,
+ value);
+ }
+ }
+
+ DBG("category %s, count %d", category, count);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static uint8_t ringer_cp_write(struct attribute *a,
+ struct btd_device *device,
+ gpointer user_data)
+{
+ DBG("a = %p", a);
+
+ if (a->len > 1) {
+ DBG("Invalid command size (%zu)", a->len);
+ return 0;
+ }
+
+ switch (a->data[0]) {
+ case RINGER_SILENT_MODE:
+ DBG("Silent Mode");
+ agent_ringer_set_ringer("disabled");
+ break;
+ case RINGER_MUTE_ONCE:
+ DBG("Mute Once");
+ agent_ringer_mute_once();
+ break;
+ case RINGER_CANCEL_SILENT_MODE:
+ DBG("Cancel Silent Mode");
+ agent_ringer_set_ringer("enabled");
+ break;
+ default:
+ DBG("Invalid command (0x%02x)", a->data[0]);
+ }
+
+ return 0;
+}
+
+static uint8_t alert_status_read(struct attribute *a,
+ struct btd_device *device,
+ gpointer user_data)
+{
+ struct btd_adapter *adapter = user_data;
+
+ DBG("a = %p", a);
+
+ if (a->data == NULL || a->data[0] != alert_status)
+ attrib_db_update(adapter, a->handle, NULL, &alert_status,
+ sizeof(alert_status), NULL);
+
+ return 0;
+}
+
+static uint8_t ringer_setting_read(struct attribute *a,
+ struct btd_device *device,
+ gpointer user_data)
+{
+ struct btd_adapter *adapter = user_data;
+
+ DBG("a = %p", a);
+
+ if (a->data == NULL || a->data[0] != ringer_setting)
+ attrib_db_update(adapter, a->handle, NULL, &ringer_setting,
+ sizeof(ringer_setting), NULL);
+
+ return 0;
+}
+
+static void register_phone_alert_service(struct alert_adapter *al_adapter)
+{
+ bt_uuid_t uuid;
+
+ bt_uuid16_create(&uuid, PHONE_ALERT_STATUS_SVC_UUID);
+
+ /* Phone Alert Status Service */
+ gatt_service_add(al_adapter->adapter, GATT_PRIM_SVC_UUID, &uuid,
+ /* Alert Status characteristic */
+ GATT_OPT_CHR_UUID16, ALERT_STATUS_CHR_UUID,
+ GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ |
+ GATT_CHR_PROP_NOTIFY,
+ GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
+ alert_status_read, al_adapter->adapter,
+ GATT_OPT_CCC_GET_HANDLE,
+ &al_adapter->hnd_ccc[NOTIFY_ALERT_STATUS],
+ GATT_OPT_CHR_VALUE_GET_HANDLE,
+ &al_adapter->hnd_value[NOTIFY_ALERT_STATUS],
+ /* Ringer Control Point characteristic */
+ GATT_OPT_CHR_UUID16, RINGER_CP_CHR_UUID,
+ GATT_OPT_CHR_PROPS, GATT_CHR_PROP_WRITE_WITHOUT_RESP,
+ GATT_OPT_CHR_VALUE_CB, ATTRIB_WRITE,
+ ringer_cp_write, NULL,
+ /* Ringer Setting characteristic */
+ GATT_OPT_CHR_UUID16, RINGER_SETTING_CHR_UUID,
+ GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ |
+ GATT_CHR_PROP_NOTIFY,
+ GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
+ ringer_setting_read, al_adapter->adapter,
+ GATT_OPT_CCC_GET_HANDLE,
+ &al_adapter->hnd_ccc[NOTIFY_RINGER_SETTING],
+ GATT_OPT_CHR_VALUE_GET_HANDLE,
+ &al_adapter->hnd_value[NOTIFY_RINGER_SETTING],
+ GATT_OPT_INVALID);
+}
+
+static uint8_t supp_new_alert_cat_read(struct attribute *a,
+ struct btd_device *device,
+ gpointer user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ uint8_t value[] = { 0x00, 0x00 };
+
+ DBG("a = %p", a);
+
+ if (a->data == NULL)
+ attrib_db_update(adapter, a->handle, NULL, value, sizeof(value),
+ NULL);
+
+ return 0;
+}
+
+static uint8_t supp_unread_alert_cat_read(struct attribute *a,
+ struct btd_device *device,
+ gpointer user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ uint8_t value[] = { 0x00, 0x00 };
+
+ DBG("a = %p", a);
+
+ if (a->data == NULL)
+ attrib_db_update(adapter, a->handle, NULL, value, sizeof(value),
+ NULL);
+
+ return 0;
+}
+
+static uint8_t alert_notif_cp_write(struct attribute *a,
+ struct btd_device *device,
+ gpointer user_data)
+{
+ DBG("a = %p", a);
+
+ if (a->len < 2)
+ return 0;
+
+ switch (a->data[0]) {
+ case ENABLE_NEW_INCOMING:
+ DBG("ENABLE_NEW_INCOMING: 0x%02x", a->data[1]);
+ break;
+ case ENABLE_UNREAD_CAT:
+ DBG("ENABLE_UNREAD_CAT: 0x%02x", a->data[1]);
+ break;
+ case DISABLE_NEW_INCOMING:
+ DBG("DISABLE_NEW_INCOMING: 0x%02x", a->data[1]);
+ break;
+ case DISABLE_UNREAD_CAT:
+ DBG("DISABLE_UNREAD_CAT: 0x%02x", a->data[1]);
+ break;
+ case NOTIFY_NEW_INCOMING:
+ DBG("NOTIFY_NEW_INCOMING: 0x%02x", a->data[1]);
+ break;
+ case NOTIFY_UNREAD_CAT:
+ DBG("NOTIFY_UNREAD_CAT: 0x%02x", a->data[1]);
+ break;
+ default:
+ DBG("0x%02x 0x%02x", a->data[0], a->data[1]);
+ }
+
+ return 0;
+}
+
+static void register_alert_notif_service(struct alert_adapter *al_adapter)
+{
+ bt_uuid_t uuid;
+
+ bt_uuid16_create(&uuid, ALERT_NOTIF_SVC_UUID);
+
+ /* Alert Notification Service */
+ gatt_service_add(al_adapter->adapter, GATT_PRIM_SVC_UUID, &uuid,
+ /* Supported New Alert Category */
+ GATT_OPT_CHR_UUID16, SUPP_NEW_ALERT_CAT_CHR_UUID,
+ GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ,
+ GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
+ supp_new_alert_cat_read, al_adapter->adapter,
+ GATT_OPT_CHR_VALUE_GET_HANDLE,
+ &al_adapter->supp_new_alert_cat_handle,
+ /* New Alert */
+ GATT_OPT_CHR_UUID16, NEW_ALERT_CHR_UUID,
+ GATT_OPT_CHR_PROPS, GATT_CHR_PROP_NOTIFY,
+ GATT_OPT_CCC_GET_HANDLE,
+ &al_adapter->hnd_ccc[NOTIFY_NEW_ALERT],
+ GATT_OPT_CHR_VALUE_GET_HANDLE,
+ &al_adapter->hnd_value[NOTIFY_NEW_ALERT],
+ /* Supported Unread Alert Category */
+ GATT_OPT_CHR_UUID16, SUPP_UNREAD_ALERT_CAT_CHR_UUID,
+ GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ,
+ GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
+ supp_unread_alert_cat_read, al_adapter->adapter,
+ GATT_OPT_CHR_VALUE_GET_HANDLE,
+ &al_adapter->supp_unread_alert_cat_handle,
+ /* Unread Alert Status */
+ GATT_OPT_CHR_UUID16, UNREAD_ALERT_CHR_UUID,
+ GATT_OPT_CHR_PROPS, GATT_CHR_PROP_NOTIFY,
+ GATT_OPT_CCC_GET_HANDLE,
+ &al_adapter->hnd_ccc[NOTIFY_UNREAD_ALERT],
+ GATT_OPT_CHR_VALUE_GET_HANDLE,
+ &al_adapter->hnd_value[NOTIFY_UNREAD_ALERT],
+ /* Alert Notification Control Point */
+ GATT_OPT_CHR_UUID16, ALERT_NOTIF_CP_CHR_UUID,
+ GATT_OPT_CHR_PROPS, GATT_CHR_PROP_WRITE,
+ GATT_OPT_CHR_VALUE_CB, ATTRIB_WRITE,
+ alert_notif_cp_write, NULL,
+ GATT_OPT_INVALID);
+}
+
+static int alert_server_probe(struct btd_profile *p,
+ struct btd_adapter *adapter)
+{
+ struct alert_adapter *al_adapter;
+
+ al_adapter = g_new0(struct alert_adapter, 1);
+ al_adapter->adapter = btd_adapter_ref(adapter);
+
+ alert_adapters = g_slist_append(alert_adapters, al_adapter);
+
+ register_phone_alert_service(al_adapter);
+ register_alert_notif_service(al_adapter);
+
+ return 0;
+}
+
+static void alert_server_remove(struct btd_profile *p,
+ struct btd_adapter *adapter)
+{
+ struct alert_adapter *al_adapter;
+
+ al_adapter = find_alert_adapter(adapter);
+ if (!al_adapter)
+ return;
+
+ alert_adapters = g_slist_remove(alert_adapters, al_adapter);
+ btd_adapter_unref(al_adapter->adapter);
+
+ g_free(al_adapter);
+}
+
+static struct btd_profile alert_profile = {
+ .name = "gatt-alert-server",
+ .adapter_probe = alert_server_probe,
+ .adapter_remove = alert_server_remove,
+};
+
+static const GDBusMethodTable alert_methods[] = {
+ { GDBUS_METHOD("RegisterAlert",
+ GDBUS_ARGS({ "category", "s" },
+ { "agent", "o" }), NULL,
+ register_alert) },
+ { GDBUS_METHOD("NewAlert",
+ GDBUS_ARGS({ "category", "s" },
+ { "count", "q" },
+ { "description", "s" }), NULL,
+ new_alert) },
+ { GDBUS_METHOD("UnreadAlert",
+ GDBUS_ARGS({ "category", "s" }, { "count", "q" }), NULL,
+ unread_alert) },
+ { }
+};
+
+static int alert_server_init(void)
+{
+ if (!g_dbus_register_interface(btd_get_dbus_connection(),
+ ALERT_OBJECT_PATH, ALERT_INTERFACE,
+ alert_methods, NULL, NULL, NULL,
+ alert_destroy)) {
+ error("D-Bus failed to register %s interface",
+ ALERT_INTERFACE);
+ return -EIO;
+ }
+
+ return btd_profile_register(&alert_profile);
+}
+
+static void alert_server_exit(void)
+{
+ btd_profile_unregister(&alert_profile);
+
+ g_dbus_unregister_interface(btd_get_dbus_connection(),
+ ALERT_OBJECT_PATH, ALERT_INTERFACE);
+}
+
+static int alert_init(void)
+{
+ return alert_server_init();
+}
+
+static void alert_exit(void)
+{
+ alert_server_exit();
+}
+
+BLUETOOTH_PLUGIN_DEFINE(alert, VERSION,
+ BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+ alert_init, alert_exit)
diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c
index db0736d6..5877219b 100755
--- a/profiles/audio/a2dp.c
+++ b/profiles/audio/a2dp.c
@@ -72,6 +72,9 @@ struct a2dp_sep {
struct avdtp *session;
struct avdtp_stream *stream;
guint suspend_timer;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ gboolean remote_suspended;
+#endif
gboolean delay_reporting;
gboolean locked;
gboolean suspending;
@@ -207,11 +210,21 @@ static void finalize_setup_errno(struct a2dp_setup *s, int err,
{
GSourceFunc finalize;
va_list args;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ struct avdtp_error *avdtp_err;
+#else
struct avdtp_error avdtp_err;
+#endif
if (err < 0) {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ avdtp_err = g_new(struct avdtp_error, 1);
+ avdtp_error_init(avdtp_err, AVDTP_ERRNO, -err);
+ s->err = avdtp_err;
+#else
avdtp_error_init(&avdtp_err, AVDTP_ERRNO, -err);
s->err = &avdtp_err;
+#endif
}
va_start(args, cb1);
@@ -420,6 +433,13 @@ static void stream_state_changed(struct avdtp_stream *stream,
return;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ 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;
@@ -844,12 +864,25 @@ static gboolean start_ind(struct avdtp *session, struct avdtp_local_sep *sep,
else
DBG("Source %p: Start_Ind", sep);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (!a2dp_sep->locked) {
+ a2dp_sep->session = avdtp_ref(session);
+ if(a2dp_sep->remote_suspended == FALSE)
+ a2dp_sep->suspend_timer = g_timeout_add_seconds(SUSPEND_TIMEOUT,
+ (GSourceFunc) suspend_timeout,
+ a2dp_sep);
+ else
+ a2dp_sep->remote_suspended = FALSE;
+ }
+#else
+
if (!a2dp_sep->locked) {
a2dp_sep->session = avdtp_ref(session);
a2dp_sep->suspend_timer = g_timeout_add_seconds(SUSPEND_TIMEOUT,
(GSourceFunc) suspend_timeout,
a2dp_sep);
}
+#endif
if (!a2dp_sep->starting)
return TRUE;
@@ -903,6 +936,10 @@ static gboolean suspend_ind(struct avdtp *session, struct avdtp_local_sep *sep,
else
DBG("Source %p: Suspend_Ind", sep);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ a2dp_sep->remote_suspended = TRUE;
+#endif
+
if (a2dp_sep->suspend_timer) {
g_source_remove(a2dp_sep->suspend_timer);
a2dp_sep->suspend_timer = 0;
@@ -1207,7 +1244,11 @@ static struct avdtp_sep_ind endpoint_ind = {
.delayreport = endpoint_delayreport_ind,
};
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static sdp_record_t *a2dp_record(uint8_t type, gboolean sink_enabled)
+#else
static sdp_record_t *a2dp_record(uint8_t type)
+#endif
{
sdp_list_t *svclass_id, *pfseq, *apseq, *root;
uuid_t root_uuid, l2cap_uuid, avdtp_uuid, a2dp_uuid;
@@ -1216,7 +1257,22 @@ static sdp_record_t *a2dp_record(uint8_t type)
sdp_record_t *record;
sdp_data_t *psm, *version, *features;
uint16_t lp = AVDTP_UUID;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ uint16_t a2dp_ver, avdtp_ver, feat;
+ if (sink_enabled) {
+ DBG("A2DP record for Sink role");
+ a2dp_ver = 0x0102;
+ avdtp_ver = 0x0103;
+ feat = 0x0002;
+ } else {
+ DBG("A2DP record for Source role");
+ a2dp_ver = 0x0102;
+ avdtp_ver = 0x0103;
+ feat = 0x0001;
+ }
+#else
uint16_t a2dp_ver = 0x0103, avdtp_ver = 0x0103, feat = 0x000f;
+#endif
record = sdp_record_alloc();
if (!record)
@@ -1400,6 +1456,13 @@ struct avdtp *a2dp_avdtp_get(struct btd_device *device)
return NULL;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (chan->auth_id) {
+ DBG("auth is already going...");
+ return NULL;
+ }
+#endif
+
if (chan->session)
return avdtp_ref(chan->session);
@@ -1524,6 +1587,18 @@ static void confirm_cb(GIOChannel *io, gpointer data)
if (!device)
goto drop;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+{
+ gboolean restricted = FALSE;
+
+ restricted = device_is_profile_restricted(device, A2DP_SINK_UUID);
+ if (restricted) {
+ DBG("A2DP is restricted");
+ goto drop;
+ }
+}
+#endif
+
chan = queue_find(server->channels, match_by_device, device);
if (chan) {
struct a2dp_setup *setup;
@@ -1564,13 +1639,34 @@ static bool a2dp_server_listen(struct a2dp_server *server)
if (server->io)
return true;
- server->io = bt_io_listen(NULL, confirm_cb, server, NULL, &err,
+#if defined(TIZEN_FEATURE_BLUEZ_MODIFY)
+ if (btd_adapter_get_a2dp_role(server->adapter) == BLUETOOTH_A2DP_SINK_ROLE) {
+ server->io = bt_io_listen(NULL, confirm_cb, server, NULL, &err,
BT_IO_OPT_SOURCE_BDADDR,
btd_adapter_get_address(server->adapter),
BT_IO_OPT_PSM, AVDTP_PSM,
BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+ BT_IO_OPT_IMTU, 895,
BT_IO_OPT_MASTER, true,
BT_IO_OPT_INVALID);
+ } else {
+ server->io = bt_io_listen(NULL, confirm_cb, server, NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR,
+ btd_adapter_get_address(server->adapter),
+ BT_IO_OPT_PSM, AVDTP_PSM,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+ BT_IO_OPT_MASTER, true,
+ BT_IO_OPT_INVALID);
+ }
+#else
+ server->io = bt_io_listen(NULL, confirm_cb, server, NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR,
+ btd_adapter_get_address(server->adapter),
+ BT_IO_OPT_PSM, AVDTP_PSM,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+ BT_IO_OPT_MASTER, true,
+ BT_IO_OPT_INVALID);
+#endif
if (server->io)
return true;
@@ -1697,7 +1793,14 @@ struct a2dp_sep *a2dp_add_sep(struct btd_adapter *adapter, uint8_t type,
if (*record_id != 0)
goto add;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (btd_adapter_get_a2dp_role(adapter) == BLUETOOTH_A2DP_SINK_ROLE)
+ record = a2dp_record(type, true);
+ else
+ record = a2dp_record(type, false);
+#else
record = a2dp_record(type);
+#endif
if (!record) {
error("Unable to allocate new service record");
a2dp_unregister_sep(sep);
@@ -1796,8 +1899,17 @@ done:
static struct a2dp_sep *a2dp_find_sep(struct avdtp *session, GSList *list,
const char *sender)
{
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ struct a2dp_sep *selected_sep = NULL;
+#endif
+
for (; list; list = list->next) {
struct a2dp_sep *sep = list->data;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ struct avdtp_remote_sep *rsep;
+ struct avdtp_media_codec_capability *cap;
+ struct avdtp_service_capability *service;
+#endif
/* Use sender's endpoint if available */
if (sender) {
@@ -1811,14 +1923,35 @@ static struct a2dp_sep *a2dp_find_sep(struct avdtp *session, GSList *list,
continue;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ rsep = avdtp_find_remote_sep(session, sep->lsep);
+ if (rsep == NULL)
+ continue;
+
+ service = avdtp_get_codec(rsep);
+ cap = (struct avdtp_media_codec_capability *) service->data;
+
+ if (cap->media_codec_type != A2DP_CODEC_VENDOR) {
+ selected_sep = sep;
+ continue;
+ }
+#else
if (avdtp_find_remote_sep(session, sep->lsep) == NULL)
continue;
+#endif
return sep;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (selected_sep)
+ return selected_sep;
+ else
+ return NULL;
+#else
return NULL;
+#endif
}
static struct a2dp_sep *a2dp_select_sep(struct avdtp *session, uint8_t type,
diff --git a/profiles/audio/avctp.c b/profiles/audio/avctp.c
index 2a43d32f..c223b019 100755
--- a/profiles/audio/avctp.c
+++ b/profiles/audio/avctp.c
@@ -321,16 +321,31 @@ static void send_key(int fd, uint16_t key, int pressed)
static gboolean auto_release(gpointer user_data)
{
struct avctp *session = user_data;
-
- session->key.timer = 0;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ uint16_t op = session->key.op;
+#endif
DBG("AV/C: key press timeout");
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (op != KEY_FASTFORWARD && op != KEY_REWIND) {
+ session->key.timer = 0;
+ send_key(session->uinput, op, 0);
+ } else {
+ return TRUE;
+ }
+#else
+ session->key.timer = 0;
send_key(session->uinput, session->key.op, 0);
+#endif
return FALSE;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+extern void avrcp_stop_position_timer(void);
+#endif
+
static void handle_press(struct avctp *session, uint16_t op)
{
if (session->key.timer > 0) {
@@ -339,8 +354,9 @@ static void handle_press(struct avctp *session, uint16_t op)
/* Only auto release if keys are different */
if (session->key.op == op)
goto done;
-
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
send_key(session->uinput, session->key.op, 0);
+#endif
}
session->key.op = op;
@@ -361,6 +377,9 @@ static void handle_release(struct avctp *session, uint16_t op)
send_key(session->uinput, op, 0);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+extern void avrcp_stop_position_timer(void);
+#endif
static size_t handle_panel_passthrough(struct avctp *session,
uint8_t transaction, uint8_t *code,
@@ -415,9 +434,13 @@ static size_t handle_panel_passthrough(struct avctp *session,
break;
}
- if (pressed)
+ if (pressed) {
handle_press(session, key_map[i].uinput);
- else
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (key_map[i].avc == AVC_REWIND)
+ avrcp_stop_position_timer();
+#endif
+ } else
handle_release(session, key_map[i].uinput);
break;
@@ -791,7 +814,11 @@ static gboolean process_queue(void *user_data)
return FALSE;
chan->p = p;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ p->timeout = g_timeout_add_seconds(5, req_timeout, chan);
+#else
p->timeout = g_timeout_add_seconds(2, req_timeout, chan);
+#endif
return FALSE;
@@ -1368,9 +1395,15 @@ static void avctp_control_confirm(struct avctp *session, GIOChannel *chan,
src = btd_adapter_get_address(device_get_adapter(dev));
dst = device_get_address(dev);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ session->auth_id = btd_request_authorization(src, dst,
+ AVRCP_TARGET_UUID,
+ auth_cb, session);
+#else
session->auth_id = btd_request_authorization(src, dst,
AVRCP_REMOTE_UUID,
auth_cb, session);
+#endif
if (session->auth_id == 0)
goto drop;
@@ -1434,6 +1467,16 @@ static void avctp_confirm_cb(GIOChannel *chan, gpointer data)
if (!device)
return;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ char name[10];
+ device_get_name(device, name, sizeof(name));
+ DBG("name : %s", name);
+ if (g_str_equal(name, "PLT_M50")) {
+ DBG("Don't accept avrcp connection with this headset");
+ return;
+ }
+#endif
+
session = avctp_get_internal(device);
if (session == NULL)
return;
@@ -1690,13 +1733,20 @@ static gboolean repeat_timeout(gpointer user_data)
struct avctp *session = user_data;
avctp_passthrough_release(session, session->key.op);
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
avctp_passthrough_press(session, session->key.op);
return TRUE;
+#else
+ return FALSE;
+#endif
}
static void release_pressed(struct avctp *session)
{
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (session->key.op != AVC_FAST_FORWARD && session->key.op != AVC_REWIND)
+#endif
avctp_passthrough_release(session, session->key.op);
if (session->key.timer > 0)
@@ -1748,6 +1798,23 @@ int avctp_send_passthrough(struct avctp *session, uint8_t op)
return avctp_passthrough_press(session, op);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+int avctp_send_release_passthrough(struct avctp *session, uint8_t op)
+{
+ DBG("+");
+
+ if (op != AVC_FAST_FORWARD && op != AVC_REWIND)
+ return FALSE;
+
+ /* Auto release if key pressed */
+ if (session->key.timer > 0)
+ g_source_remove(session->key.timer);
+ session->key.timer = 0;
+
+ DBG("-");
+ return avctp_passthrough_release(session, op);
+}
+#endif
int avctp_send_vendordep(struct avctp *session, uint8_t transaction,
uint8_t code, uint8_t subunit,
diff --git a/profiles/audio/avctp.h b/profiles/audio/avctp.h
index 68a27356..90316add 100755
--- a/profiles/audio/avctp.h
+++ b/profiles/audio/avctp.h
@@ -173,6 +173,9 @@ unsigned int avctp_register_browsing_pdu_handler(struct avctp *session,
gboolean avctp_unregister_browsing_pdu_handler(unsigned int id);
int avctp_send_passthrough(struct avctp *session, uint8_t op);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+int avctp_send_release_passthrough(struct avctp *session, uint8_t op);
+#endif
int avctp_send_vendordep(struct avctp *session, uint8_t transaction,
uint8_t code, uint8_t subunit,
uint8_t *operands, size_t operand_count);
diff --git a/profiles/audio/avdtp.c b/profiles/audio/avdtp.c
index 4ec9cca2..bc5a735b 100755
--- a/profiles/audio/avdtp.c
+++ b/profiles/audio/avdtp.c
@@ -41,6 +41,11 @@
#include "lib/sdp_lib.h"
#include "lib/uuid.h"
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#include <sys/ioctl.h>
+#include <bluetooth/hci.h>
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
+
#include "btio/btio.h"
#include "src/log.h"
#include "src/shared/util.h"
@@ -48,9 +53,14 @@
#include "src/adapter.h"
#include "src/device.h"
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#include "src/service.h"
+#endif
+
#include "avdtp.h"
#include "sink.h"
#include "source.h"
+#include "../../profile.h"
#define AVDTP_PSM 25
@@ -85,7 +95,11 @@ static unsigned int seids;
#define AVDTP_MSG_TYPE_ACCEPT 0x02
#define AVDTP_MSG_TYPE_REJECT 0x03
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define REQ_TIMEOUT 10
+#else
#define REQ_TIMEOUT 6
+#endif
#define ABORT_TIMEOUT 2
#define DISCONNECT_TIMEOUT 1
#define START_TIMEOUT 1
@@ -830,13 +844,23 @@ static void handle_transport_connect(struct avdtp *session, GIOChannel *io,
goto proceed;
DBG("sk %d, omtu %d, send buffer size %d", sk, omtu, buf_size);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ min_buf_size = omtu * 10;
+#else
min_buf_size = omtu * 2;
+#endif
if (buf_size < min_buf_size) {
DBG("send buffer size to be increassed to %d",
min_buf_size);
set_send_buffer_size(sk, min_buf_size);
}
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ else {
+ DBG("send buffer size to be decreassed to %d",
+ min_buf_size);
+ set_send_buffer_size(sk, min_buf_size);
+ }
+#endif
proceed:
if (!stream->open_acp && sep->cfm && sep->cfm->open)
sep->cfm->open(session, sep, stream, NULL, sep->user_data);
@@ -940,11 +964,182 @@ static void handle_unanswered_req(struct avdtp *session,
pending_req_free(req);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static gboolean send_broadcom_a2dp_qos(const bdaddr_t *dst, gboolean qos_high)
+{
+ int dd;
+ int err = 0;
+ struct hci_conn_info_req *cr;
+ broadcom_qos_cp cp;
+
+ dd = hci_open_dev(0);
+
+ if (dd < 0)
+ return FALSE;
+
+ cr = g_malloc0(sizeof(*cr) + sizeof(struct hci_conn_info));
+
+ cr->type = ACL_LINK;
+ bacpy(&cr->bdaddr, dst);
+
+ err = ioctl(dd, HCIGETCONNINFO, cr);
+ if (err < 0) {
+ error("Fail to get HCIGETCOINFO");
+ g_free(cr);
+ hci_close_dev(dd);
+ return FALSE;
+ }
+
+ cp.handle = cr->conn_info->handle;
+ DBG("Handle %d", cp.handle);
+ g_free(cr);
+
+ if (qos_high)
+ cp.priority = BRCM_QOS_PRIORITY_HIGH;
+ else
+ cp.priority = BRCM_QOS_PRIORITY_NORMAL;
+
+ if (hci_send_cmd(dd, OGF_VENDOR_CMD, BROADCOM_QOS_CMD,
+ BROADCOM_QOS_CP_SIZE, &cp) < 0) {
+ hci_close_dev(dd);
+ return FALSE;
+ }
+ DBG("Send Broadcom Qos Patch %s", qos_high ? "High" : "Low");
+
+ hci_close_dev(dd);
+
+ return TRUE;
+}
+
+#ifdef TIZEN_FEATURE_BLUEZ_SPRD_QOS
+static gboolean send_sprd_a2dp_qos(bdaddr_t *dst, gboolean qos_high)
+{
+ int dd;
+ int err = 0;
+ struct hci_conn_info_req *cr;
+ qos_setup_cp cp;
+
+ dd = hci_open_dev(0);
+
+ cr = g_malloc0(sizeof(*cr) + sizeof(struct hci_conn_info));
+
+ cr->type = ACL_LINK;
+ bacpy(&cr->bdaddr, dst);
+
+ err = ioctl(dd, HCIGETCONNINFO, cr);
+ if (err < 0) {
+ error("Fail to get HCIGETCOINFO");
+ g_free(cr);
+ hci_close_dev(dd);
+ return FALSE;
+ }
+
+ cp.handle = cr->conn_info->handle;
+ cp.flags = 0x00;
+ DBG("Handle %d", cp.handle);
+ g_free(cr);
+
+ if (qos_high) {
+ cp.qos.service_type = 0x02;
+ cp.qos.token_rate = 0X000000C8;
+ cp.qos.peak_bandwidth = 0X000000C8;
+ cp.qos.latency = 0x00000001;
+ cp.qos.delay_variation = 0xFFFFFFFF;
+ } else {
+ cp.qos.service_type = 0x01;
+ cp.qos.token_rate = 0X00000000;
+ cp.qos.peak_bandwidth = 0X00000000;
+ cp.qos.latency = 0x00000001;
+ cp.qos.delay_variation = 0xFFFFFFFF;
+ }
+
+ if (hci_send_cmd(dd, OGF_LINK_POLICY, OCF_QOS_SETUP,
+ QOS_SETUP_CP_SIZE, &cp) < 0) {
+ hci_close_dev(dd);
+ return FALSE;
+ }
+ DBG("Send Spreadtrum Qos Patch %s", qos_high ? "High" : "Low");
+
+ hci_close_dev(dd);
+
+ return TRUE;
+}
+#endif /* TIZEN_FEATURE_BLUEZ_SPRD_QOS */
+
+static gboolean fix_role_to_master(const bdaddr_t *dst, gboolean fix_to_master)
+{
+ int dd;
+ int err = 0;
+ struct hci_conn_info_req *cr;
+ switch_role_cp sr_cp;
+ write_link_policy_cp lp_cp;
+
+ dd = hci_open_dev(0);
+ if (dd < 0) {
+ error("hci_open_dev is failed");
+ return FALSE;
+ }
+
+ cr = g_malloc0(sizeof(*cr) + sizeof(struct hci_conn_info));
+
+ cr->type = ACL_LINK;
+ bacpy(&cr->bdaddr, dst);
+ err = ioctl(dd, HCIGETCONNINFO, cr);
+ if (err < 0) {
+ error("Fail to get HCIGETCOINFO : %d", err);
+ g_free(cr);
+ hci_close_dev(dd);
+ return FALSE;
+ }
+
+ if (!(cr->conn_info->link_mode & HCI_LM_MASTER) && fix_to_master) {
+ DBG("Need to role switch");
+
+ bacpy(&sr_cp.bdaddr, dst);
+ sr_cp.role = 0x00; /* 0x00 : Master, 0x01 : Slave */
+ if (hci_send_cmd(dd, OGF_LINK_POLICY, OCF_SWITCH_ROLE,
+ SWITCH_ROLE_CP_SIZE, &sr_cp) < 0) {
+ error("switch role is failed");
+ g_free(cr);
+ hci_close_dev(dd);
+ return FALSE;
+ }
+ }
+
+ lp_cp.handle = cr->conn_info->handle;
+ DBG("Handle %d", lp_cp.handle);
+ g_free(cr);
+
+ lp_cp.policy = fix_to_master ? 0x00 : HCI_LP_SNIFF | HCI_LP_RSWITCH;
+ DBG("Request link policy : 0x%X", lp_cp.policy);
+
+ if (hci_send_cmd(dd, OGF_LINK_POLICY, OCF_WRITE_LINK_POLICY,
+ WRITE_LINK_POLICY_CP_SIZE, &lp_cp) < 0) {
+ error("write link policy is failed : %d", lp_cp.policy);
+ hci_close_dev(dd);
+ return FALSE;
+ }
+
+ hci_close_dev(dd);
+
+ return TRUE;
+}
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
+
static void avdtp_sep_set_state(struct avdtp *session,
struct avdtp_local_sep *sep,
avdtp_state_t state)
{
struct avdtp_stream *stream = sep->stream;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ const bdaddr_t *dst;
+#if defined(TIZEN_FEATURE_BLUEZ_SPRD_QOS)
+ dst = device_get_address(session->device);
+#else
+ if (TIZEN_FEATURE_BLUEZ_BRCM_QOS || TIZEN_FEATURE_BLUEZ_ROLE_CHANGE)
+ dst = device_get_address(session->device);
+#endif
+#endif
avdtp_state_t old_state;
struct avdtp_error err, *err_ptr = NULL;
GSList *l;
@@ -975,12 +1170,38 @@ static void avdtp_sep_set_state(struct avdtp *session,
break;
case AVDTP_STATE_OPEN:
stream->starting = FALSE;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (TIZEN_FEATURE_BLUEZ_BRCM_QOS) {
+ send_broadcom_a2dp_qos(dst, FALSE);
+ } else {
+#if defined(TIZEN_FEATURE_BLUEZ_SPRD_QOS)
+ if (old_state == AVDTP_STATE_STREAMING)
+ send_sprd_a2dp_qos(dst, FALSE);
+#endif
+ }
+ if (TIZEN_FEATURE_BLUEZ_ROLE_CHANGE)
+ fix_role_to_master(dst, FALSE);
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
+
break;
case AVDTP_STATE_STREAMING:
if (stream->start_timer) {
g_source_remove(stream->start_timer);
stream->start_timer = 0;
}
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (TIZEN_FEATURE_BLUEZ_BRCM_QOS) {
+ send_broadcom_a2dp_qos(dst, TRUE);
+ } else {
+#if defined(TIZEN_FEATURE_BLUEZ_SPRD_QOS)
+ if (old_state == AVDTP_STATE_OPEN)
+ send_sprd_a2dp_qos(dst, TRUE);
+#endif
+ }
+ if (TIZEN_FEATURE_BLUEZ_ROLE_CHANGE)
+ fix_role_to_master(dst, TRUE);
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
stream->open_acp = FALSE;
break;
case AVDTP_STATE_CLOSING:
@@ -1020,7 +1241,11 @@ static void avdtp_sep_set_state(struct avdtp *session,
}
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void finalize_discovery(struct avdtp *session, int err)
+#else
static void finalize_discovery(struct avdtp *session, int err)
+#endif
{
struct discover_callback *discover = session->discover;
struct avdtp_error avdtp_err;
@@ -1104,12 +1329,20 @@ static void avdtp_free(void *data)
static void connection_lost(struct avdtp *session, int err)
{
char address[18];
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ struct btd_service *service;
+#endif
session = avdtp_ref(session);
ba2str(device_get_address(session->device), address);
DBG("Disconnected from %s", address);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ service = btd_device_get_service(session->device, A2DP_SINK_UUID);
+ if (service)
+ btd_service_connecting_complete(service, -err);
+#endif
g_slist_foreach(session->streams, (GFunc) release_stream, session);
session->streams = NULL;
@@ -1117,14 +1350,48 @@ static void connection_lost(struct avdtp *session, int err)
avdtp_set_state(session, AVDTP_SESSION_STATE_DISCONNECTED);
- avdtp_unref(session);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ DBG("%p: ref=%d", session, session->ref);
+ if (err != EIO && session->ref > 0) /* link loss*/
+ return;
+#endif
+
+ avdtp_free(session);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static gboolean disconnect_acl_timeout(gpointer user_data)
+{
+ struct btd_device *device = user_data;
+
+ DBG("");
+
+ btd_device_disconnect(device);
+
+ return FALSE;
+}
+#endif
+
static gboolean disconnect_timeout(gpointer user_data)
{
struct avdtp *session = user_data;
struct btd_service *service;
gboolean stream_setup;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ struct btd_device *device = NULL;
+ struct btd_adapter *adapter = NULL;
+ const bdaddr_t *bdaddr = NULL;
+
+ DBG("");
+#endif
+
+/* Fix : REVERSE_INULL */
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (session->device == NULL) {
+ error("session device NOT found");
+ return FALSE;
+ }
+#endif
session->dc_timer = 0;
@@ -1143,19 +1410,83 @@ static gboolean disconnect_timeout(gpointer user_data)
return FALSE;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ 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);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (device)
+ g_timeout_add(100,
+ disconnect_acl_timeout,
+ device);
+#endif
+
return FALSE;
}
+#if defined TIZEN_FEATURE_BLUEZ_MODIFY
+static void set_disconnect_timer_for_sink(struct avdtp *session, gboolean disconn)
+{
+ char name[6];
+
+ if (session->dc_timer)
+ remove_disconnect_timer(session);
+
+ device_get_name(session->device, name, sizeof(name));
+ DBG("name : [%s]", name);
+ if (g_str_equal(name, "VW BT") || g_str_equal(name, "VW MI") ||
+ g_str_equal(name, "Seat ")) {
+ session->dc_timer = g_timeout_add_seconds(3, disconnect_timeout,
+ session);
+ } else if (g_str_equal(name, "CAR M")) {
+ session->dc_timer = g_timeout_add(200, disconnect_timeout,
+ session);
+ } else {
+ if (disconn == TRUE)
+ session->dc_timer = g_timeout_add(100,
+ disconnect_timeout,
+ session);
+ else
+ session->dc_timer = g_timeout_add_seconds(DISCONNECT_TIMEOUT,
+ disconnect_timeout,
+ session);
+ }
+}
+#endif
+
static void set_disconnect_timer(struct avdtp *session)
{
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ char name[6];
+#endif
if (session->dc_timer)
remove_disconnect_timer(session);
- session->dc_timer = g_timeout_add_seconds(DISCONNECT_TIMEOUT,
- disconnect_timeout,
- session);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ device_get_name(session->device, name, sizeof(name));
+ DBG("name : [%s]", name);
+ if (g_str_equal(name, "VW BT") || g_str_equal(name, "VW MI") ||
+ g_str_equal(name, "Seat ")) {
+ session->dc_timer = g_timeout_add_seconds(3, disconnect_timeout,
+ session);
+ } else if (g_str_equal(name, "CAR M")) {
+ session->dc_timer = g_timeout_add(200, disconnect_timeout,
+ session);
+ } else {
+ session->dc_timer = g_timeout_add_seconds(DISCONNECT_TIMEOUT,
+ disconnect_timeout,
+ session);
+ }
+#endif
}
void avdtp_unref(struct avdtp *session)
@@ -1164,6 +1495,10 @@ void avdtp_unref(struct avdtp *session)
return;
session->ref--;
+#if defined TIZEN_FEATURE_BLUEZ_MODIFY
+ struct btd_adapter *adapter;
+ adapter = avdtp_get_adapter(session);
+#endif
DBG("%p: ref=%d", session, session->ref);
@@ -1172,7 +1507,14 @@ void avdtp_unref(struct avdtp *session)
switch (session->state) {
case AVDTP_SESSION_STATE_CONNECTED:
+#if defined TIZEN_FEATURE_BLUEZ_MODIFY
+ if (btd_adapter_get_a2dp_role(adapter) == BLUETOOTH_A2DP_SINK_ROLE)
+ set_disconnect_timer_for_sink(session, TRUE);
+ else
+ set_disconnect_timer(session);
+#else
set_disconnect_timer(session);
+#endif
break;
case AVDTP_SESSION_STATE_CONNECTING:
connection_lost(session, ECONNABORTED);
@@ -2163,6 +2505,21 @@ static gboolean session_cb(GIOChannel *chan, GIOCondition cond,
}
if (session->in.message_type == AVDTP_MSG_TYPE_COMMAND) {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ struct btd_service *service;
+
+ service = btd_device_get_service(session->device, A2DP_SINK_UUID);
+ if (service != NULL) {
+ DBG("A2dp state %d", btd_service_get_state(
+ btd_device_get_service(session->device, A2DP_SINK_UUID)));
+
+ if (btd_service_get_state(btd_device_get_service(session->device,
+ A2DP_SINK_UUID)) == BTD_SERVICE_STATE_DISCONNECTING) {
+ DBG("avdtp:%p , disconnect timer is going on", session);
+ return FALSE;
+ }
+ }
+#endif
if (!avdtp_parse_cmd(session, session->in.transaction,
session->in.signal_id,
session->in.buf,
@@ -2272,6 +2629,10 @@ static void avdtp_connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
struct avdtp *session = user_data;
char address[18];
int err_no = EIO;
+#if defined TIZEN_FEATURE_BLUEZ_MODIFY
+ struct btd_adapter *adapter;
+ adapter = avdtp_get_adapter(session);
+#endif
if (err) {
err_no = err->code;
@@ -2320,8 +2681,16 @@ static void avdtp_connect_cb(GIOChannel *chan, GError *err, gpointer user_data)
(GIOFunc) session_cb, session,
NULL);
- if (session->stream_setup)
+ if (session->stream_setup) {
+#if defined TIZEN_FEATURE_BLUEZ_MODIFY
+ if (btd_adapter_get_a2dp_role(adapter) == BLUETOOTH_A2DP_SINK_ROLE)
+ set_disconnect_timer_for_sink(session, FALSE);
+ else
+ set_disconnect_timer(session);
+#else
set_disconnect_timer(session);
+#endif
+ }
} else if (session->pending_open)
handle_transport_connect(session, chan, session->imtu,
session->omtu);
@@ -2385,10 +2754,26 @@ static GIOChannel *l2cap_connect(struct avdtp *session)
GError *err = NULL;
GIOChannel *io;
const bdaddr_t *src;
+#if defined TIZEN_FEATURE_BLUEZ_MODIFY
+ struct btd_adapter *adapter;
+ adapter = avdtp_get_adapter(session);
+#endif
src = btd_adapter_get_address(device_get_adapter(session->device));
- io = bt_io_connect(avdtp_connect_cb, session,
+#if defined TIZEN_FEATURE_BLUEZ_MODIFY
+ if (btd_adapter_get_a2dp_role(adapter) == BLUETOOTH_A2DP_SINK_ROLE) {
+ io = bt_io_connect(avdtp_connect_cb, session,
+ NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR, src,
+ BT_IO_OPT_DEST_BDADDR,
+ device_get_address(session->device),
+ BT_IO_OPT_PSM, AVDTP_PSM,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+ BT_IO_OPT_IMTU, 895,
+ BT_IO_OPT_INVALID);
+ } else {
+ io = bt_io_connect(avdtp_connect_cb, session,
NULL, &err,
BT_IO_OPT_SOURCE_BDADDR, src,
BT_IO_OPT_DEST_BDADDR,
@@ -2396,6 +2781,18 @@ static GIOChannel *l2cap_connect(struct avdtp *session)
BT_IO_OPT_PSM, AVDTP_PSM,
BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
BT_IO_OPT_INVALID);
+ }
+#else
+ io = bt_io_connect(avdtp_connect_cb, session,
+ NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR, src,
+ BT_IO_OPT_DEST_BDADDR,
+ device_get_address(session->device),
+ BT_IO_OPT_PSM, AVDTP_PSM,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM,
+ BT_IO_OPT_INVALID);
+#endif
+
if (!io) {
error("%s", err->message);
g_error_free(err);
@@ -2782,6 +3179,12 @@ static gboolean avdtp_abort_resp(struct avdtp *session,
struct seid_rej *resp, int size)
{
struct avdtp_local_sep *sep = stream->lsep;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (!sep) {
+ error("Error in getting sep");
+ return FALSE;
+ }
+#endif
avdtp_sep_set_state(session, sep, AVDTP_STATE_ABORTING);
@@ -3403,11 +3806,44 @@ int avdtp_start(struct avdtp *session, struct avdtp_stream *stream)
/* If timer already active wait it */
if (stream->start_timer)
return 0;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ else {
+ char address[18];
+ uint32_t timeout_sec = START_TIMEOUT;
+
+ ba2str(device_get_address(session->device), address);
+ /* For Bose headset (Bose AE2w) 2 seconds timeout is required to avoid AVDTP ABORT_CMD */
+ if (!strncasecmp(address, "00:0C:8A", 8))
+ timeout_sec = 2;
+ /* For Gear Circle, HS3000 headset, this headset doesn't initiate start command and
+ * when we add timer for 1 second so idle may trigger callback after 1.2 sec or
+ * 1.5 sec. So, don't timer for this headset.*/
+ if (!strncasecmp(address, "10:92:66", 8) ||
+ !strncasecmp(address, "A8:9F:BA", 8) ||
+ !strncasecmp(address, "00:26:B4", 8)) {
+ start_timeout(stream);
+ return 0;
+ }
+ /* Here we can't use Mac address as there are changing so check for name */
+ char name[10];
+ device_get_name(session->device, name, sizeof(name));
+ DBG("name : %s", name);
+ if (g_str_equal(name, "HS3000")) {
+ start_timeout(stream);
+ return 0;
+ }
+ stream->start_timer = g_timeout_add_seconds(timeout_sec,
+ start_timeout,
+ stream);
+ return 0;
+ }
+#else
stream->start_timer = g_timeout_add_seconds(START_TIMEOUT,
start_timeout,
stream);
return 0;
+#endif
}
if (stream->close_int == TRUE) {
diff --git a/profiles/audio/avdtp.h b/profiles/audio/avdtp.h
index 621a6e3c..54591aa2 100755
--- a/profiles/audio/avdtp.h
+++ b/profiles/audio/avdtp.h
@@ -230,6 +230,10 @@ int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb,
gboolean avdtp_has_stream(struct avdtp *session, struct avdtp_stream *stream);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void finalize_discovery(struct avdtp *session, int err);
+#endif
+
unsigned int avdtp_stream_add_cb(struct avdtp *session,
struct avdtp_stream *stream,
avdtp_stream_state_cb cb, void *data);
diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c
index 51a89b12..4d245106 100755
--- a/profiles/audio/avrcp.c
+++ b/profiles/audio/avrcp.c
@@ -137,9 +137,13 @@
#define AVRCP_CHARSET_UTF8 106
#define AVRCP_BROWSING_TIMEOUT 1
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define AVRCP_CT_VERSION 0x0103
+#define AVRCP_TG_VERSION 0x0103
+#else
#define AVRCP_CT_VERSION 0x0106
#define AVRCP_TG_VERSION 0x0105
-
+#endif
#define AVRCP_SCOPE_MEDIA_PLAYER_LIST 0x00
#define AVRCP_SCOPE_MEDIA_PLAYER_VFS 0x01
#define AVRCP_SCOPE_SEARCH 0x02
@@ -275,6 +279,9 @@ struct avrcp {
uint8_t transaction;
uint8_t transaction_events[AVRCP_EVENT_LAST + 1];
struct pending_pdu *pending_pdu;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ uint32_t playback_status_id;
+#endif
};
struct passthrough_handler {
@@ -292,6 +299,15 @@ struct control_pdu_handler {
static GSList *servers = NULL;
static unsigned int avctp_id = 0;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#ifdef TIZEN_FEATURE_BLUEZ_AVRCP_TARGET
+static uint16_t adapter_avrcp_tg_ver = 0;
+#endif
+#ifdef TIZEN_FEATURE_BLUEZ_AVRCP_CONTROL
+static uint16_t adapter_avrcp_ct_ver = 0;
+#endif
+#endif
+
/* Default feature bit mask for media player as per avctp.c:key_map */
static const uint8_t features[16] = {
0xF8, 0xBF, 0xFF, 0xBF, 0x1F,
@@ -306,6 +322,13 @@ static uint32_t company_ids[] = {
static void avrcp_register_notification(struct avrcp *session, uint8_t event);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static GList *player_list_settings(struct avrcp_player *player);
+void avrcp_stop_position_timer(void);
+unsigned int pos_timer_id = 0;
+#endif
+
+#ifdef TIZEN_FEATURE_BLUEZ_AVRCP_CONTROL
static sdp_record_t *avrcp_ct_record(void)
{
sdp_list_t *svclass_id, *pfseq, *apseq, *apseq1, *root;
@@ -315,12 +338,23 @@ static sdp_record_t *avrcp_ct_record(void)
sdp_record_t *record;
sdp_data_t *psm[2], *version, *features;
uint16_t lp = AVCTP_CONTROL_PSM, ap = AVCTP_BROWSING_PSM;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ uint16_t avctp_ver = 0x0104;
+ uint16_t feat = 0;
+#ifdef ENABLE_AVRCP_CATEGORY1
+ feat = AVRCP_FEATURE_CATEGORY_1;
+#endif
+#ifdef ENABLE_AVRCP_CATEGORY2
+ feat = feat | AVRCP_FEATURE_CATEGORY_2;
+#endif
+#else
uint16_t avctp_ver = 0x0103;
uint16_t feat = ( AVRCP_FEATURE_CATEGORY_1 |
AVRCP_FEATURE_CATEGORY_2 |
AVRCP_FEATURE_CATEGORY_3 |
AVRCP_FEATURE_CATEGORY_4 |
AVRCP_FEATURE_BROWSING);
+#endif
record = sdp_record_alloc();
if (!record)
@@ -371,6 +405,9 @@ 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;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ adapter_avrcp_ct_ver = AVRCP_CT_VERSION;
+#endif
pfseq = sdp_list_append(NULL, &profile[0]);
sdp_set_profile_descs(record, pfseq);
@@ -396,7 +433,9 @@ static sdp_record_t *avrcp_ct_record(void)
return record;
}
+#endif
+#ifdef TIZEN_FEATURE_BLUEZ_AVRCP_TARGET
static sdp_record_t *avrcp_tg_record(void)
{
sdp_list_t *svclass_id, *pfseq, *apseq, *root, *apseq_browsing;
@@ -405,9 +444,23 @@ static sdp_record_t *avrcp_tg_record(void)
sdp_list_t *aproto_control, *proto_control[2];
sdp_record_t *record;
sdp_data_t *psm_control, *version, *features, *psm_browsing;
- sdp_list_t *aproto_browsing, *proto_browsing[2] = {0};
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
+ sdp_list_t *aproto_browsing;
+#endif
+ sdp_list_t *proto_browsing[2] = {0};
uint16_t lp = AVCTP_CONTROL_PSM;
uint16_t lp_browsing = AVCTP_BROWSING_PSM;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ uint16_t avctp_ver = 0x0104;
+ uint16_t feat = 0;
+#ifdef ENABLE_AVRCP_CATEGORY1
+ feat = AVRCP_FEATURE_CATEGORY_1 |
+ AVRCP_FEATURE_PLAYER_SETTINGS;
+#endif
+#ifdef ENABLE_AVRCP_CATEGORY2
+ feat = feat | AVRCP_FEATURE_CATEGORY_2;
+#endif
+#else
uint16_t avctp_ver = 0x0103;
uint16_t feat = ( AVRCP_FEATURE_CATEGORY_1 |
AVRCP_FEATURE_CATEGORY_2 |
@@ -415,7 +468,7 @@ static sdp_record_t *avrcp_tg_record(void)
AVRCP_FEATURE_CATEGORY_4 |
AVRCP_FEATURE_BROWSING |
AVRCP_FEATURE_PLAYER_SETTINGS );
-
+#endif
record = sdp_record_alloc();
if (!record)
return NULL;
@@ -453,12 +506,17 @@ static sdp_record_t *avrcp_tg_record(void)
proto_browsing[1] = sdp_list_append(proto_browsing[1], version);
apseq_browsing = sdp_list_append(apseq_browsing, proto_browsing[1]);
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
aproto_browsing = sdp_list_append(NULL, apseq_browsing);
sdp_set_add_access_protos(record, aproto_browsing);
+#endif
/* Bluetooth Profile Descriptor List */
sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID);
profile[0].version = AVRCP_TG_VERSION;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ adapter_avrcp_tg_ver = AVRCP_TG_VERSION;
+#endif
pfseq = sdp_list_append(NULL, &profile[0]);
sdp_set_profile_descs(record, pfseq);
@@ -471,7 +529,9 @@ static sdp_record_t *avrcp_tg_record(void)
sdp_list_free(proto_browsing[0], NULL);
sdp_list_free(proto_browsing[1], NULL);
sdp_list_free(apseq_browsing, NULL);
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
sdp_list_free(aproto_browsing, NULL);
+#endif
free(psm_control);
free(version);
@@ -485,6 +545,7 @@ static sdp_record_t *avrcp_tg_record(void)
return record;
}
+#endif
static unsigned int attr_get_max_val(uint8_t attr)
{
@@ -492,9 +553,17 @@ static unsigned int attr_get_max_val(uint8_t attr)
case AVRCP_ATTRIBUTE_EQUALIZER:
return AVRCP_EQUALIZER_ON;
case AVRCP_ATTRIBUTE_REPEAT_MODE:
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ return AVRCP_REPEAT_MODE_ALL;
+#else
return AVRCP_REPEAT_MODE_GROUP;
+#endif
case AVRCP_ATTRIBUTE_SHUFFLE:
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ return AVRCP_SHUFFLE_ALL;
+#else
return AVRCP_SHUFFLE_GROUP;
+#endif
case AVRCP_ATTRIBUTE_SCAN:
return AVRCP_SCAN_GROUP;
}
@@ -673,6 +742,10 @@ void avrcp_player_event(struct avrcp_player *player, uint8_t id,
GSList *l;
int attr;
int val;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ uint32_t *position_val = NULL;
+ GList *settings;
+#endif
if (player->sessions == NULL)
return;
@@ -712,6 +785,24 @@ void avrcp_player_event(struct avrcp_player *player, uint8_t id,
break;
case AVRCP_EVENT_SETTINGS_CHANGED:
size = 2;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ settings = player_list_settings(player);
+ pdu->params[1] = g_list_length(settings);
+ for (; settings; settings = settings->next) {
+ const char *key = settings->data;
+
+ attr = attr_to_val(key);
+ if (attr < 0)
+ continue;
+
+ val = player_get_setting(player, attr);
+ if (val < 0)
+ continue;
+
+ pdu->params[size++] = attr;
+ pdu->params[size++] = val;
+ }
+#else
pdu->params[1] = 1;
attr = attr_to_val(data);
@@ -724,7 +815,19 @@ void avrcp_player_event(struct avrcp_player *player, uint8_t id,
pdu->params[size++] = attr;
pdu->params[size++] = val;
+#endif /* __TIZEN__PATCH__ */
break;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ case AVRCP_EVENT_PLAYBACK_POS_CHANGED:
+ size = 5;
+ position_val = (uint32_t *) data;
+ *position_val = (*position_val & 0x000000ff) << 24 |
+ (*position_val & 0x0000ff00) << 8 |
+ (*position_val & 0x00ff0000) >> 8 |
+ (*position_val & 0xff000000) >> 24;
+ 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));
@@ -740,6 +843,17 @@ void avrcp_player_event(struct avrcp_player *player, uint8_t id,
done:
pdu->params_len = htons(size);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ 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.
+ */
+ DBG("Removing the timer function added by register notification");
+ g_source_remove(pos_timer_id);
+ pos_timer_id = 0;
+ }
+#endif
for (l = player->sessions; l; l = l->next) {
struct avrcp *session = l->data;
@@ -920,8 +1034,10 @@ static const char *attrval_to_str(uint8_t attr, uint8_t value)
return "singletrack";
case AVRCP_REPEAT_MODE_ALL:
return "alltracks";
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
case AVRCP_REPEAT_MODE_GROUP:
return "group";
+#endif
}
break;
@@ -933,8 +1049,10 @@ static const char *attrval_to_str(uint8_t attr, uint8_t value)
return "off";
case AVRCP_SCAN_ALL:
return "alltracks";
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
case AVRCP_SCAN_GROUP:
return "group";
+#endif
}
break;
@@ -1426,6 +1544,16 @@ static GList *player_list_settings(struct avrcp_player *player)
return player->cb->list_settings(player->user_data);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static uint32_t player_get_playback_position(struct avrcp_player *player)
+{
+ if (player == NULL)
+ return UINT32_MAX;
+
+ return player->cb->get_position(player->user_data);
+}
+#endif
+
static bool avrcp_handle_play(struct avrcp *session)
{
struct avrcp_player *player = target_get_player(session);
@@ -1507,6 +1635,33 @@ static bool handle_passthrough(struct avctp *conn, uint8_t op, bool pressed,
return handler->func(session);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void avrcp_stop_position_timer(void)
+{
+ if (pos_timer_id > 0) {
+ DBG("Removing position timer id");
+ g_source_remove(pos_timer_id);
+ pos_timer_id = 0;
+ }
+}
+gboolean send_playback_position_event(gpointer user_data)
+{
+ struct avrcp_player *player = user_data;
+ uint32_t playback_position;
+ uint8_t play_status;
+
+ play_status = player_get_status(player);
+ if (play_status != AVRCP_PLAY_STATUS_PLAYING)
+ return FALSE;
+
+ playback_position = player_get_playback_position(player);
+ pos_timer_id = 0;
+ avrcp_player_event(player, AVRCP_EVENT_PLAYBACK_POS_CHANGED,
+ &playback_position);
+ return FALSE;
+}
+#endif
+
static uint8_t avrcp_handle_register_notification(struct avrcp *session,
struct avrcp_header *pdu,
uint8_t transaction)
@@ -1515,6 +1670,11 @@ static uint8_t avrcp_handle_register_notification(struct avrcp *session,
struct btd_device *dev = session->dev;
uint16_t len = ntohs(pdu->params_len);
uint64_t uid;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ uint32_t playback_interval;
+ uint32_t playback_position;
+ uint8_t play_status;
+#endif
GList *settings;
/*
@@ -1586,6 +1746,40 @@ static uint8_t avrcp_handle_register_notification(struct avrcp *session,
len = 2;
break;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ case AVRCP_EVENT_PLAYBACK_POS_CHANGED:
+ len = 5;
+
+ /* 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) |
+ ((playback_interval>>8)&0xff00) |
+ ((playback_interval<<24)&0xff000000);
+
+ play_status = player_get_status(player);
+
+ if (play_status != AVRCP_PLAY_STATUS_PLAYING) {
+ 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(
+ playback_interval,
+ send_playback_position_event, player);
+ }
+
+ /* 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;
+ memcpy(&pdu->params[1], &playback_position, sizeof(uint32_t));
+
+ break;
+#endif
+
default:
/* All other events are not supported yet */
goto err;
@@ -2918,6 +3112,26 @@ static int ct_press(struct avrcp_player *player, uint8_t op)
return 0;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static int ct_release(struct avrcp_player *player, uint8_t op)
+{
+ DBG("+");
+ int err;
+ struct avrcp *session;
+
+ session = player->sessions->data;
+ if (session == NULL)
+ return -ENOTCONN;
+
+ err = avctp_send_release_passthrough(session->conn, op);
+ if (err < 0)
+ return err;
+
+ DBG("-");
+ return 0;
+}
+#endif
+
static int ct_play(struct media_player *mp, void *user_data)
{
struct avrcp_player *player = user_data;
@@ -2953,6 +3167,43 @@ static int ct_previous(struct media_player *mp, void *user_data)
return ct_press(player, AVC_BACKWARD);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static int ct_press_fast_forward(struct media_player *mp, void *user_data)
+{
+ DBG("+");
+ struct avrcp_player *player = user_data;
+
+ DBG("-");
+ return ct_press(player, AVC_FAST_FORWARD);
+}
+
+static int ct_release_fast_forward(struct media_player *mp, void *user_data)
+{
+ DBG("+");
+ struct avrcp_player *player = user_data;
+
+ DBG("-");
+ return ct_release(player, AVC_FAST_FORWARD);
+}
+
+static int ct_press_rewind(struct media_player *mp, void *user_data)
+{
+ DBG("+");
+ struct avrcp_player *player = user_data;
+
+ DBG("-");
+ return ct_press(player, AVC_REWIND);
+}
+
+static int ct_release_rewind(struct media_player *mp, void *user_data)
+{
+ DBG("+");
+ struct avrcp_player *player = user_data;
+
+ DBG("-");
+ return ct_release(player, AVC_REWIND);
+}
+#else
static int ct_fast_forward(struct media_player *mp, void *user_data)
{
struct avrcp_player *player = user_data;
@@ -2966,6 +3217,7 @@ static int ct_rewind(struct media_player *mp, void *user_data)
return ct_press(player, AVC_REWIND);
}
+#endif
static int ct_list_items(struct media_player *mp, const char *name,
uint32_t start, uint32_t end, void *user_data)
@@ -3274,8 +3526,15 @@ static const struct media_player_callback ct_cbs = {
.stop = ct_stop,
.next = ct_next,
.previous = ct_previous,
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ .press_fast_forward = ct_press_fast_forward,
+ .release_fast_forward = ct_release_fast_forward,
+ .press_rewind = ct_press_rewind,
+ .release_rewind = ct_release_rewind,
+#else
.fast_forward = ct_fast_forward,
.rewind = ct_rewind,
+#endif
.list_items = ct_list_items,
.change_folder = ct_change_folder,
.search = ct_search,
@@ -3397,6 +3656,10 @@ static void player_destroy(gpointer data)
if (player->destroy)
player->destroy(player->user_data);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ avrcp_stop_position_timer();
+#endif
+
if (player->changed_id > 0)
g_source_remove(player->changed_id);
@@ -3724,8 +3987,42 @@ static void avrcp_register_notification(struct avrcp *session, uint8_t event)
avrcp_handle_event, session);
}
-static gboolean avrcp_get_capabilities_resp(struct avctp *conn, uint8_t code,
- uint8_t subunit, uint8_t transaction,
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static char *avrcp_event_to_string(uint8_t event)
+{
+
+ switch (event) {
+ case AVRCP_EVENT_STATUS_CHANGED:
+ return "AVRCP EVENT STATUS CHANGED";
+ case AVRCP_EVENT_TRACK_CHANGED:
+ return "AVRCP EVENT TRACK CHANGED";
+ case AVRCP_EVENT_SETTINGS_CHANGED:
+ return "AVRCP EVENT SETTINGS CHANGED";
+ case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED:
+ return "AVRCP EVENT ADDRESSED PLAYER CHANGED";
+ case AVRCP_EVENT_UIDS_CHANGED:
+ return "AVRCP EVENT UIDS CHANGED";
+ case AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED:
+ return "AVRCP EVENT AVAILABLE PLAYERS CHANGED";
+ case AVRCP_EVENT_VOLUME_CHANGED:
+ return "AVRCP EVENT VOLUME CHANGED";
+ default:
+ return "Unknown Event";
+ }
+}
+
+static gboolean avrcp_get_playback_status(gpointer user_data)
+{
+ struct avrcp *session = user_data;
+
+ avrcp_get_play_status(session);
+
+ return TRUE;
+}
+#endif
+
+static gboolean avrcp_get_capabilities_resp(struct avctp *conn,
+ uint8_t code, uint8_t subunit,
uint8_t *operands, size_t operand_count,
void *user_data)
{
@@ -3751,12 +4048,15 @@ static gboolean avrcp_get_capabilities_resp(struct avctp *conn, uint8_t code,
uint8_t event = pdu->params[1 + count];
events |= (1 << event);
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ DBG("Supported Event %s", avrcp_event_to_string(event));
+#endif
switch (event) {
case AVRCP_EVENT_STATUS_CHANGED:
case AVRCP_EVENT_TRACK_CHANGED:
case AVRCP_EVENT_PLAYBACK_POS_CHANGED:
case AVRCP_EVENT_SETTINGS_CHANGED:
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED:
case AVRCP_EVENT_UIDS_CHANGED:
case AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED:
@@ -3765,6 +4065,7 @@ static gboolean avrcp_get_capabilities_resp(struct avctp *conn, uint8_t code,
!session->controller->player)
break;
case AVRCP_EVENT_VOLUME_CHANGED:
+#endif
avrcp_register_notification(session, event);
break;
}
@@ -3781,7 +4082,12 @@ static gboolean avrcp_get_capabilities_resp(struct avctp *conn, uint8_t code,
if (!(events & (1 << AVRCP_EVENT_STATUS_CHANGED)))
avrcp_get_element_attributes(session);
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if ((events & (1 << AVRCP_EVENT_STATUS_CHANGED)) == 0) {
+ session->playback_status_id = g_timeout_add_seconds(1,
+ avrcp_get_playback_status, session);
+ }
+#endif
return FALSE;
}
@@ -3893,6 +4199,7 @@ static void avrcp_connect_browsing(struct avrcp *session)
session);
}
+#ifdef TIZEN_FEATURE_BLUEZ_AVRCP_TARGET
static void target_init(struct avrcp *session)
{
struct avrcp_server *server = session->server;
@@ -3919,13 +4226,23 @@ static void target_init(struct avrcp *session)
session->supported_events |= (1 << AVRCP_EVENT_STATUS_CHANGED) |
(1 << AVRCP_EVENT_TRACK_CHANGED) |
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
(1 << AVRCP_EVENT_TRACK_REACHED_START) |
(1 << AVRCP_EVENT_TRACK_REACHED_END) |
+#endif
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ (1 << AVRCP_EVENT_PLAYBACK_POS_CHANGED) |
+#endif
(1 << AVRCP_EVENT_SETTINGS_CHANGED);
if (target->version < 0x0104)
return;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (adapter_avrcp_tg_ver < 0x0104)
+ return;
+#endif
+
session->supported_events |=
(1 << AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED) |
(1 << AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED) |
@@ -3940,7 +4257,9 @@ static void target_init(struct avrcp *session)
avrcp_connect_browsing(session);
}
+#endif
+#ifdef TIZEN_FEATURE_BLUEZ_AVRCP_CONTROL
static void controller_init(struct avrcp *session)
{
struct avrcp_player *player;
@@ -3955,6 +4274,11 @@ static void controller_init(struct avrcp *session)
DBG("%p version 0x%04x", controller, controller->version);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if ((controller->version >= 0x0104) && (adapter_avrcp_ct_ver >= 0x0104))
+ session->supported_events |= (1 << AVRCP_EVENT_VOLUME_CHANGED);
+#endif
+
service = btd_device_get_service(session->dev, AVRCP_TARGET_UUID);
btd_service_connecting_complete(service, 0);
@@ -3973,11 +4297,17 @@ static void controller_init(struct avrcp *session)
if (controller->version < 0x0104)
return;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (adapter_avrcp_ct_ver < 0x0104)
+ return;
+#endif
+
if (!(controller->features & AVRCP_FEATURE_BROWSING))
return;
avrcp_connect_browsing(session);
}
+#endif
static void session_init_control(struct avrcp *session)
{
@@ -3991,12 +4321,14 @@ static void session_init_control(struct avrcp *session)
handle_vendordep_pdu,
session);
session->control_handlers = control_handlers;
-
+#ifdef TIZEN_FEATURE_BLUEZ_AVRCP_CONTROL
if (btd_device_get_service(session->dev, AVRCP_TARGET_UUID) != NULL)
controller_init(session);
-
+#endif
+#ifdef TIZEN_FEATURE_BLUEZ_AVRCP_TARGET
if (btd_device_get_service(session->dev, AVRCP_REMOTE_UUID) != NULL)
target_init(session);
+#endif
}
static void controller_destroy(struct avrcp *session)
@@ -4030,6 +4362,14 @@ static void session_destroy(struct avrcp *session, int err)
server->sessions = g_slist_remove(server->sessions, session);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (session->playback_status_id > 0) {
+ DBG("Removing the timer for playback status polling");
+ g_source_remove(session->playback_status_id);
+ session->playback_status_id = 0;
+ }
+#endif
+
session_abort_pending_pdu(session);
service = btd_device_get_service(session->dev, AVRCP_TARGET_UUID);
@@ -4350,9 +4690,19 @@ static int avrcp_connect(struct btd_service *service)
{
struct btd_device *dev = btd_service_get_device(service);
const char *path = device_get_path(dev);
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ char name[10];
+#endif
DBG("path %s", path);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ device_get_name(dev, name, sizeof(name));
+ DBG("name : %s", name);
+ if (g_str_equal(name, "PLT_M50")) {
+ DBG("Don't initiate avrcp connection with this headset");
+ return -ENOTSUP;
+ }
+#endif
return control_connect(service);
}
@@ -4380,6 +4730,7 @@ static void avrcp_target_remove(struct btd_service *service)
control_unregister(service);
}
+#ifdef TIZEN_FEATURE_BLUEZ_AVRCP_TARGET
static void avrcp_target_server_remove(struct btd_profile *p,
struct btd_adapter *adapter)
{
@@ -4399,7 +4750,9 @@ static void avrcp_target_server_remove(struct btd_profile *p,
if (server->ct_record_id == 0)
avrcp_server_unregister(server);
}
+#endif
+#ifdef TIZEN_FEATURE_BLUEZ_AVRCP_TARGET
static int avrcp_target_server_probe(struct btd_profile *p,
struct btd_adapter *adapter)
{
@@ -4434,6 +4787,7 @@ done:
return 0;
}
+#endif
static struct btd_profile avrcp_target_profile = {
.name = "audio-avrcp-target",
@@ -4444,9 +4798,10 @@ static struct btd_profile avrcp_target_profile = {
.connect = avrcp_connect,
.disconnect = avrcp_disconnect,
-
+#ifdef TIZEN_FEATURE_BLUEZ_AVRCP_TARGET
.adapter_probe = avrcp_target_server_probe,
.adapter_remove = avrcp_target_server_remove,
+#endif
};
static int avrcp_controller_probe(struct btd_service *service)
@@ -4463,6 +4818,7 @@ static void avrcp_controller_remove(struct btd_service *service)
control_unregister(service);
}
+#ifdef TIZEN_FEATURE_BLUEZ_AVRCP_CONTROL
static void avrcp_controller_server_remove(struct btd_profile *p,
struct btd_adapter *adapter)
{
@@ -4482,7 +4838,9 @@ static void avrcp_controller_server_remove(struct btd_profile *p,
if (server->tg_record_id == 0)
avrcp_server_unregister(server);
}
+#endif
+#ifdef TIZEN_FEATURE_BLUEZ_AVRCP_CONTROL
static int avrcp_controller_server_probe(struct btd_profile *p,
struct btd_adapter *adapter)
{
@@ -4517,6 +4875,7 @@ done:
return 0;
}
+#endif
static struct btd_profile avrcp_controller_profile = {
.name = "avrcp-controller",
@@ -4527,9 +4886,10 @@ static struct btd_profile avrcp_controller_profile = {
.connect = avrcp_connect,
.disconnect = avrcp_disconnect,
-
+#ifdef TIZEN_FEATURE_BLUEZ_AVRCP_CONTROL
.adapter_probe = avrcp_controller_server_probe,
.adapter_remove = avrcp_controller_server_remove,
+#endif
};
static int avrcp_init(void)
diff --git a/profiles/audio/media.c b/profiles/audio/media.c
index 23d15611..e7a16d70 100755
--- a/profiles/audio/media.c
+++ b/profiles/audio/media.c
@@ -44,6 +44,10 @@
#include "src/dbus-common.h"
#include "src/profile.h"
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#include "src/service.h"
+#endif
+
#include "src/uuid-helper.h"
#include "src/log.h"
#include "src/error.h"
@@ -54,13 +58,29 @@
#include "transport.h"
#include "a2dp.h"
#include "avrcp.h"
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#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"
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define A2DP_SINK_ROLE "sink"
+#define A2DP_SOURCE_ROLE "source"
+#endif
+
#define REQUEST_TIMEOUT (3 * 1000) /* 3 seconds */
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#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 {
struct btd_adapter *btd_adapter;
GSList *endpoints; /* Endpoints list */
@@ -102,6 +122,9 @@ struct media_player {
guint watch;
guint properties_watch;
guint seek_watch;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ guint sink_watch;
+#endif
char *status;
uint32_t position;
uint32_t duration;
@@ -117,6 +140,26 @@ struct media_player {
static GSList *adapters = NULL;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static gboolean set_avrcp_status = FALSE;
+static gboolean send_track_changed_event = FALSE;
+
+gboolean current_delay_reporting = false;
+struct media_endpoint *source_endpoint = NULL;
+struct media_endpoint *sink_endpoint = NULL;
+
+static int media_set_sink_callback(struct btd_device *device,
+ struct media_player *mp);
+static void media_sink_state_changed_cb(struct btd_service *service,
+ sink_state_t old_state,
+ sink_state_t new_state,
+ void *user_data);
+void media_stop_suspend_timer(void);
+struct media_player *media_adapter_get_player(struct media_adapter *adapter);
+static struct media_adapter *find_adapter(struct btd_device *device);
+static uint32_t get_position(void *user_data);
+#endif
+
static void endpoint_request_free(struct endpoint_request *request)
{
if (request->call)
@@ -199,7 +242,9 @@ static void media_endpoint_remove(struct media_endpoint *endpoint)
if (endpoint->sep) {
a2dp_remove_sep(endpoint->sep);
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
return;
+#endif
}
info("Endpoint unregistered: sender=%s path=%s", endpoint->sender,
@@ -228,6 +273,9 @@ static void clear_configuration(struct media_endpoint *endpoint,
{
DBusMessage *msg;
const char *path;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ struct media_player *mp;
+#endif
msg = dbus_message_new_method_call(endpoint->sender, endpoint->path,
MEDIA_ENDPOINT_INTERFACE,
@@ -243,6 +291,14 @@ static void clear_configuration(struct media_endpoint *endpoint,
g_dbus_send_message(btd_get_dbus_connection(), msg);
done:
endpoint->transports = g_slist_remove(endpoint->transports, transport);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if ((mp = media_adapter_get_player(endpoint->adapter)))
+ if (mp->sink_watch) {
+ sink_remove_state_cb(mp->sink_watch);
+ mp->sink_watch = 0;
+ }
+ media_stop_suspend_timer();
+#endif
media_transport_destroy(transport);
}
@@ -405,6 +461,141 @@ static struct media_transport *find_device_transport(
return match->data;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+struct media_player * media_adapter_get_player(struct media_adapter * adapter)
+{
+ GSList *l;
+ DBG(" ");
+
+ for (l = adapter->players; l; l = l->next) {
+ struct media_player *mp = l->data;
+ if (mp != NULL)
+ return mp;
+ }
+ return NULL;
+}
+
+void media_stop_suspend_timer(void)
+{
+ if (suspend_timer_id > 0) {
+ DBG("Removing sink suspend timer");
+ g_source_remove(suspend_timer_id);
+ suspend_timer_id = 0;
+ }
+}
+
+gboolean media_reset_mp_status(gpointer user_data)
+{
+ struct media_player *mp = user_data;
+ DBG(" ");
+
+ /* PlayBackStatus already reset; so return */
+ if (g_strcmp0(mp->status, "playing") != 0)
+ return FALSE;
+
+ mp->position = get_position(mp);
+ g_timer_start(mp->timer);
+
+ g_free(mp->status);
+ mp->status = g_strdup("paused");
+ suspend_timer_id = 0;
+ avrcp_player_event(mp->player, AVRCP_EVENT_STATUS_CHANGED, mp->status);
+
+ return FALSE;
+}
+
+static void media_sink_state_changed_cb(struct btd_service *service,
+ sink_state_t old_state,
+ sink_state_t new_state,
+ void *user_data)
+{
+ struct media_player *mp = user_data;
+ DBG(" ");
+
+ /* Check if A2DP streaming is suspended */
+ if ((old_state == SINK_STATE_PLAYING) &&
+ (new_state == SINK_STATE_CONNECTED)) {
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ struct btd_device *device = btd_service_get_device(service);
+ char name[20] = {0,};
+#endif
+
+ /* Check AVRCP play back status */
+ if (g_strcmp0(mp->status, "playing") != 0)
+ return;
+
+ media_stop_suspend_timer();
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ device_get_name(device, name, sizeof(name));
+ DBG("Name : %s", name);
+
+ if (g_str_has_prefix(name, "LG HBS") != TRUE) {
+#endif
+ /* PlayBackStatus is still PLAYING; start a timer */
+ suspend_timer_id = g_timeout_add_seconds(SINK_SUSPEND_TIMEOUT,
+ media_reset_mp_status, mp);
+ DBG("SINK SUSPEND TIMEOUT started");
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ }
+#endif
+ }
+
+ /* Check if A2DP streaming is started */
+ if ((old_state == SINK_STATE_CONNECTED) &&
+ (new_state == SINK_STATE_PLAYING)) {
+
+ struct btd_device *device = btd_service_get_device(service);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ char name[20] = {0,};
+#else
+ char name[20];
+#endif
+
+ media_stop_suspend_timer();
+
+ /* NULL packet streaming during initial connection */
+ if (set_avrcp_status == FALSE) {
+ set_avrcp_status = TRUE;
+ return;
+ }
+
+ /* Check for BMW, Audi, VW car kit */
+ device_get_name(device, name, sizeof(name));
+ DBG("Name : %s", name);
+ if ((g_str_has_prefix(name, "BMW") == TRUE) ||
+ (g_str_has_prefix(name, "Audi") == TRUE) ||
+ (g_str_has_prefix(name, "VW BT") == TRUE)) {
+
+ /* Check AVRCP play back status */
+ if (g_strcmp0(mp->status, "playing") == 0)
+ return;
+
+ g_free(mp->status);
+ mp->status = g_strdup("playing");
+ avrcp_player_event(mp->player,
+ AVRCP_EVENT_STATUS_CHANGED, mp->status);
+ }
+ }
+}
+
+static int media_set_sink_callback(struct btd_device *device,
+ struct media_player *mp)
+{
+ struct btd_service *service;
+ DBG(" ");
+
+ service = btd_device_get_service(device, A2DP_SINK_UUID);
+ if (service == NULL)
+ return -EINVAL;
+
+ mp->sink_watch = sink_add_state_cb(service, media_sink_state_changed_cb, mp);
+
+ return 0;
+}
+#endif
+
struct a2dp_config_data {
struct a2dp_setup *setup;
a2dp_endpoint_config_t cb;
@@ -423,6 +614,10 @@ static gboolean set_configuration(struct media_endpoint *endpoint,
const char *path;
DBusMessageIter iter;
struct media_transport *transport;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ struct media_adapter *adapter;
+ struct media_player *mp;
+#endif
transport = find_device_transport(endpoint, device);
@@ -443,6 +638,13 @@ static gboolean set_configuration(struct media_endpoint *endpoint,
return FALSE;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ set_avrcp_status = FALSE;
+ adapter = find_adapter(device);
+ if ((mp = media_adapter_get_player(adapter)))
+ media_set_sink_callback(device, mp);
+#endif
+
endpoint->transports = g_slist_append(endpoint->transports, transport);
dbus_message_iter_init_append(msg, &iter);
@@ -600,7 +802,11 @@ static gboolean endpoint_init_a2dp_source(struct media_endpoint *endpoint,
endpoint->sep = a2dp_add_sep(endpoint->adapter->btd_adapter,
AVDTP_SEP_TYPE_SOURCE, endpoint->codec,
delay_reporting, &a2dp_endpoint,
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ endpoint, NULL, err);
+#else
endpoint, a2dp_destroy_endpoint, err);
+#endif
if (endpoint->sep == NULL)
return FALSE;
@@ -614,7 +820,11 @@ static gboolean endpoint_init_a2dp_sink(struct media_endpoint *endpoint,
endpoint->sep = a2dp_add_sep(endpoint->adapter->btd_adapter,
AVDTP_SEP_TYPE_SINK, endpoint->codec,
delay_reporting, &a2dp_endpoint,
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ endpoint, NULL, err);
+#else
endpoint, a2dp_destroy_endpoint, err);
+#endif
if (endpoint->sep == NULL)
return FALSE;
@@ -744,13 +954,31 @@ static struct media_endpoint *media_endpoint_create(struct media_adapter *adapte
endpoint->adapter = adapter;
- if (strcasecmp(uuid, A2DP_SOURCE_UUID) == 0)
+ if (strcasecmp(uuid, A2DP_SOURCE_UUID) == 0) {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ source_endpoint = endpoint;
+ if (btd_adapter_get_a2dp_role(adapter->btd_adapter) == BLUETOOTH_A2DP_SINK_ROLE)
+ return endpoint;
+ else
+ succeeded = endpoint_init_a2dp_source(endpoint,
+ delay_reporting, err);
+#else
succeeded = endpoint_init_a2dp_source(endpoint,
delay_reporting, err);
- else if (strcasecmp(uuid, A2DP_SINK_UUID) == 0)
+#endif
+ } else if (strcasecmp(uuid, A2DP_SINK_UUID) == 0) {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ sink_endpoint = endpoint;
+ if (btd_adapter_get_a2dp_role(adapter->btd_adapter) == BLUETOOTH_A2DP_SOURCE_ROLE)
+ return endpoint;
+ else
+ succeeded = endpoint_init_a2dp_sink(endpoint,
+ delay_reporting, err);
+#else
succeeded = endpoint_init_a2dp_sink(endpoint,
delay_reporting, err);
- else if (strcasecmp(uuid, HFP_AG_UUID) == 0 ||
+#endif
+ } else if (strcasecmp(uuid, HFP_AG_UUID) == 0 ||
strcasecmp(uuid, HSP_AG_UUID) == 0)
succeeded = TRUE;
else if (strcasecmp(uuid, HFP_HS_UUID) == 0 ||
@@ -787,6 +1015,36 @@ static struct media_endpoint *media_endpoint_create(struct media_adapter *adapte
return endpoint;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static int parse_a2dp_uuid(DBusMessageIter *props, const char **uuid)
+{
+ gboolean has_uuid = FALSE;
+
+ while (dbus_message_iter_get_arg_type(props) == DBUS_TYPE_DICT_ENTRY) {
+ const char *key;
+ DBusMessageIter value, entry;
+ int var;
+
+ dbus_message_iter_recurse(props, &entry);
+ dbus_message_iter_get_basic(&entry, &key);
+
+ dbus_message_iter_next(&entry);
+ dbus_message_iter_recurse(&entry, &value);
+
+ var = dbus_message_iter_get_arg_type(&value);
+ if (strcasecmp(key, "UUID") == 0) {
+ if (var != DBUS_TYPE_STRING)
+ return -EINVAL;
+ dbus_message_iter_get_basic(&value, uuid);
+ has_uuid = TRUE;
+ }
+ dbus_message_iter_next(props);
+ }
+
+ return has_uuid ? 0 : -EINVAL;
+}
+#endif
+
static int parse_properties(DBusMessageIter *props, const char **uuid,
gboolean *delay_reporting, uint8_t *codec,
uint8_t **capabilities, int *size)
@@ -837,6 +1095,42 @@ static int parse_properties(DBusMessageIter *props, const char **uuid,
return (has_uuid && has_codec) ? 0 : -EINVAL;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static DBusMessage *a2dp_select_role(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct media_adapter *adapter = data;
+ DBusMessageIter args, props;
+ const char *a2dp_role;
+ gboolean ret;
+ int err;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &a2dp_role,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ if (!g_strcmp0(a2dp_role, A2DP_SINK_ROLE)) {
+ btd_adapter_set_a2dp_role(adapter->btd_adapter, BLUETOOTH_A2DP_SINK_ROLE);
+ a2dp_remove_sep(source_endpoint->sep);
+ ret = endpoint_init_a2dp_sink(sink_endpoint, current_delay_reporting, NULL);
+ if (!ret)
+ DBG("could not init a2dp sink");
+ } else if (!g_strcmp0(a2dp_role, A2DP_SOURCE_ROLE)) {
+ btd_adapter_set_a2dp_role(adapter->btd_adapter, BLUETOOTH_A2DP_SOURCE_ROLE);
+ a2dp_remove_sep(sink_endpoint->sep);
+ ret = endpoint_init_a2dp_source(source_endpoint, current_delay_reporting, NULL);
+ if (!ret)
+ DBG("could not init a2dp source");
+ } else {
+ DBG("invalid a2dp role");
+ return btd_error_invalid_args(msg);
+ }
+
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+#endif
+
static DBusMessage *register_endpoint(DBusConnection *conn, DBusMessage *msg,
void *data)
{
@@ -848,7 +1142,15 @@ static DBusMessage *register_endpoint(DBusConnection *conn, DBusMessage *msg,
uint8_t *capabilities;
int size = 0;
int err;
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (btd_adapter_get_a2dp_role(adapter->btd_adapter) == BLUETOOTH_A2DP_SINK_ROLE) {
+ a2dp_sink_support = true;
+ a2dp_source_support = false;
+ } else {
+ a2dp_sink_support = false;
+ a2dp_source_support = true;
+ }
+#endif
sender = dbus_message_get_sender(msg);
dbus_message_iter_init(msg, &args);
@@ -866,7 +1168,9 @@ 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_FEATURE_BLUEZ_MODIFY
+ current_delay_reporting = delay_reporting;
+#endif
if (media_endpoint_create(adapter, sender, path, uuid, delay_reporting,
codec, capabilities, size, &err) == NULL) {
if (err == -EPROTONOSUPPORT)
@@ -961,6 +1265,13 @@ static void media_player_free(gpointer data)
if (mp->settings)
g_hash_table_unref(mp->settings);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ media_stop_suspend_timer();
+
+ if (mp->sink_watch)
+ sink_remove_state_cb(mp->sink_watch);
+#endif
+
g_timer_destroy(mp->timer);
g_free(mp->sender);
g_free(mp->path);
@@ -1126,6 +1437,11 @@ static uint64_t get_uid(void *user_data)
if (mp->track == NULL)
return UINT64_MAX;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (!g_hash_table_lookup(mp->track, "Title"))
+ return UINT64_MAX;
+#endif
+
return 0;
}
@@ -1304,6 +1620,9 @@ static void media_player_exit(DBusConnection *connection, void *user_data)
static gboolean set_status(struct media_player *mp, DBusMessageIter *iter)
{
const char *value;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ uint32_t playback_position;
+#endif
if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
return FALSE;
@@ -1321,26 +1640,49 @@ static gboolean set_status(struct media_player *mp, DBusMessageIter *iter)
mp->status = g_strdup(value);
avrcp_player_event(mp->player, AVRCP_EVENT_STATUS_CHANGED, mp->status);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (strcasecmp(mp->status, "reverse-seek") != 0 &&
+ strcasecmp(mp->status, "playing") != 0) {
+ playback_position = get_position(mp);
+ avrcp_player_event(mp->player, AVRCP_EVENT_PLAYBACK_POS_CHANGED,
+ &playback_position);
+ }
+#endif
return TRUE;
}
static gboolean set_position(struct media_player *mp, DBusMessageIter *iter)
{
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ uint32_t value;
+#else
uint64_t value;
const char *status;
+#endif
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ uint32_t playback_position;
+#endif
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_INT64)
- return FALSE;
-
+ return FALSE;
+#else
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT32)
+ return FALSE;
+#endif
dbus_message_iter_get_basic(iter, &value);
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
value /= 1000;
-
+#endif
+ DBG("Value %d", value);
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
if (value > get_position(mp))
status = "forward-seek";
else
status = "reverse-seek";
+#endif
mp->position = value;
g_timer_start(mp->timer);
@@ -1350,6 +1692,12 @@ static gboolean set_position(struct media_player *mp, DBusMessageIter *iter)
if (!mp->position) {
avrcp_player_event(mp->player,
AVRCP_EVENT_TRACK_REACHED_START, NULL);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ playback_position = get_position(mp);
+ avrcp_player_event(mp->player, AVRCP_EVENT_PLAYBACK_POS_CHANGED,
+ &playback_position);
+#endif
+
return TRUE;
}
@@ -1360,11 +1708,23 @@ static gboolean set_position(struct media_player *mp, DBusMessageIter *iter)
if (mp->position == UINT32_MAX || mp->position >= mp->duration) {
avrcp_player_event(mp->player, AVRCP_EVENT_TRACK_REACHED_END,
NULL);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ playback_position = get_position(mp);
+ avrcp_player_event(mp->player, AVRCP_EVENT_PLAYBACK_POS_CHANGED,
+ &playback_position);
+#endif
return TRUE;
}
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
/* Send a status change to force resync the position */
avrcp_player_event(mp->player, AVRCP_EVENT_STATUS_CHANGED, status);
+#endif
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ playback_position = get_position(mp);
+ avrcp_player_event(mp->player, AVRCP_EVENT_PLAYBACK_POS_CHANGED,
+ &playback_position);
+#endif
return TRUE;
}
@@ -1372,6 +1732,15 @@ static gboolean set_position(struct media_player *mp, DBusMessageIter *iter)
static void set_metadata(struct media_player *mp, const char *key,
const char *value)
{
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ const char *current_value = NULL;
+
+ current_value = g_hash_table_lookup(mp->track, key);
+
+ if ((g_strcmp0(value, current_value) != 0) &&
+ (send_track_changed_event == FALSE))
+ send_track_changed_event = TRUE;
+#endif
DBG("%s=%s", key, value);
g_hash_table_replace(mp->track, g_strdup(key), g_strdup(value));
}
@@ -1431,7 +1800,9 @@ static gboolean parse_int64_metadata(struct media_player *mp, const char *key,
dbus_message_iter_get_basic(iter, &value);
if (strcasecmp(key, "Duration") == 0) {
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
value /= 1000;
+#endif
mp->duration = value;
}
@@ -1472,6 +1843,9 @@ static gboolean parse_player_metadata(struct media_player *mp,
int ctype;
gboolean title = FALSE;
uint64_t uid;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ uint32_t playback_position;
+#endif
ctype = dbus_message_iter_get_arg_type(iter);
if (ctype != DBUS_TYPE_ARRAY)
@@ -1479,11 +1853,13 @@ static gboolean parse_player_metadata(struct media_player *mp,
dbus_message_iter_recurse(iter, &dict);
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
if (mp->track != NULL)
g_hash_table_unref(mp->track);
mp->track = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
g_free);
+#endif
while ((ctype = dbus_message_iter_get_arg_type(&dict)) !=
DBUS_TYPE_INVALID) {
@@ -1521,6 +1897,11 @@ static gboolean parse_player_metadata(struct media_player *mp,
} else if (strcasecmp(key, "mpris:length") == 0) {
if (!parse_int64_metadata(mp, "Duration", &var))
return FALSE;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ } else if (strcasecmp(key, "xesam:totalTracks") == 0) {
+ if (!parse_int32_metadata(mp, "NumberOfTracks", &var))
+ return FALSE;
+#endif
} else if (strcasecmp(key, "xesam:trackNumber") == 0) {
if (!parse_int32_metadata(mp, "TrackNumber", &var))
return FALSE;
@@ -1534,13 +1915,25 @@ static gboolean parse_player_metadata(struct media_player *mp,
g_hash_table_insert(mp->track, g_strdup("Title"),
g_strdup(""));
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (send_track_changed_event) {
+ uid = get_uid(mp);
+ avrcp_player_event(mp->player,
+ AVRCP_EVENT_TRACK_CHANGED, &uid);
+ send_track_changed_event = FALSE;
+
+ playback_position = get_position(mp);
+ avrcp_player_event(mp->player,
+ AVRCP_EVENT_PLAYBACK_POS_CHANGED, &playback_position);
+ }
+#else
mp->position = 0;
g_timer_start(mp->timer);
uid = get_uid(mp);
avrcp_player_event(mp->player, AVRCP_EVENT_TRACK_CHANGED, &uid);
avrcp_player_event(mp->player, AVRCP_EVENT_TRACK_REACHED_START, NULL);
-
+#endif
return TRUE;
}
@@ -1791,6 +2184,10 @@ static struct media_player *media_player_create(struct media_adapter *adapter,
mp->settings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
g_free);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ mp->track = g_hash_table_new_full(g_str_hash, g_str_equal, g_free,
+ g_free);
+#endif
adapter->players = g_slist_append(adapter->players, mp);
@@ -1871,6 +2268,10 @@ static const GDBusMethodTable media_methods[] = {
NULL, register_player) },
{ GDBUS_METHOD("UnregisterPlayer",
GDBUS_ARGS({ "player", "o" }), NULL, unregister_player) },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ { GDBUS_METHOD("SelectRole",
+ GDBUS_ARGS({ "role", "s" }), NULL, a2dp_select_role) },
+#endif
{ },
};
diff --git a/profiles/audio/player.c b/profiles/audio/player.c
index 7944b493..f39b9ae9 100755
--- a/profiles/audio/player.c
+++ b/profiles/audio/player.c
@@ -534,7 +534,83 @@ static DBusMessage *media_player_previous(DBusConnection *conn,
return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static DBusMessage *media_player_press_fast_forward(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ DBG("+");
+ struct media_player *mp = data;
+ struct player_callback *cb = mp->cb;
+ int err;
+
+ if (cb->cbs->press_fast_forward == NULL)
+ return btd_error_not_supported(msg);
+
+ err = cb->cbs->press_fast_forward(mp, cb->user_data);
+ if (err < 0)
+ return btd_error_failed(msg, strerror(-err));
+
+ DBG("-");
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *media_player_release_fast_forward(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ DBG("+");
+ struct media_player *mp = data;
+ struct player_callback *cb = mp->cb;
+ int err;
+
+ if (cb->cbs->release_fast_forward == NULL)
+ return btd_error_not_supported(msg);
+
+ err = cb->cbs->release_fast_forward(mp, cb->user_data);
+ if (err < 0)
+ return btd_error_failed(msg, strerror(-err));
+
+ DBG("-");
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *media_player_press_rewind(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ DBG("+");
+ struct media_player *mp = data;
+ struct player_callback *cb = mp->cb;
+ int err;
+
+ if (cb->cbs->press_rewind == NULL)
+ return btd_error_not_supported(msg);
+
+ err = cb->cbs->press_rewind(mp, cb->user_data);
+ if (err < 0)
+ return btd_error_failed(msg, strerror(-err));
+
+ DBG("-");
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *media_player_release_rewind(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ DBG("+");
+ struct media_player *mp = data;
+ struct player_callback *cb = mp->cb;
+ int err;
+
+ if (cb->cbs->release_rewind == NULL)
+ return btd_error_not_supported(msg);
+
+ err = cb->cbs->release_rewind(mp, cb->user_data);
+ if (err < 0)
+ return btd_error_failed(msg, strerror(-err));
+ DBG("-");
+ return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
+}
+#else
static DBusMessage *media_player_fast_forward(DBusConnection *conn,
DBusMessage *msg, void *data)
{
@@ -568,7 +644,7 @@ static DBusMessage *media_player_rewind(DBusConnection *conn, DBusMessage *msg,
return g_dbus_create_reply(msg, DBUS_TYPE_INVALID);
}
-
+#endif
static void parse_folder_list(gpointer data, gpointer user_data)
{
struct media_item *item = data;
@@ -725,8 +801,15 @@ static const GDBusMethodTable media_player_methods[] = {
{ GDBUS_METHOD("Stop", NULL, NULL, media_player_stop) },
{ GDBUS_METHOD("Next", NULL, NULL, media_player_next) },
{ GDBUS_METHOD("Previous", NULL, NULL, media_player_previous) },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ { GDBUS_METHOD("PressFastForward", NULL, NULL, media_player_press_fast_forward) },
+ { GDBUS_METHOD("ReleaseFastForward", NULL, NULL, media_player_release_fast_forward) },
+ { GDBUS_METHOD("PressRewind", NULL, NULL, media_player_press_rewind) },
+ { GDBUS_METHOD("ReleaseRewind", NULL, NULL, media_player_release_rewind) },
+#else
{ GDBUS_METHOD("FastForward", NULL, NULL, media_player_fast_forward) },
{ GDBUS_METHOD("Rewind", NULL, NULL, media_player_rewind) },
+#endif
{ }
};
@@ -1328,18 +1411,37 @@ void media_player_set_metadata(struct media_player *mp,
struct media_item *item, const char *key,
void *data, size_t len)
{
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ char *value;
+ char *end, *temp;
+#else
char *value, *curval;
+#endif
value = g_strndup(data, len);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ temp = value;
+ while (g_utf8_validate(temp, -1, (const gchar **)&end) == FALSE) {
+ temp = g_utf8_find_next_char(end, NULL);
+ if (temp == NULL) {
+ *end = '\0';
+ break;
+ }
+ strcpy(end, temp);
+ temp = end;
+ }
+#endif
+
DBG("%s: %s", key, value);
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
curval = g_hash_table_lookup(mp->track, key);
if (g_strcmp0(curval, value) == 0) {
g_free(value);
return;
}
-
+#endif
if (mp->process_id == 0) {
g_hash_table_remove_all(mp->track);
mp->process_id = g_idle_add(process_metadata_changed, mp);
diff --git a/profiles/audio/player.h b/profiles/audio/player.h
index 54e395a1..21eab49e 100755
--- a/profiles/audio/player.h
+++ b/profiles/audio/player.h
@@ -52,8 +52,15 @@ struct media_player_callback {
int (*stop) (struct media_player *mp, void *user_data);
int (*next) (struct media_player *mp, void *user_data);
int (*previous) (struct media_player *mp, void *user_data);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ int (*press_fast_forward) (struct media_player *mp, void *user_data);
+ int (*release_fast_forward) (struct media_player *mp, void *user_data);
+ int (*press_rewind) (struct media_player *mp, void *user_data);
+ int (*release_rewind) (struct media_player *mp, void *user_data);
+#else
int (*fast_forward) (struct media_player *mp, void *user_data);
int (*rewind) (struct media_player *mp, void *user_data);
+#endif
int (*list_items) (struct media_player *mp, const char *name,
uint32_t start, uint32_t end, void *user_data);
int (*change_folder) (struct media_player *mp, const char *path,
diff --git a/profiles/audio/sink.c b/profiles/audio/sink.c
index 7cac2103..332d127d 100755
--- a/profiles/audio/sink.c
+++ b/profiles/audio/sink.c
@@ -106,9 +106,17 @@ static void sink_set_state(struct sink *sink, sink_state_t new_state)
if (new_state != SINK_STATE_DISCONNECTED)
return;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ btd_service_disconnecting_complete(service, 0);
+#endif
+
if (sink->session) {
avdtp_unref(sink->session);
sink->session = NULL;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ sink->connect_id = 0;
+ sink->disconnect_id = 0;
+#endif
}
}
@@ -148,7 +156,9 @@ static void stream_state_changed(struct avdtp_stream *stream,
switch (new_state) {
case AVDTP_STATE_IDLE:
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
btd_service_disconnecting_complete(sink->service, 0);
+#endif
if (sink->disconnect_id > 0) {
a2dp_cancel(sink->disconnect_id);
@@ -274,6 +284,7 @@ int sink_connect(struct btd_service *service)
{
struct sink *sink = btd_service_get_user_data(service);
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
if (!sink->session)
sink->session = a2dp_avdtp_get(btd_service_get_device(service));
@@ -281,6 +292,7 @@ int sink_connect(struct btd_service *service)
DBG("Unable to get a session");
return -EIO;
}
+#endif
if (sink->connect_id > 0 || sink->disconnect_id > 0)
return -EBUSY;
@@ -291,6 +303,16 @@ int sink_connect(struct btd_service *service)
if (sink->stream_state >= AVDTP_STATE_OPEN)
return -EALREADY;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (!sink->session)
+ sink->session = a2dp_avdtp_get(btd_service_get_device(service));
+
+ if (!sink->session) {
+ DBG("Unable to get a session");
+ return -EIO;
+ }
+#endif
+
if (!sink_setup_stream(service, NULL)) {
DBG("Failed to create a stream");
return -EIO;
@@ -309,8 +331,16 @@ static void sink_free(struct btd_service *service)
avdtp_stream_remove_cb(sink->session, sink->stream,
sink->cb_id);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (sink->session) {
+ /* We need to clear the avdtp discovery procedure */
+ finalize_discovery(sink->session, ECANCELED);
+ avdtp_unref(sink->session);
+ }
+#else
if (sink->session)
avdtp_unref(sink->session);
+#endif
if (sink->connect_id > 0) {
btd_service_connecting_complete(sink->service, -ECANCELED);
diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c
new file mode 100755
index 00000000..e4477256
--- /dev/null
+++ b/profiles/cyclingspeed/cyclingspeed.c
@@ -0,0 +1,1266 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012 Tieto Poland
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdbool.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
+#include "src/plugin.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/service.h"
+#include "src/dbus-common.h"
+#include "src/shared/util.h"
+#include "src/error.h"
+#include "attrib/gattrib.h"
+#include "attrib/att.h"
+#include "attrib/gatt.h"
+#include "src/attio.h"
+#include "src/log.h"
+
+/* min length for ATT indication or notification: opcode (1b) + handle (2b) */
+#define ATT_HDR_LEN 3
+
+#define ATT_TIMEOUT 30
+
+#define CYCLINGSPEED_INTERFACE "org.bluez.CyclingSpeed1"
+#define CYCLINGSPEED_MANAGER_INTERFACE "org.bluez.CyclingSpeedManager1"
+#define CYCLINGSPEED_WATCHER_INTERFACE "org.bluez.CyclingSpeedWatcher1"
+
+#define WHEEL_REV_SUPPORT 0x01
+#define CRANK_REV_SUPPORT 0x02
+#define MULTI_SENSOR_LOC_SUPPORT 0x04
+
+#define WHEEL_REV_PRESENT 0x01
+#define CRANK_REV_PRESENT 0x02
+
+#define SET_CUMULATIVE_VALUE 0x01
+#define START_SENSOR_CALIBRATION 0x02
+#define UPDATE_SENSOR_LOC 0x03
+#define REQUEST_SUPPORTED_SENSOR_LOC 0x04
+#define RESPONSE_CODE 0x10
+
+#define RSP_SUCCESS 0x01
+#define RSP_NOT_SUPPORTED 0x02
+#define RSP_INVALID_PARAM 0x03
+#define RSP_FAILED 0x04
+
+struct csc;
+
+struct controlpoint_req {
+ struct csc *csc;
+ uint8_t opcode;
+ guint timeout;
+ GDBusPendingReply reply_id;
+ DBusMessage *msg;
+
+ uint8_t pending_location;
+};
+
+struct csc_adapter {
+ struct btd_adapter *adapter;
+ GSList *devices; /* list of registered devices */
+ GSList *watchers;
+};
+
+struct csc {
+ struct btd_device *dev;
+ struct csc_adapter *cadapter;
+
+ GAttrib *attrib;
+ guint attioid;
+ /* attio id for measurement characteristics value notifications */
+ guint attio_measurement_id;
+ /* attio id for SC Control Point characteristics value indications */
+ guint attio_controlpoint_id;
+
+ struct att_range *svc_range;
+
+ uint16_t measurement_ccc_handle;
+ uint16_t controlpoint_val_handle;
+
+ uint16_t feature;
+ gboolean has_location;
+ uint8_t location;
+ uint8_t num_locations;
+ uint8_t *locations;
+
+ struct controlpoint_req *pending_req;
+};
+
+struct watcher {
+ struct csc_adapter *cadapter;
+ guint id;
+ char *srv;
+ char *path;
+};
+
+struct measurement {
+ struct csc *csc;
+
+ bool has_wheel_rev;
+ uint32_t wheel_rev;
+ uint16_t last_wheel_time;
+
+ bool has_crank_rev;
+ uint16_t crank_rev;
+ uint16_t last_crank_time;
+};
+
+struct characteristic {
+ struct csc *csc;
+ char uuid[MAX_LEN_UUID_STR + 1];
+};
+
+static GSList *csc_adapters = NULL;
+
+static const char * const location_enum[] = {
+ "other", "top-of-shoe", "in-shoe", "hip", "front-wheel", "left-crank",
+ "right-crank", "left-pedal", "right-pedal", "front-hub",
+ "rear-dropout", "chainstay", "rear-wheel", "rear-hub"
+};
+
+static const char *location2str(uint8_t value)
+{
+ if (value < G_N_ELEMENTS(location_enum))
+ return location_enum[value];
+
+ info("Body Sensor Location [%d] is RFU", value);
+
+ return location_enum[0];
+}
+
+static int str2location(const char *location)
+{
+ size_t i;
+
+ for (i = 0; i < G_N_ELEMENTS(location_enum); i++)
+ if (!strcmp(location_enum[i], location))
+ return i;
+
+ return -1;
+}
+
+static int cmp_adapter(gconstpointer a, gconstpointer b)
+{
+ const struct csc_adapter *cadapter = a;
+ const struct btd_adapter *adapter = b;
+
+ if (adapter == cadapter->adapter)
+ return 0;
+
+ return -1;
+}
+
+static int cmp_device(gconstpointer a, gconstpointer b)
+{
+ const struct csc *csc = a;
+ const struct btd_device *dev = b;
+
+ if (dev == csc->dev)
+ return 0;
+
+ return -1;
+}
+
+static int cmp_watcher(gconstpointer a, gconstpointer b)
+{
+ const struct watcher *watcher = a;
+ const struct watcher *match = b;
+ int ret;
+
+ ret = g_strcmp0(watcher->srv, match->srv);
+ if (ret != 0)
+ return ret;
+
+ return g_strcmp0(watcher->path, match->path);
+}
+
+static struct csc_adapter *find_csc_adapter(struct btd_adapter *adapter)
+{
+ GSList *l = g_slist_find_custom(csc_adapters, adapter, cmp_adapter);
+
+ if (!l)
+ return NULL;
+
+ return l->data;
+}
+
+static void destroy_watcher(gpointer user_data)
+{
+ struct watcher *watcher = user_data;
+
+ g_free(watcher->path);
+ g_free(watcher->srv);
+ g_free(watcher);
+}
+
+static struct watcher *find_watcher(GSList *list, const char *sender,
+ const char *path)
+{
+ struct watcher *match;
+ GSList *l;
+
+ match = g_new0(struct watcher, 1);
+ match->srv = g_strdup(sender);
+ match->path = g_strdup(path);
+
+ l = g_slist_find_custom(list, match, cmp_watcher);
+ destroy_watcher(match);
+
+ if (l != NULL)
+ return l->data;
+
+ return NULL;
+}
+
+static void remove_watcher(gpointer user_data)
+{
+ struct watcher *watcher = user_data;
+
+ g_dbus_remove_watch(btd_get_dbus_connection(), watcher->id);
+}
+
+static void destroy_csc_adapter(gpointer user_data)
+{
+ struct csc_adapter *cadapter = user_data;
+
+ g_slist_free_full(cadapter->watchers, remove_watcher);
+
+ g_free(cadapter);
+}
+
+static void destroy_csc(gpointer user_data)
+{
+ struct csc *csc = user_data;
+
+ if (csc->attioid > 0)
+ btd_device_remove_attio_callback(csc->dev, csc->attioid);
+
+ if (csc->attrib != NULL) {
+ g_attrib_unregister(csc->attrib, csc->attio_measurement_id);
+ g_attrib_unregister(csc->attrib, csc->attio_controlpoint_id);
+ g_attrib_unref(csc->attrib);
+ }
+
+ btd_device_unref(csc->dev);
+ g_free(csc->svc_range);
+ g_free(csc->locations);
+ g_free(csc);
+}
+
+static void char_write_cb(guint8 status, const guint8 *pdu, guint16 len,
+ gpointer user_data)
+{
+ char *msg = user_data;
+
+ if (status != 0)
+ error("%s failed", msg);
+
+ g_free(msg);
+}
+
+static gboolean controlpoint_timeout(gpointer user_data)
+{
+ struct controlpoint_req *req = user_data;
+
+ if (req->opcode == UPDATE_SENSOR_LOC) {
+ g_dbus_pending_property_error(req->reply_id,
+ ERROR_INTERFACE ".Failed",
+ "Operation failed (timeout)");
+ } else if (req->opcode == SET_CUMULATIVE_VALUE) {
+ DBusMessage *reply;
+
+ reply = btd_error_failed(req->msg,
+ "Operation failed (timeout)");
+
+ g_dbus_send_message(btd_get_dbus_connection(), reply);
+
+ dbus_message_unref(req->msg);
+ }
+
+ req->csc->pending_req = NULL;
+ g_free(req);
+
+ return FALSE;
+}
+
+static void controlpoint_write_cb(guint8 status, const guint8 *pdu, guint16 len,
+ gpointer user_data)
+{
+ struct controlpoint_req *req = user_data;
+
+ if (status == 0) {
+ req->timeout = g_timeout_add_seconds(ATT_TIMEOUT,
+ controlpoint_timeout,
+ req);
+ return;
+ }
+
+ error("SC Control Point write failed (opcode=%d)", req->opcode);
+
+ if (req->opcode == UPDATE_SENSOR_LOC) {
+ g_dbus_pending_property_error(req->reply_id,
+ ERROR_INTERFACE ".Failed",
+ "Operation failed (%d)", status);
+ } else if (req->opcode == SET_CUMULATIVE_VALUE) {
+ DBusMessage *reply;
+
+ reply = btd_error_failed(req->msg, "Operation failed");
+
+ g_dbus_send_message(btd_get_dbus_connection(), reply);
+
+ dbus_message_unref(req->msg);
+ }
+
+ req->csc->pending_req = NULL;
+ g_free(req);
+}
+
+static void read_supported_locations(struct csc *csc)
+{
+ struct controlpoint_req *req;
+
+ req = g_new0(struct controlpoint_req, 1);
+ req->csc = csc;
+ req->opcode = REQUEST_SUPPORTED_SENSOR_LOC;
+
+ csc->pending_req = req;
+
+ gatt_write_char(csc->attrib, csc->controlpoint_val_handle,
+ &req->opcode, sizeof(req->opcode),
+ controlpoint_write_cb, req);
+}
+
+static void read_feature_cb(guint8 status, const guint8 *pdu, guint16 len,
+ gpointer user_data)
+{
+ struct csc *csc = user_data;
+ uint8_t value[2];
+ ssize_t vlen;
+
+ if (status) {
+ error("CSC Feature read failed: %s", att_ecode2str(status));
+ return;
+ }
+
+ vlen = dec_read_resp(pdu, len, value, sizeof(value));
+ if (vlen < 0) {
+ error("Protocol error");
+ return;
+ }
+
+ if (vlen != sizeof(value)) {
+ error("Invalid value length for CSC Feature");
+ return;
+ }
+
+ csc->feature = get_le16(value);
+
+ if ((csc->feature & MULTI_SENSOR_LOC_SUPPORT)
+ && (csc->locations == NULL))
+ read_supported_locations(csc);
+}
+
+static void read_location_cb(guint8 status, const guint8 *pdu,
+ guint16 len, gpointer user_data)
+{
+ struct csc *csc = user_data;
+ uint8_t value;
+ ssize_t vlen;
+
+ if (status) {
+ error("Sensor Location read failed: %s", att_ecode2str(status));
+ return;
+ }
+
+ vlen = dec_read_resp(pdu, len, &value, sizeof(value));
+ if (vlen < 0) {
+ error("Protocol error");
+ return;
+ }
+
+ if (vlen != sizeof(value)) {
+ error("Invalid value length for Sensor Location");
+ return;
+ }
+
+ csc->has_location = TRUE;
+ csc->location = value;
+
+ g_dbus_emit_property_changed(btd_get_dbus_connection(),
+ device_get_path(csc->dev),
+ CYCLINGSPEED_INTERFACE, "Location");
+}
+
+static void discover_desc_cb(guint8 status, GSList *descs, gpointer user_data)
+{
+ struct characteristic *ch = user_data;
+ struct gatt_desc *desc;
+ uint8_t attr_val[2];
+ char *msg = NULL;
+
+ if (status != 0) {
+ error("Discover %s descriptors failed: %s", ch->uuid,
+ att_ecode2str(status));
+ goto done;
+ }
+
+ /* There will be only one descriptor on list and it will be CCC */
+ desc = descs->data;
+
+ if (g_strcmp0(ch->uuid, CSC_MEASUREMENT_UUID) == 0) {
+ ch->csc->measurement_ccc_handle = desc->handle;
+
+ if (g_slist_length(ch->csc->cadapter->watchers) == 0) {
+ put_le16(0x0000, attr_val);
+ msg = g_strdup("Disable measurement");
+ } else {
+ put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT,
+ attr_val);
+ msg = g_strdup("Enable measurement");
+ }
+ } else if (g_strcmp0(ch->uuid, SC_CONTROL_POINT_UUID) == 0) {
+ put_le16(GATT_CLIENT_CHARAC_CFG_IND_BIT, attr_val);
+ msg = g_strdup("Enable SC Control Point indications");
+ } else {
+ goto done;
+ }
+
+ gatt_write_char(ch->csc->attrib, desc->handle, attr_val,
+ sizeof(attr_val), char_write_cb, msg);
+
+done:
+ g_free(ch);
+}
+
+static void discover_desc(struct csc *csc, struct gatt_char *c,
+ struct gatt_char *c_next)
+{
+ struct characteristic *ch;
+ uint16_t start, end;
+ bt_uuid_t uuid;
+
+ start = c->value_handle + 1;
+
+ if (c_next != NULL) {
+ if (start == c_next->handle)
+ return;
+ end = c_next->handle - 1;
+ } else if (c->value_handle != csc->svc_range->end) {
+ end = csc->svc_range->end;
+ } else {
+ return;
+ }
+
+ ch = g_new0(struct characteristic, 1);
+ ch->csc = csc;
+ memcpy(ch->uuid, c->uuid, sizeof(c->uuid));
+
+ bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+
+ gatt_discover_desc(csc->attrib, start, end, &uuid, discover_desc_cb,
+ ch);
+}
+
+static void update_watcher(gpointer data, gpointer user_data)
+{
+ struct watcher *w = data;
+ struct measurement *m = user_data;
+ struct csc *csc = m->csc;
+ const char *path = device_get_path(csc->dev);
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+ DBusMessage *msg;
+
+ msg = dbus_message_new_method_call(w->srv, w->path,
+ CYCLINGSPEED_WATCHER_INTERFACE, "MeasurementReceived");
+ if (msg == NULL)
+ return;
+
+ dbus_message_iter_init_append(msg, &iter);
+
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH , &path);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+ if (m->has_wheel_rev) {
+ dict_append_entry(&dict, "WheelRevolutions",
+ DBUS_TYPE_UINT32, &m->wheel_rev);
+ dict_append_entry(&dict, "LastWheelEventTime",
+ DBUS_TYPE_UINT16, &m->last_wheel_time);
+ }
+
+ if (m->has_crank_rev) {
+ dict_append_entry(&dict, "CrankRevolutions",
+ DBUS_TYPE_UINT16, &m->crank_rev);
+ dict_append_entry(&dict, "LastCrankEventTime",
+ DBUS_TYPE_UINT16, &m->last_crank_time);
+ }
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ dbus_message_set_no_reply(msg, TRUE);
+ g_dbus_send_message(btd_get_dbus_connection(), msg);
+}
+
+static void process_measurement(struct csc *csc, const uint8_t *pdu,
+ uint16_t len)
+{
+ struct measurement m;
+ uint8_t flags;
+
+ flags = *pdu;
+
+ pdu++;
+ len--;
+
+ memset(&m, 0, sizeof(m));
+
+ if ((flags & WHEEL_REV_PRESENT) && (csc->feature & WHEEL_REV_SUPPORT)) {
+ if (len < 6) {
+ error("Wheel revolutions data fields missing");
+ return;
+ }
+
+ m.has_wheel_rev = true;
+ m.wheel_rev = get_le32(pdu);
+ m.last_wheel_time = get_le16(pdu + 4);
+ pdu += 6;
+ len -= 6;
+ }
+
+ if ((flags & CRANK_REV_PRESENT) && (csc->feature & CRANK_REV_SUPPORT)) {
+ if (len < 4) {
+ error("Crank revolutions data fields missing");
+ return;
+ }
+
+ m.has_crank_rev = true;
+ m.crank_rev = get_le16(pdu);
+ m.last_crank_time = get_le16(pdu + 2);
+ pdu += 4;
+ len -= 4;
+ }
+
+ /* Notify all registered watchers */
+ m.csc = csc;
+ g_slist_foreach(csc->cadapter->watchers, update_watcher, &m);
+}
+
+static void measurement_notify_handler(const uint8_t *pdu, uint16_t len,
+ gpointer user_data)
+{
+ struct csc *csc = user_data;
+
+ /* should be at least opcode (1b) + handle (2b) */
+ if (len < 3) {
+ error("Invalid PDU received");
+ return;
+ }
+
+ process_measurement(csc, pdu + 3, len - 3);
+}
+
+static void controlpoint_property_reply(struct controlpoint_req *req,
+ uint8_t code)
+{
+ switch (code) {
+ case RSP_SUCCESS:
+ g_dbus_pending_property_success(req->reply_id);
+ break;
+
+ case RSP_NOT_SUPPORTED:
+ g_dbus_pending_property_error(req->reply_id,
+ ERROR_INTERFACE ".NotSupported",
+ "Feature is not supported");
+ break;
+
+ case RSP_INVALID_PARAM:
+ g_dbus_pending_property_error(req->reply_id,
+ ERROR_INTERFACE ".InvalidArguments",
+ "Invalid arguments in method call");
+ break;
+
+ case RSP_FAILED:
+ g_dbus_pending_property_error(req->reply_id,
+ ERROR_INTERFACE ".Failed",
+ "Operation failed");
+ break;
+
+ default:
+ g_dbus_pending_property_error(req->reply_id,
+ ERROR_INTERFACE ".Failed",
+ "Operation failed (%d)", code);
+ break;
+ }
+}
+
+static void controlpoint_method_reply(struct controlpoint_req *req,
+ uint8_t code)
+{
+ DBusMessage *reply;
+
+ switch (code) {
+ case RSP_SUCCESS:
+ reply = dbus_message_new_method_return(req->msg);
+ break;
+ case RSP_NOT_SUPPORTED:
+ reply = btd_error_not_supported(req->msg);
+ break;
+ case RSP_INVALID_PARAM:
+ reply = btd_error_invalid_args(req->msg);
+ break;
+ case RSP_FAILED:
+ reply = btd_error_failed(req->msg, "Failed");
+ break;
+ default:
+ reply = btd_error_failed(req->msg, "Unknown error");
+ break;
+ }
+
+ g_dbus_send_message(btd_get_dbus_connection(), reply);
+
+ dbus_message_unref(req->msg);
+}
+
+static void controlpoint_ind_handler(const uint8_t *pdu, uint16_t len,
+ gpointer user_data)
+{
+ struct csc *csc = user_data;
+ struct controlpoint_req *req = csc->pending_req;
+ uint8_t opcode;
+ uint8_t req_opcode;
+ uint8_t rsp_code;
+ uint8_t *opdu;
+ uint16_t olen;
+ size_t plen;
+
+ if (len < ATT_HDR_LEN) {
+ error("Invalid PDU received");
+ return;
+ }
+
+ /* skip ATT header */
+ pdu += ATT_HDR_LEN;
+ len -= ATT_HDR_LEN;
+
+ if (len < 1) {
+ error("Op Code missing");
+ goto done;
+ }
+
+ opcode = *pdu;
+ pdu++;
+ len--;
+
+ if (opcode != RESPONSE_CODE) {
+ DBG("Unsupported Op Code received (%d)", opcode);
+ goto done;
+ }
+
+ if (len < 2) {
+ error("Invalid Response Code PDU received");
+ goto done;
+ }
+
+ req_opcode = *pdu;
+ rsp_code = *(pdu + 1);
+ pdu += 2;
+ len -= 2;
+
+ if (req == NULL || req->opcode != req_opcode) {
+ DBG("Indication received without pending request");
+ goto done;
+ }
+
+ switch (req->opcode) {
+ case SET_CUMULATIVE_VALUE:
+ controlpoint_method_reply(req, rsp_code);
+ break;
+
+ case REQUEST_SUPPORTED_SENSOR_LOC:
+ if (rsp_code == RSP_SUCCESS) {
+ csc->num_locations = len;
+ csc->locations = g_memdup(pdu, len);
+ } else {
+ error("Failed to read Supported Sendor Locations");
+ }
+ break;
+
+ case UPDATE_SENSOR_LOC:
+ csc->location = req->pending_location;
+
+ controlpoint_property_reply(req, rsp_code);
+
+ g_dbus_emit_property_changed(btd_get_dbus_connection(),
+ device_get_path(csc->dev),
+ CYCLINGSPEED_INTERFACE, "Location");
+ break;
+ }
+
+ csc->pending_req = NULL;
+ g_source_remove(req->timeout);
+ g_free(req);
+
+done:
+ opdu = g_attrib_get_buffer(csc->attrib, &plen);
+ olen = enc_confirmation(opdu, plen);
+ if (olen > 0)
+ g_attrib_send(csc->attrib, 0, opdu, olen, NULL, NULL, NULL);
+}
+
+static void discover_char_cb(uint8_t status, GSList *chars, void *user_data)
+{
+ struct csc *csc = user_data;
+ uint16_t feature_val_handle = 0;
+
+ if (status) {
+ error("Discover CSCS characteristics: %s",
+ att_ecode2str(status));
+ return;
+ }
+
+ for (; chars; chars = chars->next) {
+ struct gatt_char *c = chars->data;
+ struct gatt_char *c_next =
+ (chars->next ? chars->next->data : NULL);
+
+ if (g_strcmp0(c->uuid, CSC_MEASUREMENT_UUID) == 0) {
+ csc->attio_measurement_id =
+ g_attrib_register(csc->attrib,
+ ATT_OP_HANDLE_NOTIFY, c->value_handle,
+ measurement_notify_handler, csc, NULL);
+
+ discover_desc(csc, c, c_next);
+ } else if (g_strcmp0(c->uuid, CSC_FEATURE_UUID) == 0) {
+ feature_val_handle = c->value_handle;
+ } else if (g_strcmp0(c->uuid, SENSOR_LOCATION_UUID) == 0) {
+ DBG("Sensor Location supported");
+ gatt_read_char(csc->attrib, c->value_handle,
+ read_location_cb, csc);
+ } else if (g_strcmp0(c->uuid, SC_CONTROL_POINT_UUID) == 0) {
+ DBG("SC Control Point supported");
+ csc->controlpoint_val_handle = c->value_handle;
+
+ csc->attio_controlpoint_id = g_attrib_register(
+ csc->attrib, ATT_OP_HANDLE_IND,
+ c->value_handle,
+ controlpoint_ind_handler, csc, NULL);
+
+ discover_desc(csc, c, c_next);
+ }
+ }
+
+ if (feature_val_handle > 0)
+ gatt_read_char(csc->attrib, feature_val_handle,
+ read_feature_cb, csc);
+}
+
+static void enable_measurement(gpointer data, gpointer user_data)
+{
+ struct csc *csc = data;
+ uint16_t handle = csc->measurement_ccc_handle;
+ uint8_t value[2];
+ char *msg;
+
+ if (csc->attrib == NULL || !handle)
+ return;
+
+ put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value);
+ msg = g_strdup("Enable measurement");
+
+ gatt_write_char(csc->attrib, handle, value, sizeof(value),
+ char_write_cb, msg);
+}
+
+static void disable_measurement(gpointer data, gpointer user_data)
+{
+ struct csc *csc = data;
+ uint16_t handle = csc->measurement_ccc_handle;
+ uint8_t value[2];
+ char *msg;
+
+ if (csc->attrib == NULL || !handle)
+ return;
+
+ put_le16(0x0000, value);
+ msg = g_strdup("Disable measurement");
+
+ gatt_write_char(csc->attrib, handle, value, sizeof(value),
+ char_write_cb, msg);
+}
+
+static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
+{
+ struct csc *csc = user_data;
+
+ DBG("");
+
+ csc->attrib = g_attrib_ref(attrib);
+
+ gatt_discover_char(csc->attrib, csc->svc_range->start,
+ csc->svc_range->end, NULL,
+ discover_char_cb, csc);
+}
+
+static void attio_disconnected_cb(gpointer user_data)
+{
+ struct csc *csc = user_data;
+
+ DBG("");
+
+ if (csc->attio_measurement_id > 0) {
+ g_attrib_unregister(csc->attrib, csc->attio_measurement_id);
+ csc->attio_measurement_id = 0;
+ }
+
+ if (csc->attio_controlpoint_id > 0) {
+ g_attrib_unregister(csc->attrib, csc->attio_controlpoint_id);
+ csc->attio_controlpoint_id = 0;
+ }
+
+ g_attrib_unref(csc->attrib);
+ csc->attrib = NULL;
+}
+
+static void watcher_exit_cb(DBusConnection *conn, void *user_data)
+{
+ struct watcher *watcher = user_data;
+ struct csc_adapter *cadapter = watcher->cadapter;
+
+ DBG("cycling watcher [%s] disconnected", watcher->path);
+
+ cadapter->watchers = g_slist_remove(cadapter->watchers, watcher);
+ g_dbus_remove_watch(conn, watcher->id);
+
+ if (g_slist_length(cadapter->watchers) == 0)
+ g_slist_foreach(cadapter->devices, disable_measurement, 0);
+}
+
+static DBusMessage *register_watcher(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct csc_adapter *cadapter = data;
+ struct watcher *watcher;
+ const char *sender = dbus_message_get_sender(msg);
+ char *path;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ watcher = find_watcher(cadapter->watchers, sender, path);
+ if (watcher != NULL)
+ return btd_error_already_exists(msg);
+
+ watcher = g_new0(struct watcher, 1);
+ watcher->cadapter = cadapter;
+ watcher->id = g_dbus_add_disconnect_watch(conn, sender, watcher_exit_cb,
+ watcher, destroy_watcher);
+ watcher->srv = g_strdup(sender);
+ watcher->path = g_strdup(path);
+
+ if (g_slist_length(cadapter->watchers) == 0)
+ g_slist_foreach(cadapter->devices, enable_measurement, 0);
+
+ cadapter->watchers = g_slist_prepend(cadapter->watchers, watcher);
+
+ DBG("cycling watcher [%s] registered", path);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *unregister_watcher(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct csc_adapter *cadapter = data;
+ struct watcher *watcher;
+ const char *sender = dbus_message_get_sender(msg);
+ char *path;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ watcher = find_watcher(cadapter->watchers, sender, path);
+ if (watcher == NULL)
+ return btd_error_does_not_exist(msg);
+
+ cadapter->watchers = g_slist_remove(cadapter->watchers, watcher);
+ g_dbus_remove_watch(conn, watcher->id);
+
+ if (g_slist_length(cadapter->watchers) == 0)
+ g_slist_foreach(cadapter->devices, disable_measurement, 0);
+
+ DBG("cycling watcher [%s] unregistered", path);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable cyclingspeed_manager_methods[] = {
+ { GDBUS_METHOD("RegisterWatcher",
+ GDBUS_ARGS({ "agent", "o" }), NULL,
+ register_watcher) },
+ { GDBUS_METHOD("UnregisterWatcher",
+ GDBUS_ARGS({ "agent", "o" }), NULL,
+ unregister_watcher) },
+ { }
+};
+
+static int csc_adapter_probe(struct btd_profile *p, struct btd_adapter *adapter)
+{
+ struct csc_adapter *cadapter;
+
+ cadapter = g_new0(struct csc_adapter, 1);
+ cadapter->adapter = adapter;
+
+ if (!g_dbus_register_interface(btd_get_dbus_connection(),
+ adapter_get_path(adapter),
+ CYCLINGSPEED_MANAGER_INTERFACE,
+ cyclingspeed_manager_methods,
+ NULL, NULL, cadapter,
+ destroy_csc_adapter)) {
+ error("D-Bus failed to register %s interface",
+ CYCLINGSPEED_MANAGER_INTERFACE);
+ destroy_csc_adapter(cadapter);
+ return -EIO;
+ }
+
+ csc_adapters = g_slist_prepend(csc_adapters, cadapter);
+
+ return 0;
+}
+
+static void csc_adapter_remove(struct btd_profile *p,
+ struct btd_adapter *adapter)
+{
+ struct csc_adapter *cadapter;
+
+ cadapter = find_csc_adapter(adapter);
+ if (cadapter == NULL)
+ return;
+
+ csc_adapters = g_slist_remove(csc_adapters, cadapter);
+
+ g_dbus_unregister_interface(btd_get_dbus_connection(),
+ adapter_get_path(cadapter->adapter),
+ CYCLINGSPEED_MANAGER_INTERFACE);
+}
+
+static gboolean property_get_location(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct csc *csc = data;
+ const char *loc;
+
+ if (!csc->has_location)
+ return FALSE;
+
+ loc = location2str(csc->location);
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &loc);
+
+ return TRUE;
+}
+
+static void property_set_location(const GDBusPropertyTable *property,
+ DBusMessageIter *iter,
+ GDBusPendingPropertySet id, void *data)
+{
+ struct csc *csc = data;
+ char *loc;
+ int loc_val;
+ uint8_t att_val[2];
+ struct controlpoint_req *req;
+
+ if (csc->pending_req != NULL) {
+ g_dbus_pending_property_error(id,
+ ERROR_INTERFACE ".InProgress",
+ "Operation already in progress");
+ return;
+ }
+
+ if (!(csc->feature & MULTI_SENSOR_LOC_SUPPORT)) {
+ g_dbus_pending_property_error(id,
+ ERROR_INTERFACE ".NotSupported",
+ "Feature is not supported");
+ return;
+ }
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) {
+ g_dbus_pending_property_error(id,
+ ERROR_INTERFACE ".InvalidArguments",
+ "Invalid arguments in method call");
+ return;
+ }
+
+ dbus_message_iter_get_basic(iter, &loc);
+
+ loc_val = str2location(loc);
+
+ if (loc_val < 0) {
+ g_dbus_pending_property_error(id,
+ ERROR_INTERFACE ".InvalidArguments",
+ "Invalid arguments in method call");
+ return;
+ }
+
+ req = g_new(struct controlpoint_req, 1);
+ req->csc = csc;
+ req->reply_id = id;
+ req->opcode = UPDATE_SENSOR_LOC;
+ req->pending_location = loc_val;
+
+ csc->pending_req = req;
+
+ att_val[0] = UPDATE_SENSOR_LOC;
+ att_val[1] = loc_val;
+
+ gatt_write_char(csc->attrib, csc->controlpoint_val_handle, att_val,
+ sizeof(att_val), controlpoint_write_cb, req);
+}
+
+static gboolean property_exists_location(const GDBusPropertyTable *property,
+ void *data)
+{
+ struct csc *csc = data;
+
+ return csc->has_location;
+}
+
+static gboolean property_get_locations(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct csc *csc = data;
+ DBusMessageIter entry;
+ int i;
+
+ if (!(csc->feature & MULTI_SENSOR_LOC_SUPPORT))
+ return FALSE;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_STRING_AS_STRING, &entry);
+ for (i = 0; i < csc->num_locations; i++) {
+ char *loc = g_strdup(location2str(csc->locations[i]));
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &loc);
+ g_free(loc);
+ }
+
+ dbus_message_iter_close_container(iter, &entry);
+
+ return TRUE;
+}
+
+static gboolean property_exists_locations(const GDBusPropertyTable *property,
+ void *data)
+{
+ struct csc *csc = data;
+
+ return !!(csc->feature & MULTI_SENSOR_LOC_SUPPORT);
+}
+
+static gboolean property_get_wheel_rev_sup(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct csc *csc = data;
+ dbus_bool_t val;
+
+ val = !!(csc->feature & WHEEL_REV_SUPPORT);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &val);
+
+ return TRUE;
+}
+
+static gboolean property_get_multi_loc_sup(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct csc *csc = data;
+ dbus_bool_t val;
+
+ val = !!(csc->feature & MULTI_SENSOR_LOC_SUPPORT);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &val);
+
+ return TRUE;
+}
+
+static const GDBusPropertyTable cyclingspeed_device_properties[] = {
+ { "Location", "s", property_get_location, property_set_location,
+ property_exists_location },
+ { "SupportedLocations", "as", property_get_locations, NULL,
+ property_exists_locations },
+ { "WheelRevolutionDataSupported", "b", property_get_wheel_rev_sup },
+ { "MultipleLocationsSupported", "b", property_get_multi_loc_sup },
+ { }
+};
+
+static DBusMessage *set_cumulative_wheel_rev(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct csc *csc = data;
+ dbus_uint32_t value;
+ struct controlpoint_req *req;
+ uint8_t att_val[5]; /* uint8 opcode + uint32 value */
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &value,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ if (csc->pending_req != NULL)
+ return btd_error_in_progress(msg);
+
+ req = g_new(struct controlpoint_req, 1);
+ req->csc = csc;
+ req->opcode = SET_CUMULATIVE_VALUE;
+ req->msg = dbus_message_ref(msg);
+
+ csc->pending_req = req;
+
+ att_val[0] = SET_CUMULATIVE_VALUE;
+ put_le32(value, att_val + 1);
+
+ gatt_write_char(csc->attrib, csc->controlpoint_val_handle, att_val,
+ sizeof(att_val), controlpoint_write_cb, req);
+
+ return NULL;
+}
+
+static const GDBusMethodTable cyclingspeed_device_methods[] = {
+ { GDBUS_ASYNC_METHOD("SetCumulativeWheelRevolutions",
+ GDBUS_ARGS({ "value", "u" }), NULL,
+ set_cumulative_wheel_rev) },
+ { }
+};
+
+static int csc_device_probe(struct btd_service *service)
+{
+ struct btd_device *device = btd_service_get_device(service);
+ struct btd_adapter *adapter;
+ struct csc_adapter *cadapter;
+ struct csc *csc;
+ struct gatt_primary *prim;
+
+ prim = btd_device_get_primary(device, CYCLING_SC_UUID);
+ if (prim == NULL)
+ return -EINVAL;
+
+ adapter = device_get_adapter(device);
+
+ cadapter = find_csc_adapter(adapter);
+ if (cadapter == NULL)
+ return -1;
+
+ csc = g_new0(struct csc, 1);
+ csc->dev = btd_device_ref(device);
+ csc->cadapter = cadapter;
+
+ if (!g_dbus_register_interface(btd_get_dbus_connection(),
+ device_get_path(device),
+ CYCLINGSPEED_INTERFACE,
+ cyclingspeed_device_methods,
+ NULL,
+ cyclingspeed_device_properties,
+ csc, destroy_csc)) {
+ error("D-Bus failed to register %s interface",
+ CYCLINGSPEED_INTERFACE);
+ destroy_csc(csc);
+ return -EIO;
+ }
+
+ csc->svc_range = g_new0(struct att_range, 1);
+ csc->svc_range->start = prim->range.start;
+ csc->svc_range->end = prim->range.end;
+
+ cadapter->devices = g_slist_prepend(cadapter->devices, csc);
+
+ csc->attioid = btd_device_add_attio_callback(device, attio_connected_cb,
+ attio_disconnected_cb, csc);
+
+ return 0;
+}
+
+static void csc_device_remove(struct btd_service *service)
+{
+ struct btd_device *device = btd_service_get_device(service);
+ struct btd_adapter *adapter;
+ struct csc_adapter *cadapter;
+ struct csc *csc;
+ GSList *l;
+
+ adapter = device_get_adapter(device);
+
+ cadapter = find_csc_adapter(adapter);
+ if (cadapter == NULL)
+ return;
+
+ l = g_slist_find_custom(cadapter->devices, device, cmp_device);
+ if (l == NULL)
+ return;
+
+ csc = l->data;
+
+ cadapter->devices = g_slist_remove(cadapter->devices, csc);
+
+ g_dbus_unregister_interface(btd_get_dbus_connection(),
+ device_get_path(device),
+ CYCLINGSPEED_INTERFACE);
+}
+
+static struct btd_profile cscp_profile = {
+ .name = "Cycling Speed and Cadence GATT Driver",
+ .remote_uuid = CYCLING_SC_UUID,
+
+ .adapter_probe = csc_adapter_probe,
+ .adapter_remove = csc_adapter_remove,
+
+ .device_probe = csc_device_probe,
+ .device_remove = csc_device_remove,
+};
+
+static int cyclingspeed_init(void)
+{
+ return btd_profile_register(&cscp_profile);
+}
+
+static void cyclingspeed_exit(void)
+{
+ btd_profile_unregister(&cscp_profile);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(cyclingspeed, VERSION,
+ BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+ cyclingspeed_init, cyclingspeed_exit)
diff --git a/profiles/gap/gas.c b/profiles/gap/gas.c
index 47c8c257..ac9d4ac2 100755
--- a/profiles/gap/gas.c
+++ b/profiles/gap/gas.c
@@ -154,6 +154,40 @@ static void handle_appearance(struct gas *gas, uint16_t value_handle)
DBG("Failed to send request to read appearance");
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+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;
@@ -180,6 +214,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_FEATURE_BLUEZ_MODIFY
+ 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/heartrate/heartrate.c b/profiles/heartrate/heartrate.c
new file mode 100755
index 00000000..9e8c4993
--- /dev/null
+++ b/profiles/heartrate/heartrate.c
@@ -0,0 +1,870 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012 Tieto Poland
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdbool.h>
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
+#include "src/plugin.h"
+#include "src/adapter.h"
+#include "src/dbus-common.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/shared/util.h"
+#include "src/service.h"
+#include "src/error.h"
+#include "attrib/gattrib.h"
+#include "attrib/att.h"
+#include "attrib/gatt.h"
+#include "src/attio.h"
+#include "src/log.h"
+
+#define HEART_RATE_INTERFACE "org.bluez.HeartRate1"
+#define HEART_RATE_MANAGER_INTERFACE "org.bluez.HeartRateManager1"
+#define HEART_RATE_WATCHER_INTERFACE "org.bluez.HeartRateWatcher1"
+
+#define HR_VALUE_FORMAT 0x01
+#define SENSOR_CONTACT_DETECTED 0x02
+#define SENSOR_CONTACT_SUPPORT 0x04
+#define ENERGY_EXP_STATUS 0x08
+#define RR_INTERVAL 0x10
+
+struct heartrate_adapter {
+ struct btd_adapter *adapter;
+ GSList *devices;
+ GSList *watchers;
+};
+
+struct heartrate {
+ struct btd_device *dev;
+ struct heartrate_adapter *hradapter;
+ GAttrib *attrib;
+ guint attioid;
+ guint attionotid;
+
+ struct att_range *svc_range; /* primary svc range */
+
+ uint16_t measurement_ccc_handle;
+ uint16_t hrcp_val_handle;
+
+ gboolean has_location;
+ uint8_t location;
+};
+
+struct watcher {
+ struct heartrate_adapter *hradapter;
+ guint id;
+ char *srv;
+ char *path;
+};
+
+struct measurement {
+ struct heartrate *hr;
+ uint16_t value;
+ gboolean has_energy;
+ uint16_t energy;
+ gboolean has_contact;
+ gboolean contact;
+ uint16_t num_interval;
+ uint16_t *interval;
+};
+
+static GSList *heartrate_adapters = NULL;
+
+static const char * const location_enum[] = {
+ "other",
+ "chest",
+ "wrist",
+ "finger",
+ "hand",
+ "earlobe",
+ "foot",
+};
+
+static const char *location2str(uint8_t value)
+{
+ if (value < G_N_ELEMENTS(location_enum))
+ return location_enum[value];
+
+ error("Body Sensor Location [%d] is RFU", value);
+
+ return NULL;
+}
+
+static int cmp_adapter(gconstpointer a, gconstpointer b)
+{
+ const struct heartrate_adapter *hradapter = a;
+ const struct btd_adapter *adapter = b;
+
+ if (adapter == hradapter->adapter)
+ return 0;
+
+ return -1;
+}
+
+static int cmp_device(gconstpointer a, gconstpointer b)
+{
+ const struct heartrate *hr = a;
+ const struct btd_device *dev = b;
+
+ if (dev == hr->dev)
+ return 0;
+
+ return -1;
+}
+
+static int cmp_watcher(gconstpointer a, gconstpointer b)
+{
+ const struct watcher *watcher = a;
+ const struct watcher *match = b;
+ int ret;
+
+ ret = g_strcmp0(watcher->srv, match->srv);
+ if (ret != 0)
+ return ret;
+
+ return g_strcmp0(watcher->path, match->path);
+}
+
+static struct heartrate_adapter *
+find_heartrate_adapter(struct btd_adapter *adapter)
+{
+ GSList *l = g_slist_find_custom(heartrate_adapters, adapter,
+ cmp_adapter);
+ if (!l)
+ return NULL;
+
+ return l->data;
+}
+
+static void destroy_watcher(gpointer user_data)
+{
+ struct watcher *watcher = user_data;
+
+ g_free(watcher->path);
+ g_free(watcher->srv);
+ g_free(watcher);
+}
+
+static struct watcher *find_watcher(GSList *list, const char *sender,
+ const char *path)
+{
+ struct watcher *match;
+ GSList *l;
+
+ match = g_new0(struct watcher, 1);
+ match->srv = g_strdup(sender);
+ match->path = g_strdup(path);
+
+ l = g_slist_find_custom(list, match, cmp_watcher);
+ destroy_watcher(match);
+
+ if (l != NULL)
+ return l->data;
+
+ return NULL;
+}
+
+static void destroy_heartrate(gpointer user_data)
+{
+ struct heartrate *hr = user_data;
+
+ if (hr->attioid > 0)
+ btd_device_remove_attio_callback(hr->dev, hr->attioid);
+
+ if (hr->attrib != NULL) {
+ g_attrib_unregister(hr->attrib, hr->attionotid);
+ g_attrib_unref(hr->attrib);
+ }
+
+ btd_device_unref(hr->dev);
+ g_free(hr->svc_range);
+ g_free(hr);
+}
+
+static void remove_watcher(gpointer user_data)
+{
+ struct watcher *watcher = user_data;
+
+ g_dbus_remove_watch(btd_get_dbus_connection(), watcher->id);
+}
+
+static void destroy_heartrate_adapter(gpointer user_data)
+{
+ struct heartrate_adapter *hradapter = user_data;
+
+ g_slist_free_full(hradapter->watchers, remove_watcher);
+
+ g_free(hradapter);
+}
+
+static void read_sensor_location_cb(guint8 status, const guint8 *pdu,
+ guint16 len, gpointer user_data)
+{
+ struct heartrate *hr = user_data;
+ uint8_t value;
+ ssize_t vlen;
+
+ if (status != 0) {
+ error("Body Sensor Location read failed: %s",
+ att_ecode2str(status));
+ return;
+ }
+
+ vlen = dec_read_resp(pdu, len, &value, sizeof(value));
+ if (vlen < 0) {
+ error("Protocol error");
+ return;
+ }
+
+ if (vlen != sizeof(value)) {
+ error("Invalid length for Body Sensor Location");
+ return;
+ }
+
+ hr->has_location = TRUE;
+ hr->location = value;
+}
+
+static void char_write_cb(guint8 status, const guint8 *pdu, guint16 len,
+ gpointer user_data)
+{
+ char *msg = user_data;
+
+ if (status != 0)
+ error("%s failed", msg);
+
+ g_free(msg);
+}
+
+static void update_watcher(gpointer data, gpointer user_data)
+{
+ struct watcher *w = data;
+ struct measurement *m = user_data;
+ struct heartrate *hr = m->hr;
+ const char *path = device_get_path(hr->dev);
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+ DBusMessage *msg;
+
+ msg = dbus_message_new_method_call(w->srv, w->path,
+ HEART_RATE_WATCHER_INTERFACE, "MeasurementReceived");
+ if (msg == NULL)
+ return;
+
+ dbus_message_iter_init_append(msg, &iter);
+
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH , &path);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+ dict_append_entry(&dict, "Value", DBUS_TYPE_UINT16, &m->value);
+
+ if (m->has_energy)
+ dict_append_entry(&dict, "Energy", DBUS_TYPE_UINT16,
+ &m->energy);
+
+ if (m->has_contact)
+ dict_append_entry(&dict, "Contact", DBUS_TYPE_BOOLEAN,
+ &m->contact);
+
+ if (m->num_interval > 0)
+ dict_append_array(&dict, "Interval", DBUS_TYPE_UINT16,
+ &m->interval, m->num_interval);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ dbus_message_set_no_reply(msg, TRUE);
+ g_dbus_send_message(btd_get_dbus_connection(), msg);
+}
+
+static void process_measurement(struct heartrate *hr, const uint8_t *pdu,
+ uint16_t len)
+{
+ struct measurement m;
+ uint8_t flags;
+
+ flags = *pdu;
+
+ pdu++;
+ len--;
+
+ memset(&m, 0, sizeof(m));
+
+ if (flags & HR_VALUE_FORMAT) {
+ if (len < 2) {
+ error("Heart Rate Measurement field missing");
+ return;
+ }
+
+ m.value = get_le16(pdu);
+ pdu += 2;
+ len -= 2;
+ } else {
+ if (len < 1) {
+ error("Heart Rate Measurement field missing");
+ return;
+ }
+
+ m.value = *pdu;
+ pdu++;
+ len--;
+ }
+
+ if (flags & ENERGY_EXP_STATUS) {
+ if (len < 2) {
+ error("Energy Expended field missing");
+ return;
+ }
+
+ m.has_energy = TRUE;
+ m.energy = get_le16(pdu);
+ pdu += 2;
+ len -= 2;
+ }
+
+ if (flags & RR_INTERVAL) {
+ int i;
+
+ if (len == 0 || (len % 2 != 0)) {
+ error("RR-Interval field malformed");
+ return;
+ }
+
+ m.num_interval = len / 2;
+ m.interval = g_new(uint16_t, m.num_interval);
+
+ for (i = 0; i < m.num_interval; pdu += 2, i++)
+ m.interval[i] = get_le16(pdu);
+ }
+
+ if (flags & SENSOR_CONTACT_SUPPORT) {
+ m.has_contact = TRUE;
+ m.contact = !!(flags & SENSOR_CONTACT_DETECTED);
+ }
+
+ /* Notify all registered watchers */
+ m.hr = hr;
+ g_slist_foreach(hr->hradapter->watchers, update_watcher, &m);
+
+ g_free(m.interval);
+}
+
+static void notify_handler(const uint8_t *pdu, uint16_t len, gpointer user_data)
+{
+ struct heartrate *hr = user_data;
+
+ /* should be at least opcode (1b) + handle (2b) */
+ if (len < 3) {
+ error("Invalid PDU received");
+ return;
+ }
+
+ process_measurement(hr, pdu + 3, len - 3);
+}
+
+static void discover_ccc_cb(uint8_t status, GSList *descs, void *user_data)
+{
+ struct heartrate *hr = user_data;
+ struct gatt_desc *desc;
+ uint8_t attr_val[2];
+ char *msg;
+
+ if (status != 0) {
+ error("Discover Heart Rate Measurement descriptors failed: %s",
+ att_ecode2str(status));
+ return;
+ }
+
+ /* There will be only one descriptor on list and it will be CCC */
+ desc = descs->data;
+
+ hr->measurement_ccc_handle = desc->handle;
+
+ if (g_slist_length(hr->hradapter->watchers) == 0) {
+ put_le16(0x0000, attr_val);
+ msg = g_strdup("Disable measurement");
+ } else {
+ put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, attr_val);
+ msg = g_strdup("Enable measurement");
+ }
+
+ gatt_write_char(hr->attrib, desc->handle, attr_val, sizeof(attr_val),
+ char_write_cb, msg);
+}
+
+static void discover_measurement_ccc(struct heartrate *hr,
+ struct gatt_char *c, struct gatt_char *c_next)
+{
+ uint16_t start, end;
+ bt_uuid_t uuid;
+
+ start = c->value_handle + 1;
+
+ if (c_next != NULL) {
+ if (start == c_next->handle)
+ return;
+ end = c_next->handle - 1;
+ } else if (c->value_handle != hr->svc_range->end) {
+ end = hr->svc_range->end;
+ } else {
+ return;
+ }
+
+ bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+
+ gatt_discover_desc(hr->attrib, start, end, &uuid, discover_ccc_cb, hr);
+}
+
+static void discover_char_cb(uint8_t status, GSList *chars, void *user_data)
+{
+ struct heartrate *hr = user_data;
+
+ if (status) {
+ error("Discover HRS characteristics failed: %s",
+ att_ecode2str(status));
+ return;
+ }
+
+ for (; chars; chars = chars->next) {
+ struct gatt_char *c = chars->data;
+
+ if (g_strcmp0(c->uuid, HEART_RATE_MEASUREMENT_UUID) == 0) {
+ struct gatt_char *c_next =
+ (chars->next ? chars->next->data : NULL);
+
+ hr->attionotid = g_attrib_register(hr->attrib,
+ ATT_OP_HANDLE_NOTIFY,
+ c->value_handle,
+ notify_handler, hr, NULL);
+
+ discover_measurement_ccc(hr, c, c_next);
+ } else if (g_strcmp0(c->uuid, BODY_SENSOR_LOCATION_UUID) == 0) {
+ DBG("Body Sensor Location supported");
+
+ gatt_read_char(hr->attrib, c->value_handle,
+ read_sensor_location_cb, hr);
+ } else if (g_strcmp0(c->uuid,
+ HEART_RATE_CONTROL_POINT_UUID) == 0) {
+ DBG("Heart Rate Control Point supported");
+ hr->hrcp_val_handle = c->value_handle;
+ }
+ }
+}
+
+static void enable_measurement(gpointer data, gpointer user_data)
+{
+ struct heartrate *hr = data;
+ uint16_t handle = hr->measurement_ccc_handle;
+ uint8_t value[2];
+ char *msg;
+
+ if (hr->attrib == NULL || !handle)
+ return;
+
+ put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value);
+ msg = g_strdup("Enable measurement");
+
+ gatt_write_char(hr->attrib, handle, value, sizeof(value),
+ char_write_cb, msg);
+}
+
+static void disable_measurement(gpointer data, gpointer user_data)
+{
+ struct heartrate *hr = data;
+ uint16_t handle = hr->measurement_ccc_handle;
+ uint8_t value[2];
+ char *msg;
+
+ if (hr->attrib == NULL || !handle)
+ return;
+
+ put_le16(0x0000, value);
+ msg = g_strdup("Disable measurement");
+
+ gatt_write_char(hr->attrib, handle, value, sizeof(value),
+ char_write_cb, msg);
+}
+
+static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
+{
+ struct heartrate *hr = user_data;
+
+ DBG("");
+
+ hr->attrib = g_attrib_ref(attrib);
+
+ gatt_discover_char(hr->attrib, hr->svc_range->start, hr->svc_range->end,
+ NULL, discover_char_cb, hr);
+}
+
+static void attio_disconnected_cb(gpointer user_data)
+{
+ struct heartrate *hr = user_data;
+
+ DBG("");
+
+ if (hr->attionotid > 0) {
+ g_attrib_unregister(hr->attrib, hr->attionotid);
+ hr->attionotid = 0;
+ }
+
+ g_attrib_unref(hr->attrib);
+ hr->attrib = NULL;
+}
+
+static void watcher_exit_cb(DBusConnection *conn, void *user_data)
+{
+ struct watcher *watcher = user_data;
+ struct heartrate_adapter *hradapter = watcher->hradapter;
+
+ DBG("heartrate watcher [%s] disconnected", watcher->path);
+
+ hradapter->watchers = g_slist_remove(hradapter->watchers, watcher);
+ g_dbus_remove_watch(conn, watcher->id);
+
+ if (g_slist_length(hradapter->watchers) == 0)
+ g_slist_foreach(hradapter->devices, disable_measurement, 0);
+}
+
+static DBusMessage *register_watcher(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct heartrate_adapter *hradapter = data;
+ struct watcher *watcher;
+ const char *sender = dbus_message_get_sender(msg);
+ char *path;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ watcher = find_watcher(hradapter->watchers, sender, path);
+ if (watcher != NULL)
+ return btd_error_already_exists(msg);
+
+ watcher = g_new0(struct watcher, 1);
+ watcher->hradapter = hradapter;
+ watcher->id = g_dbus_add_disconnect_watch(conn, sender, watcher_exit_cb,
+ watcher, destroy_watcher);
+ watcher->srv = g_strdup(sender);
+ watcher->path = g_strdup(path);
+
+ if (g_slist_length(hradapter->watchers) == 0)
+ g_slist_foreach(hradapter->devices, enable_measurement, 0);
+
+ hradapter->watchers = g_slist_prepend(hradapter->watchers, watcher);
+
+ DBG("heartrate watcher [%s] registered", path);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *unregister_watcher(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct heartrate_adapter *hradapter = data;
+ struct watcher *watcher;
+ const char *sender = dbus_message_get_sender(msg);
+ char *path;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ watcher = find_watcher(hradapter->watchers, sender, path);
+ if (watcher == NULL)
+ return btd_error_does_not_exist(msg);
+
+ hradapter->watchers = g_slist_remove(hradapter->watchers, watcher);
+ g_dbus_remove_watch(conn, watcher->id);
+
+ if (g_slist_length(hradapter->watchers) == 0)
+ g_slist_foreach(hradapter->devices, disable_measurement, 0);
+
+ DBG("heartrate watcher [%s] unregistered", path);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable heartrate_manager_methods[] = {
+ { GDBUS_METHOD("RegisterWatcher",
+ GDBUS_ARGS({ "agent", "o" }), NULL,
+ register_watcher) },
+ { GDBUS_METHOD("UnregisterWatcher",
+ GDBUS_ARGS({ "agent", "o" }), NULL,
+ unregister_watcher) },
+ { }
+};
+
+static gboolean property_get_location(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct heartrate *hr = data;
+ char *loc;
+
+ if (!hr->has_location)
+ return FALSE;
+
+ loc = g_strdup(location2str(hr->location));
+
+ if (loc == NULL)
+ return FALSE;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &loc);
+
+ g_free(loc);
+
+ return TRUE;
+}
+
+static gboolean property_exists_location(const GDBusPropertyTable *property,
+ void *data)
+{
+ struct heartrate *hr = data;
+
+ if (!hr->has_location || location2str(hr->location) == NULL)
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean property_get_reset_supported(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct heartrate *hr = data;
+ dbus_bool_t has_reset = !!hr->hrcp_val_handle;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &has_reset);
+
+ return TRUE;
+}
+
+static DBusMessage *hrcp_reset(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct heartrate *hr = data;
+ uint8_t value;
+ char *vmsg;
+
+ if (!hr->hrcp_val_handle)
+ return btd_error_not_supported(msg);
+
+ if (!hr->attrib)
+ return btd_error_not_available(msg);
+
+ value = 0x01;
+ vmsg = g_strdup("Reset Control Point");
+ gatt_write_char(hr->attrib, hr->hrcp_val_handle, &value,
+ sizeof(value), char_write_cb, vmsg);
+
+ DBG("Energy Expended Value has been reset");
+
+ return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable heartrate_device_methods[] = {
+ { GDBUS_METHOD("Reset", NULL, NULL, hrcp_reset) },
+ { }
+};
+
+static const GDBusPropertyTable heartrate_device_properties[] = {
+ { "Location", "s", property_get_location, NULL,
+ property_exists_location },
+ { "ResetSupported", "b", property_get_reset_supported },
+ { }
+};
+
+static int heartrate_adapter_register(struct btd_adapter *adapter)
+{
+ struct heartrate_adapter *hradapter;
+
+ hradapter = g_new0(struct heartrate_adapter, 1);
+ hradapter->adapter = adapter;
+
+ if (!g_dbus_register_interface(btd_get_dbus_connection(),
+ adapter_get_path(adapter),
+ HEART_RATE_MANAGER_INTERFACE,
+ heartrate_manager_methods,
+ NULL, NULL, hradapter,
+ destroy_heartrate_adapter)) {
+ error("D-Bus failed to register %s interface",
+ HEART_RATE_MANAGER_INTERFACE);
+ destroy_heartrate_adapter(hradapter);
+ return -EIO;
+ }
+
+ heartrate_adapters = g_slist_prepend(heartrate_adapters, hradapter);
+
+ return 0;
+}
+
+static void heartrate_adapter_unregister(struct btd_adapter *adapter)
+{
+ struct heartrate_adapter *hradapter;
+
+ hradapter = find_heartrate_adapter(adapter);
+ if (hradapter == NULL)
+ return;
+
+ heartrate_adapters = g_slist_remove(heartrate_adapters, hradapter);
+
+ g_dbus_unregister_interface(btd_get_dbus_connection(),
+ adapter_get_path(hradapter->adapter),
+ HEART_RATE_MANAGER_INTERFACE);
+}
+
+static int heartrate_device_register(struct btd_device *device,
+ struct gatt_primary *prim)
+{
+ struct btd_adapter *adapter;
+ struct heartrate_adapter *hradapter;
+ struct heartrate *hr;
+
+ adapter = device_get_adapter(device);
+
+ hradapter = find_heartrate_adapter(adapter);
+
+ if (hradapter == NULL)
+ return -1;
+
+ hr = g_new0(struct heartrate, 1);
+ hr->dev = btd_device_ref(device);
+ hr->hradapter = hradapter;
+
+ if (!g_dbus_register_interface(btd_get_dbus_connection(),
+ device_get_path(device),
+ HEART_RATE_INTERFACE,
+ heartrate_device_methods,
+ NULL,
+ heartrate_device_properties,
+ hr, destroy_heartrate)) {
+ error("D-Bus failed to register %s interface",
+ HEART_RATE_INTERFACE);
+ destroy_heartrate(hr);
+ return -EIO;
+ }
+
+ hr->svc_range = g_new0(struct att_range, 1);
+ hr->svc_range->start = prim->range.start;
+ hr->svc_range->end = prim->range.end;
+
+ hradapter->devices = g_slist_prepend(hradapter->devices, hr);
+
+ hr->attioid = btd_device_add_attio_callback(device, attio_connected_cb,
+ attio_disconnected_cb, hr);
+
+ return 0;
+}
+
+static void heartrate_device_unregister(struct btd_device *device)
+{
+ struct btd_adapter *adapter;
+ struct heartrate_adapter *hradapter;
+ struct heartrate *hr;
+ GSList *l;
+
+ adapter = device_get_adapter(device);
+
+ hradapter = find_heartrate_adapter(adapter);
+ if (hradapter == NULL)
+ return;
+
+ l = g_slist_find_custom(hradapter->devices, device, cmp_device);
+ if (l == NULL)
+ return;
+
+ hr = l->data;
+
+ hradapter->devices = g_slist_remove(hradapter->devices, hr);
+
+ g_dbus_unregister_interface(btd_get_dbus_connection(),
+ device_get_path(device), HEART_RATE_INTERFACE);
+}
+
+static int heartrate_adapter_probe(struct btd_profile *p,
+ struct btd_adapter *adapter)
+{
+ return heartrate_adapter_register(adapter);
+}
+
+static void heartrate_adapter_remove(struct btd_profile *p,
+ struct btd_adapter *adapter)
+{
+ heartrate_adapter_unregister(adapter);
+}
+
+static int heartrate_device_probe(struct btd_service *service)
+{
+ struct btd_device *device = btd_service_get_device(service);
+ struct gatt_primary *prim;
+
+ prim = btd_device_get_primary(device, HEART_RATE_UUID);
+ if (prim == NULL)
+ return -EINVAL;
+
+ return heartrate_device_register(device, prim);
+}
+
+static void heartrate_device_remove(struct btd_service *service)
+{
+ struct btd_device *device = btd_service_get_device(service);
+
+ heartrate_device_unregister(device);
+}
+
+static struct btd_profile hrp_profile = {
+ .name = "Heart Rate GATT Driver",
+ .remote_uuid = HEART_RATE_UUID,
+
+ .device_probe = heartrate_device_probe,
+ .device_remove = heartrate_device_remove,
+
+ .adapter_probe = heartrate_adapter_probe,
+ .adapter_remove = heartrate_adapter_remove,
+};
+
+static int heartrate_init(void)
+{
+ return btd_profile_register(&hrp_profile);
+}
+
+static void heartrate_exit(void)
+{
+ btd_profile_unregister(&hrp_profile);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(heartrate, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+ heartrate_init, heartrate_exit)
diff --git a/profiles/input/device.c b/profiles/input/device.c
index a494ea2e..ff553cb3 100755
--- a/profiles/input/device.c
+++ b/profiles/input/device.c
@@ -87,6 +87,9 @@ struct input_device {
uint8_t report_req_pending;
guint report_req_timer;
uint32_t report_rsp_id;
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+ char *role;
+#endif
};
static int idle_timeout = 0;
@@ -333,8 +336,12 @@ static gboolean intr_watch_cb(GIOChannel *chan, GIOCondition cond, gpointer data
btd_service_disconnecting_complete(idev->service, 0);
/* Enter the auto-reconnect mode if needed */
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+ if (idev->role == NULL)
+ input_device_enter_reconnect_mode(idev);
+#else
input_device_enter_reconnect_mode(idev);
-
+#endif
return FALSE;
}
@@ -1002,10 +1009,21 @@ cleanup:
static bool is_connected(struct input_device *idev)
{
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+ if (idev->role == NULL) {
+ if (idev->uhid)
+ return (idev->intr_io != NULL && idev->ctrl_io != NULL);
+ else
+ return ioctl_is_connected(idev);
+ } else {
+ return (idev->intr_io != NULL && idev->ctrl_io != NULL);
+ }
+#else
if (idev->uhid)
return (idev->intr_io != NULL && idev->ctrl_io != NULL);
else
return ioctl_is_connected(idev);
+#endif
}
static int connection_disconnect(struct input_device *idev, uint32_t flags)
@@ -1019,6 +1037,10 @@ static int connection_disconnect(struct input_device *idev, uint32_t flags)
if (idev->ctrl_io)
g_io_channel_shutdown(idev->ctrl_io, TRUE, NULL);
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+ if (idev->role != NULL)
+ btd_service_disconnecting_complete(idev->service, 0);
+#endif
if (idev->uhid)
return 0;
else
@@ -1031,10 +1053,17 @@ static int input_device_connected(struct input_device *idev)
if (idev->intr_io == NULL || idev->ctrl_io == NULL)
return -ENOTCONN;
-
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+ if (idev->role == NULL) {
+ err = hidp_add_connection(idev);
+ if (err < 0)
+ return err;
+ }
+#else
err = hidp_add_connection(idev);
if (err < 0)
return err;
+#endif
btd_service_connecting_complete(idev->service, 0);
@@ -1052,11 +1081,19 @@ static void interrupt_connect_cb(GIOChannel *chan, GError *conn_err,
err = -EIO;
goto failed;
}
-
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+ if (idev->role == NULL) {
+ err = input_device_connected(idev);
+ if (err < 0)
+ goto failed;
+ } else {
+ btd_service_connecting_complete(idev->service, 0);
+ }
+#else
err = input_device_connected(idev);
if (err < 0)
goto failed;
-
+#endif
if (idev->uhid)
cond |= G_IO_IN;
@@ -1230,7 +1267,9 @@ int input_device_connect(struct btd_service *service)
DBG("");
idev = btd_service_get_user_data(service);
-
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+ DBG("Role=%s", idev->role);
+#endif
if (idev->ctrl_io)
return -EBUSY;
@@ -1251,7 +1290,9 @@ int input_device_disconnect(struct btd_service *service)
flags = device_is_temporary(idev->device) ?
(1 << HIDP_VIRTUAL_CABLE_UNPLUG) : 0;
-
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+ DBG("Role=%s", idev->role);
+#endif
err = connection_disconnect(idev, flags);
if (err < 0)
return err;
@@ -1328,6 +1369,27 @@ static struct input_device *input_device_new(struct btd_service *service)
return idev;
}
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+static struct input_device *input_device_role_new(struct btd_service *service)
+{
+ struct btd_device *device = btd_service_get_device(service);
+ const char *path = device_get_path(device);
+ struct btd_adapter *adapter = device_get_adapter(device);
+ struct input_device *idev;
+
+ idev = g_new0(struct input_device, 1);
+ bacpy(&idev->src, btd_adapter_get_address(adapter));
+ bacpy(&idev->dst, device_get_address(device));
+ idev->service = btd_service_ref(service);
+ idev->device = btd_device_ref(device);
+ idev->path = g_strdup(path);
+ idev->role = g_strdup("device");
+ idev->disable_sdp = 0;
+ idev->uhid = NULL;
+ return idev;
+}
+#endif
+
static gboolean property_get_reconnect_mode(
const GDBusPropertyTable *property,
DBusMessageIter *iter, void *data)
@@ -1345,6 +1407,37 @@ static const GDBusPropertyTable input_properties[] = {
{ }
};
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+static DBusMessage *hid_device_fd(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ struct input_device *idev = user_data;
+ GError *gerr = NULL;
+ DBusMessage *reply;
+ int ctrl_fd = -1;
+ int intr_fd = -1;
+ if (idev->ctrl_io == NULL || idev->intr_io == NULL) {
+ DBG("Return error reply");
+ reply = g_dbus_create_error(msg, ERROR_INTERFACE ".InputError",
+ "%s", "NotConnected");
+ g_error_free(gerr);
+ } else {
+ ctrl_fd = g_io_channel_unix_get_fd(idev->ctrl_io);
+ intr_fd = g_io_channel_unix_get_fd(idev->intr_io);
+ reply = g_dbus_create_reply(msg, DBUS_TYPE_UNIX_FD,
+ &ctrl_fd, DBUS_TYPE_UNIX_FD, &intr_fd ,DBUS_TYPE_INVALID);
+ }
+
+ return reply;
+}
+static const GDBusMethodTable input_device_methods[] = {
+ { GDBUS_ASYNC_METHOD("GetFD",
+ NULL, GDBUS_ARGS({ "fd", "h" } , {"fd", "h"}),
+ hid_device_fd) },
+ { }
+};
+#endif
+
int input_device_register(struct btd_service *service)
{
struct btd_device *device = btd_service_get_device(service);
@@ -1381,6 +1474,34 @@ int input_device_register(struct btd_service *service)
return 0;
}
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+int input_device_role_register(struct btd_service *service)
+{
+ struct btd_device *device = btd_service_get_device(service);
+ const char *path = device_get_path(device);
+ struct input_device *idev;
+
+ DBG("%s", path);
+
+ idev = input_device_role_new(service);
+ if (!idev)
+ return -EINVAL;
+ if (g_dbus_register_interface(btd_get_dbus_connection(),
+ idev->path, INPUT_INTERFACE,
+ input_device_methods, NULL,
+ NULL, idev,
+ NULL) == FALSE) {
+ error("Unable to register %s interface", INPUT_INTERFACE);
+ input_device_free(idev);
+ return -EINVAL;
+ }
+ btd_service_set_user_data(service, idev);
+
+ return 0;
+}
+
+#endif
+
static struct input_device *find_device(const bdaddr_t *src,
const bdaddr_t *dst)
{
@@ -1398,6 +1519,25 @@ static struct input_device *find_device(const bdaddr_t *src,
return btd_service_get_user_data(service);
}
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+static struct input_device *find_device_role(const bdaddr_t *src,
+ const bdaddr_t *dst)
+{
+ struct btd_device *device;
+ struct btd_service *service;
+
+ device = btd_adapter_find_device(adapter_find(src), dst, BDADDR_BREDR);
+ if (device == NULL)
+ return NULL;
+
+ service = btd_device_get_service(device, HID_DEVICE_UUID);
+ if (service == NULL)
+ return NULL;
+
+ return btd_service_get_user_data(service);
+}
+#endif
+
void input_device_unregister(struct btd_service *service)
{
struct btd_device *device = btd_service_get_device(service);
@@ -1412,6 +1552,19 @@ void input_device_unregister(struct btd_service *service)
input_device_free(idev);
}
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+void input_device_role_unregister(struct btd_service *service)
+{
+ struct btd_device *device = btd_service_get_device(service);
+ const char *path = device_get_path(device);
+ struct input_device *idev = btd_service_get_user_data(service);
+
+ DBG("%s", path);
+
+ input_device_free(idev);
+}
+#endif
+
static int input_device_connadd(struct input_device *idev)
{
int err;
@@ -1443,6 +1596,16 @@ bool input_device_exists(const bdaddr_t *src, const bdaddr_t *dst)
return false;
}
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+bool input_device_role_exists(const bdaddr_t *src, const bdaddr_t *dst)
+{
+ if (find_device_role(src, dst))
+ return true;
+
+ return false;
+}
+#endif
+
int input_device_set_channel(const bdaddr_t *src, const bdaddr_t *dst, int psm,
GIOChannel *io)
{
@@ -1480,6 +1643,58 @@ int input_device_set_channel(const bdaddr_t *src, const bdaddr_t *dst, int psm,
return 0;
}
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+int input_device_role_set_channel(const bdaddr_t *src, const bdaddr_t *dst, int psm,
+ GIOChannel *io)
+{
+ struct input_device *idev = find_device_role(src, dst);
+ GIOCondition cond = G_IO_HUP | G_IO_ERR | G_IO_NVAL;
+
+ DBG("idev %p psm %d", idev, psm);
+
+ if (!idev)
+ return -ENOENT;
+
+ switch (psm) {
+ case L2CAP_PSM_HIDP_CTRL:
+ if (idev->ctrl_io)
+ return -EALREADY;
+ idev->ctrl_io = g_io_channel_ref(io);
+ idev->ctrl_watch = g_io_add_watch(idev->ctrl_io, cond,
+ ctrl_watch_cb, idev);
+ break;
+ case L2CAP_PSM_HIDP_INTR:
+ if (idev->intr_io)
+ return -EALREADY;
+ idev->intr_io = g_io_channel_ref(io);
+ idev->intr_watch = g_io_add_watch(idev->intr_io, cond,
+ intr_watch_cb, idev);
+ break;
+ }
+ if (idev->intr_io && idev->ctrl_io) {
+ btd_service_connecting_complete(idev->service, 0);
+ }
+ return 0;
+}
+
+int input_device_role_close_channels(const bdaddr_t *src, const bdaddr_t *dst)
+{
+ struct input_device *idev = find_device(src, dst);
+
+ if (!idev)
+ return -ENOENT;
+
+ if (idev->intr_io)
+ g_io_channel_shutdown(idev->intr_io, TRUE, NULL);
+
+ if (idev->ctrl_io)
+ g_io_channel_shutdown(idev->ctrl_io, TRUE, NULL);
+
+ return 0;
+}
+
+#endif
+
int input_device_close_channels(const bdaddr_t *src, const bdaddr_t *dst)
{
struct input_device *idev = find_device(src, dst);
diff --git a/profiles/input/device.h b/profiles/input/device.h
index 51a9aee1..20aba315 100755
--- a/profiles/input/device.h
+++ b/profiles/input/device.h
@@ -33,6 +33,15 @@ void input_enable_userspace_hid(bool state);
int input_device_register(struct btd_service *service);
void input_device_unregister(struct btd_service *service);
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+int input_device_role_register(struct btd_service *service);
+void input_device_role_unregister(struct btd_service *service);
+bool input_device_role_exists(const bdaddr_t *src, const bdaddr_t *dst);
+int input_device_role_set_channel(const bdaddr_t *src, const bdaddr_t *dst, int psm,
+ GIOChannel *io);
+int input_device_role_close_channels(const bdaddr_t *src, const bdaddr_t *dst);
+#endif
+
bool input_device_exists(const bdaddr_t *src, const bdaddr_t *dst);
int input_device_set_channel(const bdaddr_t *src, const bdaddr_t *dst, int psm,
GIOChannel *io);
diff --git a/profiles/input/manager.c b/profiles/input/manager.c
index 1d31b065..2aaeb153 100755
--- a/profiles/input/manager.c
+++ b/profiles/input/manager.c
@@ -53,6 +53,19 @@ static void hid_server_remove(struct btd_profile *p,
{
server_stop(btd_adapter_get_address(adapter));
}
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+static int hid_device_probe(struct btd_profile *p, struct btd_adapter *adapter)
+{
+ DBG("hid device probe");
+ return server_start(btd_adapter_get_address(adapter));
+}
+
+static void hid_device_remove(struct btd_profile *p,
+ struct btd_adapter *adapter)
+{
+ server_stop(btd_adapter_get_address(adapter));
+}
+#endif
static struct btd_profile input_profile = {
.name = "input-hid",
@@ -70,6 +83,24 @@ static struct btd_profile input_profile = {
.adapter_remove = hid_server_remove,
};
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+static struct btd_profile input_device_profile = {
+ .name = "hid-device",
+ .local_uuid = HID_DEVICE_UUID,
+ .remote_uuid = HID_DEVICE_UUID,
+
+ .auto_connect = false,
+ .connect = input_device_connect,
+ .disconnect = input_device_disconnect,
+
+ .device_probe = input_device_role_register,
+ .device_remove = input_device_role_unregister,
+
+ .adapter_probe = hid_device_probe,
+ .adapter_remove = hid_device_remove,
+};
+#endif
+
static GKeyFile *load_config_file(const char *file)
{
GKeyFile *keyfile;
@@ -117,7 +148,9 @@ static int input_init(void)
}
btd_profile_register(&input_profile);
-
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+ btd_profile_register(&input_device_profile);
+#endif
if (config)
g_key_file_free(config);
@@ -127,6 +160,9 @@ static int input_init(void)
static void input_exit(void)
{
btd_profile_unregister(&input_profile);
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+ btd_profile_unregister(&input_device_profile);
+#endif
}
BLUETOOTH_PLUGIN_DEFINE(input, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
diff --git a/profiles/input/server.c b/profiles/input/server.c
index eb3fcf84..2ee3b9bc 100755
--- a/profiles/input/server.c
+++ b/profiles/input/server.c
@@ -57,6 +57,9 @@ struct input_server {
GIOChannel *ctrl;
GIOChannel *intr;
struct confirm_data *confirm;
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+ char *role;
+#endif
};
static int server_cmp(gconstpointer s, gconstpointer user_data)
@@ -182,7 +185,14 @@ static void connect_event_cb(GIOChannel *chan, GError *err, gpointer data)
sixaxis_browse_sdp(&src, &dst, chan, psm);
return;
}
-
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+ if (ret == -ENOENT) {
+ DBG("Connection request for device role");
+ ret = input_device_role_set_channel(&src, &dst, psm, chan);
+ if (ret == 0)
+ return;
+ }
+#endif
error("Refusing input device connect: %s (%d)", strerror(-ret), -ret);
/* Send unplug virtual cable to unknown devices */
@@ -208,8 +218,15 @@ static void auth_callback(DBusError *derr, void *user_data)
}
if (!input_device_exists(&server->src, &confirm->dst) &&
- !dev_is_sixaxis(&server->src, &confirm->dst))
+ !dev_is_sixaxis(&server->src, &confirm->dst)) {
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+ if (!input_device_role_exists(&server->src, &confirm->dst)) {
+ return;
+ }
+#else
return;
+#endif
+ }
if (!bt_io_accept(confirm->io, connect_event_cb, server, NULL, &err)) {
error("bt_io_accept: %s", err->message);
@@ -260,8 +277,15 @@ static void confirm_event_cb(GIOChannel *chan, gpointer user_data)
}
if (!input_device_exists(&src, &dst) && !dev_is_sixaxis(&src, &dst)) {
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+ if (!input_device_role_exists(&src, &dst)) {
+ error("Refusing connection from %s: unknown device", addr);
+ goto drop;
+ }
+#else
error("Refusing connection from %s: unknown device", addr);
goto drop;
+#endif
}
server->confirm = g_new0(struct confirm_data, 1);
@@ -344,3 +368,59 @@ void server_stop(const bdaddr_t *src)
servers = g_slist_remove(servers, server);
g_free(server);
}
+
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+int server_device_start(const bdaddr_t *src)
+{
+ struct input_server *server;
+ GError *err = NULL;
+
+ server = g_new0(struct input_server, 1);
+ bacpy(&server->src, src);
+
+ server->ctrl = bt_io_listen(connect_event_cb, NULL,
+ server, NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR, src,
+ BT_IO_OPT_PSM, L2CAP_PSM_HIDP_CTRL,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+ BT_IO_OPT_INVALID);
+ if (!server->ctrl) {
+ error("Failed to listen on control channel");
+ }
+
+ server->intr = bt_io_listen(NULL, confirm_event_cb,
+ server, NULL, &err,
+ BT_IO_OPT_SOURCE_BDADDR, src,
+ BT_IO_OPT_PSM, L2CAP_PSM_HIDP_INTR,
+ BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW,
+ BT_IO_OPT_INVALID);
+ if (!server->intr) {
+ error("Failed to listen on interrupt channel");
+ }
+ server->role = strdup("device");
+ servers = g_slist_append(servers, server);
+
+ return 0;
+}
+
+void server_device_stop(const bdaddr_t *src)
+{
+ struct input_server *server;
+ GSList *l;
+
+ l = g_slist_find_custom(servers, src, server_cmp);
+ if (!l)
+ return;
+
+ server = l->data;
+
+ g_io_channel_shutdown(server->intr, TRUE, NULL);
+ g_io_channel_unref(server->intr);
+
+ g_io_channel_shutdown(server->ctrl, TRUE, NULL);
+ g_io_channel_unref(server->ctrl);
+ g_free(server->role);
+ servers = g_slist_remove(servers, server);
+ g_free(server);
+}
+#endif
diff --git a/profiles/network/bnep.c b/profiles/network/bnep.c
index 9bf0b187..86d1eb96 100755
--- a/profiles/network/bnep.c
+++ b/profiles/network/bnep.c
@@ -54,6 +54,19 @@
static int ctl;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+/* Compatibility with old ioctls */
+#define OLD_BNEPCONADD 1
+#define OLD_BNEPCONDEL 2
+#define OLD_BNEPGETCONLIST 3
+#define OLD_BNEPGETCONINFO 4
+
+static unsigned long bnepconnadd;
+static unsigned long bnepconndel;
+static unsigned long bnepgetconnlist;
+static unsigned long bnepgetconninfo;
+#endif
+
struct __service_16 {
uint16_t dst;
uint16_t src;
@@ -88,7 +101,30 @@ int bnep_init(void)
return err;
}
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+/* Temporary ioctl compatibility hack */
+{
+ struct bnep_connlist_req req;
+ struct bnep_conninfo ci[1];
+
+ req.cnum = 1;
+ req.ci = ci;
+
+ if (!ioctl(ctl, BNEPGETCONNLIST, &req)) {
+ /* New ioctls */
+ bnepconnadd = BNEPCONNADD;
+ bnepconndel = BNEPCONNDEL;
+ bnepgetconnlist = BNEPGETCONNLIST;
+ bnepgetconninfo = BNEPGETCONNINFO;
+ } else {
+ /* Old ioctls */
+ bnepconnadd = OLD_BNEPCONADD;
+ bnepconndel = OLD_BNEPCONDEL;
+ bnepgetconnlist = OLD_BNEPGETCONLIST;
+ bnepgetconninfo = OLD_BNEPGETCONINFO;
+ }
+}
+#endif
return 0;
}
@@ -179,6 +215,11 @@ static int bnep_if_down(const char *devname)
sk = socket(AF_INET, SOCK_DGRAM, 0);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (sk < 0)
+ return -1;
+#endif
+
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, devname, IF_NAMESIZE - 1);
@@ -420,11 +461,12 @@ void bnep_disconnect(struct bnep *session)
if (!session)
return;
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
if (session->watch > 0) {
g_source_remove(session->watch);
session->watch = 0;
}
-
+#endif
if (session->io) {
g_io_channel_unref(session->io);
session->io = NULL;
@@ -434,6 +476,7 @@ void bnep_disconnect(struct bnep *session)
bnep_conndel(&session->dst_addr);
}
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
static int bnep_add_to_bridge(const char *devname, const char *bridge)
{
int ifindex;
@@ -448,7 +491,15 @@ static int bnep_add_to_bridge(const char *devname, const char *bridge)
sk = socket(AF_INET, SOCK_STREAM, 0);
if (sk < 0)
return -1;
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ err = ioctl(sk, SIOCBRADDBR, bridge);
+ if (err < 0)
+ {
+ info("bridge create err: %d", err);
+ close(sk);
+ return -errno;
+ }
+#endif
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_name, bridge, IFNAMSIZ - 1);
ifr.ifr_ifindex = ifindex;
@@ -465,6 +516,7 @@ static int bnep_add_to_bridge(const char *devname, const char *bridge)
return err;
}
+#endif
static int bnep_del_from_bridge(const char *devname, const char *bridge)
{
@@ -602,6 +654,20 @@ static uint16_t bnep_setup_decode(int sk, struct bnep_setup_conn_req *req,
return BNEP_CONN_INVALID_DST;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+int bnep_if_down_wrapper(const char *devname)
+{
+ bnep_if_down(devname);
+ return 0;
+}
+
+int bnep_conndel_wrapper(const bdaddr_t *dst)
+{
+ bnep_conndel(dst);
+ return 0;
+}
+#endif
+
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)
@@ -622,12 +688,14 @@ static int bnep_server_add_legacy(int sk, uint16_t dst, char *bridge,
goto reply;
}
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
err = bnep_add_to_bridge(iface, bridge);
if (err < 0) {
bnep_conndel(addr);
rsp = BNEP_CONN_NOT_ALLOWED;
goto reply;
}
+#endif
err = bnep_if_up(iface);
if (err < 0) {
@@ -697,9 +765,11 @@ int bnep_server_add(int sk, char *bridge, char *iface, const bdaddr_t *addr,
goto failed;
}
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
err = bnep_add_to_bridge(iface, bridge);
if (err < 0)
goto failed_conn;
+#endif
err = bnep_if_up(iface);
if (err < 0)
@@ -710,7 +780,9 @@ int bnep_server_add(int sk, char *bridge, char *iface, const bdaddr_t *addr,
failed_bridge:
bnep_del_from_bridge(iface, bridge);
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
failed_conn:
+#endif
bnep_conndel(addr);
return err;
diff --git a/profiles/network/bnep.h b/profiles/network/bnep.h
index e9f4c1cf..d34ed03a 100755
--- a/profiles/network/bnep.h
+++ b/profiles/network/bnep.h
@@ -40,3 +40,7 @@ void bnep_disconnect(struct bnep *session);
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);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+int bnep_if_down_wrapper(const char *devname);
+int bnep_conndel_wrapper(const bdaddr_t *dst);
+#endif
diff --git a/profiles/network/connection.c b/profiles/network/connection.c
index 5305ace8..53d35fe6 100755
--- a/profiles/network/connection.c
+++ b/profiles/network/connection.c
@@ -123,12 +123,14 @@ static void bnep_disconn_cb(gpointer data)
DBusConnection *conn = btd_get_dbus_connection();
const char *path = device_get_path(nc->peer->device);
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
g_dbus_emit_property_changed(conn, path,
NETWORK_PEER_INTERFACE, "Connected");
g_dbus_emit_property_changed(conn, path,
NETWORK_PEER_INTERFACE, "Interface");
g_dbus_emit_property_changed(conn, path,
NETWORK_PEER_INTERFACE, "UUID");
+#endif
device_remove_disconnect_watch(nc->peer->device, nc->dc_id);
nc->dc_id = 0;
@@ -137,6 +139,14 @@ static void bnep_disconn_cb(gpointer data)
info("%s disconnected", nc->dev);
nc->state = DISCONNECTED;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ g_dbus_emit_property_changed(conn, path,
+ NETWORK_PEER_INTERFACE, "Connected");
+ g_dbus_emit_property_changed(conn, path,
+ NETWORK_PEER_INTERFACE, "Interface");
+ g_dbus_emit_property_changed(conn, path,
+ NETWORK_PEER_INTERFACE, "UUID");
+#endif
memset(nc->dev, 0, sizeof(nc->dev));
strncpy(nc->dev, BNEP_INTERFACE, 16);
nc->dev[15] = '\0';
@@ -179,9 +189,10 @@ static void cancel_connection(struct network_conn *nc, int err)
if (nc->state == CONNECTED)
bnep_disconnect(nc->session);
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
bnep_free(nc->session);
nc->session = NULL;
-
+#endif
nc->state = DISCONNECTED;
}
@@ -226,6 +237,9 @@ static void bnep_conn_cb(char *iface, int err, void *data)
conn = btd_get_dbus_connection();
path = device_get_path(nc->peer->device);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ nc->state = CONNECTED;
+#endif
g_dbus_emit_property_changed(conn, path,
NETWORK_PEER_INTERFACE, "Connected");
g_dbus_emit_property_changed(conn, path,
@@ -233,7 +247,9 @@ static void bnep_conn_cb(char *iface, int err, void *data)
g_dbus_emit_property_changed(conn, path,
NETWORK_PEER_INTERFACE, "UUID");
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
nc->state = CONNECTED;
+#endif
nc->dc_id = device_add_disconnect_watch(nc->peer->device, disconnect_cb,
nc, NULL);
diff --git a/profiles/network/server.c b/profiles/network/server.c
index e69ffaf5..19206a11 100755
--- a/profiles/network/server.c
+++ b/profiles/network/server.c
@@ -86,6 +86,11 @@ struct network_server {
static GSList *adapters = NULL;
static gboolean security = TRUE;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static gboolean server_disconnected_cb(GIOChannel *chan,
+ GIOCondition cond, gpointer user_data);
+#endif
+
static struct network_adapter *find_adapter(GSList *list,
struct btd_adapter *adapter)
{
@@ -151,6 +156,38 @@ static struct network_server *find_server_by_uuid(GSList *list,
return NULL;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static struct network_session *find_session(GSList *list, GIOChannel *io)
+{
+ GSList *l;
+
+ for (l = list; l; l = l->next) {
+ struct network_session *session = l->data;
+
+ if (session && session->io == io)
+ return session;
+ }
+
+ return NULL;
+}
+
+static struct network_session *find_session_by_addr(GSList *list,
+ bdaddr_t dst_addr)
+{
+ GSList *l;
+
+ for (l = list; l; l = l->next) {
+ struct network_session *session = l->data;
+
+ if (!bacmp(&session->dst, &dst_addr))
+ return session;
+
+ }
+
+ return NULL;
+}
+#endif
+
static sdp_record_t *server_record_new(const char *name, uint16_t id)
{
sdp_list_t *svclass, *pfseq, *apseq, *root, *aproto;
@@ -160,8 +197,13 @@ static sdp_record_t *server_record_new(const char *name, uint16_t id)
sdp_data_t *v, *p;
uint16_t psm = BNEP_PSM, version = 0x0100;
uint16_t security_desc = (security ? 0x0001 : 0x0000);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ uint16_t net_access_type = 0x000a;
+ uint32_t max_net_access_rate = 0x001312d0;
+#else
uint16_t net_access_type = 0xfffe;
uint32_t max_net_access_rate = 0;
+#endif
const char *desc = "Network service";
sdp_record_t *record;
@@ -303,6 +345,56 @@ static void setup_destroy(void *user_data)
session_free(setup);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static gboolean server_disconnected_cb(GIOChannel *chan,
+ GIOCondition cond, gpointer user_data)
+{
+ struct network_server *ns = NULL;
+ struct network_session *session = NULL;
+ char address[20] = {0};
+ const char* paddr = address;
+ char *name_str = NULL;
+
+ info("server_disconnected_cb");
+
+ if (!user_data)
+ return FALSE;
+
+ ns = (struct network_server *) user_data;
+
+ session = find_session(ns->sessions, chan);
+ if (session) {
+ name_str = g_strdup(session->dev);
+ ba2str(&session->dst, address);
+ } else {
+ info("Session is not exist!");
+ name_str = g_strdup("bnep");
+ }
+
+ g_dbus_emit_signal(btd_get_dbus_connection(),
+ adapter_get_path(ns->na->adapter),
+ NETWORK_SERVER_INTERFACE, "PeerDisconnected",
+ DBUS_TYPE_STRING, &name_str,
+ DBUS_TYPE_STRING, &paddr,
+ DBUS_TYPE_INVALID);
+
+ if (session) {
+ ns->sessions = g_slist_remove(ns->sessions, session);
+ session_free(session);
+ }
+
+ if (g_slist_length(ns->sessions) == 0 &&
+ name_str != NULL) {
+ bnep_if_down_wrapper(name_str);
+ ns->sessions = NULL;
+ }
+
+ g_free(name_str);
+
+ return FALSE;
+}
+#endif
+
static gboolean bnep_setup(GIOChannel *chan,
GIOCondition cond, gpointer user_data)
{
@@ -381,6 +473,30 @@ static gboolean bnep_setup(GIOChannel *chan,
packet, n) < 0)
error("BNEP server cannot be added");
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+if (ns) {
+ /* Emit connected signal to BT application */
+ const gchar *adapter_path = adapter_get_path(na->adapter);
+ const char *pdev = na->setup->dev;
+ char address[24] = { 0 };
+ char *paddr = address;
+
+ ba2str(&na->setup->dst, paddr);
+
+ ns->sessions = g_slist_append(ns->sessions, na->setup);
+
+ g_dbus_emit_signal(btd_get_dbus_connection(), adapter_path,
+ NETWORK_SERVER_INTERFACE, "PeerConnected",
+ DBUS_TYPE_STRING, &pdev,
+ DBUS_TYPE_STRING, &paddr,
+ DBUS_TYPE_INVALID);
+
+ na->setup->watch = g_io_add_watch_full(chan, G_PRIORITY_DEFAULT,
+ G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ server_disconnected_cb, ns, NULL);
+}
+#endif
+
na->setup = NULL;
return FALSE;
@@ -515,9 +631,11 @@ static void server_remove_sessions(struct network_server *ns)
bnep_server_delete(ns->bridge, session->dev, &session->dst);
}
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
g_slist_free_full(ns->sessions, session_free);
ns->sessions = NULL;
+#endif
}
static void server_disconnect(DBusConnection *conn, void *user_data)
@@ -590,6 +708,11 @@ static DBusMessage *unregister_server(DBusConnection *conn,
if (!ns)
return btd_error_failed(msg, "Invalid UUID");
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (!ns->record_id)
+ return btd_error_not_available(msg);
+#endif
+
reply = dbus_message_new_method_return(msg);
if (!reply)
return NULL;
@@ -645,6 +768,94 @@ static void path_unregister(void *data)
adapter_free(na);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static DBusMessage *disconnect_device(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct network_adapter *na = data;
+ struct network_server *ns;
+ struct network_session *session;
+ const char *addr = NULL;
+ bdaddr_t dst_addr;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &addr,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ ns = find_server(na->servers, BNEP_SVC_NAP);
+
+ str2ba(addr, &dst_addr);
+ session = find_session_by_addr(ns->sessions, dst_addr);
+
+ if (session == NULL)
+ return btd_error_failed(msg, "No active session");
+
+ if (session->io == NULL)
+ return btd_error_not_connected(msg);
+
+ bnep_if_down_wrapper(session->dev);
+ bnep_conndel_wrapper(&dst_addr);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *get_properties(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct network_adapter *na = data;
+ struct network_server *ns;
+ struct network_session *session;
+ const char *addr = NULL;
+ bdaddr_t dst_addr;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+ dbus_bool_t connected;
+ const char *property;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &addr,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+ ns = find_server(na->servers, BNEP_SVC_NAP);
+
+ str2ba(addr, &dst_addr);
+ session = find_session_by_addr(ns->sessions, dst_addr);
+
+ connected = (session && session->io) ? TRUE : FALSE;
+ dict_append_entry(&dict, "Connected", DBUS_TYPE_BOOLEAN, &connected);
+
+ /* Interface */
+ property = session ? session->dev : "";
+ dict_append_entry(&dict, "Interface", DBUS_TYPE_STRING, &property);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ return reply;
+}
+#endif
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static GDBusSignalTable server_signals[] = {
+ { GDBUS_SIGNAL("PeerConnected",
+ GDBUS_ARGS({ "device", "s" }, { "address", "s" })) },
+ { GDBUS_SIGNAL("PeerDisconnected",
+ GDBUS_ARGS({ "device", "s" }, { "address", "s" })) },
+ { }
+};
+#endif
+
static const GDBusMethodTable server_methods[] = {
{ GDBUS_METHOD("Register",
GDBUS_ARGS({ "uuid", "s" }, { "bridge", "s" }), NULL,
@@ -652,6 +863,15 @@ static const GDBusMethodTable server_methods[] = {
{ GDBUS_METHOD("Unregister",
GDBUS_ARGS({ "uuid", "s" }), NULL,
unregister_server) },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ { GDBUS_METHOD("Disconnect",
+ GDBUS_ARGS({ "address", "s" }), NULL,
+ disconnect_device) },
+ { GDBUS_METHOD("GetProperties",
+ GDBUS_ARGS({ "address", "s" }),
+ GDBUS_ARGS({ "properties", "a{sv}" }),
+ get_properties) },
+#endif
{ }
};
@@ -709,6 +929,7 @@ int server_register(struct btd_adapter *adapter, uint16_t id)
if (g_slist_length(na->servers) > 0)
goto done;
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
if (!g_dbus_register_interface(btd_get_dbus_connection(), path,
NETWORK_SERVER_INTERFACE,
server_methods, NULL, NULL, na,
@@ -718,6 +939,20 @@ int server_register(struct btd_adapter *adapter, uint16_t id)
server_free(ns);
return -1;
}
+#else
+ ns->sessions = NULL;
+
+ if (!g_dbus_register_interface(btd_get_dbus_connection(),
+ path, NETWORK_SERVER_INTERFACE,
+ server_methods, server_signals,
+ NULL,
+ na, path_unregister)) {
+ error("D-Bus failed to register %s interface",
+ NETWORK_SERVER_INTERFACE);
+ server_free(ns);
+ return -1;
+ }
+#endif
DBG("Registered interface %s on path %s", NETWORK_SERVER_INTERFACE,
path);
diff --git a/profiles/proximity/immalert.c b/profiles/proximity/immalert.c
new file mode 100755
index 00000000..adf91404
--- /dev/null
+++ b/profiles/proximity/immalert.c
@@ -0,0 +1,458 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012 Texas Instruments Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdbool.h>
+
+#include <glib.h>
+
+#include <dbus/dbus.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
+#include "src/log.h"
+#include "src/adapter.h"
+#include "attrib/gattrib.h"
+#include "attrib/att.h"
+#include "attrib/gatt.h"
+#include "attrib/att-database.h"
+#include "attrib/gatt-service.h"
+#include "src/attrib-server.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/attio.h"
+#include "src/dbus-common.h"
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#include "src/shared/queue.h"
+#include "src/shared/gatt-db.h"
+#include "src/shared/att.h"
+ #include "btio/btio.h"
+#include "src/gatt-database.h"
+#endif
+
+#include "reporter.h"
+#include "immalert.h"
+
+struct imm_alert_adapter {
+ struct btd_adapter *adapter;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ struct gatt_db_attribute *imservice;
+#endif
+ GSList *connected_devices;
+};
+
+struct connected_device {
+ struct btd_device *device;
+ struct imm_alert_adapter *adapter;
+ uint8_t alert_level;
+ guint callback_id;
+};
+
+static GSList *imm_alert_adapters;
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static bool get_dest_info(struct bt_att *att, bdaddr_t *dst, uint8_t *dst_type)
+{
+ GIOChannel *io = NULL;
+ GError *gerr = NULL;
+
+ io = g_io_channel_unix_new(bt_att_get_fd(att));
+ if (!io)
+ return false;
+
+ bt_io_get(io, &gerr, BT_IO_OPT_DEST_BDADDR, dst,
+ BT_IO_OPT_DEST_TYPE, dst_type,
+ BT_IO_OPT_INVALID);
+
+ if (gerr) {
+ error("gatt: bt_io_get: %s", gerr->message);
+ g_error_free(gerr);
+ g_io_channel_unref(io);
+ return false;
+ }
+
+ g_io_channel_unref(io);
+ return true;
+}
+#endif
+
+static int imdevice_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct connected_device *condev = a;
+ const struct btd_device *device = b;
+
+ if (condev->device == device)
+ return 0;
+
+ return -1;
+}
+
+static struct connected_device *
+find_connected_device(struct imm_alert_adapter *ia, struct btd_device *device)
+{
+ GSList *l = g_slist_find_custom(ia->connected_devices, device,
+ imdevice_cmp);
+ if (!l)
+ return NULL;
+
+ return l->data;
+}
+
+static int imadapter_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct imm_alert_adapter *imadapter = a;
+ const struct btd_adapter *adapter = b;
+
+ if (imadapter->adapter == adapter)
+ return 0;
+
+ return -1;
+}
+
+static struct imm_alert_adapter *
+find_imm_alert_adapter(struct btd_adapter *adapter)
+{
+ GSList *l = g_slist_find_custom(imm_alert_adapters, adapter,
+ imadapter_cmp);
+ if (!l)
+ return NULL;
+
+ return l->data;
+}
+
+const char *imm_alert_get_level(struct btd_device *device)
+{
+ struct imm_alert_adapter *imadapter;
+ struct connected_device *condev;
+
+ if (!device)
+ return get_alert_level_string(NO_ALERT);
+
+ imadapter = find_imm_alert_adapter(device_get_adapter(device));
+ if (!imadapter)
+ return get_alert_level_string(NO_ALERT);
+
+ condev = find_connected_device(imadapter, device);
+ if (!condev)
+ return get_alert_level_string(NO_ALERT);
+
+ return get_alert_level_string(condev->alert_level);
+}
+
+static void imm_alert_emit_alert_signal(struct connected_device *condev,
+ uint8_t alert_level)
+{
+ const char *path, *alert_level_str;
+
+ if (!condev)
+ return;
+
+ path = device_get_path(condev->device);
+ alert_level_str = get_alert_level_string(alert_level);
+
+ DBG("alert %s remote %s", alert_level_str, path);
+
+ g_dbus_emit_property_changed(btd_get_dbus_connection(), path,
+ PROXIMITY_REPORTER_INTERFACE, "ImmediateAlertLevel");
+}
+
+static void imm_alert_remove_condev(struct connected_device *condev)
+{
+ struct imm_alert_adapter *ia;
+
+ if (!condev)
+ return;
+
+ ia = condev->adapter;
+
+ if (condev->callback_id && condev->device)
+ btd_device_remove_attio_callback(condev->device,
+ condev->callback_id);
+
+ if (condev->device)
+ btd_device_unref(condev->device);
+
+ ia->connected_devices = g_slist_remove(ia->connected_devices, condev);
+ g_free(condev);
+}
+
+/* condev can be NULL */
+static void imm_alert_disc_cb(gpointer user_data)
+{
+ struct connected_device *condev = user_data;
+
+ if (!condev)
+ return;
+
+ DBG("immediate alert remove device %p", condev->device);
+
+ imm_alert_emit_alert_signal(condev, NO_ALERT);
+ imm_alert_remove_condev(condev);
+}
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void imm_alert_alert_lvl_write(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ const uint8_t *value, size_t len,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ struct imm_alert_adapter *ia = user_data;
+ struct connected_device *condev = NULL;
+ uint8_t ecode = 0;
+ bdaddr_t bdaddr;
+ uint8_t bdaddr_type;
+ struct btd_device *device = NULL;
+
+ if (!value || len == 0) {
+ ecode = BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN;
+ goto done;
+ }
+
+ if (offset != 0) {
+ ecode = BT_ATT_ERROR_INVALID_OFFSET;
+ goto done;
+ }
+
+ if (!get_dest_info(att, &bdaddr, &bdaddr_type)) {
+ ecode = BT_ATT_ERROR_UNLIKELY;
+ goto done;
+ }
+
+ device = btd_adapter_get_device(ia->adapter, &bdaddr, bdaddr_type);
+ if (!device) {
+ ecode = BT_ATT_ERROR_UNLIKELY;
+ goto done;
+ }
+
+ /* Write value should be anyone of 0x00, 0x01, 0x02 */
+ if (value[0] > 0x02) {
+ ecode = 0x80;
+ goto done;
+ }
+
+ /* condev might remain NULL here if nothing is found */
+ condev = find_connected_device(ia, device);
+
+ /* Register a disconnect cb if the alert level is non-zero */
+ if (value[0] != NO_ALERT && !condev) {
+ condev = g_new0(struct connected_device, 1);
+ condev->device = btd_device_ref(device);
+ condev->adapter = ia;
+ condev->callback_id = btd_device_add_attio_callback(device,
+ NULL, imm_alert_disc_cb, condev);
+ ia->connected_devices = g_slist_append(ia->connected_devices,
+ condev);
+ DBG("added connected dev %p", device);
+ }
+
+ if (condev) {
+ if (value[0] != NO_ALERT) {
+ condev->alert_level = value[0];
+ imm_alert_emit_alert_signal(condev, value[0]);
+ } else {
+ imm_alert_emit_alert_signal(condev, value[0]);
+ imm_alert_disc_cb(condev);
+ }
+ }
+
+ DBG("alert level set to %d by device %p", value[0], device);
+ gatt_db_attribute_write_result(attrib, id, ecode);
+ return;
+
+done:
+ error("Set immediate alert level for dev %p", device);
+ /* remove alerts by erroneous devices */
+ imm_alert_disc_cb(condev);
+ gatt_db_attribute_write_result(attrib, id, ecode);
+}
+
+void imm_alert_register(struct btd_adapter *adapter)
+{
+ bt_uuid_t uuid;
+ struct imm_alert_adapter *imadapter;
+ struct gatt_db_attribute *service, *charc;
+ struct gatt_db *db;
+
+ imadapter = g_new0(struct imm_alert_adapter, 1);
+ imadapter->adapter = adapter;
+
+ imm_alert_adapters = g_slist_append(imm_alert_adapters, imadapter);
+ db = (struct gatt_db *) btd_gatt_database_get_db(btd_adapter_get_database(adapter));
+
+ /* Immediate Alert Service */
+ bt_uuid16_create(&uuid, IMMEDIATE_ALERT_SVC_UUID);
+ service = gatt_db_add_service(db, &uuid, true, 3);
+ if (!service)
+ goto err;
+
+ imadapter->imservice = service;
+
+ /*
+ * Alert Level characteristic.
+ */
+ bt_uuid16_create(&uuid, ALERT_LEVEL_CHR_UUID);
+ charc = gatt_db_service_add_characteristic(service, &uuid, BT_ATT_PERM_WRITE,
+ BT_GATT_CHRC_PROP_WRITE,
+ NULL,
+ imm_alert_alert_lvl_write, imadapter);
+ if (!charc)
+ goto err;
+
+ gatt_db_service_set_active(service, true);
+
+ DBG("Immediate Alert service added");
+ return;
+err:
+ DBG("Error adding Immediate Alert service");
+ imm_alert_unregister(adapter);
+}
+#else
+static uint8_t imm_alert_alert_lvl_write(struct attribute *a,
+ struct btd_device *device, gpointer user_data)
+{
+ uint8_t value;
+ struct imm_alert_adapter *ia = user_data;
+ struct connected_device *condev = NULL;
+
+ if (!device)
+ goto set_error;
+
+ condev = find_connected_device(ia, device);
+
+ if (a->len == 0) {
+ DBG("Illegal alert level length");
+ goto set_error;
+ }
+
+ value = a->data[0];
+ if (value != NO_ALERT && value != MILD_ALERT && value != HIGH_ALERT) {
+ DBG("Illegal alert value");
+ goto set_error;
+ }
+
+ /* Register a disconnect cb if the alert level is non-zero */
+ if (value != NO_ALERT && !condev) {
+ condev = g_new0(struct connected_device, 1);
+ condev->device = btd_device_ref(device);
+ condev->adapter = ia;
+ condev->callback_id = btd_device_add_attio_callback(device,
+ NULL, imm_alert_disc_cb, condev);
+ ia->connected_devices = g_slist_append(ia->connected_devices,
+ condev);
+ DBG("added connected dev %p", device);
+ }
+
+ if (value != NO_ALERT) {
+ condev->alert_level = value;
+ imm_alert_emit_alert_signal(condev, value);
+ }
+
+ /*
+ * Emit NO_ALERT if the alert level was non-zero before. This is
+ * guaranteed when there's a condev.
+ */
+ if (value == NO_ALERT && condev)
+ imm_alert_disc_cb(condev);
+
+ DBG("alert level set to %d by device %p", value, device);
+ return 0;
+
+set_error:
+ error("Set immediate alert level for dev %p", device);
+ /* remove alerts by erroneous devices */
+ imm_alert_disc_cb(condev);
+ return ATT_ECODE_IO;
+}
+
+void imm_alert_register(struct btd_adapter *adapter)
+{
+ gboolean svc_added;
+ bt_uuid_t uuid;
+ struct imm_alert_adapter *imadapter;
+
+ bt_uuid16_create(&uuid, IMMEDIATE_ALERT_SVC_UUID);
+
+ imadapter = g_new0(struct imm_alert_adapter, 1);
+ imadapter->adapter = adapter;
+
+ imm_alert_adapters = g_slist_append(imm_alert_adapters, imadapter);
+
+ /* Immediate Alert Service */
+ svc_added = gatt_service_add(adapter,
+ GATT_PRIM_SVC_UUID, &uuid,
+ /* Alert level characteristic */
+ GATT_OPT_CHR_UUID16, ALERT_LEVEL_CHR_UUID,
+ GATT_OPT_CHR_PROPS,
+ GATT_CHR_PROP_WRITE_WITHOUT_RESP,
+ GATT_OPT_CHR_VALUE_CB, ATTRIB_WRITE,
+ imm_alert_alert_lvl_write, imadapter,
+ GATT_OPT_INVALID);
+
+ if (!svc_added) {
+ imm_alert_unregister(adapter);
+ return;
+ }
+
+ DBG("Immediate Alert service added");
+}
+#endif
+
+static void remove_condev_list_item(gpointer data, gpointer user_data)
+{
+ struct connected_device *condev = data;
+
+ imm_alert_remove_condev(condev);
+}
+
+void imm_alert_unregister(struct btd_adapter *adapter)
+{
+ struct imm_alert_adapter *imadapter;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ struct gatt_db *db;
+#endif
+
+ imadapter = find_imm_alert_adapter(adapter);
+ if (!imadapter)
+ return;
+
+ g_slist_foreach(imadapter->connected_devices, remove_condev_list_item,
+ NULL);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ /* Remove registered service */
+ if (imadapter->imservice) {
+ db = (struct gatt_db *) btd_gatt_database_get_db(btd_adapter_get_database(adapter));
+ gatt_db_remove_service(db, imadapter->imservice);
+ }
+#endif
+
+ imm_alert_adapters = g_slist_remove(imm_alert_adapters, imadapter);
+ g_free(imadapter);
+}
diff --git a/profiles/proximity/immalert.h b/profiles/proximity/immalert.h
new file mode 100755
index 00000000..1a09fa98
--- /dev/null
+++ b/profiles/proximity/immalert.h
@@ -0,0 +1,26 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012 Texas Instruments Corporation
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+void imm_alert_register(struct btd_adapter *adapter);
+void imm_alert_unregister(struct btd_adapter *adapter);
+const char *imm_alert_get_level(struct btd_device *device);
diff --git a/profiles/proximity/linkloss.c b/profiles/proximity/linkloss.c
new file mode 100755
index 00000000..60b10647
--- /dev/null
+++ b/profiles/proximity/linkloss.c
@@ -0,0 +1,547 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012 Texas Instruments Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdbool.h>
+
+#include <glib.h>
+
+#include <dbus/dbus.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
+#include "src/log.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "attrib/att-database.h"
+#include "attrib/gattrib.h"
+#include "attrib/att.h"
+#include "attrib/gatt.h"
+#include "attrib/gatt-service.h"
+#include "src/attrib-server.h"
+#include "src/service.h"
+#include "src/profile.h"
+#include "src/attio.h"
+#include "src/dbus-common.h"
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#include "src/shared/queue.h"
+#include "src/shared/gatt-db.h"
+#include "src/shared/att.h"
+#include "btio/btio.h"
+#include "src/gatt-database.h"
+#endif
+
+#include "reporter.h"
+#include "linkloss.h"
+
+struct link_loss_adapter {
+ struct btd_adapter *adapter;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ struct gatt_db_attribute *llservice;
+#else
+ uint16_t alert_lvl_value_handle;
+#endif
+ GSList *connected_devices;
+};
+
+struct connected_device {
+ struct btd_device *device;
+ struct link_loss_adapter *adapter;
+ uint8_t alert_level;
+ guint callback_id;
+ guint local_disc_id;
+};
+
+static GSList *link_loss_adapters;
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static bool get_dest_info(struct bt_att *att, bdaddr_t *dst, uint8_t *dst_type)
+{
+ GIOChannel *io = NULL;
+ GError *gerr = NULL;
+
+ io = g_io_channel_unix_new(bt_att_get_fd(att));
+ if (!io)
+ return false;
+
+ bt_io_get(io, &gerr, BT_IO_OPT_DEST_BDADDR, dst,
+ BT_IO_OPT_DEST_TYPE, dst_type,
+ BT_IO_OPT_INVALID);
+
+ if (gerr) {
+ error("gatt: bt_io_get: %s", gerr->message);
+ g_error_free(gerr);
+ g_io_channel_unref(io);
+ return false;
+ }
+
+ g_io_channel_unref(io);
+ return true;
+}
+#endif
+
+static int lldevice_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct connected_device *llcondev = a;
+ const struct btd_device *device = b;
+
+ if (llcondev->device == device)
+ return 0;
+
+ return -1;
+}
+
+static struct connected_device *
+find_connected_device(struct link_loss_adapter *la, struct btd_device *device)
+{
+ GSList *l = g_slist_find_custom(la->connected_devices, device,
+ lldevice_cmp);
+ if (!l)
+ return NULL;
+
+ return l->data;
+}
+
+static int lladapter_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct link_loss_adapter *lladapter = a;
+ const struct btd_adapter *adapter = b;
+
+ if (lladapter->adapter == adapter)
+ return 0;
+
+ return -1;
+}
+
+static struct link_loss_adapter *
+find_link_loss_adapter(struct btd_adapter *adapter)
+{
+ GSList *l = g_slist_find_custom(link_loss_adapters, adapter,
+ lladapter_cmp);
+ if (!l)
+ return NULL;
+
+ return l->data;
+}
+
+const char *link_loss_get_alert_level(struct btd_device *device)
+{
+ struct link_loss_adapter *lladapter;
+ struct connected_device *condev;
+
+ if (!device)
+ return get_alert_level_string(NO_ALERT);
+
+ lladapter = find_link_loss_adapter(device_get_adapter(device));
+ if (!lladapter)
+ return get_alert_level_string(NO_ALERT);
+
+ condev = find_connected_device(lladapter, device);
+ if (!condev)
+ return get_alert_level_string(NO_ALERT);
+
+ return get_alert_level_string(condev->alert_level);
+}
+
+static void link_loss_emit_alert_signal(struct connected_device *condev)
+{
+ const char *alert_level_str, *path;
+
+ if (!condev->device)
+ return;
+
+ path = device_get_path(condev->device);
+ alert_level_str = get_alert_level_string(condev->alert_level);
+
+ DBG("alert %s remote %s", alert_level_str, path);
+
+ g_dbus_emit_property_changed(btd_get_dbus_connection(), path,
+ PROXIMITY_REPORTER_INTERFACE, "LinkLossAlertLevel");
+}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void link_loss_alert_lvl_read(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ struct link_loss_adapter *la = user_data;
+ struct connected_device *condev;
+ uint8_t value;
+ uint8_t ecode = 0;
+ bdaddr_t bdaddr;
+ uint8_t bdaddr_type;
+ struct btd_device *device;
+
+ value = NO_ALERT;
+
+ if (offset != 0) {
+ ecode = BT_ATT_ERROR_INVALID_OFFSET;
+ goto out;
+ }
+
+ if (!get_dest_info(att, &bdaddr, &bdaddr_type)) {
+ ecode = BT_ATT_ERROR_UNLIKELY;
+ goto out;
+ }
+
+ device = btd_adapter_get_device(la->adapter, &bdaddr, bdaddr_type);
+ if (!device) {
+ ecode = BT_ATT_ERROR_UNLIKELY;
+ goto out;
+ }
+
+ condev = find_connected_device(la, device);
+ if (!condev) {
+ ecode = BT_ATT_ERROR_UNLIKELY;
+ goto out;
+ }
+
+ if (condev->alert_level)
+ value = condev->alert_level;
+ else
+ DBG("Alert Level is NULL");
+
+ DBG("Alert Level %d", value);
+out:
+ gatt_db_attribute_read_result(attrib, id, ecode, &value, sizeof(value));
+}
+#else
+static uint8_t link_loss_alert_lvl_read(struct attribute *a,
+ struct btd_device *device, gpointer user_data)
+{
+ struct link_loss_adapter *la = user_data;
+ struct connected_device *condev;
+ uint8_t alert_level = NO_ALERT;
+
+ if (!device)
+ goto out;
+
+ condev = find_connected_device(la, device);
+ if (!condev)
+ goto out;
+
+ alert_level = condev->alert_level;
+
+out:
+ DBG("return alert level %d for dev %p", alert_level, device);
+
+ /* update the alert level according to the requesting device */
+ attrib_db_update(la->adapter, a->handle, NULL, &alert_level,
+ sizeof(alert_level), NULL);
+
+ return 0;
+}
+#endif
+
+/* condev can be NULL */
+static void link_loss_remove_condev(struct connected_device *condev)
+{
+ struct link_loss_adapter *la;
+
+ if (!condev)
+ return;
+
+ la = condev->adapter;
+
+ if (condev->callback_id && condev->device)
+ btd_device_remove_attio_callback(condev->device,
+ condev->callback_id);
+
+ if (condev->local_disc_id && condev->device)
+ device_remove_disconnect_watch(condev->device,
+ condev->local_disc_id);
+
+ if (condev->device)
+ btd_device_unref(condev->device);
+
+ la->connected_devices = g_slist_remove(la->connected_devices, condev);
+ g_free(condev);
+}
+
+static void link_loss_disc_cb(gpointer user_data)
+{
+ struct connected_device *condev = user_data;
+
+ DBG("alert loss disconnect device %p", condev->device);
+
+ /* if an alert-level is set, emit a signal */
+ if (condev->alert_level != NO_ALERT)
+ link_loss_emit_alert_signal(condev);
+
+ /* we are open for more changes now */
+ link_loss_remove_condev(condev);
+}
+
+static void link_loss_local_disc(struct btd_device *device,
+ gboolean removal, void *user_data)
+{
+ struct connected_device *condev = user_data;
+
+ /* no need to alert on this device - we requested disconnection */
+ link_loss_remove_condev(condev);
+
+ DBG("alert level zeroed for locally disconnecting dev %p", device);
+}
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void link_loss_alert_lvl_write(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ const uint8_t *value, size_t len,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ struct link_loss_adapter *la = user_data;
+ struct connected_device *condev = NULL;
+ uint8_t ecode = 0;
+ bdaddr_t bdaddr;
+ uint8_t bdaddr_type;
+ struct btd_device *device = NULL;
+
+ if (!value || len == 0) {
+ ecode = BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN;
+ goto done;
+ }
+
+ if (offset != 0) {
+ ecode = BT_ATT_ERROR_INVALID_OFFSET;
+ goto done;
+ }
+
+ if (!get_dest_info(att, &bdaddr, &bdaddr_type)) {
+ ecode = BT_ATT_ERROR_UNLIKELY;
+ goto done;
+ }
+
+ device = btd_adapter_get_device(la->adapter, &bdaddr, bdaddr_type);
+ if (!device) {
+ ecode = BT_ATT_ERROR_UNLIKELY;
+ goto done;
+ }
+
+ /* Write value should be anyone of 0x00, 0x01, 0x02 */
+ if (value[0] > 0x02) {
+ ecode = 0x80;
+ goto done;
+ }
+
+ /* condev might remain NULL here if nothing is found */
+ condev = find_connected_device(la, device);
+
+ /* Register a disconnect cb if the alert level is non-zero */
+ if (value[0] != NO_ALERT && !condev) {
+ condev = g_new0(struct connected_device, 1);
+ condev->device = btd_device_ref(device);
+ condev->adapter = la;
+ condev->callback_id = btd_device_add_attio_callback(device,
+ NULL, link_loss_disc_cb, condev);
+ condev->local_disc_id = device_add_disconnect_watch(device,
+ link_loss_local_disc, condev, NULL);
+
+ la->connected_devices = g_slist_append(la->connected_devices,
+ condev);
+ }
+
+ if (condev) {
+ if (value[0] != NO_ALERT) {
+ condev->alert_level = value[0];
+ link_loss_emit_alert_signal(condev);
+ } else {
+ link_loss_emit_alert_signal(condev);
+ link_loss_remove_condev(condev);
+ condev = NULL;
+ }
+ }
+
+ DBG("alert level set to %d by device %p", value[0], device);
+ gatt_db_attribute_write_result(attrib, id, ecode);
+ return;
+done:
+ DBG("Set link loss alert level for dev %p", device);
+ /* reset alert level on erroneous devices */
+ link_loss_remove_condev(condev);
+ gatt_db_attribute_write_result(attrib, id, ecode);
+}
+#else
+static uint8_t link_loss_alert_lvl_write(struct attribute *a,
+ struct btd_device *device, gpointer user_data)
+{
+ uint8_t value;
+ struct link_loss_adapter *la = user_data;
+ struct connected_device *condev = NULL;
+
+ if (!device)
+ goto set_error;
+
+ /* condev might remain NULL here if nothing is found */
+ condev = find_connected_device(la, device);
+
+ if (a->len == 0) {
+ DBG("Illegal alert level length");
+ goto set_error;
+ }
+
+ value = a->data[0];
+ if (value != NO_ALERT && value != MILD_ALERT && value != HIGH_ALERT) {
+ DBG("Illegal alert value");
+ goto set_error;
+ }
+
+ /* Register a disconnect cb if the alert level is non-zero */
+ if (value != NO_ALERT && !condev) {
+ condev = g_new0(struct connected_device, 1);
+ condev->device = btd_device_ref(device);
+ condev->adapter = la;
+ condev->callback_id = btd_device_add_attio_callback(device,
+ NULL, link_loss_disc_cb, condev);
+ condev->local_disc_id = device_add_disconnect_watch(device,
+ link_loss_local_disc, condev, NULL);
+
+ la->connected_devices = g_slist_append(la->connected_devices,
+ condev);
+ } else if (value == NO_ALERT && condev) {
+ link_loss_remove_condev(condev);
+ condev = NULL;
+ }
+
+ DBG("alert level set to %d by device %p", value, device);
+
+ if (condev)
+ condev->alert_level = value;
+
+ return 0;
+
+set_error:
+ error("Set link loss alert level for dev %p", device);
+ /* reset alert level on erroneous devices */
+ link_loss_remove_condev(condev);
+ return ATT_ECODE_IO;
+}
+#endif
+
+void link_loss_register(struct btd_adapter *adapter)
+{
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ struct gatt_db_attribute *service, *charc;
+ struct gatt_db *db;
+#else
+ gboolean svc_added;
+#endif
+ bt_uuid_t uuid;
+ struct link_loss_adapter *lladapter;
+
+ bt_uuid16_create(&uuid, LINK_LOSS_SVC_UUID);
+
+ lladapter = g_new0(struct link_loss_adapter, 1);
+ lladapter->adapter = adapter;
+
+ link_loss_adapters = g_slist_append(link_loss_adapters, lladapter);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ db = (struct gatt_db *) btd_gatt_database_get_db(btd_adapter_get_database(adapter));
+
+ /*
+ * Link Loss Service
+ */
+ service = gatt_db_add_service(db, &uuid, true, 3);
+ if (!service)
+ goto err;
+
+ lladapter->llservice = service;
+
+ /*
+ * Alert Level characteristic.
+ */
+ bt_uuid16_create(&uuid, ALERT_LEVEL_CHR_UUID);
+ charc = gatt_db_service_add_characteristic(service, &uuid, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+ BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_WRITE,
+ link_loss_alert_lvl_read,
+ link_loss_alert_lvl_write, lladapter);
+
+ if (!charc)
+ goto err;
+
+ gatt_db_service_set_active(service, true);
+#else
+ /* Link Loss Service */
+ svc_added = gatt_service_add(adapter,
+ GATT_PRIM_SVC_UUID, &uuid,
+ /* Alert level characteristic */
+ GATT_OPT_CHR_UUID16, ALERT_LEVEL_CHR_UUID,
+ GATT_OPT_CHR_PROPS,
+ GATT_CHR_PROP_READ | GATT_CHR_PROP_WRITE,
+ GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
+ link_loss_alert_lvl_read, lladapter,
+ GATT_OPT_CHR_VALUE_CB, ATTRIB_WRITE,
+ link_loss_alert_lvl_write, lladapter,
+ GATT_OPT_CHR_VALUE_GET_HANDLE,
+ &lladapter->alert_lvl_value_handle,
+ GATT_OPT_INVALID);
+
+ if (!svc_added)
+ goto err;
+#endif
+ DBG("Link Loss service added");
+ return;
+
+err:
+ error("Error adding Link Loss service");
+ link_loss_unregister(adapter);
+}
+
+static void remove_condev_list_item(gpointer data, gpointer user_data)
+{
+ struct connected_device *condev = data;
+
+ link_loss_remove_condev(condev);
+}
+
+void link_loss_unregister(struct btd_adapter *adapter)
+{
+ struct link_loss_adapter *lladapter;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ struct gatt_db *db;
+#endif
+
+ lladapter = find_link_loss_adapter(adapter);
+ if (!lladapter)
+ return;
+
+ g_slist_foreach(lladapter->connected_devices, remove_condev_list_item,
+ NULL);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ /* Remove registered service */
+ if (lladapter->llservice) {
+ db = (struct gatt_db *) btd_gatt_database_get_db(btd_adapter_get_database(adapter));
+ gatt_db_remove_service(db, lladapter->llservice);
+ }
+#endif
+
+ link_loss_adapters = g_slist_remove(link_loss_adapters, lladapter);
+ g_free(lladapter);
+}
diff --git a/profiles/proximity/linkloss.h b/profiles/proximity/linkloss.h
new file mode 100755
index 00000000..0447def3
--- /dev/null
+++ b/profiles/proximity/linkloss.h
@@ -0,0 +1,26 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012 Texas Instruments Corporation
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+void link_loss_register(struct btd_adapter *adapter);
+void link_loss_unregister(struct btd_adapter *adapter);
+const char *link_loss_get_alert_level(struct btd_device *device);
diff --git a/profiles/proximity/main.c b/profiles/proximity/main.c
new file mode 100755
index 00000000..38a51f12
--- /dev/null
+++ b/profiles/proximity/main.c
@@ -0,0 +1,81 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ * Copyright (C) 2011 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <stdint.h>
+#include <glib.h>
+
+#include "gdbus/gdbus.h"
+
+#include "src/log.h"
+#include "src/plugin.h"
+#include "manager.h"
+
+static GKeyFile *config = NULL;
+
+static GKeyFile *open_config_file(const char *file)
+{
+ GError *gerr = NULL;
+ GKeyFile *keyfile;
+
+ keyfile = g_key_file_new();
+
+ g_key_file_set_list_separator(keyfile, ',');
+
+ if (!g_key_file_load_from_file(keyfile, file, 0, &gerr)) {
+ if (!g_error_matches(gerr, G_FILE_ERROR, G_FILE_ERROR_NOENT))
+ error("Parsing %s failed: %s", file, gerr->message);
+ g_error_free(gerr);
+ g_key_file_free(keyfile);
+ return NULL;
+ }
+
+ return keyfile;
+}
+
+static int proximity_init(void)
+{
+ config = open_config_file(CONFIGDIR "/proximity.conf");
+
+ if (proximity_manager_init(config) < 0)
+ return -EIO;
+
+ return 0;
+}
+
+static void proximity_exit(void)
+{
+ if (config)
+ g_key_file_free(config);
+
+ proximity_manager_exit();
+}
+
+BLUETOOTH_PLUGIN_DEFINE(proximity, VERSION,
+ BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+ proximity_init, proximity_exit)
diff --git a/profiles/proximity/manager.c b/profiles/proximity/manager.c
new file mode 100755
index 00000000..dbb3bda2
--- /dev/null
+++ b/profiles/proximity/manager.c
@@ -0,0 +1,196 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ * Copyright (C) 2011 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdbool.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/service.h"
+#include "attrib/att.h"
+#include "attrib/gattrib.h"
+#include "attrib/gatt.h"
+#include "monitor.h"
+#include "reporter.h"
+#include "manager.h"
+
+static struct enabled enabled = {
+ .linkloss = TRUE,
+ .pathloss = TRUE,
+ .findme = TRUE,
+};
+
+static int monitor_linkloss_probe(struct btd_service *service)
+{
+ struct btd_device *device = btd_service_get_device(service);
+ struct gatt_primary *linkloss;
+
+ linkloss = btd_device_get_primary(device, LINK_LOSS_UUID);
+ if (linkloss == NULL)
+ return -1;
+
+ return monitor_register_linkloss(device, &enabled, linkloss);
+}
+
+static int monitor_immediate_probe(struct btd_service *service)
+{
+ struct btd_device *device = btd_service_get_device(service);
+ struct gatt_primary *immediate;
+
+ immediate = btd_device_get_primary(device, IMMEDIATE_ALERT_UUID);
+ if (immediate == NULL)
+ return -1;
+
+ return monitor_register_immediate(device, &enabled, immediate);
+}
+
+static int monitor_txpower_probe(struct btd_service *service)
+{
+ struct btd_device *device = btd_service_get_device(service);
+ struct gatt_primary *txpower;
+
+ txpower = btd_device_get_primary(device, TX_POWER_UUID);
+ if (txpower == NULL)
+ return -1;
+
+ return monitor_register_txpower(device, &enabled, txpower);
+}
+
+static void monitor_linkloss_remove(struct btd_service *service)
+{
+ struct btd_device *device = btd_service_get_device(service);
+
+ monitor_unregister_linkloss(device);
+}
+
+static void monitor_immediate_remove(struct btd_service *service)
+{
+ struct btd_device *device = btd_service_get_device(service);
+
+ monitor_unregister_immediate(device);
+}
+
+static void monitor_txpower_remove(struct btd_service *service)
+{
+ struct btd_device *device = btd_service_get_device(service);
+
+ monitor_unregister_txpower(device);
+}
+
+static struct btd_profile pxp_monitor_linkloss_profile = {
+ .name = "proximity-linkloss",
+ .remote_uuid = LINK_LOSS_UUID,
+ .device_probe = monitor_linkloss_probe,
+ .device_remove = monitor_linkloss_remove,
+};
+
+static struct btd_profile pxp_monitor_immediate_profile = {
+ .name = "proximity-immediate",
+ .remote_uuid = IMMEDIATE_ALERT_UUID,
+ .device_probe = monitor_immediate_probe,
+ .device_remove = monitor_immediate_remove,
+};
+
+static struct btd_profile pxp_monitor_txpower_profile = {
+ .name = "proximity-txpower",
+ .remote_uuid = TX_POWER_UUID,
+ .device_probe = monitor_txpower_probe,
+ .device_remove = monitor_txpower_remove,
+};
+
+static struct btd_profile pxp_reporter_profile = {
+ .name = "Proximity Reporter GATT Driver",
+ .remote_uuid = GATT_UUID,
+ .device_probe = reporter_device_probe,
+ .device_remove = reporter_device_remove,
+
+ .adapter_probe = reporter_adapter_probe,
+ .adapter_remove = reporter_adapter_remove,
+};
+
+static void load_config_file(GKeyFile *config)
+{
+ char **list;
+ int i;
+
+ if (config == NULL)
+ return;
+
+ list = g_key_file_get_string_list(config, "General", "Disable",
+ NULL, NULL);
+ for (i = 0; list && list[i] != NULL; i++) {
+ if (g_str_equal(list[i], "FindMe"))
+ enabled.findme = FALSE;
+ else if (g_str_equal(list[i], "LinkLoss"))
+ enabled.linkloss = FALSE;
+ else if (g_str_equal(list[i], "PathLoss"))
+ enabled.pathloss = FALSE;
+ }
+
+ g_strfreev(list);
+}
+
+int proximity_manager_init(GKeyFile *config)
+{
+ load_config_file(config);
+
+ if (btd_profile_register(&pxp_monitor_linkloss_profile) < 0)
+ goto fail;
+
+ if (btd_profile_register(&pxp_monitor_immediate_profile) < 0)
+ goto fail;
+
+ if (btd_profile_register(&pxp_monitor_txpower_profile) < 0)
+ goto fail;
+
+ if (btd_profile_register(&pxp_reporter_profile) < 0)
+ goto fail;
+
+ return 0;
+
+fail:
+ proximity_manager_exit();
+
+ return -1;
+}
+
+void proximity_manager_exit(void)
+{
+ btd_profile_unregister(&pxp_reporter_profile);
+ btd_profile_unregister(&pxp_monitor_txpower_profile);
+ btd_profile_unregister(&pxp_monitor_immediate_profile);
+ btd_profile_unregister(&pxp_monitor_linkloss_profile);
+}
diff --git a/profiles/proximity/manager.h b/profiles/proximity/manager.h
new file mode 100755
index 00000000..e65c31d8
--- /dev/null
+++ b/profiles/proximity/manager.h
@@ -0,0 +1,26 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ * Copyright (C) 2011 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+int proximity_manager_init(GKeyFile *conf);
+void proximity_manager_exit(void);
diff --git a/profiles/proximity/monitor.c b/profiles/proximity/monitor.c
new file mode 100755
index 00000000..a583eb7d
--- /dev/null
+++ b/profiles/proximity/monitor.c
@@ -0,0 +1,822 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ * Copyright (C) 2011 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
+#include "src/dbus-common.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/error.h"
+#include "src/log.h"
+#include "attrib/att.h"
+#include "attrib/gattrib.h"
+#include "attrib/gatt.h"
+#include "src/attio.h"
+#include "src/textfile.h"
+
+#include "monitor.h"
+
+#define PROXIMITY_INTERFACE "org.bluez.ProximityMonitor1"
+
+#define ALERT_LEVEL_CHR_UUID 0x2A06
+#define POWER_LEVEL_CHR_UUID 0x2A07
+
+#define IMMEDIATE_TIMEOUT 5
+#define TX_POWER_SIZE 1
+
+enum {
+ ALERT_NONE = 0,
+ ALERT_MILD,
+ ALERT_HIGH,
+};
+
+struct monitor {
+ struct btd_device *device;
+ GAttrib *attrib;
+ struct att_range *linkloss;
+ struct att_range *txpower;
+ struct att_range *immediate;
+ struct enabled enabled;
+ char *linklosslevel; /* Link Loss Alert Level */
+ char *fallbacklevel; /* Immediate fallback alert level */
+ char *immediatelevel; /* Immediate Alert Level */
+ char *signallevel; /* Path Loss RSSI level */
+ uint16_t linklosshandle; /* Link Loss Characteristic
+ * Value Handle */
+ uint16_t txpowerhandle; /* Tx Characteristic Value Handle */
+ uint16_t immediatehandle; /* Immediate Alert Value Handle */
+ guint immediateto; /* Reset Immediate Alert to "none" */
+ guint attioid;
+};
+
+static GSList *monitors = NULL;
+
+static struct monitor *find_monitor(struct btd_device *device)
+{
+ GSList *l;
+
+ for (l = monitors; l; l = l->next) {
+ struct monitor *monitor = l->data;
+
+ if (monitor->device == device)
+ return monitor;
+ }
+
+ return NULL;
+}
+
+static void write_proximity_config(struct btd_device *device, const char *alert,
+ const char *level)
+{
+ char *filename;
+ GKeyFile *key_file;
+ char *data;
+ gsize length = 0;
+
+ filename = btd_device_get_storage_path(device, "proximity");
+ if (!filename) {
+ warn("Unable to get proximity storage path for device");
+ return;
+ }
+
+ key_file = g_key_file_new();
+ g_key_file_load_from_file(key_file, filename, 0, NULL);
+
+ if (level)
+ g_key_file_set_string(key_file, alert, "Level", level);
+ else
+ g_key_file_remove_group(key_file, alert, NULL);
+
+ data = g_key_file_to_data(key_file, &length, NULL);
+ if (length > 0) {
+ create_file(filename, S_IRUSR | S_IWUSR);
+ g_file_set_contents(filename, data, length, NULL);
+ }
+
+ g_free(data);
+ g_free(filename);
+ g_key_file_free(key_file);
+}
+
+static char *read_proximity_config(struct btd_device *device, const char *alert)
+{
+ char *filename;
+ GKeyFile *key_file;
+ char *str;
+
+ filename = btd_device_get_storage_path(device, "proximity");
+ if (!filename) {
+ warn("Unable to get proximity storage path for device");
+ return NULL;
+ }
+
+ key_file = g_key_file_new();
+ g_key_file_load_from_file(key_file, filename, 0, NULL);
+
+ str = g_key_file_get_string(key_file, alert, "Level", NULL);
+
+ g_free(filename);
+ g_key_file_free(key_file);
+
+ return str;
+}
+
+static uint8_t str2level(const char *level)
+{
+ if (g_strcmp0("high", level) == 0)
+ return ALERT_HIGH;
+ else if (g_strcmp0("mild", level) == 0)
+ return ALERT_MILD;
+
+ return ALERT_NONE;
+}
+
+static void linkloss_written(guint8 status, const guint8 *pdu, guint16 plen,
+ gpointer user_data)
+{
+ struct monitor *monitor = user_data;
+ struct btd_device *device = monitor->device;
+ const char *path = device_get_path(device);
+
+ if (status != 0) {
+ error("Link Loss Write Request failed: %s",
+ att_ecode2str(status));
+ return;
+ }
+
+ if (!dec_write_resp(pdu, plen)) {
+ error("Link Loss Write Request: protocol error");
+ return;
+ }
+
+ DBG("Link Loss Alert Level written");
+
+ g_dbus_emit_property_changed(btd_get_dbus_connection(), path,
+ PROXIMITY_INTERFACE, "LinkLossAlertLevel");
+}
+
+static void char_discovered_cb(uint8_t status, GSList *characteristics,
+ void *user_data)
+{
+ struct monitor *monitor = user_data;
+ struct gatt_char *chr;
+ uint8_t value = str2level(monitor->linklosslevel);
+
+ if (status) {
+ error("Discover Link Loss handle: %s", att_ecode2str(status));
+ return;
+ }
+
+ DBG("Setting alert level \"%s\" on Reporter", monitor->linklosslevel);
+
+ /* Assume there is a single Alert Level characteristic */
+ chr = characteristics->data;
+ monitor->linklosshandle = chr->value_handle;
+
+ gatt_write_char(monitor->attrib, monitor->linklosshandle, &value, 1,
+ linkloss_written, monitor);
+}
+
+static int write_alert_level(struct monitor *monitor)
+{
+ struct att_range *linkloss = monitor->linkloss;
+ bt_uuid_t uuid;
+
+ if (monitor->linklosshandle) {
+ uint8_t value = str2level(monitor->linklosslevel);
+
+ gatt_write_char(monitor->attrib, monitor->linklosshandle,
+ &value, 1, linkloss_written, monitor);
+ return 0;
+ }
+
+ bt_uuid16_create(&uuid, ALERT_LEVEL_CHR_UUID);
+
+ /* FIXME: use cache (requires service changed support) ? */
+ gatt_discover_char(monitor->attrib, linkloss->start, linkloss->end,
+ &uuid, char_discovered_cb, monitor);
+
+ return 0;
+}
+
+static void tx_power_read_cb(guint8 status, const guint8 *pdu, guint16 plen,
+ gpointer user_data)
+{
+ uint8_t value[TX_POWER_SIZE];
+ ssize_t vlen;
+
+ if (status != 0) {
+ DBG("Tx Power Level read failed: %s", att_ecode2str(status));
+ return;
+ }
+
+ vlen = dec_read_resp(pdu, plen, value, sizeof(value));
+ if (vlen < 0) {
+ DBG("Protocol error");
+ return;
+ }
+
+ if (vlen != 1) {
+ DBG("Invalid length for TX Power value: %zd", vlen);
+ return;
+ }
+
+ DBG("Tx Power Level: %02x", (int8_t) value[0]);
+}
+
+static void tx_power_handle_cb(uint8_t status, GSList *characteristics,
+ void *user_data)
+{
+ struct monitor *monitor = user_data;
+ struct gatt_char *chr;
+
+ if (status) {
+ error("Discover Tx Power handle: %s", att_ecode2str(status));
+ return;
+ }
+
+ chr = characteristics->data;
+ monitor->txpowerhandle = chr->value_handle;
+
+ DBG("Tx Power handle: 0x%04x", monitor->txpowerhandle);
+
+ gatt_read_char(monitor->attrib, monitor->txpowerhandle,
+ tx_power_read_cb, monitor);
+}
+
+static void read_tx_power(struct monitor *monitor)
+{
+ struct att_range *txpower = monitor->txpower;
+ bt_uuid_t uuid;
+
+ if (monitor->txpowerhandle != 0) {
+ gatt_read_char(monitor->attrib, monitor->txpowerhandle,
+ tx_power_read_cb, monitor);
+ return;
+ }
+
+ bt_uuid16_create(&uuid, POWER_LEVEL_CHR_UUID);
+
+ gatt_discover_char(monitor->attrib, txpower->start, txpower->end,
+ &uuid, tx_power_handle_cb, monitor);
+}
+
+static gboolean immediate_timeout(gpointer user_data)
+{
+ struct monitor *monitor = user_data;
+ const char *path = device_get_path(monitor->device);
+
+ monitor->immediateto = 0;
+
+ if (g_strcmp0(monitor->immediatelevel, "none") == 0)
+ return FALSE;
+
+ if (monitor->attrib) {
+ uint8_t value = ALERT_NONE;
+ gatt_write_cmd(monitor->attrib, monitor->immediatehandle,
+ &value, 1, NULL, NULL);
+ }
+
+ g_free(monitor->immediatelevel);
+ monitor->immediatelevel = g_strdup("none");
+
+
+ g_dbus_emit_property_changed(btd_get_dbus_connection(), path,
+ PROXIMITY_INTERFACE, "ImmediateAlertLevel");
+
+ return FALSE;
+}
+
+static void immediate_written(gpointer user_data)
+{
+ struct monitor *monitor = user_data;
+ const char *path = device_get_path(monitor->device);
+
+ g_free(monitor->fallbacklevel);
+ monitor->fallbacklevel = NULL;
+
+
+ g_dbus_emit_property_changed(btd_get_dbus_connection(), path,
+ PROXIMITY_INTERFACE, "ImmediateAlertLevel");
+
+ monitor->immediateto = g_timeout_add_seconds(IMMEDIATE_TIMEOUT,
+ immediate_timeout, monitor);
+}
+
+static void write_immediate_alert(struct monitor *monitor)
+{
+ uint8_t value = str2level(monitor->immediatelevel);
+
+ gatt_write_cmd(monitor->attrib, monitor->immediatehandle, &value, 1,
+ immediate_written, monitor);
+}
+
+static void immediate_handle_cb(uint8_t status, GSList *characteristics,
+ void *user_data)
+{
+ struct monitor *monitor = user_data;
+ struct gatt_char *chr;
+
+ if (status) {
+ error("Discover Immediate Alert handle: %s",
+ att_ecode2str(status));
+ return;
+ }
+
+ chr = characteristics->data;
+ monitor->immediatehandle = chr->value_handle;
+
+ DBG("Immediate Alert handle: 0x%04x", monitor->immediatehandle);
+
+ if (monitor->fallbacklevel)
+ write_immediate_alert(monitor);
+}
+
+static void discover_immediate_handle(struct monitor *monitor)
+{
+ struct att_range *immediate = monitor->immediate;
+ bt_uuid_t uuid;
+
+ bt_uuid16_create(&uuid, ALERT_LEVEL_CHR_UUID);
+
+ gatt_discover_char(monitor->attrib, immediate->start, immediate->end,
+ &uuid, immediate_handle_cb, monitor);
+}
+
+static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
+{
+ struct monitor *monitor = user_data;
+
+ monitor->attrib = g_attrib_ref(attrib);
+
+ if (monitor->enabled.linkloss)
+ write_alert_level(monitor);
+
+ if (monitor->enabled.pathloss)
+ read_tx_power(monitor);
+
+ if (monitor->immediatehandle == 0) {
+ if(monitor->enabled.pathloss || monitor->enabled.findme)
+ discover_immediate_handle(monitor);
+ } else if (monitor->fallbacklevel)
+ write_immediate_alert(monitor);
+}
+
+static void attio_disconnected_cb(gpointer user_data)
+{
+ struct monitor *monitor = user_data;
+ const char *path = device_get_path(monitor->device);
+
+ g_attrib_unref(monitor->attrib);
+ monitor->attrib = NULL;
+
+ if (monitor->immediateto == 0)
+ return;
+
+ g_source_remove(monitor->immediateto);
+ monitor->immediateto = 0;
+
+ if (g_strcmp0(monitor->immediatelevel, "none") == 0)
+ return;
+
+ g_free(monitor->immediatelevel);
+ monitor->immediatelevel = g_strdup("none");
+
+ g_dbus_emit_property_changed(btd_get_dbus_connection(), path,
+ PROXIMITY_INTERFACE, "ImmediateAlertLevel");
+}
+
+static gboolean level_is_valid(const char *level)
+{
+ return (g_str_equal("none", level) ||
+ g_str_equal("mild", level) ||
+ g_str_equal("high", level));
+}
+
+static gboolean property_get_link_loss_level(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct monitor *monitor = data;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
+ &monitor->linklosslevel);
+
+ return TRUE;
+}
+
+static void property_set_link_loss_level(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, GDBusPendingPropertySet id, void *data)
+{
+ struct monitor *monitor = data;
+ const char *level;
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) {
+ g_dbus_pending_property_error(id,
+ ERROR_INTERFACE ".InvalidArguments",
+ "Invalid arguments in method call");
+ return;
+ }
+
+ dbus_message_iter_get_basic(iter, &level);
+
+ if (!level_is_valid(level)) {
+ g_dbus_pending_property_error(id,
+ ERROR_INTERFACE ".InvalidArguments",
+ "Invalid arguments in method call");
+ return;
+ }
+
+ if (g_strcmp0(monitor->linklosslevel, level) == 0)
+ goto done;
+
+ g_free(monitor->linklosslevel);
+ monitor->linklosslevel = g_strdup(level);
+
+ write_proximity_config(monitor->device, "LinkLossAlertLevel", level);
+
+ if (monitor->attrib)
+ write_alert_level(monitor);
+
+done:
+ g_dbus_pending_property_success(id);
+}
+
+static gboolean property_exists_link_loss_level(
+ const GDBusPropertyTable *property, void *data)
+{
+ struct monitor *monitor = data;
+
+ if (!monitor->enabled.linkloss)
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean property_get_immediate_alert_level(
+ const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct monitor *monitor = data;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
+ &monitor->immediatelevel);
+
+ return TRUE;
+}
+
+static void property_set_immediate_alert_level(
+ const GDBusPropertyTable *property, DBusMessageIter *iter,
+ GDBusPendingPropertySet id, void *data)
+{
+ struct monitor *monitor = data;
+ struct btd_device *device = monitor->device;
+ const char *level;
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) {
+ g_dbus_pending_property_error(id,
+ ERROR_INTERFACE ".InvalidArguments",
+ "Invalid arguments in method call");
+ return;
+ }
+
+ dbus_message_iter_get_basic(iter, &level);
+
+ if (!level_is_valid(level)) {
+ g_dbus_pending_property_error(id,
+ ERROR_INTERFACE ".InvalidArguments",
+ "Invalid arguments in method call");
+ return;
+ }
+
+ if (g_strcmp0(monitor->immediatelevel, level) == 0)
+ goto done;
+
+ if (monitor->immediateto) {
+ g_source_remove(monitor->immediateto);
+ monitor->immediateto = 0;
+ }
+
+ /* Previous Immediate Alert level if connection/write fails */
+ g_free(monitor->fallbacklevel);
+ monitor->fallbacklevel = monitor->immediatelevel;
+
+ monitor->immediatelevel = g_strdup(level);
+
+ /*
+ * Means that Link/Path Loss are disabled or there is a pending
+ * writting for Find Me(Immediate Alert characteristic value).
+ * If enabled, Path Loss always registers a connection callback
+ * when the Proximity Monitor starts.
+ */
+ if (monitor->attioid == 0)
+ monitor->attioid = btd_device_add_attio_callback(device,
+ attio_connected_cb,
+ attio_disconnected_cb,
+ monitor);
+ else if (monitor->attrib)
+ write_immediate_alert(monitor);
+
+done:
+ g_dbus_pending_property_success(id);
+}
+
+static gboolean property_exists_immediate_alert_level(
+ const GDBusPropertyTable *property, void *data)
+{
+ struct monitor *monitor = data;
+
+ if (!(monitor->enabled.findme || monitor->enabled.pathloss))
+ return FALSE;
+
+ return TRUE;
+}
+
+static gboolean property_get_signal_level(
+ const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct monitor *monitor = data;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING,
+ &monitor->signallevel);
+
+ return TRUE;
+}
+
+static gboolean property_exists_signal_level(const GDBusPropertyTable *property,
+ void *data)
+{
+ struct monitor *monitor = data;
+
+ if (!monitor->enabled.pathloss)
+ return FALSE;
+
+ return TRUE;
+}
+
+static const GDBusPropertyTable monitor_device_properties[] = {
+ { "LinkLossAlertLevel", "s", property_get_link_loss_level,
+ property_set_link_loss_level,
+ property_exists_link_loss_level },
+ { "ImmediateAlertLevel", "s", property_get_immediate_alert_level,
+ property_set_immediate_alert_level,
+ property_exists_immediate_alert_level },
+ { "SignalLevel", "s", property_get_signal_level, NULL,
+ property_exists_signal_level },
+ { }
+};
+
+static void monitor_destroy(gpointer user_data)
+{
+ struct monitor *monitor = user_data;
+
+ monitors = g_slist_remove(monitors, monitor);
+
+ btd_device_unref(monitor->device);
+ g_free(monitor->linklosslevel);
+ g_free(monitor->immediatelevel);
+ g_free(monitor->signallevel);
+ g_free(monitor);
+}
+
+static struct monitor *register_monitor(struct btd_device *device)
+{
+ const char *path = device_get_path(device);
+ struct monitor *monitor;
+ char *level;
+
+ monitor = find_monitor(device);
+ if (monitor != NULL)
+ return monitor;
+
+ level = read_proximity_config(device, "LinkLossAlertLevel");
+
+ monitor = g_new0(struct monitor, 1);
+ monitor->device = btd_device_ref(device);
+ monitor->linklosslevel = (level ? : g_strdup("high"));
+ monitor->signallevel = g_strdup("unknown");
+ monitor->immediatelevel = g_strdup("none");
+
+ monitors = g_slist_append(monitors, monitor);
+
+ if (g_dbus_register_interface(btd_get_dbus_connection(), path,
+ PROXIMITY_INTERFACE,
+ NULL, NULL, monitor_device_properties,
+ monitor, monitor_destroy) == FALSE) {
+ error("D-Bus failed to register %s interface",
+ PROXIMITY_INTERFACE);
+ monitor_destroy(monitor);
+ return NULL;
+ }
+
+ DBG("Registered interface %s on path %s", PROXIMITY_INTERFACE, path);
+
+ return monitor;
+}
+
+static void update_monitor(struct monitor *monitor)
+{
+ if (monitor->txpower != NULL && monitor->immediate != NULL)
+ monitor->enabled.pathloss = TRUE;
+ else
+ monitor->enabled.pathloss = FALSE;
+
+ DBG("Link Loss: %s, Path Loss: %s, FindMe: %s",
+ monitor->enabled.linkloss ? "TRUE" : "FALSE",
+ monitor->enabled.pathloss ? "TRUE" : "FALSE",
+ monitor->enabled.findme ? "TRUE" : "FALSE");
+
+ if (!monitor->enabled.linkloss && !monitor->enabled.pathloss)
+ return;
+
+ if (monitor->attioid != 0)
+ return;
+
+ monitor->attioid = btd_device_add_attio_callback(monitor->device,
+ attio_connected_cb,
+ attio_disconnected_cb,
+ monitor);
+}
+
+int monitor_register_linkloss(struct btd_device *device,
+ struct enabled *enabled,
+ struct gatt_primary *linkloss)
+{
+ struct monitor *monitor;
+
+ if (!enabled->linkloss)
+ return 0;
+
+ monitor = register_monitor(device);
+ if (monitor == NULL)
+ return -1;
+
+ monitor->linkloss = g_new0(struct att_range, 1);
+ monitor->linkloss->start = linkloss->range.start;
+ monitor->linkloss->end = linkloss->range.end;
+ monitor->enabled.linkloss = TRUE;
+
+ update_monitor(monitor);
+
+ return 0;
+}
+
+int monitor_register_txpower(struct btd_device *device,
+ struct enabled *enabled,
+ struct gatt_primary *txpower)
+{
+ struct monitor *monitor;
+
+ if (!enabled->pathloss)
+ return 0;
+
+ monitor = register_monitor(device);
+ if (monitor == NULL)
+ return -1;
+
+ monitor->txpower = g_new0(struct att_range, 1);
+ monitor->txpower->start = txpower->range.start;
+ monitor->txpower->end = txpower->range.end;
+
+ update_monitor(monitor);
+
+ return 0;
+}
+
+int monitor_register_immediate(struct btd_device *device,
+ struct enabled *enabled,
+ struct gatt_primary *immediate)
+{
+ struct monitor *monitor;
+
+ if (!enabled->pathloss && !enabled->findme)
+ return 0;
+
+ monitor = register_monitor(device);
+ if (monitor == NULL)
+ return -1;
+
+ monitor->immediate = g_new0(struct att_range, 1);
+ monitor->immediate->start = immediate->range.start;
+ monitor->immediate->end = immediate->range.end;
+ monitor->enabled.findme = enabled->findme;
+
+ update_monitor(monitor);
+
+ return 0;
+}
+
+static void cleanup_monitor(struct monitor *monitor)
+{
+ struct btd_device *device = monitor->device;
+ const char *path = device_get_path(device);
+
+ if (monitor->immediate != NULL || monitor->txpower != NULL)
+ return;
+
+ if (monitor->immediateto != 0) {
+ g_source_remove(monitor->immediateto);
+ monitor->immediateto = 0;
+ }
+
+ if (monitor->linkloss != NULL)
+ return;
+
+ if (monitor->attioid != 0) {
+ btd_device_remove_attio_callback(device, monitor->attioid);
+ monitor->attioid = 0;
+ }
+
+ if (monitor->attrib != NULL) {
+ g_attrib_unref(monitor->attrib);
+ monitor->attrib = NULL;
+ }
+
+ g_dbus_unregister_interface(btd_get_dbus_connection(), path,
+ PROXIMITY_INTERFACE);
+}
+
+void monitor_unregister_linkloss(struct btd_device *device)
+{
+ struct monitor *monitor;
+
+ monitor = find_monitor(device);
+ if (monitor == NULL)
+ return;
+
+ g_free(monitor->linkloss);
+ monitor->linkloss = NULL;
+ monitor->enabled.linkloss = FALSE;
+
+ cleanup_monitor(monitor);
+}
+
+void monitor_unregister_txpower(struct btd_device *device)
+{
+ struct monitor *monitor;
+
+ monitor = find_monitor(device);
+ if (monitor == NULL)
+ return;
+
+ g_free(monitor->txpower);
+ monitor->txpower = NULL;
+ monitor->enabled.pathloss = FALSE;
+
+ cleanup_monitor(monitor);
+}
+
+void monitor_unregister_immediate(struct btd_device *device)
+{
+ struct monitor *monitor;
+
+ monitor = find_monitor(device);
+ if (monitor == NULL)
+ return;
+
+ g_free(monitor->immediate);
+ monitor->immediate = NULL;
+ monitor->enabled.findme = FALSE;
+ monitor->enabled.pathloss = FALSE;
+
+ cleanup_monitor(monitor);
+}
diff --git a/profiles/proximity/monitor.h b/profiles/proximity/monitor.h
new file mode 100755
index 00000000..d9a40c60
--- /dev/null
+++ b/profiles/proximity/monitor.h
@@ -0,0 +1,43 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ * Copyright (C) 2011 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+struct enabled {
+ gboolean linkloss;
+ gboolean pathloss;
+ gboolean findme;
+};
+
+int monitor_register_linkloss(struct btd_device *device,
+ struct enabled *enabled,
+ struct gatt_primary *linkloss);
+int monitor_register_txpower(struct btd_device *device,
+ struct enabled *enabled,
+ struct gatt_primary *txpower);
+int monitor_register_immediate(struct btd_device *device,
+ struct enabled *enabled,
+ struct gatt_primary *immediate);
+
+void monitor_unregister_linkloss(struct btd_device *device);
+void monitor_unregister_txpower(struct btd_device *device);
+void monitor_unregister_immediate(struct btd_device *device);
diff --git a/profiles/proximity/reporter.c b/profiles/proximity/reporter.c
new file mode 100755
index 00000000..30fc7c2f
--- /dev/null
+++ b/profiles/proximity/reporter.c
@@ -0,0 +1,330 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ * Copyright (C) 2011 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <errno.h>
+
+#include <glib.h>
+
+#include <dbus/dbus.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
+#include "src/log.h"
+#include "src/dbus-common.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/service.h"
+#include "src/shared/util.h"
+#include "attrib/gattrib.h"
+#include "attrib/att.h"
+#include "attrib/gatt.h"
+#include "attrib/att-database.h"
+#include "src/attrib-server.h"
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#include "src/error.h"
+#endif
+
+#include "reporter.h"
+#include "linkloss.h"
+#include "immalert.h"
+
+struct reporter_adapter {
+ struct btd_adapter *adapter;
+ GSList *devices;
+};
+
+static GSList *reporter_adapters;
+
+static int radapter_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct reporter_adapter *radapter = a;
+ const struct btd_adapter *adapter = b;
+
+ if (radapter->adapter == adapter)
+ return 0;
+
+ return -1;
+}
+
+static struct reporter_adapter *
+find_reporter_adapter(struct btd_adapter *adapter)
+{
+ GSList *l = g_slist_find_custom(reporter_adapters, adapter,
+ radapter_cmp);
+ if (!l)
+ return NULL;
+
+ return l->data;
+}
+
+const char *get_alert_level_string(uint8_t level)
+{
+ switch (level) {
+ case NO_ALERT:
+ return "none";
+ case MILD_ALERT:
+ return "mild";
+ case HIGH_ALERT:
+ return "high";
+ }
+
+ return "unknown";
+}
+
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
+static void register_tx_power(struct btd_adapter *adapter)
+{
+ uint16_t start_handle, h;
+ const int svc_size = 4;
+ uint8_t atval[256];
+ bt_uuid_t uuid;
+
+ bt_uuid16_create(&uuid, TX_POWER_SVC_UUID);
+ start_handle = attrib_db_find_avail(adapter, &uuid, svc_size);
+ if (start_handle == 0) {
+ error("Not enough free handles to register service");
+ return;
+ }
+
+ DBG("start_handle=0x%04x", start_handle);
+
+ h = start_handle;
+
+ /* Primary service definition */
+ bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
+ put_le16(TX_POWER_SVC_UUID, &atval[0]);
+ attrib_db_add(adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2);
+
+ /* Power level characteristic */
+ bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+ atval[0] = GATT_CHR_PROP_READ | GATT_CHR_PROP_NOTIFY;
+ put_le16(h + 1, &atval[1]);
+ put_le16(POWER_LEVEL_CHR_UUID, &atval[3]);
+ attrib_db_add(adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5);
+
+ /* Power level value */
+ bt_uuid16_create(&uuid, POWER_LEVEL_CHR_UUID);
+ atval[0] = 0x00;
+ attrib_db_add(adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 1);
+
+ /* Client characteristic configuration */
+ bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+ atval[0] = 0x00;
+ atval[1] = 0x00;
+ attrib_db_add(adapter, h++, &uuid, ATT_NONE, ATT_NONE, atval, 2);
+
+ g_assert(h - start_handle == svc_size);
+}
+#endif
+
+static gboolean property_get_link_loss_level(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct btd_device *device = data;
+ const char *level;
+
+ level = link_loss_get_alert_level(device);
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &level);
+
+ return TRUE;
+}
+
+static gboolean property_get_immediate_alert_level(
+ const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct btd_device *device = data;
+ const char *level;
+
+ level = imm_alert_get_level(device);
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &level);
+
+ return TRUE;
+}
+
+static const GDBusPropertyTable reporter_device_properties[] = {
+ { "LinkLossAlertLevel", "s", property_get_link_loss_level },
+ { "ImmediateAlertLevel", "s", property_get_immediate_alert_level },
+ { }
+};
+
+static void unregister_reporter_device(gpointer data, gpointer user_data)
+{
+ struct btd_device *device = data;
+ struct reporter_adapter *radapter = user_data;
+ const char *path = device_get_path(device);
+
+ DBG("unregister on device %s", path);
+
+ g_dbus_unregister_interface(btd_get_dbus_connection(), path,
+ PROXIMITY_REPORTER_INTERFACE);
+
+ radapter->devices = g_slist_remove(radapter->devices, device);
+ btd_device_unref(device);
+}
+
+static void register_reporter_device(struct btd_device *device,
+ struct reporter_adapter *radapter)
+{
+ const char *path = device_get_path(device);
+
+ DBG("register on device %s", path);
+
+ g_dbus_register_interface(btd_get_dbus_connection(), path,
+ PROXIMITY_REPORTER_INTERFACE,
+ NULL, NULL, reporter_device_properties,
+ device, NULL);
+
+ btd_device_ref(device);
+ radapter->devices = g_slist_prepend(radapter->devices, device);
+}
+
+int reporter_device_probe(struct btd_service *service)
+{
+ struct btd_device *device = btd_service_get_device(service);
+ struct reporter_adapter *radapter;
+ struct btd_adapter *adapter = device_get_adapter(device);
+
+ radapter = find_reporter_adapter(adapter);
+ if (!radapter)
+ return -1;
+
+ register_reporter_device(device, radapter);
+
+ return 0;
+}
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static DBusMessage *register_proximity(DBusConnection *conn, DBusMessage *msg,
+ void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+
+ if(adapter == NULL) {
+ DBG("Adapter is NULL");
+ return btd_error_invalid_args(msg);
+ }
+
+ link_loss_register(adapter);
+ imm_alert_register(adapter);
+
+ /* TODO: TX Power service implementation
+ * is incomplete in BlueZ.
+ */
+ //register_tx_power(adapter);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *unregister_proximity(DBusConnection *conn, DBusMessage *msg,
+ void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+
+ if(adapter == NULL) {
+ DBG("Adapter is NULL");
+ return btd_error_invalid_args(msg);
+ }
+
+ link_loss_unregister(adapter);
+ imm_alert_unregister(adapter);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable reporter_adapter_methods[] = {
+ { GDBUS_METHOD("RegisterProximity", NULL, NULL,
+ register_proximity) },
+ { GDBUS_METHOD("UnregisterProximity", NULL, NULL,
+ unregister_proximity) },
+ { }
+};
+#endif
+
+void reporter_device_remove(struct btd_service *service)
+{
+ struct btd_device *device = btd_service_get_device(service);
+ struct reporter_adapter *radapter;
+ struct btd_adapter *adapter = device_get_adapter(device);
+
+ radapter = find_reporter_adapter(adapter);
+ if (!radapter)
+ return;
+
+ unregister_reporter_device(device, radapter);
+}
+
+int reporter_adapter_probe(struct btd_profile *p, struct btd_adapter *adapter)
+{
+ struct reporter_adapter *radapter;
+
+ radapter = g_new0(struct reporter_adapter, 1);
+ radapter->adapter = adapter;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ const char *path = adapter_get_path(adapter);
+
+ g_dbus_register_interface(btd_get_dbus_connection(), path,
+ PROXIMITY_REPORTER_INTERFACE,
+ reporter_adapter_methods,
+ NULL, NULL, adapter, NULL);
+#else
+ link_loss_register(adapter);
+ register_tx_power(adapter);
+ imm_alert_register(adapter);
+#endif
+ reporter_adapters = g_slist_prepend(reporter_adapters, radapter);
+ DBG("Proximity Reporter for adapter %p", adapter);
+
+ return 0;
+}
+
+void reporter_adapter_remove(struct btd_profile *p,
+ struct btd_adapter *adapter)
+{
+ struct reporter_adapter *radapter = find_reporter_adapter(adapter);
+ if (!radapter)
+ return;
+
+ g_slist_foreach(radapter->devices, unregister_reporter_device,
+ radapter);
+
+ link_loss_unregister(adapter);
+ imm_alert_unregister(adapter);
+
+ reporter_adapters = g_slist_remove(reporter_adapters, radapter);
+ g_free(radapter);
+}
diff --git a/profiles/proximity/reporter.h b/profiles/proximity/reporter.h
new file mode 100755
index 00000000..ed2c4dc5
--- /dev/null
+++ b/profiles/proximity/reporter.h
@@ -0,0 +1,46 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ * Copyright (C) 2011 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#define PROXIMITY_REPORTER_INTERFACE "org.bluez.ProximityReporter1"
+
+#define IMMEDIATE_ALERT_SVC_UUID 0x1802
+#define LINK_LOSS_SVC_UUID 0x1803
+#define TX_POWER_SVC_UUID 0x1804
+#define ALERT_LEVEL_CHR_UUID 0x2A06
+#define POWER_LEVEL_CHR_UUID 0x2A07
+
+enum {
+ NO_ALERT = 0x00,
+ MILD_ALERT = 0x01,
+ HIGH_ALERT = 0x02,
+};
+
+void reporter_device_remove(struct btd_service *service);
+int reporter_device_probe(struct btd_service *service);
+
+int reporter_adapter_probe(struct btd_profile *p, struct btd_adapter *adapter);
+void reporter_adapter_remove(struct btd_profile *p,
+ struct btd_adapter *adapter);
+
+const char *get_alert_level_string(uint8_t level); \ No newline at end of file
diff --git a/profiles/tds/manager.c b/profiles/tds/manager.c
new file mode 100755
index 00000000..6d13ee32
--- /dev/null
+++ b/profiles/tds/manager.c
@@ -0,0 +1,68 @@
+/*
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2016 Samsung Electronics Co. Ltd.
+ *
+ * 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 "lib/bluetooth.h"
+#include "lib/uuid.h"
+#include "src/plugin.h"
+
+#include "gdbus/gdbus.h"
+
+#include "src/adapter.h"
+#include "src/profile.h"
+
+#include "tds.h"
+
+int tds_provider_adapter_probe(struct btd_profile *p,
+ struct btd_adapter *adapter)
+{
+ tds_register_provider_interface(adapter);
+ return 0;
+}
+
+void tds_provider_adapter_remove(struct btd_profile *p,
+ struct btd_adapter *adapter)
+{
+ tds_unregister_provider_interface(adapter);
+}
+
+static struct btd_profile tds_provider = {
+ .name = "TDS Provider GATT Driver",
+ .remote_uuid = GATT_UUID,
+ .adapter_probe = tds_provider_adapter_probe,
+ .adapter_remove = tds_provider_adapter_remove,
+};
+
+static int tds_provider_init(void)
+{
+ return btd_profile_register(&tds_provider);
+}
+
+static void tds_provider_exit(void)
+{
+ btd_profile_unregister(&tds_provider);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(tds, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+ tds_provider_init, tds_provider_exit)
diff --git a/profiles/tds/tds.c b/profiles/tds/tds.c
new file mode 100755
index 00000000..786bf3dc
--- /dev/null
+++ b/profiles/tds/tds.c
@@ -0,0 +1,785 @@
+/*
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2016 Samsung Electronics Co. Ltd.
+ *
+ * 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 <stdbool.h>
+
+#include <glib.h>
+
+#include <dbus/dbus.h>
+#include <time.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+#include "src/plugin.h"
+
+#include "gdbus/gdbus.h"
+
+#include "src/error.h"
+#include "src/log.h"
+#include "src/adapter.h"
+
+#include "attrib/gattrib.h"
+#include "attrib/att.h"
+#include "attrib/gatt.h"
+#include "attrib/att-database.h"
+#include "attrib/gatt-service.h"
+
+#include "src/shared/gatt-server.h"
+#include "src/attrib-server.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/attio.h"
+#include "src/dbus-common.h"
+
+#include "tds.h"
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#include "src/shared/queue.h"
+#include "src/shared/gatt-db.h"
+#include "src/shared/att.h"
+ #include "btio/btio.h"
+#include "src/gatt-database.h"
+#endif
+
+
+#define TDS_USER_CHARACTERITIC_UUID 0x2af6
+#define TDS_USER_CHARACTERITIC_DESCRIPTOR_UUID 0x2a56
+
+/* TDS Block Data */
+struct tds_block_data {
+ uint8_t *val;
+ unsigned int len;
+};
+
+/* pointer to User characteristic data */
+static struct tds_block_data *ptr = NULL;
+
+/* Adapter Instance for the provider */
+struct tds_service_adapter {
+ struct btd_adapter *adapter;
+ struct gatt_db_attribute *service;
+ GSList *connected_devices;
+};
+
+static GSList *tds_service_adapters;
+
+struct connected_device {
+ struct btd_device *device;
+ struct tds_service_adapter *adapter;
+ guint callback_id;
+ uint16_t gatt_chr_handle;
+ unsigned int timeout_id;
+ bool tds_control_point_ccc_enabled;
+};
+
+static int tds_adapter_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct tds_service_adapter *tdsadapter = a;
+ const struct btd_adapter *adapter = b;
+
+ if (tdsadapter->adapter == adapter)
+ return 0;
+
+ return -1;
+}
+
+static struct tds_service_adapter *
+find_tds_service_adapter(struct btd_adapter *adapter)
+{
+ GSList *l = g_slist_find_custom(tds_service_adapters, adapter,
+ tds_adapter_cmp);
+ if (!l)
+ return NULL;
+
+ return l->data;
+}
+
+static int device_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct connected_device *condev = a;
+ const struct btd_device *device = b;
+
+ if (condev->device == device)
+ return 0;
+
+ return -1;
+}
+
+static struct connected_device *
+find_connected_device(struct tds_service_adapter *adapter, struct btd_device *device)
+{
+ GSList *l = g_slist_find_custom(adapter->connected_devices, device,
+ device_cmp);
+ if (!l)
+ return NULL;
+
+ return l->data;
+}
+
+static void indication_cfm_cb(void *user_data)
+{
+ struct connected_device *condev = user_data;
+ DBG("Received confirmation of Indication Confirmation");
+ g_dbus_emit_signal(btd_get_dbus_connection(), device_get_path(condev->device),
+ TDS_SERVICE_PROVIDER_INTERFACE, "TdsActivationIndCnfm",
+ DBUS_TYPE_INVALID);
+}
+
+static DBusMessage *tds_activation_response(DBusConnection *connection,
+ DBusMessage *msg, void *user_data)
+{
+ struct connected_device *condev = user_data;
+ uint8_t *value;
+ int32_t len = 0;
+ uint8_t result = 0x04; /* Operation Failed */
+ int k; /* Debug */
+ uint8_t *pdu = NULL;
+
+ DBG("+");
+ if (condev->tds_control_point_ccc_enabled == false) {
+ DBG("CCCD is disabled, can not send indication to remote device");
+ return dbus_message_new_method_return(msg);
+ }
+
+ if (condev->timeout_id == 0) {
+ DBG("Timer is not running: either no request pending or response came late!!");
+ return btd_error_failed(msg, "TDS Activation Request not pending");
+ }
+
+ /* Remove & reset Timer */
+ g_source_remove(condev->timeout_id);
+ condev->timeout_id = 0;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_BYTE, &result,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &value, &len,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ DBG("Result [0x%x] data length [%d]", result, len);
+
+ for(k=0; k < len ; k++)
+ DBG("Data[%d] = [0x%x]", k, value[k]);
+
+ switch(result) {
+ case 0x00:
+ DBG("Success");
+ break;
+ case 0x02:
+ DBG("Invalid Parameter");
+ break;
+ case 0x03:
+ DBG("Unsupported Organization ID");
+ break;
+ case 0x04:
+ DBG("Operation Failed");
+ break;
+ default:
+ return btd_error_invalid_args(msg);
+ }
+
+ pdu = g_malloc0(sizeof(uint8_t)* (2+ len));
+ pdu[0] = 0x01; /* Opcode - TDS Control Point Activation Request */
+ pdu[1] = result;
+
+ if (len > 0) {
+ memcpy(pdu+2, value, len);
+ } else {
+ DBG("TDS Response with no parameters");
+ }
+
+ DBG("Send Indication to device [%s], chr handle [%d]", device_get_path(condev->device), condev->gatt_chr_handle);
+
+ if (!bt_gatt_server_send_indication(btd_device_get_gatt_server(condev->device),
+ condev->gatt_chr_handle,
+ pdu, (2+len), indication_cfm_cb, condev, NULL))
+ DBG("Sending Indication Failed!!");
+ else
+ DBG("Sending Indication Successful, wait for confirmation!!");
+
+ g_free(pdu);
+ DBG("-");
+ return dbus_message_new_method_return(msg);
+}
+
+static void tds_client_remove_condev(struct connected_device *condev)
+{
+ struct tds_service_adapter *a;
+
+ if (!condev)
+ return;
+
+ a = condev->adapter;
+
+ if (condev->callback_id && condev->device)
+ btd_device_remove_attio_callback(condev->device,
+ condev->callback_id);
+
+ if (condev->device)
+ btd_device_unref(condev->device);
+
+ a->connected_devices = g_slist_remove(a->connected_devices, condev);
+ g_free(condev);
+}
+
+static void tds_client_disc_cb(gpointer user_data)
+{
+ struct connected_device *condev = user_data;
+
+ if (!condev)
+ return;
+
+ /* Unregister Interface */
+ g_dbus_unregister_interface(btd_get_dbus_connection(),
+ device_get_path(condev->device),
+ TDS_SERVICE_PROVIDER_INTERFACE);
+
+ DBG("TDS Client remove device %p", condev->device);
+ tds_client_remove_condev(condev);
+}
+
+static const GDBusSignalTable tds_signals[] = {
+ { GDBUS_SIGNAL("TdsActivationRequested",
+ GDBUS_ARGS({ "org_id", "y"},
+ { "TdsDataBlock", "ay"})) },
+ { GDBUS_SIGNAL("TdsActivationIndCnfm", NULL) },
+};
+
+static const GDBusMethodTable tds_methods[] = {
+ { GDBUS_ASYNC_METHOD("TdsActivationResponse",
+ GDBUS_ARGS({ "result", "y" }, { "response_param", "ay" }), NULL,
+ tds_activation_response) },
+ { }
+};
+
+static bool indication_wait_cb(gpointer user_data)
+{
+ struct connected_device *condev = (struct connected_device *)user_data;
+ uint16_t len = 2;
+ uint8_t pdu[2];
+ DBG("Indication Timer Expired!!");
+ condev->timeout_id = 0;
+
+ if (!condev->tds_control_point_ccc_enabled) {
+ DBG("CCCD is not Enabled!! No need to send indication");
+ return false;
+ } else {
+ DBG("CCCD is Enabled!!..Send Indication with Operation Failed!");
+ }
+
+ pdu[0] = 0x01; /* Op Code: Activation Request */
+ pdu[1] = 0x04; /* Result: Operation Failed*/
+
+ DBG("Send Indication to device [%s], chr handle [%d]", device_get_path(condev->device), condev->gatt_chr_handle);
+
+ if (!bt_gatt_server_send_indication(btd_device_get_gatt_server(condev->device),
+ condev->gatt_chr_handle,
+ pdu, len, indication_cfm_cb, condev, NULL))
+ DBG("Sending Indication Failed!!");
+ else
+ DBG("Sending Indication Successful, wait for confirmation!!");
+
+ return false;
+}
+
+static void tds_control_point_char_write(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ const uint8_t *value, size_t len,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ DBG("len [%d]", len);
+ DBG("Opcode [%d]", opcode);
+ DBG("TRansaction ID [%d]", id);
+ DBG("Offset [%d]", offset);
+
+ uint8_t ecode = 0;
+ struct btd_device *device = NULL;
+ struct tds_service_adapter *tsadapter = user_data;
+ bdaddr_t bdaddr;
+ uint8_t bdaddr_type;
+ struct connected_device *condev = NULL;
+ int k;
+ const uint8_t *param = NULL;
+
+ if (!value || len < 2) {
+ ecode = BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN;
+ goto done;
+ }
+
+ if (offset != 0) {
+ ecode = BT_ATT_ERROR_INVALID_OFFSET;
+ goto done;
+ }
+
+ if (!bt_att_get_remote_addr(att, &bdaddr, &bdaddr_type)) {
+ ecode = BT_ATT_ERROR_UNLIKELY;
+ goto done;
+ }
+
+ device = btd_adapter_get_device(tsadapter->adapter, &bdaddr, bdaddr_type);
+
+ if (!device) {
+ ecode = BT_ATT_ERROR_UNLIKELY;
+ goto done;
+ }
+ DBG("Device path [%s]", device_get_path(device));
+
+ /* Create Connected device and Register SIgnal Interface */
+ condev = find_connected_device(tsadapter, device);
+
+ if (!condev) {
+ DBG("Device is NULL..create device");
+ condev = g_new0(struct connected_device, 1);
+ condev->device = btd_device_ref(device);
+ condev->adapter = tsadapter;
+ condev->callback_id = btd_device_add_attio_callback(device,
+ NULL, tds_client_disc_cb, condev);
+
+ tsadapter->connected_devices = g_slist_append(tsadapter->connected_devices,
+ condev);
+ DBG("added connected dev %p", device);
+ /* Register Signal on Device Interface */
+ if (!g_dbus_register_interface(btd_get_dbus_connection(), device_get_path(device),
+ TDS_SERVICE_PROVIDER_INTERFACE,
+ tds_methods, tds_signals,
+ NULL,
+ condev, NULL)) {
+ error("Unable to register TDS Activation Signal");
+ tds_client_remove_condev(condev);
+ goto done;
+ }
+ }
+
+ if (condev->timeout_id) {
+ DBG("Already one activation request is under progress from device [%s]", device_get_path(device));
+ ecode = BT_ERROR_ALREADY_IN_PROGRESS;
+ goto done;
+ }
+
+ condev->gatt_chr_handle = gatt_db_attribute_get_handle(attrib);
+ DBG("Characteristic Attribute handle [0x%x]", condev->gatt_chr_handle);
+
+ /* Write value should be anyone of 0x00, 0x01, 0x02 */
+ switch(value[0]) {
+ case 0x00: {
+ DBG("Opcode reserved for future use");
+ ecode = BT_ATT_ERROR_REQUEST_NOT_SUPPORTED;
+ goto done;
+ }
+ case 0x01: {
+ DBG("TDS Control Point Activation Request");
+ break;
+ }
+ default: {
+ DBG("Invalid Opcode [0x%x]", value[0]);
+ ecode = 0x80;
+ goto done;
+ }
+ }
+
+ for(k=0; k < len; k++)
+ DBG("@@TDS Control Point [%d] 0x%x", k, value[k]);
+
+ /* Success case*/
+ if (gatt_db_attribute_write_result(attrib, id, ecode)) {
+ DBG("TDS Control Point Activation write resp sent successfully!!");
+ /* Emit Signal */
+ len = len -2;
+
+ if (len > 0) {
+ param = &value[2];
+ }
+ g_dbus_emit_signal(btd_get_dbus_connection(), device_get_path(device),
+ TDS_SERVICE_PROVIDER_INTERFACE, "TdsActivationRequested",
+ DBUS_TYPE_BYTE, &value[1],
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &param, len,
+ DBUS_TYPE_INVALID);
+
+ /* Start timer for max 10 seconds to wait for Indication from app */
+ if (condev->tds_control_point_ccc_enabled) {
+ DBG("Control point is enabled for device [%s] start the Indication Timer", device_get_path(device));
+ if (condev->timeout_id)
+ g_source_remove(condev->timeout_id);
+ condev->timeout_id = g_timeout_add(10000, (GSourceFunc)indication_wait_cb, condev);
+ } else {
+ DBG("Control point is Not enabled for device [%s] Dont start the Indication Timer",device_get_path(device));
+ }
+ } else {
+ DBG("TDS Control Point Activation write resp sending failed!!!");
+ }
+
+ return;
+done:
+ gatt_db_attribute_write_result(attrib, id, ecode);
+}
+
+static void tds_user_data_descriptor_read_cb(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ DBG("TDS User Characteritsic descriptor Read requested..");
+
+ if (!ptr) {
+ DBG("TDS Block data still not set");
+ gatt_db_attribute_read_result(attrib, id, 0, NULL, 0);
+ } else {
+ gatt_db_attribute_read_result(attrib, id, 0, ptr->val, ptr->len);
+ }
+}
+
+static void tds_control_point_ccc_read_cb(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ struct tds_service_adapter *adapter = user_data;
+ struct btd_device *device = NULL;
+ bdaddr_t bdaddr;
+ uint8_t bdaddr_type;
+ struct connected_device *condev = NULL;
+ uint8_t ecode = 0;
+ uint8_t value[2];
+ DBG("TDS Control Point CCC Read requested..");
+
+ if (!bt_att_get_remote_addr(att, &bdaddr, &bdaddr_type)) {
+ ecode = BT_ATT_ERROR_UNLIKELY;
+ goto done;
+ }
+
+ device = btd_adapter_get_device(adapter->adapter, &bdaddr, bdaddr_type);
+
+ if (!device) {
+ ecode = BT_ATT_ERROR_UNLIKELY;
+ goto done;
+ }
+ DBG("Device path [%s]", device_get_path(device));
+
+ /* Create Connected device and Register Signal Interface */
+ condev = find_connected_device(adapter, device);
+ if (!condev) {
+ DBG("Device is not created yet, default CCCD value is Disabled");
+ value[0] = 0x00;
+ } else {
+ DBG("CCCD is [%s] for device [%s]", condev->tds_control_point_ccc_enabled ? "Enabled" : "Disabled", device_get_path(device));
+ value[0] = condev->tds_control_point_ccc_enabled;
+ }
+
+ value[1] = 0x00;
+
+done:
+ gatt_db_attribute_read_result(attrib, id, ecode, value, 2);
+}
+
+
+static void tds_user_char_read_cb(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ uint8_t value[1];
+ DBG("TDS user char Read requested..");
+ value[0] = 0x01;
+ gatt_db_attribute_read_result(attrib, id, 0, value, 1);
+}
+
+static void tds_control_point_ccc_write_cb(struct gatt_db_attribute *attrib,
+ unsigned int id, uint16_t offset,
+ const uint8_t *value, size_t len,
+ uint8_t opcode, struct bt_att *att,
+ void *user_data)
+{
+ struct tds_service_adapter *adapter = user_data;
+ struct btd_device *device = NULL;
+ bdaddr_t bdaddr;
+ uint8_t bdaddr_type;
+ struct connected_device *condev = NULL;
+ uint8_t ecode = 0;
+ DBG("TDS Control Point CCC Write requested..len [%d] val [0x%x] val [0x%x]", len, value[0], value[1]);
+
+ if (!value || len != 2) {
+ ecode = BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN;
+ goto done;
+ }
+
+ if (offset) {
+ ecode = BT_ATT_ERROR_INVALID_OFFSET;
+ goto done;
+ }
+
+ if (!bt_att_get_remote_addr(att, &bdaddr, &bdaddr_type)) {
+ ecode = BT_ATT_ERROR_UNLIKELY;
+ goto done;
+ }
+
+ device = btd_adapter_get_device(adapter->adapter, &bdaddr, bdaddr_type);
+
+ if (!device) {
+ ecode = BT_ATT_ERROR_UNLIKELY;
+ goto done;
+ }
+ DBG("Device path [%s]", device_get_path(device));
+
+ /* Create Connected device and Register Signal Interface */
+ condev = find_connected_device(adapter, device);
+
+ if (!condev) {
+ DBG("Device is NULL..create device");
+ condev = g_new0(struct connected_device, 1);
+ condev->device = btd_device_ref(device);
+ condev->adapter = adapter;
+ condev->callback_id = btd_device_add_attio_callback(device,
+ NULL, tds_client_disc_cb, condev);
+
+ adapter->connected_devices = g_slist_append(adapter->connected_devices,
+ condev);
+ DBG("added connected dev %p", device);
+
+ /* Register Signal on Device Interface */
+ if (!g_dbus_register_interface(btd_get_dbus_connection(), device_get_path(device),
+ TDS_SERVICE_PROVIDER_INTERFACE,
+ tds_methods, tds_signals,
+ NULL,
+ condev, NULL)) {
+ error("Unable to register TDS Activation Signal");
+ tds_client_remove_condev(condev);
+ goto done;
+ }
+ }
+
+ if (value[0] == 0x00) {
+ DBG("CCCD is Disabled by Client [%s]", device_get_path(device));
+ condev->tds_control_point_ccc_enabled = false;
+ } else if (value[0] == 0x02) { /* Indication */
+ if (condev->tds_control_point_ccc_enabled) {
+ DBG("TDS Control point CCCD Already Enabled\n");
+ goto done;
+ }
+
+ DBG("CCCD is Enabled by Client [%s]", device_get_path(device));
+ condev->tds_control_point_ccc_enabled = true;
+ } else
+ ecode = 0x80;
+
+ DBG("TDS Server: Control Point Enabled: [%s]\n",
+ condev->tds_control_point_ccc_enabled ? "true" : "false");
+
+done:
+ gatt_db_attribute_write_result(attrib, id, ecode);
+}
+
+void tds_service_unregister(struct tds_service_adapter *tsadapter)
+{
+ DBG("TDS Service UnRegister..");
+ struct gatt_db *db;
+
+ /* Remove registered service */
+ if (tsadapter->service) {
+ db = (struct gatt_db *) btd_gatt_database_get_db(btd_adapter_get_database(tsadapter->adapter));
+ gatt_db_remove_service(db, tsadapter->service);
+ }
+
+ if (ptr) {
+ g_free(ptr->val);
+ g_free(ptr);
+ ptr = NULL;
+ }
+}
+
+void tds_service_register(struct tds_service_adapter *tsadapter)
+{
+ DBG("TDS Service Register..");
+ struct gatt_db_attribute *service, *char_tds_control, *char_user_char, *desc_tds_ccc, *desc_user;
+ struct gatt_db *db;
+
+ bt_uuid_t uuid;
+ bt_uuid16_create(&uuid, TRANSPORT_DISCOVERY_SERVICE_UUID);
+
+ db = (struct gatt_db *) btd_gatt_database_get_db(btd_adapter_get_database(tsadapter->adapter));
+
+ /*
+ * TDS Primary Service
+ */
+ service = gatt_db_add_service(db, &uuid, true, 7);
+ if (!service)
+ goto err;
+
+ tsadapter->service = service;
+ DBG("TDS Primary Service added");
+
+ /*
+ * TDS Control Point characteristic.
+ */
+ bt_uuid16_create(&uuid, TDS_CONTROL_POINT_CHARACTERISTIC_UUID);
+ char_tds_control = gatt_db_service_add_characteristic(service, &uuid, BT_ATT_PERM_WRITE,
+ BT_GATT_CHRC_PROP_WRITE | BT_GATT_CHRC_PROP_INDICATE,
+ NULL, /* Non Readable */
+ tds_control_point_char_write, tsadapter);
+
+ if (!char_tds_control)
+ goto err;
+ DBG("TDS Control Point char added");
+
+
+ bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+ desc_tds_ccc = gatt_db_service_add_descriptor(char_tds_control, &uuid,
+ BT_ATT_PERM_READ | BT_ATT_PERM_WRITE,
+ tds_control_point_ccc_read_cb,
+ tds_control_point_ccc_write_cb, tsadapter);
+
+ if (!desc_tds_ccc)
+ goto err;
+ DBG("TDS Control Point CCCD added");
+ /*
+ * TDS User characteristic.
+ */
+ bt_uuid16_create(&uuid, TDS_USER_CHARACTERITIC_UUID);
+ char_user_char = gatt_db_service_add_characteristic(service, &uuid, BT_ATT_PERM_NONE,
+ BT_ATT_PERM_READ,
+ tds_user_char_read_cb,
+ NULL, /* Non Writable */
+ NULL);
+
+ if (!char_user_char)
+ goto err;
+
+ DBG("TDS User Characteristic added");
+ bt_uuid16_create(&uuid, TDS_USER_CHARACTERITIC_DESCRIPTOR_UUID);
+ desc_user = gatt_db_service_add_descriptor(char_user_char, &uuid,
+ BT_ATT_PERM_READ,
+ tds_user_data_descriptor_read_cb,
+ NULL, /* Non Writable */
+ tsadapter);
+ if (!desc_user)
+ goto err;
+
+ DBG("TDS User Char Descriptor added...");
+ gatt_db_service_set_active(service, true);
+
+ DBG("TDS Service activated");
+ return;
+
+err:
+ error("Error adding TDS service");
+ tds_service_unregister(tsadapter);
+}
+
+static DBusMessage *register_tds_proider(DBusConnection *conn, DBusMessage *msg,
+ void *user_data)
+{
+ DBG("TDS Provider Register");
+ struct tds_service_adapter *tsadapter = user_data;
+
+ if (tsadapter->adapter == NULL) {
+ DBG("Adapter is NULL");
+ return btd_error_invalid_args(msg);
+ }
+
+ tds_service_register(tsadapter);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *set_tds_block_data(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ uint8_t *value;
+ int32_t len = 0;
+
+ DBG("Set TDS Block data");
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &value, &len,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ /*TODO Max length to be checked*/
+ if (len < 1)
+ return btd_error_invalid_args(msg);
+
+ if (ptr) {
+ g_free(ptr->val);
+ g_free(ptr);
+ }
+ ptr = g_malloc0(sizeof(struct tds_block_data));
+ ptr->val = g_memdup(value, len);
+ ptr->len = len;
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *unregister_tds_provider(DBusConnection *conn, DBusMessage *msg,
+ void *user_data)
+{
+ struct tds_service_adapter *tsadapter = user_data;
+
+ if (tsadapter->adapter == NULL) {
+ DBG("Adapter is NULL");
+ return btd_error_invalid_args(msg);
+ }
+
+ tds_service_unregister(tsadapter);
+ return dbus_message_new_method_return(msg);
+}
+
+static const GDBusMethodTable tds_provider_adapter_methods[] = {
+ { GDBUS_METHOD("RegisterTdsProvider", NULL, NULL,
+ register_tds_proider) },
+ { GDBUS_METHOD("UnregisterTdsProvider", NULL, NULL,
+ unregister_tds_provider) },
+ { GDBUS_METHOD("SetTdsBlockData",
+ GDBUS_ARGS({ "value", "ay" }), NULL,
+ set_tds_block_data) },
+ { }
+};
+
+void tds_unregister_provider_interface(struct btd_adapter *adapter)
+{
+ struct tds_service_adapter *tsadapter = find_tds_service_adapter(adapter);
+ if (!tsadapter)
+ return;
+ tds_service_unregister(tsadapter);
+
+ tds_service_adapters = g_slist_remove(tds_service_adapters, tsadapter);
+ g_free(tsadapter);
+}
+
+void tds_register_provider_interface(struct btd_adapter *adapter)
+{
+ struct tds_service_adapter *tsadapter;
+ const char *path = adapter_get_path(adapter);
+
+ tsadapter = g_new0(struct tds_service_adapter, 1);
+ tsadapter->adapter = adapter;
+
+ g_dbus_register_interface(btd_get_dbus_connection(), path,
+ TDS_SERVICE_PROVIDER_INTERFACE,
+ tds_provider_adapter_methods,
+ NULL, NULL, tsadapter, NULL);
+ tds_service_adapters = g_slist_append(tds_service_adapters, tsadapter);
+}
diff --git a/profiles/tds/tds.h b/profiles/tds/tds.h
new file mode 100755
index 00000000..85b9b100
--- /dev/null
+++ b/profiles/tds/tds.h
@@ -0,0 +1,34 @@
+/*
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ * Copyright (C) 2011 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#define TRANSPORT_DISCOVERY_SERVICE_UUID 0x1824
+#define TDS_CONTROL_POINT_CHARACTERISTIC_UUID 0x2abc
+
+#define TDS_USER_CHARACTERITIC_UUID 0x2af6
+#define TDS_USER_CHARACTERITIC_DESCRIPTOR_UUID 0x2a56
+
+#define TDS_SERVICE_PROVIDER_INTERFACE "org.bluez.TdsServiceProvider1"
+
+void tds_register_provider_interface(struct btd_adapter *adapter);
+
+void tds_unregister_provider_interface(struct btd_adapter *adapter);
diff --git a/profiles/thermometer/thermometer.c b/profiles/thermometer/thermometer.c
new file mode 100755
index 00000000..b0fc3e00
--- /dev/null
+++ b/profiles/thermometer/thermometer.c
@@ -0,0 +1,1321 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdbool.h>
+#include <errno.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "gdbus/gdbus.h"
+
+#include "src/plugin.h"
+#include "src/dbus-common.h"
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/service.h"
+#include "src/shared/util.h"
+#include "src/error.h"
+#include "src/log.h"
+#include "attrib/gattrib.h"
+#include "src/attio.h"
+#include "attrib/att.h"
+#include "attrib/gatt.h"
+
+#define THERMOMETER_INTERFACE "org.bluez.Thermometer1"
+#define THERMOMETER_MANAGER_INTERFACE "org.bluez.ThermometerManager1"
+#define THERMOMETER_WATCHER_INTERFACE "org.bluez.ThermometerWatcher1"
+
+/* Temperature measurement flag fields */
+#define TEMP_UNITS 0x01
+#define TEMP_TIME_STAMP 0x02
+#define TEMP_TYPE 0x04
+
+#define FLOAT_MAX_MANTISSA 16777216 /* 2^24 */
+
+#define VALID_RANGE_DESC_SIZE 4
+#define TEMPERATURE_TYPE_SIZE 1
+#define MEASUREMENT_INTERVAL_SIZE 2
+
+struct thermometer_adapter {
+ struct btd_adapter *adapter;
+ GSList *devices;
+ GSList *fwatchers; /* Final measurements */
+ GSList *iwatchers; /* Intermediate measurements */
+};
+
+struct thermometer {
+ struct btd_device *dev; /* Device reference */
+ struct thermometer_adapter *tadapter;
+ GAttrib *attrib; /* GATT connection */
+ struct att_range *svc_range; /* Thermometer range */
+ guint attioid; /* Att watcher id */
+ /* attio id for Temperature Measurement value indications */
+ guint attio_measurement_id;
+ /* attio id for Intermediate Temperature value notifications */
+ guint attio_intermediate_id;
+ /* attio id for Measurement Interval value indications */
+ guint attio_interval_id;
+ gboolean intermediate;
+ uint8_t type;
+ uint16_t interval;
+ uint16_t max;
+ uint16_t min;
+ gboolean has_type;
+ gboolean has_interval;
+
+ uint16_t measurement_ccc_handle;
+ uint16_t intermediate_ccc_handle;
+ uint16_t interval_val_handle;
+};
+
+struct characteristic {
+ struct thermometer *t; /* Thermometer where the char belongs */
+ char uuid[MAX_LEN_UUID_STR + 1];
+};
+
+struct watcher {
+ struct thermometer_adapter *tadapter;
+ guint id;
+ char *srv;
+ char *path;
+};
+
+struct measurement {
+ struct thermometer *t;
+ int16_t exp;
+ int32_t mant;
+ uint64_t time;
+ gboolean suptime;
+ char *unit;
+ char *type;
+ char *value;
+};
+
+struct tmp_interval_data {
+ struct thermometer *thermometer;
+ uint16_t interval;
+};
+
+static GSList *thermometer_adapters = NULL;
+
+static const char * const temp_type[] = {
+ "<reserved>",
+ "armpit",
+ "body",
+ "ear",
+ "finger",
+ "intestines",
+ "mouth",
+ "rectum",
+ "toe",
+ "tympanum"
+};
+
+static const char *temptype2str(uint8_t value)
+{
+ if (value > 0 && value < G_N_ELEMENTS(temp_type))
+ return temp_type[value];
+
+ error("Temperature type %d reserved for future use", value);
+ return NULL;
+}
+
+static void destroy_watcher(gpointer user_data)
+{
+ struct watcher *watcher = user_data;
+
+ g_free(watcher->path);
+ g_free(watcher->srv);
+ g_free(watcher);
+}
+
+static void remove_watcher(gpointer user_data)
+{
+ struct watcher *watcher = user_data;
+
+ g_dbus_remove_watch(btd_get_dbus_connection(), watcher->id);
+}
+
+static void destroy_thermometer(gpointer user_data)
+{
+ struct thermometer *t = user_data;
+
+ if (t->attioid > 0)
+ btd_device_remove_attio_callback(t->dev, t->attioid);
+
+ if (t->attrib != NULL) {
+ g_attrib_unregister(t->attrib, t->attio_measurement_id);
+ g_attrib_unregister(t->attrib, t->attio_intermediate_id);
+ g_attrib_unregister(t->attrib, t->attio_interval_id);
+ g_attrib_unref(t->attrib);
+ }
+
+ btd_device_unref(t->dev);
+ g_free(t->svc_range);
+ g_free(t);
+}
+
+static void destroy_thermometer_adapter(gpointer user_data)
+{
+ struct thermometer_adapter *tadapter = user_data;
+
+ if (tadapter->devices != NULL)
+ g_slist_free_full(tadapter->devices, destroy_thermometer);
+
+ if (tadapter->fwatchers != NULL)
+ g_slist_free_full(tadapter->fwatchers, remove_watcher);
+
+ g_free(tadapter);
+}
+
+static int cmp_adapter(gconstpointer a, gconstpointer b)
+{
+ const struct thermometer_adapter *tadapter = a;
+ const struct btd_adapter *adapter = b;
+
+ if (adapter == tadapter->adapter)
+ return 0;
+
+ return -1;
+}
+
+static int cmp_device(gconstpointer a, gconstpointer b)
+{
+ const struct thermometer *t = a;
+ const struct btd_device *dev = b;
+
+ if (dev == t->dev)
+ return 0;
+
+ return -1;
+}
+
+static int cmp_watcher(gconstpointer a, gconstpointer b)
+{
+ const struct watcher *watcher = a;
+ const struct watcher *match = b;
+ int ret;
+
+ ret = g_strcmp0(watcher->srv, match->srv);
+ if (ret != 0)
+ return ret;
+
+ return g_strcmp0(watcher->path, match->path);
+}
+
+static struct thermometer_adapter *
+find_thermometer_adapter(struct btd_adapter *adapter)
+{
+ GSList *l = g_slist_find_custom(thermometer_adapters, adapter,
+ cmp_adapter);
+ if (!l)
+ return NULL;
+
+ return l->data;
+}
+
+static void change_property(struct thermometer *t, const char *name,
+ gpointer value) {
+ if (g_strcmp0(name, "Intermediate") == 0) {
+ gboolean *intermediate = value;
+ if (t->intermediate == *intermediate)
+ return;
+
+ t->intermediate = *intermediate;
+ } else if (g_strcmp0(name, "Interval") == 0) {
+ uint16_t *interval = value;
+ if (t->has_interval && t->interval == *interval)
+ return;
+
+ t->has_interval = TRUE;
+ t->interval = *interval;
+ } else if (g_strcmp0(name, "Maximum") == 0) {
+ uint16_t *max = value;
+ if (t->max == *max)
+ return;
+
+ t->max = *max;
+ } else if (g_strcmp0(name, "Minimum") == 0) {
+ uint16_t *min = value;
+ if (t->min == *min)
+ return;
+
+ t->min = *min;
+ } else {
+ DBG("%s is not a thermometer property", name);
+ return;
+ }
+
+ g_dbus_emit_property_changed(btd_get_dbus_connection(),
+ device_get_path(t->dev),
+ THERMOMETER_INTERFACE, name);
+}
+
+static void update_watcher(gpointer data, gpointer user_data)
+{
+ struct watcher *w = data;
+ struct measurement *m = user_data;
+ const char *path = device_get_path(m->t->dev);
+ DBusMessageIter iter;
+ DBusMessageIter dict;
+ DBusMessage *msg;
+
+ msg = dbus_message_new_method_call(w->srv, w->path,
+ THERMOMETER_WATCHER_INTERFACE,
+ "MeasurementReceived");
+ if (msg == NULL)
+ return;
+
+ dbus_message_iter_init_append(msg, &iter);
+
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH , &path);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+ dict_append_entry(&dict, "Exponent", DBUS_TYPE_INT16, &m->exp);
+ dict_append_entry(&dict, "Mantissa", DBUS_TYPE_INT32, &m->mant);
+ dict_append_entry(&dict, "Unit", DBUS_TYPE_STRING, &m->unit);
+
+ if (m->suptime)
+ dict_append_entry(&dict, "Time", DBUS_TYPE_UINT64, &m->time);
+
+ dict_append_entry(&dict, "Type", DBUS_TYPE_STRING, &m->type);
+ dict_append_entry(&dict, "Measurement", DBUS_TYPE_STRING, &m->value);
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ dbus_message_set_no_reply(msg, TRUE);
+ g_dbus_send_message(btd_get_dbus_connection(), msg);
+}
+
+static void recv_measurement(struct thermometer *t, struct measurement *m)
+{
+ GSList *wlist;
+
+ m->t = t;
+
+ if (g_strcmp0(m->value, "intermediate") == 0)
+ wlist = t->tadapter->iwatchers;
+ else
+ wlist = t->tadapter->fwatchers;
+
+ g_slist_foreach(wlist, update_watcher, m);
+}
+
+static void proc_measurement(struct thermometer *t, const uint8_t *pdu,
+ uint16_t len, gboolean final)
+{
+ struct measurement m;
+ const char *type = NULL;
+ uint8_t flags;
+ uint32_t raw;
+
+ /* skip opcode and handle */
+ pdu += 3;
+ len -= 3;
+
+ if (len < 1) {
+ DBG("Mandatory flags are not provided");
+ return;
+ }
+
+ memset(&m, 0, sizeof(m));
+
+ flags = *pdu;
+
+ if (flags & TEMP_UNITS)
+ m.unit = "fahrenheit";
+ else
+ m.unit = "celsius";
+
+ pdu++;
+ len--;
+
+ if (len < 4) {
+ DBG("Mandatory temperature measurement value is not provided");
+ return;
+ }
+
+ raw = get_le32(pdu);
+ m.mant = raw & 0x00FFFFFF;
+ m.exp = ((int32_t) raw) >> 24;
+
+ if (m.mant & 0x00800000) {
+ /* convert to C2 negative value */
+ m.mant = m.mant - FLOAT_MAX_MANTISSA;
+ }
+
+ pdu += 4;
+ len -= 4;
+
+ if (flags & TEMP_TIME_STAMP) {
+ struct tm ts;
+ time_t time;
+
+ if (len < 7) {
+ DBG("Time stamp is not provided");
+ return;
+ }
+
+ ts.tm_year = get_le16(pdu) - 1900;
+ ts.tm_mon = *(pdu + 2) - 1;
+ ts.tm_mday = *(pdu + 3);
+ ts.tm_hour = *(pdu + 4);
+ ts.tm_min = *(pdu + 5);
+ ts.tm_sec = *(pdu + 6);
+ ts.tm_isdst = -1;
+
+ time = mktime(&ts);
+ m.time = (uint64_t) time;
+ m.suptime = TRUE;
+
+ pdu += 7;
+ len -= 7;
+ }
+
+ if (flags & TEMP_TYPE) {
+ if (len < 1) {
+ DBG("Temperature type is not provided");
+ return;
+ }
+
+ type = temptype2str(*pdu);
+ } else if (t->has_type) {
+ type = temptype2str(t->type);
+ }
+
+ m.type = g_strdup(type);
+ m.value = final ? "final" : "intermediate";
+
+ recv_measurement(t, &m);
+ g_free(m.type);
+}
+
+
+static void measurement_ind_handler(const uint8_t *pdu, uint16_t len,
+ gpointer user_data)
+{
+ struct thermometer *t = user_data;
+ uint8_t *opdu;
+ uint16_t olen;
+ size_t plen;
+
+ if (len < 3) {
+ DBG("Bad pdu received");
+ return;
+ }
+
+ proc_measurement(t, pdu, len, TRUE);
+
+ opdu = g_attrib_get_buffer(t->attrib, &plen);
+ olen = enc_confirmation(opdu, plen);
+
+ if (olen > 0)
+ g_attrib_send(t->attrib, 0, opdu, olen, NULL, NULL, NULL);
+}
+
+static void intermediate_notify_handler(const uint8_t *pdu, uint16_t len,
+ gpointer user_data)
+{
+ struct thermometer *t = user_data;
+
+ if (len < 3) {
+ DBG("Bad pdu received");
+ return;
+ }
+
+ proc_measurement(t, pdu, len, FALSE);
+}
+
+static void interval_ind_handler(const uint8_t *pdu, uint16_t len,
+ gpointer user_data)
+{
+ struct thermometer *t = user_data;
+ uint16_t interval;
+ uint8_t *opdu;
+ uint16_t olen;
+ size_t plen;
+
+ if (len < 5) {
+ DBG("Bad pdu received");
+ return;
+ }
+
+ interval = get_le16(pdu + 3);
+ change_property(t, "Interval", &interval);
+
+ opdu = g_attrib_get_buffer(t->attrib, &plen);
+ olen = enc_confirmation(opdu, plen);
+
+ if (olen > 0)
+ g_attrib_send(t->attrib, 0, opdu, olen, NULL, NULL, NULL);
+}
+
+static void valid_range_desc_cb(guint8 status, const guint8 *pdu, guint16 len,
+ gpointer user_data)
+{
+ struct thermometer *t = user_data;
+ uint8_t value[VALID_RANGE_DESC_SIZE];
+ uint16_t max, min;
+ ssize_t vlen;
+
+ if (status != 0) {
+ DBG("Valid Range descriptor read failed: %s",
+ att_ecode2str(status));
+ return;
+ }
+
+ vlen = dec_read_resp(pdu, len, value, sizeof(value));
+ if (vlen < 0) {
+ DBG("Protocol error\n");
+ return;
+ }
+
+ if (vlen < 4) {
+ DBG("Invalid range received");
+ return;
+ }
+
+ min = get_le16(&value[0]);
+ max = get_le16(&value[2]);
+
+ if (min == 0 || min > max) {
+ DBG("Invalid range");
+ return;
+ }
+
+ change_property(t, "Maximum", &max);
+ change_property(t, "Minimum", &min);
+}
+
+static void write_ccc_cb(guint8 status, const guint8 *pdu,
+ guint16 len, gpointer user_data)
+{
+ char *msg = user_data;
+
+ if (status != 0)
+ error("%s failed", msg);
+
+ g_free(msg);
+}
+
+static void process_thermometer_desc(struct characteristic *ch, uint16_t uuid,
+ uint16_t handle)
+{
+ uint8_t atval[2];
+ uint16_t val;
+ char *msg;
+
+ if (uuid == GATT_CHARAC_VALID_RANGE_UUID) {
+ if (g_strcmp0(ch->uuid, MEASUREMENT_INTERVAL_UUID) == 0)
+ gatt_read_char(ch->t->attrib, handle,
+ valid_range_desc_cb, ch->t);
+ return;
+ }
+
+ if (uuid != GATT_CLIENT_CHARAC_CFG_UUID)
+ return;
+
+ if (g_strcmp0(ch->uuid, TEMPERATURE_MEASUREMENT_UUID) == 0) {
+ ch->t->measurement_ccc_handle = handle;
+
+ if (g_slist_length(ch->t->tadapter->fwatchers) == 0) {
+ val = 0x0000;
+ msg = g_strdup("Disable Temperature Measurement ind");
+ } else {
+ val = GATT_CLIENT_CHARAC_CFG_IND_BIT;
+ msg = g_strdup("Enable Temperature Measurement ind");
+ }
+ } else if (g_strcmp0(ch->uuid, INTERMEDIATE_TEMPERATURE_UUID) == 0) {
+ ch->t->intermediate_ccc_handle = handle;
+
+ if (g_slist_length(ch->t->tadapter->iwatchers) == 0) {
+ val = 0x0000;
+ msg = g_strdup("Disable Intermediate Temperature noti");
+ } else {
+ val = GATT_CLIENT_CHARAC_CFG_NOTIF_BIT;
+ msg = g_strdup("Enable Intermediate Temperature noti");
+ }
+ } else if (g_strcmp0(ch->uuid, MEASUREMENT_INTERVAL_UUID) == 0) {
+ val = GATT_CLIENT_CHARAC_CFG_IND_BIT;
+ msg = g_strdup("Enable Measurement Interval indication");
+ } else {
+ return;
+ }
+
+ put_le16(val, atval);
+ gatt_write_char(ch->t->attrib, handle, atval, sizeof(atval),
+ write_ccc_cb, msg);
+}
+
+static void discover_desc_cb(guint8 status, GSList *descs, gpointer user_data)
+{
+ struct characteristic *ch = user_data;
+
+ if (status != 0) {
+ error("Discover all characteristic descriptors failed [%s]: %s",
+ ch->uuid, att_ecode2str(status));
+ goto done;
+ }
+
+ for ( ; descs; descs = descs->next) {
+ struct gatt_desc *desc = descs->data;
+
+ process_thermometer_desc(ch, desc->uuid16, desc->handle);
+ }
+
+done:
+ g_free(ch);
+}
+
+static void discover_desc(struct thermometer *t, struct gatt_char *c,
+ struct gatt_char *c_next)
+{
+ struct characteristic *ch;
+ uint16_t start, end;
+
+ start = c->value_handle + 1;
+
+ if (c_next != NULL) {
+ if (start == c_next->handle)
+ return;
+ end = c_next->handle - 1;
+ } else if (c->value_handle != t->svc_range->end) {
+ end = t->svc_range->end;
+ } else {
+ return;
+ }
+
+ ch = g_new0(struct characteristic, 1);
+ ch->t = t;
+ memcpy(ch->uuid, c->uuid, sizeof(c->uuid));
+
+ gatt_discover_desc(t->attrib, start, end, NULL, discover_desc_cb, ch);
+}
+
+static void read_temp_type_cb(guint8 status, const guint8 *pdu, guint16 len,
+ gpointer user_data)
+{
+ struct thermometer *t = user_data;
+ uint8_t value[TEMPERATURE_TYPE_SIZE];
+ ssize_t vlen;
+
+ if (status != 0) {
+ DBG("Temperature Type value read failed: %s",
+ att_ecode2str(status));
+ return;
+ }
+
+ vlen = dec_read_resp(pdu, len, value, sizeof(value));
+ if (vlen < 0) {
+ DBG("Protocol error.");
+ return;
+ }
+
+ if (vlen != 1) {
+ DBG("Invalid length for Temperature type");
+ return;
+ }
+
+ t->has_type = TRUE;
+ t->type = value[0];
+}
+
+static void read_interval_cb(guint8 status, const guint8 *pdu, guint16 len,
+ gpointer user_data)
+{
+ struct thermometer *t = user_data;
+ uint8_t value[MEASUREMENT_INTERVAL_SIZE];
+ uint16_t interval;
+ ssize_t vlen;
+
+ if (status != 0) {
+ DBG("Measurement Interval value read failed: %s",
+ att_ecode2str(status));
+ return;
+ }
+
+ vlen = dec_read_resp(pdu, len, value, sizeof(value));
+ if (vlen < 0) {
+ DBG("Protocol error\n");
+ return;
+ }
+
+ if (vlen < 2) {
+ DBG("Invalid Interval received");
+ return;
+ }
+
+ interval = get_le16(&value[0]);
+ change_property(t, "Interval", &interval);
+}
+
+static void process_thermometer_char(struct thermometer *t,
+ struct gatt_char *c, struct gatt_char *c_next)
+{
+ if (g_strcmp0(c->uuid, INTERMEDIATE_TEMPERATURE_UUID) == 0) {
+ gboolean intermediate = TRUE;
+ change_property(t, "Intermediate", &intermediate);
+
+ t->attio_intermediate_id = g_attrib_register(t->attrib,
+ ATT_OP_HANDLE_NOTIFY, c->value_handle,
+ intermediate_notify_handler, t, NULL);
+
+ discover_desc(t, c, c_next);
+ } else if (g_strcmp0(c->uuid, TEMPERATURE_MEASUREMENT_UUID) == 0) {
+
+ t->attio_measurement_id = g_attrib_register(t->attrib,
+ ATT_OP_HANDLE_IND, c->value_handle,
+ measurement_ind_handler, t, NULL);
+
+ discover_desc(t, c, c_next);
+ } else if (g_strcmp0(c->uuid, TEMPERATURE_TYPE_UUID) == 0) {
+ gatt_read_char(t->attrib, c->value_handle,
+ read_temp_type_cb, t);
+ } else if (g_strcmp0(c->uuid, MEASUREMENT_INTERVAL_UUID) == 0) {
+ bool need_desc = false;
+
+ gatt_read_char(t->attrib, c->value_handle, read_interval_cb, t);
+
+ if (c->properties & GATT_CHR_PROP_WRITE) {
+ t->interval_val_handle = c->value_handle;
+ need_desc = true;
+ }
+
+ if (c->properties & GATT_CHR_PROP_INDICATE) {
+ t->attio_interval_id = g_attrib_register(t->attrib,
+ ATT_OP_HANDLE_IND, c->value_handle,
+ interval_ind_handler, t, NULL);
+ need_desc = true;
+ }
+
+ if (need_desc)
+ discover_desc(t, c, c_next);
+ }
+}
+
+static void configure_thermometer_cb(uint8_t status, GSList *characteristics,
+ void *user_data)
+{
+ struct thermometer *t = user_data;
+ GSList *l;
+
+ if (status != 0) {
+ error("Discover thermometer characteristics: %s",
+ att_ecode2str(status));
+ return;
+ }
+
+ for (l = characteristics; l; l = l->next) {
+ struct gatt_char *c = l->data;
+ struct gatt_char *c_next = (l->next ? l->next->data : NULL);
+
+ process_thermometer_char(t, c, c_next);
+ }
+}
+
+static void write_interval_cb(guint8 status, const guint8 *pdu, guint16 len,
+ gpointer user_data)
+{
+ struct tmp_interval_data *data = user_data;
+
+ if (status != 0) {
+ error("Interval Write Request failed %s",
+ att_ecode2str(status));
+ goto done;
+ }
+
+ if (!dec_write_resp(pdu, len)) {
+ error("Interval Write Request: protocol error");
+ goto done;
+ }
+
+ change_property(data->thermometer, "Interval", &data->interval);
+
+done:
+ g_free(user_data);
+}
+
+static void enable_final_measurement(gpointer data, gpointer user_data)
+{
+ struct thermometer *t = data;
+ uint16_t handle = t->measurement_ccc_handle;
+ uint8_t value[2];
+ char *msg;
+
+ if (t->attrib == NULL || !handle)
+ return;
+
+ put_le16(GATT_CLIENT_CHARAC_CFG_IND_BIT, value);
+ msg = g_strdup("Enable Temperature Measurement indications");
+
+ gatt_write_char(t->attrib, handle, value, sizeof(value),
+ write_ccc_cb, msg);
+}
+
+static void enable_intermediate_measurement(gpointer data, gpointer user_data)
+{
+ struct thermometer *t = data;
+ uint16_t handle = t->intermediate_ccc_handle;
+ uint8_t value[2];
+ char *msg;
+
+ if (t->attrib == NULL || !handle)
+ return;
+
+ put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value);
+ msg = g_strdup("Enable Intermediate Temperature notifications");
+
+ gatt_write_char(t->attrib, handle, value, sizeof(value),
+ write_ccc_cb, msg);
+}
+
+static void disable_final_measurement(gpointer data, gpointer user_data)
+{
+ struct thermometer *t = data;
+ uint16_t handle = t->measurement_ccc_handle;
+ uint8_t value[2];
+ char *msg;
+
+ if (t->attrib == NULL || !handle)
+ return;
+
+ put_le16(0x0000, value);
+ msg = g_strdup("Disable Temperature Measurement indications");
+
+ gatt_write_char(t->attrib, handle, value, sizeof(value),
+ write_ccc_cb, msg);
+}
+
+static void disable_intermediate_measurement(gpointer data, gpointer user_data)
+{
+ struct thermometer *t = data;
+ uint16_t handle = t->intermediate_ccc_handle;
+ uint8_t value[2];
+ char *msg;
+
+ if (t->attrib == NULL || !handle)
+ return;
+
+ put_le16(0x0000, value);
+ msg = g_strdup("Disable Intermediate Temperature notifications");
+
+ gatt_write_char(t->attrib, handle, value, sizeof(value),
+ write_ccc_cb, msg);
+}
+
+static void remove_int_watcher(struct thermometer_adapter *tadapter,
+ struct watcher *w)
+{
+ if (!g_slist_find(tadapter->iwatchers, w))
+ return;
+
+ tadapter->iwatchers = g_slist_remove(tadapter->iwatchers, w);
+
+ if (g_slist_length(tadapter->iwatchers) == 0)
+ g_slist_foreach(tadapter->devices,
+ disable_intermediate_measurement, 0);
+}
+
+static void watcher_exit(DBusConnection *conn, void *user_data)
+{
+ struct watcher *watcher = user_data;
+ struct thermometer_adapter *tadapter = watcher->tadapter;
+
+ DBG("Thermometer watcher %s disconnected", watcher->path);
+
+ remove_int_watcher(tadapter, watcher);
+
+ tadapter->fwatchers = g_slist_remove(tadapter->fwatchers, watcher);
+ g_dbus_remove_watch(btd_get_dbus_connection(), watcher->id);
+
+ if (g_slist_length(tadapter->fwatchers) == 0)
+ g_slist_foreach(tadapter->devices,
+ disable_final_measurement, 0);
+}
+
+static struct watcher *find_watcher(GSList *list, const char *sender,
+ const char *path)
+{
+ struct watcher *match;
+ GSList *l;
+
+ match = g_new0(struct watcher, 1);
+ match->srv = g_strdup(sender);
+ match->path = g_strdup(path);
+
+ l = g_slist_find_custom(list, match, cmp_watcher);
+ destroy_watcher(match);
+
+ if (l != NULL)
+ return l->data;
+
+ return NULL;
+}
+
+static DBusMessage *register_watcher(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ const char *sender = dbus_message_get_sender(msg);
+ struct thermometer_adapter *tadapter = data;
+ struct watcher *watcher;
+ char *path;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ watcher = find_watcher(tadapter->fwatchers, sender, path);
+ if (watcher != NULL)
+ return btd_error_already_exists(msg);
+
+ DBG("Thermometer watcher %s registered", path);
+
+ watcher = g_new0(struct watcher, 1);
+ watcher->srv = g_strdup(sender);
+ watcher->path = g_strdup(path);
+ watcher->tadapter = tadapter;
+ watcher->id = g_dbus_add_disconnect_watch(conn, sender, watcher_exit,
+ watcher, destroy_watcher);
+
+ if (g_slist_length(tadapter->fwatchers) == 0)
+ g_slist_foreach(tadapter->devices, enable_final_measurement, 0);
+
+ tadapter->fwatchers = g_slist_prepend(tadapter->fwatchers, watcher);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *unregister_watcher(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ const char *sender = dbus_message_get_sender(msg);
+ struct thermometer_adapter *tadapter = data;
+ struct watcher *watcher;
+ char *path;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ watcher = find_watcher(tadapter->fwatchers, sender, path);
+ if (watcher == NULL)
+ return btd_error_does_not_exist(msg);
+
+ DBG("Thermometer watcher %s unregistered", path);
+
+ remove_int_watcher(tadapter, watcher);
+
+ tadapter->fwatchers = g_slist_remove(tadapter->fwatchers, watcher);
+ g_dbus_remove_watch(btd_get_dbus_connection(), watcher->id);
+
+ if (g_slist_length(tadapter->fwatchers) == 0)
+ g_slist_foreach(tadapter->devices,
+ disable_final_measurement, 0);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *enable_intermediate(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ const char *sender = dbus_message_get_sender(msg);
+ struct thermometer_adapter *ta = data;
+ struct watcher *watcher;
+ char *path;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ watcher = find_watcher(ta->fwatchers, sender, path);
+ if (watcher == NULL)
+ return btd_error_does_not_exist(msg);
+
+ if (find_watcher(ta->iwatchers, sender, path))
+ return btd_error_already_exists(msg);
+
+ DBG("Intermediate measurement watcher %s registered", path);
+
+ if (g_slist_length(ta->iwatchers) == 0)
+ g_slist_foreach(ta->devices,
+ enable_intermediate_measurement, 0);
+
+ ta->iwatchers = g_slist_prepend(ta->iwatchers, watcher);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *disable_intermediate(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ const char *sender = dbus_message_get_sender(msg);
+ struct thermometer_adapter *ta = data;
+ struct watcher *watcher;
+ char *path;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ watcher = find_watcher(ta->iwatchers, sender, path);
+ if (watcher == NULL)
+ return btd_error_does_not_exist(msg);
+
+ DBG("Intermediate measurement %s unregistered", path);
+
+ remove_int_watcher(ta, watcher);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static gboolean property_get_intermediate(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct thermometer *t = data;
+ dbus_bool_t val;
+
+ val = !!t->intermediate;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &val);
+
+ return TRUE;
+}
+
+static gboolean property_get_interval(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct thermometer *t = data;
+
+ if (!t->has_interval)
+ return FALSE;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &t->interval);
+
+ return TRUE;
+}
+
+static void property_set_interval(const GDBusPropertyTable *property,
+ DBusMessageIter *iter,
+ GDBusPendingPropertySet id, void *data)
+{
+ struct thermometer *t = data;
+ struct tmp_interval_data *interval_data;
+ uint16_t val;
+ uint8_t atval[2];
+
+ if (t->interval_val_handle == 0) {
+ g_dbus_pending_property_error(id,
+ ERROR_INTERFACE ".NotSupported",
+ "Operation is not supported");
+ return;
+ }
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16) {
+ g_dbus_pending_property_error(id,
+ ERROR_INTERFACE ".InvalidArguments",
+ "Invalid arguments in method call");
+ return;
+ }
+
+ dbus_message_iter_get_basic(iter, &val);
+
+ if (val < t->min || val > t->max) {
+ g_dbus_pending_property_error(id,
+ ERROR_INTERFACE ".InvalidArguments",
+ "Invalid arguments in method call");
+ return;
+ }
+
+ put_le16(val, &atval[0]);
+
+ interval_data = g_new0(struct tmp_interval_data, 1);
+ interval_data->thermometer = t;
+ interval_data->interval = val;
+ gatt_write_char(t->attrib, t->interval_val_handle, atval, sizeof(atval),
+ write_interval_cb, interval_data);
+
+ g_dbus_pending_property_success(id);
+}
+
+static gboolean property_exists_interval(const GDBusPropertyTable *property,
+ void *data)
+{
+ struct thermometer *t = data;
+
+ return t->has_interval;
+}
+
+static gboolean property_get_maximum(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct thermometer *t = data;
+
+ if (!t->has_interval)
+ return FALSE;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &t->max);
+
+ return TRUE;
+}
+
+static gboolean property_get_minimum(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct thermometer *t = data;
+
+ if (!t->has_interval)
+ return FALSE;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &t->min);
+
+ return TRUE;
+}
+
+static const GDBusPropertyTable thermometer_properties[] = {
+ { "Intermediate", "b", property_get_intermediate },
+ { "Interval", "q", property_get_interval, property_set_interval,
+ property_exists_interval },
+ { "Maximum", "q", property_get_maximum, NULL,
+ property_exists_interval },
+ { "Minimum", "q", property_get_minimum, NULL,
+ property_exists_interval },
+ { }
+};
+
+static void attio_connected_cb(GAttrib *attrib, gpointer user_data)
+{
+ struct thermometer *t = user_data;
+
+ t->attrib = g_attrib_ref(attrib);
+
+ gatt_discover_char(t->attrib, t->svc_range->start, t->svc_range->end,
+ NULL, configure_thermometer_cb, t);
+}
+
+static void attio_disconnected_cb(gpointer user_data)
+{
+ struct thermometer *t = user_data;
+
+ DBG("GATT Disconnected");
+
+ if (t->attio_measurement_id > 0) {
+ g_attrib_unregister(t->attrib, t->attio_measurement_id);
+ t->attio_measurement_id = 0;
+ }
+
+ if (t->attio_intermediate_id > 0) {
+ g_attrib_unregister(t->attrib, t->attio_intermediate_id);
+ t->attio_intermediate_id = 0;
+ }
+
+ if (t->attio_interval_id > 0) {
+ g_attrib_unregister(t->attrib, t->attio_interval_id);
+ t->attio_interval_id = 0;
+ }
+
+ g_attrib_unref(t->attrib);
+ t->attrib = NULL;
+}
+
+static int thermometer_register(struct btd_device *device,
+ struct gatt_primary *tattr)
+{
+ const char *path = device_get_path(device);
+ struct thermometer *t;
+ struct btd_adapter *adapter;
+ struct thermometer_adapter *tadapter;
+
+ adapter = device_get_adapter(device);
+
+ tadapter = find_thermometer_adapter(adapter);
+
+ if (tadapter == NULL)
+ return -1;
+
+ t = g_new0(struct thermometer, 1);
+ t->dev = btd_device_ref(device);
+ t->tadapter = tadapter;
+ t->svc_range = g_new0(struct att_range, 1);
+ t->svc_range->start = tattr->range.start;
+ t->svc_range->end = tattr->range.end;
+
+ tadapter->devices = g_slist_prepend(tadapter->devices, t);
+
+ if (!g_dbus_register_interface(btd_get_dbus_connection(),
+ path, THERMOMETER_INTERFACE,
+ NULL, NULL, thermometer_properties,
+ t, destroy_thermometer)) {
+ error("D-Bus failed to register %s interface",
+ THERMOMETER_INTERFACE);
+ destroy_thermometer(t);
+ return -EIO;
+ }
+
+ t->attioid = btd_device_add_attio_callback(device, attio_connected_cb,
+ attio_disconnected_cb, t);
+ return 0;
+}
+
+static void thermometer_unregister(struct btd_device *device)
+{
+ struct thermometer *t;
+ struct btd_adapter *adapter;
+ struct thermometer_adapter *tadapter;
+ GSList *l;
+
+ adapter = device_get_adapter(device);
+
+ tadapter = find_thermometer_adapter(adapter);
+
+ if (tadapter == NULL)
+ return;
+
+ l = g_slist_find_custom(tadapter->devices, device, cmp_device);
+ if (l == NULL)
+ return;
+
+ t = l->data;
+
+ tadapter->devices = g_slist_remove(tadapter->devices, t);
+
+ g_dbus_unregister_interface(btd_get_dbus_connection(),
+ device_get_path(t->dev), THERMOMETER_INTERFACE);
+}
+
+static const GDBusMethodTable thermometer_manager_methods[] = {
+ { GDBUS_METHOD("RegisterWatcher",
+ GDBUS_ARGS({ "agent", "o" }), NULL,
+ register_watcher) },
+ { GDBUS_METHOD("UnregisterWatcher",
+ GDBUS_ARGS({ "agent", "o" }), NULL,
+ unregister_watcher) },
+ { GDBUS_METHOD("EnableIntermediateMeasurement",
+ GDBUS_ARGS({ "agent", "o" }), NULL,
+ enable_intermediate) },
+ { GDBUS_METHOD("DisableIntermediateMeasurement",
+ GDBUS_ARGS({ "agent", "o" }), NULL,
+ disable_intermediate) },
+ { }
+};
+
+static int thermometer_adapter_register(struct btd_adapter *adapter)
+{
+ struct thermometer_adapter *tadapter;
+
+ tadapter = g_new0(struct thermometer_adapter, 1);
+ tadapter->adapter = adapter;
+
+ if (!g_dbus_register_interface(btd_get_dbus_connection(),
+ adapter_get_path(adapter),
+ THERMOMETER_MANAGER_INTERFACE,
+ thermometer_manager_methods,
+ NULL, NULL, tadapter,
+ destroy_thermometer_adapter)) {
+ error("D-Bus failed to register %s interface",
+ THERMOMETER_MANAGER_INTERFACE);
+ destroy_thermometer_adapter(tadapter);
+ return -EIO;
+ }
+
+ thermometer_adapters = g_slist_prepend(thermometer_adapters, tadapter);
+
+ return 0;
+}
+
+static void thermometer_adapter_unregister(struct btd_adapter *adapter)
+{
+ struct thermometer_adapter *tadapter;
+
+ tadapter = find_thermometer_adapter(adapter);
+ if (tadapter == NULL)
+ return;
+
+ thermometer_adapters = g_slist_remove(thermometer_adapters, tadapter);
+
+ g_dbus_unregister_interface(btd_get_dbus_connection(),
+ adapter_get_path(tadapter->adapter),
+ THERMOMETER_MANAGER_INTERFACE);
+}
+
+static int thermometer_device_probe(struct btd_service *service)
+{
+ struct btd_device *device = btd_service_get_device(service);
+ struct gatt_primary *tattr;
+
+ tattr = btd_device_get_primary(device, HEALTH_THERMOMETER_UUID);
+ if (tattr == NULL)
+ return -EINVAL;
+
+ return thermometer_register(device, tattr);
+}
+
+static void thermometer_device_remove(struct btd_service *service)
+{
+ struct btd_device *device = btd_service_get_device(service);
+
+ thermometer_unregister(device);
+}
+
+static int thermometer_adapter_probe(struct btd_profile *p,
+ struct btd_adapter *adapter)
+{
+ return thermometer_adapter_register(adapter);
+}
+
+static void thermometer_adapter_remove(struct btd_profile *p,
+ struct btd_adapter *adapter)
+{
+ thermometer_adapter_unregister(adapter);
+}
+
+static struct btd_profile thermometer_profile = {
+ .name = "Health Thermometer GATT driver",
+ .remote_uuid = HEALTH_THERMOMETER_UUID,
+ .device_probe = thermometer_device_probe,
+ .device_remove = thermometer_device_remove,
+ .adapter_probe = thermometer_adapter_probe,
+ .adapter_remove = thermometer_adapter_remove
+};
+
+static int thermometer_init(void)
+{
+ return btd_profile_register(&thermometer_profile);
+}
+
+static void thermometer_exit(void)
+{
+ btd_profile_unregister(&thermometer_profile);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(thermometer, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+ thermometer_init, thermometer_exit)
diff --git a/profiles/time/server.c b/profiles/time/server.c
new file mode 100755
index 00000000..2289c6a4
--- /dev/null
+++ b/profiles/time/server.c
@@ -0,0 +1,283 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ * Copyright (C) 2011 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <time.h>
+#include <errno.h>
+#include <string.h>
+#include <stdbool.h>
+
+#include <glib.h>
+
+#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "lib/uuid.h"
+
+#include "src/adapter.h"
+#include "src/device.h"
+#include "src/profile.h"
+#include "src/plugin.h"
+#include "attrib/gattrib.h"
+#include "attrib/att.h"
+#include "attrib/gatt.h"
+#include "attrib/att-database.h"
+#include "src/shared/util.h"
+#include "src/attrib-server.h"
+#include "attrib/gatt-service.h"
+#include "src/log.h"
+
+#define CURRENT_TIME_SVC_UUID 0x1805
+#define REF_TIME_UPDATE_SVC_UUID 0x1806
+
+#define LOCAL_TIME_INFO_CHR_UUID 0x2A0F
+#define TIME_UPDATE_CTRL_CHR_UUID 0x2A16
+#define TIME_UPDATE_STAT_CHR_UUID 0x2A17
+#define CT_TIME_CHR_UUID 0x2A2B
+
+enum {
+ UPDATE_RESULT_SUCCESSFUL = 0,
+ UPDATE_RESULT_CANCELED = 1,
+ UPDATE_RESULT_NO_CONN = 2,
+ UPDATE_RESULT_ERROR = 3,
+ UPDATE_RESULT_TIMEOUT = 4,
+ UPDATE_RESULT_NOT_ATTEMPTED = 5,
+};
+
+enum {
+ UPDATE_STATE_IDLE = 0,
+ UPDATE_STATE_PENDING = 1,
+};
+
+enum {
+ GET_REFERENCE_UPDATE = 1,
+ CANCEL_REFERENCE_UPDATE = 2,
+};
+
+static int encode_current_time(uint8_t value[10])
+{
+ struct timespec tp;
+ struct tm tm;
+
+ if (clock_gettime(CLOCK_REALTIME, &tp) == -1) {
+ int err = -errno;
+
+ error("clock_gettime: %s", strerror(-err));
+ return err;
+ }
+
+ if (localtime_r(&tp.tv_sec, &tm) == NULL) {
+ error("localtime_r() failed");
+ /* localtime_r() does not set errno */
+ return -EINVAL;
+ }
+
+ put_le16(1900 + tm.tm_year, &value[0]); /* Year */
+ value[2] = tm.tm_mon + 1; /* Month */
+ value[3] = tm.tm_mday; /* Day */
+ value[4] = tm.tm_hour; /* Hours */
+ value[5] = tm.tm_min; /* Minutes */
+ value[6] = tm.tm_sec; /* Seconds */
+ value[7] = tm.tm_wday == 0 ? 7 : tm.tm_wday; /* Day of Week */
+ /* From Time Profile spec: "The number of 1/256 fractions of a second."
+ * In 1s there are 256 fractions, in 1ns there are 256/10^9 fractions.
+ * To avoid integer overflow, we use the equivalent 1/3906250 ratio. */
+ value[8] = tp.tv_nsec / 3906250; /* Fractions256 */
+ value[9] = 0x00; /* Adjust Reason */
+
+ return 0;
+}
+
+static uint8_t current_time_read(struct attribute *a,
+ struct btd_device *device, gpointer user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ uint8_t value[10];
+
+ if (encode_current_time(value) < 0)
+ return ATT_ECODE_IO;
+
+ attrib_db_update(adapter, a->handle, NULL, value, sizeof(value), NULL);
+
+ return 0;
+}
+
+static uint8_t local_time_info_read(struct attribute *a,
+ struct btd_device *device, gpointer user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ uint8_t value[2];
+
+ DBG("a=%p", a);
+
+ tzset();
+
+ /* Convert POSIX "timezone" (seconds West of GMT) to Time Profile
+ * format (offset from UTC in number of 15 minutes increments). */
+ value[0] = (uint8_t) (-1 * timezone / (60 * 15));
+
+ /* FIXME: POSIX "daylight" variable only indicates whether there
+ * is DST for the local time or not. The offset is unknown. */
+ value[1] = daylight ? 0xff : 0x00;
+
+ attrib_db_update(adapter, a->handle, NULL, value, sizeof(value), NULL);
+
+ return 0;
+}
+
+static gboolean register_current_time_service(struct btd_adapter *adapter)
+{
+ bt_uuid_t uuid;
+
+ bt_uuid16_create(&uuid, CURRENT_TIME_SVC_UUID);
+
+ /* Current Time service */
+ return gatt_service_add(adapter, GATT_PRIM_SVC_UUID, &uuid,
+ /* CT Time characteristic */
+ GATT_OPT_CHR_UUID16, CT_TIME_CHR_UUID,
+ GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ |
+ GATT_CHR_PROP_NOTIFY,
+ GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
+ current_time_read, adapter,
+
+ /* Local Time Information characteristic */
+ GATT_OPT_CHR_UUID16, LOCAL_TIME_INFO_CHR_UUID,
+ GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ,
+ GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
+ local_time_info_read, adapter,
+
+ GATT_OPT_INVALID);
+}
+
+static uint8_t time_update_control(struct attribute *a,
+ struct btd_device *device,
+ gpointer user_data)
+{
+ DBG("handle 0x%04x", a->handle);
+
+ if (a->len != 1)
+ DBG("Invalid control point value size: %zu", a->len);
+
+ switch (a->data[0]) {
+ case GET_REFERENCE_UPDATE:
+ DBG("Get Reference Update");
+ break;
+ case CANCEL_REFERENCE_UPDATE:
+ DBG("Cancel Reference Update");
+ break;
+ default:
+ DBG("Unknown command: 0x%02x", a->data[0]);
+ }
+
+ return 0;
+}
+
+static uint8_t time_update_status(struct attribute *a,
+ struct btd_device *device,
+ gpointer user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ uint8_t value[2];
+
+ DBG("handle 0x%04x", a->handle);
+
+ value[0] = UPDATE_STATE_IDLE;
+ value[1] = UPDATE_RESULT_SUCCESSFUL;
+ attrib_db_update(adapter, a->handle, NULL, value, sizeof(value), NULL);
+
+ return 0;
+}
+
+static gboolean register_ref_time_update_service(struct btd_adapter *adapter)
+{
+ bt_uuid_t uuid;
+
+ bt_uuid16_create(&uuid, REF_TIME_UPDATE_SVC_UUID);
+
+ /* Reference Time Update service */
+ return gatt_service_add(adapter, GATT_PRIM_SVC_UUID, &uuid,
+ /* Time Update control point */
+ GATT_OPT_CHR_UUID16, TIME_UPDATE_CTRL_CHR_UUID,
+ GATT_OPT_CHR_PROPS,
+ GATT_CHR_PROP_WRITE_WITHOUT_RESP,
+ GATT_OPT_CHR_VALUE_CB, ATTRIB_WRITE,
+ time_update_control, adapter,
+
+ /* Time Update status */
+ GATT_OPT_CHR_UUID16, TIME_UPDATE_STAT_CHR_UUID,
+ GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ,
+ GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
+ time_update_status, adapter,
+
+ GATT_OPT_INVALID);
+}
+
+static int time_server_init(struct btd_profile *p, struct btd_adapter *adapter)
+{
+ const char *path = adapter_get_path(adapter);
+
+ DBG("path %s", path);
+
+ if (!register_current_time_service(adapter)) {
+ error("Current Time Service could not be registered");
+ return -EIO;
+ }
+
+ if (!register_ref_time_update_service(adapter)) {
+ error("Reference Time Update Service could not be registered");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static void time_server_exit(struct btd_profile *p,
+ struct btd_adapter *adapter)
+{
+ const char *path = adapter_get_path(adapter);
+
+ DBG("path %s", path);
+}
+
+struct btd_profile time_profile = {
+ .name = "gatt-time-server",
+ .adapter_probe = time_server_init,
+ .adapter_remove = time_server_exit,
+};
+
+static int time_init(void)
+{
+ return btd_profile_register(&time_profile);
+}
+
+static void time_exit(void)
+{
+ btd_profile_unregister(&time_profile);
+}
+
+BLUETOOTH_PLUGIN_DEFINE(time, VERSION,
+ BLUETOOTH_PLUGIN_PRIORITY_DEFAULT,
+ time_init, time_exit)
diff --git a/src/adapter.c b/src/adapter.c
index 5d7a9a97..2a3e40bd 100755
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -77,8 +77,20 @@
#include "advertising.h"
#include "eir.h"
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#include "adapter_le_vsc_features.h"
+#endif
+
#define ADAPTER_INTERFACE "org.bluez.Adapter1"
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#ifdef TIZEN_FEATURE_BLUEZ_BATTERY_WATCH
+#define DEVICED_DEST "org.tizen.system.deviced"
+#define DEVICED_BATT_INTERFACE "org.tizen.system.deviced.Battery"
+#define DEVICED_BATT_OBJECT_PATH "/Org/Tizen/System/DeviceD/Battery"
+#endif /* TIZEN_FEATURE_BLUEZ_BATTERY_WATCH */
+#endif
+
#define MODE_OFF 0x00
#define MODE_CONNECTABLE 0x01
#define MODE_DISCOVERABLE 0x02
@@ -97,6 +109,16 @@
#define DISTANCE_VAL_INVALID 0x7FFF
#define PATHLOSS_MAX 137
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define ADV_DATA_MAX_LENGTH 31
+#define SCAN_RESPONSE_DATA_LENGTH_MAX 31
+#define EIR_MANUFACTURER_DATA_LENGTH_MAX 100
+
+#define LE_BEARER_POSTFIX " LE"
+#define LE_BEARER_POSTFIX_LEN 3
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
+
+
static DBusConnection *dbus_conn = NULL;
static bool kernel_conn_control = false;
@@ -181,6 +203,20 @@ struct btd_adapter_pin_cb_iter {
/* When the iterator reaches the end, it is NULL and attempt is 0 */
};
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+struct adv_info {
+ int slot_id; /* Reservied slot id is 0 (Single adv) */
+ bool status; /* Advertising status */
+};
+
+static GSList *read_requests = NULL;
+
+struct le_data_length_read_request {
+ struct btd_adapter *adapter;
+ DBusMessage *msg;
+};
+#endif
+
struct btd_adapter {
int ref_count;
@@ -188,6 +224,9 @@ struct btd_adapter {
struct mgmt *mgmt;
bdaddr_t bdaddr; /* controller Bluetooth address */
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ bdaddr_t le_static_addr;
+#endif
uint32_t dev_class; /* controller class of device */
char *name; /* controller device name */
char *short_name; /* controller short name */
@@ -205,6 +244,18 @@ struct btd_adapter {
char *current_alias; /* current adapter name alias */
char *stored_alias; /* stored adapter name alias */
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ uint8_t *local_irk; /* adapter local IRK */
+ uint8_t disc_type;
+ bool ipsp_intialized; /* Ipsp Initialization state */
+ struct le_data_length_read_handler *read_handler;
+ struct le_data_length_read_default_data_length_handler *def_read_handler;
+#ifdef TIZEN_FEATURE_BLUEZ_BATTERY_WATCH
+ guint charging_watch;
+ guint charging_timeout;
+ charging_state_e charging;
+#endif /* TIZEN_FEATURE_BLUEZ_BATTERY_WATCH */
+#endif
bool discovering; /* discovering property state */
bool filtered_discovery; /* we are doing filtered discovery */
@@ -222,6 +273,9 @@ struct btd_adapter {
GSList *discovery_found; /* list of found devices */
guint discovery_idle_timeout; /* timeout between discovery runs */
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ guint le_discovery_idle_timeout; /* timeout between le discovery runs */
+#endif
guint passive_scan_timeout; /* timeout between passive scans */
guint temp_devices_timeout; /* timeout for temporary devices */
@@ -239,6 +293,14 @@ struct btd_adapter {
struct btd_advertising *adv_manager;
gboolean initialized;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ GSList *adv_list; /* List of advertising instance */
+ bool advertising; /* Advertising active */
+ gchar *version; /* Bluetooth Version */
+ uint8_t adv_tx_power;
+ bool le_discovering; /* LE Discovery active */
+ GSList *le_discovery_list; /* list of LE discovery clients */
+#endif
GSList *pin_callbacks;
GSList *msd_callbacks;
@@ -258,10 +320,32 @@ struct btd_adapter {
guint pair_device_timeout;
unsigned int db_id; /* Service event handler for GATT db */
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ uint8_t central_rpa_res_support;
+ bluetooth_a2dp_role_t a2dp_role;
+#ifdef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+ bool scan_filter_support; /* platform's scan filtering support */
+ uint8_t scan_type; /* scan type */
+ GSList *scan_params; /* scan filter parameters */
+ GSList *addr_filters; /* adress scan filters list */
+ GSList *service_data_changed_filters; /* service data changed scan filters list */
+ GSList *service_uuid_filters; /* service uuid scan filters list */
+ GSList *solicit_data_filters; /* solicitation data scan filters list */
+ GSList *local_name_filters; /* local name scan filters list */
+ GSList *manufaturer_data_filters; /* manufacturer data scan filters list */
+ GSList *service_data_filters; /* service data scan filters list */
+#endif
+#endif
bool is_default; /* true if adapter is default one */
};
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+enum {
+ DEINIT_6LOWPAN,
+ INIT_6LOWPAN
+};
+#endif
+
static struct btd_adapter *btd_adapter_lookup(uint16_t index)
{
GList *list;
@@ -454,7 +538,13 @@ static void store_adapter_info(struct btd_adapter *adapter)
if (adapter->stored_alias)
g_key_file_set_string(key_file, "General", "Alias",
adapter->stored_alias);
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ /* Store A2DP Role */
+ if (adapter->a2dp_role == BLUETOOTH_A2DP_SINK_ROLE)
+ g_key_file_set_string(key_file, "General", "DefaultA2DPRole", "sink");
+ else
+ g_key_file_set_string(key_file, "General", "DefaultA2DPRole", "source");
+#endif
ba2str(&adapter->bdaddr, address);
snprintf(filename, PATH_MAX, STORAGEDIR "/%s/settings", address);
@@ -467,12 +557,252 @@ static void store_adapter_info(struct btd_adapter *adapter)
g_key_file_free(key_file);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+bluetooth_a2dp_role_t btd_adapter_get_a2dp_role(struct btd_adapter *adapter)
+{
+ if (!adapter)
+ return BLUETOOTH_A2DP_SOURCE_ROLE;
+
+ return adapter->a2dp_role;
+}
+
+void btd_adapter_set_a2dp_role(struct btd_adapter *adapter, bluetooth_a2dp_role_t role)
+{
+ if (!adapter) {
+ DBG("Could not set a2dp role");
+ return;
+ }
+
+ if (role == BLUETOOTH_A2DP_SOURCE_ROLE) {
+ DBG("Set audio source role");
+ adapter->a2dp_role = BLUETOOTH_A2DP_SOURCE_ROLE;
+ } else if (role == BLUETOOTH_A2DP_SINK_ROLE) {
+ DBG("Set audio sink role");
+ adapter->a2dp_role = BLUETOOTH_A2DP_SINK_ROLE;
+ }
+
+ store_adapter_info(adapter);
+}
+#endif
+
static void trigger_pairable_timeout(struct btd_adapter *adapter);
static void adapter_start(struct btd_adapter *adapter);
static void adapter_stop(struct btd_adapter *adapter);
static void trigger_passive_scanning(struct btd_adapter *adapter);
static bool set_mode(struct btd_adapter *adapter, uint16_t opcode,
uint8_t mode);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static bool load_local_irk(struct btd_adapter *adapter);
+static bool set_local_irk(struct btd_adapter *adapter);
+static bool set_privacy(struct btd_adapter *adapter, bool privacy);
+#endif
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#ifdef TIZEN_FEATURE_BLUEZ_BATTERY_WATCH
+static gboolean charging_state_timeout_cb(gpointer user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ int bredr_pkt_type = ACL_PTYPE_MASK;
+
+ adapter->charging_timeout = 0;
+
+ DBG("Set all connections to BR/EDR type");
+ g_slist_foreach(adapter->devices, device_change_pkt_type,
+ (gpointer)bredr_pkt_type);
+
+ return FALSE;
+}
+
+static void set_charging_state(struct btd_adapter *adapter,
+ charging_state_e state)
+{
+ int br_pkt_type = ACL_PTYPE_MASK |
+ HCI_2DH1 | HCI_2DH3 | HCI_2DH5 |
+ HCI_3DH1 | HCI_3DH3 | HCI_3DH5;
+
+ if (adapter->charging == state)
+ return;
+
+ DBG("old charging state : %d, new charging_state : %d",
+ adapter->charging, state);
+
+ /*
+ * Only none / wire charging <-> wireless charging state change should
+ * be handled.
+ */
+ if ((adapter->charging == NONE_CHARGING && state == WIRE_CHARGING) ||
+ (adapter->charging == WIRE_CHARGING && state == NONE_CHARGING)) {
+ DBG("Just update charging state");
+ adapter->charging = state;
+ return;
+ }
+
+ if (adapter->charging_timeout) {
+ g_source_remove(adapter->charging_timeout);
+ adapter->charging_timeout = 0;
+ }
+
+ adapter->charging = state;
+ if (adapter->charging == NONE_CHARGING ||
+ adapter->charging == WIRE_CHARGING) {
+ DBG("Trigger timeout to set connection to BR/EDR type");
+ adapter->charging_timeout = g_timeout_add(2000,
+ charging_state_timeout_cb, adapter);
+ } else if (adapter->charging == WIRELESS_CHARGING) {
+ DBG("Set all connections to BR type");
+ g_slist_foreach(adapter->devices, device_change_pkt_type,
+ (gpointer)br_pkt_type);
+ }
+
+ return;
+}
+
+static gboolean charging_state_changed(DBusConnection *connection,
+ DBusMessage *msg, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ int state = 0;
+
+ DBG("charging_state_changed");
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_INT32, &state,
+ DBUS_TYPE_INVALID))
+ return TRUE;
+
+ set_charging_state(adapter, state);
+
+ return TRUE;
+}
+
+charging_state_e get_charging_state(struct btd_adapter *adapter)
+{
+ DBG("charging_state: %d", adapter->charging);
+ return adapter->charging;
+}
+#endif /* TIZEN_FEATURE_BLUEZ_BATTERY_WATCH */
+
+static int compare_slot(gconstpointer a, gconstpointer b)
+{
+ const struct adv_info *adv = a;
+ const int id = *(int*)b;
+
+ return (adv->slot_id == id ? 0 : -1);
+}
+
+static struct adv_info *find_advertiser(struct btd_adapter *adapter,
+ int slot_id)
+{
+ GSList *list;
+
+ list = g_slist_find_custom(adapter->adv_list, &slot_id,
+ compare_slot);
+ if (list)
+ return list->data;
+
+ return NULL;
+}
+
+static void create_advertiser(struct btd_adapter *adapter,
+ int slot_id)
+{
+ struct adv_info *adv;
+
+ if (!adapter)
+ return;
+
+ if (find_advertiser(adapter, slot_id) != NULL) {
+ error("Aleady existed [%d]", slot_id);
+ return;
+ }
+
+ DBG("Create adv slot id : %d", slot_id);
+
+ adv = g_new0(struct adv_info, 1);
+ if (adv == NULL)
+ return;
+
+ adv->slot_id = slot_id;
+
+ adapter->adv_list= g_slist_append(adapter->adv_list, adv);
+ return;
+}
+
+
+static void advertising_state_changed(struct btd_adapter *adapter,
+ int slot_id, bool enabled)
+{
+ struct adv_info *adv;
+ int id = slot_id;
+ int state = enabled;
+
+ if (!adapter)
+ return;
+
+ adv = find_advertiser(adapter, slot_id);
+ if (!adv) {
+ DBG("Unable to find advertiser [%d]", slot_id);
+ return;
+ }
+
+ adv->status = enabled;
+ DBG("slot_id %d, status %d", adv->slot_id, adv->status);
+
+ g_dbus_emit_signal(dbus_conn, adapter->path,
+ ADAPTER_INTERFACE, "AdvertisingEnabled",
+ DBUS_TYPE_INT32, &id,
+ DBUS_TYPE_BOOLEAN, &state,
+ DBUS_TYPE_INVALID);
+}
+
+static void clear_advertiser_cb(gpointer data, gpointer user_data)
+{
+ struct adv_info *adv = data;
+ struct btd_adapter *adapter = user_data;
+
+ if (adv->status)
+ advertising_state_changed(adapter, adv->slot_id, 0);
+}
+
+static void advertiser_cleanup(struct btd_adapter *adapter)
+{
+ if (!adapter->adv_list)
+ return;
+
+ g_slist_foreach(adapter->adv_list, clear_advertiser_cb, adapter);
+ g_slist_free(adapter->adv_list);
+ adapter->adv_list = NULL;
+}
+#endif
+
+#if defined TIZEN_FEATURE_BLUEZ_MODIFY && defined TIZEN_FEATURE_BLUEZ_SPRD_PAGE_SCAN
+#define OCF_PAGE_SCAN_TIMEOUT 0x0018
+#define OGF_PAGE_SCAN_TIMEOUT 0x03
+
+typedef struct {
+ uint16_t timeout; /* Value */
+} __attribute__ ((packed)) hci_page_scan_timeout;
+#define HCI_PAGE_SCAN_TIMEOUT_CP_SIZE 2
+
+static gboolean send_sprd_page_scan_timeout(gint value)
+{
+ int dd;
+ hci_page_scan_timeout cp;
+ DBG("+");
+ dd = hci_open_dev(0);
+ cp.timeout = value;
+ if (hci_send_cmd(dd, OGF_PAGE_SCAN_TIMEOUT, OCF_PAGE_SCAN_TIMEOUT,
+ HCI_PAGE_SCAN_TIMEOUT_CP_SIZE, &cp) < 0) {
+ DBG("Error: While setting Page Timeout value");
+ hci_close_dev(dd);
+ return FALSE;
+ }
+ DBG("Page Scan Timeout Value Patch %d", value);
+
+ hci_close_dev(dd);
+
+ return TRUE;
+}
+#endif
static void settings_changed(struct btd_adapter *adapter, uint32_t settings)
{
@@ -490,6 +820,15 @@ static void settings_changed(struct btd_adapter *adapter, uint32_t settings)
if (adapter->current_settings & MGMT_SETTING_POWERED) {
adapter_start(adapter);
+#if defined TIZEN_FEATURE_BLUEZ_MODIFY && defined TIZEN_FEATURE_BLUEZ_SPRD_PAGE_SCAN
+ /* Approx 6.4 Seconds of timeout */
+ /* This Added because Z3 device was not able to connect with
+ * some device as it was getting Page Timeout
+ * (LG HBS800, sony carkit) etc. So, Increasing Page timeout value
+ * from 5.12 Sec (which is default) to ~6.4sec*/
+ DBG("Setting value");
+ send_sprd_page_scan_timeout(10240);
+#endif /* TIZEN_FEATURE_BLUEZ_SPRD_PAGE_SCAN */
} else {
adapter_stop(adapter);
@@ -524,6 +863,31 @@ static void settings_changed(struct btd_adapter *adapter, uint32_t settings)
trigger_pairable_timeout(adapter);
}
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (changed_mask & MGMT_SETTING_ADVERTISING) {
+ if ((adapter->current_settings & MGMT_SETTING_ADVERTISING) &&
+ (adapter->advertising)) {
+ return;
+ }
+
+ adapter->advertising = adapter->current_settings & MGMT_SETTING_ADVERTISING;
+ advertising_state_changed(adapter, 0, adapter->advertising);
+ }
+
+ if ((changed_mask & MGMT_SETTING_PRIVACY) &&
+ !(adapter->current_settings & MGMT_SETTING_PRIVACY)) {
+ DBG("LE Privacy feature is disabled");
+
+ /*
+ * Some Android devices don't consider the device as LE one,
+ * if the device doesn't distribute IRK when pairing.
+ * Because of this compatibility issue, set IRK
+ * even though privacy feature is disabled.
+ */
+ set_local_irk(adapter);
+ }
+#endif
}
static void new_settings_callback(uint16_t index, uint16_t length,
@@ -783,6 +1147,10 @@ struct btd_device *btd_adapter_find_device(struct btd_adapter *adapter,
struct device_addr_type addr;
struct btd_device *device;
GSList *list;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ bool exact_match = false;
+#endif
+ char addr_str[18];
if (!adapter)
return NULL;
@@ -790,13 +1158,33 @@ struct btd_device *btd_adapter_find_device(struct btd_adapter *adapter,
bacpy(&addr.bdaddr, dst);
addr.bdaddr_type = bdaddr_type;
+ ba2str(dst, addr_str);
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
list = g_slist_find_custom(adapter->devices, &addr,
+ device_addr_type_strict_cmp);
+ if (list) {
+ device = list->data;
+ exact_match = true;
+ } else {
+#endif
+ list = g_slist_find_custom(adapter->devices, &addr,
device_addr_type_cmp);
+ if (list) {
+ device = list->data;
+ }
+ }
+
if (!list)
return NULL;
device = list->data;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (exact_match)
+ return device;
+#endif
+
/*
* If we're looking up based on public address and the address
* was not previously used over this bearer we may need to
@@ -1045,6 +1433,13 @@ static void adapter_service_insert(struct btd_adapter *adapter, sdp_record_t *re
DBG("%s", adapter->path);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ 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");
@@ -1106,6 +1501,18 @@ void adapter_service_remove(struct btd_adapter *adapter, uint32_t handle)
remove_record_from_server(rec->handle);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void adapter_print_devices(struct btd_adapter *adapter)
+{
+ GSList *dev;
+
+ dev = adapter->devices;
+ for (; dev; dev = dev->next) {
+ device_print_addr(dev->data);
+ }
+}
+#endif
+
static struct btd_device *adapter_create_device(struct btd_adapter *adapter,
const bdaddr_t *bdaddr,
uint8_t bdaddr_type)
@@ -1118,6 +1525,10 @@ static struct btd_device *adapter_create_device(struct btd_adapter *adapter,
adapter->devices = g_slist_append(adapter->devices, device);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ adapter_print_devices(adapter);
+#endif
+
return device;
}
@@ -1144,6 +1555,46 @@ static void service_auth_cancel(struct service_auth *auth)
g_free(auth);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void btd_adapter_unpair_device(struct btd_adapter *adapter,
+ struct btd_device *dev)
+{
+ DBG("+");
+ GList *l;
+
+ adapter->connect_list = g_slist_remove(adapter->connect_list, dev);
+
+// adapter->devices = g_slist_remove(adapter->devices, dev);
+//
+// adapter->discovery_found = g_slist_remove(adapter->discovery_found,
+// dev);
+
+ adapter->connections = g_slist_remove(adapter->connections, dev);
+
+ if (adapter->connect_le == dev)
+ adapter->connect_le = NULL;
+
+ l = adapter->auths->head;
+ while (l != NULL) {
+ struct service_auth *auth = l->data;
+ GList *next = g_list_next(l);
+
+ if (auth->device != dev) {
+ l = next;
+ continue;
+ }
+
+ g_queue_delete_link(adapter->auths, l);
+ l = next;
+
+ service_auth_cancel(auth);
+ }
+
+ device_unpair(dev, TRUE);
+ DBG("-");
+}
+#endif
+
void btd_adapter_remove_device(struct btd_adapter *adapter,
struct btd_device *dev)
{
@@ -1189,6 +1640,11 @@ struct btd_device *btd_adapter_get_device(struct btd_adapter *adapter,
if (!adapter)
return NULL;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (!bacmp(addr, BDADDR_ANY))
+ return NULL;
+#endif
+
device = btd_adapter_find_device(adapter, addr, addr_type);
if (device)
return device;
@@ -1207,6 +1663,11 @@ static void passive_scanning_complete(uint8_t status, uint16_t length,
struct btd_adapter *adapter = user_data;
const struct mgmt_cp_start_discovery *rp = param;
+ if (!rp) {
+ error("Error ocurred in Scanning, rp is NULL");
+ return;
+ }
+
DBG("status 0x%02x", status);
if (length < sizeof(*rp)) {
@@ -1229,11 +1690,15 @@ static gboolean passive_scanning_timeout(gpointer user_data)
adapter->passive_scan_timeout = 0;
cp.type = SCAN_TYPE_LE;
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ mgmt_send(adapter->mgmt, MGMT_OP_START_LE_DISCOVERY,
+ adapter->dev_id, sizeof(cp), &cp,
+ passive_scanning_complete, adapter, NULL);
+#else
mgmt_send(adapter->mgmt, MGMT_OP_START_DISCOVERY,
adapter->dev_id, sizeof(cp), &cp,
passive_scanning_complete, adapter, NULL);
-
+#endif
return FALSE;
}
@@ -1264,8 +1729,13 @@ static void trigger_passive_scanning(struct btd_adapter *adapter)
* The discovery procedure is using interleaved scanning and
* thus will discover Low Energy devices as well.
*/
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (adapter->discovery_list || adapter->le_discovery_list)
+ return;
+#else
if (adapter->discovery_list)
return;
+#endif
if (adapter->discovery_enable == 0x01)
return;
@@ -1339,22 +1809,45 @@ static void stop_passive_scanning_complete(uint8_t status, uint16_t length,
static void stop_passive_scanning(struct btd_adapter *adapter)
{
struct mgmt_cp_stop_discovery cp;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ struct mgmt_cp_stop_discovery le_cp;
+#endif
DBG("");
/* If there are any normal discovery clients passive scanning
* wont be running */
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (adapter->discovery_list || adapter->le_discovery_list)
+ return;
+#else
if (adapter->discovery_list)
return;
+#endif
if (adapter->discovery_enable == 0x00)
return;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if ((adapter->discovery_type & 0x01) > 0) {
+ cp.type = 0x01;
+ mgmt_send(adapter->mgmt, MGMT_OP_STOP_DISCOVERY,
+ adapter->dev_id, sizeof(cp), &cp,
+ stop_passive_scanning_complete, adapter, NULL);
+ }
+ if ((adapter->discovery_type & 0x06) > 0) {
+ le_cp.type = 0x06;
+ mgmt_send(adapter->mgmt, MGMT_OP_STOP_LE_DISCOVERY,
+ adapter->dev_id, sizeof(le_cp), &le_cp,
+ stop_passive_scanning_complete, adapter, NULL);
+ }
+#else
cp.type = adapter->discovery_type;
mgmt_send(adapter->mgmt, MGMT_OP_STOP_DISCOVERY,
adapter->dev_id, sizeof(cp), &cp,
stop_passive_scanning_complete, adapter, NULL);
+#endif
}
static void cancel_passive_scanning(struct btd_adapter *adapter)
@@ -1403,6 +1896,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_FEATURE_BLUEZ_MODIFY
+ DBG("Discovery Type 0x%02x", rp->type);
+#endif
if (length < sizeof(*rp)) {
btd_error(adapter->dev_id,
@@ -1411,7 +1907,12 @@ static void start_discovery_complete(uint8_t status, uint16_t length,
}
if (status == MGMT_STATUS_SUCCESS) {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ DBG("Return param discovery type 0x%02x", rp->type);
+ adapter->discovery_type |= rp->type;
+#else
adapter->discovery_type = rp->type;
+#endif
adapter->discovery_enable = 0x01;
if (adapter->current_discovery_filter)
@@ -1426,25 +1927,145 @@ static void start_discovery_complete(uint8_t status, uint16_t length,
g_dbus_emit_property_changed(dbus_conn, adapter->path,
ADAPTER_INTERFACE, "Discovering");
return;
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ } else {
+ adapter->discovering = false;
+ g_dbus_emit_property_changed(dbus_conn, adapter->path,
+ ADAPTER_INTERFACE, "Discovering");
+#endif
+
}
/*
* In case the restart of the discovery failed, then just trigger
* it for the next idle timeout again.
*/
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
trigger_start_discovery(adapter, IDLE_DISCOV_TIMEOUT * 2);
+#endif
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void start_le_discovery_complete(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ const struct mgmt_cp_start_discovery *rp = param;
+
+ if (!rp) {
+ error("Error ocurred in LEDiscovering, rp is NULL");
+ return;
+ }
+
+ DBG("status 0x%02x", status);
+ if (length < sizeof(*rp)) {
+ error("Wrong size of start discovery return parameters");
+ return;
+ }
+
+ DBG("Discovery Type 0x%02x", rp->type);
+ if (status == MGMT_STATUS_SUCCESS) {
+ adapter->discovery_type |= rp->type;
+ adapter->discovery_enable = 0x01;
+
+ if (adapter->le_discovering)
+ return;
+
+ adapter->le_discovering = true;
+ g_dbus_emit_property_changed(dbus_conn, adapter->path,
+ ADAPTER_INTERFACE, "LEDiscovering");
+
+ return;
+ } else {
+ adapter->le_discovering = false;
+ g_dbus_emit_property_changed(dbus_conn, adapter->path,
+ ADAPTER_INTERFACE, "LEDiscovering");
+
+ }
+}
+#endif
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static gboolean start_le_discovery_timeout(gpointer user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ uint8_t new_type;
+
+ DBG("");
+
+ adapter->le_discovery_idle_timeout = 0;
+
+ new_type = SCAN_TYPE_LE;
+
+ if (adapter->discovery_enable == 0x01) {
+ /*
+ * If there is an already running discovery and it has the
+ * same type, then just keep it.
+ */
+
+ if ((adapter->discovery_type & new_type) == SCAN_TYPE_LE) {
+ if (adapter->le_discovering)
+ return FALSE;
+
+ adapter->le_discovering = true;
+ g_dbus_emit_property_changed(dbus_conn, adapter->path,
+ ADAPTER_INTERFACE, "LEDiscovering");
+
+ return FALSE;
+ }
+ }
+
+ struct mgmt_cp_start_discovery cp;
+
+ cp.type = new_type;
+ mgmt_send(adapter->mgmt, MGMT_OP_START_LE_DISCOVERY,
+ adapter->dev_id, sizeof(cp), &cp,
+ start_le_discovery_complete, adapter, NULL);
+
+ return FALSE;
+}
+#endif
+
static gboolean start_discovery_timeout(gpointer user_data)
{
struct btd_adapter *adapter = user_data;
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
struct mgmt_cp_start_service_discovery *sd_cp;
+#endif
uint8_t new_type;
DBG("");
adapter->discovery_idle_timeout = 0;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ new_type = SCAN_TYPE_BREDR;
+
+ if (adapter->discovery_enable == 0x01) {
+ /*
+ * If there is an already running discovery and it has the
+ * same type, then just keep it.
+ */
+ if ((adapter->discovery_type & new_type) == SCAN_TYPE_BREDR) {
+ if (adapter->discovering)
+ return FALSE;
+
+ adapter->discovering = true;
+ g_dbus_emit_property_changed(dbus_conn, adapter->path,
+ ADAPTER_INTERFACE, "Discovering");
+
+ return FALSE;
+ }
+ }
+
+ struct mgmt_cp_start_discovery cp;
+ cp.type = new_type;
+ mgmt_send(adapter->mgmt, MGMT_OP_START_DISCOVERY,
+ adapter->dev_id, sizeof(cp), &cp,
+ start_discovery_complete, adapter, NULL);
+
+#else
/* If we're doing filtered discovery, it must be quickly restarted */
adapter->no_scan_restart_delay = !!adapter->current_discovery_filter;
@@ -1516,9 +2137,38 @@ static gboolean start_discovery_timeout(gpointer user_data)
adapter->dev_id, sizeof(*sd_cp) + sd_cp->uuid_count * 16,
sd_cp, start_discovery_complete, adapter, NULL);
+#endif
+
return FALSE;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void trigger_start_le_discovery(struct btd_adapter *adapter, guint delay)
+{
+
+ DBG("");
+
+ cancel_passive_scanning(adapter);
+
+ if (adapter->le_discovery_idle_timeout > 0) {
+ g_source_remove(adapter->le_discovery_idle_timeout);
+ adapter->le_discovery_idle_timeout = 0;
+ }
+
+ /*
+ * If the controller got powered down in between, then ensure
+ * that we do not keep trying to restart discovery.
+ *
+ * This is safe-guard and should actually never trigger.
+ */
+ if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+ return;
+
+ adapter->le_discovery_idle_timeout = g_timeout_add_seconds(delay,
+ start_le_discovery_timeout, adapter);
+}
+#endif
+
static void trigger_start_discovery(struct btd_adapter *adapter, guint delay)
{
@@ -1544,6 +2194,7 @@ static void trigger_start_discovery(struct btd_adapter *adapter, guint delay)
start_discovery_timeout, adapter);
}
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
static void suspend_discovery_complete(uint8_t status, uint16_t length,
const void *param, void *user_data)
{
@@ -1614,6 +2265,7 @@ static void resume_discovery(struct btd_adapter *adapter)
*/
trigger_start_discovery(adapter, IDLE_DISCOV_TIMEOUT);
}
+#endif
static void discovering_callback(uint16_t index, uint16_t length,
const void *param, void *user_data)
@@ -1629,11 +2281,40 @@ static void discovering_callback(uint16_t index, uint16_t length,
DBG("hci%u type %u discovering %u method %d", adapter->dev_id, ev->type,
ev->discovering, adapter->filtered_discovery);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ DBG("info discov_type %d", adapter->discovery_type);
+ if (ev->type == SCAN_TYPE_BREDR) {
+ if (ev->discovering == FALSE) {
+ hci_clear_bit(BDADDR_BREDR, &adapter->discovery_type);
+ adapter->discovering = false;
+ } else {
+ hci_set_bit(BDADDR_BREDR, &adapter->discovery_type);
+ adapter->discovering = true;
+ }
+ g_dbus_emit_property_changed(dbus_conn, adapter->path,
+ ADAPTER_INTERFACE, "Discovering");
+
+ } else if (ev->type == SCAN_TYPE_LE) {
+ if (ev->discovering == FALSE) {
+ hci_clear_bit(BDADDR_LE_PUBLIC, &adapter->discovery_type);
+ hci_clear_bit(BDADDR_LE_RANDOM, &adapter->discovery_type);
+ adapter->le_discovering = false;
+ } else {
+ hci_set_bit(BDADDR_LE_PUBLIC, &adapter->discovery_type);
+ hci_set_bit(BDADDR_LE_RANDOM, &adapter->discovery_type);
+ adapter->le_discovering = true;
+ }
+
+ g_dbus_emit_property_changed(dbus_conn, adapter->path,
+ ADAPTER_INTERFACE, "LEDiscovering");
+ }
+#else
if (adapter->discovery_enable == ev->discovering)
return;
adapter->discovery_type = ev->type;
adapter->discovery_enable = ev->discovering;
+#endif
/*
* Check for existing discoveries triggered by client applications
@@ -1642,11 +2323,19 @@ static void discovering_callback(uint16_t index, uint16_t length,
* If there are no clients, then it is good idea to trigger a
* passive scanning attempt.
*/
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (adapter->discovery_list == NULL && adapter->le_discovery_list == NULL) {
+ if (!adapter->connect_le)
+ trigger_passive_scanning(adapter);
+ return;
+ }
+#else
if (!adapter->discovery_list) {
if (!adapter->connect_le)
trigger_passive_scanning(adapter);
return;
}
+#endif
if (adapter->discovery_suspended)
return;
@@ -1669,6 +2358,57 @@ static void discovering_callback(uint16_t index, uint16_t length,
}
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void stop_discovery_complete(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+
+ DBG("status 0x%02x", status);
+
+ if (status == MGMT_STATUS_SUCCESS) {
+ adapter->discovery_type &= (~0x01);
+ DBG("Discovery Type 0x%02x", adapter->discovery_type);
+
+ adapter->filtered_discovery = false;
+ adapter->no_scan_restart_delay = false;
+ adapter->discovering = false;
+ g_dbus_emit_property_changed(dbus_conn, adapter->path,
+ ADAPTER_INTERFACE, "Discovering");
+
+ if (adapter->discovery_list == NULL && adapter->le_discovery_list == NULL) {
+ adapter->discovery_enable = 0x00;
+ trigger_passive_scanning(adapter);
+ }
+ }
+}
+
+static void stop_le_discovery_complete(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+
+ DBG("status 0x%02x", status);
+
+ if (status == MGMT_STATUS_SUCCESS) {
+ adapter->discovery_type &= (~0x06);
+ DBG("Discovery Type 0x%02x", adapter->discovery_type);
+
+ adapter->filtered_discovery = false;
+ adapter->no_scan_restart_delay = false;
+ adapter->le_discovering = false;
+ g_dbus_emit_property_changed(dbus_conn, adapter->path,
+ ADAPTER_INTERFACE, "LEDiscovering");
+
+ if (adapter->discovery_list == NULL && adapter->le_discovery_list == NULL) {
+ adapter->discovery_enable = 0x00;
+ trigger_passive_scanning(adapter);
+ }
+ }
+}
+
+#else
+
static void stop_discovery_complete(uint8_t status, uint16_t length,
const void *param, void *user_data)
{
@@ -1688,6 +2428,7 @@ static void stop_discovery_complete(uint8_t status, uint16_t length,
trigger_passive_scanning(adapter);
}
}
+#endif
static int compare_sender(gconstpointer a, gconstpointer b)
{
@@ -1969,7 +2710,11 @@ static void discovery_destroy(void *user_data)
if (adapter->discovery_list)
return;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ hci_clear_bit(BDADDR_BREDR, &adapter->discovery_type);
+#else
adapter->discovery_type = 0x00;
+#endif
if (adapter->discovery_idle_timeout > 0) {
g_source_remove(adapter->discovery_idle_timeout);
@@ -2059,6 +2804,2798 @@ static bool get_discovery_client(struct btd_adapter *adapter,
return false;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void le_discovery_destroy(void *user_data)
+{
+ struct watch_client *client = user_data;
+ struct btd_adapter *adapter = client->adapter;
+
+ DBG("owner %s", client->owner);
+
+ adapter->le_discovery_list = g_slist_remove(adapter->le_discovery_list,
+ client);
+
+ g_free(client->owner);
+ g_free(client);
+
+ /*
+ * If there are other client discoveries in progress, then leave
+ * it active. If not, then make sure to stop the restart timeout.
+ */
+ DBG("adapter->discovery_list[%p] adapter->le_discovery_list[%p]",
+ adapter->discovery_list, adapter->le_discovery_list);
+ if (adapter->discovery_list || adapter->le_discovery_list)
+ return;
+
+ hci_clear_bit(BDADDR_LE_PUBLIC, &adapter->discovery_type);
+ hci_clear_bit(BDADDR_LE_RANDOM, &adapter->discovery_type);
+
+ DBG("Restart Timer... adapter->discovery_type[%d]", adapter->discovery_type);
+ if (adapter->discovery_idle_timeout > 0) {
+ g_source_remove(adapter->discovery_idle_timeout);
+ adapter->discovery_idle_timeout = 0;
+ }
+
+ if (adapter->temp_devices_timeout > 0) {
+ g_source_remove(adapter->temp_devices_timeout);
+ adapter->temp_devices_timeout = 0;
+ }
+
+ discovery_cleanup(adapter);
+}
+
+static void le_discovery_disconnect(DBusConnection *conn, void *user_data)
+{
+ struct watch_client *client = user_data;
+ struct btd_adapter *adapter = client->adapter;
+ struct mgmt_cp_stop_le_discovery cp;
+
+ DBG("owner %s", client->owner);
+
+ adapter->le_discovery_list = g_slist_remove(adapter->le_discovery_list,
+ client);
+
+ /*
+ * There is no need for extra cleanup of the client since that
+ * will be done by the destroy callback.
+ *
+ * However in case this is the last client, the discovery in
+ * the kernel needs to be disabled.
+ */
+ if (adapter->le_discovery_list)
+ return;
+
+ /*
+ * In the idle phase of a discovery, there is no need to stop it
+ * and so it is enough to send out the signal and just return.
+ */
+ if (adapter->discovery_enable == 0x00) {
+ adapter->le_discovering = false;
+ g_dbus_emit_property_changed(dbus_conn, adapter->path,
+ ADAPTER_INTERFACE, "LEDiscovering");
+
+ if (adapter->discovering == false && adapter->le_discovering == false) {
+ trigger_passive_scanning(adapter);
+ return;
+ }
+ }
+
+ cp.type = 0x06;
+
+ mgmt_send(adapter->mgmt, MGMT_OP_STOP_LE_DISCOVERY,
+ adapter->dev_id, sizeof(cp), &cp,
+ stop_discovery_complete, adapter, NULL);
+}
+
+#ifdef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+static void addr_filter_params_free(gpointer data, gpointer user_data)
+{
+ adapter_le_address_filter_params_t *params = data;
+
+ g_free(params);
+}
+
+static void uuid_filter_params_free(gpointer data, gpointer user_data)
+{
+ adapter_le_uuid_params_t *params = data;
+
+ g_free((char *)params->uuid);
+ g_free((char *)params->uuid_mask);
+ g_free(params);
+}
+
+static void manufacturer_filter_params_free(gpointer data, gpointer user_data)
+{
+ adapter_le_manf_data_params_t *params = data;
+
+ g_free((char *)params->man_data);
+ g_free((char *)params->man_data_mask);
+ g_free(params);
+}
+
+static void local_name_filter_params_free(gpointer data, gpointer user_data)
+{
+ adapter_le_local_name_params_t *params = data;
+
+ g_free((char *)params->local_name);
+ g_free(params);
+}
+
+static void service_data_filter_params_free(gpointer data, gpointer user_data)
+{
+ adapter_le_service_data_params_t *params = data;
+
+ g_free((char *)params->service_data);
+ g_free((char *)params->service_data_mask);
+ g_free(params);
+}
+
+static void scan_filter_params_free(gpointer data, gpointer user_data)
+{
+ adapter_le_scan_filter_param_t *params = data;
+ g_free(params);
+}
+
+int adapter_le_address_cmp(gconstpointer a, gconstpointer b)
+{
+ const adapter_le_address_filter_params_t *params = a;
+ const char *address = b;
+ char addr[18];
+
+ ba2str(&params->broadcaster_addr, addr);
+ return strcasecmp(addr, address);
+}
+
+int adapter_le_uuid_cmp(gconstpointer a, gconstpointer b)
+{
+ const adapter_le_uuid_params_t *params = a;
+ const char *uuid = b;
+
+ return strcasecmp((const char *)params->uuid, uuid);
+}
+
+int adapter_le_manufacturer_data_cmp(gconstpointer a, gconstpointer b)
+{
+ const adapter_le_manf_data_params_t *params = a;
+ const struct eir_msd *msd = b;
+
+ if (msd->company == params->company_id) {
+ /* if the advertisiement packet is an iBeacon */
+ if (msd->company == COMPANY_ID_APPLE)
+ return 0;
+ return strncasecmp((const char *)params->man_data, msd->data, params->man_data_len);
+ } else {
+ return -1;
+ }
+}
+
+int adapter_le_local_name_cmp(gconstpointer a, gconstpointer b)
+{
+ const adapter_le_local_name_params_t *params = a;
+ const char *name = b;
+
+ return strcasecmp(params->local_name, name);
+}
+
+int adapter_le_service_data_cmp(gconstpointer a, gconstpointer b)
+{
+ const adapter_le_service_data_params_t *params = a;
+ const struct eir_sd *sd = b;
+ /* Todo, the service data format for 16 bit, 32bit and
+ * 128 bit uuids needs to addressed */
+ return strncasecmp((const char *)(params->service_data), sd->data, sd->data_len);
+}
+
+int adapter_le_address_filter_index_cmp(gconstpointer a, gconstpointer b)
+{
+ const adapter_le_address_filter_params_t *params = a;
+ uint16_t filter_inex = GPOINTER_TO_UINT(b);
+
+ return params->filter_index - filter_inex;
+}
+
+int adapter_le_uuid_filter_index_cmp(gconstpointer a, gconstpointer b)
+{
+ const adapter_le_uuid_params_t *params = a;
+ uint16_t filter_inex = GPOINTER_TO_UINT(b);
+
+ return params->filter_index - filter_inex;
+}
+
+int adapter_le_manufacturer_data_filter_index_cmp(gconstpointer a, gconstpointer b)
+{
+ const adapter_le_manf_data_params_t *params = a;
+ uint16_t filter_inex = GPOINTER_TO_UINT(b);
+
+ return params->filter_index - filter_inex;
+}
+
+int adapter_le_local_name_filter_index_cmp(gconstpointer a, gconstpointer b)
+{
+ const adapter_le_local_name_params_t *params = a;
+ uint16_t filter_inex = GPOINTER_TO_UINT(b);
+
+ return params->filter_index - filter_inex;
+}
+
+int adapter_le_service_data_filter_index_cmp(gconstpointer a, gconstpointer b)
+{
+ const adapter_le_service_data_params_t *params = a;
+ uint16_t filter_inex = GPOINTER_TO_UINT(b);
+
+ return params->filter_index - filter_inex;
+}
+
+int adapter_le_scan_params_filter_index_cmp(gconstpointer a, gconstpointer b)
+{
+ const adapter_le_scan_filter_param_t *params = a;
+ uint16_t filter_inex = GPOINTER_TO_UINT(b);
+
+ return params->index - filter_inex;
+}
+
+static gboolean adapter_le_clear_platform_scan_filter_data(
+ struct btd_adapter *adapter, int filter_index)
+{
+ DBG("");
+ GSList *list;
+ if (!adapter)
+ return FALSE;
+
+ list = g_slist_find_custom(adapter->addr_filters,
+ GINT_TO_POINTER(filter_index), adapter_le_address_filter_index_cmp);
+ if (list && list->data) {
+ /* Delete info from the struct to list */
+ adapter->addr_filters = g_slist_delete_link(adapter->addr_filters, list);
+ }
+ list = g_slist_find_custom(adapter->service_data_changed_filters,
+ GINT_TO_POINTER(filter_index), adapter_le_service_data_filter_index_cmp);
+ if (list && list->data) {
+ /* Delete info from the struct to list */
+ adapter->service_data_changed_filters = g_slist_delete_link(adapter->service_data_changed_filters, list);
+ }
+
+ list = g_slist_find_custom(adapter->service_uuid_filters,
+ GINT_TO_POINTER(filter_index), adapter_le_uuid_filter_index_cmp);
+ if (list && list->data) {
+ /* Delete info from the struct to list */
+ adapter->service_uuid_filters = g_slist_delete_link(adapter->service_uuid_filters, list);
+ }
+
+ list = g_slist_find_custom(adapter->solicit_data_filters,
+ GINT_TO_POINTER(filter_index), adapter_le_uuid_filter_index_cmp);
+ if (list && list->data) {
+ /* Delete info from the struct to list */
+ adapter->solicit_data_filters = g_slist_delete_link(adapter->solicit_data_filters, list);
+ }
+
+ list = g_slist_find_custom(adapter->local_name_filters,
+ GINT_TO_POINTER(filter_index), adapter_le_local_name_filter_index_cmp);
+ if (list && list->data) {
+ /* Delete info from the struct to list */
+ adapter->local_name_filters = g_slist_delete_link(adapter->local_name_filters, list);
+ }
+
+ list = g_slist_find_custom(adapter->manufaturer_data_filters,
+ GINT_TO_POINTER(filter_index), adapter_le_manufacturer_data_filter_index_cmp);
+ if (list && list->data) {
+ /* Delete info from the struct to list */
+ adapter->manufaturer_data_filters = g_slist_delete_link(adapter->manufaturer_data_filters, list);
+ }
+
+ list = g_slist_find_custom(adapter->service_data_filters,
+ GINT_TO_POINTER(filter_index), adapter_le_service_data_filter_index_cmp);
+ if (list && list->data) {
+ /* Delete info from the struct to list */
+ adapter->service_data_filters = g_slist_delete_link(adapter->service_data_filters, list);
+ }
+
+ list = g_slist_find_custom(adapter->scan_params,
+ GINT_TO_POINTER(filter_index), adapter_le_scan_params_filter_index_cmp);
+ if (list && list->data) {
+ /* Delete info from the struct to list */
+ adapter->scan_params = g_slist_delete_link(adapter->scan_params, list);
+ }
+
+ return TRUE;
+}
+
+static gboolean adapter_le_enable_platform_scan_filtering(
+ struct btd_adapter *adapter, gboolean enable)
+{
+ if (!adapter)
+ return FALSE;
+
+ DBG("Platform scan filtering enable[%d]", enable);
+
+ adapter->scan_filter_support = enable;
+
+ return TRUE;
+}
+
+
+static gboolean adapter_le_service_add_addr_scan_filter_data(struct btd_adapter *adapter,
+ int filter_index, gchar *string, int addr_type)
+{
+ /* TYPE_DEVICE_ADDRESS */
+ adapter_le_address_filter_params_t *params;
+
+ DBG("");
+
+ params = g_new0(adapter_le_address_filter_params_t, 1);
+ if (!params)
+ return FALSE;
+
+ params->filter_index = filter_index;
+ str2ba(string, &params->broadcaster_addr);
+ params->bdaddr_type = addr_type;
+
+ /* Store the struct to list */
+ adapter->addr_filters = g_slist_append(adapter->addr_filters, params);
+ return TRUE;
+}
+
+static const char *adapter_le_service_find_addr_scan_filter_data(
+ struct btd_adapter *adapter, gchar *string)
+{
+ GSList *list;
+ DBG("");
+
+ list = g_slist_find_custom(adapter->addr_filters, string, adapter_le_address_cmp);
+ if (!list)
+ return NULL;
+ else
+ return list->data;
+
+ return NULL;
+}
+
+static gboolean adapter_le_service_delete_addr_scan_filter_data(struct btd_adapter *adapter,
+ int filter_index, gchar *string, int addr_type)
+{
+ GSList *list;
+ DBG("");
+
+ list = g_slist_find_custom(adapter->addr_filters, string, adapter_le_address_cmp);
+ if (!list)
+ return FALSE;
+ else
+ /* Delete info from the struct to list */
+ adapter->addr_filters = g_slist_delete_link(adapter->addr_filters, list);
+
+ return TRUE;
+}
+
+static gboolean adapter_le_service_clear_addr_scan_filter_data(struct btd_adapter *adapter)
+{
+ DBG("");
+
+ g_slist_free_full(adapter->addr_filters, addr_filter_params_free);
+ adapter->addr_filters = NULL;
+
+ return TRUE;
+}
+
+static gboolean adapter_le_service_add_uuid_scan_filter_data(struct btd_adapter *adapter,
+ int filter_index, gboolean is_solicited, uint8_t *p_uuid,
+ uint8_t *p_uuid_mask, int uuid_mask_len)
+{
+
+ adapter_le_uuid_params_t *params;
+ bt_uuid_t uuid;
+
+ DBG("");
+
+ params = g_new0(adapter_le_uuid_params_t, 1);
+ if (!params)
+ return FALSE;
+
+ if (uuid_mask_len == UUID_16_LEN) {
+ uint16_t *uuid16 = (void *)p_uuid;
+ sdp_uuid16_create(&uuid, get_be16(uuid16));
+ } else if (uuid_mask_len == UUID_32_LEN) {
+ uint32_t *uuid32 = (void *)p_uuid;
+ sdp_uuid32_create(&uuid, get_be32(uuid32));
+ } else {
+ sdp_uuid128_create(&uuid, p_uuid);
+ }
+ params->filter_index = filter_index;
+ params->uuid = bt_uuid2string(&uuid);
+ params->uuid_mask = g_new0(uint8_t, uuid_mask_len);
+ memcpy(params->uuid_mask, p_uuid_mask, uuid_mask_len);
+ params->uuid_len = uuid_mask_len;
+
+ /* Store the struct to list */
+ adapter->solicit_data_filters = g_slist_append(adapter->solicit_data_filters, params);
+
+ return TRUE;
+}
+
+static adapter_le_uuid_params_t *adapter_le_service_find_uuid_scan_filter_data(struct btd_adapter *adapter,
+ uint8_t *p_uuid)
+{
+ GSList *list;
+ DBG("");
+
+ list = g_slist_find_custom(adapter->solicit_data_filters, p_uuid, adapter_le_uuid_cmp);
+ if (!list)
+ return NULL;
+ else
+ /* Delete info from the struct to list */
+ return list->data;
+
+ return NULL;
+}
+
+static gboolean adapter_le_service_delete_uuid_scan_filter_data(struct btd_adapter *adapter,
+ int filter_index, gboolean is_solicited, uint8_t *p_uuid,
+ uint8_t *p_uuid_mask, int uuid_mask_len)
+{
+ GSList *list;
+ DBG("");
+
+ list = g_slist_find_custom(adapter->solicit_data_filters, GINT_TO_POINTER(filter_index), adapter_le_uuid_filter_index_cmp);
+ if (!list)
+ return FALSE;
+ else {
+ adapter_le_uuid_params_t *params = list->data;
+ /* Delete info from the struct to list */
+ if (params && strcasecmp((const char *)params->uuid, (const char *)p_uuid)) {
+ adapter->solicit_data_filters = g_slist_delete_link(adapter->solicit_data_filters, list);
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean adapter_le_service_clear_uuid_scan_filter_data(struct btd_adapter *adapter)
+{
+ DBG("");
+
+ g_slist_free_full(adapter->solicit_data_filters, uuid_filter_params_free);
+ adapter->solicit_data_filters = NULL;
+
+ return TRUE;
+}
+
+static gboolean adapter_le_service_add_manufacturer_scan_filter_data(struct btd_adapter *adapter,
+ int filter_index, int company_id, int company_id_mask,
+ uint8_t *p_data, uint8_t *p_mask, int data_len)
+{
+
+ adapter_le_manf_data_params_t *params;
+
+ DBG("");
+
+ params = g_new0(adapter_le_manf_data_params_t, 1);
+ if (!params)
+ return FALSE;
+
+ params->filter_index = filter_index;
+ params->company_id = company_id;
+ params->company_id_mask = company_id_mask;
+ params->man_data = g_new0(uint8_t, data_len);
+ memcpy(params->man_data, p_data, data_len);
+ params->man_data_mask = g_new0(uint8_t, data_len);
+ memcpy(params->man_data_mask, p_mask, data_len);
+ params->man_data_len = data_len;
+
+ /* Store the struct to list */
+ adapter->manufaturer_data_filters = g_slist_append(adapter->manufaturer_data_filters, params);
+
+ return TRUE;
+}
+
+static adapter_le_manf_data_params_t *adapter_le_service_find_manufacturer_scan_filter_data(struct btd_adapter *adapter,
+ struct eir_msd *msd)
+{
+ GSList *list;
+ DBG("");
+ list = g_slist_find_custom(adapter->manufaturer_data_filters, msd, adapter_le_manufacturer_data_cmp);
+ if (!list)
+ return NULL;
+ else
+ return list->data;
+
+ return NULL;
+}
+
+static gboolean adapter_le_service_delete_manufacturer_scan_filter_data(struct btd_adapter *adapter,
+ int filter_index, int company_id, int company_id_mask,
+ uint8_t *p_data, uint8_t *p_mask, int data_len)
+{
+ GSList *list;
+ DBG("");
+ list = g_slist_find_custom(adapter->manufaturer_data_filters, GINT_TO_POINTER(filter_index), adapter_le_manufacturer_data_filter_index_cmp);
+ if (!list)
+ return FALSE;
+ else {
+ adapter_le_manf_data_params_t *params = list->data;
+ /* Delete info from the struct to list */
+ if (params && strcasecmp((const char *)params->man_data, (const char *)p_data)) {
+ adapter->manufaturer_data_filters = g_slist_delete_link(adapter->manufaturer_data_filters, list);
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean adapter_le_service_clear_manufacturer_scan_filter_data(struct btd_adapter *adapter)
+{
+ DBG("");
+
+ g_slist_free_full(adapter->manufaturer_data_filters, manufacturer_filter_params_free);
+ adapter->manufaturer_data_filters = NULL;
+
+ return TRUE;
+}
+
+static gboolean adapter_le_service_add_local_name_scan_filter_data(struct btd_adapter *adapter,
+ int filter_index, gchar *name)
+{
+
+ adapter_le_local_name_params_t *params;
+
+ DBG("");
+
+ params = g_new0(adapter_le_local_name_params_t, 1);
+ if (!params)
+ return FALSE;
+
+ params->filter_index = filter_index;
+ params->local_name = g_strdup(name);
+ params->name_len = strlen(name);
+
+ /* Store the struct to list */
+ adapter->local_name_filters = g_slist_append(adapter->local_name_filters, params);
+
+ return TRUE;
+}
+
+static adapter_le_local_name_params_t *adapter_le_service_find_local_name_scan_filter_data(
+ struct btd_adapter *adapter,
+ gchar *name)
+{
+ GSList *list;
+ DBG("");
+ list = g_slist_find_custom(adapter->local_name_filters, name, adapter_le_local_name_cmp);
+ if (!list)
+ return NULL;
+ else
+ return list->data;
+
+ return NULL;
+}
+
+static gboolean adapter_le_service_delete_local_name_scan_filter_data(struct btd_adapter *adapter,
+ int filter_index, gchar *name)
+{
+ GSList *list;
+ DBG("");
+ list = g_slist_find_custom(adapter->local_name_filters, GINT_TO_POINTER(filter_index), adapter_le_local_name_filter_index_cmp);
+ if (!list)
+ return FALSE;
+ else {
+ adapter_le_local_name_params_t *params = list->data;
+ /* Delete info from the struct to list */
+ if (params && strcasecmp((const char *)params->local_name, (const char *)name)) {
+ adapter->local_name_filters = g_slist_delete_link(adapter->local_name_filters, list);
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean adapter_le_service_clear_local_name_scan_filter_data(struct btd_adapter *adapter)
+{
+ DBG("");
+
+ g_slist_free_full(adapter->local_name_filters, local_name_filter_params_free);
+ adapter->local_name_filters = NULL;
+
+ return TRUE;
+}
+
+static gboolean adapter_le_service_add_service_scan_filter_data(struct btd_adapter *adapter,
+ int filter_index, uint8_t *p_data, uint8_t *p_mask, int data_len)
+{
+ adapter_le_service_data_params_t *params;
+
+ DBG("");
+
+ params = g_new0(adapter_le_service_data_params_t, 1);
+ if (!params)
+ return FALSE;
+
+ params->filter_index = filter_index;
+ params->service_data = g_new0(uint8_t, data_len);
+ memcpy(params->service_data, p_data, data_len);
+ params->service_data_mask = g_new0(uint8_t, data_len);
+ memcpy(params->service_data_mask, p_mask, data_len);
+ params->service_data_len = data_len;
+
+ /* Store the struct to list */
+ adapter->service_data_filters = g_slist_append(adapter->service_data_filters, params);
+
+ return TRUE;
+}
+
+static adapter_le_service_data_params_t *adapter_le_service_find_service_scan_filter_data(
+ struct btd_adapter *adapter, struct eir_sd *sd)
+{
+ GSList *list;
+ DBG("");
+
+ list = g_slist_find_custom(adapter->service_data_filters, sd, adapter_le_service_data_cmp);
+ if (!list)
+ return NULL;
+ else
+ return list->data;
+
+ return NULL;
+}
+
+static gboolean adapter_le_service_delete_service_scan_filter_data(struct btd_adapter *adapter,
+ int filter_index, uint8_t *p_data, uint8_t *p_mask, int data_len)
+{
+ GSList *list;
+ DBG("");
+
+ list = g_slist_find_custom(adapter->service_data_filters, GINT_TO_POINTER(filter_index), adapter_le_service_data_filter_index_cmp);
+ if (!list)
+ return FALSE;
+ else {
+ adapter_le_service_data_params_t *params = list->data;
+ /* Delete info from the struct to list */
+ if (params && strcasecmp((const char *)params->service_data, (const char *)p_data)) {
+ adapter->service_data_filters = g_slist_delete_link(adapter->service_data_filters, list);
+ }
+ }
+ return TRUE;
+}
+
+static gboolean adapter_le_service_clear_service_scan_filter_data(struct btd_adapter *adapter)
+{
+ DBG("");
+
+ g_slist_free_full(adapter->service_data_filters, service_data_filter_params_free);
+ adapter->service_data_filters = NULL;
+
+ return TRUE;
+}
+
+static gboolean adapter_le_service_add_scan_filter_params(struct btd_adapter *adapter,
+ adapter_le_scan_filter_param_t *params)
+{
+ adapter_le_scan_filter_param_t *l_params;
+
+ DBG("");
+
+ l_params = g_new0(adapter_le_scan_filter_param_t, 1);
+ if (!l_params)
+ return FALSE;
+
+ l_params->action = params->action;
+ l_params->delivery_mode = params->delivery_mode;
+ l_params->feature = params->feature;
+ l_params->filter_logic_type = params->filter_logic_type;
+ l_params->index = params->index;
+ l_params->list_logic_type = params->list_logic_type;
+ l_params->onfound_timeout = params->onfound_timeout;
+ l_params->onfound_timeout_cnt = params->onfound_timeout_cnt;
+ l_params->rssi_high_threshold = params->rssi_high_threshold;
+ l_params->rssi_low_threshold = params->rssi_low_threshold;
+
+ /* Store the struct to list */
+ adapter->scan_params = g_slist_append(adapter->scan_params, l_params);
+
+ return TRUE;
+}
+
+static adapter_le_service_data_params_t *adapter_le_service_find_scan_filter_params(
+ struct btd_adapter *adapter, int filter_index)
+{
+ GSList *list;
+ DBG("");
+
+ list = g_slist_find_custom(adapter->scan_params, GINT_TO_POINTER(filter_index), adapter_le_scan_params_filter_index_cmp);
+ if (!list)
+ return NULL;
+ else
+ return list->data;
+
+ return NULL;
+}
+
+static gboolean adapter_le_service_delete_scan_filter_params(struct btd_adapter *adapter,
+ adapter_le_scan_filter_param_t *params)
+{
+ GSList *list;
+ DBG("");
+
+ list = g_slist_find_custom(adapter->scan_params, GINT_TO_POINTER(params->index), adapter_le_scan_params_filter_index_cmp);
+ if (!list)
+ return FALSE;
+ else
+ adapter->scan_params = g_slist_remove(adapter->scan_params, list);
+
+ return TRUE;
+}
+
+static gboolean adapter_le_service_clear_scan_filter_params(struct btd_adapter *adapter)
+{
+ DBG("");
+
+ g_slist_free_full(adapter->scan_params, scan_filter_params_free);
+ adapter->scan_params = NULL;
+
+ return TRUE;
+}
+
+int adapter_byte_arr_cmp_with_mask(const char *data1, const char *data2,
+ const char *mask, int data_len)
+{
+ int i;
+ char a, b;
+ if (data1 == NULL || data2 == NULL || mask == NULL)
+ return -1;
+ for (i = 0; i < data_len; i++) {
+ a = data1[i] & mask[i];
+ b = data2[i] & mask[i];
+ if (a != b)
+ return (int)(a - b);
+ }
+ return 0;
+}
+
+static uint8_t validate_for_filter_policy(struct btd_adapter *adapter,
+ const struct eir_data *eir, gchar *addr)
+{
+ uint8_t allow_report = NONE_REPORT;
+ DBG("");
+
+ if (adapter->scan_filter_support == FALSE)
+ allow_report = SCAN_REPORT;
+ else {
+ if (adapter_le_service_find_addr_scan_filter_data(adapter, addr))
+ allow_report = SCAN_REPORT;
+ if (eir->name) {
+ if(adapter_le_service_find_local_name_scan_filter_data(adapter, eir->name))
+ allow_report = SCAN_REPORT;
+ }
+ if (eir->sd_list) {
+ GSList *list = NULL;
+ for (list = eir->sd_list; list != NULL; list = g_slist_next(list)) {
+ struct eir_sd *sd = list->data;
+ if (sd != NULL) {
+ static adapter_le_uuid_params_t *uuid_data = NULL;
+ static adapter_le_service_data_params_t *service_data = NULL;
+ static adapter_le_scan_filter_param_t *scan_param_data = NULL;
+ uuid_data = adapter_le_service_find_uuid_scan_filter_data(adapter, (uint8_t *)sd->uuid);
+ service_data = adapter_le_service_find_service_scan_filter_data(adapter, sd);
+ if (service_data != NULL) {
+ if (!adapter_byte_arr_cmp_with_mask((const char *)service_data->service_data,
+ (const char *)sd->data, (const char *)service_data->service_data_mask,
+ service_data->service_data_len)) {
+ scan_param_data = adapter_le_service_find_scan_filter_params(adapter,
+ service_data->filter_index);
+ if (scan_param_data && scan_param_data->rssi_high_threshold > eir->tx_power &&
+ scan_param_data->rssi_low_threshold < eir->tx_power)
+ allow_report = SCAN_REPORT;
+ }
+ }
+ if (uuid_data != NULL) {
+ if (!adapter_byte_arr_cmp_with_mask((const char *)uuid_data->uuid,
+ (const char *)sd->uuid, (const char *)uuid_data->uuid_mask,
+ uuid_data->uuid_len)) {
+ scan_param_data = adapter_le_service_find_scan_filter_params(adapter,
+ uuid_data->filter_index);
+ if (scan_param_data && scan_param_data->rssi_high_threshold > eir->tx_power &&
+ scan_param_data->rssi_low_threshold < eir->tx_power)
+ allow_report = SCAN_REPORT;
+ }
+ }
+ if (allow_report)
+ break;
+ }
+ }
+ }
+ if (eir->msd_list) {
+ GSList *list = NULL;
+ for (list = eir->msd_list; list != NULL; list = g_slist_next(list)) {
+ struct eir_msd *msd = list->data;
+ if (msd != NULL) {
+ static adapter_le_manf_data_params_t *manuf_data;
+ static adapter_le_scan_filter_param_t *scan_param_data = NULL;
+ manuf_data = adapter_le_service_find_manufacturer_scan_filter_data(adapter,
+ msd);
+ if (manuf_data != NULL) {
+ if (!adapter_byte_arr_cmp_with_mask((const char *)msd->data,
+ (const char *)manuf_data->man_data, (const char *)manuf_data->man_data_mask,
+ manuf_data->man_data_len)) {
+ scan_param_data = adapter_le_service_find_scan_filter_params(adapter,
+ manuf_data->filter_index);
+ if (scan_param_data && scan_param_data->rssi_high_threshold > eir->tx_power &&
+ scan_param_data->rssi_low_threshold < eir->tx_power)
+ allow_report = SCAN_REPORT;
+ }
+ if (msd->company == COMPANY_ID_APPLE)
+ allow_report = IBEACON_REPORT;
+ }
+ }
+ }
+ }
+ }
+ return allow_report;
+}
+
+gboolean adapter_le_set_platform_scan_filter_params(struct btd_adapter *adapter,
+ adapter_le_scan_filter_param_t *params)
+{
+ gboolean ret = TRUE;
+ DBG("adapter_le_scan_filter_param_t [%d]", params->index);
+ adapter_le_scan_filter_action_type action_type = params->action;
+
+ if (action_type == ADD) {
+ ret = adapter_le_service_add_scan_filter_params(adapter, params);
+ } else if (action_type == DELETE) {
+ ret = adapter_le_service_delete_scan_filter_params(adapter, params);
+ } else if (action_type == CLEAR) {
+ ret = adapter_le_service_clear_scan_filter_params(adapter);
+ } else {
+ DBG("filter_action error");
+ ret = FALSE;
+ }
+
+ DBG("Scan Filter VSC :: Action [%x]",
+ params->action);
+ return ret;
+}
+
+gboolean adapter_le_set_platform_scan_filter_data(struct btd_adapter *adapter,
+ int client_if, int action,
+ int filt_type, int filter_index,
+ int company_id,
+ int company_id_mask,
+ int uuid_len, uint8_t *p_uuid,
+ int uuid_mask_len, uint8_t *p_uuid_mask,
+ gchar *string, int addr_type,
+ int data_len, uint8_t *p_data,
+ int mask_len, uint8_t *p_mask)
+{
+ gboolean ret = TRUE;
+
+ DBG("");
+
+ switch (filt_type) {
+ case TYPE_DEVICE_ADDRESS: {
+ /* TYPE_DEVICE_ADDRESS */
+ adapter_le_scan_filter_action_type action_type = action;
+
+ if (action_type == ADD) {
+ ret = adapter_le_service_add_addr_scan_filter_data(adapter,
+ filter_index, string, addr_type);
+ } else if (action_type == DELETE) {
+ ret = adapter_le_service_delete_addr_scan_filter_data(adapter,
+ filter_index, string, addr_type);
+ } else if (action_type == CLEAR) {
+ ret = adapter_le_service_clear_addr_scan_filter_data(adapter);
+ } else {
+ DBG("filter_action error");
+ ret = FALSE;
+ }
+
+ break;
+ }
+
+ case TYPE_SERVICE_UUID:
+ case TYPE_SOLICIT_UUID: {
+ adapter_le_scan_filter_action_type action_type = action;
+
+ gboolean is_solicited = (filt_type == TYPE_SOLICIT_UUID) ? TRUE : FALSE;
+
+ if (uuid_len != UUID_16_LEN && uuid_len != UUID_32_LEN
+ && uuid_len != UUID_128_LEN) {
+ DBG("UUID length error");
+ return FALSE;
+ }
+
+ if (uuid_len != uuid_mask_len) {
+ DBG("Both UUID and UUID_MASK length shoule be samed");
+ return FALSE;
+ }
+
+ if (action_type == ADD) {
+ ret = adapter_le_service_add_uuid_scan_filter_data(adapter,
+ filter_index, is_solicited, p_uuid,
+ p_uuid_mask, uuid_len);
+ } else if (action_type == DELETE) {
+ ret = adapter_le_service_delete_uuid_scan_filter_data(adapter,
+ filter_index, is_solicited, p_uuid,
+ p_uuid_mask, uuid_len);
+ } else if (action_type == CLEAR) {
+ ret = adapter_le_service_clear_uuid_scan_filter_data(adapter);
+ } else {
+ DBG("filter_action error");
+ ret = FALSE;
+ }
+
+ break;
+ }
+
+ case TYPE_LOCAL_NAME: {
+ adapter_le_scan_filter_action_type action_type = action;
+
+ if (action_type == ADD) {
+ ret = adapter_le_service_add_local_name_scan_filter_data(adapter,
+ filter_index, (gchar*)string);
+ } else if (action_type == DELETE) {
+ ret = adapter_le_service_delete_local_name_scan_filter_data(adapter,
+ filter_index, (gchar*)string);
+ } else if (action_type == CLEAR) {
+ ret = adapter_le_service_clear_local_name_scan_filter_data(adapter);
+ } else {
+ DBG("filter_action error");
+ ret = FALSE;
+ }
+
+ break;
+ }
+
+ case TYPE_MANUFACTURER_DATA: {
+ adapter_le_scan_filter_action_type action_type = action;
+
+ if (data_len == 0 || (data_len != mask_len)) {
+ DBG("parameter length error");
+ return FALSE;
+ }
+
+ if (action_type == ADD) {
+ ret = adapter_le_service_add_manufacturer_scan_filter_data(adapter,
+ filter_index,company_id, company_id_mask, p_data, p_mask, data_len);
+ } else if (action_type == DELETE) {
+ ret = adapter_le_service_delete_manufacturer_scan_filter_data(adapter,
+ filter_index, company_id, company_id_mask, p_data, p_mask, data_len);
+ } else if (action_type == CLEAR) {
+ ret = adapter_le_service_clear_manufacturer_scan_filter_data(adapter);
+ } else {
+ DBG("filter_action error");
+ ret = FALSE;
+ }
+
+ break;
+ }
+
+ case TYPE_SERVICE_DATA: {
+ adapter_le_scan_filter_action_type action_type = action;
+
+ if (data_len == 0 || (data_len != mask_len)) {
+ DBG("parameter length error");
+ return FALSE;
+ }
+
+ if (action_type == ADD) {
+ ret = adapter_le_service_add_service_scan_filter_data(adapter,
+ filter_index, p_data, p_mask, data_len);
+ } else if (action_type == DELETE) {
+ ret = adapter_le_service_delete_service_scan_filter_data(adapter,
+ filter_index, p_data, p_mask, data_len);
+ } else if (action_type == CLEAR) {
+ ret = adapter_le_service_clear_service_scan_filter_data(adapter);
+ } else {
+ DBG("filter_action error");
+ ret = FALSE;
+ }
+
+ break;
+ }
+
+ default:
+ DBG("filter_type error");
+ ret = FALSE;
+ }
+
+ return ret;
+}
+#endif
+
+static int set_adv_data_flag(uint8_t *adv_data, uint8_t *data, int data_len, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+
+ adv_data[0] = 2;
+ adv_data[1] = EIR_FLAGS;
+
+ if (adapter->le_static_addr.b[5] != 0)
+ adv_data[2] = EIR_GEN_DISC | EIR_CONTROLLER |
+ EIR_SIM_HOST | EIR_BREDR_UNSUP;
+ else
+ adv_data[2] = EIR_GEN_DISC | EIR_CONTROLLER | EIR_SIM_HOST;
+
+ memcpy(adv_data + 3, data, data_len);
+ return data_len + 3;
+}
+
+static int set_adv_data_device_name(uint8_t *adv_data, int adv_len, char *name)
+{
+ int ad_type;
+ int ad_len;
+ int i, j;
+ int name_len;
+ uint8_t *data = NULL;
+
+ if (!name)
+ return adv_len;
+
+ data = g_memdup(adv_data, adv_len);
+ if (!data)
+ return adv_len;
+
+ name_len = strlen(name);
+
+ for (i = 0; i <adv_len ; i++) {
+ ad_len = data[i];
+ ad_type = data[i + 1];
+
+ if (ad_type == EIR_NAME_COMPLETE) {
+ /* Move to last position and update local name */
+ for (j = i; j < adv_len - 2; j++)
+ adv_data[j] = data[j + 2];
+
+ adv_data[j] = name_len + 1;
+ if (name_len > ADV_DATA_MAX_LENGTH - adv_len) {
+ adv_data[j] = ADV_DATA_MAX_LENGTH - adv_len + 1;
+ adv_data[j + 1] = EIR_NAME_SHORT;
+ memcpy(adv_data + j + 2, name, ADV_DATA_MAX_LENGTH - adv_len);
+ g_free(data);
+ return ADV_DATA_MAX_LENGTH;
+ } else {
+ adv_data[j + 1] = EIR_NAME_COMPLETE;
+ memcpy(adv_data + j + 2, name, name_len);
+ g_free(data);
+ return adv_len + name_len;
+ }
+
+ } else {
+ memcpy(adv_data + i, &data[i], ad_len + 1);
+ i = i + data[i];
+ }
+ }
+
+ g_free(data);
+ return adv_len;
+}
+
+static int set_adv_data_tx_power(uint8_t *adv_data, int adv_len, int8_t tx_power)
+{
+ int ad_type;
+ int ad_len;
+ int i, j;
+ uint8_t *data = NULL;
+
+ data = g_memdup(adv_data, adv_len);
+ if (!data)
+ return adv_len;
+
+ for (i = 0; i <adv_len ; i++) {
+ ad_len = data[i];
+ ad_type = data[i + 1];
+
+ if (ad_type == EIR_TX_POWER) {
+ adv_data[i] = 2;
+ adv_data[i + 1] = EIR_TX_POWER;
+ adv_data[i + 2] = tx_power;
+
+ for(j = i + 2; j < adv_len; j++)
+ adv_data[j + 1] = data[j];
+
+ g_free(data);
+ return adv_len + 1;
+ } else {
+ memcpy(adv_data + i, &data[i], ad_len + 1);
+ i = i + data[i];
+ }
+ }
+
+ g_free(data);
+ return adv_len;
+}
+
+
+static int adapter_le_set_missed_adv_data(uint8_t *p_data, uint8_t data_len,
+ gboolean is_scan_rsp, char *adapter_name, int8_t tx_power, uint8_t **adv_data, int *adv_len,
+ void *user_data)
+{
+ uint8_t *data;
+ int len;
+
+ data = g_malloc0(ADV_DATA_MAX_LENGTH);
+ memcpy(data, p_data, data_len);
+ len = data_len;
+
+ /* In case multi advertising, need to update the below AD type
+ since it handled into kernel */
+ if (!is_scan_rsp) {
+ len = set_adv_data_flag(data, p_data, data_len, user_data);
+ }
+
+ len = set_adv_data_tx_power(data, len, tx_power);
+
+ len = set_adv_data_device_name(data, len, adapter_name);
+
+ *adv_data = data;
+ *adv_len = len;
+ return 0;
+}
+
+static DBusMessage *adapter_start_custom_discovery(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ const char *sender = dbus_message_get_sender(msg);
+ struct watch_client *client;
+ GSList *list;
+ const gchar *disc_type;
+
+ DBG("sender %s", sender);
+
+ if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+ return btd_error_not_ready(msg);
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &disc_type,
+ DBUS_TYPE_INVALID)) {
+ return btd_error_invalid_args(msg);
+ }
+
+ DBG("discovery type = %s", disc_type);
+
+ /*Valid strings: "BREDR", "LE", "LE_BREDR" */
+ if (g_strcmp0(disc_type, "BREDR") == 0)
+ adapter->disc_type = BT_DISC_TYPE_BREDR_ONLY;
+ else if (g_strcmp0(disc_type, "LE") == 0)
+ adapter->disc_type = BT_DISC_TYPE_LE_ONLY;
+ else if (g_strcmp0(disc_type, "LE_BREDR") == 0)
+ adapter->disc_type = BT_DISC_TYPE_LE_BREDR;
+ else
+ return btd_error_invalid_args(msg);
+
+ /*
+ * Every client can only start one discovery, if the client
+ * already started a discovery then return an error.
+ */
+ list = g_slist_find_custom(adapter->discovery_list, sender,
+ compare_sender);
+ if (list)
+ return btd_error_busy(msg);
+
+ client = g_new0(struct watch_client, 1);
+
+ client->adapter = adapter;
+ client->owner = g_strdup(sender);
+ client->watch = g_dbus_add_disconnect_watch(dbus_conn, sender,
+ discovery_disconnect, client,
+ discovery_destroy);
+
+ adapter->discovery_list = g_slist_prepend(adapter->discovery_list,
+ client);
+
+ /*
+ * Just trigger the discovery here. In case an already running
+ * discovery in idle phase exists, it will be restarted right
+ * away.
+ */
+ trigger_start_discovery(adapter, 0);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *adapter_start_le_discovery(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ const char *sender = dbus_message_get_sender(msg);
+ struct watch_client *client;
+ GSList *list;
+
+ DBG("sender %s", sender);
+
+ if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+ return btd_error_not_ready(msg);
+
+ /*
+ * Every client can only start one discovery, if the client
+ * already started a discovery then return an error.
+ */
+
+ adapter->disc_type = BT_DISC_TYPE_LE_ONLY;
+ DBG("adapter->disc_type[%d]", adapter->disc_type);
+ DBG("adapter->discovery_type [%d]", adapter->discovery_type);
+
+ list = g_slist_find_custom(adapter->le_discovery_list, sender,
+ compare_sender);
+ if (list)
+ return btd_error_busy(msg);
+
+ client = g_new0(struct watch_client, 1);
+
+ client->adapter = adapter;
+ client->owner = g_strdup(sender);
+ client->watch = g_dbus_add_disconnect_watch(dbus_conn, sender,
+ le_discovery_disconnect, client,
+ le_discovery_destroy);
+
+ adapter->le_discovery_list = g_slist_prepend(adapter->le_discovery_list,
+ client);
+
+ /*
+ * Just trigger the discovery here. In case an already running
+ * discovery in idle phase exists, it will be restarted right
+ * away.
+ */
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
+ trigger_start_discovery(adapter, 0);
+#else
+ trigger_start_le_discovery(adapter, 0);
+#endif
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *adapter_stop_le_discovery(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ const char *sender = dbus_message_get_sender(msg);
+ struct mgmt_cp_stop_le_discovery cp;
+ struct watch_client *client;
+ GSList *list;
+
+ DBG("sender %s", sender);
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (adapter->le_discovery_idle_timeout > 0) {
+ DBG("Remove LE scan trigger");
+ g_source_remove(adapter->le_discovery_idle_timeout);
+ adapter->le_discovery_idle_timeout = 0;
+ }
+#endif
+ if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+ return btd_error_not_ready(msg);
+
+ list = g_slist_find_custom(adapter->le_discovery_list, sender,
+ compare_sender);
+ if (!list)
+ return btd_error_failed(msg, "No discovery started");
+
+ client = list->data;
+
+ adapter->disc_type = BT_DISC_TYPE_LE_ONLY;
+ DBG("adapter->disc_type[%d]", adapter->disc_type);
+ DBG("adapter->discovery_type [%d]", adapter->discovery_type);
+
+ cp.type = adapter->discovery_type;
+ DBG("cp.type %d", cp.type);
+
+ /*
+ * The destroy function will cleanup the client information and
+ * also remove it from the list of discovery clients.
+ */
+ g_dbus_remove_watch(dbus_conn, client->watch);
+
+ /*
+ * As long as other discovery clients are still active, just
+ * return success.
+ */
+ DBG("cp.type %d", cp.type);
+ DBG("adapter->le_discovery_list %d", adapter->discovery_type);
+ if (adapter->le_discovery_list)
+ return dbus_message_new_method_return(msg);
+
+ /*
+ * In the idle phase of a discovery, there is no need to stop it
+ * and so it is enough to send out the signal and just return.
+ */
+ DBG("cp.type %d", cp.type);
+ DBG("adapter->discovery_enable %d", adapter->discovery_enable);
+ if (adapter->discovery_enable == 0x00) {
+ adapter->le_discovering = false;
+ g_dbus_emit_property_changed(dbus_conn, adapter->path,
+ ADAPTER_INTERFACE, "LEDiscovering");
+
+ trigger_passive_scanning(adapter);
+
+ return dbus_message_new_method_return(msg);
+ }
+ DBG("adapter->discovery_type %d", adapter->discovery_type);
+ cp.type = 0x06;
+ DBG("cp.type %d", cp.type);
+ mgmt_send(adapter->mgmt, MGMT_OP_STOP_LE_DISCOVERY,
+ adapter->dev_id, sizeof(cp), &cp,
+ stop_le_discovery_complete, adapter, NULL);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *adapter_set_advertising(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct btd_adapter *adapter = data;
+ dbus_bool_t err;
+ dbus_bool_t enable = FALSE;
+ dbus_int32_t slot_id;
+
+ DBG("adapter_set_advertising");
+
+ if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+ return btd_error_not_ready(msg);
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_BOOLEAN, &enable,
+ DBUS_TYPE_INT32, &slot_id,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ if (adapter_le_is_supported_multi_advertising() && slot_id > 0)
+ err = adapter_le_enable_multi_adv(adapter, enable, slot_id);
+ else
+ err = set_mode(adapter, MGMT_OP_SET_ADVERTISING, enable);
+
+ if (!err)
+ return btd_error_failed(msg, "Set Advertising failed");
+
+ if (enable)
+ create_advertiser(adapter, slot_id);
+
+ if (err && slot_id > 0)
+ advertising_state_changed(adapter, slot_id, enable);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *adapter_set_advertising_params(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct btd_adapter *adapter = data;
+ struct mgmt_cp_set_advertising_params cp;
+ dbus_uint32_t interval_min;
+ dbus_uint32_t interval_max;
+ dbus_uint32_t filter_policy;
+ dbus_uint32_t type;
+ dbus_int32_t slot_id;
+ gboolean ret;
+
+ DBG("Set customised advertising parameters");
+
+ if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+ return btd_error_not_ready(msg);
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_UINT32, &interval_min,
+ DBUS_TYPE_UINT32, &interval_max,
+ DBUS_TYPE_UINT32, &filter_policy,
+ DBUS_TYPE_UINT32, &type,
+ DBUS_TYPE_INT32, &slot_id,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ memset(&cp, 0, sizeof(cp));
+
+ DBG("advertising interval min %x, max %x, filter %x type %x",
+ interval_min, interval_max, filter_policy, type);
+
+ if (filter_policy > 0x03)
+ return btd_error_invalid_args(msg);
+
+ if (type > 0x04)
+ return btd_error_invalid_args(msg);
+
+ if (adapter_le_is_supported_multi_advertising() && slot_id > 0) {
+ adapter_le_adv_inst_info_t *p_inst;
+ adapter_le_adv_param_t *p_params;
+
+ p_inst = malloc(sizeof(adapter_le_adv_inst_info_t));
+ p_params = malloc(sizeof(adapter_le_adv_param_t));
+ memset(p_inst, 0, sizeof(adapter_le_adv_inst_info_t));
+ memset(p_params, 0, sizeof(adapter_le_adv_param_t));
+ p_inst->inst_id = slot_id;
+ p_params->adv_int_min = interval_min;
+ p_params->adv_int_max = interval_max;
+ p_params->adv_type = type;
+ p_params->channel_map = 0x07; /* fixed channel :: will be used all */
+ p_params->adv_filter_policy = filter_policy;
+ p_params->tx_power = BLE_ADV_TX_POWER_MID; /* TODO:need to optimize */
+ if (adapter->le_static_addr.b[5] != 0) {
+ p_inst->bdaddr_type = 0x01;
+ bacpy(&p_inst->bdaddr, &adapter->le_static_addr);
+ } else {
+ p_inst->bdaddr_type = 0x00;
+ bacpy(&p_inst->bdaddr, &adapter->bdaddr);
+ }
+
+ ret = adapter_le_set_multi_adv_params(p_inst, p_params);
+
+ free(p_inst);
+ free(p_params);
+
+ if (ret)
+ return dbus_message_new_method_return(msg);
+ else
+ return btd_error_failed(msg, "set advertising param failed");
+ } else {
+ cp.interval_max = interval_max;
+ cp.interval_min = interval_min;
+ cp.filter_policy = filter_policy;
+ cp.type = type;
+
+ if (mgmt_send(adapter->mgmt, MGMT_OP_SET_ADVERTISING_PARAMS,
+ adapter->dev_id, sizeof(cp), &cp,
+ NULL, NULL, NULL) > 0)
+ return dbus_message_new_method_return(msg);
+
+ return btd_error_failed(msg, "set advertising param failed");
+ }
+}
+
+static DBusMessage *adapter_set_advertising_data(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct btd_adapter *adapter = data;
+ struct mgmt_cp_set_advertising_data cp;
+ uint8_t *value;
+ int32_t len = 0;
+ dbus_int32_t slot_id;
+ uint8_t *adv_data = NULL;
+ int adv_len = 0;
+ char *adapter_name = adapter->name;
+ char le_name[MAX_NAME_LENGTH + 1] = { 0 };
+
+ DBG("Set advertising data");
+
+ if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+ return btd_error_not_ready(msg);
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &value, &len,
+ DBUS_TYPE_INT32, &slot_id,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ if (len > ADV_DATA_MAX_LENGTH - 3)
+ return btd_error_invalid_args(msg);
+
+ if (adapter->le_static_addr.b[5] != 0) {
+ char *ptr = NULL;
+
+ g_strlcpy(le_name, adapter_name,
+ sizeof(le_name) - LE_BEARER_POSTFIX_LEN);
+ if (!g_utf8_validate(le_name, -1, (const char **)&ptr))
+ *ptr = '\0';
+
+ g_strlcat(le_name, LE_BEARER_POSTFIX, sizeof(le_name));
+ adapter_name = le_name;
+ }
+
+ adapter_le_set_missed_adv_data(value, len, FALSE,
+ adapter_name, adapter->adv_tx_power, &adv_data, &adv_len, adapter);
+
+ if (adapter_le_is_supported_multi_advertising() && slot_id > 0) {
+ if (adapter_le_set_multi_adv_data(slot_id, FALSE, adv_len, adv_data)) {
+ g_free(adv_data);
+ return dbus_message_new_method_return(msg);
+ } else {
+ g_free(adv_data);
+ return btd_error_failed(msg, "set advertising data failed");
+ }
+ } else {
+ memcpy(&cp, adv_data, adv_len);
+
+ if (mgmt_send(adapter->mgmt, MGMT_OP_SET_ADVERTISING_DATA,
+ adapter->dev_id, adv_len,
+ &cp, NULL, NULL, NULL) > 0) {
+ g_free(adv_data);
+ return dbus_message_new_method_return(msg);
+ }
+
+ g_free(adv_data);
+ return btd_error_failed(msg, "set advertising data failed");
+ }
+}
+
+static DBusMessage *adapter_le_scan_filter_param_setup(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct btd_adapter *adapter = data;
+#ifdef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+ dbus_bool_t ctlr_filter_support = TRUE;
+#endif
+ dbus_int32_t client_if, action, filt_index;
+ dbus_int32_t feat_seln, list_logic_type, filt_logic_type;
+ dbus_int32_t rssi_high_thres, rssi_low_thres, dely_mode;
+ dbus_int32_t found_timeout, lost_timeout, found_timeout_cnt;
+ adapter_le_scan_filter_param_t params;
+ gboolean err;
+
+ DBG("adapter_le_scan_filter_param_setup");
+
+ if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+ return btd_error_not_ready(msg);
+
+ if (adapter_le_get_scan_filter_size() == 0)
+#ifndef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+ return btd_error_not_supported(msg);
+#else
+ ctlr_filter_support = FALSE;
+#endif
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_INT32, &client_if,
+ DBUS_TYPE_INT32, &action,
+ DBUS_TYPE_INT32, &filt_index,
+ DBUS_TYPE_INT32, &feat_seln,
+ DBUS_TYPE_INT32, &list_logic_type,
+ DBUS_TYPE_INT32, &filt_logic_type,
+ DBUS_TYPE_INT32, &rssi_high_thres,
+ DBUS_TYPE_INT32, &rssi_low_thres,
+ DBUS_TYPE_INT32, &dely_mode,
+ DBUS_TYPE_INT32, &found_timeout,
+ DBUS_TYPE_INT32, &lost_timeout,
+ DBUS_TYPE_INT32, &found_timeout_cnt,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ memset(&params, 0, sizeof(params));
+
+ params.action = action;
+ params.index = filt_index;
+ params.feature = feat_seln;
+ params.filter_logic_type = filt_logic_type;
+ params.list_logic_type = list_logic_type;
+ params.delivery_mode = dely_mode;
+ params.rssi_high_threshold = rssi_high_thres;
+
+ if (params.delivery_mode == ON_FOUND) {
+ params.rssi_low_threshold = rssi_low_thres;
+ params.onfound_timeout = found_timeout;
+ params.onfound_timeout_cnt = found_timeout_cnt;
+ params.onlost_timeout = lost_timeout;
+ }
+
+#ifdef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+ if (ctlr_filter_support)
+#endif
+ err = adapter_le_set_scan_filter_params(&params);
+#ifdef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+ else
+ err = adapter_le_set_platform_scan_filter_params(adapter, &params);
+#endif
+
+ if (!err)
+ return btd_error_failed(msg, "Failed to scan filter param setup");
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *adapter_le_scan_filter_add_remove(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct btd_adapter *adapter = data;
+ struct btd_device *dev = NULL;
+ dbus_int32_t client_if, action, filt_type, filt_index;
+ dbus_int32_t company_id, company_id_mask;
+ gchar *address = NULL;
+ dbus_uint32_t address_type = 0;
+ uint8_t addr_type;
+ GSList *list;
+ char ida_string[18];
+ uint8_t *p_uuid, *p_uuid_mask, *p_data, *p_mask;
+ int32_t uuid_len = 0, uuid_mask_len = 0, data_len = 0, mask_len = 0;
+ gboolean err;
+#ifdef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+ dbus_bool_t ctlr_filter_support = TRUE;
+#endif
+
+ DBG("adapter_le_scan_filter_add_remove");
+
+ if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+ return btd_error_not_ready(msg);
+
+ /* if controller does not support vendor specific scan filtering feature
+ * then add the filter into platform supported scan filters.
+ */
+ if (adapter_le_get_scan_filter_size() == 0) {
+#ifndef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+ return btd_error_not_supported(msg);
+#else
+ ctlr_filter_support = FALSE;
+#endif
+ }
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_INT32, &client_if,
+ DBUS_TYPE_INT32, &action,
+ DBUS_TYPE_INT32, &filt_type,
+ DBUS_TYPE_INT32, &filt_index,
+ DBUS_TYPE_INT32, &company_id,
+ DBUS_TYPE_INT32, &company_id_mask,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &p_uuid, &uuid_len,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &p_uuid_mask, &uuid_mask_len,
+ DBUS_TYPE_STRING, &address,
+ DBUS_TYPE_UINT32, &address_type,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &p_data, &data_len,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &p_mask, &mask_len,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ list = g_slist_find_custom(adapter->devices, address, device_rpa_cmp);
+ if (!list)
+ list = g_slist_find_custom(adapter->devices, address,
+ device_address_cmp);
+ if (list)
+ dev = list->data;
+ if (dev && device_get_rpa_exist(dev) == true) {
+ ba2str(device_get_address(dev), ida_string);
+ if (btd_device_get_bdaddr_type(dev) == BDADDR_LE_PUBLIC)
+ addr_type = 0x00;
+ else
+ addr_type = 0x01;
+ } else {
+ memcpy(ida_string, address, sizeof(ida_string));
+ addr_type = 0x00;
+ }
+
+ DBG("addr %s, type %d", ida_string, addr_type);
+
+#ifdef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+ if (ctlr_filter_support)
+#endif
+ err = adapter_le_set_scan_filter_data(client_if, action, filt_type,
+ filt_index, company_id, company_id_mask,
+ uuid_len, p_uuid, uuid_mask_len, p_uuid_mask,
+ ida_string, addr_type, data_len, p_data, mask_len, p_mask);
+#ifdef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+ else
+ err = adapter_le_set_platform_scan_filter_data(adapter, client_if, action, filt_type,
+ filt_index, company_id, company_id_mask,
+ uuid_len, p_uuid, uuid_mask_len, p_uuid_mask,
+ ida_string, addr_type, data_len, p_data, mask_len, p_mask);
+#endif
+ if (!err)
+ return btd_error_failed(msg, "Failed to add/remove filter");
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *adapter_le_scan_filter_clear(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct btd_adapter *adapter = data;
+ dbus_int32_t client_if = 0;
+ dbus_int32_t filt_index = 0;
+ gboolean err;
+#ifdef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+ dbus_bool_t ctlr_filter_support = TRUE;
+#endif
+
+ DBG("adapter_le_scan_filter_clear");
+
+ if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+ return btd_error_not_ready(msg);
+
+ if (adapter_le_get_scan_filter_size() == 0)
+#ifndef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+ return btd_error_not_supported(msg);
+#else
+ ctlr_filter_support = FALSE;
+#endif
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_INT32, &client_if,
+ DBUS_TYPE_INT32, &filt_index,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+#ifdef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+ if (ctlr_filter_support)
+#endif
+ err = adapter_le_clear_scan_filter_data(client_if, filt_index);
+#ifdef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+ else
+ err = adapter_le_clear_platform_scan_filter_data(adapter, filt_index);
+#endif
+
+ if (!err)
+ return btd_error_failed(msg, "Failed to clear filter");
+
+ return dbus_message_new_method_return(msg);
+}
+
+
+static DBusMessage *adapter_le_scan_filter_enable(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct btd_adapter *adapter = data;
+ dbus_bool_t enable = FALSE;
+ dbus_int32_t client_if = 0;
+ gboolean err;
+#ifdef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+ dbus_bool_t ctlr_filter_support = TRUE;
+#endif
+
+ DBG("adapter_le_scan_filter_enable");
+
+ if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+ return btd_error_not_ready(msg);
+
+#ifdef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+ /* if controller does not support vendor specific scan filtering feature
+ * then enable platform supported scan filtering functionalites.
+ */
+#endif
+ if (adapter_le_get_scan_filter_size() == 0)
+#ifndef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+ return btd_error_not_supported(msg);
+#else
+ ctlr_filter_support = FALSE;
+#endif
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_INT32, &client_if,
+ DBUS_TYPE_BOOLEAN, &enable,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+#ifdef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+ if (ctlr_filter_support)
+#endif
+ err = adapter_le_enable_scan_filtering(enable);
+#ifdef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+ else
+ err = adapter_le_enable_platform_scan_filtering(adapter, enable);
+#endif
+
+ if (!err)
+ return btd_error_failed(msg, "Failed to enable scan filtering");
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *adapter_le_set_scan_params(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct btd_adapter *adapter = data;
+ struct mgmt_cp_le_set_scan_params cp;
+ uint32_t type;
+ uint32_t interval;
+ uint32_t window;
+
+ DBG("Set scan parameters");
+
+ if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+ return btd_error_not_ready(msg);
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_UINT32, &type,
+ DBUS_TYPE_UINT32, &interval,
+ DBUS_TYPE_UINT32, &window,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ DBG("scan type %x, interval %x, window %x",
+ type, interval, window);
+ memset(&cp, 0, sizeof(cp));
+
+ cp.type = type;
+ cp.interval = interval;
+ cp.window = window;
+ adapter->scan_type = type;
+
+ if (mgmt_send(adapter->mgmt, MGMT_OP_LE_SET_SCAN_PARAMS,
+ adapter->dev_id, sizeof(cp), &cp,
+ NULL, NULL, NULL) > 0)
+ return dbus_message_new_method_return(msg);
+
+ return btd_error_failed(msg, "set scan parameters failed");
+}
+
+static DBusMessage *adapter_set_scan_rsp_data(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct btd_adapter *adapter = data;
+ struct mgmt_cp_set_scan_rsp_data cp;
+ uint8_t *value;
+ int32_t len = 0;
+ dbus_int32_t slot_id;
+ uint8_t *adv_data = NULL;
+ int adv_len = 0;
+
+ char *adapter_name = adapter->name;
+ char le_name[MAX_NAME_LENGTH + 1] = { 0 };
+
+ DBG("Set scan response data");
+
+ if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+ return btd_error_not_ready(msg);
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &value, &len,
+ DBUS_TYPE_INT32, &slot_id,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ if (len > SCAN_RESPONSE_DATA_LENGTH_MAX)
+ return btd_error_invalid_args(msg);
+
+ if (adapter->le_static_addr.b[5] != 0) {
+ char *ptr = NULL;
+
+ g_strlcpy(le_name, adapter_name,
+ sizeof(le_name) - LE_BEARER_POSTFIX_LEN);
+ if (!g_utf8_validate(le_name, -1, (const char **)&ptr))
+ *ptr = '\0';
+
+ g_strlcat(le_name, LE_BEARER_POSTFIX, sizeof(le_name));
+ adapter_name = le_name;
+ }
+
+ adapter_le_set_missed_adv_data(value, len, TRUE,
+ adapter_name, adapter->adv_tx_power, &adv_data, &adv_len, adapter);
+
+ if (adapter_le_is_supported_multi_advertising() && slot_id > 0) {
+ if (adapter_le_set_multi_adv_data(slot_id, TRUE, adv_len, (uint8_t *)adv_data)) {
+ g_free(adv_data);
+ return dbus_message_new_method_return(msg);
+ } else {
+ g_free(adv_data);
+ return btd_error_failed(msg, "set advertising data failed");
+ }
+ } else {
+ memcpy(&cp, adv_data, adv_len);
+
+ if (mgmt_send(adapter->mgmt, MGMT_OP_SET_SCAN_RSP_DATA,
+ adapter->dev_id, adv_len, &cp,
+ NULL, NULL, NULL) > 0) {
+ g_free(adv_data);
+ return dbus_message_new_method_return(msg);
+ }
+
+ g_free(adv_data);
+ return btd_error_failed(msg, "set scan reponse data failed");
+ }
+}
+
+static DBusMessage *adapter_add_device_white_list(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct btd_adapter *adapter = data;
+ struct mgmt_cp_add_dev_white_list cp;
+ const gchar *address;
+ bdaddr_t bdaddr;
+ dbus_uint32_t address_type;
+ struct btd_device *dev;
+
+ DBG("Add device whie list");
+ if (dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &address,
+ DBUS_TYPE_UINT32, &address_type,
+ DBUS_TYPE_INVALID) == FALSE)
+ return btd_error_invalid_args(msg);
+
+ if (bachk(address) < 0)
+ return btd_error_invalid_args(msg);
+
+ if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+ return btd_error_not_ready(msg);
+
+ DBG("addr %s, type %d", address, address_type);
+ str2ba(address, &bdaddr);
+
+ dev = btd_adapter_find_device(adapter, &bdaddr,
+ address_type ? BDADDR_LE_RANDOM : BDADDR_LE_PUBLIC);
+ if (dev && device_get_rpa_exist(dev) == true) {
+ if (adapter_le_is_supported_offloading() == FALSE) {
+ error("Spec based command is not supported yet");
+ return btd_error_not_supported(msg);
+ }
+
+ /* Add IRK value to list */
+ if (adapter_le_add_irk_to_list(device_get_irk_value(dev),
+ device_get_address(dev),
+ btd_device_get_bdaddr_type(dev))) {
+ return dbus_message_new_method_return(msg);
+ } else {
+ return btd_error_failed(msg, "Add LE IRK to list failed");
+ }
+ }
+
+ memset(&cp, 0, sizeof(cp));
+
+ cp.bdaddr_type = address_type;
+ memcpy(&cp.bdaddr, &bdaddr, sizeof(bdaddr_t));
+
+ if (mgmt_send(adapter->mgmt, MGMT_OP_ADD_DEV_WHITE_LIST,
+ adapter->dev_id, sizeof(cp), &cp,
+ NULL, NULL, NULL) > 0)
+ return dbus_message_new_method_return(msg);
+
+ return btd_error_failed(msg, "add device white list failed");
+}
+
+static DBusMessage *adapter_remove_device_white_list(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct btd_adapter *adapter = data;
+ struct mgmt_cp_remove_dev_white_list cp;
+ const gchar *address;
+ bdaddr_t bdaddr;
+ dbus_uint32_t address_type;
+ struct btd_device *dev;
+
+ DBG("Remove device whie list");
+
+ if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+ return btd_error_not_ready(msg);
+
+ if (dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &address,
+ DBUS_TYPE_UINT32, &address_type,
+ DBUS_TYPE_INVALID) == FALSE)
+ return btd_error_invalid_args(msg);
+
+ if (bachk(address) < 0)
+ return btd_error_invalid_args(msg);
+
+ DBG("addr %s, type %d", address, address_type);
+ str2ba(address, &bdaddr);
+
+ dev = btd_adapter_find_device(adapter, &bdaddr,
+ address_type ? BDADDR_LE_RANDOM : BDADDR_LE_PUBLIC);
+ if (dev && device_get_rpa_exist(dev) == true) {
+ if (adapter_le_is_supported_offloading() == FALSE) {
+ error("Spec based command is not supported yet");
+ return btd_error_not_supported(msg);
+ }
+
+ /* Remove IRK value to list */
+ if (adapter_le_remove_irk_to_list(device_get_address(dev),
+ btd_device_get_bdaddr_type(dev))) {
+ return dbus_message_new_method_return(msg);
+ } else {
+ return btd_error_failed(msg, "Remove IRK is failed");
+ }
+ }
+
+ memset(&cp, 0, sizeof(cp));
+
+ cp.bdaddr_type = address_type;
+ memcpy(&cp.bdaddr, &bdaddr, sizeof(bdaddr_t));
+
+ if (mgmt_send(adapter->mgmt, MGMT_OP_REMOVE_DEV_FROM_WHITE_LIST,
+ adapter->dev_id, sizeof(cp), &cp,
+ NULL, NULL, NULL) > 0)
+ return dbus_message_new_method_return(msg);
+
+ return btd_error_failed(msg, "remove device white list failed");
+}
+
+static DBusMessage *adapter_clear_device_white_list(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct btd_adapter *adapter = data;
+
+ DBG("Clear device whie list");
+
+ if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+ return btd_error_not_ready(msg);
+
+ if (mgmt_send(adapter->mgmt, MGMT_OP_CLEAR_DEV_WHITE_LIST,
+ adapter->dev_id, 0, NULL,
+ NULL, NULL, NULL) > 0)
+ return dbus_message_new_method_return(msg);
+
+ return btd_error_failed(msg, "clear white list failed");
+}
+
+static DBusMessage *adapter_set_le_privacy(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct btd_adapter *adapter = data;
+ dbus_bool_t err;
+ dbus_bool_t enable_privacy = FALSE;
+
+ if (!(adapter->supported_settings & MGMT_SETTING_PRIVACY))
+ return btd_error_not_supported(msg);
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_BOOLEAN,
+ &enable_privacy, DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ if (enable_privacy) {
+ if (adapter->current_settings & MGMT_SETTING_PRIVACY)
+ return btd_error_already_exists(msg);
+ } else {
+ if (!(adapter->current_settings & MGMT_SETTING_PRIVACY))
+ return btd_error_already_exists(msg);
+ }
+
+ err = set_privacy(adapter, enable_privacy);
+
+ if (!err)
+ return btd_error_failed(msg, "Set Le Privacy failed");
+
+ return dbus_message_new_method_return(msg);
+}
+
+static void set_le_static_address(struct btd_adapter *adapter)
+{
+ int fd;
+ int ret;
+ char address[18];
+ char dirname[PATH_MAX];
+
+ snprintf(dirname, PATH_MAX, STORAGEDIR "/%s", "le_static_addr");
+ if (access(dirname, F_OK) < 0) {
+ int i;
+ bdaddr_t le_static_addr;
+
+ le_static_addr.b[5] = adapter->bdaddr.b[5] | 0xc0;
+ for (i = 0; i < 5; i++) {
+ le_static_addr.b[i] =
+ (adapter->bdaddr.b[i] & 0x7f) << 1 |
+ (adapter->bdaddr.b[i] & 0x80) >> 7;
+ }
+
+ /*
+ * < How to get Public address from above static address >
+ *
+ * for (i = 0; i < 5; i++) {
+ * bredr_addr.b[i] =
+ * (adapter->le_static_addr.b[i] & 0xfe) >> 1 |
+ * (adapter->le_static_addr.b[i] & 0x01) << 7;
+ * }
+ * bredr_addr.b[5] = {the value from advertising data}
+ */
+
+ fd = open(dirname, O_WRONLY | O_CREAT, 0644);
+ if (fd >= 0) {
+ ba2str(&le_static_addr, address);
+ DBG("LE static random : %s", address);
+ ret = write(fd, address, strlen(address));
+ if (ret < 0) {
+ error("Cannot save LE address : %s",
+ strerror(errno));
+ }
+
+ ret = fdatasync(fd);
+ if (ret < 0)
+ error("sync failed : %s", strerror(errno));
+
+ close(fd);
+ } else {
+ error("Cannot save LE address");
+ }
+ bacpy(&adapter->le_static_addr, &le_static_addr);
+ } else {
+ fd = open(dirname, O_RDONLY);
+ if (fd >= 0) {
+ ret = read(fd, address, sizeof(address));
+ if (ret >= 17) {
+ /* xx:xx:xx:xx:xx:xx */
+ address[17] = '\0';
+ DBG("LE static random : %s", address);
+ str2ba(address, &adapter->le_static_addr);
+ adapter->le_static_addr.b[5] |= 0xc0;
+ } else
+ error("Invalid LE address");
+ close(fd);
+ } else {
+ error("Cannot get LE address");
+ }
+ }
+
+ return;
+}
+
+static void set_le_static_address_complete(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+
+ DBG("index %u status 0x%02x", adapter->dev_id, status);
+
+ if (status != MGMT_STATUS_SUCCESS) {
+ error("Failed to set static address for index %u: %s (0x%02x)",
+ adapter->dev_id, mgmt_errstr(status), status);
+ if (adapter->le_static_addr.b[5] != 0)
+ bacpy(&adapter->le_static_addr, BDADDR_ANY);
+ else
+ set_le_static_address(adapter);
+ return;
+ }
+
+ return;
+}
+
+static DBusMessage *adapter_set_le_static_address(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct btd_adapter *adapter = data;
+ dbus_bool_t is_enable = FALSE;
+ struct mgmt_cp_set_static_address cp;
+
+ if (!(adapter->supported_settings & MGMT_OP_SET_STATIC_ADDRESS)) {
+ error("LE static address is not supported");
+ return btd_error_not_supported(msg);
+ }
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_BOOLEAN, &is_enable,
+ DBUS_TYPE_INVALID)) {
+ error("Invalid arguments");
+ return btd_error_invalid_args(msg);
+ }
+
+ memset(&cp, 0x00, sizeof(cp));
+ if (is_enable) {
+ if (adapter->le_static_addr.b[5] != 0) {
+ DBG("LE static address is already configured");
+ return dbus_message_new_method_return(msg);
+ }
+ set_le_static_address(adapter);
+ bacpy(&cp.bdaddr, &adapter->le_static_addr);
+ } else {
+ if (adapter->le_static_addr.b[5] == 0) {
+ DBG("LE static address is not configured");
+ return dbus_message_new_method_return(msg);
+ }
+ bacpy(&adapter->le_static_addr, BDADDR_ANY);
+ }
+ DBG("Set static random address : %d", is_enable);
+
+ if (mgmt_send(mgmt_master, MGMT_OP_SET_STATIC_ADDRESS, adapter->dev_id,
+ sizeof(cp), &cp,
+ set_le_static_address_complete, adapter, NULL) <= 0) {
+ error("Failed to set static address : %d", is_enable);
+ if (is_enable)
+ bacpy(&adapter->le_static_addr, BDADDR_ANY);
+ else
+ set_le_static_address(adapter);
+ return btd_error_failed(msg, "Unable to set static address");
+ }
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *adapter_enable_rssi(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct btd_adapter *adapter = data;
+ struct mgmt_cp_set_enable_rssi cp;
+ struct mgmt_cp_disable_rssi cp_dis;
+ bdaddr_t bt_addr = { { 0, } };
+ const gchar *address = NULL;
+
+ const char *sender = dbus_message_get_sender(msg);
+ dbus_int32_t link_type;
+ dbus_int32_t low_threshold;
+ dbus_int32_t in_range_threshold;
+ dbus_int32_t high_threshold;
+
+ DBG("Enable RSSI called");
+ DBG("sender %s", sender);
+ if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+ return btd_error_not_ready(msg);
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &address,
+ DBUS_TYPE_INT32, &link_type,
+ DBUS_TYPE_INT32, &low_threshold,
+ DBUS_TYPE_INT32, &in_range_threshold,
+ DBUS_TYPE_INT32, &high_threshold,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ DBG("Enable RSSI: [%s %d %d %d %d]", address, link_type,
+ low_threshold, in_range_threshold, high_threshold);
+
+ DBG("BT address [%s]", address);
+ memset(&bt_addr, 0, sizeof(bdaddr_t));
+ str2ba(address, &bt_addr);
+ memset(&cp, 0, sizeof(struct mgmt_cp_set_enable_rssi));
+ memset(&cp_dis, 0, sizeof(struct mgmt_cp_disable_rssi));
+
+ if (bachk(address) < 0)
+ return btd_error_invalid_args(msg);
+
+// if (!btd_adapter_find_device(adapter, address))
+// return btd_error_not_found(msg);
+
+ if (low_threshold == 0 && in_range_threshold == 0 && high_threshold == 0) {
+ cp_dis.bdaddr = bt_addr;
+ cp_dis.link_type = link_type;
+ DBG("Disable Request");
+ if (mgmt_send(adapter->mgmt, MGMT_OP_SET_RSSI_DISABLE,
+ adapter->dev_id, sizeof(cp_dis), &cp_dis,
+ NULL, NULL, NULL) > 0)
+ return dbus_message_new_method_return(msg);
+ } else {
+ cp.low_th = low_threshold;
+ cp.in_range_th = in_range_threshold;
+ cp.high_th = high_threshold;
+ cp.bdaddr = bt_addr;
+ cp.link_type = link_type;
+ DBG("Enable Request");
+ if (mgmt_send(adapter->mgmt, MGMT_OP_SET_RSSI_ENABLE,
+ adapter->dev_id, sizeof(cp), &cp,
+ NULL, NULL, NULL) > 0)
+ return dbus_message_new_method_return(msg);
+ }
+ return btd_error_failed(msg, "Enable/Disable RSSI Failed");
+}
+
+static DBusMessage *adapter_get_rssi(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct btd_adapter *adapter = data;
+ struct mgmt_cp_get_raw_rssi cp;
+ bdaddr_t bt_addr;
+ const gchar *address = NULL;
+ dbus_int32_t link_type;
+ const char *sender = dbus_message_get_sender(msg);
+
+ DBG("Get RSSI called");
+ DBG("sender %s", sender);
+ if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+ return btd_error_not_ready(msg);
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &address,
+ DBUS_TYPE_INT32, &link_type,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ DBG("BT address [%s] link type [%d]", address, link_type);
+ memset(&bt_addr, 0, sizeof(bdaddr_t));
+ str2ba(address, &bt_addr);
+ memset(&cp, 0, sizeof(struct mgmt_cp_get_raw_rssi));
+
+ if (bachk(address) < 0)
+ return btd_error_invalid_args(msg);
+
+// if (!btd_adapter_find_device(adapter, address))
+// return btd_error_not_found(msg);
+
+ memcpy(&(cp.bt_address), &bt_addr, sizeof(bdaddr_t));
+ cp.link_type = link_type;
+ DBG("RAW RSSI Request");
+ if (mgmt_send(adapter->mgmt, MGMT_OP_GET_RAW_RSSI,
+ adapter->dev_id, sizeof(cp), &cp,
+ NULL, NULL, NULL) > 0)
+ return dbus_message_new_method_return(msg);
+
+ return btd_error_failed(msg, "Get Raw RSSI Failed");
+}
+
+#if !defined(__SPRD_PATCH__)
+static void get_adv_tx_power_complete(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ const struct mgmt_rp_get_adv_tx_power *rp = param;
+
+ if (!rp) {
+ error("Error ocurred in Getting adv tx power, rp is NULL");
+ return;
+ }
+
+ if (status != MGMT_STATUS_SUCCESS) {
+ error("Failed to get adv tx power: %s (0x%02x)",
+ mgmt_errstr(status), status);
+ return;
+ }
+
+ if (length < sizeof(*rp)) {
+ error("Wrong size of get adv tx power");
+ return;
+ }
+
+ adapter->adv_tx_power = rp->adv_tx_power;
+ return;
+}
+
+static void adapter_get_adv_tx_power(void *data)
+{
+ struct btd_adapter *adapter = data;
+
+ mgmt_send(adapter->mgmt, MGMT_OP_GET_ADV_TX_POWER,
+ adapter->dev_id, 0, NULL,
+ get_adv_tx_power_complete, adapter, NULL);
+ return;
+}
+#endif
+
+static DBusMessage *set_wbs_parameters(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct btd_adapter *adapter = data;
+ const gchar *role = NULL;
+ const gchar *address = NULL;
+ struct mgmt_cp_set_voice_setting cp;
+ bdaddr_t bt_addr = { { 0, } };
+
+ DBG("+");
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &role,
+ DBUS_TYPE_STRING, &address,
+ DBUS_TYPE_INVALID)) {
+ return btd_error_invalid_args(msg);
+ }
+
+ DBG("Role = %s", role);
+ DBG("Address = %s", address);
+
+ memset(&cp, 0, sizeof(cp));
+
+ cp.voice_setting = BT_VOICE_TRANSPARENT | BT_VOICE_CVSD_16BIT;
+
+ if (g_strcmp0(role, "Handsfree") == 0)
+ cp.sco_role = MGMT_SCO_ROLE_HANDSFREE;
+ else if (g_strcmp0(role, "Gateway") == 0)
+ cp.sco_role = MGMT_SCO_ROLE_AUDIO_GATEWAY;
+
+ str2ba(address, &bt_addr);
+ memcpy(&(cp.bdaddr), &bt_addr, sizeof(bdaddr_t));
+
+ if (mgmt_send(adapter->mgmt, MGMT_OP_SET_VOICE_SETTING,
+ adapter->dev_id, sizeof(cp), &cp,
+ NULL, NULL, NULL) == 0)
+ error("mgmt_send failed for voice setting");
+
+ DBG("-");
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *set_nb_parameters(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct btd_adapter *adapter = data;
+ const gchar *role;
+ const gchar *address = NULL;
+ struct mgmt_cp_set_voice_setting cp;
+ bdaddr_t bt_addr = { { 0, } };
+
+ DBG("+");
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &role,
+ DBUS_TYPE_STRING, &address,
+ DBUS_TYPE_INVALID)) {
+ return btd_error_invalid_args(msg);
+ }
+
+ DBG("Role = %s", role);
+ DBG("Address = %s", address);
+
+ memset(&cp, 0, sizeof(cp));
+
+ cp.voice_setting = BT_VOICE_CVSD_16BIT;
+
+ if (g_strcmp0(role, "Handsfree") == 0)
+ cp.sco_role = MGMT_SCO_ROLE_HANDSFREE;
+ else if (g_strcmp0(role, "Gateway") == 0)
+ cp.sco_role = MGMT_SCO_ROLE_AUDIO_GATEWAY;
+
+ str2ba(address, &bt_addr);
+ memcpy(&(cp.bdaddr), &bt_addr, sizeof(bdaddr_t));
+
+ if (mgmt_send(adapter->mgmt, MGMT_OP_SET_VOICE_SETTING,
+ adapter->dev_id, sizeof(cp), &cp,
+ NULL, NULL, NULL) == 0)
+ error("mgmt_send failed for voice setting");
+
+ DBG("-");
+
+ return dbus_message_new_method_return(msg);
+}
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void btd_adapter_set_read_le_data_length_handler(
+ struct btd_adapter *adapter,
+ struct le_data_length_read_handler *handler)
+{
+ adapter->read_handler = handler;
+}
+
+static void le_read_maximum_data_length_return_param_complete(
+ uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ 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;
+
+ if (!rp) {
+ error("Error ocurred in Reading maximum data length, rp is NULL");
+ g_free(adapter->read_handler);
+ return;
+ }
+
+ if (status != MGMT_STATUS_SUCCESS) {
+ error("le read maximum data length failed: %s (0x%02x)",
+ mgmt_errstr(status), status);
+ max_tx_octects = 0;
+ max_tx_time =0;
+ max_rx_octects = 0;
+ max_rx_time = 0;
+
+ g_free(adapter->read_handler);
+ return;
+ }
+
+ if (length < sizeof(*rp)) {
+ error("Too small le read maximum data length response");
+ 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;
+ }
+
+ if (!adapter->read_handler ||
+ !adapter->read_handler->read_callback) {
+ g_free(adapter->read_handler);
+ return;
+ }
+
+ adapter->read_handler->read_callback(adapter,
+ max_tx_octects, max_tx_time,
+ max_rx_octects, max_rx_time,
+ adapter->read_handler->user_data);
+
+ g_free(adapter->read_handler);
+ adapter->read_handler = NULL;
+}
+
+int btd_adapter_le_read_maximum_data_length(
+ struct btd_adapter *adapter)
+{
+ if (mgmt_send(adapter->mgmt,
+ MGMT_OP_LE_READ_MAXIMUM_DATA_LENGTH,
+ adapter->dev_id, 0, NULL,
+ le_read_maximum_data_length_return_param_complete,
+ adapter, NULL) > 0)
+ return 0;
+
+ return -EIO;
+}
+
+static gint read_request_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct le_data_length_read_request *data = a;
+ const struct btd_adapter *adapter = b;
+
+ return data->adapter != adapter;
+}
+
+static struct le_data_length_read_request *find_read_le_data_length_request(
+ struct btd_adapter *adapter)
+{
+ GSList *match;
+
+ match = g_slist_find_custom(read_requests, adapter, read_request_cmp);
+
+ if (match)
+ return match->data;
+
+ return NULL;
+}
+
+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,
+ void *user_data)
+{
+ DBusMessage *reply;
+ struct le_data_length_read_request *read_request;
+
+ read_request = find_read_le_data_length_request(adapter);
+ if (!read_request)
+ return;
+
+ reply = g_dbus_create_reply(read_request->msg,
+ DBUS_TYPE_UINT16, &max_tx_octects,
+ DBUS_TYPE_UINT16, &max_tx_time,
+ DBUS_TYPE_UINT16, &max_rx_octects,
+ DBUS_TYPE_UINT16, &max_rx_time,
+ DBUS_TYPE_INVALID);
+
+ if (!reply) {
+ btd_error_failed(read_request->msg,
+ "Failed to read max data length.");
+ return;
+ }
+
+ read_requests = g_slist_remove(read_requests, read_request);
+ dbus_message_unref(read_request->msg);
+ g_free(read_request);
+
+ if (!g_dbus_send_message(dbus_conn, reply))
+ error("D-Bus send failed");
+}
+
+static DBusMessage *le_read_maximum_data_length(
+ DBusConnection *conn, DBusMessage *msg,
+ void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ struct le_data_length_read_request *read_request;
+ struct le_data_length_read_handler *handler;
+
+ if (find_read_le_data_length_request(adapter))
+ return btd_error_in_progress(msg);
+
+ if (btd_adapter_le_read_maximum_data_length(adapter))
+ return btd_error_failed(msg, "Unable to read maximum le data length");
+
+ read_request = g_new(struct le_data_length_read_request, 1);
+
+ read_request->msg = dbus_message_ref(msg);
+ read_request->adapter = adapter;
+
+ read_requests = g_slist_append(read_requests, read_request);
+
+ handler = g_new0(struct le_data_length_read_handler, 1);
+
+ handler->read_callback =
+ (read_max_data_length_cb_t)le_read_data_length_complete;
+
+ btd_adapter_set_read_le_data_length_handler(
+ read_request->adapter, handler);
+
+ return NULL;
+
+}
+
+void le_write_host_suggested_data_length_return_param_complete(
+ uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ if (status != MGMT_STATUS_SUCCESS) {
+ error("le write host suggested data length failed: %s (0x%02x)",
+ mgmt_errstr(status), status);
+ }
+
+ return;
+}
+
+static DBusMessage *le_write_host_suggested_default_data_length(
+ DBusConnection *conn, DBusMessage *msg,
+ void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ struct mgmt_cp_le_write_host_suggested_data_length cp;
+ dbus_uint16_t def_tx_Octets;
+ dbus_uint16_t def_tx_time;
+
+ if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+ return btd_error_not_ready(msg);
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_UINT16, &def_tx_Octets,
+ DBUS_TYPE_UINT16, &def_tx_time,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ memset(&cp, 0, sizeof(cp));
+ cp.def_tx_octets = def_tx_Octets;
+ cp.def_tx_time = def_tx_time;
+
+ if (mgmt_send(adapter->mgmt,
+ MGMT_OP_LE_WRITE_HOST_SUGGESTED_DATA_LENGTH,
+ adapter->dev_id, sizeof(cp), &cp,
+ le_write_host_suggested_data_length_return_param_complete,
+ adapter, NULL) > 0)
+ return dbus_message_new_method_return(msg);
+
+ return btd_error_failed(msg, "Unable to write host suggested le data length values");
+}
+
+static void le_read_suggested_default_data_length_return_param_complete(
+ uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ 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;
+
+ if (!rp) {
+ error("Error ocurred in Reading suggested data length, rp is NULL");
+ if (adapter->def_read_handler)
+ g_free(adapter->def_read_handler->user_data);
+
+ g_free(adapter->def_read_handler);
+ return;
+ }
+
+ 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;
+
+ if (adapter->def_read_handler)
+ g_free(adapter->def_read_handler->user_data);
+
+ g_free(adapter->def_read_handler);
+ return;
+ }
+
+ if (length < sizeof(*rp)) {
+ goto done;
+ } else {
+ def_tx_octects = rp->def_tx_octets;
+ def_tx_time =rp->def_tx_time;
+ DBG("retrieving host suggested data length values %d %d", def_tx_octects, def_tx_time);
+ }
+
+ if (!adapter->def_read_handler)
+ return;
+
+ if(!adapter->def_read_handler->read_callback) {
+ goto done;
+ }
+
+ adapter->def_read_handler->read_callback(adapter,
+ def_tx_octects, def_tx_time,
+ adapter->def_read_handler->user_data);
+done:
+ if (adapter->def_read_handler)
+ g_free(adapter->def_read_handler->user_data);
+
+ g_free(adapter->def_read_handler);
+ adapter->def_read_handler = NULL;
+}
+
+int btd_adapter_le_read_suggested_default_data_length(
+ struct btd_adapter *adapter)
+{
+ if (mgmt_send(adapter->mgmt,
+ MGMT_OP_LE_READ_HOST_SUGGESTED_DATA_LENGTH,
+ adapter->dev_id, 0, NULL,
+ le_read_suggested_default_data_length_return_param_complete,
+ adapter, NULL) > 0) {
+ return 0;
+ }
+
+ return -EIO;
+}
+
+static void le_read_host_suggested_default_length_complete(
+ struct btd_adapter *adapter,
+ uint16_t def_tx_octects, uint16_t def_tx_time,
+ void *user_data)
+{
+ DBusMessage *reply;
+ struct le_data_length_read_request *read_request;
+
+ 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_INVALID);
+
+ if (!reply) {
+ btd_error_failed(read_request->msg,
+ "Failed to read host suggested def data length values");
+ return;
+ }
+
+ read_requests = g_slist_remove(read_requests, read_request);
+ dbus_message_unref(read_request->msg);
+ g_free(read_request);
+
+ if (!g_dbus_send_message(dbus_conn, reply))
+ error("D-Bus send failed");
+}
+
+static DBusMessage *le_read_host_suggested_default_data_length(
+ DBusConnection *conn, DBusMessage *msg,
+ void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ struct le_data_length_read_request *read_request;
+ struct le_data_length_read_default_data_length_handler *handler;
+
+ if (find_read_le_data_length_request(adapter))
+ return btd_error_in_progress(msg);
+
+ if (btd_adapter_le_read_suggested_default_data_length(adapter))
+ return btd_error_failed(msg, "Unable to read host suggested def data length");
+
+ read_request = g_new(struct le_data_length_read_request, 1);
+
+ read_request->msg = dbus_message_ref(msg);
+ read_request->adapter = adapter;
+
+ read_requests = g_slist_append(read_requests, read_request);
+
+ handler = g_new0(struct le_data_length_read_default_data_length_handler, 1);
+
+ handler->read_callback =
+ (read_host_suggested_default_data_length_cb_t)le_read_host_suggested_default_length_complete;
+
+ read_request->adapter->def_read_handler = handler;
+
+ return NULL;
+}
+
+void le_set_data_length_return_param_complete(
+ uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ if (status != MGMT_STATUS_SUCCESS) {
+ error("le_set_data_length failed: %s (0x%02x)",
+ mgmt_errstr(status), status);
+ }
+
+ return;
+}
+
+int btd_adapter_le_set_data_length(struct btd_adapter *adapter, bdaddr_t *bdaddr,
+ uint16_t max_tx_octets, uint16_t max_tx_time)
+{
+ struct mgmt_cp_le_set_data_length cp;
+
+ memset(&cp, 0, sizeof(cp));
+
+ bacpy(&cp.bdaddr, bdaddr);
+
+ cp.max_tx_octets = max_tx_octets;
+ cp.max_tx_time = max_tx_time;
+
+ if (mgmt_send(adapter->mgmt, MGMT_OP_LE_SET_DATA_LENGTH,
+ adapter->dev_id, sizeof(cp), &cp,
+ le_set_data_length_return_param_complete,
+ adapter, NULL) > 0)
+ return 0;
+
+ return -EIO;
+}
+#endif
+
+static DBusMessage *adapter_set_manufacturer_data(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct btd_adapter *adapter = data;
+ struct mgmt_cp_set_manufacturer_data cp;
+ uint8_t *value;
+ int32_t len = 0;
+
+ DBG("Set manufacturer data");
+
+ if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+ return btd_error_not_ready(msg);
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &value, &len,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ if (len > EIR_MANUFACTURER_DATA_LENGTH_MAX)
+ return btd_error_invalid_args(msg);
+
+ memcpy(&cp, value, len);
+
+ if (mgmt_send(adapter->mgmt, MGMT_OP_SET_MANUFACTURER_DATA,
+ adapter->dev_id, EIR_MANUFACTURER_DATA_LENGTH_MAX,
+ &cp, NULL, NULL, NULL) > 0)
+ return dbus_message_new_method_return(msg);
+
+ return btd_error_failed(msg, "Set manufacturer data failed");
+}
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
+
static DBusMessage *start_discovery(DBusConnection *conn,
DBusMessage *msg, void *user_data)
{
@@ -2105,6 +5642,10 @@ static DBusMessage *start_discovery(DBusConnection *conn,
adapter->discovery_list = g_slist_prepend(adapter->discovery_list,
client);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ adapter->disc_type = BT_DISC_TYPE_BREDR_ONLY;
+#endif
+
/*
* Just trigger the discovery here. In case an already running
* discovery in idle phase exists, it will be restarted right
@@ -2187,7 +5728,9 @@ static bool parse_transport(DBusMessageIter *value, uint8_t *transport)
*transport = SCAN_TYPE_BREDR;
else if (!strcmp(transport_str, "le"))
*transport = SCAN_TYPE_LE;
- else if (strcmp(transport_str, "auto"))
+ else if (!strcmp(transport_str, "auto"))
+ *transport = SCAN_TYPE_DUAL;
+ else
return false;
return true;
@@ -2368,6 +5911,9 @@ static DBusMessage *stop_discovery(DBusConnection *conn,
client = list->data;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ adapter->disc_type = BT_DISC_TYPE_BREDR_ONLY;
+#endif
cp.type = adapter->discovery_type;
/*
@@ -2394,7 +5940,9 @@ static DBusMessage *stop_discovery(DBusConnection *conn,
return dbus_message_new_method_return(msg);
}
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ cp.type = 0x01;
+#endif
mgmt_send(adapter->mgmt, MGMT_OP_STOP_DISCOVERY,
adapter->dev_id, sizeof(cp), &cp,
stop_discovery_complete, adapter, NULL);
@@ -2416,6 +5964,38 @@ static gboolean property_get_address(const GDBusPropertyTable *property,
return TRUE;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static gboolean property_get_le_address(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ DBusMessageIter entry;
+ char addr[18];
+ const char *str = addr;
+ char *type = NULL;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_STRING_AS_STRING, &entry);
+
+ if (adapter->le_static_addr.b[5] != 0) {
+ ba2str(&adapter->le_static_addr, addr);
+ type = g_strdup_printf("%d", BDADDR_LE_RANDOM);
+ } else {
+ ba2str(&adapter->bdaddr, addr);
+ type = g_strdup_printf("%d", BDADDR_LE_PUBLIC);
+ }
+
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &type);
+ g_free((void *)type);
+
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &str);
+
+ dbus_message_iter_close_container(iter, &entry);
+
+ return TRUE;
+}
+#endif
+
static gboolean property_get_name(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *user_data)
{
@@ -2592,6 +6172,7 @@ static void property_set_mode(struct btd_adapter *adapter, uint32_t setting,
len = sizeof(mode);
break;
case MGMT_SETTING_DISCOVERABLE:
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
if (kernel_conn_control) {
if (mode) {
set_mode(adapter, MGMT_OP_SET_CONNECTABLE,
@@ -2603,6 +6184,7 @@ static void property_set_mode(struct btd_adapter *adapter, uint32_t setting,
break;
}
}
+#endif
memset(&cp, 0, sizeof(cp));
cp.val = mode;
@@ -2618,6 +6200,13 @@ static void property_set_mode(struct btd_adapter *adapter, uint32_t setting,
param = &mode;
len = sizeof(mode);
break;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ case MGMT_SETTING_CONNECTABLE:
+ opcode = MGMT_OP_SET_CONNECTABLE;
+ param = &mode;
+ len = sizeof(mode);
+ break;
+#endif
default:
goto failed;
}
@@ -2632,8 +6221,16 @@ static void property_set_mode(struct btd_adapter *adapter, uint32_t setting,
data->adapter = adapter;
data->id = id;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ /*
+ * Use mgmt_send_nowait to avoid dbus timeout in a state of bonding.
+ */
+ 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,
property_set_mode_complete, data, g_free) > 0)
+#endif
return;
g_free(data);
@@ -2815,6 +6412,122 @@ static void iter_append_uuid(gpointer key, gpointer value, gpointer user_data)
dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static gboolean property_get_le_discovering(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ dbus_bool_t discovering = adapter->le_discovering;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &discovering);
+
+ return TRUE;
+}
+
+static gboolean property_get_connectable(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+
+ return property_get_mode(adapter, MGMT_SETTING_CONNECTABLE, iter);
+}
+
+static void property_set_connectable(const GDBusPropertyTable *property,
+ DBusMessageIter *iter,
+ GDBusPendingPropertySet id, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+
+ property_set_mode(adapter, MGMT_SETTING_CONNECTABLE, iter, id);
+}
+
+static gboolean property_get_version(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ const char *str = adapter->version ? : "";
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str);
+
+ return TRUE;
+}
+
+static gboolean property_get_supported_le_features(
+ const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *user_data)
+{
+ const char *str, *val;
+ int value;
+ DBusMessageIter entry;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_STRING_AS_STRING, &entry);
+
+ value = adapter_le_get_max_adv_instance();
+ if (value > 0) {
+ str = g_strdup("adv_inst_max");
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &str);
+
+ val = g_strdup_printf("%d", value);
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &val);
+
+ g_free((void *)str);
+ g_free((void *)val);
+ }
+
+ value = adapter_le_is_supported_offloading();
+ if (value > 0) {
+ str = g_strdup("rpa_offloading");
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &str);
+
+ val = g_strdup_printf("%d", value);
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &val);
+
+ g_free((void *)str);
+ g_free((void *)val);
+ }
+
+ value = adapter_le_get_scan_filter_size();
+#ifdef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+ if (value <= 0)
+ value = SCAN_FILTER_SLOTS_MAX;
+#endif
+ if (value > 0) {
+ str = g_strdup("max_filter");
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &str);
+
+ val = g_strdup_printf("%d", value);
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &val);
+
+ g_free((void *)str);
+ g_free((void *)val);
+ }
+
+ dbus_message_iter_close_container(iter, &entry);
+
+ return TRUE;
+}
+
+static gboolean property_get_ipsp_init_state(
+ const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct btd_adapter *adapter = data;
+ dbus_bool_t ipsp_initialized;
+
+ DBG("property_get_ipsp_init_state called");
+ if (adapter->ipsp_intialized)
+ ipsp_initialized = TRUE;
+ else
+ ipsp_initialized = FALSE;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN,
+ &ipsp_initialized);
+
+ return TRUE;
+}
+#endif
+
static gboolean property_get_uuids(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *user_data)
{
@@ -2883,6 +6596,249 @@ static int device_path_cmp(gconstpointer a, gconstpointer b)
return strcasecmp(dev_path, path);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static DBusMessage *adapter_unpair_device(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ DBG("+");
+ struct btd_adapter *adapter = user_data;
+ struct btd_device *device;
+ const char *path;
+ GSList *list;
+
+ if (dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID) == FALSE)
+ return btd_error_invalid_args(msg);
+
+ list = g_slist_find_custom(adapter->devices, path, device_path_cmp);
+ if (!list)
+ return btd_error_does_not_exist(msg);
+
+ if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+ return btd_error_not_ready(msg);
+
+ device = list->data;
+
+ btd_device_set_temporary(device, TRUE);
+
+ if (!btd_device_is_connected(device)) {
+ btd_adapter_unpair_device(adapter, device);
+ return dbus_message_new_method_return(msg);
+ }
+
+ device_request_disconnect(device, msg);
+
+ DBG("-");
+ return NULL;
+}
+
+static DBusMessage *create_device(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct btd_adapter *adapter = data;
+ const gchar *address;
+ bdaddr_t addr;
+ DBG("+");
+
+ if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &address,
+ DBUS_TYPE_INVALID) == FALSE)
+ return btd_error_invalid_args(msg);
+
+ if (bachk(address) < 0)
+ return btd_error_invalid_args(msg);
+
+ DBG("%s", address);
+
+ str2ba(address, &addr);
+ btd_adapter_get_device(adapter, &addr, BDADDR_BREDR);
+
+ DBG("-");
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *find_device(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct btd_adapter *adapter = data;
+ struct btd_device *device;
+ DBusMessage *reply;
+ const gchar *address;
+ GSList *l;
+ const gchar *dev_path;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &address,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ l = g_slist_find_custom(adapter->devices, address, device_rpa_cmp);
+ if (!l)
+ l = g_slist_find_custom(adapter->devices, address,
+ device_address_cmp);
+ if (!l)
+ return btd_error_does_not_exist(msg);
+
+ device = l->data;
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ dev_path = device_get_path(device);
+
+ dbus_message_append_args(reply,
+ DBUS_TYPE_OBJECT_PATH, &dev_path,
+ DBUS_TYPE_INVALID);
+
+ return reply;
+}
+
+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)
+ return;
+
+ adapter->ipsp_intialized = initialized;
+
+ DBG("Set Ipsp init state for adapter %s", adapter->path);
+
+ g_dbus_emit_property_changed(dbus_conn, adapter->path,
+ ADAPTER_INTERFACE, "IpspInitStateChanged");
+}
+
+static void deinitialize_6lowpan_complete(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ bool initialized = FALSE;
+
+ if (status != MGMT_STATUS_SUCCESS)
+ error("De-Initialize BT 6lowpan failed for hci%u: %s (0x%02x)",
+ adapter->dev_id, mgmt_errstr(status), status);
+ else {
+ adapter_set_ipsp_init_state(adapter, initialized);
+ DBG("De-Initialize BT 6lowpan successfully for hci%u",
+ adapter->dev_id);
+ }
+}
+
+static bool deinitialize_6lowpan(struct btd_adapter *adapter)
+{
+ struct mgmt_cp_enable_6lowpan cp;
+
+ memset(&cp, 0, sizeof(cp));
+
+ cp.enable_6lowpan = DEINIT_6LOWPAN;
+ if (mgmt_send(adapter->mgmt, MGMT_OP_ENABLE_6LOWPAN,
+ adapter->dev_id, sizeof(cp), &cp,
+ deinitialize_6lowpan_complete, adapter, NULL) > 0)
+ return true;
+
+ error("Failed to de-initialize BT 6Lowpan for index %u",
+ adapter->dev_id);
+ return false;
+}
+
+static void initialize_6lowpan_complete(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ bool initialized = TRUE;
+
+ if (status != MGMT_STATUS_SUCCESS)
+ error("Initialize BT 6lowpan failed for hci%u: %s (0x%02x)",
+ adapter->dev_id, mgmt_errstr(status), status);
+ else {
+ adapter_set_ipsp_init_state(adapter, initialized);
+ DBG("Initialize BT 6lowpan successfully for hci%u",
+ adapter->dev_id);
+ }
+}
+
+static bool initialize_6lowpan(struct btd_adapter *adapter)
+{
+ struct mgmt_cp_enable_6lowpan cp;
+
+ memset(&cp, 0, sizeof(cp));
+
+ cp.enable_6lowpan = INIT_6LOWPAN;
+ if (mgmt_send(adapter->mgmt, MGMT_OP_ENABLE_6LOWPAN,
+ adapter->dev_id, sizeof(cp), &cp,
+ initialize_6lowpan_complete, adapter, NULL) > 0)
+ return true;
+
+ error("Failed to initialize BT 6Lowpan for index %u",
+ adapter->dev_id);
+ return false;
+}
+
+static DBusMessage *adapter_initialize_ipsp(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct btd_adapter *adapter = data;
+ dbus_bool_t err;
+
+ DBG("Initialize IPSP");
+
+ if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+ return btd_error_not_ready(msg);
+
+ if (adapter->ipsp_intialized)
+ return btd_error_already_exists(msg);
+
+ /* Enable BT 6lowpan in kernel */
+ err = initialize_6lowpan(adapter);
+
+ if (!err)
+ return btd_error_failed(msg, "Failed to initialize BT 6lowpan");
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *adapter_deinitialize_ipsp(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct btd_adapter *adapter = data;
+ dbus_bool_t err;
+
+ DBG("De-initialize IPSP");
+
+ if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+ return btd_error_not_ready(msg);
+
+ if (!adapter->ipsp_intialized)
+ return btd_error_not_permitted(msg, "IPSP not initialized");
+
+ 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);
+
+ if (!err)
+ return btd_error_failed(msg, "Failed to deinitialize BT 6lowpan");
+
+ return dbus_message_new_method_return(msg);
+}
+#endif
+
static DBusMessage *remove_device(DBusConnection *conn,
DBusMessage *msg, void *user_data)
{
@@ -2922,10 +6878,157 @@ static const GDBusMethodTable adapter_methods[] = {
GDBUS_ARGS({ "properties", "a{sv}" }), NULL,
set_discovery_filter) },
{ GDBUS_METHOD("StopDiscovery", NULL, NULL, stop_discovery) },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ { GDBUS_METHOD("StartCustomDiscovery",
+ GDBUS_ARGS({ "type", "s" }), NULL,
+ adapter_start_custom_discovery) },
+ { GDBUS_METHOD("StartLEDiscovery", NULL, NULL,
+ adapter_start_le_discovery) },
+ { GDBUS_ASYNC_METHOD("StopLEDiscovery", NULL, NULL,
+ adapter_stop_le_discovery) },
+ { GDBUS_METHOD("SetAdvertising",
+ GDBUS_ARGS({ "enable", "b" },
+ { "slot_id", "i" }), NULL,
+ adapter_set_advertising) },
+ { GDBUS_METHOD("SetAdvertisingParameters",
+ GDBUS_ARGS({ "interval_min", "u" },
+ { "interval_max", "u" },
+ { "filter_policy", "u" },
+ { "type", "u" },
+ { "slot_id", "i" }), NULL,
+ adapter_set_advertising_params) },
+ { GDBUS_METHOD("SetAdvertisingData",
+ GDBUS_ARGS({ "value", "ay" },
+ { "slot_id", "i" }), NULL,
+ adapter_set_advertising_data) },
+ { GDBUS_METHOD("SetScanParameters",
+ GDBUS_ARGS({ "type", "u" },
+ { "interval", "u" },
+ { "window", "u" }), NULL,
+ adapter_le_set_scan_params) },
+ { GDBUS_ASYNC_METHOD("scan_filter_param_setup",
+ GDBUS_ARGS({ "client_if", "i" }, { "action", "i" },
+ { "filt_index", "i" }, { "feat_seln", "i"},
+ { "list_logic_type", "i" }, { "filt_logic_type", "i"},
+ { "rssi_high_thres", "i" }, { "rssi_low_thres", "i"},
+ { "dely_mode", "i" }, { "found_timeout", "i"},
+ { "lost_timeout", "i" }, { "found_timeout_cnt", "i"}), NULL,
+ adapter_le_scan_filter_param_setup) },
+ { GDBUS_ASYNC_METHOD("scan_filter_add_remove",
+ GDBUS_ARGS({ "client_if", "i" }, { "action", "i" },
+ { "filt_type", "i" }, { "filt_index", "i"},
+ { "company_id", "i" }, { "company_id_mask", "i"},
+ { "p_uuid", "ay" }, { "p_uuid_mask", "ay" },
+ { "string", "s" }, { "address_type", "u" },
+ /*{ "data_len", "i" },*/ { "p_data", "ay" },
+ /*{ "mask_len", "i" },*/ { "p_mask", "ay" }), NULL,
+ adapter_le_scan_filter_add_remove) },
+ { GDBUS_ASYNC_METHOD("scan_filter_clear",
+ GDBUS_ARGS({ "client_if", "i" }, { "filt_index", "i" }), NULL,
+ adapter_le_scan_filter_clear) },
+ { GDBUS_ASYNC_METHOD("scan_filter_enable",
+ GDBUS_ARGS({ "client_if", "i" }, { "enable", "b" }), NULL,
+ adapter_le_scan_filter_enable) },
+ { GDBUS_METHOD("InitializeIpsp",
+ NULL, NULL,
+ adapter_initialize_ipsp) },
+ { GDBUS_METHOD("DeinitializeIpsp",
+ NULL, NULL,
+ adapter_deinitialize_ipsp) },
+ { GDBUS_METHOD("SetScanRespData",
+ GDBUS_ARGS({ "value", "ay" },
+ { "slot_id", "i" }), NULL,
+ adapter_set_scan_rsp_data) },
+ { GDBUS_METHOD("AddDeviceWhiteList",
+ GDBUS_ARGS({ "address", "s" },
+ { "address_type", "u" }), NULL,
+ adapter_add_device_white_list) },
+ { GDBUS_METHOD("RemoveDeviceWhiteList",
+ GDBUS_ARGS({ "address", "s" },
+ { "address_type", "u" }), NULL,
+ adapter_remove_device_white_list) },
+ { GDBUS_METHOD("ClearDeviceWhiteList",
+ NULL, NULL,
+ adapter_clear_device_white_list) },
+ { GDBUS_METHOD("SetLePrivacy",
+ GDBUS_ARGS({ "enable", "b" }), NULL,
+ adapter_set_le_privacy) },
+ { GDBUS_METHOD("SetLeStaticRandomAddress",
+ GDBUS_ARGS({ "enable", "b" }), NULL,
+ adapter_set_le_static_address) },
+ { GDBUS_ASYNC_METHOD("EnableRssi",
+ GDBUS_ARGS({ "bt_address", "s" },
+ { "link_type", "i" },
+ { "low_th", "i" },
+ { "in_range_th", "i" },
+ { "high_th", "i"}),
+ NULL,
+ adapter_enable_rssi) },
+ { GDBUS_ASYNC_METHOD("GetRssiStrength",
+ GDBUS_ARGS({ "bt_address", "s" }, { "link_type", "i" }),
+ NULL,
+ adapter_get_rssi) },
+ { GDBUS_ASYNC_METHOD("UnpairDevice",
+ GDBUS_ARGS({ "device", "o" }), NULL, adapter_unpair_device) },
+ { GDBUS_METHOD("FindDevice",
+ GDBUS_ARGS({ "address", "s" }),
+ GDBUS_ARGS({ "device", "o" }),
+ find_device) },
+ { GDBUS_METHOD("SetWbsParameters",
+ GDBUS_ARGS({ "role", "s" }, { "bt_address", "s" }),
+ NULL,
+ set_wbs_parameters) },
+ { GDBUS_METHOD("SetNbParameters",
+ GDBUS_ARGS({ "role", "s" }, { "bt_address", "s" }),
+ NULL,
+ set_nb_parameters) },
+ { GDBUS_METHOD("SetManufacturerData",
+ GDBUS_ARGS({ "value", "ay" }), NULL,
+ adapter_set_manufacturer_data) },
+ { GDBUS_ASYNC_METHOD("CreateDevice",
+ GDBUS_ARGS({ "address", "s" }), NULL,
+ create_device) },
+#endif
{ GDBUS_ASYNC_METHOD("RemoveDevice",
GDBUS_ARGS({ "device", "o" }), NULL, remove_device) },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ { GDBUS_ASYNC_METHOD("LEReadMaximumDataLength", NULL,
+ GDBUS_ARGS({"maxTxOctets", "q" }, { "maxTxTime", "q" },
+ {"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" }),
+ le_read_host_suggested_default_data_length)},
+#endif
+ { }
+};
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static const GDBusSignalTable adapter_signals[] = {
+ { GDBUS_SIGNAL("AdvertisingEnabled",
+ GDBUS_ARGS({ "slot_id", "i" },
+ { "enabled", "b"})) },
+ { GDBUS_SIGNAL("RssiEnabled",
+ GDBUS_ARGS({"address","s"},
+ { "link_type", "i" },
+ { "enabled", "b"})) },
+ { GDBUS_SIGNAL("RssiAlert",
+ GDBUS_ARGS({"address","s"},
+ { "link_type", "i" },
+ { "alert_type", "i" },
+ { "rssi_dbm", "i"})) },
+ { GDBUS_SIGNAL("RawRssi",
+ GDBUS_ARGS({"address","s"},
+ { "link_type", "i" },
+ { "rssi_dbm", "i"})) },
+ { GDBUS_SIGNAL("HardwareError", NULL) },
+ { GDBUS_SIGNAL("TxTimeoutError", NULL) },
{ }
};
+#endif
static const GDBusPropertyTable adapter_properties[] = {
{ "Address", "s", property_get_address },
@@ -2941,9 +7044,21 @@ static const GDBusPropertyTable adapter_properties[] = {
{ "PairableTimeout", "u", property_get_pairable_timeout,
property_set_pairable_timeout },
{ "Discovering", "b", property_get_discovering },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ { "LEDiscovering", "b", property_get_le_discovering },
+#endif
{ "UUIDs", "as", property_get_uuids },
{ "Modalias", "s", property_get_modalias, NULL,
property_exists_modalias },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ { "Connectable", "b", property_get_connectable,
+ property_set_connectable },
+ { "Version", "s", property_get_version },
+ { "SupportedLEFeatures", "as", property_get_supported_le_features},
+ { "IpspInitStateChanged", "b", property_get_ipsp_init_state},
+ { "LEAddress", "as", property_get_le_address },
+#endif
+
{ }
};
@@ -3094,8 +7209,17 @@ static struct irk_info *get_irk_info(GKeyFile *key_file, const char *peer,
char *str;
str = g_key_file_get_string(key_file, "IdentityResolvingKey", "Key", NULL);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (!str)
+ return NULL;
+ if (strlen(str) < 32) {
+ g_free(str);
+ return NULL;
+ }
+#else
if (!str || strlen(str) < 32)
goto failed;
+#endif
irk = g_new0(struct irk_info, 1);
@@ -3106,8 +7230,9 @@ static struct irk_info *get_irk_info(GKeyFile *key_file, const char *peer,
str2buf(&str[2], irk->val, sizeof(irk->val));
else
str2buf(&str[0], irk->val, sizeof(irk->val));
-
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
failed:
+#endif
g_free(str);
return irk;
@@ -3216,6 +7341,7 @@ static int load_irk(struct btd_adapter *adapter, uint8_t *irk)
return 0;
}
+#if 0
static void set_privacy_complete(uint8_t status, uint16_t length,
const void *param, void *user_data)
{
@@ -3259,6 +7385,7 @@ static int set_privacy(struct btd_adapter *adapter, uint8_t privacy)
return -1;
}
+#endif
static void load_link_keys_complete(uint8_t status, uint16_t length,
const void *param, void *user_data)
@@ -3611,6 +7738,47 @@ static uint8_t get_le_addr_type(GKeyFile *keyfile)
return addr_type;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static uint8_t get_addr_type(GKeyFile *keyfile)
+{
+ char **techno, **t;
+ char *str;
+ uint8_t bdaddr_type = BDADDR_BREDR;
+ bool le = false;
+
+ /* Load device technology */
+ techno = g_key_file_get_string_list(keyfile, "General",
+ "SupportedTechnologies", NULL, NULL);
+ if (!techno)
+ return 0xff;
+
+ for (t = techno; *t; t++) {
+ if (g_str_equal(*t, "LE"))
+ le = true;
+ }
+
+ if (!le) {
+ bdaddr_type = BDADDR_BREDR;
+ } else {
+ str = g_key_file_get_string(keyfile, "General",
+ "AddressType", NULL);
+
+ if (str && g_str_equal(str, "public"))
+ bdaddr_type = BDADDR_LE_PUBLIC;
+ else if (str && g_str_equal(str, "static"))
+ bdaddr_type = BDADDR_LE_RANDOM;
+ else
+ error("Unknown LE device technology");
+
+ g_free(str);
+ }
+
+ g_strfreev(techno);
+
+ return bdaddr_type;
+}
+#endif
+
static void probe_devices(void *user_data)
{
struct btd_device *device = user_data;
@@ -3646,8 +7814,14 @@ static void load_devices(struct btd_adapter *adapter)
struct btd_device *device;
char filename[PATH_MAX];
GKeyFile *key_file;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ struct link_key_info *key_info = NULL;
+ GSList *list, *ltk_info = NULL;
+ struct device_addr_type addr;
+#else
struct link_key_info *key_info;
GSList *list, *ltk_info;
+#endif
struct irk_info *irk_info;
struct conn_param *param;
uint8_t bdaddr_type;
@@ -3658,6 +7832,18 @@ static void load_devices(struct btd_adapter *adapter)
if (entry->d_type != DT_DIR || bachk(entry->d_name) < 0)
continue;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+{
+ bdaddr_t bdaddr;
+
+ str2ba(entry->d_name, &bdaddr);
+
+ if (!bacmp(&bdaddr, BDADDR_ANY)) {
+ error("No Bluetooth address");
+ continue;
+ }
+}
+#endif
snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info", srcaddr,
entry->d_name);
@@ -3681,9 +7867,25 @@ static void load_devices(struct btd_adapter *adapter)
if (param)
params = g_slist_append(params, param);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ str2ba(entry->d_name, &addr.bdaddr);
+ addr.bdaddr_type = get_addr_type(key_file);
+ if (addr.bdaddr_type == 0xff) {
+ error("No SupportedTechnologies. Skipping");
+ goto free;
+ }
+
+ list = g_slist_find_custom(adapter->devices, &addr,
+ device_addr_type_strict_cmp);
+#else
list = g_slist_find_custom(adapter->devices, entry->d_name,
device_address_cmp);
+#endif
if (list) {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ DBG("Skip already loaded device [%s] [%d]",
+ entry->d_name, addr.bdaddr_type);
+#endif
device = list->data;
goto device_exist;
}
@@ -3693,6 +7895,42 @@ static void load_devices(struct btd_adapter *adapter)
if (!device)
goto free;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+{
+ char idaddr[18];
+
+ /*
+ * After loading IRK information from file,
+ * store it into device->bdaddr.
+ * RPA is stored in device->rpa_addr
+ */
+ ba2str(device_get_address(device), idaddr);
+
+ DBG("irk address: %s, rpa_exist %d",
+ idaddr, device_get_rpa_exist(device));
+
+ if (device_get_rpa_exist(device) == true) {
+ if (key_info)
+ str2ba(idaddr, &key_info->bdaddr);
+
+ if (ltk_info) {
+ ltks = g_slist_remove(ltks, ltk_info);
+ ltk_info = get_ltk_info(key_file,
+ idaddr, bdaddr_type);
+ ltks = g_slist_concat(ltks, ltk_info);
+ }
+
+ if (irk_info) {
+ str2ba(idaddr, &irk_info->bdaddr);
+ device_set_irk_value(device, irk_info->val);
+ }
+
+ if (param)
+ str2ba(idaddr, &param->bdaddr);
+ }
+}
+#endif
+
btd_device_set_temporary(device, false);
adapter->devices = g_slist_append(adapter->devices, device);
@@ -4276,8 +8514,30 @@ void adapter_auto_connect_remove(struct btd_adapter *adapter,
static void adapter_start(struct btd_adapter *adapter)
{
+#if defined(TIZEN_FEATURE_BLUEZ_MODIFY) && !defined(__SPRD_PATCH__)
+ if (adapter_le_read_ble_feature_info())
+ g_dbus_emit_property_changed(dbus_conn, adapter->path,
+ ADAPTER_INTERFACE, "SupportedLEFeatures");
+#ifdef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+ else
+ g_dbus_emit_property_changed(dbus_conn, adapter->path,
+ ADAPTER_INTERFACE, "SupportedLEFeatures");
+#endif
+
+ adapter_get_adv_tx_power(adapter);
+
+ /* By default enable offloading for testing, this should be modified */
+ if (adapter_le_is_supported_offloading())
+ adapter_le_enable_offloading(TRUE);
+#endif
+
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
g_dbus_emit_property_changed(dbus_conn, adapter->path,
ADAPTER_INTERFACE, "Powered");
+#else
+ g_dbus_emit_property_changed_full(dbus_conn, adapter->path,
+ ADAPTER_INTERFACE, "Powered", 1);
+#endif
DBG("adapter %s has been enabled", adapter->path);
@@ -5234,7 +9494,9 @@ static void load_config(struct btd_adapter *adapter)
char address[18];
struct stat st;
GError *gerr = NULL;
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ char *str;
+#endif
ba2str(&adapter->bdaddr, address);
key_file = g_key_file_new();
@@ -5284,12 +9546,35 @@ static void load_config(struct btd_adapter *adapter)
gerr = NULL;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ /* Get A2DP Role */
+ str = g_key_file_get_string(key_file, "General", "DefaultA2DPRole", &gerr);
+ if (gerr || !str) {
+ adapter->a2dp_role = BLUETOOTH_A2DP_SOURCE_ROLE;
+ g_error_free(gerr);
+ gerr = NULL;
+ } else {
+ if (g_strcmp0(str, "sink") == 0)
+ adapter->a2dp_role = BLUETOOTH_A2DP_SINK_ROLE;
+ else if (g_strcmp0(str, "source") == 0)
+ adapter->a2dp_role = BLUETOOTH_A2DP_SOURCE_ROLE;
+ }
+#endif
+
g_key_file_free(key_file);
}
static struct btd_adapter *btd_adapter_new(uint16_t index)
{
struct btd_adapter *adapter;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#ifdef TIZEN_FEATURE_BLUEZ_BATTERY_WATCH
+ DBusConnection *conn = btd_get_dbus_connection();
+ DBusMessage *msg = NULL;
+ DBusMessage *reply = NULL;
+ int charging_state = 0;
+#endif /* TIZEN_FEATURE_BLUEZ_BATTERY_WATCH */
+#endif
adapter = g_try_new0(struct btd_adapter, 1);
if (!adapter)
@@ -5316,6 +9601,9 @@ static struct btd_adapter *btd_adapter_new(uint16_t index)
main_opts.did_version);
adapter->discoverable_timeout = main_opts.discovto;
adapter->pairable_timeout = main_opts.pairto;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ adapter->advertising = FALSE;
+#endif
DBG("System name: %s", adapter->system_name);
DBG("Major class: %u", adapter->major_class);
@@ -5323,19 +9611,67 @@ static struct btd_adapter *btd_adapter_new(uint16_t index)
DBG("Modalias: %s", adapter->modalias);
DBG("Discoverable timeout: %u seconds", adapter->discoverable_timeout);
DBG("Pairable timeout: %u seconds", adapter->pairable_timeout);
-
adapter->auths = g_queue_new();
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#ifdef TIZEN_FEATURE_BLUEZ_BATTERY_WATCH
+ adapter->charging_watch = g_dbus_add_signal_watch(conn, DEVICED_DEST,
+ DEVICED_BATT_OBJECT_PATH,
+ DEVICED_BATT_INTERFACE, "ChargerType",
+ charging_state_changed, adapter, NULL);
+ if (adapter->charging_watch == 0)
+ error("Cannot add signal watch for ChargerType");
+
+ msg = dbus_message_new_method_call(DEVICED_DEST,
+ DEVICED_BATT_OBJECT_PATH,
+ DEVICED_BATT_INTERFACE, "ChargerType");
+ if (msg) {
+ reply = dbus_connection_send_with_reply_and_block(conn,
+ msg, 1000, NULL);
+ if (reply) {
+ if (dbus_message_get_args(reply, NULL,
+ DBUS_TYPE_INT32, &charging_state,
+ DBUS_TYPE_INVALID) == TRUE) {
+ set_charging_state(adapter, charging_state);
+ }
+ dbus_message_unref(reply);
+ } else {
+ error("Reply is NULL");
+ }
+ dbus_message_unref(msg);
+ } else {
+ error("Unable to create dbus message for charging state");
+ }
+#endif /* TIZEN_FEATURE_BLUEZ_BATTERY_WATCH */
+#endif
+
return btd_adapter_ref(adapter);
}
static void adapter_remove(struct btd_adapter *adapter)
{
GSList *l;
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
struct gatt_db *db;
+#endif
DBG("Removing adapter %s", adapter->path);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#ifdef TIZEN_FEATURE_BLUEZ_BATTERY_WATCH
+ if (adapter->charging_watch > 0) {
+ g_dbus_remove_watch(btd_get_dbus_connection(),
+ adapter->charging_watch);
+ adapter->charging_watch = 0;
+ }
+
+ if (adapter->charging_timeout) {
+ g_source_remove(adapter->charging_timeout);
+ adapter->charging_timeout = 0;
+ }
+#endif /* TIZEN_FEATURE_BLUEZ_BATTERY_WATCH */
+#endif
+
if (adapter->discovery_idle_timeout > 0) {
g_source_remove(adapter->discovery_idle_timeout);
adapter->discovery_idle_timeout = 0;
@@ -5359,12 +9695,16 @@ static void adapter_remove(struct btd_adapter *adapter)
unload_drivers(adapter);
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
db = btd_gatt_database_get_db(adapter->database);
gatt_db_unregister(db, adapter->db_id);
adapter->db_id = 0;
btd_gatt_database_destroy(adapter->database);
adapter->database = NULL;
+#else
+ btd_adapter_gatt_server_stop(adapter);
+#endif
btd_advertising_manager_destroy(adapter->adv_manager);
adapter->adv_manager = NULL;
@@ -5389,6 +9729,24 @@ const bdaddr_t *btd_adapter_get_address(struct btd_adapter *adapter)
return &adapter->bdaddr;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+const bdaddr_t *btd_adapter_get_le_address(struct btd_adapter *adapter)
+{
+ if (adapter->le_static_addr.b[5] != 0)
+ return &adapter->le_static_addr;
+ else
+ return &adapter->bdaddr;
+}
+
+uint8_t btd_adapter_get_le_address_type(struct btd_adapter * adapter)
+{
+ if (adapter->le_static_addr.b[5] != 0)
+ return BDADDR_LE_RANDOM;
+ else
+ return BDADDR_LE_PUBLIC;
+}
+#endif
+
static gboolean confirm_name_timeout(gpointer user_data)
{
struct btd_adapter *adapter = user_data;
@@ -5554,28 +9912,53 @@ static bool is_filter_match(GSList *discovery_filter, struct eir_data *eir_data,
return got_match;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void update_found_devices(struct btd_adapter *adapter,
+ const bdaddr_t *bdaddr,
+ uint8_t bdaddr_type, int8_t rssi,
+ bool confirm, bool legacy, uint8_t adv_type,
+ const uint8_t *data, uint8_t data_len)
+#else
static void update_found_devices(struct btd_adapter *adapter,
const bdaddr_t *bdaddr,
uint8_t bdaddr_type, int8_t rssi,
bool confirm, bool legacy,
bool not_connectable,
const uint8_t *data, uint8_t data_len)
+#endif
{
struct btd_device *dev;
struct eir_data eir_data;
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
bool name_known, discoverable;
+#else
+ bool name_known;
+#endif
+#ifdef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+ uint8_t allow_report;
+#endif
char addr[18];
memset(&eir_data, 0, sizeof(eir_data));
eir_parse(&eir_data, data, data_len);
-
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
if (bdaddr_type == BDADDR_BREDR || adapter->filtered_discovery)
discoverable = true;
else
discoverable = eir_data.flags & (EIR_LIM_DISC | EIR_GEN_DISC);
-
+#endif
ba2str(bdaddr, addr);
+#ifdef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+ /* Check if the any filter policy */
+ allow_report = validate_for_filter_policy(adapter, &eir_data, addr);
+ if (allow_report == NONE_REPORT &&
+ ((adapter->scan_type == LE_ACTIVE_SCAN && adv_type == ADV_TYPE_SCAN_RESPONSE) ||
+ adapter->scan_type == LE_PASSIVE_SCAN)) {
+ eir_data_free(&eir_data);
+ return;
+ }
+#endif
dev = btd_adapter_find_device(adapter, bdaddr, bdaddr_type);
if (!dev) {
/*
@@ -5583,10 +9966,21 @@ static void update_found_devices(struct btd_adapter *adapter,
* not marked as discoverable, then do not create new
* device objects.
*/
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ /*DBG("List BREDR:%p LE:%p Discoverable:%d", adapter->discovery_list,
+ adapter->le_discovery_list, discoverable);*/
+ if ((adapter->discovery_list == NULL &&
+ adapter->le_discovery_list == NULL)) {
+ DBG("discovery list is NULL");
+ eir_data_free(&eir_data);
+ return;
+ }
+#else
if (!adapter->discovery_list || !discoverable) {
eir_data_free(&eir_data);
return;
}
+#endif
dev = adapter_create_device(adapter, bdaddr, bdaddr_type);
}
@@ -5598,6 +9992,11 @@ static void update_found_devices(struct btd_adapter *adapter,
return;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if(device_get_rpa_exist(dev) == true)
+ bdaddr_type = BDADDR_LE_RANDOM;
+#endif
+
device_update_last_seen(dev, bdaddr_type);
/*
@@ -5620,11 +10019,26 @@ static void update_found_devices(struct btd_adapter *adapter,
* If no client has requested discovery, then only update
* already paired devices (skip temporary ones).
*/
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (device_is_temporary(dev) && adapter->discovery_list == NULL &&
+ adapter->le_discovery_list == NULL) {
+ DBG("discovery list is NULL");
+ eir_data_free(&eir_data);
+ return;
+ }
+
+ device_set_last_addr_type(dev, bdaddr_type);
+ device_set_ipsp_connected(dev, FALSE, NULL);
+#else
if (device_is_temporary(dev) && !adapter->discovery_list) {
eir_data_free(&eir_data);
return;
}
+#endif
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (bdaddr_type == BDADDR_BREDR) {
+#endif
if (adapter->filtered_discovery &&
!is_filter_match(adapter->discovery_list, &eir_data, rssi)) {
eir_data_free(&eir_data);
@@ -5640,13 +10054,23 @@ static void update_found_devices(struct btd_adapter *adapter,
if (eir_data.tx_power != 127)
device_set_tx_power(dev, eir_data.tx_power);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ }
+#endif
if (eir_data.appearance != 0)
device_set_appearance(dev, eir_data.appearance);
/* 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. */
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ 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);
@@ -5663,6 +10087,19 @@ static void update_found_devices(struct btd_adapter *adapter,
device_add_eir_uuids(dev, eir_data.services);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (eir_data.flags != 0)
+ device_set_remote_feature_flag(dev, eir_data.flags);
+
+ if (bdaddr_type == BDADDR_BREDR)
+ device_set_manufacturer_info(dev, &eir_data);
+ else {
+ /* if the application has registered for iBeacon report,
+ * then send ibeacon report along with advertisement report */
+ device_set_adv_report_info(dev, (void*)data, data_len, adv_type, rssi);
+ }
+#endif
+
if (eir_data.msd_list) {
device_set_manufacturer_data(dev, eir_data.msd_list);
adapter_msd_notify(adapter, dev, eir_data.msd_list);
@@ -5682,7 +10119,11 @@ static void update_found_devices(struct btd_adapter *adapter,
* Otherwise, this is an event from passive discovery and we
* should check if the device needs connecting to.
*/
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (!adapter->discovery_list && !adapter->le_discovery_list)
+#else
if (!adapter->discovery_list)
+#endif
goto connect_le;
if (g_slist_find(adapter->discovery_found, dev))
@@ -5697,9 +10138,11 @@ static void update_found_devices(struct btd_adapter *adapter,
return;
connect_le:
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
/* Ignore non-connectable events */
if (not_connectable)
return;
+#endif
/*
* If we're in the process of stopping passive scanning and
@@ -5768,12 +10211,66 @@ static void device_found_callback(uint16_t index, uint16_t length,
confirm_name = (flags & MGMT_DEV_FOUND_CONFIRM_NAME);
legacy = (flags & MGMT_DEV_FOUND_LEGACY_PAIRING);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ update_found_devices(adapter, &ev->addr.bdaddr, ev->addr.type,
+ ev->rssi, confirm_name, legacy, 0,
+ eir, eir_len);
+#else
update_found_devices(adapter, &ev->addr.bdaddr, ev->addr.type,
ev->rssi, confirm_name, legacy,
flags & MGMT_DEV_FOUND_NOT_CONNECTABLE,
eir, eir_len);
+#endif
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void le_device_found_callback(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_le_device_found *ev = param;
+ struct btd_adapter *adapter = user_data;
+ const uint8_t *eir;
+ uint16_t eir_len;
+ uint32_t flags;
+ bool confirm_name;
+ bool legacy;
+ char addr[18];
+
+ if (length < sizeof(*ev)) {
+ error("Too short device found event (%u bytes)", length);
+ return;
+ }
+
+ eir_len = btohs(ev->eir_len);
+ if (length != sizeof(*ev) + eir_len) {
+ error("Device found event size mismatch (%u != %zu)",
+ length, sizeof(*ev) + eir_len);
+ return;
+ }
+
+ if (eir_len == 0)
+ eir = NULL;
+ else
+ eir = ev->eir;
+
+ flags = btohl(ev->flags);
+
+ ba2str(&ev->addr.bdaddr, addr);
+ /*DBG("hci%u addr %s, rssi %d flags 0x%04x eir_len %u",
+ index, addr, ev->rssi, flags, eir_len);*/
+
+ confirm_name = (flags & MGMT_DEV_FOUND_CONFIRM_NAME);
+ legacy = (flags & MGMT_DEV_FOUND_LEGACY_PAIRING);
+
+ /*DBG("hci%u addr %s, addr_type %d rssi %d flags 0x%04x eir_len %u confirm_name %d legacy %d, adv_type %02x",
+ index, addr, ev->addr.type, ev->rssi, flags, eir_len, confirm_name, legacy, ev->adv_type);*/
+
+ update_found_devices(adapter, &ev->addr.bdaddr, ev->addr.type,
+ ev->rssi, confirm_name, legacy, ev->adv_type,
+ eir, eir_len);
+}
+#endif
+
struct agent *adapter_get_agent(struct btd_adapter *adapter)
{
return agent_get(NULL);
@@ -5792,7 +10289,11 @@ static void adapter_remove_connection(struct btd_adapter *adapter,
device_remove_connection(device, bdaddr_type);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (device_is_authenticating(device, bdaddr_type))
+#else
if (device_is_authenticating(device))
+#endif
device_cancel_authentication(device, TRUE);
/* If another bearer is still connected */
@@ -5800,12 +10301,31 @@ static void adapter_remove_connection(struct btd_adapter *adapter,
return;
adapter->connections = g_slist_remove(adapter->connections, device);
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ DBG("isPaired %d, isBonded %d", device_is_paired(device, bdaddr_type),
+ device_is_bonded(device, bdaddr_type));
+ if ((device_is_temporary(device) && !device_is_retrying(device)) ||
+ (!device_is_bonded(device, bdaddr_type))) {
+#else
if (device_is_temporary(device) && !device_is_retrying(device)) {
+#endif
const char *path = device_get_path(device);
DBG("Removing temporary device %s", path);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ /* device_is_paired is added incase of tempoary bonded
+ * oop file transfer in that device is not bonded it's paired.
+ */
+ if (!(device_is_bonded(device, bdaddr_type) ||
+ device_is_paired(device, bdaddr_type))) {
+ DBG("addr type %d, bonded", bdaddr_type);
+ return;
+ }
+
+ btd_adapter_unpair_device(adapter, device);
+#else
btd_adapter_remove_device(adapter, device);
+#endif
}
}
@@ -5858,6 +10378,10 @@ static void adapter_stop(struct btd_adapter *adapter)
g_dbus_emit_property_changed(dbus_conn, adapter->path,
ADAPTER_INTERFACE, "Discovering");
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ g_dbus_emit_property_changed(dbus_conn, adapter->path,
+ ADAPTER_INTERFACE, "LEDiscovering");
+#endif
if (adapter->dev_class) {
/* the kernel should reset the class of device when powering
@@ -5867,6 +10391,9 @@ static void adapter_stop(struct btd_adapter *adapter)
ADAPTER_INTERFACE, "Class");
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ advertiser_cleanup(adapter);
+#endif
g_dbus_emit_property_changed(dbus_conn, adapter->path,
ADAPTER_INTERFACE, "Powered");
@@ -5949,14 +10476,31 @@ static gboolean process_auth_queue(gpointer user_data)
const char *dev_path;
/* Wait services to be resolved before asking authorization */
- if (auth->svc_id > 0)
+ if (auth->svc_id > 0) {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ DBG("Wait services to be resolved before asking authorization");
+#endif
return FALSE;
+ }
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
if (device_is_trusted(device) == TRUE) {
+#else
+ if (device_is_trusted(device) == TRUE ||
+ device_is_profile_trusted(device, auth->uuid)) {
+#endif
auth->cb(NULL, auth->user_data);
goto next;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ /* If Profile is Blocked, Simply reject Authorization*/
+ if (device_is_profile_blocked(device, auth->uuid) == TRUE) {
+ auth->cb(&err, auth->user_data);
+ goto next;
+ }
+#endif
+
/* If agent is set authorization is already ongoing */
if (auth->agent)
return FALSE;
@@ -6302,6 +10846,9 @@ static void user_confirm_request_callback(uint16_t index, uint16_t length,
return;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ device_set_auth_addr_type(device, ev->addr.type);
+#endif
err = device_confirm_passkey(device, ev->addr.type, btohl(ev->value),
ev->confirm_hint);
if (err < 0) {
@@ -6376,6 +10923,9 @@ static void user_passkey_request_callback(uint16_t index, uint16_t length,
return;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ device_set_auth_addr_type(device, ev->addr.type);
+#endif
err = device_request_passkey(device, ev->addr.type);
if (err < 0) {
btd_error(adapter->dev_id,
@@ -6422,6 +10972,348 @@ static void user_passkey_notify_callback(uint16_t index, uint16_t length,
"device_notify_passkey: %s", strerror(-err));
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void rssi_alert_callback(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_vendor_specific_rssi_alert *ev = param;
+ struct btd_adapter *adapter = user_data;
+ char addr[18];
+ char *bt_addr = NULL;
+ int link_type = -1;
+ int alert_type = -1;
+ int rssi_dbm = 0;
+
+ if (length < sizeof(*ev)) {
+ error("Too small rssi alert event");
+ return;
+ }
+
+ ba2str(&ev->bdaddr, addr);
+ DBG("hci%u %s %d", index, addr, ev->link_type);
+ DBG("RSSI Alert Params [%d %d]", ev->alert_type, ev->rssi_dbm);
+
+ bt_addr = (char *)&addr;
+ link_type = ev->link_type;
+ alert_type = ev->alert_type;
+ rssi_dbm = ev->rssi_dbm;
+ g_dbus_emit_signal(dbus_conn, adapter->path,
+ ADAPTER_INTERFACE, "RssiAlert",
+ DBUS_TYPE_STRING, &bt_addr,
+ DBUS_TYPE_INT32, &link_type,
+ DBUS_TYPE_INT32, &alert_type,
+ DBUS_TYPE_INT32, &rssi_dbm,
+ DBUS_TYPE_INVALID);
+}
+
+static void get_raw_rssi_callback(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
+{
+ const struct mgmt_cc_rp_get_raw_rssi *ev = param;
+ struct btd_adapter *adapter = user_data;
+ char addr[18];
+ char *bt_addr = NULL;
+ int link_type = -1;
+ int rssi_dbm = 0;
+
+ if (length < sizeof(*ev)) {
+ error("Too small raw RSSI event");
+ return;
+ }
+
+ ba2str(&ev->bt_address, addr);
+ DBG("hci%u %s", index, addr);
+ DBG("Raw RSSI Params [%d %d]", ev->link_type, ev->rssi_dbm);
+
+ bt_addr = (char *)&addr;
+ link_type = ev->link_type;
+ rssi_dbm = ev->rssi_dbm;
+
+ g_dbus_emit_signal(dbus_conn, adapter->path,
+ ADAPTER_INTERFACE, "RawRssi",
+ DBUS_TYPE_STRING, &bt_addr,
+ DBUS_TYPE_INT32, &link_type,
+ DBUS_TYPE_INT32, &rssi_dbm,
+ DBUS_TYPE_INVALID);
+}
+
+static void rssi_enabled_callback(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
+{
+ const struct mgmt_cc_rsp_enable_rssi *ev = param;
+ struct btd_adapter *adapter = user_data;
+ char addr[18];
+ char *bt_addr = NULL;
+ int enabled = TRUE;
+ int link_type = -1;
+
+ if (length < sizeof(*ev)) {
+ error("Too small rssi enabled event");
+ return;
+ }
+
+ ba2str(&ev->bt_address, addr);
+ DBG("hci%u %s %d", index, addr, ev->link_type);
+ DBG("RSSI Enabled [%d %d]", ev->le_ext_opcode, ev->status);
+
+ bt_addr = (char *)&addr;
+ link_type = ev->link_type;
+
+ g_dbus_emit_signal(dbus_conn, adapter->path,
+ ADAPTER_INTERFACE, "RssiEnabled",
+ DBUS_TYPE_STRING, &bt_addr,
+ DBUS_TYPE_INT32, &link_type,
+ DBUS_TYPE_BOOLEAN, &enabled,
+ DBUS_TYPE_INVALID);
+}
+
+static void rssi_disabled_callback(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
+{
+ const struct mgmt_cc_rp_disable_rssi *ev = param;
+ struct btd_adapter *adapter = user_data;
+ char addr[18];
+ char *bt_addr = NULL;
+ int disabled = FALSE;
+ int link_type = -1;
+
+ if (length < sizeof(*ev)) {
+ error("Too small RSSI disabled event");
+ return;
+ }
+
+ ba2str(&ev->bt_address, addr);
+ DBG("hci%u %s %d", index, addr, ev->link_type);
+ DBG("RSSI Disabled Params [%d %d]", ev->le_ext_opcode, ev->status);
+
+ bt_addr = (char *)&addr;
+ link_type = ev->link_type;
+
+ g_dbus_emit_signal(dbus_conn, adapter->path,
+ ADAPTER_INTERFACE, "RssiEnabled",
+ DBUS_TYPE_STRING, &bt_addr,
+ DBUS_TYPE_INT32, &link_type,
+ DBUS_TYPE_BOOLEAN, &disabled,
+ DBUS_TYPE_INVALID);
+}
+
+void adapter_check_version(struct btd_adapter *adapter, uint8_t hci_ver)
+{
+ char *ver;
+
+ switch (hci_ver) {
+ case 0:
+ ver = "Bluetooth 1.0b";
+ break;
+ case 1:
+ ver = "Bluetooth 1.1";
+ break;
+ case 2:
+ ver = "Bluetooth 1.2";
+ break;
+ case 3:
+ ver = "Bluetooth 2.0 + EDR";
+ break;
+ case 4:
+ ver = "Bluetooth 2.1 + EDR";
+ break;
+ case 5:
+ ver = "Bluetooth 3.0 + HS";
+ break;
+ case 6:
+ ver = "Bluetooth 4.0";
+ break;
+ case 7:
+ ver = "Bluetooth 4.1";
+ break;
+ default:
+ ver = "Unknown";
+ break;
+ }
+
+ if (adapter->version)
+ g_free(adapter->version);
+
+ adapter->version = g_strdup(ver);
+}
+
+static void hardware_error_callback(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_hardware_error *ev = param;
+ struct btd_adapter *adapter = user_data;
+
+ if (length < sizeof(*ev)) {
+ error("Too small Hardware error event");
+ return;
+ }
+
+ error("Hardware error occurred : %d", ev->error_code);
+ g_dbus_emit_signal(dbus_conn, adapter->path,
+ ADAPTER_INTERFACE, "HardwareError",
+ DBUS_TYPE_INVALID);
+}
+
+static void tx_timeout_error_callback(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+
+ error("Tx Timeout error occurred");
+ g_dbus_emit_signal(dbus_conn, adapter->path,
+ ADAPTER_INTERFACE, "TxTimeoutError",
+ DBUS_TYPE_INVALID);
+}
+
+static void device_name_update_callback(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_device_name_update *ev = param;
+ struct btd_adapter *adapter = user_data;
+ struct btd_device *device;
+ char addr[18];
+ const uint8_t *eir_name;
+ struct eir_data eir_data;
+
+ if (length < sizeof(*ev)) {
+ error("Name update error event");
+ return;
+ }
+
+ ba2str(&ev->addr.bdaddr, addr);
+ DBG("hci%u %s", index, addr);
+
+ device = btd_adapter_get_device(adapter, &ev->addr.bdaddr,
+ ev->addr.type);
+ if (!device) {
+ error("Unable to get device object for %s", addr);
+ return;
+ }
+
+ if (ev->eir_len == 0)
+ return;
+
+ eir_name = ev->eir;
+
+ memset(&eir_data, 0, sizeof(eir_data));
+ eir_parse(&eir_data, eir_name, ev->eir_len);
+
+ if (eir_data.name)
+ btd_device_device_set_name(device, eir_data.name);
+
+ eir_data_free(&eir_data);
+}
+
+static void multi_adv_state_change_callback(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_vendor_specific_multi_adv_state_changed *ev = param;
+ struct btd_adapter *adapter = user_data;
+
+ if (length < sizeof(*ev)) {
+ error("Too small adv state change event");
+ return;
+ }
+
+ DBG("adv id %d, state change reason %d, connection_handle %x",
+ ev->adv_instance, ev->state_change_reason, ev->connection_handle);
+
+ if ((ev->adv_instance > 0 && ev->adv_instance < adapter_le_get_max_adv_instance()) &&
+ ev->state_change_reason == 0)
+ adapter_le_enable_multi_adv(adapter, TRUE, ev->adv_instance);
+}
+
+static void le_conn_update_completed_callback(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_conn_updated *ev = param;
+ struct btd_adapter *adapter = user_data;
+ struct btd_device *device;
+ char addr[18];
+ GSList *list;
+
+ if (length < sizeof(*ev)) {
+ error("Too small le conn update completed event");
+ return;
+ }
+
+ ba2str(&ev->addr.bdaddr, addr);
+ list = g_slist_find_custom(adapter->devices, addr,
+ device_address_cmp);
+ if (list) {
+ device = list->data;
+ if (device_get_conn_update_state(device))
+ device_set_conn_update_state(device, false);
+ }
+}
+
+static void bt_6lowpan_conn_state_change_callback(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_6lowpan_conn_state_changed *ev = param;
+ struct btd_adapter *adapter = user_data;
+ struct btd_device *device;
+ char addr[18];
+ gboolean connected = 0;
+
+ if (length < sizeof(*ev)) {
+ btd_error(adapter->dev_id,
+ "Too small device connected event");
+ return;
+ }
+
+ ba2str(&ev->addr.bdaddr, addr);
+
+ DBG("hci%u device %s", index, addr);
+
+ device = btd_adapter_find_device(adapter, &ev->addr.bdaddr,
+ ev->addr.type);
+ if (!device) {
+ btd_error(adapter->dev_id,
+ "Unable to get device object for %s", addr);
+ return;
+ }
+
+ if (ev->connected)
+ connected = TRUE;
+ else
+ connected = FALSE;
+
+ device_set_ipsp_connected(device, connected, ev->ifname);
+}
+
+static void bt_le_data_length_changed_callback(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_le_data_length_changed *ev = param;
+ struct btd_adapter *adapter = user_data;
+ struct btd_device *device;
+ char addr[18];
+
+ if (length < sizeof(*ev)) {
+ btd_error(adapter->dev_id,
+ "Too small data length changed event");
+ return;
+ }
+
+ ba2str(&ev->addr.bdaddr, addr);
+
+ DBG("hci%u device %s", index, addr);
+
+ device = btd_adapter_find_device(adapter, &ev->addr.bdaddr,
+ ev->addr.type);
+ if (!device) {
+ btd_error(adapter->dev_id,
+ "Unable to get device object for %s", addr);
+ return;
+ }
+
+ 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(
struct btd_adapter *adapter)
{
@@ -6499,6 +11391,9 @@ static void pin_code_request_callback(uint16_t index, uint16_t length,
/* Flag the request of a pincode to allow a bonding retry. */
adapter->pincode_requested = true;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ btd_device_set_legacy_pairing(device, true);
+#endif
memset(pin, 0, sizeof(pin));
@@ -6586,8 +11481,9 @@ static void bonding_complete(struct btd_adapter *adapter,
if (device != NULL)
device_bonding_complete(device, addr_type, status);
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
resume_discovery(adapter);
-
+#endif
check_oob_bonding_complete(adapter, bdaddr, status);
}
@@ -6704,8 +11600,9 @@ int adapter_create_bonding(struct btd_adapter *adapter, const bdaddr_t *bdaddr,
return -EBUSY;
}
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
suspend_discovery(adapter);
-
+#endif
return adapter_bonding_attempt(adapter, bdaddr, addr_type, io_cap);
}
@@ -6775,16 +11672,38 @@ static void dev_disconnected(struct btd_adapter *adapter,
{
struct btd_device *device;
char dst[18];
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ struct device_addr_type t_addr;
+#endif
ba2str(&addr->bdaddr, dst);
DBG("Device %s disconnected, reason %u", dst, reason);
device = btd_adapter_find_device(adapter, &addr->bdaddr, addr->type);
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (device) {
+ device_get_tizen_addr(device, addr->type, &t_addr);
+
+ device_set_disconnect_reason(device, reason);
+ adapter_remove_connection(adapter, device, t_addr.bdaddr_type);
+ disconnect_notify(device, reason);
+ if (device_is_bonded(device, t_addr.bdaddr_type)) {
+ DBG("addr type %d, bonded", t_addr.bdaddr_type);
+ return;
+ }
+
+ bonding_attempt_complete(adapter, &t_addr.bdaddr,
+ t_addr.bdaddr_type, MGMT_STATUS_DISCONNECTED);
+ return;
+ }
+#else
if (device) {
adapter_remove_connection(adapter, device, addr->type);
disconnect_notify(device, reason);
}
+#endif
bonding_attempt_complete(adapter, &addr->bdaddr, addr->type,
MGMT_STATUS_DISCONNECTED);
@@ -6822,7 +11741,12 @@ static void disconnect_complete(uint8_t status, uint16_t length,
return;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ /* Use HCI error code instead of MGMT disconnection reason */
+ dev_disconnected(adapter, &rp->addr, 0x16);
+#else
dev_disconnected(adapter, &rp->addr, MGMT_DEV_DISCONN_LOCAL_HOST);
+#endif
}
int btd_adapter_disconnect_device(struct btd_adapter *adapter,
@@ -6938,7 +11862,16 @@ static void new_link_key_callback(uint16_t index, uint16_t length,
key->pin_len);
device_set_bonded(device, BDADDR_BREDR);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ } else {
+ if (btd_adapter_get_a2dp_role(adapter) == BLUETOOTH_A2DP_SINK_ROLE) {
+ DBG("store_hint %d", ev->store_hint);
+ btd_device_set_temporary(device, false);
+ }
}
+#else
+ }
+#endif
bonding_complete(adapter, &addr->bdaddr, addr->type, 0);
}
@@ -7005,6 +11938,9 @@ static void new_long_term_key_callback(uint16_t index, uint16_t length,
struct btd_device *device;
bool persistent;
char dst[18];
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ struct device_addr_type t_addr;
+#endif
if (length < sizeof(*ev)) {
btd_error(adapter->dev_id, "Too small long term key event");
@@ -7049,9 +11985,16 @@ static void new_long_term_key_callback(uint16_t index, uint16_t length,
ediv = le16_to_cpu(key->ediv);
rand = le64_to_cpu(key->rand);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ device_get_tizen_addr(device, addr->type, &t_addr);
+ store_longtermkey(bdaddr, &t_addr.bdaddr, t_addr.bdaddr_type,
+ key->val, key->master, key->type,
+ key->enc_size, ediv, rand);
+#else
store_longtermkey(bdaddr, &key->addr.bdaddr,
key->addr.type, key->val, key->master,
key->type, key->enc_size, ediv, rand);
+#endif
device_set_bonded(device, addr->type);
}
@@ -7131,6 +12074,9 @@ static void new_csrk_callback(uint16_t index, uint16_t length,
const bdaddr_t *bdaddr = btd_adapter_get_address(adapter);
struct btd_device *device;
char dst[18];
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ struct device_addr_type t_addr;
+#endif
if (length < sizeof(*ev)) {
btd_error(adapter->dev_id, "Too small CSRK event");
@@ -7152,8 +12098,14 @@ static void new_csrk_callback(uint16_t index, uint16_t length,
if (!ev->store_hint)
return;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ device_get_tizen_addr(device, addr->type, &t_addr);
+ store_csrk(bdaddr, &t_addr.bdaddr, t_addr.bdaddr_type, key->val, 0,
+ key->type);
+#else
store_csrk(bdaddr, &key->addr.bdaddr, key->addr.type, key->val, 0,
key->type);
+#endif
btd_device_set_temporary(device, false);
}
@@ -7202,6 +12154,9 @@ static void new_irk_callback(uint16_t index, uint16_t length,
struct btd_device *device, *duplicate;
bool persistent;
char dst[18], rpa[18];
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ struct device_addr_type t_addr;
+#endif
if (length < sizeof(*ev)) {
btd_error(adapter->dev_id, "Too small New IRK event");
@@ -7215,11 +12170,16 @@ static void new_irk_callback(uint16_t index, uint16_t length,
if (bacmp(&ev->rpa, BDADDR_ANY)) {
device = btd_adapter_get_device(adapter, &ev->rpa,
- BDADDR_LE_RANDOM);
+ BDADDR_LE_RANDOM);
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
duplicate = btd_adapter_find_device(adapter, &addr->bdaddr,
- addr->type);
+ addr->type);
if (duplicate == device)
duplicate = NULL;
+#else
+ device_set_rpa(device, &ev->rpa);
+ duplicate = NULL;
+#endif
} else {
device = btd_adapter_get_device(adapter, &addr->bdaddr,
addr->type);
@@ -7237,11 +12197,20 @@ static void new_irk_callback(uint16_t index, uint16_t length,
if (duplicate)
device_merge_duplicate(device, duplicate);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ device_set_irk_value(device, irk->val);
+#endif
+
persistent = !!ev->store_hint;
if (!persistent)
return;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ device_get_tizen_addr(device, addr->type, &t_addr);
+ store_irk(adapter, &t_addr.bdaddr, t_addr.bdaddr_type, irk->val);
+#else
store_irk(adapter, &addr->bdaddr, addr->type, irk->val);
+#endif
btd_device_set_temporary(device, false);
}
@@ -7294,7 +12263,9 @@ static void new_conn_param(uint16_t index, uint16_t length,
uint16_t min, max, latency, timeout;
struct btd_device *dev;
char dst[18];
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ struct device_addr_type t_addr;
+#endif
if (length < sizeof(*ev)) {
btd_error(adapter->dev_id,
@@ -7322,9 +12293,16 @@ static void new_conn_param(uint16_t index, uint16_t length,
if (!ev->store_hint)
return;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ device_get_tizen_addr(dev, ev->addr.type, &t_addr);
+ store_conn_param(adapter, &t_addr.bdaddr, t_addr.bdaddr_type,
+ ev->min_interval, ev->max_interval,
+ ev->latency, ev->timeout);
+#else
store_conn_param(adapter, &ev->addr.bdaddr, ev->addr.type,
ev->min_interval, ev->max_interval,
ev->latency, ev->timeout);
+#endif
}
int adapter_set_io_capability(struct btd_adapter *adapter, uint8_t io_cap)
@@ -7387,6 +12365,62 @@ int btd_adapter_remove_remote_oob_data(struct btd_adapter *adapter,
return -EIO;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+int btd_adapter_add_remote_oob_ext_data(struct btd_adapter *adapter,
+ const bdaddr_t *bdaddr, uint8_t bdaddr_type,
+ uint8_t *hash192, uint8_t *randomizer192,
+ uint8_t *hash256, uint8_t *randomizer256)
+{
+ struct mgmt_cp_add_remote_oob_data cp;
+ char addr[18];
+
+ ba2str(bdaddr, addr);
+ DBG("hci%d bdaddr %s type %d", adapter->dev_id, addr, bdaddr_type);
+
+ memset(&cp, 0, sizeof(cp));
+ bacpy(&cp.addr.bdaddr, bdaddr);
+ cp.addr.type = bdaddr_type;
+
+ if (hash192 && randomizer192) {
+ memcpy(cp.hash192, hash192, 16);
+ memcpy(cp.rand192, randomizer192, 16);
+ }
+
+ if (hash256 && randomizer256) {
+ memcpy(cp.hash256, hash256, 16);
+ memcpy(cp.rand256, randomizer256, 16);
+ }
+
+ if (mgmt_send(adapter->mgmt, MGMT_OP_ADD_REMOTE_OOB_DATA,
+ adapter->dev_id, sizeof(cp), &cp,
+ NULL, NULL, NULL) > 0)
+ return 0;
+
+ return -EIO;
+}
+
+int btd_adapter_remove_remote_oob_ext_data(struct btd_adapter *adapter,
+ const bdaddr_t *bdaddr, uint8_t bdaddr_type)
+{
+ struct mgmt_cp_remove_remote_oob_data cp;
+ char addr[18];
+
+ ba2str(bdaddr, addr);
+ DBG("hci%d bdaddr %s type %d", adapter->dev_id, addr, bdaddr_type);
+
+ memset(&cp, 0, sizeof(cp));
+ bacpy(&cp.addr.bdaddr, bdaddr);
+ cp.addr.type = bdaddr_type;
+
+ if (mgmt_send(adapter->mgmt, MGMT_OP_REMOVE_REMOTE_OOB_DATA,
+ adapter->dev_id, sizeof(cp), &cp,
+ NULL, NULL, NULL) > 0)
+ return 0;
+
+ return -EIO;
+}
+#endif
+
bool btd_adapter_ssp_enabled(struct btd_adapter *adapter)
{
if (adapter->current_settings & MGMT_SETTING_SSP)
@@ -7412,6 +12446,10 @@ static void read_local_oob_data_complete(uint8_t status, uint16_t length,
const struct mgmt_rp_read_local_oob_data *rp = param;
struct btd_adapter *adapter = user_data;
const uint8_t *hash, *randomizer;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ const uint8_t *hash256 = NULL;
+ const uint8_t *randomizer256 = NULL;
+#endif
if (status != MGMT_STATUS_SUCCESS) {
btd_error(adapter->dev_id,
@@ -7419,20 +12457,36 @@ static void read_local_oob_data_complete(uint8_t status, uint16_t length,
mgmt_errstr(status), status);
hash = NULL;
randomizer = NULL;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ } else if (length < 32) {
+#else
} else if (length < sizeof(*rp)) {
+#endif
btd_error(adapter->dev_id,
"Too small read local OOB data response");
return;
} else {
hash = rp->hash192;
randomizer = rp->rand192;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (length > 32) {
+ hash256 = rp->hash256;
+ randomizer256 = rp->rand256;
+ }
+#endif
}
if (!adapter->oob_handler || !adapter->oob_handler->read_local_cb)
return;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ adapter->oob_handler->read_local_cb(adapter, hash, randomizer,
+ hash256, randomizer256,
+ adapter->oob_handler->user_data);
+#else
adapter->oob_handler->read_local_cb(adapter, hash, randomizer,
adapter->oob_handler->user_data);
+#endif
g_free(adapter->oob_handler);
adapter->oob_handler = NULL;
@@ -7544,7 +12598,11 @@ static int adapter_register(struct btd_adapter *adapter)
if (!g_dbus_register_interface(dbus_conn,
adapter->path, ADAPTER_INTERFACE,
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ adapter_methods, adapter_signals,
+#else
adapter_methods, NULL,
+#endif
adapter_properties, adapter,
adapter_free)) {
btd_error(adapter->dev_id,
@@ -7598,6 +12656,30 @@ static int adapter_register(struct btd_adapter *adapter)
clear_blocked(adapter);
load_devices(adapter);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ adapter_print_devices(adapter);
+
+ if (load_local_irk(adapter)) {
+ if (!(adapter->supported_settings & MGMT_SETTING_PRIVACY))
+ main_opts.le_privacy = false;
+
+ /*
+ * Some Android devices don't consider the device as LE one,
+ * if the device doesn't distribute IRK when pairing.
+ * Because of this compatibility issue, set IRK
+ * even though privacy feature is disabled.
+ */
+ set_local_irk(adapter);
+
+ if (main_opts.le_privacy) {
+ DBG("Enable LE Privacy feature");
+ set_privacy(adapter, true);
+ } else {
+ DBG("Disable LE Privacy feature");
+ }
+ }
+#endif
+
/* retrieve the active connections: address the scenario where
* the are active connections before the daemon've started */
if (adapter->current_settings & MGMT_SETTING_POWERED)
@@ -7825,11 +12907,13 @@ static void connect_failed_callback(uint16_t index, uint16_t length,
device_bonding_failed(device, ev->status);
}
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
/* In the case the bonding was canceled or did exists, remove the device
* when it is temporary. */
if (device && !device_is_bonding(device, NULL)
&& device_is_temporary(device))
btd_adapter_remove_device(adapter, device);
+#endif
}
static void remove_keys(struct btd_adapter *adapter,
@@ -7845,6 +12929,11 @@ static void remove_keys(struct btd_adapter *adapter,
ba2str(btd_adapter_get_address(adapter), adapter_addr);
ba2str(device_get_address(device), device_addr);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (device_get_rpa_exist(device) == true)
+ ba2str(device_get_rpa(device), device_addr);
+#endif
+
snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info", adapter_addr,
device_addr);
key_file = g_key_file_new();
@@ -7895,6 +12984,264 @@ static void unpaired_callback(uint16_t index, uint16_t length,
device_set_unpaired(device, ev->addr.type);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static uint8_t *generate_irk(void)
+{
+ int fd;
+ uint8_t *irk;
+
+ DBG("Generate IRK");
+
+ fd = open("/dev/urandom", O_RDONLY);
+ if (fd < 0)
+ return NULL;
+
+ irk = g_malloc0(MGMT_IRK_SIZE);
+ if (read(fd, irk, MGMT_IRK_SIZE) != MGMT_IRK_SIZE) {
+ error("Cannot read random bytes");
+ g_free(irk);
+ close(fd);
+ return NULL;
+ }
+ close(fd);
+
+ return irk;
+}
+
+#define LOCAL_IRK_DIRNAME "/csa/bluetooth"
+#define LOCAL_IRK_FILENAME ".local_irk"
+
+static bool store_local_irk(struct btd_adapter *adapter)
+{
+ int fd;
+ int ret;
+
+ if (adapter->local_irk == NULL) {
+ error("Local IRK is not proper");
+ return false;
+ }
+
+ if (access(LOCAL_IRK_DIRNAME, F_OK) < 0) {
+ if (mkdir(LOCAL_IRK_DIRNAME, 0755) < 0) {
+ error("Cannot create a directory for local IRK : %s",
+ strerror(errno));
+ return false;
+ }
+ }
+
+ fd = open(LOCAL_IRK_DIRNAME"/"LOCAL_IRK_FILENAME,
+ O_WRONLY | O_CREAT | O_TRUNC, 0644);
+ if (fd < 0) {
+ error("Cannot open a file for local IRK : %s", strerror(errno));
+ return false;
+ }
+
+ ret = write(fd, adapter->local_irk, MGMT_IRK_SIZE);
+ if (ret != MGMT_IRK_SIZE) {
+ error("Cannot write local IRK [%d] : %s", ret, strerror(errno));
+
+ close(fd);
+ unlink(LOCAL_IRK_DIRNAME"/"LOCAL_IRK_FILENAME);
+ return false;
+ }
+
+ ret = fdatasync(fd);
+ if (ret < 0)
+ error("sync failed : %s", strerror(errno));
+
+ close(fd);
+ return true;
+}
+
+static bool load_local_irk(struct btd_adapter *adapter)
+{
+ int fd;
+ int ret;
+
+ if (access(LOCAL_IRK_DIRNAME"/"LOCAL_IRK_FILENAME, F_OK) < 0) {
+ adapter->local_irk = generate_irk();
+ if (store_local_irk(adapter) == false) {
+ error("Cannot store Local IRK");
+ g_free(adapter->local_irk);
+ return false;
+ }
+
+ return true;
+ }
+
+ if (adapter->local_irk) {
+ DBG("Local IRK is already loaded");
+ return true;
+ }
+
+ fd = open(LOCAL_IRK_DIRNAME"/"LOCAL_IRK_FILENAME, O_RDONLY);
+ if (fd < 0) {
+ error("Cannot open local IRK file : %s", strerror(errno));
+ return false;
+ }
+
+ adapter->local_irk = g_malloc0(MGMT_IRK_SIZE);
+
+ ret = read(fd, adapter->local_irk, MGMT_IRK_SIZE);
+ if (ret != MGMT_IRK_SIZE) {
+ error("Cannot read local IRK [%d] : %s", ret, strerror(errno));
+ g_free(adapter->local_irk);
+ close(fd);
+ return false;
+ }
+
+ close(fd);
+ return true;
+}
+
+static void set_privacy_complete(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+
+ if (status != MGMT_STATUS_SUCCESS)
+ error("Setting privacy failed for hci%u: %s (0x%02x)",
+ adapter->dev_id, mgmt_errstr(status), status);
+ else
+ DBG("Privacy feature is set/unset successfully for hci%u",
+ adapter->dev_id);
+}
+
+static bool set_privacy(struct btd_adapter *adapter, bool privacy)
+{
+ struct mgmt_cp_set_privacy cp;
+
+ if (!adapter->local_irk) {
+ error("Local IRK is not available");
+ return false;
+ }
+
+ memset(&cp, 0, sizeof(cp));
+ memcpy(cp.irk, adapter->local_irk, MGMT_IRK_SIZE);
+
+ if (privacy)
+ cp.privacy = 0x01;
+
+ if (mgmt_send(adapter->mgmt, MGMT_OP_SET_PRIVACY,
+ adapter->dev_id, sizeof(cp), &cp,
+ set_privacy_complete, adapter, NULL) > 0)
+ return true;
+
+ error("Failed to set privacy and load local irk for index %u",
+ adapter->dev_id);
+ return false;
+}
+
+static void set_irk_complete(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+
+ if (status != MGMT_STATUS_SUCCESS)
+ error("Setting IRK is failed for hci%u: %s (0x%02x)",
+ adapter->dev_id, mgmt_errstr(status), status);
+ else
+ DBG("Setting IRK is succeed for hci%u", adapter->dev_id);
+}
+
+static bool set_local_irk(struct btd_adapter *adapter)
+{
+ struct mgmt_cp_set_irk cp;
+
+ if (!adapter->local_irk) {
+ error("Local IRK is not available");
+ return false;
+ }
+
+ memcpy(cp.irk, adapter->local_irk, MGMT_IRK_SIZE);
+
+ if (mgmt_send(adapter->mgmt, MGMT_OP_SET_IRK,
+ adapter->dev_id, sizeof(cp), &cp,
+ set_irk_complete, adapter, NULL) > 0)
+ return true;
+
+ error("Failed to set irk %u", adapter->dev_id);
+ return false;
+}
+
+int btd_adapter_connect_ipsp(struct btd_adapter *adapter,
+ const bdaddr_t *bdaddr,
+ uint8_t bdaddr_type)
+
+{
+ struct mgmt_cp_connect_6lowpan cp;
+
+ memset(&cp, 0, sizeof(cp));
+ bacpy(&cp.addr.bdaddr, bdaddr);
+ cp.addr.type = bdaddr_type;
+
+ if (mgmt_send(adapter->mgmt, MGMT_OP_CONNECT_6LOWPAN,
+ adapter->dev_id, sizeof(cp), &cp,
+ NULL, NULL, NULL) > 0)
+ return 0;
+
+ return -EIO;
+}
+
+int btd_adapter_disconnect_ipsp(struct btd_adapter *adapter,
+ const bdaddr_t *bdaddr,
+ uint8_t bdaddr_type)
+
+{
+ struct mgmt_cp_disconnect_6lowpan cp;
+
+ memset(&cp, 0, sizeof(cp));
+ bacpy(&cp.addr.bdaddr, bdaddr);
+ cp.addr.type = bdaddr_type;
+
+ if (mgmt_send(adapter->mgmt, MGMT_OP_DISCONNECT_6LOWPAN,
+ adapter->dev_id, sizeof(cp), &cp,
+ NULL, NULL, NULL) > 0)
+ return 0;
+
+ return -EIO;
+}
+
+uint8_t btd_adapter_get_rpa_res_support_value(
+ struct btd_adapter *adapter)
+{
+ return adapter->central_rpa_res_support;
+}
+
+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,
const void *param, void *user_data)
{
@@ -7927,6 +13274,17 @@ static int clear_devices(struct btd_adapter *adapter)
return -EIO;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static gboolean adapter_start_idle_cb(gpointer user_data)
+{
+ struct btd_adapter *adapter = (struct btd_adapter*)user_data;
+
+ adapter_start(adapter);
+
+ return FALSE;
+}
+#endif
+
static void read_info_complete(uint8_t status, uint16_t length,
const void *param, void *user_data)
{
@@ -7973,6 +13331,10 @@ static void read_info_complete(uint8_t status, uint16_t length,
adapter->supported_settings = btohl(rp->supported_settings);
adapter->current_settings = btohl(rp->current_settings);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ adapter_check_version(adapter, rp->version);
+#endif
+
clear_uuids(adapter);
clear_devices(adapter);
@@ -8026,6 +13388,13 @@ static void read_info_complete(uint8_t status, uint16_t length,
(missing_settings & MGMT_SETTING_FAST_CONNECTABLE))
set_mode(adapter, MGMT_OP_SET_FAST_CONNECTABLE, 0x01);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ /* Set the RPA resolution value to '1' if privacy is supported */
+ if (main_opts.le_privacy &&
+ adapter->supported_settings & MGMT_SETTING_PRIVACY)
+ adapter->central_rpa_res_support = 0x01;
+#endif
+
err = adapter_register(adapter);
if (err < 0) {
btd_error(adapter->dev_id, "Unable to register new adapter");
@@ -8061,6 +13430,13 @@ static void read_info_complete(uint8_t status, uint16_t length,
device_found_callback,
adapter, NULL);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ mgmt_register(adapter->mgmt, MGMT_EV_LE_DEVICE_FOUND,
+ adapter->dev_id,
+ le_device_found_callback,
+ adapter, NULL);
+#endif
+
mgmt_register(adapter->mgmt, MGMT_EV_DEVICE_DISCONNECTED,
adapter->dev_id,
disconnected_callback,
@@ -8140,6 +13516,63 @@ static void read_info_complete(uint8_t status, uint16_t length,
user_passkey_notify_callback,
adapter, NULL);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ mgmt_register(adapter->mgmt, MGMT_EV_RSSI_ALERT,
+ adapter->dev_id,
+ rssi_alert_callback,
+ adapter, NULL);
+
+ mgmt_register(adapter->mgmt, MGMT_EV_RAW_RSSI,
+ adapter->dev_id,
+ get_raw_rssi_callback,
+ adapter, NULL);
+
+ mgmt_register(adapter->mgmt, MGMT_EV_RSSI_ENABLED,
+ adapter->dev_id,
+ rssi_enabled_callback,
+ adapter, NULL);
+
+ mgmt_register(adapter->mgmt, MGMT_EV_RSSI_DISABLED,
+ adapter->dev_id,
+ rssi_disabled_callback,
+ adapter, NULL);
+
+ mgmt_register(adapter->mgmt, MGMT_EV_HARDWARE_ERROR,
+ adapter->dev_id,
+ hardware_error_callback,
+ adapter, NULL);
+
+ mgmt_register(adapter->mgmt, MGMT_EV_TX_TIMEOUT_ERROR,
+ adapter->dev_id,
+ tx_timeout_error_callback,
+ adapter, NULL);
+
+ mgmt_register(adapter->mgmt, MGMT_EV_DEVICE_NAME_UPDATE,
+ adapter->dev_id,
+ device_name_update_callback,
+ adapter, NULL);
+
+ mgmt_register(adapter->mgmt, MGMT_EV_MULTI_ADV_STATE_CHANGED,
+ adapter->dev_id,
+ multi_adv_state_change_callback,
+ adapter, NULL);
+
+ mgmt_register(adapter->mgmt, MGMT_EV_6LOWPAN_CONN_STATE_CHANGED,
+ adapter->dev_id,
+ bt_6lowpan_conn_state_change_callback,
+ adapter, NULL);
+
+ mgmt_register(adapter->mgmt, MGMT_EV_LE_DATA_LENGTH_CHANGED,
+ adapter->dev_id,
+ bt_le_data_length_changed_callback,
+ adapter, NULL);
+
+ mgmt_register(adapter->mgmt, MGMT_EV_CONN_UPDATED,
+ adapter->dev_id,
+ le_conn_update_completed_callback,
+ adapter, NULL);
+#endif
+
set_dev_class(adapter);
set_name(adapter, btd_adapter_get_name(adapter));
@@ -8156,7 +13589,15 @@ static void read_info_complete(uint8_t status, uint16_t length,
set_discoverable(adapter, 0x01, 0);
if (adapter->current_settings & MGMT_SETTING_POWERED)
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ g_idle_add(adapter_start_idle_cb, adapter);
+#else
adapter_start(adapter);
+#endif
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ else
+ set_mode(adapter, MGMT_OP_SET_POWERED, 0x01);
+#endif
return;
diff --git a/src/adapter.h b/src/adapter.h
index f2947feb..985e28ed 100755
--- a/src/adapter.h
+++ b/src/adapter.h
@@ -25,21 +25,75 @@
#include <stdbool.h>
#include <dbus/dbus.h>
#include <glib.h>
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/sdp.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+#endif
#define MAX_NAME_LENGTH 248
/* Invalid SSP passkey value used to indicate negative replies */
#define INVALID_PASSKEY 0xffffffff
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define BT_DISC_TYPE_DEFAULT 0
+#define BT_DISC_TYPE_BREDR_ONLY 1
+#define BT_DISC_TYPE_LE_ONLY 2
+#define BT_DISC_TYPE_LE_BREDR 3
+
+#define TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+#ifdef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+
+#define COMPANY_ID_APPLE 0x004C
+/* Deafult value set to 16(same as vendor specific value)
+ * and value shall be changed when required */
+#define SCAN_FILTER_SLOTS_MAX 16
+#define ADV_TYPE_SCAN_RESPONSE 04
+
+typedef enum {
+ NONE_REPORT,
+ SCAN_REPORT,
+ IBEACON_REPORT,
+} adapter_le_scan_report_type;
+
+typedef enum {
+ LE_PASSIVE_SCAN,
+ LE_ACTIVE_SCAN,
+} adapter_le_scan_type;
+#endif /* TIZEN_FEATURE_PLATFROM_SCAN_FILTER */
+
+#endif
+
struct btd_adapter;
struct btd_device;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+typedef enum {
+ BLUETOOTH_A2DP_SOURCE_ROLE = 0,
+ BLUETOOTH_A2DP_SINK_ROLE,
+} bluetooth_a2dp_role_t;
+#endif
+
struct btd_adapter *btd_adapter_get_default(void);
bool btd_adapter_is_default(struct btd_adapter *adapter);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+bluetooth_a2dp_role_t btd_adapter_get_a2dp_role(struct btd_adapter *adapter);
+void btd_adapter_set_a2dp_role(struct btd_adapter *adapter, bluetooth_a2dp_role_t role);
+#endif
uint16_t btd_adapter_get_index(struct btd_adapter *adapter);
typedef void (*adapter_cb) (struct btd_adapter *adapter, gpointer user_data);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+typedef void (*oob_ext_read_local_cb_t) (struct btd_adapter *adapter,
+ const uint8_t *hash192,
+ const uint8_t *randomizer192,
+ const uint8_t *hash256,
+ const uint8_t *randomizer256,
+ void *user_data);
+#endif
typedef void (*oob_read_local_cb_t) (struct btd_adapter *adapter,
const uint8_t *hash,
const uint8_t *randomizer,
@@ -49,12 +103,26 @@ typedef void (*oob_bonding_cb_t) (struct btd_adapter *adapter,
void *user_data);
struct oob_handler {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ oob_ext_read_local_cb_t read_local_cb;
+#else
oob_read_local_cb_t read_local_cb;
+#endif
oob_bonding_cb_t bonding_cb;
bdaddr_t remote_addr;
void *user_data;
};
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#ifdef TIZEN_FEATURE_BLUEZ_BATTERY_WATCH
+typedef enum {
+ NONE_CHARGING,
+ WIRE_CHARGING,
+ WIRELESS_CHARGING,
+} charging_state_e;
+#endif /* TIZEN_FEATURE_BLUEZ_BATTERY_WATCH */
+#endif
+
int adapter_init(void);
void adapter_cleanup(void);
void adapter_shutdown(void);
@@ -92,6 +160,10 @@ struct btd_device *btd_adapter_find_device(struct btd_adapter *adapter,
const char *adapter_get_path(struct btd_adapter *adapter);
const bdaddr_t *btd_adapter_get_address(struct btd_adapter *adapter);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+const bdaddr_t *btd_adapter_get_le_address(struct btd_adapter *adapter);
+uint8_t btd_adapter_get_le_address_type(struct btd_adapter * adapter);
+#endif
int adapter_set_name(struct btd_adapter *adapter, const char *name);
int adapter_service_add(struct btd_adapter *adapter, sdp_record_t *rec);
@@ -198,6 +270,16 @@ int btd_adapter_add_remote_oob_data(struct btd_adapter *adapter,
int btd_adapter_remove_remote_oob_data(struct btd_adapter *adapter,
const bdaddr_t *bdaddr);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+int btd_adapter_add_remote_oob_ext_data(struct btd_adapter *adapter,
+ const bdaddr_t *bdaddr, uint8_t bdaddr_type,
+ uint8_t *hash192, uint8_t *randomizer192,
+ uint8_t *hash256, uint8_t *randomizer256);
+
+int btd_adapter_remove_remote_oob_ext_data(struct btd_adapter *adapter,
+ const bdaddr_t *bdaddr, uint8_t bdaddr_type);
+#endif
+
int btd_adapter_gatt_server_start(struct btd_adapter *adapter);
void btd_adapter_gatt_server_stop(struct btd_adapter *adapter);
@@ -226,3 +308,48 @@ void btd_adapter_for_each_device(struct btd_adapter *adapter,
bool btd_le_connect_before_pairing(void);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+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);
+
+uint8_t btd_adapter_get_rpa_res_support_value(
+ struct btd_adapter *adapter);
+
+int btd_adapter_set_dev_rpa_res_support(struct btd_adapter *adapter,
+ struct btd_device *device);
+#endif
+
+typedef void (*read_max_data_length_cb_t) (struct btd_adapter *adapter,
+ 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 {
+ read_max_data_length_cb_t read_callback;
+ void *user_data;
+};
+
+typedef void (*read_host_suggested_default_data_length_cb_t) (struct btd_adapter *adapter,
+ 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);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void adapter_check_version(struct btd_adapter *adapter, uint8_t hci_ver);
+#ifdef TIZEN_FEATURE_BLUEZ_BATTERY_WATCH
+charging_state_e get_charging_state(struct btd_adapter *adapter);
+#endif /* TIZEN_FEATURE_BLUEZ_BATTERY_WATCH */
+#endif
diff --git a/src/adapter_le_vsc_features.c b/src/adapter_le_vsc_features.c
new file mode 100755
index 00000000..6c586eb7
--- /dev/null
+++ b/src/adapter_le_vsc_features.c
@@ -0,0 +1,769 @@
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+
+#include <errno.h>
+
+#include "log.h"
+#include "adapter.h"
+#include "eir.h"
+
+#include "adapter_le_vsc_features.h"
+
+
+static apater_le_vsc_rp_get_vendor_cap ble_vsc_cb = { -1, };
+
+static int update_le_address(const bdaddr_t *le_addr)
+{
+ int hdev = 0;
+ le_set_random_address_cp cp;
+
+ hdev = hci_open_dev(0);
+ if (hdev < 0) {
+ error("Cannot open hdev");
+ return -1;
+ }
+
+ bacpy(&cp.bdaddr, le_addr);
+
+ if (hci_send_cmd(hdev, OGF_LE_CTL, OCF_LE_SET_RANDOM_ADDRESS,
+ LE_SET_RANDOM_ADDRESS_CP_SIZE, &cp) < 0) {
+ error("hci_send_cmd is failed");
+ hci_close_dev(hdev);
+ return -1;
+ }
+
+ hci_close_dev(hdev);
+
+ return 0;
+}
+
+static int send_vsc_command(uint16_t ocf, uint8_t *cp, uint8_t cp_len,
+ uint8_t *rp, uint8_t rp_len)
+{
+ int dd;
+ struct hci_request rq;
+
+ dd = hci_open_dev(0);
+ if (dd < 0) {
+ error("hci_open_dev is failed");
+ return -1;
+ }
+
+ memset(&rq, 0, sizeof(rq));
+ rq.ogf = OGF_VENDOR_CMD;
+ rq.ocf = ocf;
+ rq.cparam = cp;
+ rq.clen = cp_len;
+ rq.rparam = rp;
+ rq.rlen = rp_len;
+
+ if (hci_send_req(dd, &rq, 5000) < 0) {
+ error("Fail to send VSC");
+ hci_close_dev(dd);
+ return -1;
+ }
+
+ hci_close_dev(dd);
+ return 0;
+}
+
+gboolean adapter_le_read_ble_feature_info(void)
+{
+ int ret;
+
+ DBG("");
+
+ ret = send_vsc_command(OCF_BCM_LE_GET_VENDOR_CAP, (uint8_t *) NULL, 0,
+ (uint8_t *) &ble_vsc_cb, sizeof(ble_vsc_cb));
+ if (ret < 0)
+ return FALSE;
+
+ if (HCI_SUCCESS != ble_vsc_cb.status) {
+ error("Fail to read ble feature info");
+ return FALSE;
+ }
+
+ DBG("======== BLE support info ========");
+ DBG("adv_inst_max [%d]", ble_vsc_cb.adv_inst_max);
+ DBG("rpa_offloading [%d]", ble_vsc_cb.rpa_offloading);
+ DBG("tot_scan_results_strg [%d]", ble_vsc_cb.tot_scan_results_strg);
+ DBG("max_irk_list_sz [%d]", ble_vsc_cb.max_irk_list_sz);
+ DBG("filter_support [%d]", ble_vsc_cb.filter_support);
+ DBG("max_filter [%d]", ble_vsc_cb.max_filter);
+ DBG("energy_support [%d]", ble_vsc_cb.energy_support);
+ DBG("onlost_follow [%d]", ble_vsc_cb.onlost_follow);
+ DBG("=================================");
+
+ return TRUE;
+}
+
+int adapter_le_get_max_adv_instance(void)
+{
+ if (HCI_SUCCESS != ble_vsc_cb.status) {
+ error("not yet acquired chipset info");
+ return 0;
+ }
+
+ /* GearS does not support multi advertising.
+ but its official firmware returns adv_inst_max vaule to 5.
+ So here check rpa_offloading support and filter_support */
+ if (!ble_vsc_cb.rpa_offloading || !ble_vsc_cb.max_filter)
+ return 0;
+
+ return ble_vsc_cb.adv_inst_max;
+}
+
+gboolean adapter_le_is_supported_multi_advertising(void)
+{
+ if (HCI_SUCCESS != ble_vsc_cb.status) {
+ error("not yet acquired chipset info");
+ return FALSE;
+ }
+
+ /* GearS does not support multi advertising.
+ but its official firmware returns adv_inst_max vaule to 5.
+ So here check rpa_offloading support and filter_support */
+ if (!ble_vsc_cb.rpa_offloading || !ble_vsc_cb.max_filter)
+ return FALSE;
+
+ if (ble_vsc_cb.adv_inst_max >= 5)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+gboolean adapter_le_is_supported_offloading(void)
+{
+ if (HCI_SUCCESS != ble_vsc_cb.status) {
+ error("not yet acquired chipset info");
+ return FALSE;
+ }
+
+ return ble_vsc_cb.rpa_offloading ? TRUE : FALSE;
+}
+
+int adapter_le_get_scan_filter_size(void)
+{
+ if (HCI_SUCCESS != ble_vsc_cb.status) {
+ error("not yet acquired chipset info");
+ return 0;
+ }
+#if 0
+ if (!ble_vsc_cb.filter_support) {
+ error("filter_support is not supported");
+ return 0;
+ }
+#endif
+ return ble_vsc_cb.max_filter;
+}
+
+gboolean adapter_le_set_multi_adv_params (adapter_le_adv_inst_info_t *p_inst,
+ adapter_le_adv_param_t *p_params)
+{
+ int ret;
+ adapter_le_vsc_cp_set_multi_adv_params cp;
+ apater_le_vsc_rp_multi_adv rp;
+
+ DBG("");
+
+ memset(&cp, 0, sizeof(cp));
+ cp.subcode = SUB_CMD_LE_MULTI_ADV_SET_PARAM;
+ cp.adv_int_min = p_params->adv_int_min;
+ cp.adv_int_max = p_params->adv_int_max;
+ cp.adv_type = p_params->adv_type;
+ cp.bdaddr_type = p_inst->bdaddr_type;
+ bacpy(&cp.bdaddr, &p_inst->bdaddr);
+ cp.direct_bdaddr_type = 0;
+ bacpy(&cp.direct_bdaddr, BDADDR_ANY);
+
+ cp.channel_map = p_params->channel_map;
+ cp.adv_filter_policy = p_params->adv_filter_policy;
+ cp.inst_id = p_inst->inst_id;
+ cp.tx_power = p_params->tx_power;
+
+ ret = send_vsc_command(OCF_BCM_LE_MULTI_ADV, (uint8_t *) &cp, sizeof(cp),
+ (uint8_t *) &rp, sizeof(rp));
+
+ if (ret < 0)
+ return FALSE;
+
+ if (HCI_SUCCESS != rp.status) {
+ DBG("Fail to send VSC :: sub[%x] - status [0x%02x]", rp.subcode, rp.status);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean adapter_le_set_multi_adv_data(uint8_t inst_id, gboolean is_scan_rsp,
+ uint8_t data_len, uint8_t *p_data)
+{
+ int ret;
+ adapter_le_vsc_cp_set_multi_adv_data cp;
+ apater_le_vsc_rp_multi_adv rp;
+
+ DBG("");
+
+ memset(&cp, 0, sizeof(cp));
+ cp.subcode = (is_scan_rsp) ?
+ SUB_CMD_LE_MULTI_ADV_WRITE_SCAN_RSP_DATA :
+ SUB_CMD_LE_MULTI_ADV_WRITE_ADV_DATA;
+ cp.data_len = data_len;
+ memcpy(&cp.data, p_data, data_len);
+ cp.inst_id = inst_id;
+
+ ret = send_vsc_command(OCF_BCM_LE_MULTI_ADV, (uint8_t *) &cp, sizeof(cp),
+ (uint8_t *) &rp, sizeof(rp));
+
+ if (ret < 0)
+ return FALSE;
+
+ if (HCI_SUCCESS != rp.status) {
+ DBG("Fail to send VSC :: sub[%x] - status [0x%02x]", rp.subcode, rp.status);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean adapter_le_enable_multi_adv (struct btd_adapter *adapter,
+ gboolean enable, uint8_t inst_id)
+{
+ int ret;
+ adapter_le_vsc_cp_enable_multi_adv cp;
+ apater_le_vsc_rp_multi_adv rp;
+ uint8_t bdaddr_type;
+ const bdaddr_t *bdaddr;
+
+ DBG("");
+ if (enable) {
+ bdaddr_type = btd_adapter_get_le_address_type(adapter);
+ if (bdaddr_type == BDADDR_LE_RANDOM) {
+ bdaddr = btd_adapter_get_le_address(adapter);
+ update_le_address(bdaddr);
+ }
+ }
+
+ memset(&cp, 0, sizeof(cp));
+ cp.subcode = SUB_CMD_LE_MULTI_ADV_ENB;
+ cp.enable = enable;
+ cp.inst_id = inst_id;
+
+ ret = send_vsc_command(OCF_BCM_LE_MULTI_ADV, (uint8_t *) &cp, sizeof(cp),
+ (uint8_t *) &rp, sizeof(rp));
+
+ if (ret < 0)
+ return FALSE;
+
+ if (HCI_SUCCESS != rp.status) {
+ DBG("Fail to send VSC :: sub[%x] - status [0x%02x]", rp.subcode, rp.status);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean adapter_le_enable_scan_filtering (gboolean enable)
+{
+ int ret;
+ adapter_le_vsc_cp_enable_scan_filter cp;
+ apater_le_vsc_rp_enable_scan_filter rp;
+
+ DBG(" enable[%d]", enable);
+
+ memset(&cp, 0, sizeof(cp));
+ cp.subcode = SUB_CMD_LE_META_PF_ENABLE;
+ cp.enable = enable;
+
+ ret = send_vsc_command(OCF_BCM_LE_SCAN_FILTER, (uint8_t *) &cp, sizeof(cp),
+ (uint8_t *) &rp, sizeof(rp));
+
+ if (ret < 0)
+ return FALSE;
+
+ if (HCI_SUCCESS != rp.status) {
+ DBG("Fail to send VSC :: sub[%x] - status [%x]", rp.subcode, rp.status);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean adapter_le_set_scan_filter_params(adapter_le_scan_filter_param_t *params)
+{
+ int ret;
+ adapter_le_vsc_cp_apcf_set_filter_params cp;
+ adapter_le_vsc_rp_apcf_set_scan_filter rp;
+
+ DBG("filter_index [%d]", params->index);
+
+ memset(&cp, 0, sizeof(cp));
+ cp.subcode = SUB_CMD_LE_META_PF_FEAT_SEL;
+ cp.action = params->action;
+ cp.filter_index= params->index;
+ cp.feature= params->feature;
+ cp.feature_list_logic = params->filter_logic_type;
+ cp.filter_logic = params->filter_logic_type;
+ cp.rssi_high_threshold = params->rssi_high_threshold;
+ cp.rssi_low_thresh = params->rssi_low_threshold;
+ cp.delivery_mode = params->delivery_mode;
+ cp.onfound_timeout = params->onfound_timeout;
+ cp.onfound_timeout_cnt = params->onfound_timeout_cnt;
+ cp.onlost_timeout = params->onlost_timeout;
+
+ ret = send_vsc_command(OCF_BCM_LE_SCAN_FILTER, (uint8_t *) &cp, sizeof(cp),
+ (uint8_t *) &rp, sizeof(rp));
+
+ if (ret < 0)
+ return FALSE;
+
+ if (HCI_SUCCESS != rp.status) {
+ DBG("Fail to send VSC :: sub[%x] - status [%x] Available space [%x]",
+ rp.subcode, rp.status, rp.available_space);
+ return FALSE;
+ }
+
+ DBG("Scan Filter VSC :: sub[%x] - status [%x] Action [%x] Available space [%x]",
+ rp.subcode, rp.status, rp.action, rp.available_space);
+ return TRUE;
+}
+
+gboolean adapter_le_service_address_scan_filtering(adapter_le_address_filter_params_t *params)
+{
+ int ret;
+ adapter_le_vsc_cp_address_scan_filter cp;
+ adapter_le_vsc_rp_apcf_set_scan_filter rp;
+
+ DBG("");
+
+ memset(&cp, 0, sizeof(cp));
+ cp.subcode = SUB_CMD_LE_META_PF_ADDR;
+ cp.action= params ->action;
+ cp.filter_index = params->filter_index;
+
+ bacpy(&cp.bdaddr, &params->broadcaster_addr);
+ cp.bdaddr_type = params->bdaddr_type;
+
+ ret = send_vsc_command(OCF_BCM_LE_SCAN_FILTER, (uint8_t *) &cp, sizeof(cp),
+ (uint8_t *) &rp, sizeof(rp));
+
+ if (ret < 0)
+ return FALSE;
+
+ if (HCI_SUCCESS != rp.status) {
+ DBG("Fail to send VSC :: sub[%x] - status [%x]", rp.subcode, rp.status);
+ return FALSE;
+ }
+
+ DBG("Scan Filter VSC :: sub[%x] - status [%x] Action [%x] Available space [%x]",
+ rp.subcode, rp.status, rp.action, rp.available_space);
+
+ return TRUE;
+}
+
+gboolean adapter_le_service_uuid_scan_filtering(gboolean is_solicited,
+ adapter_le_uuid_params_t *params)
+{
+ int ret;
+ adapter_le_vsc_cp_service_uuid_scan_filter cp;
+ adapter_le_vsc_rp_apcf_set_scan_filter rp;
+ uint8_t *p = cp.data;
+ int cp_len = UUID_SCAN_FILTER_HEADER_SIZE;
+
+ DBG("");
+
+ memset(&cp, 0, sizeof(cp));
+ cp.subcode = (is_solicited) ? SUB_CMD_LE_META_PF_SOL_UUID :
+ SUB_CMD_LE_META_PF_UUID;
+
+ cp.action= params ->action;
+ cp.filter_index = params->filter_index;
+
+ memcpy(&cp.data, params->uuid, params->uuid_len);
+ cp_len += params->uuid_len;
+
+ memcpy(p + params->uuid_len, params->uuid_mask, params->uuid_len);
+ cp_len += params->uuid_len;
+
+ ret = send_vsc_command(OCF_BCM_LE_SCAN_FILTER, (uint8_t *) &cp, cp_len,
+ (uint8_t *) &rp, sizeof(rp));
+
+ if (ret < 0)
+ return FALSE;
+
+ if (HCI_SUCCESS != rp.status) {
+ DBG("Fail to send VSC :: sub[%x] - status [%x]", rp.subcode, rp.status);
+ return FALSE;
+ }
+
+ DBG("Scan Filter VSC :: sub[%x] - status [%x] Action [%x] Available space [%x]",
+ rp.subcode, rp.status, rp.action, rp.available_space);
+
+ return TRUE;
+}
+
+gboolean adapter_le_local_name_scan_filtering(adapter_le_local_name_params_t *params)
+{
+ int ret;
+ adapter_le_vsc_cp_local_name_scan_filter cp;
+ adapter_le_vsc_rp_apcf_set_scan_filter rp;
+ int cp_len = NAME_SCAN_FILTER_HEADER_SIZE;
+ int name_len;
+
+ DBG("");
+
+ memset(&cp, 0, sizeof(cp));
+ cp.subcode = SUB_CMD_LE_META_PF_LOCAL_NAME;
+ cp.action= params->action;
+ cp.filter_index = params->filter_index;
+
+ name_len = params->name_len;
+ DBG("name [%s], len [%d]",params->local_name, name_len);
+
+ if (name_len > SCAN_FILTER_DATA_MAX_LEN)
+ name_len = SCAN_FILTER_DATA_MAX_LEN;
+
+ if (name_len > 0) {
+ memcpy(&cp.name, params->local_name, name_len);
+ cp_len += name_len;
+ }
+
+ ret = send_vsc_command(OCF_BCM_LE_SCAN_FILTER, (uint8_t *) &cp, cp_len,
+ (uint8_t *) &rp, sizeof(rp));
+
+ if (ret < 0)
+ return FALSE;
+
+ if (HCI_SUCCESS != rp.status) {
+ DBG("Fail to send VSC :: sub[%x] - status [%x]", rp.subcode, rp.status);
+ return FALSE;
+ }
+
+ DBG("Scan Filter VSC :: sub[%x] - status [%x] Action [%x] Available space [%x]",
+ rp.subcode, rp.status, rp.action, rp.available_space);
+
+ return TRUE;
+}
+
+gboolean adapter_le_manf_data_scan_filtering (adapter_le_manf_data_params_t *params)
+{
+ int ret;
+ adapter_le_vsc_cp_manf_data_scan_filter cp;
+ adapter_le_vsc_rp_apcf_set_scan_filter rp;
+ uint8_t *p = cp.data;
+ int data_len = 0;
+ DBG("");
+
+ memset(&cp, 0, sizeof(cp));
+ cp.subcode = SUB_CMD_LE_META_PF_MANU_DATA;
+ cp.action= params->action;
+ cp.filter_index = params->filter_index;
+
+ /* add company_id and data */
+ cp.data[data_len++] = (uint8_t) params->company_id;
+ cp.data[data_len++] = (uint8_t) (params->company_id >> 8);
+ DBG("");
+ memcpy(p + data_len, params->man_data, params->man_data_len);
+ data_len += params->man_data_len;
+
+ /* add company_id mask and data mask */
+ cp.data[data_len++] = (uint8_t) params->company_id_mask;
+ cp.data[data_len++] = (uint8_t) (params->company_id_mask >> 8);
+ memcpy(p + data_len, params->man_data_mask, params->man_data_len);
+ data_len += params->man_data_len;
+
+ ret = send_vsc_command(OCF_BCM_LE_SCAN_FILTER, (uint8_t *) &cp,
+ MANF_DATA_SCAN_FILTER_HEADER_SIZE + data_len,
+ (uint8_t *) &rp, sizeof(rp));
+
+ if (ret < 0)
+ return FALSE;
+
+ if (HCI_SUCCESS != rp.status) {
+ DBG("Fail to send VSC :: sub[%x] - status [%x]", rp.subcode, rp.status);
+ return FALSE;
+ }
+
+ DBG("Scan Filter VSC :: sub[%x] - status [%x] Action [%x] Available space [%x]",
+ rp.subcode, rp.status, rp.action, rp.available_space);
+
+ return TRUE;
+}
+
+gboolean adapter_le_service_data_scan_filtering (adapter_le_service_data_params_t *params)
+{
+ int ret;
+ adapter_le_vsc_cp_service_data_scan_filter cp;
+ adapter_le_vsc_rp_apcf_set_scan_filter rp;
+ uint8_t *p = cp.data;
+ int cp_len = SERVICE_DATA_SCAN_FILTER_HEADER_SIZE;
+
+ DBG("");
+
+ memset(&cp, 0, sizeof(cp));
+ cp.subcode = SUB_CMD_LE_META_PF_SRVC_DATA;
+ cp.action= params->action;
+ cp.filter_index = params->filter_index;
+
+ memcpy(&cp.data, params->service_data, params->service_data_len);
+ cp_len += params->service_data_len;
+
+ memcpy(p+params->service_data_len, params->service_data_mask,
+ params->service_data_len);
+ cp_len += params->service_data_len;
+
+ ret = send_vsc_command(OCF_BCM_LE_SCAN_FILTER, (uint8_t *) &cp, cp_len,
+ (uint8_t *) &rp, sizeof(rp));
+
+ if (ret < 0)
+ return FALSE;
+
+ if (HCI_SUCCESS != rp.status) {
+ DBG("Fail to send VSC :: sub[%x] - status [%x]", rp.subcode, rp.status);
+ return FALSE;
+ }
+
+ DBG("Scan Filter VSC :: sub[%x] - status [%x] Action [%x] Available space [%x]",
+ rp.subcode, rp.status, rp.action, rp.available_space);
+
+ return TRUE;
+}
+
+gboolean adapter_le_set_scan_filter_data(int client_if, int action,
+ int filt_type, int filter_index,
+ int company_id,
+ int company_id_mask,
+ int uuid_len, uint8_t *p_uuid,
+ int uuid_mask_len, uint8_t *p_uuid_mask,
+ gchar *string, int addr_type,
+ int data_len, uint8_t *p_data,
+ int mask_len, uint8_t *p_mask)
+{
+ gboolean ret;
+
+ DBG("");
+
+ switch (filt_type) {
+ case TYPE_DEVICE_ADDRESS: {
+ /* TYPE_DEVICE_ADDRESS */
+ adapter_le_address_filter_params_t params;
+ bdaddr_t bd_addr;
+
+ str2ba(string, &bd_addr);
+
+ params.action = action;
+ params.filter_index = filter_index;
+ bacpy(&params.broadcaster_addr, &bd_addr);
+ params.bdaddr_type = addr_type;
+
+ ret = adapter_le_service_address_scan_filtering(&params);
+ break;
+ }
+
+ case TYPE_SERVICE_UUID:
+ case TYPE_SOLICIT_UUID: {
+ adapter_le_uuid_params_t params;
+ gboolean is_solicited = (filt_type == TYPE_SOLICIT_UUID) ? TRUE : FALSE;
+
+ if (uuid_len != UUID_16_LEN && uuid_len != UUID_32_LEN
+ && uuid_len != UUID_128_LEN) {
+ DBG("UUID length error");
+ return FALSE;
+ }
+
+ if (uuid_len != uuid_mask_len) {
+ DBG("Both UUID and UUID_MASK length shoule be samed");
+ return FALSE;
+ }
+
+ params.action = action;
+ params.filter_index = filter_index;
+ params.uuid = p_uuid;
+ params.uuid_mask = p_uuid_mask;
+ params.uuid_len = uuid_len;
+
+ ret = adapter_le_service_uuid_scan_filtering(is_solicited, &params);
+ break;
+ }
+
+ case TYPE_LOCAL_NAME: {
+ adapter_le_local_name_params_t params;
+
+ params.action = action;
+ params.filter_index = filter_index;
+ params.local_name = string;
+ params.name_len = strlen(string);
+ ret = adapter_le_local_name_scan_filtering(&params);
+ break;
+ }
+
+ case TYPE_MANUFACTURER_DATA: {
+ adapter_le_manf_data_params_t params;
+
+ if (data_len == 0 || (data_len != mask_len)) {
+ DBG("parameter length error");
+ return FALSE;
+ }
+
+ params.action = action;
+ params.filter_index = filter_index;
+ params.company_id = company_id;
+ params.company_id_mask = company_id_mask;
+ params.man_data = p_data;
+ params.man_data_mask = p_mask;
+ params.man_data_len = data_len;
+
+ ret = adapter_le_manf_data_scan_filtering(&params);
+ break;
+ }
+
+ case TYPE_SERVICE_DATA: {
+ adapter_le_service_data_params_t params;
+
+ if (data_len == 0 || (data_len != mask_len)) {
+ DBG("parameter length error");
+ return FALSE;
+ }
+
+ params.action = action;
+ params.filter_index = filter_index;
+ params.service_data = p_data;
+ params.service_data_mask = p_mask;
+ params.service_data_len = data_len;
+
+ ret = adapter_le_service_data_scan_filtering(&params);
+ break;
+ }
+
+ default:
+ DBG("filter_type error");
+ ret = FALSE;
+ }
+
+ return ret;
+}
+
+gboolean adapter_le_clear_scan_filter_data(int client_if, int filter_index)
+{
+ int ret;
+ adapter_le_vsc_cp_service_data_scan_filter cp;
+ adapter_le_vsc_rp_apcf_set_scan_filter rp;
+
+ DBG("");
+
+ memset(&cp, 0, sizeof(cp));
+ cp.subcode = SUB_CMD_LE_META_PF_FEAT_SEL;
+ cp.action= 0x02; // (Add - 0x00, Delete - 0x01, Clear - 0x02)
+ cp.filter_index = filter_index;
+
+ ret = send_vsc_command(OCF_BCM_LE_SCAN_FILTER, (uint8_t *) &cp, sizeof(cp),
+ (uint8_t *) &rp, sizeof(rp));
+
+ if (ret < 0)
+ return FALSE;
+
+ if (HCI_SUCCESS != rp.status) {
+ DBG("Fail to send VSC :: sub[%x] - status [%x] Available space [%x]",
+ rp.subcode, rp.status, rp.available_space);
+ return FALSE;
+ }
+
+ DBG("Scan Filter VSC :: sub[%x] - status [%x] Action [%x] Available space [%x]",
+ rp.subcode, rp.status, rp.action, rp.available_space);
+ return TRUE;
+}
+
+gboolean adapter_le_enable_offloading(gboolean enable)
+{
+ int ret;
+ adapter_le_vsc_cp_enable_rpa_offload cp;
+ adapter_le_vsc_rp_enable_rpa_offload rp;
+
+ DBG("");
+
+ memset(&cp, 0, sizeof(cp));
+ cp.subcode = SUB_CMD_LE_ENABLE_OFFLOADING;
+ cp.enable = enable;
+
+ ret = send_vsc_command(OCF_BCM_LE_RPA_OFFLOAD, (uint8_t *) &cp, sizeof(cp),
+ (uint8_t *) &rp, sizeof(rp));
+
+ if (ret < 0)
+ return FALSE;
+
+ if (HCI_SUCCESS != rp.status) {
+ DBG("Fail to send VSC :: sub[%x] - status [0x%02x]", rp.subcode, rp.status);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean adapter_le_add_irk_to_list(const uint8_t *le_irk, const bdaddr_t *bdaddr, uint8_t bdaddr_type)
+{
+ int ret;
+ adapter_le_vsc_cp_add_irk_to_list cp;
+ adapter_le_vsc_rp_irk_to_list rp;
+
+ DBG("addr_type %d, irk %x %x %x...", bdaddr_type, le_irk[0], le_irk[1], le_irk[2]);
+
+ memset(&cp, 0, sizeof(cp));
+ cp.subcode = SUB_CMD_LE_ADD_IRK_TO_LIST;
+ memcpy(&cp.le_irk, le_irk, sizeof(cp.le_irk));
+ bacpy(&cp.bdaddr, bdaddr);
+
+ if (bdaddr_type == BDADDR_LE_PUBLIC)
+ cp.bdaddr_type = 0x0;
+ else
+ cp.bdaddr_type = 0x1;
+
+ ret = send_vsc_command(OCF_BCM_LE_RPA_OFFLOAD, (uint8_t *) &cp, sizeof(cp),
+ (uint8_t *) &rp, sizeof(rp));
+
+ if (ret < 0)
+ return FALSE;
+
+ if (HCI_SUCCESS != rp.status) {
+ DBG("Fail to send VSC :: sub[%x] - status [0x%02x]", rp.subcode, rp.status);
+ return FALSE;
+ }
+
+ DBG("Add IRK to VCS :: available space[%d]", rp.available_space);
+
+ return TRUE;
+}
+
+gboolean adapter_le_remove_irk_to_list(const bdaddr_t *bdaddr, uint8_t bdaddr_type)
+{
+ int ret;
+ adapter_le_vsc_cp_remove_irk_to_list cp;
+ adapter_le_vsc_rp_irk_to_list rp;
+
+ DBG("");
+
+ memset(&cp, 0, sizeof(cp));
+ cp.subcode = SUB_CMD_LE_REMOVE_IRK_TO_LIST;
+ bacpy(&cp.bdaddr, bdaddr);
+
+ if (bdaddr_type == BDADDR_LE_PUBLIC)
+ cp.bdaddr_type = 0x0;
+ else
+ cp.bdaddr_type = 0x1;
+
+ ret = send_vsc_command(OCF_BCM_LE_RPA_OFFLOAD, (uint8_t *) &cp, sizeof(cp),
+ (uint8_t *) &rp, sizeof(rp));
+
+ if (ret < 0)
+ return FALSE;
+
+ if (HCI_SUCCESS != rp.status) {
+ DBG("Fail to send VSC :: sub[%x] - status [0x%02x]", rp.subcode, rp.status);
+ return FALSE;
+ }
+
+ DBG("Remove IRK to VCS :: available space[%d]", rp.available_space);
+
+ return TRUE;
+}
+
+
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
diff --git a/src/adapter_le_vsc_features.h b/src/adapter_le_vsc_features.h
new file mode 100755
index 00000000..44a4bcc8
--- /dev/null
+++ b/src/adapter_le_vsc_features.h
@@ -0,0 +1,503 @@
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+
+typedef enum {
+ BLE_ADV_TX_POWER_MIN = 0x00,
+ BLE_ADV_TX_POWER_LOW = 0x01,
+ BLE_ADV_TX_POWER_MID = 0x02,
+ BLE_ADV_TX_POWER_UPPER = 0x03,
+ BLE_ADV_TX_POWER_MAX = 0x04,
+} adapter_le_tx_power_t;
+
+typedef struct {
+ uint8_t inst_id;
+ uint8_t bdaddr_type;
+ bdaddr_t bdaddr;
+} adapter_le_adv_inst_info_t;
+
+typedef struct {
+ uint16_t adv_int_min; /* minimum adv interval */
+ uint16_t adv_int_max; /* maximum adv interval */
+ uint8_t adv_type; /* adv event type (0x00 ~ 0x04) */
+ uint8_t channel_map; /* adv channel map (all channel = 0x07) */
+ uint8_t adv_filter_policy; /* advertising filter policy (0x00 ~ 0x04) */
+ adapter_le_tx_power_t tx_power; /* adv tx power */
+} adapter_le_adv_param_t;
+
+typedef enum {
+ ADD,
+ DELETE,
+ CLEAR,
+} adapter_le_scan_filter_action_type;
+
+typedef enum {
+ ADDR_LE_PUBLIC,
+ ADDR_LE_RANDOM,
+} adapter_vsc_le_addr_type;
+
+typedef enum {
+ TYPE_DEVICE_ADDRESS = 0x01,
+ TYPE_SERVICE_DATA_CHANGED = 0x02,
+ TYPE_SERVICE_UUID = 0x04,
+ TYPE_SOLICIT_UUID = 0x08,
+ TYPE_LOCAL_NAME = 0x10,
+ TYPE_MANUFACTURER_DATA = 0x20,
+ TYPE_SERVICE_DATA = 0x40,
+} adapter_le_scan_filter_type;
+
+#define BDADDR_BREDR 0x00
+#define BDADDR_LE_PUBLIC 0x01
+
+#define BROADCAST_ADDR_FILTER 0x01
+#define SERVICE_DATA_CHANGE_FILTER 0x02
+#define SERVICE_UUID_CHECK 0x04
+#define SERVICE_SOLICITATION_UUID_CHECK 0x08
+#define LOCAL_NAME_CHECK 0x10
+#define MANUFACTURE_DATA_CHECK 0x20
+#define SERVICE_DATA_CHECK 0x40
+
+typedef uint16_t adapter_le_scan_filter_feature_t;
+
+typedef enum {
+ OR,
+ AND,
+} adapter_le_scan_filter_logic_type;
+
+typedef enum {
+ IMMEDIATE,
+ ON_FOUND,
+ BATCHED,
+} adapter_le_scan_filter_delivery_mode;
+
+typedef enum {
+ UUID_16_LEN=2,
+ UUID_32_LEN=4,
+ UUID_128_LEN =16,
+} adapter_le_uuid_len;
+
+typedef struct {
+ adapter_le_scan_filter_action_type action;
+ uint8_t index;
+ adapter_le_scan_filter_feature_t feature;
+ adapter_le_scan_filter_logic_type list_logic_type;
+ adapter_le_scan_filter_logic_type filter_logic_type;
+ uint8_t rssi_high_threshold;
+ adapter_le_scan_filter_delivery_mode delivery_mode;
+ uint16_t onfound_timeout;
+ uint8_t onfound_timeout_cnt;
+ uint8_t rssi_low_threshold;
+ uint16_t onlost_timeout;
+}adapter_le_scan_filter_param_t;
+
+typedef struct {
+ adapter_le_scan_filter_action_type action;
+ uint8_t filter_index;
+ bdaddr_t broadcaster_addr;
+ adapter_vsc_le_addr_type bdaddr_type;
+} adapter_le_address_filter_params_t;
+
+typedef struct {
+ adapter_le_scan_filter_action_type action;
+ uint8_t filter_index;
+ uint8_t *uuid;
+ uint8_t *uuid_mask;
+ adapter_le_uuid_len uuid_len;
+}adapter_le_uuid_params_t;
+
+typedef struct {
+ adapter_le_scan_filter_action_type action;
+ uint8_t filter_index;
+ const char *local_name;
+ uint8_t name_len;
+}adapter_le_local_name_params_t;
+
+typedef struct {
+ adapter_le_scan_filter_action_type action;
+ uint8_t filter_index;
+ uint16_t company_id;
+ uint16_t company_id_mask;
+ uint8_t *man_data;
+ uint8_t *man_data_mask;
+ uint8_t man_data_len;
+}adapter_le_manf_data_params_t;
+
+typedef struct {
+ adapter_le_scan_filter_action_type action;
+ uint8_t filter_index;
+ uint8_t *service_data;
+ uint8_t *service_data_mask;
+ uint8_t service_data_len;
+}adapter_le_service_data_params_t;
+
+/*****************************************************************************
+** Defentions for HCI Error Codes that are past in the events
+*/
+#define HCI_SUCCESS 0x00
+
+
+
+/*****************************************************************************
+** Vendor Specific Commands
+**
+*/
+
+#define OCF_BCM_LE_GET_VENDOR_CAP 0x0153 /* LE Get Vendor Capabilities */
+
+#define OCF_BCM_LE_MULTI_ADV 0x0154 /* Multi adv OCF */
+
+/* subcode for multi adv feature */
+#define SUB_CMD_LE_MULTI_ADV_SET_PARAM 0x01
+#define SUB_CMD_LE_MULTI_ADV_WRITE_ADV_DATA 0x02
+#define SUB_CMD_LE_MULTI_ADV_WRITE_SCAN_RSP_DATA 0x03
+#define SUB_CMD_LE_MULTI_ADV_SET_RANDOM_ADDR 0x04
+#define SUB_CMD_LE_MULTI_ADV_ENB 0x05
+
+/* APCF : Advertising Packet Content Filter feature */
+#define OCF_BCM_LE_SCAN_FILTER 0x0157 /* Advertising filter OCF */
+
+/* Sub codes for APCF */
+#define SUB_CMD_LE_META_PF_ENABLE 0x00
+#define SUB_CMD_LE_META_PF_FEAT_SEL 0x01
+#define SUB_CMD_LE_META_PF_ADDR 0x02
+#define SUB_CMD_LE_META_PF_UUID 0x03
+#define SUB_CMD_LE_META_PF_SOL_UUID 0x04
+#define SUB_CMD_LE_META_PF_LOCAL_NAME 0x05
+#define SUB_CMD_LE_META_PF_MANU_DATA 0x06
+#define SUB_CMD_LE_META_PF_SRVC_DATA 0x07
+#define SUB_CMD_LE_META_PF_ALL 0x08
+
+/* Offloaded resolution of private address */
+#define OCF_BCM_LE_RPA_OFFLOAD 0x0155 /* RPA Offload OCF */
+
+/* subcode for rpa offloading feature */
+#define SUB_CMD_LE_ENABLE_OFFLOADING 0x01
+#define SUB_CMD_LE_ADD_IRK_TO_LIST 0x02
+#define SUB_CMD_LE_REMOVE_IRK_TO_LIST 0x03
+#define SUB_CMD_LE_CLEAR_IRK_TO_LIST 0x04
+#define SUB_CMD_LE_READ_IRK_TO_LIST 0x05
+
+/*****************************************************************************
+** CP & RP for OCF_BCM_LE_GET_VENDOR_CAP
+**
+*/
+
+/**
+*
+* RP
+*
+* (1 octet) status : Command complete status
+* (1 octet) adv_inst_max : Num of advertisement instances supported
+* (1 octet) rpa_offloading: BT chip capability of RPA
+* (value 0 not capable, value 1 capable)
+* If supported by chip, it needs enablement by host
+* (2 octet) tot_scan_results_strg : Storage for scan results in bytes
+* (1 octet) max_irk_list_sz : Num of IRK entries supported in f/w
+* (1 octet) filter_support : Support Filtering in controller.
+* 0 = Not supported / 1 = supported
+* (1 octet) max_filter : Number of filters supported
+* (1 octet) energy_support : Supports reporting of activity and energy info
+* 0 = not capable, 1 = capable
+* (1 octet) onlost_follow : Number of advertisers that can be analysed
+* for onlost per filter
+*/
+typedef struct {
+ uint8_t status;
+ uint8_t adv_inst_max;
+ uint8_t rpa_offloading;
+ uint16_t tot_scan_results_strg;
+ uint8_t max_irk_list_sz;
+ uint8_t filter_support;
+ uint8_t max_filter;
+ uint8_t energy_support;
+ uint8_t onlost_follow;
+} __attribute__ ((packed)) apater_le_vsc_rp_get_vendor_cap;
+
+
+
+/*****************************************************************************
+** CP & RP for OCF_BCM_LE_MULTI_ADV
+**
+*/
+
+/**
+*
+* CP for OCF_BCM_LE_MULTI_ADV & SUB_CMD_LE_MULTI_ADV_SET_PARAM
+*
+* (1 octet) subcode : SUB_CMD_LE_MULTI_ADV_SET_PARAM
+* (2 octet) adv_int_min : per spec
+* (2 octet) adv_int_max : per spec
+* (1 octet) adv_type : per spec
+* (1 octet) bdaddr_type : per spec
+* (6 octet) bdaddr : per spec
+* (1 octet) direct_bdaddr_type : per spec
+* (6 octet) direct_bdaddr : per spec
+* (1 octet) channel_map : per spec
+* (1 octet) adv_filter_policy : per spec
+* (1 octet) inst_id : Specifies the applicability of the above parameters to an instance
+* (1 octet) tx_power : Transmit_Power Unit - in dBm (signed integer) Range (70 to +20)
+*/
+typedef struct {
+ uint8_t subcode;
+ uint16_t adv_int_min;
+ uint16_t adv_int_max;
+ uint8_t adv_type;
+ uint8_t bdaddr_type;
+ bdaddr_t bdaddr;
+ uint8_t direct_bdaddr_type;
+ bdaddr_t direct_bdaddr;
+ uint8_t channel_map;
+ uint8_t adv_filter_policy;
+ uint8_t inst_id;
+ uint8_t tx_power;
+} __attribute__ ((packed)) adapter_le_vsc_cp_set_multi_adv_params;
+
+/**
+*
+* CP for SUB_CMD_LE_MULTI_ADV_WRITE_ADV_DATA
+* CP for SUB_CMD_LE_MULTI_ADV_WRITE_SCAN_RSP_DATA
+*
+* ( 1 octet) subcode : SUB_CMD_LE_MULTI_ADV_WRITE_ADV_DATA
+* or SUB_CMD_LE_MULTI_ADV_WRITE_SCAN_RSP_DATA
+* ( 1 octet) data_len : per spec
+* (31 octet) data : per spec
+* ( 1 octet) inst_id : Specifies the applicability of the above parameters to an instance.
+*/
+typedef struct {
+ uint8_t subcode;
+ uint8_t data_len;
+ uint8_t data[31];
+ uint8_t inst_id;
+} __attribute__ ((packed)) adapter_le_vsc_cp_set_multi_adv_data;
+
+/**
+*
+* CP for SUB_CMD_LE_MULTI_ADV_ENB
+*
+* (1 octet) subcode : SUB_CMD_LE_MULTI_ADV_ENB
+* (1 octet) enable : When set to 1, it means enable, otherwise disable.
+* (1 octet) inst_id : Specifies the applicability of the above parameters
+* to an instance. Instance 0 has special meaning this
+* refers to std HCI instance.
+*/
+typedef struct {
+ uint8_t subcode;
+ uint8_t enable;
+ uint8_t inst_id;
+} __attribute__ ((packed)) adapter_le_vsc_cp_enable_multi_adv;
+
+/**
+*
+* RP
+*
+* (1 octet) status : Command complete status
+* (1 octet) subcode : subcode of OCF_BCM_LE_MULTI_ADV
+*/
+typedef struct {
+ uint8_t status;
+ uint8_t subcode;
+} __attribute__ ((packed)) apater_le_vsc_rp_multi_adv;
+
+
+
+/*****************************************************************************
+** CP & RP for OCF_BCM_LE_SCAN_FILTER
+**
+*/
+
+
+/* CP for SUB_CMD_LE_META_PF_ENABLE */
+typedef struct {
+ uint8_t subcode;
+ uint8_t enable;
+} __attribute__ ((packed)) adapter_le_vsc_cp_enable_scan_filter;
+
+/* RP for SUB_CMD_LE_META_PF_ENABLE */
+typedef struct {
+ uint8_t status;
+ uint8_t subcode;
+ uint8_t enable;
+} __attribute__ ((packed)) apater_le_vsc_rp_enable_scan_filter;
+
+/* CP for SUB_CMD_LE_META_PF_FEAT_SEL */
+typedef struct {
+ uint8_t subcode;
+ uint8_t action;
+ uint8_t filter_index;
+ uint16_t feature;
+ uint16_t feature_list_logic;
+ uint8_t filter_logic;
+ int8_t rssi_high_threshold;
+ uint8_t delivery_mode;
+ uint16_t onfound_timeout;
+ uint8_t onfound_timeout_cnt;
+ uint8_t rssi_low_thresh;
+ uint16_t onlost_timeout;
+} __attribute__ ((packed)) adapter_le_vsc_cp_apcf_set_filter_params;
+
+/* CP for SUB_CMD_LE_META_PF_ADDR */
+typedef struct {
+ uint8_t subcode;
+ uint8_t action;
+ uint8_t filter_index;
+ bdaddr_t bdaddr;
+ uint8_t bdaddr_type;
+} __attribute__ ((packed))adapter_le_vsc_cp_address_scan_filter;
+
+/* CP for SUB_CMD_LE_META_PF_UUID & SUB_CMD_LE_META_PF_SOL_UUID */
+typedef struct {
+ uint8_t subcode;
+ uint8_t action;
+ uint8_t filter_index;
+ uint8_t data[40]; /* UUID + UUID_MASK */
+} __attribute__ ((packed))adapter_le_vsc_cp_service_uuid_scan_filter;
+#define UUID_SCAN_FILTER_HEADER_SIZE 3
+
+#define SCAN_FILTER_DATA_MAX_LEN 29
+
+/* CP for SUB_CMD_LE_META_PF_LOCAL_NAME*/
+typedef struct {
+ uint8_t subcode;
+ uint8_t action;
+ uint8_t filter_index;
+ uint8_t name[SCAN_FILTER_DATA_MAX_LEN];
+} __attribute__ ((packed)) adapter_le_vsc_cp_local_name_scan_filter;
+#define NAME_SCAN_FILTER_HEADER_SIZE 3
+
+/* CP for SUB_CMD_LE_META_PF_MANU_DATA*/
+typedef struct {
+ uint8_t subcode;
+ uint8_t action;
+ uint8_t filter_index;
+ uint8_t data[SCAN_FILTER_DATA_MAX_LEN * 2]; /* data + mask filed */
+} __attribute__ ((packed)) adapter_le_vsc_cp_manf_data_scan_filter;
+#define MANF_DATA_SCAN_FILTER_HEADER_SIZE 3
+
+/* CP for SUB_CMD_LE_META_PF_SRVC_DATA*/
+typedef struct {
+ uint8_t subcode;
+ uint8_t action;
+ uint8_t filter_index;
+ uint8_t data[SCAN_FILTER_DATA_MAX_LEN * 2]; /* data + mask filed */
+} __attribute__ ((packed)) adapter_le_vsc_cp_service_data_scan_filter;
+#define SERVICE_DATA_SCAN_FILTER_HEADER_SIZE 3
+
+/* RP for SUB_CMD_LE_META_PF_ADDR & SUB_CMD_LE_META_PF_FEAT_SEL &
+ SUB_CMD_LE_META_PF_UUID & SUB_CMD_LE_META_PF_SOL_UUID &
+ SUB_CMD_LE_META_PF_LOCAL_NAME & SUB_CMD_LE_META_PF_MANU_DATA &
+ SUB_CMD_LE_META_PF_SRVC_DATA */
+typedef struct {
+ uint8_t status;
+ uint8_t subcode;
+ uint8_t action;
+ uint8_t available_space;
+} __attribute__ ((packed)) adapter_le_vsc_rp_apcf_set_scan_filter;
+
+
+/*****************************************************************************
+** CP & RP for OCF_BCM_LE_RPA_OFFLOAD
+**
+*/
+
+/**
+*
+* CP for SUB_CMD_ENABLE_RPA_OFFLOAD
+*
+* (1 octet) subcode : SUB_CMD_ENABLE_RPA_OFFLOAD (0x01)
+* (2 octet) enable : When set to 1, it means enable, otherwise disable.
+*/
+typedef struct {
+ uint8_t subcode;
+ uint8_t enable;
+} __attribute__ ((packed)) adapter_le_vsc_cp_enable_rpa_offload;
+
+/* RP for SUB_CMD_ENABLE_RPA_OFFLOAD */
+typedef struct {
+ uint8_t status;
+ uint8_t subcode;
+} __attribute__ ((packed)) adapter_le_vsc_rp_enable_rpa_offload;
+
+/**
+*
+* CP for SUB_CMD_ADD_IRK_TO_LIST
+*
+* (1 octet) subcode : SUB_CMD_ADD_IRK_TO_LIST (0x02)
+* (16 octet) le_irk : LE IRK (1st byte LSB)
+* (1 octet) bdaddr_type : per spec
+* (6 octet) bdaddr : per spec
+*/
+typedef struct {
+ uint8_t subcode;
+ uint8_t le_irk[16];
+ uint8_t bdaddr_type;
+ bdaddr_t bdaddr;
+} __attribute__ ((packed)) adapter_le_vsc_cp_add_irk_to_list;
+
+/**
+*
+* CP for SUB_CMD_REMOVE_IRK_TO_LIST
+*
+* (1 octet) subcode : SUB_CMD_REMOVE_IRK_TO_LIST (0x03)
+* (16 octet) le_irk : LE IRK (1st byte LSB)
+* (1 octet) bdaddr_type : per spec
+* (6 octet) bdaddr : per spec
+*/
+typedef struct {
+ uint8_t subcode;
+ uint8_t bdaddr_type;
+ bdaddr_t bdaddr;
+} __attribute__ ((packed)) adapter_le_vsc_cp_remove_irk_to_list;
+
+/* RP for SUB_CMD_ADD_IRK_TO_LIST & SUB_CMD_REMOVE_IRK_TO_LIST */
+typedef struct {
+ uint8_t status;
+ uint8_t subcode;
+ uint8_t available_space;
+} __attribute__ ((packed)) adapter_le_vsc_rp_irk_to_list;
+
+
+/*****************************************************************************
+** Functions
+**
+*/
+
+/* Read supported BLE feature info from chipset */
+gboolean adapter_le_read_ble_feature_info(void);
+
+gboolean adapter_le_is_supported_multi_advertising(void);
+
+gboolean adapter_le_is_supported_offloading(void);
+
+int adapter_le_get_max_adv_instance(void);
+
+int adapter_le_get_scan_filter_size(void);
+
+gboolean adapter_le_set_multi_adv_params (adapter_le_adv_inst_info_t *p_inst,
+ adapter_le_adv_param_t *p_params);
+
+gboolean adapter_le_set_multi_adv_data(uint8_t inst_id, gboolean is_scan_rsp,
+ uint8_t data_len, uint8_t *p_data);
+
+gboolean adapter_le_enable_multi_adv (struct btd_adapter *adapter,
+ gboolean enable, uint8_t inst_id);
+
+gboolean adapter_le_enable_scan_filtering (gboolean enable);
+
+gboolean adapter_le_set_scan_filter_params(adapter_le_scan_filter_param_t *params);
+
+gboolean adapter_le_set_scan_filter_data(int client_if, int action,
+ int filt_type, int filter_index,
+ int company_id,
+ int company_id_mask,
+ int uuid_len, uint8_t *p_uuid,
+ int uuid_mask_len, uint8_t *p_uuid_mask,
+ gchar *string, int addr_type,
+ int data_len, uint8_t *p_data,
+ int mask_len, uint8_t *p_mask);
+gboolean adapter_le_clear_scan_filter_data(int client_if, int filter_index);
+
+gboolean adapter_le_enable_offloading(gboolean enable);
+
+gboolean adapter_le_add_irk_to_list(const uint8_t *le_irk, const bdaddr_t *bdaddr, uint8_t bdaddr_type);
+
+gboolean adapter_le_remove_irk_to_list(const bdaddr_t *bdaddr, uint8_t bdaddr_type);
+
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
diff --git a/src/attio.h b/src/attio.h
new file mode 100755
index 00000000..16e28732
--- /dev/null
+++ b/src/attio.h
@@ -0,0 +1,33 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ * Copyright (C) 2011 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+typedef void (*attio_connect_cb) (GAttrib *attrib, gpointer user_data);
+typedef void (*attio_disconnect_cb) (gpointer user_data);
+
+guint btd_device_add_attio_callback(struct btd_device *device,
+ attio_connect_cb cfunc,
+ attio_disconnect_cb dcfunc,
+ gpointer user_data);
+
+gboolean btd_device_remove_attio_callback(struct btd_device *device, guint id);
diff --git a/src/attrib-server.c b/src/attrib-server.c
index 4439c27a..de6919d4 100755
--- a/src/attrib-server.c
+++ b/src/attrib-server.c
@@ -255,6 +255,37 @@ static int attribute_cmp(gconstpointer a1, gconstpointer a2)
return attrib1->handle - attrib2->handle;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static int attribute_uuid_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct attribute *attrib1 = a;
+ const bt_uuid_t *uuid = b;
+
+ return bt_uuid_cmp(&attrib1->uuid, uuid);
+}
+
+struct attribute *attribute_find(struct btd_adapter *adapter, const bt_uuid_t *uuid)
+{
+ GSList *l;
+ GList *ldata;
+ struct gatt_server *server;
+
+ /* Find the attrib server database for the given adapter */
+ l = g_slist_find_custom(servers, adapter, adapter_cmp);
+ if (!l)
+ return NULL;
+
+ server = l->data;
+
+ ldata = g_list_find_custom(server->database, GUINT_TO_POINTER(uuid),
+ attribute_uuid_cmp);
+ if (!ldata)
+ return NULL;
+
+ return ldata->data;
+}
+#endif
+
static struct attribute *find_svc_range(struct gatt_server *server,
uint16_t start, uint16_t *end)
{
@@ -306,6 +337,10 @@ static uint32_t attrib_create_sdp_new(struct gatt_server *server,
if (a->len == 2)
sdp_uuid16_create(&svc, get_le16(a->data));
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ else if (a->len == 4)
+ sdp_uuid32_create(&svc, get_le32(a->data));
+#endif
else if (a->len == 16) {
uint8_t be128[16];
@@ -320,14 +355,20 @@ static uint32_t attrib_create_sdp_new(struct gatt_server *server,
return 0;
if (name != NULL)
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ sdp_set_info_attr(record, name, "Samsung", NULL);
+#else
sdp_set_info_attr(record, name, "BlueZ", NULL);
+#endif
sdp_uuid16_create(&gap_uuid, GENERIC_ACCESS_PROFILE_ID);
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
if (sdp_uuid_cmp(&svc, &gap_uuid) == 0) {
sdp_set_url_attr(record, "http://www.bluez.org/",
"http://www.bluez.org/",
"http://www.bluez.org/");
}
+#endif
if (adapter_service_add(server->adapter, record) == 0)
return record->handle;
@@ -498,6 +539,10 @@ static uint16_t read_by_group(struct gatt_channel *channel, uint16_t start,
if (dl == NULL)
cur->end = a->handle;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ else if (a->handle == end && end == 0xffff)
+ cur->end = a->handle;
+#endif
else
cur->end = last_handle;
@@ -936,6 +981,35 @@ static uint16_t write_value(struct gatt_channel *channel, uint16_t handle,
g_file_set_contents(filename, data, length, NULL);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ g_free(filename);
+ filename = btd_device_get_storage_path(channel->device, "ccc_sc");
+ if (!filename) {
+ warn("Unable to get ccc storage path for device");
+ g_free(data);
+ return enc_error_resp(ATT_OP_WRITE_REQ, handle,
+ ATT_ECODE_WRITE_NOT_PERM,
+ pdu, len);
+ }
+
+ g_key_file_free(key_file);
+ key_file = g_key_file_new();
+ g_key_file_load_from_file(key_file, filename, 0, NULL);
+
+ memset(&group, 0x00, 6);
+ memset(&value, 0x00, 5);
+ sprintf(group, "%hu", handle);
+ sprintf(value, "%hX", cccval);
+ g_key_file_set_string(key_file, group, "Value", value);
+
+ g_free(data);
+ data = g_key_file_to_data(key_file, &length, NULL);
+ if (length > 0) {
+ create_file(filename, S_IRUSR | S_IWUSR);
+ g_file_set_contents(filename, data, length, NULL);
+ }
+#endif
+
g_free(data);
g_free(filename);
g_key_file_free(key_file);
@@ -1066,6 +1140,12 @@ static void channel_handler(const uint8_t *ipdu, uint16_t len,
}
length = find_info(channel, start, end, opdu, channel->mtu);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (length == 0 && start == end) {
+ status = ATT_ECODE_ATTR_NOT_FOUND;
+ goto done;
+ }
+#endif
break;
case ATT_OP_WRITE_REQ:
length = dec_write_req(ipdu, len, &start, value, &vlen);
@@ -1289,6 +1369,9 @@ static gboolean register_core_services(struct gatt_server *server)
uint8_t atval[256];
bt_uuid_t uuid;
uint16_t appearance = 0x0000;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ uint16_t service_changed_handle;
+#endif
/* GAP service: primary service definition */
bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
@@ -1337,6 +1420,29 @@ static gboolean register_core_services(struct gatt_server *server)
attrib_db_add_new(server, 0x0010, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
atval, 2);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ /* GATT service: service changed characteristic */
+ service_changed_handle = 0x0012;
+ bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
+
+ atval[0] = GATT_CHR_PROP_INDICATE;
+ put_le16(service_changed_handle, &atval[1]);
+ put_le16(GATT_CHARAC_SERVICE_CHANGED, &atval[3]);
+
+ attrib_db_add_new(server, 0x0011, &uuid, ATT_NONE, ATT_NOT_PERMITTED,
+ atval, 5);
+
+ /* GATT service: service changed attribute */
+ bt_uuid16_create(&uuid, GATT_CHARAC_SERVICE_CHANGED);
+ attrib_db_add_new(server, service_changed_handle, &uuid, ATT_NOT_PERMITTED,
+ ATT_NOT_PERMITTED, NULL, 0);
+
+ bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
+ atval[0] = GATT_CHR_PROP_READ | GATT_CHR_PROP_WRITE;
+ atval[1] = 0;
+ attrib_db_add_new(server, 0x0013, &uuid, ATT_NONE, ATT_NONE, atval, 2);
+#endif
+
server->gatt_sdp_handle = attrib_create_sdp_new(server, 0x0010,
"Generic Attribute Profile");
if (server->gatt_sdp_handle == 0) {
@@ -1623,6 +1729,97 @@ int attrib_db_del(struct btd_adapter *adapter, uint16_t handle)
return 0;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+uint16_t send_sc_indication(uint16_t start_handle, uint16_t end_handle, size_t vlen,
+ uint8_t *pdu, size_t len)
+{
+ const uint16_t min_len = sizeof(pdu[0]) + sizeof(uint16_t);
+
+ if (pdu == NULL)
+ return 0;
+
+ if (len < (vlen + min_len))
+ return 0;
+
+ pdu[0] = ATT_OP_HANDLE_IND;
+/* API replaced by put_le16 in bluez 5.25
+ att_put_u16(start_handle, &pdu[1]);
+ att_put_u16(end_handle, &pdu[3]);*/
+ put_le16(start_handle, &pdu[1]);
+ put_le16(end_handle, &pdu[3]);
+
+ return vlen + min_len;
+}
+
+static uint8_t attrib_get_ccc_info(struct btd_device *device, uint16_t handle)
+{
+ uint16_t cccval = 0;
+ char *filename;
+ GKeyFile *key_file;
+ char group[6];
+ char *value;
+
+ filename = btd_device_get_storage_path(device, "ccc");
+ if (!filename) {
+ warn("Unable to get ccc storage path for device");
+ return 0;
+ }
+
+ key_file = g_key_file_new();
+ g_key_file_load_from_file(key_file, filename, 0, NULL);
+ sprintf(group, "%hu", handle);
+
+ /* Get the CCC value */
+ value = g_key_file_get_string(key_file, group, "Value", NULL);
+ if (!value)
+ return 0;
+
+ sscanf(value, "%hX", &cccval);
+
+ g_free(value);
+ g_free(filename);
+ g_key_file_free(key_file);
+
+ return cccval;
+}
+
+void attrib_send_sc_ind(struct btd_device *device, GAttrib *attrib,
+ uint16_t start_handle, uint16_t end_handle,
+ size_t vlen)
+{
+ size_t length = 0;
+ uint8_t *pdu;
+ size_t mtu;
+
+ pdu = g_attrib_get_buffer(attrib, &mtu);
+ length = send_sc_indication(start_handle, end_handle, vlen, pdu, mtu);
+ g_attrib_send(attrib, 0, pdu, length, NULL, NULL, NULL);
+}
+
+void attrib_send_noty_ind(struct btd_device *device, GAttrib *attrib,
+ uint16_t handle, uint16_t desc_handle,
+ uint8_t *value, size_t vlen)
+{
+ size_t length = 0;
+ uint16_t cccval;
+ uint8_t *pdu;
+ size_t mtu;
+
+ cccval = attrib_get_ccc_info(device, desc_handle);
+ if (!cccval)
+ return;
+
+ pdu = g_attrib_get_buffer(attrib, &mtu);
+ if (cccval == GATT_CLIENT_CHARAC_CFG_NOTIF_BIT) {
+ length = enc_notification(handle, value, vlen, pdu, mtu);
+ g_attrib_send(attrib, 0, pdu, length, NULL, NULL, NULL);
+ } else if (cccval == GATT_CLIENT_CHARAC_CFG_IND_BIT) {
+ length = enc_indication(handle, value, vlen, pdu, mtu);
+ g_attrib_send(attrib, 0, pdu, length, NULL, NULL, NULL);
+ }
+}
+#endif
+
int attrib_gap_set(struct btd_adapter *adapter, uint16_t uuid,
const uint8_t *value, size_t len)
{
diff --git a/src/attrib-server.h b/src/attrib-server.h
index 063cb662..2a951e4e 100755
--- a/src/attrib-server.h
+++ b/src/attrib-server.h
@@ -40,3 +40,15 @@ void attrib_free_sdp(struct btd_adapter *adapter, uint32_t sdp_handle);
GAttrib *attrib_from_device(struct btd_device *device);
guint attrib_channel_attach(GAttrib *attrib);
gboolean attrib_channel_detach(GAttrib *attrib, guint id);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+struct attribute *attribute_find(struct btd_adapter *adapter, const bt_uuid_t *uuid);
+void attrib_send_noty_ind(struct btd_device *device, GAttrib *attrib,
+ uint16_t handle, uint16_t desc_handle,
+ uint8_t *value, size_t vlen);
+uint16_t send_sc_indication(uint16_t handle, uint16_t end_handle, size_t vlen,
+ uint8_t *pdu, size_t len);
+
+void attrib_send_sc_ind(struct btd_device *device, GAttrib *attrib,
+ uint16_t start_handle, uint16_t end_handle,
+ size_t vlen);
+#endif
diff --git a/src/bluetooth.conf b/src/bluetooth.conf
index 10d2d368..2add5d78 100755
--- a/src/bluetooth.conf
+++ b/src/bluetooth.conf
@@ -10,12 +10,40 @@
<policy user="root">
<allow own="org.bluez"/>
<allow send_destination="org.bluez"/>
+ <allow own="org.projectx.bluetooth"/>
+ <allow send_interface="org.projectx.bluetooth"/>
+ <allow send_destination="org.projectx.bluetooth"/>
+ <allow send_interface="org.projectx.bt_event"/>
+ <allow send_destination="org.projectx.bt_event"/>
+ <allow own="org.bluez.frwk_agent"/>
+ <allow send_interface="org.bluez.frwk_agent"/>
+ <allow send_destination="org.bluez.frwk_agent"/>
+ <allow own="org.bluez.Agent1"/>
<allow send_interface="org.bluez.Agent1"/>
+ <allow send_destination="org.bluez.Agent1"/>
+ <allow own="org.bluez.Adapter1"/>
+ <allow send_interface="org.bluez.Adapter1"/>
+ <allow send_destination="org.bluez.Adapter1"/>
+ <allow own="org.bluez.Manager"/>
+ <allow send_interface="org.bluez.Manager"/>
+ <allow send_destination="org.bluez.Manager"/>
+ <allow own="org.bluez.Device1"/>
+ <allow send_interface="org.bluez.Device1"/>
+ <allow send_destination="org.bluez.Device1"/>
+ <allow own="org.bluez.MediaEndpoint1"/>
<allow send_interface="org.bluez.MediaEndpoint1"/>
+ <allow send_destination="org.bluez.MediaEndpoint1"/>
+ <allow own="org.bluez.MediaPlayer1"/>
<allow send_interface="org.bluez.MediaPlayer1"/>
<allow send_interface="org.bluez.ThermometerWatcher1"/>
<allow send_interface="org.bluez.AlertAgent1"/>
+ <allow send_destination="org.bluez.MediaPlayer1"/>
+ <allow own="org.bluez.MediaTransport1"/>
+ <allow send_interface="org.bluez.MediaTransport1"/>
+ <allow send_destination="org.bluez.MediaTransport1"/>
+ <allow own="org.bluez.Profile1"/>
<allow send_interface="org.bluez.Profile1"/>
+ <allow send_destination="org.bluez.Profile1"/>
<allow send_interface="org.bluez.HeartRateWatcher1"/>
<allow send_interface="org.bluez.CyclingSpeedWatcher1"/>
<allow send_interface="org.bluez.GattCharacteristic1"/>
@@ -28,14 +56,91 @@
<allow send_destination="org.bluez"/>
</policy>
- <!-- allow users of lp group (printing subsystem) to
+ <!-- allow users of bt_use group (Tizen BT group) to
+ communicate with bluetoothd -->
+ <policy group="bt_use">
+ <allow send_interface="org.freedesktop.DBus.ObjectManager"/>
+ <allow send_destination="org.bluez"/>
+ <allow send_interface="org.projectx.bluetooth"/>
+ <allow send_destination="org.projectx.bluetooth"/>
+ <allow send_interface="org.projectx.bt_event"/>
+ <allow send_destination="org.projectx.bt_event"/>
+ <allow send_interface="org.bluez.frwk_agent"/>
+ <allow send_destination="org.bluez.frwk_agent"/>
+ <allow send_interface="org.bluez.Agent1"/>
+ <allow send_destination="org.bluez.Agent1"/>
+ <allow send_interface="org.bluez.Adapter1"/>
+ <allow send_destination="org.bluez.Adapter1"/>
+ <allow send_interface="org.bluez.Manager"/>
+ <allow send_destination="org.bluez.Manager"/>
+ <allow send_interface="org.bluez.Device1"/>
+ <allow send_destination="org.bluez.Device1"/>
+ <allow send_interface="org.bluez.MediaEndpoint1"/>
+ <allow send_destination="org.bluez.MediaEndpoint1"/>
+ <allow send_interface="org.bluez.MediaTransport1"/>
+ <allow send_destination="org.bluez.MediaTransport1"/>
+ <allow send_interface="org.bluez.MediaPlayer1"/>
+ <allow send_destination="org.bluez.MediaPlayer1"/>
+ <allow send_interface="org.bluez.Profile1"/>
+ <allow send_destination="org.bluez.Profile1"/>
+ </policy>
+
+ <!-- allow users of system group (Tizen BT group) to
+ communicate with bluetoothd -->
+ <policy group="users">
+ <allow send_interface="org.freedesktop.DBus.ObjectManager"/>
+ <allow send_destination="org.bluez"/>
+ <allow send_interface="org.projectx.bluetooth"/>
+ <allow send_destination="org.projectx.bluetooth"/>
+ <allow send_interface="org.projectx.bt_event"/>
+ <allow send_destination="org.projectx.bt_event"/>
+ <allow send_interface="org.bluez.frwk_agent"/>
+ <allow send_destination="org.bluez.frwk_agent"/>
+ <allow send_interface="org.bluez.Agent1"/>
+ <allow send_destination="org.bluez.Agent1"/>
+ <allow send_interface="org.bluez.Adapter1"/>
+ <allow send_destination="org.bluez.Adapter1"/>
+ <allow send_interface="org.bluez.Manager"/>
+ <allow send_destination="org.bluez.Manager"/>
+ <allow send_interface="org.bluez.Device1"/>
+ <allow send_destination="org.bluez.Device1"/>
+ <allow send_interface="org.bluez.MediaEndpoint1"/>
+ <allow send_destination="org.bluez.MediaEndpoint1"/>
+ <allow send_interface="org.bluez.MediaTransport1"/>
+ <allow send_destination="org.bluez.MediaTransport1"/>
+ <allow send_interface="org.bluez.MediaPlayer1"/>
+ <allow send_destination="org.bluez.MediaPlayer1"/>
+ <allow send_interface="org.bluez.Profile1"/>
+ <allow send_destination="org.bluez.Profile1"/>
+ </policy>
+
+ <!-- allow users of lp group (printing subsystem) to
communicate with bluetoothd -->
<policy group="lp">
<allow send_destination="org.bluez"/>
</policy>
<policy context="default">
- <deny send_destination="org.bluez"/>
+ <deny send_interface="org.projectx.bluetooth"/>
+ <deny send_destination="org.projectx.bluetooth"/>
+ <deny send_interface="org.bluez.frwk_agent"/>
+ <deny send_destination="org.bluez.frwk_agent"/>
+ <deny send_interface="org.bluez.Agent1"/>
+ <deny send_destination="org.bluez.Agent1"/>
+ <deny send_interface="org.bluez.Adapter1"/>
+ <deny send_destination="org.bluez.Adapter1"/>
+ <deny send_interface="org.bluez.Manager"/>
+ <deny send_destination="org.bluez.Manager"/>
+ <deny send_interface="org.bluez.Device1"/>
+ <deny send_destination="org.bluez.Device1"/>
+ <deny send_interface="org.bluez.MediaEndpoint1"/>
+ <deny send_destination="org.bluez.MediaEndpoint1"/>
+ <deny send_interface="org.bluez.MediaTransport1"/>
+ <deny send_destination="org.bluez.MediaTransport1"/>
+ <deny send_interface="org.bluez.MediaPlayer1"/>
+ <deny send_destination="org.bluez.MediaPlayer1"/>
+ <deny send_interface="org.bluez.Profile1"/>
+ <deny send_destination="org.bluez.Profile1"/>
</policy>
</busconfig>
diff --git a/src/bluetooth.service.in b/src/bluetooth.service.in
index f799f65f..e41c2da4 100755
--- a/src/bluetooth.service.in
+++ b/src/bluetooth.service.in
@@ -1,20 +1,17 @@
[Unit]
Description=Bluetooth service
Documentation=man:bluetoothd(8)
-ConditionPathIsDirectory=/sys/class/bluetooth
[Service]
Type=dbus
BusName=org.bluez
-ExecStart=@libexecdir@/bluetoothd
+ExecStart=/usr/libexec/bluetooth/bluetoothd -d -C
NotifyAccess=main
-#WatchdogSec=10
-#Restart=on-failure
CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE
LimitNPROC=1
ProtectHome=true
ProtectSystem=full
[Install]
-WantedBy=bluetooth.target
+WantedBy=multi-user.target
Alias=dbus-org.bluez.service
diff --git a/src/device.c b/src/device.c
index 99454a78..217e4023 100755
--- a/src/device.c
+++ b/src/device.c
@@ -75,6 +75,13 @@
#include "attrib-server.h"
#include "eir.h"
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#include "sdp-xml.h"
+#ifdef TIZEN_FEATURE_BLUEZ_BATTERY_WATCH
+#include <sys/ioctl.h>
+#endif /* TIZEN_FEATURE_BLUEZ_BATTERY_WATCH */
+#endif
+
#define IO_CAPABILITY_NOINPUTNOOUTPUT 0x03
#define DISCONNECT_TIMER 2
@@ -92,6 +99,10 @@
#define GATT_INCLUDE_UUID_STR "2802"
#define GATT_CHARAC_UUID_STR "2803"
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define DEV_MAX_MANUFACTURER_DATA_LEN 248
+#endif
+
static DBusConnection *dbus_conn = NULL;
static unsigned service_state_cb_id;
@@ -159,6 +170,14 @@ struct svc_callback {
void *user_data;
};
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+struct le_adv_report_info {
+ uint8_t flags;
+ char manufacturer_data[DEV_MAX_MANUFACTURER_DATA_LEN];
+ uint8_t manufacturer_data_len;
+};
+#endif
+
/* Per-bearer (LE or BR/EDR) device state */
struct bearer_state {
bool paired;
@@ -172,6 +191,33 @@ struct csrk_info {
uint32_t counter;
};
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+typedef enum {
+ DEV_PAIRED_NONE = 0,
+ DEV_PAIRED_BREDR = 1,
+ DEV_PAIRED_LE,
+ DEV_PAIRED_BREDR_LE,
+} dev_paired_state;
+
+typedef enum {
+ DEV_CONNECTED_NONE = 0,
+ DEV_CONNECTED_BREDR = 1,
+ DEV_CONNECTED_LE,
+ DEV_CONNECTED_BREDR_LE,
+} dev_connected_state;
+
+struct trusted_profile_t {
+ uint32_t pbap:2;
+ uint32_t map:2;
+ uint32_t sap:2;
+} __packed;
+
+struct restricted_profile_t {
+ uint32_t hfp_hs;
+ uint32_t a2dp;
+};
+#endif
+
struct btd_device {
int ref_count;
@@ -239,6 +285,10 @@ struct btd_device {
time_t le_seen;
gboolean trusted;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ struct trusted_profile_t trusted_profiles;
+ struct restricted_profile_t restricted_profiles;
+#endif
gboolean blocked;
gboolean auto_connect;
gboolean disable_auto_connect;
@@ -250,6 +300,30 @@ struct btd_device {
GIOChannel *att_io;
guint store_id;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ bool legacy_pairing;
+ char *manufacturer_data;
+ int manufacturer_data_len;
+ struct le_adv_report_info le_adv_data;
+ int remote_feature_flags;
+ guint attio_id;
+ gboolean gatt_connected;
+ uint16_t auth_payload_timeout;
+ uint8_t disc_reason;
+ uint8_t last_bdaddr_type;
+ uint8_t auth_bdaddr_type;
+ gboolean ipsp_connected; /* IPSP Connection state */
+ char if_name[16 + 1]; /* BT interface UP after IPSP connection */
+ 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;
+ uint16_t max_rx_time;
+ bdaddr_t *rpa;
+ DBusMessage *req_att_mtu; /* Attribute MTU request message */
+ uint8_t irk_val[16];
+ bool pending_conn_update;
+#endif
};
static const uint16_t uuid_list[] = {
@@ -259,6 +333,29 @@ static const uint16_t uuid_list[] = {
0
};
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+typedef enum {
+ SHOW_AUTHORIZATION = 0x0, /* 0b00 */
+ SUPPORTED_BLOCKED = 0x1, /* 0b01 */
+ SUPPORTED_TRUSTED= 0x2, /* 0b10 */
+} bt_profile_trusted_states;
+
+#define PBAP_SHIFT_OFFSET 0
+#define MAP_SHIFT_OFFSET 2
+#define SAP_SHIFT_OFFSET 4
+
+#define PROFILE_SUPPORTED 0x3 /* This corresponds to binary 0b11*/
+
+typedef enum {
+ CONNECTION_PERMITTED = 0x0, /* 0b00 */
+ CONNECTION_RESTRICTED = 0x1, /* 0b01 */
+} bt_profile_restricted_states;
+
+#define HFP_HS_SHIFT_OFFSET 0
+#define A2DP_SHIFT_OFFSET 2
+
+#endif
+
static int device_browse_gatt(struct btd_device *device, DBusMessage *msg);
static int device_browse_sdp(struct btd_device *device, DBusMessage *msg);
@@ -353,6 +450,42 @@ static void store_csrk(struct csrk_info *csrk, GKeyFile *key_file,
g_key_file_set_integer(key_file, group, "Counter", csrk->counter);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static char *manufacturer_data2str(char *data, int size)
+{
+ char str[DEV_MAX_MANUFACTURER_DATA_LEN * 3 + 1];
+ char tmp[5];
+ int i;
+
+ str[0] = '\0';
+ for(i = 0; i < size; i++) {
+ snprintf(tmp, sizeof(tmp), "%d ", data[i]);
+ g_strlcat(str, tmp, sizeof(str));
+ }
+
+ return g_strdup(str);
+}
+
+static void load_manufacturer_data_2digit(char *data, int len, char *buf)
+{
+ int i;
+ char **split;
+
+ split = g_strsplit(data, " ", 0);
+
+ for (i = 0; i < len; i++) {
+ if (split[i] == NULL)
+ break;
+
+ buf[i] = (char)g_ascii_strtoull(split[i], NULL, 10);
+ }
+
+ g_strfreev(split);
+
+ return;
+}
+#endif
+
static gboolean store_device_info_cb(gpointer user_data)
{
struct btd_device *device = user_data;
@@ -364,11 +497,19 @@ static gboolean store_device_info_cb(gpointer user_data)
char class[9];
char **uuids = NULL;
gsize length = 0;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ gboolean svc_change_regd = false;
+#endif
device->store_id = 0;
ba2str(btd_adapter_get_address(device->adapter), adapter_addr);
ba2str(&device->bdaddr, device_addr);
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (device->rpa)
+ ba2str(device->rpa, device_addr);
+#endif
snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info", adapter_addr,
device_addr);
@@ -397,11 +538,36 @@ static gboolean store_device_info_cb(gpointer user_data)
g_key_file_remove_key(key_file, "General", "Appearance", NULL);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ 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",
device->trusted);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ struct trusted_profile_t trust_profile = device->trusted_profiles;
+ int trusted_profiles = (trust_profile.pbap << PBAP_SHIFT_OFFSET) |
+ (trust_profile.map << MAP_SHIFT_OFFSET) |
+ (trust_profile.sap << SAP_SHIFT_OFFSET);
+ DBG("Storing TrustedProfiles %d", trusted_profiles);
+ g_key_file_set_integer(key_file, "General", "TrustedProfiles",
+ trusted_profiles);
+
+ struct restricted_profile_t restrict_profile = device->restricted_profiles;
+ int restricted_profiles = (restrict_profile.hfp_hs << HFP_HS_SHIFT_OFFSET) |
+ (restrict_profile.a2dp << A2DP_SHIFT_OFFSET);
+ DBG("Storing RestrictedProfiles %d", restricted_profiles);
+ g_key_file_set_integer(key_file, "General", "RestrictedProfiles",
+ restricted_profiles);
+#endif
g_key_file_set_boolean(key_file, "General", "Blocked",
device->blocked);
@@ -418,6 +584,43 @@ static gboolean store_device_info_cb(gpointer user_data)
g_key_file_remove_key(key_file, "General", "Services", NULL);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (device->le_adv_data.flags) {
+ g_key_file_set_integer(key_file, "General", "Flags",
+ device->le_adv_data.flags);
+ } else {
+ g_key_file_remove_key(key_file, "General", "Flags", NULL);
+ }
+
+ if (device->manufacturer_data) {
+ str = manufacturer_data2str(device->manufacturer_data,
+ device->manufacturer_data_len);
+ g_key_file_set_string(key_file, "General",
+ "ManufacturerData",
+ str);
+ g_free(str);
+ g_key_file_set_integer(key_file, "General",
+ "ManufacturerDataLen",
+ device->manufacturer_data_len);
+ } else {
+ g_key_file_remove_key(key_file, "General",
+ "ManufacturerData", NULL);
+ g_key_file_remove_key(key_file, "General",
+ "ManufacturerDataLen", NULL);
+ }
+
+ if (device->rpa) {
+ char irk_addr[18];
+
+ ba2str(&device->bdaddr, irk_addr);
+ g_key_file_set_string(key_file, "General", "IdentityAddress",
+ irk_addr);
+ } else {
+ g_key_file_remove_key(key_file, "General", "IdentityAddress",
+ NULL);
+ }
+#endif
+
if (device->vendor_src) {
g_key_file_set_integer(key_file, "DeviceID", "Source",
device->vendor_src);
@@ -431,6 +634,12 @@ static gboolean store_device_info_cb(gpointer user_data)
g_key_file_remove_group(key_file, "DeviceID", NULL);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ svc_change_regd = bt_att_get_svc_changed_indication_registered(device->att);
+ g_key_file_set_boolean(key_file, "Att", "SvcChangeRegd",
+ svc_change_regd);
+#endif
+
if (device->local_csrk)
store_csrk(device->local_csrk, key_file, "LocalSignatureKey");
@@ -493,6 +702,12 @@ void device_store_cached_name(struct btd_device *dev, const char *name)
ba2str(btd_adapter_get_address(dev->adapter), s_addr);
ba2str(&dev->bdaddr, d_addr);
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (dev->rpa)
+ ba2str(dev->rpa, d_addr);
+#endif
+
snprintf(filename, PATH_MAX, STORAGEDIR "/%s/cache/%s", s_addr, d_addr);
create_file(filename, S_IRUSR | S_IWUSR);
@@ -509,6 +724,9 @@ void device_store_cached_name(struct btd_device *dev, const char *name)
static void browse_request_free(struct browse_req *req)
{
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ DBG("");
+#endif
if (req->listener_id)
g_dbus_remove_watch(dbus_conn, req->listener_id);
if (req->msg)
@@ -644,6 +862,9 @@ static void device_free(gpointer user_data)
if (device->eir_uuids)
g_slist_free_full(device->eir_uuids, g_free);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ g_free(device->rpa);
+#endif
g_free(device->local_csrk);
g_free(device->remote_csrk);
g_free(device->path);
@@ -652,6 +873,18 @@ static void device_free(gpointer user_data)
g_free(device);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void device_set_remote_feature_flag(struct btd_device *device, int flags)
+{
+ device->remote_feature_flags = flags;
+}
+
+gboolean device_is_bredrle(struct btd_device *device)
+{
+ return (device->remote_feature_flags & (EIR_CONTROLLER | EIR_SIM_HOST));
+}
+#endif
+
bool device_is_paired(struct btd_device *device, uint8_t bdaddr_type)
{
struct bearer_state *state = get_state(device, bdaddr_type);
@@ -671,6 +904,53 @@ gboolean device_is_trusted(struct btd_device *device)
return device->trusted;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+gboolean device_is_profile_trusted(struct btd_device *device,
+ const char *uuid)
+{
+ if (g_strcmp0(uuid, OBEX_PSE_UUID) == 0) {
+ if (device->trusted_profiles.pbap == SUPPORTED_TRUSTED)
+ return TRUE;
+ } else if (g_strcmp0(uuid, OBEX_MAS_UUID) == 0) {
+ if (device->trusted_profiles.map == SUPPORTED_TRUSTED)
+ return TRUE;
+ } else if (g_strcmp0(uuid, SAP_UUID) == 0) {
+ if (device->trusted_profiles.sap == SUPPORTED_TRUSTED)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+gboolean device_is_profile_restricted(struct btd_device *device,
+ const char *uuid)
+{
+ if (g_strcmp0(uuid, HFP_HS_UUID) == 0) {
+ if (device->restricted_profiles.hfp_hs == CONNECTION_RESTRICTED)
+ return TRUE;
+ } else if (g_strcmp0(uuid, A2DP_SINK_UUID) == 0) {
+ if (device->restricted_profiles.a2dp == CONNECTION_RESTRICTED)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+gboolean device_is_profile_blocked(struct btd_device *device,
+ const char *uuid)
+{
+ if (g_strcmp0(uuid, OBEX_PSE_UUID) == 0) {
+ if (device->trusted_profiles.pbap == SUPPORTED_BLOCKED)
+ return TRUE;
+ } else if (g_strcmp0(uuid, OBEX_MAS_UUID) == 0) {
+ if (device->trusted_profiles.map == SUPPORTED_BLOCKED)
+ return TRUE;
+ } else if (g_strcmp0(uuid, SAP_UUID) == 0) {
+ if (device->trusted_profiles.sap == SUPPORTED_BLOCKED)
+ return TRUE;
+ }
+ return FALSE;
+}
+#endif
+
static gboolean dev_property_get_address(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *data)
{
@@ -678,7 +958,12 @@ static gboolean dev_property_get_address(const GDBusPropertyTable *property,
char dstaddr[18];
const char *ptr = dstaddr;
- ba2str(&device->bdaddr, dstaddr);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (device->rpa)
+ ba2str(device->rpa, dstaddr);
+ else
+#endif
+ ba2str(&device->bdaddr, dstaddr);
dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &ptr);
return TRUE;
@@ -717,7 +1002,9 @@ static gboolean dev_property_get_alias(const GDBusPropertyTable *property,
ptr = device->name;
} else {
ba2str(&device->bdaddr, dstaddr);
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
g_strdelimit(dstaddr, ":", '-');
+#endif
ptr = dstaddr;
}
@@ -726,6 +1013,24 @@ static gboolean dev_property_get_alias(const GDBusPropertyTable *property,
return TRUE;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static gboolean dev_property_get_alias_set(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct btd_device *device = data;
+ dbus_bool_t val;
+
+ if (device->alias != NULL)
+ val = TRUE;
+ else
+ val = FALSE;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &val);
+
+ return TRUE;
+}
+#endif
+
static void set_alias(GDBusPendingPropertySet id, const char *alias,
void *data)
{
@@ -1005,6 +1310,40 @@ static void dev_property_set_trusted(const GDBusPropertyTable *property,
set_trust(id, b, data);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static gboolean dev_property_get_trusted_profiles(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct btd_device *device = data;
+ uint32_t pbap = device->trusted_profiles.pbap;
+ uint32_t map = device->trusted_profiles.map;
+ uint32_t sap = device->trusted_profiles.sap;
+
+ unsigned int val = (pbap << PBAP_SHIFT_OFFSET) |
+ (map << MAP_SHIFT_OFFSET) |
+ (sap << SAP_SHIFT_OFFSET);
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &val);
+
+ return TRUE;
+}
+
+static gboolean dev_property_get_restricted_profiles(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct btd_device *device = data;
+ uint32_t hfp_hs = device->restricted_profiles.hfp_hs;
+ uint32_t a2dp = device->restricted_profiles.a2dp;
+
+ unsigned int val = (hfp_hs << HFP_HS_SHIFT_OFFSET) |
+ (a2dp << A2DP_SHIFT_OFFSET);
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &val);
+
+ return TRUE;
+}
+#endif
+
static gboolean dev_property_get_blocked(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *data)
{
@@ -1060,10 +1399,112 @@ static void dev_property_set_blocked(const GDBusPropertyTable *property,
set_blocked(id, b, data);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static uint8_t device_get_connected_state(struct btd_device *device)
+{
+ if (device->bredr_state.connected && device->le_state.connected)
+ return DEV_CONNECTED_BREDR_LE;
+ else if (device->bredr_state.connected)
+ return DEV_CONNECTED_BREDR;
+ else if (device->le_state.connected)
+ return DEV_CONNECTED_LE;
+ else
+ return DEV_CONNECTED_NONE;
+}
+
+static gboolean dev_property_get_payload(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct btd_device *dev = data;
+ dbus_uint16_t payload_timeout = dev->auth_payload_timeout;
+
+ dbus_message_iter_append_basic(iter,
+ DBUS_TYPE_UINT16, &payload_timeout);
+
+ return TRUE;
+}
+
+static gboolean dev_property_get_last_addr_type(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct btd_device *dev = data;
+ uint8_t last_addr_type = dev->last_bdaddr_type;
+
+ dbus_message_iter_append_basic(iter,
+ DBUS_TYPE_BYTE, &last_addr_type);
+
+ return TRUE;
+}
+
+static gboolean dev_property_get_att_mtu(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct btd_device *device = data;
+ dbus_uint16_t mtu = bt_gatt_client_get_mtu(device->client);
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &mtu);
+
+ return TRUE;
+}
+
+static gboolean dev_property_get_gatt_connected(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct btd_device *device = data;
+ dbus_bool_t gatt_connected;
+
+ if (device->gatt_connected)
+ gatt_connected = TRUE;
+ else
+ gatt_connected = FALSE;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN,
+ &gatt_connected);
+
+ return TRUE;
+}
+
+static gboolean dev_property_get_ipsp_conn_state(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct btd_device *dev = data;
+ dbus_bool_t ipsp_connected;
+
+ if (dev->ipsp_connected)
+ ipsp_connected = TRUE;
+ else
+ ipsp_connected = FALSE;
+
+ dbus_message_iter_append_basic(iter,
+ DBUS_TYPE_BOOLEAN, &ipsp_connected);
+
+ return TRUE;
+}
+
+static gboolean dev_property_get_ipsp_conn_bt_iface_name(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct btd_device *dev = data;
+ const char *ptr = g_strdup(dev->if_name);
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &ptr);
+
+ g_free(ptr);
+
+ return TRUE;
+}
+#endif
+
static gboolean dev_property_get_connected(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *data)
{
struct btd_device *dev = data;
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ uint8_t connected = device_get_connected_state(dev);
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE, &connected);
+#else
dbus_bool_t connected;
if (dev->bredr_state.connected || dev->le_state.connected)
@@ -1072,6 +1513,7 @@ static gboolean dev_property_get_connected(const GDBusPropertyTable *property,
connected = FALSE;
dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &connected);
+#endif
return TRUE;
}
@@ -1135,6 +1577,48 @@ static gboolean dev_property_get_adapter(const GDBusPropertyTable *property,
return TRUE;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static gboolean property_get_manufacturer_data_len(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *user_data)
+{
+ struct btd_device *device = user_data;
+ dbus_uint16_t val = device->manufacturer_data_len;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &val);
+
+ return TRUE;
+}
+
+static gboolean property_get_manufacturer_data(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *user_data)
+{
+ struct btd_device *device = user_data;
+ char str[DEV_MAX_MANUFACTURER_DATA_LEN] = {0};
+ DBusMessageIter array;
+
+ memset(str, 0, DEV_MAX_MANUFACTURER_DATA_LEN);
+ if (device->manufacturer_data_len)
+ memcpy(str, device->manufacturer_data,
+ device->manufacturer_data_len);
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_BYTE_AS_STRING, &array);
+
+ dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
+ &device->manufacturer_data,
+ device->manufacturer_data_len);
+
+ dbus_message_iter_close_container(iter, &array);
+
+ return TRUE;
+}
+
+gboolean device_get_gatt_connected(const struct btd_device *device)
+{
+ return device->gatt_connected;
+}
+#endif
+
static void append_manufacturer_data(void *data, void *user_data)
{
struct bt_ad_manufacturer_data *md = data;
@@ -1483,8 +1967,10 @@ static void device_profile_connected(struct btd_device *dev,
if (!err)
btd_device_set_temporary(dev, false);
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
if (dev->pending == NULL)
goto done;
+#endif
if (!btd_device_is_connected(dev)) {
switch (-err) {
@@ -1495,6 +1981,10 @@ static void device_profile_connected(struct btd_device *dev,
}
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (dev->pending == NULL)
+ return;
+#endif
pending = dev->pending->data;
l = find_service_with_profile(dev->pending, profile);
@@ -1530,10 +2020,28 @@ done:
g_dbus_send_message(dbus_conn,
btd_error_failed(dev->connect, strerror(-err)));
else {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ /* SDP is not required for Samsung TV Power on */
+ if (g_strcmp0(profile->name, "hid-device") == 0) {
+ DBG("Skip SDP discovery.");
+ } else {
+#endif
/* Start passive SDP discovery to update known services */
if (dev->bredr && !dev->svc_refreshed)
device_browse_sdp(dev, NULL);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ }
+#endif
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (err)
+ g_dbus_send_message(dbus_conn,
+ btd_error_failed(dev->connect, strerror(-err)));
+ else
+ g_dbus_send_reply(dbus_conn, dev->connect, DBUS_TYPE_INVALID);
+#else
g_dbus_send_reply(dbus_conn, dev->connect, DBUS_TYPE_INVALID);
+#endif
}
dbus_message_unref(dev->connect);
@@ -1604,7 +2112,9 @@ static struct btd_service *find_connectable_service(struct btd_device *dev,
const char *uuid)
{
GSList *l;
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ struct btd_service *s = NULL;
+#endif
for (l = dev->services; l != NULL; l = g_slist_next(l)) {
struct btd_service *service = l->data;
struct btd_profile *p = btd_service_get_profile(service);
@@ -1612,9 +2122,24 @@ static struct btd_service *find_connectable_service(struct btd_device *dev,
if (!p->connect || !p->remote_uuid)
continue;
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
if (strcasecmp(uuid, p->remote_uuid) == 0)
return service;
+#else
+ if (strcasecmp(uuid, p->remote_uuid) == 0) {
+ s = service;
+ if (ext_profile_is_registered_as_client_role(p) == TRUE) {
+ return service;
+ } else {
+ continue;
+ }
+ }
+#endif
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (s)
+ return s;
+#endif
return NULL;
}
@@ -1632,12 +2157,29 @@ static GSList *create_pending_list(struct btd_device *dev, const char *uuid)
struct btd_service *service;
struct btd_profile *p;
GSList *l;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ bool hs_hf_verify = FALSE;
+#endif
if (uuid) {
service = find_connectable_service(dev, uuid);
if (service)
return g_slist_prepend(dev->pending, service);
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ else if ((service == NULL) &&
+ (g_strcmp0(uuid, HFP_HS_UUID) == 0)) {
+ DBG("HFP service not found check for HSP service");
+ service = find_connectable_service(dev, HSP_HS_UUID);
+ if (service)
+ return g_slist_prepend(dev->pending, service);
+ } else if (g_strcmp0(uuid, HID_UUID) == 0) {
+ DBG("HID service not found, add HID service");
+ btd_device_add_uuid(dev, uuid);
+ service = find_connectable_service(dev, HID_UUID);
+ if (service)
+ return g_slist_prepend(dev->pending, service);
+ }
+#endif
return dev->pending;
}
@@ -1645,6 +2187,28 @@ static GSList *create_pending_list(struct btd_device *dev, const char *uuid)
service = l->data;
p = btd_service_get_profile(service);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ DBG("profile uuid %s", p->remote_uuid);
+ if (g_strcmp0(p->remote_uuid, HSP_HS_UUID) == 0) {
+ DBG("HSP service is found check for HFP service");
+ struct btd_service *service;
+ struct btd_profile *p;
+ GSList *h;
+
+ for (h = dev->services; h != NULL; h = g_slist_next(h)) {
+ service = h->data;
+ p = btd_service_get_profile(service);
+
+ if (g_strcmp0(p->remote_uuid, HFP_HS_UUID) == 0) {
+ DBG("HFP found,ignore HSP ");
+ hs_hf_verify = TRUE;
+ break;
+ }
+ }
+ if (hs_hf_verify)
+ continue;
+ }
+#endif
if (!p->auto_connect)
continue;
@@ -1693,8 +2257,13 @@ static DBusMessage *connect_profiles(struct btd_device *dev, uint8_t bdaddr_type
DBG("%s %s, client %s", dev->path, uuid ? uuid : "(all)",
dbus_message_get_sender(msg));
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (dev->pending || dev->connect)
+ return btd_error_in_progress(msg);
+#else
if (dev->pending || dev->connect || dev->browse)
return btd_error_in_progress(msg);
+#endif
if (!btd_adapter_get_powered(dev->adapter))
return btd_error_not_ready(msg);
@@ -1707,8 +2276,13 @@ static DBusMessage *connect_profiles(struct btd_device *dev, uint8_t bdaddr_type
dev->pending = create_pending_list(dev, uuid);
if (!dev->pending) {
if (dev->svc_refreshed) {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (!uuid && find_service_with_state(dev->services,
+ BTD_SERVICE_STATE_CONNECTED))
+#else
if (find_service_with_state(dev->services,
BTD_SERVICE_STATE_CONNECTED))
+#endif
return dbus_message_new_method_return(msg);
else
return btd_error_not_available(msg);
@@ -1892,6 +2466,12 @@ static DBusMessage *disconnect_profile(DBusConnection *conn, DBusMessage *msg,
return btd_error_invalid_args(msg);
service = find_connectable_service(dev, uuid);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if ((service == NULL) && (g_strcmp0(uuid, HFP_HS_UUID) == 0)) {
+ DBG("HFP service is not found check for HSP service");
+ service = find_connectable_service(dev, HSP_HS_UUID);
+ }
+#endif
free(uuid);
if (!service)
@@ -1906,6 +2486,9 @@ static DBusMessage *disconnect_profile(DBusConnection *conn, DBusMessage *msg,
if (err == 0)
return NULL;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (dev->disconnect)
+#endif
dbus_message_unref(dev->disconnect);
dev->disconnect = NULL;
@@ -1941,6 +2524,11 @@ static void store_services(struct btd_device *device)
ba2str(btd_adapter_get_address(adapter), src_addr);
ba2str(&device->bdaddr, dst_addr);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (device->rpa)
+ ba2str(device->rpa, dst_addr);
+#endif
+
snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/attributes", src_addr,
dst_addr);
key_file = g_key_file_new();
@@ -2211,9 +2799,18 @@ static void device_svc_resolved(struct btd_device *dev, uint8_t bdaddr_type,
struct bearer_state *state = get_state(dev, bdaddr_type);
struct browse_req *req = dev->browse;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ DBG("%s bdaddr_type %d err %d", dev->path, bdaddr_type, err);
+#else
DBG("%s err %d", dev->path, err);
+#endif
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
state->svc_resolved = true;
+#else
+ if (err == 0)
+ state->svc_resolved = true;
+#endif
/* Disconnection notification can happen before this function
* gets called, so don't set svc_refreshed for a disconnected
@@ -2253,6 +2850,14 @@ static void device_svc_resolved(struct btd_device *dev, uint8_t bdaddr_type,
if (!req)
return;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ /* If bdaddr_type is LE but req is for SDP, don't complete browse req. */
+ if (bdaddr_type != BDADDR_BREDR && req->search_uuid) {
+ DBG("Discover comp. is for LE but browse req. is for SDP.");
+ return;
+ }
+#endif
+
dev->browse = NULL;
browse_request_complete(req, bdaddr_type, err);
}
@@ -2376,22 +2981,52 @@ static DBusMessage *pair_device(DBusConnection *conn, DBusMessage *msg,
{
struct btd_device *device = data;
struct btd_adapter *adapter = device->adapter;
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
struct bearer_state *state;
+#endif
uint8_t bdaddr_type;
const char *sender;
struct agent *agent;
struct bonding_req *bonding;
uint8_t io_cap;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ uint8_t conn_type;
+ bool connect_le = FALSE;
+#endif
int err;
btd_device_set_temporary(device, false);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (dbus_message_get_args(msg, NULL, DBUS_TYPE_BYTE, &conn_type,
+ DBUS_TYPE_INVALID) == FALSE)
+#else
if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_INVALID))
+#endif
return btd_error_invalid_args(msg);
if (device->bonding)
return btd_error_in_progress(msg);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (conn_type == DEV_CONN_DEFAULT) {
+ if (device_is_bonded(device, DEV_CONN_BREDR))
+ return btd_error_already_exists(msg);
+ else if (device_is_bonded(device, DEV_CONN_LE))
+ return btd_error_already_exists(msg);
+
+ if (device->bredr)
+ conn_type = DEV_CONN_BREDR;
+ else if (device->le)
+ conn_type = DEV_CONN_LE;
+ else
+ conn_type = DEV_CONN_BREDR;
+ } else {
+ if (device_is_bonded(device, conn_type))
+ return btd_error_already_exists(msg);
+ }
+ bdaddr_type = device->bdaddr_type;
+#else
if (device->bredr_state.bonded)
bdaddr_type = device->bdaddr_type;
else if (device->le_state.bonded)
@@ -2403,6 +3038,15 @@ static DBusMessage *pair_device(DBusConnection *conn, DBusMessage *msg,
if (state->bonded)
return btd_error_already_exists(msg);
+#endif
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (conn_type == DEV_CONN_LE &&
+ (device_is_bredrle(device) || bdaddr_type != BDADDR_BREDR)) {
+ DBG("LE Connect request");
+ connect_le = TRUE;
+ }
+#endif
sender = dbus_message_get_sender(msg);
@@ -2412,7 +3056,15 @@ static DBusMessage *pair_device(DBusConnection *conn, DBusMessage *msg,
else
io_cap = IO_CAPABILITY_NOINPUTNOOUTPUT;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if ((conn_type == DEV_CONN_LE && bdaddr_type != BDADDR_BREDR) ||
+ connect_le)
+ bonding = bonding_request_new(msg, device, bdaddr_type, agent);
+ else
+ bonding = bonding_request_new(msg, device, BDADDR_BREDR, agent);
+#else
bonding = bonding_request_new(msg, device, bdaddr_type, agent);
+#endif
if (agent)
agent_unref(agent);
@@ -2429,6 +3081,17 @@ static DBusMessage *pair_device(DBusConnection *conn, DBusMessage *msg,
* channel first and only then start pairing (there's code for
* this in the ATT connect callback)
*/
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (((conn_type == DEV_CONN_LE && bdaddr_type != BDADDR_BREDR) ||
+ (connect_le)) && !device->le_state.connected)
+ 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_cap);
+ else
+ err = adapter_create_bonding(adapter, &device->bdaddr,
+ BDADDR_BREDR, io_cap);
+#else
if (bdaddr_type != BDADDR_BREDR) {
if (!state->connected && btd_le_connect_before_pairing())
err = device_connect_le(device);
@@ -2440,6 +3103,7 @@ static DBusMessage *pair_device(DBusConnection *conn, DBusMessage *msg,
err = adapter_create_bonding(adapter, &device->bdaddr,
BDADDR_BREDR, io_cap);
}
+#endif
if (err < 0) {
bonding_request_free(device->bonding);
@@ -2470,7 +3134,9 @@ static DBusMessage *new_authentication_return(DBusMessage *msg, uint8_t status)
"Authentication Rejected");
case MGMT_STATUS_CANCELLED:
case MGMT_STATUS_NO_RESOURCES:
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
case MGMT_STATUS_DISCONNECTED:
+#endif
return dbus_message_new_error(msg,
ERROR_INTERFACE ".AuthenticationCanceled",
"Authentication Canceled");
@@ -2523,6 +3189,643 @@ static DBusMessage *cancel_pairing(DBusConnection *conn, DBusMessage *msg,
return dbus_message_new_method_return(msg);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static DBusMessage *discover_services(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ struct btd_device *device = user_data;
+ const char *pattern;
+ int err;
+
+ if (device->browse)
+ return btd_error_in_progress(msg);
+
+ if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &pattern,
+ DBUS_TYPE_INVALID) == FALSE)
+ return btd_error_invalid_args(msg);
+
+ err = device_browse_sdp(device, msg);
+ if (err < 0)
+ goto fail;
+
+ return NULL;
+
+fail:
+ return btd_error_failed(msg,
+ "Unable to search the SDP services");
+}
+
+static const char *browse_request_get_requestor(struct browse_req *req)
+{
+ if (!req->msg)
+ return NULL;
+
+ return dbus_message_get_sender(req->msg);
+}
+
+static void iter_append_record(DBusMessageIter *dict, uint32_t handle,
+ const char *record)
+{
+ DBusMessageIter entry;
+
+ dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY,
+ NULL, &entry);
+
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_UINT32, &handle);
+
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &record);
+
+ dbus_message_iter_close_container(dict, &entry);
+}
+
+static void discover_services_reply(struct browse_req *req, int err,
+ sdp_list_t *recs)
+{
+ DBusMessage *reply;
+ DBusMessageIter iter, dict;
+ sdp_list_t *seq;
+
+ if (!req->msg)
+ return;
+
+ if (err) {
+ const char *err_if;
+
+ if (err == -EHOSTDOWN)
+ err_if = ERROR_INTERFACE ".ConnectionAttemptFailed";
+ else
+ err_if = ERROR_INTERFACE ".Failed";
+
+ reply = dbus_message_new_error(req->msg, err_if,
+ strerror(-err));
+ g_dbus_send_message(dbus_conn, reply);
+ return;
+ }
+
+ reply = dbus_message_new_method_return(req->msg);
+ if (!reply)
+ return;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_UINT32_AS_STRING DBUS_TYPE_STRING_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+ for (seq = recs; seq; seq = seq->next) {
+ sdp_record_t *rec = (sdp_record_t *) seq->data;
+ GString *result;
+
+ if (!rec)
+ break;
+
+ result = g_string_new(NULL);
+
+ convert_sdp_record_to_xml(rec, result,
+ (void *) g_string_append);
+
+ if (result->len)
+ iter_append_record(&dict, rec->handle, result->str);
+
+ g_string_free(result, TRUE);
+ }
+
+ dbus_message_iter_close_container(&iter, &dict);
+
+ g_dbus_send_message(dbus_conn, reply);
+}
+
+static DBusMessage *cancel_discover(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ struct btd_device *device = user_data;
+ const char *sender = dbus_message_get_sender(msg);
+ const char *requestor;
+
+ if (!device->browse)
+ return btd_error_does_not_exist(msg);
+
+ if (!dbus_message_is_method_call(device->browse->msg, DEVICE_INTERFACE,
+ "DiscoverServices"))
+ return btd_error_not_authorized(msg);
+
+ requestor = browse_request_get_requestor(device->browse);
+
+ /* only the discover requestor can cancel the inquiry process */
+ if (!requestor || !g_str_equal(requestor, sender))
+ return btd_error_not_authorized(msg);
+
+ discover_services_reply(device->browse, -ECANCELED, NULL);
+
+ if (device->browse)
+ browse_request_cancel(device->browse);
+
+ return dbus_message_new_method_return(msg);
+}
+
+void device_set_gatt_connected(struct btd_device *device, gboolean connected)
+{
+ if (device == NULL) {
+ error("device is NULL");
+ return;
+ }
+
+ if (device->gatt_connected == connected) {
+ error("same state change for gatt_connected : %d", connected);
+ return;
+ }
+ DBG("GattConnected %d", connected);
+
+ device->gatt_connected = connected;
+ g_dbus_emit_property_changed(dbus_conn, device->path,
+ DEVICE_INTERFACE, "GattConnected");
+}
+
+static DBusMessage *connect_le(DBusConnection *conn, DBusMessage *msg,
+ void *user_data)
+{
+ struct btd_device *dev = user_data;
+ dbus_bool_t auto_connect = FALSE;
+ int err;
+
+ if (!dev->le) {
+ /*
+ * If a LE connection is requested without device discovery,
+ * we try to get device object. Here, technology can be updated
+ * if there is matched device object. Or, a new device object
+ * will be created.
+ */
+ dev = btd_adapter_get_device(dev->adapter, &dev->bdaddr,
+ BDADDR_LE_PUBLIC);
+ if (dev == NULL) {
+ error("Unable to get device object");
+ return btd_error_not_supported(msg);
+ }
+ }
+
+ if (dev->le_state.connected)
+ return dbus_message_new_method_return(msg);
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_BOOLEAN, &auto_connect,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ btd_device_set_temporary(dev, false);
+
+ if (auto_connect) {
+ DBG("Start BLE auto connection");
+ dev->disable_auto_connect = FALSE;
+ device_set_auto_connect(dev, TRUE);
+
+ return dbus_message_new_method_return(msg);
+ }
+
+ err = device_connect_le(dev);
+ if (err < 0)
+ return btd_error_failed(msg, strerror(-err));
+
+ dev->connect = dbus_message_ref(msg);
+
+ return NULL;
+}
+
+static DBusMessage *disconnect_le(DBusConnection *conn, DBusMessage *msg,
+ void *user_data)
+{
+ struct btd_device *dev = user_data;
+
+ if (!dev->le)
+ return btd_error_not_supported(msg);
+
+ /*
+ * Disable connections through passive sccanning
+ */
+ if (dev->auto_connect) {
+ DBG("Stop BLE auto connection");
+ dev->disable_auto_connect = FALSE;
+ device_set_auto_connect(dev, FALSE);
+
+ if (!dev->le_state.connected) {
+ g_dbus_send_reply(dbus_conn, msg, DBUS_TYPE_INVALID);
+ return NULL;
+ }
+ } else if (!dev->le_state.connected) {
+ return btd_error_not_connected(msg);
+ }
+
+ dev->disconnects = g_slist_append(dev->disconnects,
+ dbus_message_ref(msg));
+
+ btd_adapter_disconnect_device(dev->adapter, &dev->bdaddr,
+ dev->bdaddr_type);
+
+ return NULL;
+}
+
+static DBusMessage *connect_ipsp(DBusConnection *conn, DBusMessage *msg,
+ void *user_data)
+{
+ struct btd_device *device = user_data;
+
+ DBG("bdaddr_type %d", device->bdaddr_type);
+
+ if (device->bdaddr_type == BDADDR_BREDR) {
+ if(device->le)
+ device->bdaddr_type = BDADDR_LE_PUBLIC;
+ else {
+ device = btd_adapter_get_device(device->adapter,
+ &device->bdaddr, BDADDR_LE_PUBLIC);
+ if (device == NULL)
+ return btd_error_no_such_adapter(msg);
+ }
+ }
+
+ if (device->ipsp_connected)
+ return btd_error_already_connected(msg);
+
+ /* Initiate Connection for 6Lowan*/
+ if (btd_adapter_connect_ipsp(device->adapter, &device->bdaddr,
+ device->bdaddr_type) != 0)
+ return btd_error_failed(msg, "ConnectFailed");
+
+ return dbus_message_new_method_return(msg);;
+}
+
+static DBusMessage *disconnect_ipsp(DBusConnection *conn, DBusMessage *msg,
+ void *user_data)
+{
+ struct btd_device *device = user_data;
+ DBG("bdaddr_type %d", device->bdaddr_type);
+
+ if (device->bdaddr_type == BDADDR_BREDR)
+ return btd_error_not_supported(msg);
+
+ if (!device->ipsp_connected)
+ return btd_error_not_connected(msg);
+
+ /* Disconnect the 6Lowpan connection */
+ if (btd_adapter_disconnect_ipsp(device->adapter, &device->bdaddr,
+ device->bdaddr_type) != 0)
+ return btd_error_failed(msg, "DisconnectFailed");
+
+ /* TODO: Handle disconnection of GATT connection, If the connection
+ * is established as part of IPSP connection. */
+
+ return dbus_message_new_method_return(msg);;
+}
+
+static DBusMessage *le_set_data_length(
+ DBusConnection *conn, DBusMessage *msg,
+ void *user_data)
+{
+ dbus_uint16_t max_tx_octets;
+ dbus_uint16_t max_tx_time;
+ struct btd_device *device = user_data;
+ int status;
+ char addr[18];
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_UINT16, &max_tx_octets,
+ DBUS_TYPE_UINT16, &max_tx_time,
+ DBUS_TYPE_INVALID)) {
+ DBG("error in retrieving values");
+ return btd_error_invalid_args(msg);
+ }
+
+ if (device->bdaddr_type == BDADDR_BREDR)
+ return btd_error_not_supported(msg);
+
+ ba2str(&device->bdaddr, addr);
+
+ DBG("Remote device address: %s", addr);
+ DBG("Max tx octets: %u, Max tx time: %u",
+ max_tx_octets, max_tx_time);
+
+ status = btd_adapter_le_set_data_length(device->adapter,
+ &device->bdaddr, max_tx_octets,
+ max_tx_time);
+
+ if (status != 0)
+ return btd_error_failed(msg, "Unable to set le data length values");
+ else
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *set_trusted_profile(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct btd_device *dev = data;
+ dbus_bool_t profile_trusted;
+ const char *pattern;
+ char *uuid;
+ uint32_t pbap = dev->trusted_profiles.pbap;
+ uint32_t map = dev->trusted_profiles.map;
+ uint32_t sap = dev->trusted_profiles.sap;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &pattern,
+ DBUS_TYPE_BOOLEAN, &profile_trusted,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ DBG("Pattern : %s", pattern);
+ uuid = bt_name2string(pattern);
+ DBG("UUID : %s", uuid);
+ DBG("profile Trusted : %d %d %d", dev->trusted_profiles.pbap,
+ dev->trusted_profiles.map, dev->trusted_profiles.sap);
+ if (g_strcmp0(uuid, OBEX_PBAP_UUID) == 0) {
+ if (profile_trusted)
+ pbap = SUPPORTED_TRUSTED;
+ else
+ pbap = SUPPORTED_BLOCKED;
+ } else if (g_strcmp0(uuid, OBEX_MAP_UUID) == 0) {
+ if (profile_trusted)
+ map = SUPPORTED_TRUSTED;
+ else
+ map = SUPPORTED_BLOCKED;
+ } else if (g_strcmp0(uuid, SAP_UUID) == 0) {
+ if (profile_trusted)
+ sap = SUPPORTED_TRUSTED;
+ else
+ sap = SUPPORTED_BLOCKED;
+ } else {
+ return btd_error_invalid_args(msg);
+ }
+
+ btd_device_set_trusted_profiles(dev, pbap, map, sap);
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *set_restricted_profile(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ struct btd_device *dev = data;
+ dbus_bool_t profile_restricted;
+ const char *pattern;
+ char *uuid;
+ uint32_t hfp_hs = dev->restricted_profiles.hfp_hs;
+ uint32_t a2dp = dev->restricted_profiles.a2dp;
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_STRING, &pattern,
+ DBUS_TYPE_BOOLEAN, &profile_restricted,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ DBG("Pattern : %s", pattern);
+ uuid = bt_name2string(pattern);
+ DBG("UUID : %s", uuid);
+ DBG("profile Restricted : %d %d", dev->restricted_profiles.hfp_hs,
+ dev->restricted_profiles.a2dp);
+ if (g_strcmp0(uuid, HFP_HS_UUID) == 0) {
+ if (profile_restricted)
+ hfp_hs = CONNECTION_RESTRICTED;
+ else
+ hfp_hs = CONNECTION_PERMITTED;
+ } else if (g_strcmp0(uuid, A2DP_SINK_UUID) == 0) {
+ if (profile_restricted)
+ a2dp = CONNECTION_RESTRICTED;
+ else
+ a2dp = CONNECTION_PERMITTED;
+ } else {
+ return btd_error_invalid_args(msg);
+ }
+
+ btd_device_set_restricted_profiles(dev, hfp_hs, a2dp);
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *is_connected_profile(DBusConnection *conn, DBusMessage *msg,
+ void *user_data)
+{
+ struct btd_device *dev = user_data;
+ struct btd_service *service;
+ btd_service_state_t state;
+ const char *pattern;
+ char *uuid;
+ DBusMessage *reply;
+ dbus_bool_t val;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &pattern,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return btd_error_invalid_args(reply);
+
+ uuid = bt_name2string(pattern);
+ DBG("is_connected_profile_uuid : %s", uuid);
+ service = btd_device_get_service(dev, uuid);
+
+ if ((service == NULL) && (g_strcmp0(uuid, HFP_HS_UUID) == 0)) {
+ DBG("HFP service is not found check for HSP service");
+ service = btd_device_get_service(dev, HSP_HS_UUID);
+ }
+ if (uuid)
+ free(uuid);
+
+ if (!service)
+ return btd_error_not_connected(msg);
+
+ state = btd_service_get_state(service);
+ DBG("Connected State : %d", state);
+
+ if (state == BTD_SERVICE_STATE_CONNECTED)
+ val = TRUE;
+ else
+ val = FALSE;
+
+ dbus_message_append_args(reply,
+ DBUS_TYPE_BOOLEAN, &val,
+ DBUS_TYPE_INVALID);
+
+ return reply;
+}
+
+static DBusMessage *update_le_conn_parm(DBusConnection *conn, DBusMessage *msg,
+ void *user_data)
+{
+ struct btd_device *device = user_data;
+ GIOChannel *io;
+ int fd;
+ struct le_conn_param param = {0, 0, 0, 0};
+
+ DBG("");
+
+ if (device == NULL) {
+ error("device is NULL");
+ return btd_error_invalid_args(msg);
+ }
+
+ if (!device->le) {
+ error("le is not supported");
+ return btd_error_not_supported(msg);
+ }
+
+ if (!device->gatt_connected || !device->attrib)
+ return btd_error_not_connected(msg);
+
+ io = g_attrib_get_channel(device->attrib);
+ if (!io)
+ return btd_error_not_connected(msg);
+
+ fd = g_io_channel_unix_get_fd(io);
+ if (fd < 0)
+ return btd_error_not_connected(msg);
+
+ if (device_get_conn_update_state(device))
+ return btd_error_in_progress(msg);
+ else
+ device_set_conn_update_state(device, true);
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &param.min,
+ DBUS_TYPE_UINT32, &param.max,
+ DBUS_TYPE_UINT32, &param.latency,
+ DBUS_TYPE_UINT32, &param.to_multiplier,
+ DBUS_TYPE_INVALID)) {
+ error("Invalid args");
+ return btd_error_invalid_args(msg);
+ }
+
+ if (setsockopt(fd, SOL_BLUETOOTH, BT_LE_CONN_PARAM,
+ &param, sizeof(param)) < 0) {
+ error("Can't Update LE conn param : %s (%d)",
+ strerror(errno), errno);
+ return btd_error_failed(msg, strerror(errno));
+ }
+
+ return dbus_message_new_method_return(msg);
+}
+
+static void device_request_att_mtu_reponse_cb(bool success, uint8_t att_ecode,
+ void *user_data)
+{
+ struct btd_device *device = user_data;
+ DBusMessage *reply;
+ DBusMessageIter iter;
+ uint16_t mtu;
+
+ if (!device->req_att_mtu)
+ return;
+
+ mtu = bt_gatt_client_get_mtu(device->client);
+
+ if (!success) {
+ const char *err_if;
+ err_if = ERROR_INTERFACE ".Failed";
+
+ reply = dbus_message_new_error(device->req_att_mtu, err_if,
+ "Request Att MTU failed");
+ g_dbus_send_message(dbus_conn, reply);
+ return;
+ }
+
+ DBG("MTU exchange complete, with MTU: %u", mtu);
+
+ reply = dbus_message_new_method_return(device->req_att_mtu);
+ if (!reply)
+ return;
+
+ dbus_message_iter_init_append(reply, &iter);
+
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_UINT16,
+ &mtu);
+ dbus_message_iter_append_basic(&iter, DBUS_TYPE_BYTE,
+ &att_ecode);
+ g_dbus_send_message(dbus_conn, reply);
+
+ dbus_message_unref(device->req_att_mtu);
+ device->req_att_mtu = NULL;
+}
+
+static DBusMessage *request_att_mtu(DBusConnection *conn, DBusMessage *msg,
+ void *user_data)
+{
+ struct btd_device *device = user_data;
+ uint16_t mtu;
+
+ DBG("");
+
+ if (device == NULL) {
+ error("device is NULL");
+ return btd_error_invalid_args(msg);
+ }
+
+ if (!device->le) {
+ error("le is not supported");
+ return btd_error_not_supported(msg);
+ }
+
+ if (!device->gatt_connected || !device->attrib)
+ return btd_error_not_connected(msg);
+
+ if (!dbus_message_get_args(msg, NULL,
+ DBUS_TYPE_UINT16, &mtu,
+ DBUS_TYPE_INVALID)) {
+ error("Invalid args");
+ return btd_error_invalid_args(msg);
+ }
+
+ DBG("MTU %d", mtu);
+
+ if (!bt_gatt_request_att_mtu(device->client, mtu,
+ device_request_att_mtu_reponse_cb, device))
+ return btd_error_failed(msg, "Unable to Request MTU");
+
+ device->req_att_mtu = dbus_message_ref(msg);
+ return NULL;
+}
+
+static DBusMessage *device_get_ida(DBusConnection *conn, DBusMessage *msg,
+ void *user_data)
+{
+ struct btd_device *device = user_data;
+ char device_idaddr[18] = { 0 };
+ DBusMessage *reply;
+ const gchar *id_address = device_idaddr;
+
+ DBG("");
+
+ if (device == NULL)
+ return btd_error_invalid_args(msg);
+
+ if (!device->le)
+ return btd_error_not_supported(msg);
+
+ if (device->rpa) {
+ if (device->bredr)
+ ba2str(device->rpa, device_idaddr);
+ else
+ ba2str(&device->bdaddr, device_idaddr);
+ } else
+ return btd_error_does_not_exist(msg);
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return NULL;
+
+ dbus_message_append_args(reply, DBUS_TYPE_STRING, &id_address,
+ DBUS_TYPE_INVALID);
+
+ return reply;
+}
+
+void device_set_conn_update_state(struct btd_device *device, bool state)
+{
+ if (!device)
+ return;
+
+ device->pending_conn_update = state;
+}
+
+bool device_get_conn_update_state(struct btd_device *device)
+{
+ return device->pending_conn_update;
+}
+#endif
+
static const GDBusMethodTable device_methods[] = {
{ GDBUS_ASYNC_METHOD("Disconnect", NULL, NULL, dev_disconnect) },
{ GDBUS_ASYNC_METHOD("Connect", NULL, NULL, dev_connect) },
@@ -2530,8 +3833,45 @@ static const GDBusMethodTable device_methods[] = {
NULL, connect_profile) },
{ GDBUS_ASYNC_METHOD("DisconnectProfile", GDBUS_ARGS({ "UUID", "s" }),
NULL, disconnect_profile) },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ { GDBUS_ASYNC_METHOD("Pair", GDBUS_ARGS({ "conn_type", "y" }), NULL,
+ pair_device) },
+#else
{ GDBUS_ASYNC_METHOD("Pair", NULL, NULL, pair_device) },
+#endif
{ GDBUS_METHOD("CancelPairing", NULL, NULL, cancel_pairing) },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ { GDBUS_ASYNC_METHOD("ConnectLE", GDBUS_ARGS({ "auto_connect", "b"}),
+ NULL, connect_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", GDBUS_ARGS({ "interval_min", "u" },
+ { "interval_max", "u" }, { "latency", "u" },
+ { "time_out", "u" }), NULL,
+ update_le_conn_parm) },
+ { GDBUS_ASYNC_METHOD("DiscoverServices", GDBUS_ARGS({ "pattern", "s" }),
+ NULL, discover_services) },
+ { GDBUS_METHOD("CancelDiscovery", NULL, NULL, cancel_discover) },
+ { GDBUS_ASYNC_METHOD("ConnectIpsp", NULL, NULL, connect_ipsp) },
+ { GDBUS_ASYNC_METHOD("DisconnectIpsp", NULL, NULL, disconnect_ipsp) },
+ { GDBUS_ASYNC_METHOD("LESetDataLength",
+ GDBUS_ARGS({"max_tx_octets", "q" },
+ { "max_tx_time", "q" }), NULL,
+ le_set_data_length)},
+ { GDBUS_ASYNC_METHOD("RequestAttMtu", GDBUS_ARGS({ "mtu", "q" }),
+ GDBUS_ARGS({ "mtu", "q" }, { "status", "y"}),
+ request_att_mtu) },
+ { GDBUS_METHOD("GetIDAddress", NULL, GDBUS_ARGS({ "IDAdress", "s" }),
+ device_get_ida) },
+ { GDBUS_METHOD("SetTrustedProfile",
+ GDBUS_ARGS({ "uuid", "s"}, { "trusted", "b"}), NULL,
+ set_trusted_profile) },
+ { GDBUS_METHOD("SetRestrictedProfile",
+ GDBUS_ARGS({ "uuid", "s"}, { "restricted", "b"}), NULL,
+ set_restricted_profile) },
+#endif
{ }
};
@@ -2550,11 +3890,30 @@ static const GDBusPropertyTable device_properties[] = {
{ "Blocked", "b", dev_property_get_blocked, dev_property_set_blocked },
{ "LegacyPairing", "b", dev_property_get_legacy },
{ "RSSI", "n", dev_property_get_rssi, NULL, dev_property_exists_rssi },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ {"IsAliasSet", "b", dev_property_get_alias_set },
+ { "Connected", "y", dev_property_get_connected },
+#else
{ "Connected", "b", dev_property_get_connected },
+#endif
{ "UUIDs", "as", dev_property_get_uuids },
{ "Modalias", "s", dev_property_get_modalias, NULL,
dev_property_exists_modalias },
{ "Adapter", "o", dev_property_get_adapter },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ /* To handle Failed Legacy Pairing when initiated from Remote device*/
+ { "LegacyPaired", "b", dev_property_get_paired },
+ { "ManufacturerDataLen", "q", property_get_manufacturer_data_len },
+ { "ManufacturerData", "ay", property_get_manufacturer_data },
+ { "GattConnected", "b", dev_property_get_gatt_connected },
+ { "PayloadTimeout", "q", dev_property_get_payload},
+ { "LastAddrType", "y", dev_property_get_last_addr_type},
+ { "IpspConnected", "b", dev_property_get_ipsp_conn_state },
+ { "IpspBtInterfaceInfo", "s", dev_property_get_ipsp_conn_bt_iface_name },
+ { "AttMtu", "q", dev_property_get_att_mtu },
+ { "TrustedProfiles", "u", dev_property_get_trusted_profiles},
+ { "RestrictedProfiles", "u", dev_property_get_restricted_profiles},
+#endif
{ "ManufacturerData", "a{qv}", dev_property_get_manufacturer_data,
NULL, dev_property_manufacturer_data_exist },
{ "ServiceData", "a{sv}", dev_property_get_service_data,
@@ -2569,6 +3928,28 @@ static const GDBusPropertyTable device_properties[] = {
{ }
};
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static const GDBusSignalTable device_signals[] = {
+ { GDBUS_SIGNAL("Disconnected",
+ GDBUS_ARGS({ "bdaddr_type", "y" }, { "reason", "y" },
+ { "name", "s" })) },
+ { GDBUS_SIGNAL("DeviceConnected", GDBUS_ARGS({ "bdaddr_type", "y"})) },
+ { GDBUS_SIGNAL("ProfileStateChanged",
+ GDBUS_ARGS({ "profile", "s"}, {"state", "i"})) },
+ { GDBUS_SIGNAL("AdvReport",
+ GDBUS_ARGS({"Address","s"}, { "Address Type", "y" },
+ { "Adv Type", "y"}, { "RSSI", "i"},
+ { "AdvDataLen", "i"}, { "AdvData", "ay"})) },
+ { GDBUS_SIGNAL("LEDataLengthChanged",
+ GDBUS_ARGS({"max_tx_octets","q"},
+ { "max_tx_time", "q" },
+ { "max_rx_octets", "q"},
+ { "max_rx_time", "q"})) },
+ { GDBUS_SIGNAL("IpspStateChanged",
+ GDBUS_ARGS({"connected","b"},{"if_name","s"}))},
+};
+#endif
+
uint8_t btd_device_get_bdaddr_type(struct btd_device *dev)
{
return dev->bdaddr_type;
@@ -2600,16 +3981,38 @@ void device_add_connection(struct btd_device *dev, uint8_t bdaddr_type)
state->connected = true;
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
if (dev->le_state.connected && dev->bredr_state.connected)
return;
g_dbus_emit_property_changed(dbus_conn, dev->path, DEVICE_INTERFACE,
"Connected");
+#else
+#ifdef TIZEN_FEATURE_BLUEZ_BATTERY_WATCH
+ if (bdaddr_type == BDADDR_BREDR &&
+ get_charging_state(dev->adapter) == WIRELESS_CHARGING) {
+ int br_pkt_type = ACL_PTYPE_MASK |
+ HCI_2DH1 | HCI_2DH3 | HCI_2DH5 |
+ HCI_3DH1 | HCI_3DH3 | HCI_3DH5;
+
+ DBG("During wireless charging... Change packet type");
+ device_change_pkt_type(dev, (gpointer)br_pkt_type);
+ }
+#endif /* TIZEN_FEATURE_BLUEZ_BATTERY_WATCH */
+
+ g_dbus_emit_signal(dbus_conn, dev->path,
+ DEVICE_INTERFACE, "DeviceConnected",
+ DBUS_TYPE_BYTE, &bdaddr_type,
+ DBUS_TYPE_INVALID);
+#endif
}
void device_remove_connection(struct btd_device *device, uint8_t bdaddr_type)
{
struct bearer_state *state = get_state(device, bdaddr_type);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ char *dev_name = device->name;
+#endif
if (!state->connected)
return;
@@ -2624,6 +4027,18 @@ void device_remove_connection(struct btd_device *device, uint8_t bdaddr_type)
device->disconn_timer = 0;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (device->browse) {
+ struct browse_req *req = device->browse;
+
+ if ((bdaddr_type == BDADDR_BREDR && req->search_uuid != 0) ||
+ (bdaddr_type != BDADDR_BREDR && req->search_uuid == 0))
+ device->browse = NULL;
+ else
+ DBG("device->browse is for other link");
+ }
+#endif
+
while (device->disconnects) {
DBusMessage *msg = device->disconnects->data;
@@ -2636,11 +4051,20 @@ void device_remove_connection(struct btd_device *device, uint8_t bdaddr_type)
btd_adapter_remove_bonding(device->adapter, &device->bdaddr,
bdaddr_type);
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
if (device->bredr_state.connected || device->le_state.connected)
return;
g_dbus_emit_property_changed(dbus_conn, device->path,
DEVICE_INTERFACE, "Connected");
+#else
+ g_dbus_emit_signal(dbus_conn, device->path,
+ DEVICE_INTERFACE, "Disconnected",
+ DBUS_TYPE_BYTE, &bdaddr_type,
+ DBUS_TYPE_BYTE, &device->disc_reason,
+ DBUS_TYPE_STRING, &dev_name,
+ DBUS_TYPE_INVALID);
+#endif
}
guint device_add_disconnect_watch(struct btd_device *device,
@@ -2808,7 +4232,10 @@ static void load_info(struct btd_device *device, const char *local,
char **uuids;
int source, vendor, product, version;
char **techno, **t;
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ gboolean svc_change_regd;
+ char buf[DEV_MAX_MANUFACTURER_DATA_LEN] = { 0, };
+#endif
/* Load device name from storage info file, if that fails fall back to
* the cache.
*/
@@ -2845,6 +4272,37 @@ static void load_info(struct btd_device *device, const char *local,
g_free(str);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ /* 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);
+ g_free(str);
+
+ str = g_key_file_get_string(key_file, "General", "ManufacturerData", NULL);
+ if (str) {
+ load_manufacturer_data_2digit(str,
+ device->manufacturer_data_len, buf);
+ device->manufacturer_data = g_memdup(buf,
+ device->manufacturer_data_len);
+ g_free(str);
+ }
+ }
+
+ str = g_key_file_get_string(key_file, "General", "IdentityAddress",
+ NULL);
+
+ if (str) {
+ device->rpa = g_malloc0(sizeof(bdaddr_t));
+ bacpy(device->rpa, &device->bdaddr);
+ str2ba(str, &device->bdaddr);
+ g_free(str);
+ }
+#endif
+
/* Load device technology */
techno = g_key_file_get_string_list(key_file, "General",
"SupportedTechnologies", NULL, NULL);
@@ -2886,6 +4344,27 @@ next:
device->trusted = g_key_file_get_boolean(key_file, "General",
"Trusted", NULL);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ /* Load Trusted Profiles*/
+ int trusted_profiles = g_key_file_get_integer(key_file, "General",
+ "TrustedProfiles", NULL);
+ DBG("Loading TrustedProfiles %d", trusted_profiles);
+ device->trusted_profiles.pbap = ((trusted_profiles &
+ (PROFILE_SUPPORTED << PBAP_SHIFT_OFFSET)) >> PBAP_SHIFT_OFFSET);
+ device->trusted_profiles.map = ((trusted_profiles &
+ (PROFILE_SUPPORTED << MAP_SHIFT_OFFSET)) >> MAP_SHIFT_OFFSET);
+ device->trusted_profiles.sap = ((trusted_profiles &
+ (PROFILE_SUPPORTED << SAP_SHIFT_OFFSET)) >> SAP_SHIFT_OFFSET);
+
+ /* Load Restricted Profiles*/
+ int restricted_profiles = g_key_file_get_integer(key_file, "General",
+ "RestrictedProfiles", NULL);
+ DBG("Loading RestrictedProfiles %d", restricted_profiles);
+ device->restricted_profiles.hfp_hs = (restricted_profiles >> HFP_HS_SHIFT_OFFSET) & 0x01;
+ device->restricted_profiles.a2dp = (restricted_profiles >> A2DP_SHIFT_OFFSET) & 0x01;
+
+#endif
+
/* Load device blocked */
blocked = g_key_file_get_boolean(key_file, "General", "Blocked", NULL);
if (blocked)
@@ -2916,6 +4395,15 @@ next:
btd_device_set_pnpid(device, source, vendor, product, version);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ /* Load Service changed Registered flag */
+ svc_change_regd = g_key_file_get_boolean(key_file, "Att",
+ "SvcChangeRegd", NULL);
+
+ bt_att_set_svc_changed_indication_registered(device->att,
+ svc_change_regd);
+#endif
+
if (store_needed)
store_device_info(device);
}
@@ -3342,8 +4830,13 @@ static bool device_match_profile(struct btd_device *device,
return false;
if (g_slist_find_custom(uuids, profile->remote_uuid,
- bt_uuid_strcmp) == NULL)
+ bt_uuid_strcmp) == NULL) {
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+ if (strcmp(profile->name, "hid-device") == 0)
+ return true;
+#endif
return false;
+ }
return true;
}
@@ -3404,12 +4897,35 @@ static void device_add_gatt_services(struct btd_device *device)
gatt_db_foreach_service(device->db, NULL, add_gatt_service, device);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void accept_gatt_service(struct gatt_db_attribute *attr, void *user_data)
+{
+ struct btd_device *device = user_data;
+ GSList *l;
+ bt_uuid_t uuid;
+ char uuid_str[MAX_LEN_UUID_STR];
+
+ gatt_db_attribute_get_service_uuid(attr, &uuid);
+ bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str));
+
+ l = find_service_with_uuid(device->services, uuid_str);
+ if (!l)
+ return;
+
+ service_accept(l->data);
+}
+#endif
+
static void device_accept_gatt_profiles(struct btd_device *device)
{
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
GSList *l;
for (l = device->services; l != NULL; l = g_slist_next(l))
service_accept(l->data);
+#else
+ gatt_db_foreach_service(device->db, NULL, accept_gatt_service, device);
+#endif
}
static void device_remove_gatt_service(struct btd_device *device,
@@ -3608,7 +5124,11 @@ static struct btd_device *device_new(struct btd_adapter *adapter,
if (g_dbus_register_interface(dbus_conn,
device->path, DEVICE_INTERFACE,
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ device_methods, device_signals,
+#else
device_methods, NULL,
+#endif
device_properties, device,
device_free) == FALSE) {
error("Unable to register device interface for %s", address);
@@ -3625,6 +5145,28 @@ static struct btd_device *device_new(struct btd_adapter *adapter,
return btd_device_ref(device);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void device_print_addr(struct btd_device *dev)
+{
+ char ida[18];
+ char rpa[18];
+
+ ba2str(&dev->bdaddr, ida);
+
+ if (dev->rpa) {
+ ba2str(dev->rpa, rpa);
+
+ DBG("IDA %s [%d] : RPA [%s], BREDR [%d], LE [%d]",
+ ida, dev->bdaddr_type, rpa,
+ dev->bredr ? 1 : 0, dev->le ? 1 : 0);
+ } else {
+ DBG("ADDR %s [%d] : BREDR [%d], LE [%d]",
+ ida, dev->bdaddr_type,
+ dev->bredr ? 1 : 0, dev->le ? 1 : 0);
+ }
+}
+#endif
+
struct btd_device *device_create_from_storage(struct btd_adapter *adapter,
const char *address, GKeyFile *key_file)
{
@@ -3697,6 +5239,11 @@ char *btd_device_get_storage_path(struct btd_device *device,
ba2str(btd_adapter_get_address(device->adapter), srcaddr);
ba2str(&device->bdaddr, dstaddr);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (device->rpa)
+ ba2str(device->rpa, dstaddr);
+#endif
+
if (!filename)
return g_strdup_printf(STORAGEDIR "/%s/%s", srcaddr, dstaddr);
@@ -3778,6 +5325,23 @@ void device_update_addr(struct btd_device *device, const bdaddr_t *bdaddr,
void device_set_bredr_support(struct btd_device *device)
{
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ char addr_str[18];
+
+ if (device->rpa) {
+ ba2str(device->rpa, addr_str);
+ error("Cannot set bredr support to RPA device [%s]", addr_str);
+ return;
+ }
+
+ if (device->bdaddr_type == BDADDR_LE_RANDOM) {
+ ba2str(&device->bdaddr, addr_str);
+ error("Cannot set bredr support to LE random device [%s]",
+ addr_str);
+ return;
+ }
+#endif
+
if (device->bredr)
return;
@@ -3785,6 +5349,22 @@ void device_set_bredr_support(struct btd_device *device)
store_device_info(device);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void device_set_rpa(struct btd_device *device, const bdaddr_t *rpa)
+{
+ if (device->rpa == NULL) {
+ device->rpa = g_malloc0(sizeof(bdaddr_t));
+ bacpy(device->rpa, rpa);
+ } else
+ error("RPA is already set");
+}
+
+void device_set_irk_value(struct btd_device *device, const uint8_t *val)
+{
+ memcpy(&device->irk_val, val, sizeof(device->irk_val));
+}
+#endif
+
void device_set_le_support(struct btd_device *device, uint8_t bdaddr_type)
{
if (device->le)
@@ -3925,6 +5505,11 @@ static void device_remove_stored(struct btd_device *device)
ba2str(src, adapter_addr);
ba2str(&device->bdaddr, device_addr);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (device->rpa)
+ ba2str(device->rpa, device_addr);
+#endif
+
snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s", adapter_addr,
device_addr);
delete_folder_tree(filename);
@@ -3946,6 +5531,102 @@ static void device_remove_stored(struct btd_device *device)
g_key_file_free(key_file);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void device_unpair(struct btd_device *device, gboolean remove_stored)
+{
+ DBG("+");
+ DBG("Unpairing device %s", device->path);
+
+ if (device->bonding) {
+ uint8_t status;
+
+ if (device->bredr_state.connected)
+ status = MGMT_STATUS_DISCONNECTED;
+ else
+ status = MGMT_STATUS_CONNECT_FAILED;
+
+ device_cancel_bonding(device, status);
+ }
+
+ if (device->browse)
+ browse_request_cancel(device->browse);
+
+
+// while (device->services != NULL) {
+// struct btd_service *service = device->services->data;
+//
+// device->services = g_slist_remove(device->services, service);
+// service_remove(service);
+// }
+
+ g_slist_free(device->pending);
+ device->pending = NULL;
+
+ if (btd_device_is_connected(device))
+ disconnect_all(device);
+
+ if (device->store_id > 0) {
+ g_source_remove(device->store_id);
+ device->store_id = 0;
+
+ if (!remove_stored)
+ store_device_info_cb(device);
+ }
+
+ if (remove_stored)
+ device_remove_stored(device);
+
+ gatt_db_clear(device->db);
+
+ if (device->rpa) {
+ bacpy(&device->bdaddr, device->rpa);
+ device->bdaddr_type = BDADDR_LE_RANDOM;
+
+ g_free(device->rpa);
+ device->rpa = NULL;
+ }
+
+ device->bredr_state.paired = 0;
+ device->le_state.paired = 0;
+ device->bredr_state.svc_resolved = false;
+ device->trusted = false;
+ device->trusted_profiles.pbap = SHOW_AUTHORIZATION;
+ device->trusted_profiles.map = SHOW_AUTHORIZATION;
+ device->trusted_profiles.sap = SHOW_AUTHORIZATION;
+ if (device->alias != NULL) {
+ /* Remove alias name because
+ * In UG if we rename and then unpair device and
+ * initiates connection without scanning then paired
+ * list will have alias name as first preference is
+ * given to alias name.
+ */
+ DBG("Freeing device alias name");
+ g_free(device->alias);
+ device->alias = NULL;
+ }
+ g_dbus_emit_property_changed(dbus_conn, device->path,
+ DEVICE_INTERFACE, "Paired");
+// btd_device_unref(device);
+ DBG("-");
+ }
+
+void device_remove_stored_folder(struct btd_device *device)
+{
+ const bdaddr_t *src = btd_adapter_get_address(device->adapter);
+ char adapter_addr[18];
+ char device_addr[18];
+ char filename[PATH_MAX];
+
+ ba2str(src, adapter_addr);
+ ba2str(&device->bdaddr, device_addr);
+
+ snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s", adapter_addr,
+ device_addr);
+
+ delete_folder_tree(filename);
+}
+#endif
+
void device_remove(struct btd_device *device, gboolean remove_stored)
{
DBG("Removing device %s", device->path);
@@ -3993,6 +5674,30 @@ void device_remove(struct btd_device *device, gboolean remove_stored)
btd_device_unref(device);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+int device_rpa_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct btd_device *device = a;
+ const char *address = b;
+ char addr[18];
+
+ if (!device->rpa)
+ return -1;
+
+ ba2str(device->rpa, addr);
+
+ return strcasecmp(addr, address);
+}
+
+int device_addr_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct btd_device *device = a;
+ const bdaddr_t *bdaddr = b;
+
+ return bacmp(&device->bdaddr, bdaddr);
+}
+#endif
+
int device_address_cmp(gconstpointer a, gconstpointer b)
{
const struct btd_device *device = a;
@@ -4019,6 +5724,36 @@ static bool addr_is_public(uint8_t addr_type)
return false;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+int device_addr_type_strict_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct btd_device *dev = a;
+ const struct device_addr_type *addr = b;
+ int cmp;
+
+ cmp = bacmp(&dev->bdaddr, &addr->bdaddr);
+
+ if (addr->bdaddr_type == BDADDR_BREDR) {
+ if (!dev->bredr)
+ return -1;
+
+ return cmp;
+ }
+
+ if (!dev->le)
+ return -1;
+
+ if (cmp && dev->rpa && addr->bdaddr_type == BDADDR_LE_RANDOM &&
+ (addr->bdaddr.b[5] >> 6) == 0x01)
+ return bacmp(dev->rpa, &addr->bdaddr);
+
+ if (addr->bdaddr_type != dev->bdaddr_type)
+ return -1;
+
+ return cmp;
+}
+#endif
+
int device_addr_type_cmp(gconstpointer a, gconstpointer b)
{
const struct btd_device *dev = a;
@@ -4034,7 +5769,20 @@ int device_addr_type_cmp(gconstpointer a, gconstpointer b)
*/
if (!cmp && addr_is_public(addr->bdaddr_type) &&
addr_is_public(dev->bdaddr_type))
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ {
+ if (dev->rpa && addr->bdaddr_type == BDADDR_BREDR) {
+ char addr_str[18];
+
+ ba2str(&dev->bdaddr, addr_str);
+ DBG("Don't match. LE Only device [%s]", addr_str);
+ return -1;
+ }
+ return 0;
+ }
+#else
return 0;
+#endif
if (addr->bdaddr_type == BDADDR_BREDR) {
if (!dev->bredr)
@@ -4052,6 +5800,68 @@ int device_addr_type_cmp(gconstpointer a, gconstpointer b)
return cmp;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#ifdef TIZEN_FEATURE_BLUEZ_BATTERY_WATCH
+void device_change_pkt_type(gpointer data, gpointer user_data)
+{
+ int pkt_type = (int)user_data;
+ struct btd_device *device = data;
+ struct hci_conn_info_req *cr;
+ set_conn_ptype_cp cp;
+ char addr[18];
+ int hdev = 0;
+ int err = 0;
+
+ /* Change a packet type only for Phone device */
+ if ((device->class & 0x00001F00) >> 8 != 0x02)
+ return;
+
+ if (!device->bredr_state.connected)
+ return;
+
+ hdev = hci_open_dev(0);
+ if (hdev < 0) {
+ error("Cannot open hdev");
+ return;
+ }
+
+ cr = g_malloc0(sizeof(*cr) + sizeof(struct hci_conn_info));
+ if (cr == NULL) {
+ error("Out of memory");
+ return;
+ }
+ cr->type = ACL_LINK;
+ bacpy(&cr->bdaddr, &device->bdaddr);
+
+ err = ioctl(hdev, HCIGETCONNINFO, cr);
+ if (err < 0) {
+ error("Fail to get HCIGETCOINFO");
+ g_free(cr);
+ hci_close_dev(hdev);
+ return;
+ }
+
+ cp.handle = cr->conn_info->handle;
+ g_free(cr);
+ cp.pkt_type = cpu_to_le16((uint16_t)pkt_type);
+
+ ba2str(&device->bdaddr, addr);
+ DBG("Handle %d, Addr %s", cp.handle, addr);
+ DBG("Send Change pkt type request : 0x%X", pkt_type);
+
+ if (hci_send_cmd(hdev, OGF_LINK_CTL, OCF_SET_CONN_PTYPE,
+ SET_CONN_PTYPE_CP_SIZE, &cp) < 0) {
+ error("hci_send_cmd is failed");
+ hci_close_dev(hdev);
+ return;
+ }
+
+ hci_close_dev(hdev);
+ return;
+}
+#endif /* TIZEN_FEATURE_BLUEZ_BATTERY_WATCH */
+#endif
+
static gboolean record_has_uuid(const sdp_record_t *rec,
const char *profile_uuid)
{
@@ -4108,8 +5918,10 @@ static struct btd_service *probe_service(struct btd_device *device,
/* Only set auto connect if profile has set the flag and can really
* accept connections.
*/
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
if (profile->auto_connect && profile->accept)
device_set_auto_connect(device, TRUE);
+#endif
return service;
}
@@ -4119,6 +5931,13 @@ static void dev_probe(struct btd_profile *p, void *user_data)
struct probe_data *d = user_data;
struct btd_service *service;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (find_service_with_profile(d->dev->services, p)) {
+ DBG("%s is already probed.(UUID:%s)", p->name, p->remote_uuid);
+ return;
+ }
+#endif
+
service = probe_service(d->dev, p, d->uuids);
if (!service)
return;
@@ -4155,8 +5974,30 @@ void device_remove_profile(gpointer a, gpointer b)
GSList *l;
l = find_service_with_profile(device->services, profile);
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+ if (l == NULL) {
+ if (g_strcmp0(profile->local_uuid , HID_DEVICE_UUID) == 0) {
+ l = find_service_with_uuid(device->services,
+ HID_DEVICE_UUID);
+ if (l == NULL)
+ return;
+
+ service = l->data;
+
+ if (btd_service_get_state(service) ==
+ BTD_SERVICE_STATE_CONNECTED) {
+ int err;
+ err = btd_service_disconnect(service);
+ if (err)
+ error("error: %s", strerror(-err));
+ }
+ }
+ return;
+ }
+#else
if (l == NULL)
return;
+#endif
service = l->data;
device->services = g_slist_delete_link(device->services, l);
@@ -4513,6 +6354,19 @@ static void search_cb(sdp_list_t *recs, int err, gpointer user_data)
DEVICE_INTERFACE, "UUIDs");
send_reply:
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (!req->msg)
+ goto done;
+
+ /* since no new services are found, UUID signal is not emitted,
+ ** so send a reply to the framework with the existing services */
+ if (dbus_message_is_method_call(req->msg, DEVICE_INTERFACE,
+ "DiscoverServices"))
+ discover_services_reply(req, err, device->tmp_records);
+
+done:
+#endif
+
device_svc_resolved(device, BDADDR_BREDR, err);
}
@@ -4598,9 +6452,26 @@ static void att_disconnected_cb(int err, void *user_data)
adapter_connect_list_add(device->adapter, device);
done:
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ device_set_gatt_connected(device, FALSE);
+#endif
attio_cleanup(device);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void att_mtu_changed(uint16_t mtu, void *user_data)
+{
+ struct btd_device *device = user_data;
+
+ DBG("att mtu changed %d", mtu);
+
+ g_dbus_emit_signal(dbus_conn, device->path,
+ DEVICE_INTERFACE, "AttMtuChanged",
+ DBUS_TYPE_UINT16, &mtu,
+ DBUS_TYPE_INVALID);
+}
+#endif
+
static void register_gatt_services(struct btd_device *device)
{
struct browse_req *req = device->browse;
@@ -4617,8 +6488,17 @@ static void register_gatt_services(struct btd_device *device)
btd_device_set_temporary(device, false);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (req) {
+ if (req->search_uuid)
+ DBG("browse req. is for SDP. Ignore it.");
+ else
+ update_gatt_uuids(req, device->primaries, services);
+ }
+#else
if (req)
update_gatt_uuids(req, device->primaries, services);
+#endif
g_slist_free_full(device->primaries, g_free);
device->primaries = NULL;
@@ -4649,6 +6529,15 @@ static void gatt_client_ready_cb(bool success, uint8_t att_ecode,
device_svc_resolved(device, device->bdaddr_type, 0);
store_gatt_db(device);
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (device->name[0] == '\0') {
+ char *name = NULL;
+ name = bt_gatt_client_get_gap_device_name(device->client);
+ if (name)
+ strncpy(device->name, name, MAX_NAME_LENGTH);
+ }
+#endif
}
static void gatt_client_service_changed(uint16_t start_handle,
@@ -4715,6 +6604,15 @@ static void gatt_server_init(struct btd_device *device, struct gatt_db *db)
error("Failed to initialize bt_gatt_server");
bt_gatt_server_set_debug(device->server, gatt_debug, NULL, NULL);
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (!bt_gatt_server_set_mtu_changed(device->server,
+ att_mtu_changed,
+ device, NULL)) {
+ DBG("Failed to set mtu changed handler");
+ return;
+ }
+#endif
}
static bool local_counter(uint32_t *sign_cnt, void *user_data)
@@ -4745,6 +6643,33 @@ static bool remote_counter(uint32_t *sign_cnt, void *user_data)
return true;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static bool load_svc_change_indication_status(struct btd_device *device, const char *local,
+ const char *peer)
+{
+ char filename[PATH_MAX];
+ GKeyFile *key_file;
+ gboolean svc_change_regd = false;
+ snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info", local, peer);
+
+ key_file = g_key_file_new();
+ if (!g_key_file_load_from_file(key_file, filename, 0, NULL))
+ goto failed;
+
+ /* Load Service changed Registered flag */
+ svc_change_regd = g_key_file_get_boolean(key_file, "Att",
+ "SvcChangeRegd", NULL);
+ bt_att_set_svc_changed_indication_registered(device->att,
+ svc_change_regd);
+
+
+failed:
+ g_key_file_free(key_file);
+
+ return svc_change_regd;
+}
+#endif
+
bool device_attach_att(struct btd_device *dev, GIOChannel *io)
{
GError *gerr = NULL;
@@ -4755,10 +6680,16 @@ bool device_attach_att(struct btd_device *dev, GIOChannel *io)
struct btd_gatt_database *database;
const bdaddr_t *src, *dst;
char srcaddr[18], dstaddr[18];
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ uint8_t dst_type = BDADDR_BREDR;
+#endif
bt_io_get(io, &gerr, BT_IO_OPT_SEC_LEVEL, &sec_level,
BT_IO_OPT_IMTU, &mtu,
BT_IO_OPT_CID, &cid,
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ BT_IO_OPT_DEST_TYPE, &dst_type,
+#endif
BT_IO_OPT_INVALID);
if (gerr) {
@@ -4767,6 +6698,7 @@ bool device_attach_att(struct btd_device *dev, GIOChannel *io)
return false;
}
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
if (sec_level == BT_IO_SEC_LOW && dev->le_state.paired) {
DBG("Elevating security level since LTK is available");
@@ -4779,6 +6711,7 @@ bool device_attach_att(struct btd_device *dev, GIOChannel *io)
return false;
}
}
+#endif
dev->att_mtu = MIN(mtu, BT_ATT_MAX_LE_MTU);
attrib = g_attrib_new(io, BT_ATT_DEFAULT_LE_MTU, false);
@@ -4804,6 +6737,16 @@ bool device_attach_att(struct btd_device *dev, GIOChannel *io)
bt_att_set_remote_key(dev->att, dev->remote_csrk->key,
remote_counter, dev);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (dst_type != BDADDR_BREDR && device_get_rpa_exist(dev) == true) {
+ bt_att_set_remote_addr(dev->att,
+ device_get_rpa(dev), BDADDR_LE_RANDOM);
+ } else {
+ bt_att_set_remote_addr(dev->att,
+ &dev->bdaddr, dev->bdaddr_type);
+ }
+#endif
+
database = btd_adapter_get_database(dev->adapter);
src = btd_adapter_get_address(dev->adapter);
@@ -4825,6 +6768,11 @@ bool device_attach_att(struct btd_device *dev, GIOChannel *io)
*/
adapter_connect_list_remove(dev->adapter, dev);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ /* load the service changed indication status on connection */
+ load_svc_change_indication_status(dev, srcaddr, dstaddr);
+#endif
+
return true;
}
@@ -4988,6 +6936,9 @@ static int device_browse_gatt(struct btd_device *device, DBusMessage *msg)
struct btd_adapter *adapter = device->adapter;
struct browse_req *req;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ DBG("");
+#endif
req = browse_request_new(device, msg);
if (!req)
return -EBUSY;
@@ -5060,6 +7011,9 @@ static int device_browse_sdp(struct btd_device *device, DBusMessage *msg)
uuid_t uuid;
int err;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ DBG("");
+#endif
req = browse_request_new(device, msg);
if (!req)
return -EBUSY;
@@ -5072,6 +7026,9 @@ static int device_browse_sdp(struct btd_device *device, DBusMessage *msg)
&device->bdaddr, &uuid, browse_cb, req, NULL,
req->sdp_flags);
if (err < 0) {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ device->browse = NULL;
+#endif
browse_request_free(req);
return err;
}
@@ -5079,6 +7036,123 @@ static int device_browse_sdp(struct btd_device *device, DBusMessage *msg)
return err;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void device_set_last_addr_type(struct btd_device *device, uint8_t type)
+{
+ if (!device)
+ return;
+
+ //DBG("Last addr type %d", type);
+
+ device->last_bdaddr_type = type;
+}
+
+gboolean device_is_ipsp_connected(struct btd_device * device)
+{
+ return device->ipsp_connected;
+}
+
+void device_set_ipsp_connected(struct btd_device *device, gboolean connected,
+ const unsigned char *ifname)
+{
+ char *iface_name = NULL;
+
+ if (device == NULL) {
+ error("device is NULL");
+ return;
+ }
+
+ if (device->ipsp_connected == connected)
+ return;
+
+ device->ipsp_connected = connected;
+
+ memset(device->if_name, 0, sizeof(device->if_name));
+ memcpy(device->if_name, ifname, 16);
+ iface_name = device->if_name;
+
+ DBG("ipsp_connected %d", connected);
+ DBG("ipsp_iface: %s is Up !", iface_name);
+
+ g_dbus_emit_signal(dbus_conn, device->path,
+ DEVICE_INTERFACE, "IpspStateChanged",
+ DBUS_TYPE_BOOLEAN, &connected,
+ DBUS_TYPE_STRING, &iface_name,
+ DBUS_TYPE_INVALID);
+}
+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)
+{
+ if (device == NULL) {
+ error("device is NULL");
+ return;
+ }
+
+ device->max_tx_octets = max_tx_octets;
+ device->max_tx_time = max_tx_time;
+ device->max_rx_octets = max_rx_octets;
+ device->max_rx_time = max_rx_time;
+
+ DBG("data length changed values :max_tx_octets: %d max_tx_time: %d max_rx_octets: %d max_rx_time: %d",
+ max_tx_octets, max_tx_time, max_rx_octets, max_rx_time);
+
+ g_dbus_emit_signal(dbus_conn, device->path,
+ DEVICE_INTERFACE, "LEDataLengthChanged",
+ DBUS_TYPE_UINT16, &max_tx_octets,
+ DBUS_TYPE_UINT16, &max_tx_time,
+ DBUS_TYPE_UINT16, &max_rx_octets,
+ DBUS_TYPE_UINT16, &max_rx_time,
+ DBUS_TYPE_INVALID);
+}
+
+const bdaddr_t *device_get_rpa(struct btd_device *device)
+{
+ return device->rpa;
+}
+
+const uint8_t *device_get_irk_value(struct btd_device *device)
+{
+ return device->irk_val;
+}
+
+bool device_get_rpa_exist(struct btd_device *device)
+{
+ return device->rpa ? true : false;
+}
+
+void device_set_auth_addr_type(struct btd_device *device, uint8_t type)
+{
+ if (!device)
+ return;
+
+ DBG("Auth addr type %d", type);
+
+ device->auth_bdaddr_type = type;
+}
+
+void device_get_tizen_addr(struct btd_device *device, uint8_t type,
+ struct device_addr_type *addr)
+{
+ if (!device || !addr)
+ return;
+
+ if (type == BDADDR_BREDR) {
+ bacpy(&addr->bdaddr, &device->bdaddr);
+ addr->bdaddr_type = BDADDR_BREDR;
+ return;
+ }
+
+ if (device->rpa) {
+ bacpy(&addr->bdaddr, device->rpa);
+ addr->bdaddr_type = BDADDR_LE_RANDOM;
+ return;
+ }
+
+ bacpy(&addr->bdaddr, &device->bdaddr);
+ addr->bdaddr_type = device->bdaddr_type;
+}
+#endif
+
int device_discover_services(struct btd_device *device)
{
int err;
@@ -5168,6 +7242,50 @@ void btd_device_set_trusted(struct btd_device *device, gboolean trusted)
DEVICE_INTERFACE, "Trusted");
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void btd_device_set_trusted_profiles(struct btd_device *device,
+ uint32_t pbap, uint32_t map, uint32_t sap)
+{
+ if (!device)
+ return;
+ DBG("TrustedProfiles Parameters: [PBAP %d] [MAP %d] [SAP %d]", pbap, map, sap);
+
+ if (device->trusted_profiles.pbap == pbap &&
+ device->trusted_profiles.map == map &&
+ device->trusted_profiles.sap == sap)
+ return;
+
+ device->trusted_profiles.pbap = pbap;
+ device->trusted_profiles.map = map;
+ device->trusted_profiles.sap = sap;
+
+ store_device_info(device);
+
+ g_dbus_emit_property_changed(dbus_conn, device->path,
+ DEVICE_INTERFACE, "TrustedProfiles");
+}
+
+void btd_device_set_restricted_profiles(struct btd_device *device,
+ uint32_t hfp_hs, uint32_t a2dp)
+{
+ if (!device)
+ return;
+ DBG("RestrictedProfiles Parameters: [HFP %d] [A2DP %d]", hfp_hs, a2dp);
+
+ if (device->restricted_profiles.hfp_hs == hfp_hs &&
+ device->restricted_profiles.a2dp == a2dp)
+ return;
+
+ device->restricted_profiles.hfp_hs = hfp_hs;
+ device->restricted_profiles.a2dp = a2dp;
+
+ store_device_info(device);
+
+ g_dbus_emit_property_changed(dbus_conn, device->path,
+ DEVICE_INTERFACE, "RestrictedProfiles");
+}
+#endif
+
void device_set_bonded(struct btd_device *device, uint8_t bdaddr_type)
{
if (!device)
@@ -5205,6 +7323,15 @@ void device_set_rssi_with_delta(struct btd_device *device, int8_t rssi,
if (!device)
return;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (rssi == 0 || device->rssi == 0) {
+ if (device->rssi == rssi)
+ return;
+ }
+
+ device->rssi = rssi;
+ DBG("rssi %d", rssi);
+#else
if (rssi == 0 || device->rssi == 0) {
if (device->rssi == rssi)
return;
@@ -5228,6 +7355,7 @@ void device_set_rssi_with_delta(struct btd_device *device, int8_t rssi,
device->rssi = rssi;
}
+#endif
g_dbus_emit_property_changed(dbus_conn, device->path,
DEVICE_INTERFACE, "RSSI");
@@ -5378,14 +7506,36 @@ void device_bonding_complete(struct btd_device *device, uint8_t bdaddr_type,
if (status) {
device_cancel_authentication(device, TRUE);
device_bonding_failed(device, status);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ device->legacy_pairing = false;
+#endif
return;
}
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ device->legacy_pairing = false;
+#endif
device_auth_req_free(device);
/* If we're already paired nothing more is needed */
- if (state->paired)
+ if (state->paired) {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (bdaddr_type == BDADDR_BREDR && state->svc_resolved) {
+ DBG("Link key has been changed. Report it");
+ if (!device->rpa)
+ g_dbus_emit_property_changed(dbus_conn,
+ device->path, DEVICE_INTERFACE,
+ "Paired");
+ else
+ DBG("Just overwrite Link key");
+ } else if (bdaddr_type == BDADDR_LE_RANDOM ||
+ bdaddr_type == BDADDR_LE_PUBLIC) {
+ DBG("Long Term Key has been changed. Report it");
+ g_dbus_emit_property_changed(dbus_conn, device->path,
+ DEVICE_INTERFACE, "Paired");
+ }
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
return;
+ }
device_set_paired(device, bdaddr_type);
@@ -5473,6 +7623,13 @@ unsigned int device_wait_for_svc_complete(struct btd_device *dev,
g_source_remove(dev->discov_timer);
dev->discov_timer = g_idle_add(start_discovery, dev);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ else if (!dev->browse) {
+ DBG("Service is not going on. Start discovery");
+ dev->discov_timer = g_idle_add(start_discovery, dev);
+ } else
+ DBG("Wait for service discovery");
+#endif
return cb->id;
}
@@ -5578,8 +7735,19 @@ void device_bonding_failed(struct btd_device *device, uint8_t status)
DBG("status %u", status);
- if (!bonding)
+ if (!bonding) {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (device->legacy_pairing) {
+ DBG("Emit LegacyPaired");
+ g_dbus_emit_property_changed(dbus_conn, device->path,
+ DEVICE_INTERFACE, "LegacyPaired");
+ }
+#endif
return;
+ }
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ btd_device_set_temporary(device, TRUE);
+#endif
if (device->authr)
device_cancel_authentication(device, FALSE);
@@ -5624,9 +7792,16 @@ static void confirm_cb(struct agent *agent, DBusError *err, void *data)
if (auth->agent == NULL)
return;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ btd_adapter_confirm_reply(device->adapter, &device->bdaddr,
+ auth->addr_type,
+ err ? FALSE : TRUE);
+ device_set_auth_addr_type(device, BDADDR_BREDR);
+#else
btd_adapter_confirm_reply(device->adapter, &device->bdaddr,
auth->addr_type,
err ? FALSE : TRUE);
+#endif
agent_unref(device->authr->agent);
device->authr->agent = NULL;
@@ -5645,8 +7820,14 @@ static void passkey_cb(struct agent *agent, DBusError *err,
if (err)
passkey = INVALID_PASSKEY;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ btd_adapter_passkey_reply(device->adapter, &device->bdaddr,
+ auth->addr_type, passkey);
+ device_set_auth_addr_type(device, BDADDR_BREDR);
+#else
btd_adapter_passkey_reply(device->adapter, &device->bdaddr,
auth->addr_type, passkey);
+#endif
agent_unref(device->authr->agent);
device->authr->agent = NULL;
@@ -5751,10 +7932,12 @@ int device_confirm_passkey(struct btd_device *device, uint8_t type,
auth->passkey = passkey;
+#ifndef TIZEN_FEATURE_BLUEZ_CONFIRM_ONLY
if (confirm_hint)
err = agent_request_authorization(auth->agent, device,
confirm_cb, auth, NULL);
else
+#endif
err = agent_request_confirmation(auth->agent, device, passkey,
confirm_cb, auth, NULL);
@@ -5866,12 +8049,23 @@ void device_cancel_authentication(struct btd_device *device, gboolean aborted)
cancel_authentication(auth);
device_auth_req_free(device);
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ device_set_auth_addr_type(device, BDADDR_BREDR);
+#endif
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+gboolean device_is_authenticating(struct btd_device *dev, uint8_t bdaddr_type)
+{
+ return (dev->auth_bdaddr_type == bdaddr_type && dev->authr != NULL);
+}
+#else
gboolean device_is_authenticating(struct btd_device *device)
{
return (device->authr != NULL);
}
+#endif
struct gatt_primary *btd_device_get_primary(struct btd_device *device,
const char *uuid)
@@ -5966,6 +8160,11 @@ static sdp_list_t *read_device_records(struct btd_device *device)
ba2str(btd_adapter_get_address(device->adapter), local);
ba2str(&device->bdaddr, peer);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (device->rpa)
+ ba2str(device->rpa, peer);
+#endif
+
snprintf(filename, PATH_MAX, STORAGEDIR "/%s/cache/%s", local, peer);
key_file = g_key_file_new();
@@ -6053,6 +8252,126 @@ void device_set_appearance(struct btd_device *device, uint16_t value)
store_device_info(device);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+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)
+ return;
+
+ if (eir->manufacturer_data_len == 0)
+ return;
+
+ device->manufacturer_data = g_memdup(eir->manufacturer_data,
+ eir->manufacturer_data_len);
+ device->manufacturer_data_len = eir->manufacturer_data_len;
+
+ store_device_info(device);
+
+ g_dbus_emit_property_changed(dbus_conn, device->path,
+ DEVICE_INTERFACE, "ManufacturerDataLen");
+
+ g_dbus_emit_property_changed(dbus_conn, device->path,
+ DEVICE_INTERFACE, "ManufacturerData");
+}
+
+
+void device_set_adv_report_info(struct btd_device *device, void *data, uint8_t data_len,
+ uint8_t adv_type, int8_t rssi)
+{
+ if (!device)
+ return;
+
+ char peer_addr[18];
+ const char *paddr = peer_addr;
+ dbus_int32_t rssi_val = rssi;
+ int adv_len = data_len;
+ uint8_t addr_type;
+
+ ba2str(&device->bdaddr, peer_addr);
+
+ /* Replace address type for paired RPA device since IDA passed from controller */
+ if (device->rpa)
+ addr_type = BDADDR_LE_RANDOM;
+ else
+ addr_type = device->bdaddr_type;
+
+ g_dbus_emit_signal(dbus_conn, device->path,
+ DEVICE_INTERFACE, "AdvReport",
+ DBUS_TYPE_STRING, &paddr,
+ DBUS_TYPE_BYTE, &addr_type,
+ DBUS_TYPE_BYTE, &adv_type,
+ DBUS_TYPE_INT32, &rssi_val,
+ DBUS_TYPE_INT32, &adv_len,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &data, data_len,
+ DBUS_TYPE_INVALID);
+}
+
+void device_set_payload_timeout(struct btd_device *device,
+ uint16_t payload_timeout)
+{
+ if (!device)
+ return;
+ if (device->auth_payload_timeout == payload_timeout)
+ return;
+
+ DBG("Payload timeout %d", payload_timeout);
+
+ device->auth_payload_timeout = payload_timeout;
+ g_dbus_emit_property_changed(dbus_conn, device->path,
+ DEVICE_INTERFACE, "PayloadTimeout");
+}
+
+void device_set_disconnect_reason(struct btd_device *device, uint8_t reason)
+{
+ device->disc_reason = reason;
+}
+
+void btd_device_disconnect(struct btd_device *device)
+{
+ char dst[18];
+ struct btd_service *service;
+ btd_service_state_t state;
+
+ ba2str(&device->bdaddr, dst);
+
+ DBG("");
+ if (device->bredr_state.connected == false)
+ return;
+
+ service = btd_device_get_service(device, HFP_HS_UUID);
+ if (!service)
+ return;
+
+ state = btd_service_get_state(service);
+ DBG("Connected State : %d", state);
+
+ if (state == BTD_SERVICE_STATE_DISCONNECTED) {
+ btd_adapter_disconnect_device(device->adapter, &device->bdaddr,
+ BDADDR_BREDR);
+ }
+
+ return;
+}
+
+#endif
+
void btd_device_set_pnpid(struct btd_device *device, uint16_t source,
uint16_t vendor, uint16_t product, uint16_t version)
{
@@ -6083,8 +8402,26 @@ static void service_state_changed(struct btd_service *service,
struct btd_device *device = btd_service_get_device(service);
int err = btd_service_get_error(service);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (!err) {
+ if (old_state == BTD_SERVICE_STATE_UNAVAILABLE ||
+ new_state == BTD_SERVICE_STATE_UNAVAILABLE)
+ DBG("Skip status updating ([%d] -> [%d])", old_state, new_state);
+ else
+ g_dbus_emit_signal(dbus_conn, device->path,
+ DEVICE_INTERFACE, "ProfileStateChanged",
+ DBUS_TYPE_STRING, &profile->remote_uuid,
+ DBUS_TYPE_INT32, &new_state,
+ DBUS_TYPE_INVALID);
+ }
+
+ if (new_state == BTD_SERVICE_STATE_CONNECTING ||
+ new_state == BTD_SERVICE_STATE_DISCONNECTING ||
+ new_state == BTD_SERVICE_STATE_UNAVAILABLE)
+#else
if (new_state == BTD_SERVICE_STATE_CONNECTING ||
new_state == BTD_SERVICE_STATE_DISCONNECTING)
+#endif
return;
if (old_state == BTD_SERVICE_STATE_CONNECTING)
@@ -6104,6 +8441,13 @@ struct btd_service *btd_device_get_service(struct btd_device *dev,
if (g_str_equal(p->remote_uuid, remote_uuid))
return service;
+
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+ if (g_str_equal(HID_UUID, remote_uuid)) {
+ if (strcmp(p->name, "hid-device") == 0)
+ return service;
+ }
+#endif
}
return NULL;
@@ -6120,3 +8464,21 @@ void btd_device_cleanup(void)
{
btd_service_remove_state_cb(service_state_cb_id);
}
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void btd_device_set_legacy_pairing(struct btd_device *dev, bool legacy_pairing)
+{
+ dev->legacy_pairing = legacy_pairing;
+}
+
+void btd_device_set_svc_changed_indication(struct btd_device *dev, bool value)
+{
+ bt_att_set_svc_changed_indication_registered(dev->att, value);
+ store_device_info(dev);
+}
+
+bool btd_device_get_svc_changed_indication(struct btd_device *dev)
+{
+ return bt_att_get_svc_changed_indication_registered(dev->att);
+}
+#endif
diff --git a/src/device.h b/src/device.h
index 3cab366e..ff39856e 100755
--- a/src/device.h
+++ b/src/device.h
@@ -26,6 +26,13 @@
struct btd_device;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+/* Device Physical channel connection Type */
+#define DEV_CONN_DEFAULT 0xFF /* Represents support for BREDR and LE */
+#define DEV_CONN_BREDR 0x00 /* Only BREDR */
+#define DEV_CONN_LE 0x01 /* Only LE*/
+#endif
+
struct btd_device *device_create(struct btd_adapter *adapter,
const bdaddr_t *address, uint8_t bdaddr_type);
struct btd_device *device_create_from_storage(struct btd_adapter *adapter,
@@ -59,6 +66,9 @@ struct device_addr_type {
uint8_t bdaddr_type;
};
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+int device_addr_type_strict_cmp(gconstpointer a, gconstpointer b);
+#endif
int device_addr_type_cmp(gconstpointer a, gconstpointer b);
GSList *btd_device_get_uuids(struct btd_device *device);
void device_probe_profiles(struct btd_device *device, GSList *profiles);
@@ -83,6 +93,26 @@ void device_remove_profile(gpointer a, gpointer b);
struct btd_adapter *device_get_adapter(struct btd_device *device);
const bdaddr_t *device_get_address(struct btd_device *device);
const char *device_get_path(const struct btd_device *device);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void device_set_remote_feature_flag(struct btd_device *device, int flags);
+gboolean device_is_bredrle(struct btd_device *device);
+void device_set_disconnect_reason(struct btd_device *device, uint8_t reason);
+void device_set_gatt_connected(struct btd_device *device, gboolean connected);
+void device_unpair(struct btd_device *device, gboolean remove_stored);
+gboolean device_get_gatt_connected(const struct btd_device *device);
+void device_set_rpa(struct btd_device *device, const bdaddr_t *rpa_addr);
+const bdaddr_t *device_get_rpa(struct btd_device *device);
+bool device_get_rpa_exist(struct btd_device *device);
+int device_rpa_cmp(gconstpointer a, gconstpointer b);
+int device_addr_cmp(gconstpointer a, gconstpointer b);
+void device_remove_stored_folder(struct btd_device *device);
+const uint8_t *device_get_irk_value(struct btd_device *device);
+void device_set_irk_value(struct btd_device *device, const uint8_t *val);
+void device_set_conn_update_state(struct btd_device *device, bool state);
+bool device_get_conn_update_state(struct btd_device *device);
+void btd_device_set_trusted_profiles(struct btd_device *device,
+ uint32_t pbap, uint32_t map, uint32_t sap);
+#endif
gboolean device_is_temporary(struct btd_device *device);
bool device_is_paired(struct btd_device *device, uint8_t bdaddr_type);
bool device_is_bonded(struct btd_device *device, uint8_t bdaddr_type);
@@ -119,7 +149,11 @@ int device_notify_passkey(struct btd_device *device, uint8_t type,
int device_notify_pincode(struct btd_device *device, gboolean secure,
const char *pincode);
void device_cancel_authentication(struct btd_device *device, gboolean aborted);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+gboolean device_is_authenticating(struct btd_device *dev, uint8_t bdaddr_type);
+#else
gboolean device_is_authenticating(struct btd_device *device);
+#endif
void device_add_connection(struct btd_device *dev, uint8_t bdaddr_type);
void device_remove_connection(struct btd_device *device, uint8_t bdaddr_type);
void device_request_disconnect(struct btd_device *device, DBusMessage *msg);
@@ -135,6 +169,27 @@ void device_remove_disconnect_watch(struct btd_device *device, guint id);
int device_get_appearance(struct btd_device *device, uint16_t *value);
void device_set_appearance(struct btd_device *device, uint16_t value);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+struct eir_data;
+void device_set_manufacturer_info(struct btd_device *dev, struct eir_data *eir);
+void device_set_adv_report_info(struct btd_device *device, void *data,
+ uint8_t data_len, uint8_t adv_info, int8_t rssi);
+void device_set_payload_timeout(struct btd_device *device,
+ uint16_t payload_timeout);
+void device_set_auth_addr_type(struct btd_device *device, uint8_t type);
+void device_set_last_addr_type(struct btd_device *device, uint8_t type);
+gboolean device_is_ipsp_connected(struct btd_device * device);
+void device_set_ipsp_connected(struct btd_device *device, gboolean connected,
+ const unsigned char *ifname);
+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);
+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);
+void device_get_tizen_addr(struct btd_device *device, uint8_t type,
+ struct device_addr_type *addr);
+#endif
+
struct btd_device *btd_device_ref(struct btd_device *device);
void btd_device_unref(struct btd_device *device);
@@ -158,6 +213,23 @@ bool device_remove_svc_complete_callback(struct btd_device *dev,
struct btd_service *btd_device_get_service(struct btd_device *dev,
const char *remote_uuid);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void device_print_addr(struct btd_device *dev);
+gboolean device_is_profile_trusted(struct btd_device *device,
+ const char *uuid);
+gboolean device_is_profile_blocked(struct btd_device *device,
+ const char *uuid);
+gboolean device_is_profile_restricted(struct btd_device *device,
+ const char *uuid);
+void btd_device_disconnect(struct btd_device *dev);
+void btd_device_set_legacy_pairing(struct btd_device *dev, bool legacy_pairing);
+void btd_device_set_svc_changed_indication(struct btd_device *dev, bool value);
+bool btd_device_get_svc_changed_indication(struct btd_device *dev);
+#ifdef TIZEN_FEATURE_BLUEZ_BATTERY_WATCH
+void device_change_pkt_type(gpointer data, gpointer user_data);
+#endif /* TIZEN_FEATURE_BLUEZ_BATTERY_WATCH */
+#endif
+
int device_discover_services(struct btd_device *device);
int btd_device_connect_services(struct btd_device *dev, GSList *services);
diff --git a/src/eir.c b/src/eir.c
index c984fa5a..ee5975ab 100755
--- a/src/eir.c
+++ b/src/eir.c
@@ -65,6 +65,10 @@ void eir_data_free(struct eir_data *eir)
eir->msd_list = NULL;
g_slist_free_full(eir->sd_list, sd_free);
eir->sd_list = NULL;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ g_free(eir->manufacturer_data);
+ eir->manufacturer_data = NULL;
+#endif
}
static void eir_parse_uuid16(struct eir_data *eir, const void *data,
@@ -128,6 +132,26 @@ static void eir_parse_uuid128(struct eir_data *eir, const uint8_t *data,
static char *name2utf8(const uint8_t *name, uint8_t len)
{
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ char *utf8_name;
+ char *in_name;
+ char *ptr;
+
+ in_name = g_malloc0(sizeof(char) * (len + 1));
+ /* Fix : NULL_RETURNS */
+ if (in_name == NULL)
+ return NULL;
+ memcpy(in_name, name, sizeof(char) * len);
+ in_name[len] = '\0';
+
+ if (!g_utf8_validate(in_name, -1, (const char **)&ptr))
+ *ptr = '\0';
+
+ utf8_name = g_strdup(in_name);
+ g_free(in_name);
+
+ return utf8_name;
+#else
char utf8_name[HCI_MAX_NAME_LENGTH + 2];
int i;
@@ -147,6 +171,7 @@ static char *name2utf8(const uint8_t *name, uint8_t len)
g_strstrip(utf8_name);
return g_strdup(utf8_name);
+#endif
}
static void eir_parse_msd(struct eir_data *eir, const uint8_t *data,
@@ -343,6 +368,14 @@ void eir_parse(struct eir_data *eir, const uint8_t *eir_data, uint8_t eir_len)
break;
case EIR_MANUFACTURER_DATA:
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (data_len < 1)
+ break;
+
+ eir->manufacturer_data = g_memdup(data,
+ data_len);
+ eir->manufacturer_data_len = data_len;
+#endif
eir_parse_msd(eir, data, data_len);
break;
diff --git a/src/eir.h b/src/eir.h
index 219ee794..5e8bbd52 100755
--- a/src/eir.h
+++ b/src/eir.h
@@ -92,6 +92,10 @@ struct eir_data {
uint16_t did_source;
GSList *msd_list;
GSList *sd_list;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ char *manufacturer_data;
+ uint8_t manufacturer_data_len;
+#endif
};
void eir_data_free(struct eir_data *eir);
diff --git a/src/gatt-client.c b/src/gatt-client.c
index 114981c2..a7d08296 100755
--- a/src/gatt-client.c
+++ b/src/gatt-client.c
@@ -62,6 +62,9 @@ struct btd_gatt_client {
struct queue *services;
struct queue *all_notify_clients;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ guint wait_charcs_id;
+#endif
};
struct service {
@@ -72,6 +75,12 @@ struct service {
bt_uuid_t uuid;
char *path;
struct queue *chrcs;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ bool chrcs_ready;
+ struct queue *pending_ext_props;
+ guint idle_id;
+ guint idle_id2;
+#endif
};
typedef bool (*async_dbus_op_complete_t)(void *data);
@@ -216,6 +225,26 @@ static int parse_value_arg(DBusMessageIter *iter, uint8_t **value, int *len)
return 0;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static int parse_type_value_arg(DBusMessageIter *iter, uint8_t *type, uint8_t **value,
+ int *len)
+{
+ DBusMessageIter array;
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_BYTE)
+ return -EINVAL;
+ dbus_message_iter_get_basic(iter, type);
+ dbus_message_iter_next(iter);
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY)
+ return -EINVAL;
+
+ dbus_message_iter_recurse(iter, &array);
+ dbus_message_iter_get_fixed_array(&array, value, len);
+
+ return 0;
+}
+#endif
+
static void async_dbus_op_free(void *data)
{
struct async_dbus_op *op = data;
@@ -310,6 +339,12 @@ static void async_dbus_op_reply(struct async_dbus_op *op, int err,
if (err) {
reply = err > 0 ? create_gatt_dbus_error(msg, err) :
btd_error_failed(msg, strerror(-err));
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (err > 0)
+ dbus_message_append_args(reply,
+ DBUS_TYPE_BYTE, &err,
+ DBUS_TYPE_INVALID);
+#endif
goto send_reply;
}
@@ -545,6 +580,29 @@ static struct async_dbus_op *start_write_request(DBusMessage *msg,
return op;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static struct async_dbus_op *start_write_cmd(DBusMessage *msg, uint16_t handle,
+ struct bt_gatt_client *gatt, bool signed_write,
+ const uint8_t *value, size_t value_len,
+ void *data, async_dbus_op_complete_t complete)
+{
+ struct async_dbus_op *op;
+
+ op = async_dbus_op_new(msg, data);
+ op->complete = complete;
+
+ op->id = bt_gatt_client_write_without_response_async(gatt, handle,
+ signed_write, value, value_len,
+ write_cb, op, async_dbus_op_free);
+
+ if (!op->id) {
+ async_dbus_op_free(op);
+ return NULL;
+ }
+
+ return op;
+}
+#endif
static bool desc_write_complete(void *data)
{
@@ -824,6 +882,17 @@ static void write_characteristic_cb(struct gatt_db_attribute *attr, int err,
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void notify_characteristic_cb(struct gatt_db_attribute *attr, int err,
+ void *user_data)
+{
+ if (err) {
+ error("Failed to notify_characteristic_cb : %d", err);
+ return;
+ }
+}
+#endif
+
static void chrc_read_cb(bool success, uint8_t att_ecode, const uint8_t *value,
uint16_t length, void *user_data)
{
@@ -939,6 +1008,7 @@ static DBusMessage *characteristic_write_value(DBusConnection *conn,
* - If value is larger than MTU - 3: long-write
* * "write-without-response" property set -> write command.
*/
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
if ((chrc->ext_props & BT_GATT_CHRC_EXT_PROP_RELIABLE_WRITE)) {
supported = true;
chrc->write_op = start_long_write(msg, chrc->value_handle, gatt,
@@ -947,6 +1017,7 @@ static DBusMessage *characteristic_write_value(DBusConnection *conn,
if (chrc->write_op)
return NULL;
}
+#endif
if (chrc->props & BT_GATT_CHRC_PROP_WRITE) {
uint16_t mtu;
@@ -989,6 +1060,78 @@ fail:
return btd_error_not_supported(msg);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static DBusMessage *characteristic_write_value_by_type(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ struct characteristic *chrc = user_data;
+ struct bt_gatt_client *gatt = chrc->service->client->gatt;
+ DBusMessageIter iter;
+ uint8_t *value = NULL;
+ int value_len = 0;
+ bool supported = false;
+ uint8_t write_type = 0;
+ uint16_t offset = 0;
+
+ if (!gatt)
+ return btd_error_failed(msg, "Not connected");
+
+ if (chrc->write_op)
+ return btd_error_in_progress(msg);
+
+ dbus_message_iter_init(msg, &iter);
+
+ if (parse_type_value_arg(&iter, &write_type, &value, &value_len))
+ return btd_error_invalid_args(msg);
+
+ if (parse_options(&iter, &offset))
+ return btd_error_invalid_args(msg);
+
+ if ((write_type & chrc->props) == BT_GATT_CHRC_PROP_WRITE) {
+ uint16_t mtu;
+
+ supported = true;
+ mtu = bt_gatt_client_get_mtu(gatt);
+ if (!mtu)
+ return btd_error_failed(msg, "No ATT transport");
+
+ if (value_len <= mtu - 3 && !offset)
+ chrc->write_op = start_write_request(msg,
+ chrc->value_handle,
+ gatt, value, value_len,
+ chrc, chrc_write_complete);
+ else
+ chrc->write_op = start_long_write(msg,
+ chrc->value_handle, gatt,
+ false, value, value_len, offset,
+ chrc, chrc_write_complete);
+
+ if (chrc->write_op)
+ return NULL;
+ } else if ((write_type & chrc->props) ==
+ BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP) {
+ supported = true;
+ chrc->write_op = start_write_cmd(msg, chrc->value_handle, gatt,
+ false, value, value_len,
+ chrc, chrc_write_complete);
+ if (chrc->write_op)
+ return NULL;
+ } else if ((write_type & chrc->props) == BT_GATT_CHRC_PROP_AUTH) {
+ supported = true;
+ chrc->write_op = start_write_cmd(msg, chrc->value_handle, gatt,
+ true, value, value_len,
+ chrc, chrc_write_complete);
+ if (chrc->write_op)
+ return NULL;
+ }
+
+ if (supported)
+ return btd_error_failed(msg, "Failed to initiate write");
+
+ return btd_error_not_supported(msg);
+}
+#endif
+
struct notify_client {
struct characteristic *chrc;
int ref_count;
@@ -1099,6 +1242,27 @@ static bool match_notify_sender(const void *a, const void *b)
return strcmp(client->owner, sender) == 0;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void gatt_characteristic_value_changed(struct notify_client *client, const uint8_t *data, uint16_t data_len, void *user_data)
+{
+ struct characteristic *chrc = user_data;
+ char *chrc_path = strdup(chrc->path);
+ dbus_int32_t result = 0;
+
+ g_dbus_emit_signal_to_dest(btd_get_dbus_connection(),
+ client->owner, 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);
+
+ 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)
{
@@ -1112,8 +1276,15 @@ static void notify_cb(uint16_t value_handle, const uint8_t *value,
* applications.
*/
gatt_db_attribute_reset(chrc->attr);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ gatt_db_attribute_write(chrc->attr, 0, value, length, 0, NULL,
+ notify_characteristic_cb, chrc);
+
+ gatt_characteristic_value_changed(client, value, length, chrc);
+#else
gatt_db_attribute_write(chrc->attr, 0, value, length, 0, NULL,
write_characteristic_cb, chrc);
+#endif
}
static void create_notify_reply(struct async_dbus_op *op, bool success,
@@ -1245,14 +1416,48 @@ static DBusMessage *characteristic_stop_notify(DBusConnection *conn,
return dbus_message_new_method_return(msg);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void append_desc_path(void *data, void *user_data)
+{
+ struct descriptor *desc = data;
+ DBusMessageIter *array = user_data;
+
+ dbus_message_iter_append_basic(array, DBUS_TYPE_OBJECT_PATH,
+ &desc->path);
+}
+
+static gboolean characteristic_get_descriptors(
+ const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct characteristic *chrc = data;
+ DBusMessageIter array;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "o", &array);
+
+ queue_foreach(chrc->descs, append_desc_path, &array);
+
+ dbus_message_iter_close_container(iter, &array);
+
+ return TRUE;
+}
+#endif
+
static const GDBusPropertyTable characteristic_properties[] = {
{ "UUID", "s", characteristic_get_uuid, NULL, NULL },
{ "Service", "o", characteristic_get_service, NULL, NULL },
{ "Value", "ay", characteristic_get_value, NULL,
characteristic_value_exists },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ { "ChangedValue", "ay", characteristic_get_value, NULL,
+ characteristic_value_exists },
+#endif
{ "Notifying", "b", characteristic_get_notifying, NULL,
characteristic_notifying_exists },
{ "Flags", "as", characteristic_get_flags, NULL, NULL },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ { "Descriptors", "ao", characteristic_get_descriptors },
+#endif
{ }
};
@@ -1264,6 +1469,11 @@ static const GDBusMethodTable characteristic_methods[] = {
{ "options", "a{sv}" }),
NULL,
characteristic_write_value) },
+ { GDBUS_ASYNC_METHOD("WriteValuebyType",
+ GDBUS_ARGS({ "type", "y" }, { "value", "ay" },
+ { "options", "a{sv}" }),
+ GDBUS_ARGS({ "result", "y" }),
+ characteristic_write_value_by_type) },
{ GDBUS_ASYNC_METHOD("StartNotify", NULL, NULL,
characteristic_start_notify) },
{ GDBUS_METHOD("StopNotify", NULL, NULL,
@@ -1271,6 +1481,19 @@ static const GDBusMethodTable characteristic_methods[] = {
{ }
};
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static const GDBusSignalTable service_signals[] = {
+ { GDBUS_SIGNAL("GattServiceAdded",
+ GDBUS_ARGS({ "Service Path","s"})) },
+};
+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;
@@ -1295,11 +1518,24 @@ static struct characteristic *characteristic_create(
chrc->notify_clients = queue_new();
chrc->service = service;
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
gatt_db_attribute_get_char_data(attr, &chrc->handle,
&chrc->value_handle,
&chrc->props,
&chrc->ext_props,
&uuid);
+#else
+ if (!gatt_db_attribute_get_char_data(attr, &chrc->handle,
+ &chrc->value_handle,
+ &chrc->props,
+ &chrc->ext_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);
@@ -1316,7 +1552,11 @@ static struct characteristic *characteristic_create(
if (!g_dbus_register_interface(btd_get_dbus_connection(), chrc->path,
GATT_CHARACTERISTIC_IFACE,
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ characteristic_methods, characteristic_signals,
+#else
characteristic_methods, NULL,
+#endif
characteristic_properties,
chrc, characteristic_free)) {
error("Unable to register GATT characteristic with handle "
@@ -1398,10 +1638,40 @@ static gboolean service_get_primary(const GDBusPropertyTable *property,
return TRUE;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void append_chrc_path(void *data, void *user_data)
+{
+ struct characteristic *chrc = data;
+ DBusMessageIter *array = user_data;
+
+ dbus_message_iter_append_basic(array, DBUS_TYPE_OBJECT_PATH,
+ &chrc->path);
+}
+
+static gboolean service_get_characteristics(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct service *service = data;
+ DBusMessageIter array;
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "o", &array);
+
+ if (service->chrcs_ready)
+ queue_foreach(service->chrcs, append_chrc_path, &array);
+
+ dbus_message_iter_close_container(iter, &array);
+
+ return TRUE;
+}
+#endif
+
static const GDBusPropertyTable service_properties[] = {
{ "UUID", "s", service_get_uuid },
{ "Device", "o", service_get_device },
{ "Primary", "b", service_get_primary },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ { "Characteristics", "ao", service_get_characteristics },
+#endif
{ }
};
@@ -1436,7 +1706,11 @@ static struct service *service_create(struct gatt_db_attribute *attr,
if (!g_dbus_register_interface(btd_get_dbus_connection(), service->path,
GATT_SERVICE_IFACE,
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ NULL, service_signals,
+#else
NULL, NULL,
+#endif
service_properties,
service, service_free)) {
error("Unable to register GATT service with handle 0x%04x for "
@@ -1465,12 +1739,36 @@ static void unregister_service(void *data)
DBG("Removing GATT service: %s", service->path);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (service->idle_id)
+ g_source_remove(service->idle_id);
+
+ if (service->idle_id2)
+ g_source_remove(service->idle_id2);
+#endif
+
queue_remove_all(service->chrcs, NULL, NULL, unregister_characteristic);
g_dbus_unregister_interface(btd_get_dbus_connection(), service->path,
GATT_SERVICE_IFACE);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void notify_chrcs(struct service *service)
+{
+
+ if (service->chrcs_ready ||
+ !queue_isempty(service->pending_ext_props))
+ return;
+
+ service->chrcs_ready = true;
+
+ g_dbus_emit_property_changed(btd_get_dbus_connection(), service->path,
+ GATT_SERVICE_IFACE,
+ "Characteristics");
+}
+#endif
+
struct export_data {
void *root;
bool failed;
@@ -1527,6 +1825,11 @@ static void export_char(struct gatt_db_attribute *attr, void *user_data)
queue_push_tail(service->chrcs, charac);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (charac->ext_props_handle)
+ queue_push_tail(service->pending_ext_props, charac);
+#endif
+
return;
fail:
@@ -1546,6 +1849,38 @@ static bool create_characteristics(struct gatt_db_attribute *attr,
return !data.failed;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static gboolean set_chrcs_ready(gpointer user_data)
+{
+ struct service *service = user_data;
+
+ service->idle_id = 0;
+ notify_chrcs(service);
+
+ return FALSE;
+}
+
+static gboolean notify_service_added_complete(gpointer user_data)
+{
+ struct service *service = user_data;
+ char *svc_path;
+
+ if (service == NULL || service->path == NULL)
+ return FALSE;
+
+ svc_path = g_strdup(service->path);
+
+ g_dbus_emit_signal(btd_get_dbus_connection(), service->path,
+ GATT_SERVICE_IFACE, "GattServiceAdded",
+ DBUS_TYPE_STRING, &svc_path,
+ DBUS_TYPE_INVALID);
+
+ g_free(svc_path);
+
+ return FALSE;
+}
+#endif
+
static void export_service(struct gatt_db_attribute *attr, void *user_data)
{
struct btd_gatt_client *client = user_data;
@@ -1565,6 +1900,19 @@ static void export_service(struct gatt_db_attribute *attr, void *user_data)
}
queue_push_tail(client->services, service);
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ /*
+ * Asynchronously update the "Characteristics" property of the service.
+ * If there are any pending reads to obtain the value of the "Extended
+ * Properties" descriptor then wait until they are complete.
+ */
+ if (!service->chrcs_ready && queue_isempty(service->pending_ext_props))
+ service->idle_id = g_idle_add(set_chrcs_ready, service);
+
+ if (service->primary == true)
+ service->idle_id2 = g_idle_add(notify_service_added_complete, service);
+#endif
}
static void create_services(struct btd_gatt_client *client)
@@ -1602,6 +1950,10 @@ void btd_gatt_client_destroy(struct btd_gatt_client *client)
if (!client)
return;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ 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);
@@ -1637,6 +1989,58 @@ static void register_notify(void *data, void *user_data)
notify_client_free(notify_client);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+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 50 times (Assuming more
+ * no of services). Emit signal only when all characteristics are ready.
+ */
+ if (all_chrcs_ready == FALSE && count < 50) {
+ count++;
+ return TRUE;
+ }
+
+ /*
+ * There are chances when all of the primary services are not found,
+ * instead service changed is received while reading REQs in progress,
+ * so emit signal after service changed operation is completed (if any).
+ */
+ if (bt_gatt_client_svc_changed_received(client->gatt))
+ 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)
{
if (!client)
@@ -1658,6 +2062,20 @@ void btd_gatt_client_ready(struct btd_gatt_client *client)
DBG("GATT client ready");
create_services(client);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ /*
+ * 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
}
void btd_gatt_client_connected(struct btd_gatt_client *client)
@@ -1731,6 +2149,13 @@ void btd_gatt_client_disconnected(struct btd_gatt_client *client)
DBG("Device disconnected. Cleaning up.");
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (client->wait_charcs_id) {
+ g_source_remove(client->wait_charcs_id);
+ client->wait_charcs_id = 0;
+ }
+#endif
+
/*
* TODO: Once GATT over BR/EDR is properly supported, we should pass the
* correct bdaddr_type based on the transport over which GATT is being
diff --git a/src/gatt-database.c b/src/gatt-database.c
index 0877b25c..9fa059b1 100755
--- a/src/gatt-database.c
+++ b/src/gatt-database.c
@@ -170,6 +170,10 @@ struct device_info {
uint8_t bdaddr_type;
};
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void conf_cb(void *user_data);
+#endif
+
static void ccc_cb_free(void *data)
{
struct ccc_cb_data *ccc_cb = data;
@@ -223,6 +227,56 @@ find_device_state(struct btd_gatt_database *database, bdaddr_t *bdaddr,
return queue_find(database->device_states, dev_state_match, &dev_info);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void send_service_changed_indication_on_reconnect(
+ struct btd_device *device,
+ struct bt_gatt_server *server)
+{
+ struct btd_gatt_database *database;
+ uint16_t start_handle = 0X0001, end_handle = 0xFFFF;
+ uint8_t value[4];
+ uint16_t svc_chngd_handle;
+ DBG("");
+
+ database = btd_adapter_get_database(device_get_adapter(device));
+
+ if (!database)
+ return;
+
+ svc_chngd_handle = gatt_db_attribute_get_handle(database->svc_chngd_ccc);
+
+ put_le16(start_handle, value);
+ put_le16(end_handle, value + 2);
+
+ bt_gatt_server_send_indication(server, svc_chngd_handle,
+ value, sizeof(value), conf_cb,
+ NULL, NULL);
+}
+
+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;
@@ -242,6 +296,12 @@ static struct device_state *device_state_create(bdaddr_t *bdaddr,
uint8_t bdaddr_type)
{
struct device_state *dev_state;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ char addr[18] = { 0 };
+
+ ba2str(bdaddr, addr);
+ DBG("create device_state for %s [%d]", addr, bdaddr_type);
+#endif
dev_state = new0(struct device_state, 1);
dev_state->ccc_states = queue_new();
@@ -279,6 +339,9 @@ static struct ccc_state *get_ccc_state(struct btd_gatt_database *database,
{
struct device_state *dev_state;
struct ccc_state *ccc;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ char addr[18] = { 0 };
+#endif
dev_state = get_device_state(database, bdaddr, bdaddr_type);
@@ -286,6 +349,12 @@ static struct ccc_state *get_ccc_state(struct btd_gatt_database *database,
if (ccc)
return ccc;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ ba2str(bdaddr, addr);
+ DBG("create ccc_state of handle: 0x%04x for %s [%d]",
+ handle, addr, bdaddr_type);
+#endif
+
ccc = new0(struct ccc_state, 1);
ccc->handle = handle;
queue_push_tail(dev_state->ccc_states, ccc);
@@ -499,8 +568,22 @@ static void connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
return;
device_attach_att(device, io);
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (btd_device_get_svc_changed_indication(device)) {
+ send_service_changed_indication_on_reconnect(device,
+ btd_device_get_gatt_server(device));
+ }
+#endif
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define LE_BEARER_POSTFIX " LE"
+#define LE_BEARER_POSTFIX_LEN 3
+
+static bool get_dst_info(struct bt_att *att, bdaddr_t *dst, uint8_t *dst_type);
+#endif
+
static void gap_device_name_read_cb(struct gatt_db_attribute *attrib,
unsigned int id, uint16_t offset,
uint8_t opcode, struct bt_att *att,
@@ -511,10 +594,32 @@ static void gap_device_name_read_cb(struct gatt_db_attribute *attrib,
size_t len = 0;
const uint8_t *value = NULL;
const char *device_name;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ bdaddr_t dst = { { 0 } };
+ uint8_t dst_type = 0;
+ char le_name[MAX_NAME_LENGTH + 1];
+#endif
DBG("GAP Device Name read request\n");
device_name = btd_adapter_get_name(database->adapter);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (get_dst_info(att, &dst, &dst_type) && dst_type != BDADDR_BREDR &&
+ bacmp(btd_adapter_get_address(database->adapter),
+ btd_adapter_get_le_address(database->adapter))) {
+ char *ptr = NULL;
+
+ g_strlcpy(le_name, device_name,
+ sizeof(le_name) - LE_BEARER_POSTFIX_LEN);
+ if (!g_utf8_validate(le_name, -1, (const char **)&ptr))
+ *ptr = '\0';
+
+ g_strlcat(le_name, LE_BEARER_POSTFIX, sizeof(le_name));
+ DBG("Append LE bearer postfix : %s", le_name);
+
+ device_name = le_name;
+ }
+#endif
len = strlen(device_name);
if (offset > len) {
@@ -560,6 +665,33 @@ done:
gatt_db_attribute_read_result(attrib, id, error, value, len);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+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;
@@ -632,14 +764,20 @@ static uint32_t database_add_record(struct btd_gatt_database *database,
return 0;
if (name != NULL)
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ sdp_set_info_attr(record, name, "Samsung", NULL);
+#else
sdp_set_info_attr(record, name, "BlueZ", NULL);
+#endif
sdp_uuid16_create(&gap_uuid, UUID_GAP);
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
if (sdp_uuid_cmp(&svc, &gap_uuid) == 0) {
sdp_set_url_attr(record, "http://www.bluez.org/",
"http://www.bluez.org/",
"http://www.bluez.org/");
}
+#endif
if (adapter_service_add(database->adapter, record) == 0)
return record->handle;
@@ -677,11 +815,23 @@ static void populate_gap_service(struct btd_gatt_database *database)
gap_appearance_read_cb,
NULL, database);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ /* 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);
}
static bool get_dst_info(struct bt_att *att, bdaddr_t *dst, uint8_t *dst_type)
{
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ return bt_att_get_remote_addr(att, dst, dst_type);
+#else
GIOChannel *io = NULL;
GError *gerr = NULL;
@@ -700,7 +850,9 @@ static bool get_dst_info(struct bt_att *att, bdaddr_t *dst, uint8_t *dst_type)
}
g_io_channel_unref(io);
+
return true;
+#endif
}
static void gatt_ccc_read_cb(struct gatt_db_attribute *attrib,
@@ -796,6 +948,23 @@ static void gatt_ccc_write_cb(struct gatt_db_attribute *attrib,
done:
gatt_db_attribute_write_result(attrib, id, ecode);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (!ecode) {
+ uint16_t svc_chngd_handle = gatt_db_attribute_get_handle(database->svc_chngd_ccc);
+ if (handle == svc_chngd_handle) {
+ if (ccc->value[0] != 0) {
+ struct btd_device *device = NULL;
+ device = btd_adapter_get_device(
+ database->adapter,
+ &bdaddr, bdaddr_type);
+
+ if (!device)
+ return;
+ btd_device_set_svc_changed_indication(device, true);
+ }
+ }
+ }
+#endif
}
static struct gatt_db_attribute *
@@ -868,10 +1037,182 @@ struct notify {
bool indicate;
};
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+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 dst_addr[18] = { 0 };
+ char *addr_value = dst_addr;
+ gboolean complete = FALSE;
+
+ if (device_get_rpa_exist(device) == true) {
+ ba2str(device_get_rpa(device), dst_addr);
+ } else {
+ ba2str(device_get_address(device), dst_addr);
+ }
+
+ 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_FEATURE_BLUEZ_MODIFY
+ 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_FEATURE_BLUEZ_MODIFY
+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);
+ indicate_confirm_free((void *)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("notify for handle: 0x%04x", handle);
+
+ 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("notify for handle: 0x%04x", handle);
+
+ 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)
{
@@ -1620,6 +1961,7 @@ static struct pending_op *pending_read_new(struct btd_device *device,
return op;
}
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
static void append_options(DBusMessageIter *iter, void *user_data)
{
struct pending_op *op = user_data;
@@ -1627,10 +1969,22 @@ static void append_options(DBusMessageIter *iter, void *user_data)
dict_append_entry(iter, "device", DBUS_TYPE_OBJECT_PATH, &path);
}
+#endif
static void read_setup_cb(DBusMessageIter *iter, void *user_data)
{
struct pending_op *op = user_data;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ char dst_addr[18] = { 0 };
+ char *addr_value = dst_addr;
+ uint16_t offset = 0;
+ const bdaddr_t *bdaddr = device_get_address(op->device);
+
+ ba2str(bdaddr, dst_addr);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &addr_value);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &op->id);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &offset);
+#else
DBusMessageIter dict;
dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
@@ -1643,6 +1997,7 @@ static void read_setup_cb(DBusMessageIter *iter, void *user_data)
append_options(&dict, op);
dbus_message_iter_close_container(iter, &dict);
+#endif
}
static struct pending_op *send_read(struct btd_device *device,
@@ -1668,12 +2023,25 @@ static void write_setup_cb(DBusMessageIter *iter, void *user_data)
{
struct pending_op *op = user_data;
DBusMessageIter array, dict;
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ char dst_addr[18] = { 0 };
+ char *addr_value = dst_addr;
+ uint16_t offset = 0;
+ gboolean response_needed = TRUE;
+ const bdaddr_t *bdaddr = device_get_address(op->device);
+
+ ba2str(bdaddr, dst_addr);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &addr_value);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &op->id);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &offset);
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &response_needed);
+#endif
dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array);
dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE,
&op->data.iov_base, op->data.iov_len);
dbus_message_iter_close_container(iter, &array);
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
DBUS_TYPE_STRING_AS_STRING
@@ -1684,6 +2052,7 @@ static void write_setup_cb(DBusMessageIter *iter, void *user_data)
append_options(&dict, op);
dbus_message_iter_close_container(iter, &dict);
+#endif
if (!op->owner_queue) {
gatt_db_attribute_write_result(op->attrib, op->id, 0);
@@ -1826,7 +2195,87 @@ static void property_changed_cb(GDBusProxy *proxy, const char *name,
DBusMessageIter array;
uint8_t *value = NULL;
int len = 0;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ bool enable = FALSE;
+ const bdaddr_t *unicast_addr = NULL;
+#endif
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ 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->app->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->app->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;
@@ -1852,6 +2301,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,
@@ -2096,6 +2546,24 @@ fail:
NULL, 0);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static bool database_check_ccc_desc(struct external_desc *desc)
+{
+ bt_uuid_t uuid, uuid_ccc;
+
+ 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)
{
@@ -2121,8 +2589,13 @@ static bool database_add_chrc(struct external_service *service,
return false;
}
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
+ /* 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;
@@ -2135,11 +2608,28 @@ static bool database_add_chrc(struct external_service *service,
if (desc->handled || g_strcmp0(desc->chrc_path, chrc->path))
continue;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ /* 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
}
return true;
diff --git a/src/hcid.h b/src/hcid.h
index 0b785ee9..6449879a 100755
--- a/src/hcid.h
+++ b/src/hcid.h
@@ -41,6 +41,9 @@ struct main_opts {
gboolean name_resolv;
gboolean debug_keys;
gboolean fast_conn;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ gboolean le_privacy;
+#endif
uint16_t did_source;
uint16_t did_vendor;
diff --git a/src/log.c b/src/log.c
index d2a20de7..8a5057e4 100755
--- a/src/log.c
+++ b/src/log.c
@@ -302,6 +302,22 @@ void __btd_toggle_debug(void)
desc->flags |= BTD_DEBUG_FLAG_PRINT;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void __hci_attach_log_init(void)
+{
+ int option = LOG_NDELAY | LOG_PID;
+
+ enabled = g_strsplit_set(g_strdup("*"), ":, ", 0);
+
+ __btd_enable_debug(__start___debug, __stop___debug);
+
+ openlog("hciattach", option, LOG_DAEMON);
+
+ syslog(LOG_INFO, "hciattach daemon for debugging");
+}
+#endif
+
+
void __btd_log_init(const char *debug, int detach)
{
int option = LOG_NDELAY | LOG_PID;
diff --git a/src/log.h b/src/log.h
index 0d243cec..1299cddd 100755
--- a/src/log.h
+++ b/src/log.h
@@ -39,6 +39,9 @@ void btd_info(uint16_t index, const char *format, ...)
void btd_debug(uint16_t index, const char *format, ...)
__attribute__((format(printf, 2, 3)));
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void __hci_attach_log_init(void );
+#endif
void __btd_log_init(const char *debug, int detach);
void __btd_log_cleanup(void);
void __btd_toggle_debug(void);
diff --git a/src/main.c b/src/main.c
index bcc1e6fa..44110c5d 100755
--- a/src/main.c
+++ b/src/main.c
@@ -90,6 +90,9 @@ static const char * const supported_options[] = {
"ControllerMode",
"MultiProfile",
"Privacy",
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ "EnableLEPrivacy",
+#endif
};
GKeyFile *btd_get_main_conf(void)
@@ -357,6 +360,14 @@ static void parse_config(GKeyFile *config)
g_clear_error(&err);
else
main_opts.fast_conn = boolean;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ boolean = g_key_file_get_boolean(config, "General",
+ "EnableLEPrivacy", &err);
+ if (err)
+ g_clear_error(&err);
+ else
+ main_opts.le_privacy = boolean;
+#endif
}
static void init_defaults(void)
@@ -372,14 +383,18 @@ static void init_defaults(void)
main_opts.reverse_sdp = TRUE;
main_opts.name_resolv = TRUE;
main_opts.debug_keys = FALSE;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ main_opts.le_privacy = FALSE;
+#endif
if (sscanf(VERSION, "%hhu.%hhu", &major, &minor) != 2)
return;
-
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
main_opts.did_source = 0x0002; /* USB */
main_opts.did_vendor = 0x1d6b; /* Linux Foundation */
main_opts.did_product = 0x0246; /* BlueZ */
main_opts.did_version = (major << 8 | minor);
+#endif
}
static void log_handler(const gchar *log_domain, GLogLevelFlags log_level,
diff --git a/src/main_hive.conf b/src/main_hive.conf
new file mode 100755
index 00000000..f5210925
--- /dev/null
+++ b/src/main_hive.conf
@@ -0,0 +1,87 @@
+[General]
+
+# Default adaper name
+# %h - substituted for hostname
+# %d - substituted for adapter id
+# Defaults to 'BlueZ'
+#Name = %h-%d
+
+# Default device class. Only the major and minor device class bits are
+# considered. Defaults to '0x000000'.
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+Class = 0x40414 # HIVE
+#else
+#Class = 0x000100
+#endif
+
+# How long to stay in discoverable mode before going back to non-discoverable
+# The value is in seconds. Default is 180, i.e. 3 minutes.
+# 0 = disable timer, i.e. stay discoverable forever
+#DiscoverableTimeout = 0
+
+# How long to stay in pairable mode before going back to non-discoverable
+# The value is in seconds. Default is 0.
+# 0 = disable timer, i.e. stay pairable forever
+#PairableTimeout = 0
+
+# Automatic connection for bonded devices driven by platform/user events.
+# If a platform plugin uses this mechanism, automatic connections will be
+# enabled during the interval defined below. Initially, this feature
+# intends to be used to establish connections to ATT channels. Default is 60.
+#AutoConnectTimeout = 60
+
+# Use vendor id source (assigner), vendor, product and version information for
+# DID profile support. The values are separated by ":" and assigner, VID, PID
+# and version.
+# Possible vendor id source values: bluetooth, usb (defaults to usb)
+#DeviceID = bluetooth:1234:5678:abcd
+
+# Do reverse service discovery for previously unknown devices that connect to
+# us. This option is really only needed for qualification since the BITE tester
+# doesn't like us doing reverse SDP for some test cases (though there could in
+# theory be other useful purposes for this too). Defaults to 'true'.
+#ReverseServiceDiscovery = true
+
+# Enable name resolving after inquiry. Set it to 'false' if you don't need
+# remote devices name and want shorter discovery cycle. Defaults to 'true'.
+#NameResolving = true
+
+# Enable runtime persistency of debug link keys. Default is false which
+# makes debug link keys valid only for the duration of the connection
+# that they were created for.
+#DebugKeys = false
+
+# Restricts all controllers to the specified transport. Default value
+# is "dual", i.e. both BR/EDR and LE enabled (when supported by the HW).
+# Possible values: "dual", "bredr", "le"
+#ControllerMode = dual
+
+# Enables Multi Profile Specification support. This allows to specify if
+# system supports only Multiple Profiles Single Device (MPSD) configuration
+# or both Multiple Profiles Single Device (MPSD) and Multiple Profiles Multiple
+# Devices (MPMD) configurations.
+# Possible values: "off", "single", "multiple"
+#MultiProfile = off
+
+# 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_FEATURE_BLUEZ_MODIFY
+# Enable the LE Privacy feature. If value is true, i.e. LE Privacy is enabled
+# otherwise the feature is disabled by default for the local device.
+EnableLEPrivacy = true
+#endif
+
+#[Policy]
+#
+# The ReconnectUUIDs defines the set of remote services that should try
+# to be reconnected to in case of a link loss (link supervision
+# timeout). The policy plugin should contain a sane set of values by
+# default, but this list can be overridden here. By setting the list to
+# empty the reconnection feature gets disabled.
+#ReconnectUUIDs=
+
diff --git a/src/main_ivi.conf b/src/main_ivi.conf
new file mode 100755
index 00000000..4e7b1f29
--- /dev/null
+++ b/src/main_ivi.conf
@@ -0,0 +1,87 @@
+[General]
+
+# Default adaper name
+# %h - substituted for hostname
+# %d - substituted for adapter id
+# Defaults to 'BlueZ'
+#Name = %h-%d
+
+# Default device class. Only the major and minor device class bits are
+# considered. Defaults to '0x000000'.
+#ifdef __TIZEN_PATCH__
+Class = 0x200428 # IVI device
+#else
+#Class = 0x000100
+#endif
+
+# How long to stay in discoverable mode before going back to non-discoverable
+# The value is in seconds. Default is 180, i.e. 3 minutes.
+# 0 = disable timer, i.e. stay discoverable forever
+#DiscoverableTimeout = 0
+
+# How long to stay in pairable mode before going back to non-discoverable
+# The value is in seconds. Default is 0.
+# 0 = disable timer, i.e. stay pairable forever
+#PairableTimeout = 0
+
+# Automatic connection for bonded devices driven by platform/user events.
+# If a platform plugin uses this mechanism, automatic connections will be
+# enabled during the interval defined below. Initially, this feature
+# intends to be used to establish connections to ATT channels. Default is 60.
+#AutoConnectTimeout = 60
+
+# Use vendor id source (assigner), vendor, product and version information for
+# DID profile support. The values are separated by ":" and assigner, VID, PID
+# and version.
+# Possible vendor id source values: bluetooth, usb (defaults to usb)
+#DeviceID = bluetooth:1234:5678:abcd
+
+# Do reverse service discovery for previously unknown devices that connect to
+# us. This option is really only needed for qualification since the BITE tester
+# doesn't like us doing reverse SDP for some test cases (though there could in
+# theory be other useful purposes for this too). Defaults to 'true'.
+#ReverseServiceDiscovery = true
+
+# Enable name resolving after inquiry. Set it to 'false' if you don't need
+# remote devices name and want shorter discovery cycle. Defaults to 'true'.
+#NameResolving = true
+
+# Enable runtime persistency of debug link keys. Default is false which
+# makes debug link keys valid only for the duration of the connection
+# that they were created for.
+#DebugKeys = false
+
+# Restricts all controllers to the specified transport. Default value
+# is "dual", i.e. both BR/EDR and LE enabled (when supported by the HW).
+# Possible values: "dual", "bredr", "le"
+#ControllerMode = dual
+
+# Enables Multi Profile Specification support. This allows to specify if
+# system supports only Multiple Profiles Single Device (MPSD) configuration
+# or both Multiple Profiles Single Device (MPSD) and Multiple Profiles Multiple
+# Devices (MPMD) configurations.
+# Possible values: "off", "single", "multiple"
+#MultiProfile = off
+
+# 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.
+EnableLEPrivacy = true
+#endif
+
+#[Policy]
+#
+# The ReconnectUUIDs defines the set of remote services that should try
+# to be reconnected to in case of a link loss (link supervision
+# timeout). The policy plugin should contain a sane set of values by
+# default, but this list can be overridden here. By setting the list to
+# empty the reconnection feature gets disabled.
+#ReconnectUUIDs=
+
diff --git a/src/main_m.conf b/src/main_m.conf
new file mode 100755
index 00000000..bb431e47
--- /dev/null
+++ b/src/main_m.conf
@@ -0,0 +1,87 @@
+[General]
+
+# Default adaper name
+# %h - substituted for hostname
+# %d - substituted for adapter id
+# Defaults to 'BlueZ'
+#Name = %h-%d
+
+# Default device class. Only the major and minor device class bits are
+# considered. Defaults to '0x000000'.
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+Class = 0x00020C # Smart phone
+#else
+#Class = 0x000100
+#endif
+
+# How long to stay in discoverable mode before going back to non-discoverable
+# The value is in seconds. Default is 180, i.e. 3 minutes.
+# 0 = disable timer, i.e. stay discoverable forever
+#DiscoverableTimeout = 0
+
+# How long to stay in pairable mode before going back to non-discoverable
+# The value is in seconds. Default is 0.
+# 0 = disable timer, i.e. stay pairable forever
+#PairableTimeout = 0
+
+# Automatic connection for bonded devices driven by platform/user events.
+# If a platform plugin uses this mechanism, automatic connections will be
+# enabled during the interval defined below. Initially, this feature
+# intends to be used to establish connections to ATT channels. Default is 60.
+#AutoConnectTimeout = 60
+
+# Use vendor id source (assigner), vendor, product and version information for
+# DID profile support. The values are separated by ":" and assigner, VID, PID
+# and version.
+# Possible vendor id source values: bluetooth, usb (defaults to usb)
+#DeviceID = bluetooth:1234:5678:abcd
+
+# Do reverse service discovery for previously unknown devices that connect to
+# us. This option is really only needed for qualification since the BITE tester
+# doesn't like us doing reverse SDP for some test cases (though there could in
+# theory be other useful purposes for this too). Defaults to 'true'.
+#ReverseServiceDiscovery = true
+
+# Enable name resolving after inquiry. Set it to 'false' if you don't need
+# remote devices name and want shorter discovery cycle. Defaults to 'true'.
+#NameResolving = true
+
+# Enable runtime persistency of debug link keys. Default is false which
+# makes debug link keys valid only for the duration of the connection
+# that they were created for.
+#DebugKeys = false
+
+# Restricts all controllers to the specified transport. Default value
+# is "dual", i.e. both BR/EDR and LE enabled (when supported by the HW).
+# Possible values: "dual", "bredr", "le"
+#ControllerMode = dual
+
+# Enables Multi Profile Specification support. This allows to specify if
+# system supports only Multiple Profiles Single Device (MPSD) configuration
+# or both Multiple Profiles Single Device (MPSD) and Multiple Profiles Multiple
+# Devices (MPMD) configurations.
+# Possible values: "off", "single", "multiple"
+#MultiProfile = off
+
+# 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_FEATURE_BLUEZ_MODIFY
+# Enable the LE Privacy feature. If value is true, i.e. LE Privacy is enabled
+# otherwise the feature is disabled by default for the local device.
+EnableLEPrivacy = false
+#endif
+
+#[Policy]
+#
+# The ReconnectUUIDs defines the set of remote services that should try
+# to be reconnected to in case of a link loss (link supervision
+# timeout). The policy plugin should contain a sane set of values by
+# default, but this list can be overridden here. By setting the list to
+# empty the reconnection feature gets disabled.
+#ReconnectUUIDs=
+
diff --git a/src/main_w.conf b/src/main_w.conf
new file mode 100755
index 00000000..822be5e7
--- /dev/null
+++ b/src/main_w.conf
@@ -0,0 +1,84 @@
+[General]
+
+# Default adaper name
+# %h - substituted for hostname
+# %d - substituted for adapter id
+# Defaults to 'BlueZ'
+#Name = %h-%d
+
+# Default device class. Only the major and minor device class bits are
+# considered. Defaults to '0x000000'.
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+Class = 0x000704 # Wearable, Wrist Watch
+#else
+#Class = 0x000100
+#endif
+
+# How long to stay in discoverable mode before going back to non-discoverable
+# The value is in seconds. Default is 180, i.e. 3 minutes.
+# 0 = disable timer, i.e. stay discoverable forever
+#DiscoverableTimeout = 0
+
+# How long to stay in pairable mode before going back to non-discoverable
+# The value is in seconds. Default is 0.
+# 0 = disable timer, i.e. stay pairable forever
+#PairableTimeout = 0
+
+# Automatic connection for bonded devices driven by platform/user events.
+# If a platform plugin uses this mechanism, automatic connections will be
+# enabled during the interval defined below. Initially, this feature
+# intends to be used to establish connections to ATT channels. Default is 60.
+#AutoConnectTimeout = 60
+
+# Use vendor id source (assigner), vendor, product and version information for
+# DID profile support. The values are separated by ":" and assigner, VID, PID
+# and version.
+# Possible vendor id source values: bluetooth, usb (defaults to usb)
+#DeviceID = bluetooth:1234:5678:abcd
+
+# Do reverse service discovery for previously unknown devices that connect to
+# us. This option is really only needed for qualification since the BITE tester
+# doesn't like us doing reverse SDP for some test cases (though there could in
+# theory be other useful purposes for this too). Defaults to 'true'.
+#ReverseServiceDiscovery = true
+
+# Enable name resolving after inquiry. Set it to 'false' if you don't need
+# remote devices name and want shorter discovery cycle. Defaults to 'true'.
+#NameResolving = true
+
+# Enable runtime persistency of debug link keys. Default is false which
+# makes debug link keys valid only for the duration of the connection
+# that they were created for.
+#DebugKeys = false
+
+# Restricts all controllers to the specified transport. Default value
+# is "dual", i.e. both BR/EDR and LE enabled (when supported by the HW).
+# 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_FEATURE_BLUEZ_MODIFY
+# Enable the LE Privacy feature. If value is true, i.e. LE Privacy is enabled
+# otherwise the feature is disabled by default for the local device.
+EnableLEPrivacy = false
+#endif
+
+[Policy]
+#
+# The ReconnectUUIDs defines the set of remote services that should try
+# to be reconnected to in case of a link loss (link supervision
+# timeout). The policy plugin should contain a sane set of values by
+# default, but this list can be overridden here. By setting the list to
+# empty the reconnection feature gets disabled.
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ReconnectUUIDs=
+#else
+#ReconnectUUIDs=
+#endif
+
diff --git a/src/oob.c b/src/oob.c
new file mode 100755
index 00000000..708467b8
--- /dev/null
+++ b/src/oob.c
@@ -0,0 +1,46 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 ST-Ericsson SA
+ *
+ * Author: Szymon Janc <szymon.janc@tieto.com> for ST-Ericsson
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include "adapter.h"
+#include "oob.h"
+
+static oob_read_cb_t local_oob_read_cb = NULL;
+
+void oob_register_cb(oob_read_cb_t cb)
+{
+ local_oob_read_cb = cb;
+}
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void oob_read_local_data_complete(struct btd_adapter *adapter, uint8_t *hash,
+ uint8_t *randomizer, void *user_data)
+#else
+void oob_read_local_data_complete(struct btd_adapter *adapter, uint8_t *hash,
+ uint8_t *randomizer)
+#endif
+{
+ if (local_oob_read_cb)
+ local_oob_read_cb(adapter, hash, randomizer);
+}
diff --git a/src/oob.h b/src/oob.h
new file mode 100755
index 00000000..d7203156
--- /dev/null
+++ b/src/oob.h
@@ -0,0 +1,36 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 ST-Ericsson SA
+ *
+ * Author: Szymon Janc <szymon.janc@tieto.com> for ST-Ericsson
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+typedef void (*oob_read_cb_t) (struct btd_adapter *adapter, uint8_t *hash,
+ uint8_t *randomizer);
+
+void oob_register_cb(oob_read_cb_t cb);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+void oob_read_local_data_complete(struct btd_adapter *adapter, uint8_t *hash,
+ uint8_t *randomizer, void *user_data);
+#else
+void oob_read_local_data_complete(struct btd_adapter *adapter, uint8_t *hash,
+ uint8_t *randomizer);
+#endif
diff --git a/src/profile.c b/src/profile.c
index 7c5318ca..bb833bd8 100755
--- a/src/profile.c
+++ b/src/profile.c
@@ -69,6 +69,11 @@
#define BTD_PROFILE_PSM_AUTO -1
#define BTD_PROFILE_CHAN_AUTO -1
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+#define HID_DEVICE_INTR_PSM 17
+#define HID_DEVICE_CTRL_PSM 19
+#endif
+
#define HFP_HF_RECORD \
"<?xml version=\"1.0\" encoding=\"UTF-8\" ?> \
<record> \
@@ -192,6 +197,54 @@
</attribute> \
</record>"
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define SPP_RECORD \
+ "<?xml version=\"1.0\" encoding=\"UTF-8\" ?> \
+ <record> \
+ <attribute id=\"0x0001\"> \
+ <sequence> \
+ %s \
+ </sequence> \
+ </attribute> \
+ <attribute id=\"0x0004\"> \
+ <sequence> \
+ <sequence> \
+ <uuid value=\"0x0100\" /> \
+ </sequence> \
+ <sequence> \
+ <uuid value=\"0x0003\" /> \
+ <uint8 value=\"0x%02x\" /> \
+ </sequence> \
+ </sequence> \
+ </attribute> \
+ <attribute id=\"0x0005\"> \
+ <sequence> \
+ <uuid value=\"0x1002\" /> \
+ </sequence> \
+ </attribute> \
+ %s \
+ <attribute id=\"0x0009\"> \
+ <sequence> \
+ <sequence> \
+ <uuid value=\"0x1101\" /> \
+ <uint16 value=\"0x%04x\" /> \
+ </sequence> \
+ </sequence> \
+ </attribute> \
+ <attribute id=\"0x0100\"> \
+ <text value=\"%s\" /> \
+ </attribute> \
+ </record>"
+
+#define LANG_SEQ \
+ "<attribute id=\"0x0006\"> \
+ <sequence> \
+ <uint16 value=\"0x%04x\" /> \
+ <uint16 value=\"0x%04x\" /> \
+ <uint16 value=\"0x%04x\" /> \
+ </sequence> \
+ </attribute>"
+#else
#define SPP_RECORD \
"<?xml version=\"1.0\" encoding=\"UTF-8\" ?> \
<record> \
@@ -229,6 +282,7 @@
<text value=\"%s\" /> \
</attribute> \
</record>"
+#endif
#define DUN_RECORD \
"<?xml version=\"1.0\" encoding=\"UTF-8\" ?> \
@@ -268,6 +322,58 @@
</attribute> \
</record>"
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define OPP_RECORD \
+ "<?xml version=\"1.0\" encoding=\"UTF-8\" ?> \
+ <record> \
+ <attribute id=\"0x0001\"> \
+ <sequence> \
+ <uuid value=\"0x1105\" /> \
+ </sequence> \
+ </attribute> \
+ <attribute id=\"0x0004\"> \
+ <sequence> \
+ <sequence> \
+ <uuid value=\"0x0100\" /> \
+ </sequence> \
+ <sequence> \
+ <uuid value=\"0x0003\" /> \
+ <uint8 value=\"0x%02x\" /> \
+ </sequence> \
+ <sequence> \
+ <uuid value=\"0x0008\"/> \
+ </sequence> \
+ </sequence> \
+ </attribute> \
+ <attribute id=\"0x0005\"> \
+ <sequence> \
+ <uuid value=\"0x1002\" /> \
+ </sequence> \
+ </attribute> \
+ <attribute id=\"0x0009\"> \
+ <sequence> \
+ <sequence> \
+ <uuid value=\"0x1105\" /> \
+ <uint16 value=\"0x%04x\" /> \
+ </sequence> \
+ </sequence> \
+ </attribute> \
+ <attribute id=\"0x0303\"> \
+ <sequence> \
+ <uint8 value=\"0x01\"/> \
+ <uint8 value=\"0x02\"/> \
+ <uint8 value=\"0x03\"/> \
+ <uint8 value=\"0x04\"/> \
+ <uint8 value=\"0x05\"/> \
+ <uint8 value=\"0x06\"/> \
+ <uint8 value=\"0xff\"/> \
+ </sequence> \
+ </attribute> \
+ <attribute id=\"0x0100\"> \
+ <text value=\"%s\" /> \
+ </attribute> \
+ </record>"
+#else
#define OPP_RECORD \
"<?xml version=\"1.0\" encoding=\"UTF-8\" ?> \
<record> \
@@ -321,6 +427,7 @@
<text value=\"%s\" /> \
</attribute> \
</record>"
+#endif
#define FTP_RECORD \
"<?xml version=\"1.0\" encoding=\"UTF-8\" ?> \
@@ -391,6 +498,57 @@
</attribute> \
</record>"
+#ifdef TIZEN_FEATURE_BLUEZ_PBAP_SIM
+#define PBAP_ACCESS "0x03" /* Phone and SIM access support*/
+#else
+#define PBAP_ACCESS "0x01" /* Phone access support only*/
+#endif
+
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define PSE_RECORD \
+ "<?xml version=\"1.0\" encoding=\"UTF-8\" ?> \
+ <record> \
+ <attribute id=\"0x0001\"> \
+ <sequence> \
+ <uuid value=\"0x112f\" /> \
+ </sequence> \
+ </attribute> \
+ <attribute id=\"0x0004\"> \
+ <sequence> \
+ <sequence> \
+ <uuid value=\"0x0100\" /> \
+ </sequence> \
+ <sequence> \
+ <uuid value=\"0x0003\" /> \
+ <uint8 value=\"0x%02x\" /> \
+ </sequence> \
+ <sequence> \
+ <uuid value=\"0x0008\"/> \
+ </sequence> \
+ </sequence> \
+ </attribute> \
+ <attribute id=\"0x0005\"> \
+ <sequence> \
+ <uuid value=\"0x1002\" /> \
+ </sequence> \
+ </attribute> \
+ <attribute id=\"0x0009\"> \
+ <sequence> \
+ <sequence> \
+ <uuid value=\"0x1130\" /> \
+ <uint16 value=\"0x%04x\" /> \
+ </sequence> \
+ </sequence> \
+ </attribute> \
+ <attribute id=\"0x0100\"> \
+ <text value=\"%s\" /> \
+ </attribute> \
+ <attribute id=\"0x0314\"> \
+ <uint8 value=\""PBAP_ACCESS"\"/> \
+ </attribute> \
+ </record>"
+#else
#define PSE_RECORD \
"<?xml version=\"1.0\" encoding=\"UTF-8\" ?> \
<record> \
@@ -439,6 +597,13 @@
<uint16 value=\"%u\" name=\"psm\"/> \
</attribute> \
</record>"
+#endif
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define SUPPORTED_MESSAGE_TYPES "0x03" /* EMAIL and SMS_GSM */
+#else
+#define SUPPORTED_MESSAGE_TYPES "0x0F" /* EMAIL, SMS_GSM, SMS_CDMA and MMS */
+#endif
#define MAS_RECORD \
"<?xml version=\"1.0\" encoding=\"UTF-8\" ?> \
@@ -611,6 +776,109 @@
</attribute> \
</record>"
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+#define HID_DEVICE_RECORD \
+ "<?xml version=\"1.0\" encoding=\"UTF-8\" ?> \
+ <record> \
+ <attribute id=\"0x0001\"> \
+ <sequence> \
+ <uuid value=\"0x1124\" /> \
+ </sequence> \
+ </attribute> \
+ <attribute id=\"0x0004\"> \
+ <sequence> \
+ <sequence> \
+ <uuid value=\"0x0100\" /> \
+ <uint16 value=\"0x0011\" /> \
+ </sequence> \
+ <sequence> \
+ <uuid value=\"0x0011\" /> \
+ </sequence> \
+ </sequence> \
+ </attribute> \
+ <attribute id=\"0x0005\"> \
+ <sequence> \
+ <uuid value=\"0x1002\" /> \
+ </sequence> \
+ </attribute> \
+ <attribute id=\"0x0006\"> \
+ <sequence> \
+ <uint16 value=\"0x656e\" /> \
+ <uint16 value=\"0x006a\" /> \
+ <uint16 value=\"0x0100\" /> \
+ </sequence> \
+ </attribute> \
+ <attribute id=\"0x0009\"> \
+ <sequence> \
+ <sequence> \
+ <uuid value=\"0x0011\" /> \
+ <uint16 value=\"0x0100\" /> \
+ </sequence> \
+ </sequence> \
+ </attribute> \
+ <attribute id=\"0x000d\"> \
+ <sequence> \
+ <sequence> \
+ <sequence> \
+ <uuid value=\"0x0100\" /> \
+ <uint16 value=\"0x0013\" /> \
+ </sequence> \
+ <sequence> \
+ <uuid value=\"0x0011\" /> \
+ </sequence> \
+ </sequence> \
+ </sequence> \
+ </attribute> \
+ <attribute id=\"0x0100\"> \
+ <text value=\"Bluez Mouse\" /> \
+ </attribute> \
+ <attribute id=\"0x0101\"> \
+ <text value=\"Mouse\" /> \
+ </attribute> \
+ <attribute id=\"0x0200\"> \
+ <uint16 value=\"0x0100\" /> \
+ </attribute> \
+ <attribute id=\"0x0201\"> \
+ <uint16 value=\"0x0111\" /> \
+ </attribute> \
+ <attribute id=\"0x0202\"> \
+ <uint8 value=\"0x40\" /> \
+ </attribute> \
+ <attribute id=\"0x0203\"> \
+ <uint8 value=\"0x00\" /> \
+ </attribute> \
+ <attribute id=\"0x0204\"> \
+ <boolean value=\"true\" /> \
+ </attribute> \
+ <attribute id=\"0x0205\"> \
+ <boolean value=\"true\" /> \
+ </attribute> \
+ <attribute id=\"0x0206\"> \
+ <sequence> \
+ <sequence> \
+ <uint8 value=\"0x22\" /> \
+ <text encoding=\"hex\" value=\"05010902a10185010901a100050919012903150025017501950381027505950181010501093009311581257f750895028106a10285010938950175081581257f8106c0c0c005010906a1018502a100050719e029e71500250175019508810295087508150025650507190029658100c0c005010905A10185030901A1000930093109330934150026FF00350046FF0075089504810209397504950115002507463B016614008142750195048103050919012910750195108102C0C0\" /> \
+ </sequence> \
+ </sequence> \
+ </attribute> \
+ <attribute id=\"0x0207\"> \
+ <sequence> \
+ <sequence> \
+ <uint16 value=\"0x0409\" /> \
+ <uint16 value=\"0x0100\" /> \
+ </sequence> \
+ </sequence> \
+ </attribute> \
+ <attribute id=\"0x020b\"> \
+ <uint16 value=\"0x0100\" /> \
+ </attribute> \
+ <attribute id=\"0x020e\"> \
+ <boolean value=\"true\" /> \
+ </attribute> \
+ </record>"
+#endif
+
+
struct ext_io;
struct ext_profile {
@@ -652,6 +920,10 @@ struct ext_profile {
GSList *conns;
GSList *connects;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ char *destination;
+ char *app_path;
+#endif
};
struct ext_io {
@@ -695,6 +967,11 @@ static GSList *custom_props = NULL;
static GSList *profiles = NULL;
static GSList *ext_profiles = NULL;
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+static int connect_io(struct ext_io *conn, const bdaddr_t *src,
+ const bdaddr_t *dst);
+#endif
+
void btd_profile_foreach(void (*func)(struct btd_profile *p, void *data),
void *data)
{
@@ -1174,6 +1451,23 @@ static void ext_confirm(GIOChannel *io, gpointer user_data)
DBG("incoming connect from %s", addr);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+{
+ struct btd_device *device;
+ gboolean restricted = FALSE;
+
+ device = btd_adapter_find_device(adapter_find(&src), &dst,
+ BDADDR_BREDR);
+ if (device) {
+ restricted = device_is_profile_restricted(device, HFP_HS_UUID);
+ if (restricted) {
+ DBG("HFP_HS is restricted");
+ return;
+ }
+ }
+}
+#endif
+
conn = create_conn(server, io, &src, &dst);
if (conn == NULL)
return;
@@ -1368,6 +1662,21 @@ static struct ext_profile *find_ext(struct btd_profile *p)
return l->data;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+gboolean ext_profile_is_registered_as_client_role(struct btd_profile *p)
+{
+ struct ext_profile *ext = find_ext(p);
+ if (ext && ext->role) {
+ if(strcasecmp(ext->role, "client") == 0) {
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+ }
+ return FALSE;
+}
+#endif
+
static int ext_adapter_probe(struct btd_profile *p,
struct btd_adapter *adapter)
{
@@ -1569,6 +1878,38 @@ static void record_cb(sdp_list_t *recs, int err, gpointer user_data)
sdp_record_t *rec = r->data;
sdp_list_t *protos;
int port;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ char *profile_uuid;
+ sdp_list_t *svcclass = NULL;
+
+ if (sdp_get_service_classes(rec, &svcclass) < 0)
+ continue;
+
+ /* Check for empty service classes list */
+ if (svcclass == NULL) {
+ DBG("Skipping record with no service classes");
+ continue;
+ }
+
+ /* Extract the first element and skip the remainning */
+ profile_uuid = bt_uuid2string(svcclass->data);
+ if (!profile_uuid) {
+ sdp_list_free(svcclass, free);
+ continue;
+ }
+
+ sdp_list_free(svcclass, free);
+
+ DBG("profile uuid %s port uuid %s", profile_uuid, ext->remote_uuid);
+
+ if (g_ascii_strncasecmp(profile_uuid, ext->remote_uuid,
+ strlen(profile_uuid)) != 0) {
+ free(profile_uuid);
+ continue;
+ }
+
+ free(profile_uuid);
+#endif
if (sdp_get_access_protos(rec, &protos) < 0) {
error("Unable to get proto list from %s record",
@@ -1771,14 +2112,28 @@ static char *get_spp_record(struct ext_profile *ext, struct ext_io *l2cap,
struct ext_io *rfcomm)
{
char *svc, *rec;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ char *lan_seq;
+ uint16_t code_ISO639 = (0x65 << 8) | 0x6e;
+ uint16_t encoding = 106;
+ uint16_t base_offset = SDP_PRIMARY_LANG_BASE;
+#endif
if (ext->service)
svc = g_strdup_printf("<uuid value=\"%s\" />", ext->service);
else
svc = g_strdup("");
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ lan_seq = g_strdup_printf(LANG_SEQ, code_ISO639, encoding,
+ base_offset);
+ rec = g_strdup_printf(SPP_RECORD, svc, rfcomm->chan, lan_seq, ext->version,
+ ext->name);
+ g_free(lan_seq);
+#else
rec = g_strdup_printf(SPP_RECORD, svc, rfcomm->chan, ext->version,
ext->name);
+#endif
g_free(svc);
return rec;
}
@@ -1799,6 +2154,14 @@ 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)
{
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ uint8_t chan = 0;
+
+ if (rfcomm)
+ chan = rfcomm->chan;
+
+ return g_strdup_printf(PSE_RECORD, chan, ext->version, ext->name);
+#else
uint16_t psm = 0;
uint8_t chan = 0;
@@ -1808,6 +2171,7 @@ static char *get_pse_record(struct ext_profile *ext, struct ext_io *l2cap,
chan = rfcomm->chan;
return g_strdup_printf(PSE_RECORD, chan, ext->version, ext->name, psm);
+#endif
}
static char *get_mas_record(struct ext_profile *ext, struct ext_io *l2cap,
@@ -1845,18 +2209,34 @@ static char *get_sync_record(struct ext_profile *ext, struct ext_io *l2cap,
ext->name);
}
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+static char *get_hid_device_record(struct ext_profile *ext, struct ext_io *l2cap,
+ struct ext_io *rfcomm)
+{
+ return g_strdup(HID_DEVICE_RECORD);
+}
+#endif
+
static char *get_opp_record(struct ext_profile *ext, struct ext_io *l2cap,
struct ext_io *rfcomm)
{
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
uint16_t psm = 0;
+#endif
uint8_t chan = 0;
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
if (l2cap)
psm = l2cap->psm;
+#endif
if (rfcomm)
chan = rfcomm->chan;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ return g_strdup_printf(OPP_RECORD, chan, ext->version, ext->name);
+#else
return g_strdup_printf(OPP_RECORD, chan, ext->version, psm, ext->name);
+#endif
}
static char *get_ftp_record(struct ext_profile *ext, struct ext_io *l2cap,
@@ -2001,7 +2381,11 @@ static struct default_settings {
.sec_level = BT_IO_SEC_LOW,
.authorize = false,
.get_record = get_opp_record,
- .version = 0x0102,
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ .version = 0x0100,
+#else
+ .version = 0x0102,
+#endif
}, {
.uuid = OBEX_FTP_UUID,
.name = "File Transfer",
@@ -2052,7 +2436,18 @@ static struct default_settings {
.authorize = true,
.get_record = get_mns_record,
.version = 0x0102
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+ }, {
+ .uuid = HID_DEVICE_UUID,
+ .name = "HID Device",
+ .psm = HID_DEVICE_INTR_PSM,
+ .authorize = TRUE,
+ .get_record = get_hid_device_record,
+ .version = 0x0100,
},
+#else
+ },
+#endif
};
static void ext_set_defaults(struct ext_profile *ext)
@@ -2151,10 +2546,32 @@ static int parse_ext_opt(struct ext_profile *ext, const char *key,
return -EINVAL;
dbus_message_iter_get_basic(value, &b);
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
if (b)
ext->sec_level = BT_IO_SEC_MEDIUM;
else
ext->sec_level = BT_IO_SEC_LOW;
+#else
+#ifdef TIZEN_BT_IO_CAPA_NO_INPUT_OUTPUT
+ /*
+ * NoInputOut device should have another authentication method.
+ * So turn off force authentication setting for that device.
+ */
+ if (b)
+ ext->sec_level = BT_IO_SEC_MEDIUM;
+ else
+ ext->sec_level = BT_IO_SEC_LOW;
+#else
+ if (!strcasecmp(ext->uuid, WEARABLE_OLD_SAP_UUID) ||
+ !strcasecmp(ext->uuid, WEARABLE_NEW_SAP_UUID)) {
+ DBG("Set SAP UUID's sec_level to HIGH");
+ ext->sec_level = BT_IO_SEC_HIGH;
+ } else if (b)
+ ext->sec_level = BT_IO_SEC_MEDIUM;
+ else
+ ext->sec_level = BT_IO_SEC_LOW;
+#endif
+#endif
} else if (strcasecmp(key, "RequireAuthorization") == 0) {
if (type != DBUS_TYPE_BOOLEAN)
return -EINVAL;
@@ -2235,6 +2652,89 @@ static void set_service(struct ext_profile *ext)
}
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static struct ext_profile *create_ext2(const char *owner, const char *path,
+ const char *uuid, const char *destination, const char *app_path,
+ DBusMessageIter *opts)
+{
+ struct btd_profile *p;
+ struct ext_profile *ext;
+
+ ext = g_new0(struct ext_profile, 1);
+
+ ext->uuid = bt_name2string(uuid);
+ if (ext->uuid == NULL) {
+ g_free(ext);
+ return NULL;
+ }
+
+ ext->owner = g_strdup(destination);
+ ext->path = g_strdup(app_path);
+ ext->destination = g_strdup(destination);
+ ext->app_path = g_strdup(app_path);
+ DBG("VALUES Dest %s, path2 %s", destination, app_path);
+ ext_set_defaults(ext);
+
+ while (dbus_message_iter_get_arg_type(opts) == DBUS_TYPE_DICT_ENTRY) {
+ DBusMessageIter value, entry;
+ const char *key;
+
+ dbus_message_iter_recurse(opts, &entry);
+ dbus_message_iter_get_basic(&entry, &key);
+
+ dbus_message_iter_next(&entry);
+ dbus_message_iter_recurse(&entry, &value);
+
+ if (parse_ext_opt(ext, key, &value) < 0)
+ error("Invalid value for profile option %s", key);
+
+ dbus_message_iter_next(opts);
+ }
+
+ if (!ext->service)
+ set_service(ext);
+
+ if (ext->enable_server && !(ext->record || ext->get_record))
+ ext->get_record = get_generic_record;
+
+ if (!ext->name)
+ ext->name = g_strdup_printf("%s%s/%s", owner, path, uuid);
+
+ if (!ext->remote_uuid) {
+ if (ext->service)
+ ext->remote_uuid = g_strdup(ext->service);
+ else
+ ext->remote_uuid = g_strdup(ext->uuid);
+ }
+
+ p = &ext->p;
+
+ p->name = ext->name;
+ p->local_uuid = ext->service ? ext->service : ext->uuid;
+ p->remote_uuid = ext->remote_uuid;
+
+ if (ext->enable_server) {
+ p->adapter_probe = ext_adapter_probe;
+ p->adapter_remove = ext_adapter_remove;
+ }
+
+ if (ext->enable_client) {
+ p->device_probe = ext_device_probe;
+ p->device_remove = ext_device_remove;
+ p->connect = ext_connect_dev;
+ p->disconnect = ext_disconnect_dev;
+ }
+
+ DBG("Created \"%s\"", ext->name);
+
+ ext_profiles = g_slist_append(ext_profiles, ext);
+
+ adapter_foreach(adapter_add_profile, &ext->p);
+
+ return ext;
+}
+#endif
+
static struct ext_profile *create_ext(const char *owner, const char *path,
const char *uuid,
DBusMessageIter *opts)
@@ -2307,7 +2807,10 @@ static struct ext_profile *create_ext(const char *owner, const char *path,
}
DBG("Created \"%s\"", ext->name);
-
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+ if (g_strcmp0(ext->uuid , HID_DEVICE_UUID) == 0)
+ ext->local_psm = 0;
+#endif
ext_profiles = g_slist_append(ext_profiles, ext);
adapter_foreach(adapter_add_profile, &ext->p);
@@ -2411,11 +2914,72 @@ static DBusMessage *unregister_profile(DBusConnection *conn,
return dbus_message_new_method_return(msg);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static DBusMessage *register_profile2(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ const char *path, *sender, *uuid;
+ DBusMessageIter args, opts;
+ struct ext_profile *ext;
+ const char *destination, *app_path;
+ sender = dbus_message_get_sender(msg);
+
+ DBG("sender %s", sender);
+
+ dbus_message_iter_init(msg, &args);
+
+ dbus_message_iter_get_basic(&args, &path);
+ dbus_message_iter_next(&args);
+ DBG("path %s", path);
+
+ DBG("path %s", path);
+ dbus_message_iter_get_basic(&args, &uuid);
+ dbus_message_iter_next(&args);
+ DBG("uuid %s", uuid);
+ dbus_message_iter_get_basic(&args, &destination);
+ dbus_message_iter_next(&args);
+ DBG("destination %s", destination);
+ dbus_message_iter_get_basic(&args, &app_path);
+ dbus_message_iter_next(&args);
+ DBG("path2 %s", app_path);
+ ext = find_ext_profile(destination, path);
+ if (ext)
+ return btd_error_already_exists(msg);
+ if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_ARRAY)
+ return btd_error_invalid_args(msg);
+ DBG("interator");
+ dbus_message_iter_recurse(&args, &opts);
+
+ ext = create_ext2(sender, path, uuid, destination, app_path, &opts);
+ if (!ext)
+ return btd_error_invalid_args(msg);
+#if 0
+ ext->id = g_dbus_add_disconnect_watch(conn, sender, ext_exited, ext,
+ NULL);
+#endif
+
+ return dbus_message_new_method_return(msg);
+}
+#endif
+
static const GDBusMethodTable methods[] = {
{ GDBUS_METHOD("RegisterProfile",
GDBUS_ARGS({ "profile", "o"}, { "UUID", "s" },
{ "options", "a{sv}" }),
NULL, register_profile) },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ /* For Dbus Smack devides dbus API, the functionality is same */
+ { GDBUS_METHOD("RegisterProfile1",
+ GDBUS_ARGS({ "profile", "o"}, { "UUID", "s" },
+ { "options", "a{sv}" }),
+ NULL, register_profile) },
+ { GDBUS_METHOD("RegisterProfile2",
+ GDBUS_ARGS({"profile", "o"}, { "UUID", "s" },
+ {"destination", "s"}, {"path", "s"},
+ { "options", "a{sv}"}),
+ NULL, register_profile2) },
+#endif
+
{ GDBUS_METHOD("UnregisterProfile", GDBUS_ARGS({ "profile", "o" }),
NULL, unregister_profile) },
{ }
@@ -2509,11 +3073,22 @@ void btd_profile_cleanup(void)
g_slist_free_full(ext->conns, ext_io_destroy);
ext->conns = NULL;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (ext->destination == NULL) {
+ msg = dbus_message_new_method_call(ext->owner,
+ ext->path,
+ "org.bluez.Profile1",
+ "Release");
+ if (msg)
+ g_dbus_send_message(conn, msg);
+ }
+#else
msg = dbus_message_new_method_call(ext->owner, ext->path,
"org.bluez.Profile1",
"Release");
if (msg)
g_dbus_send_message(conn, msg);
+#endif
g_dbus_remove_watch(conn, ext->id);
remove_ext(ext);
diff --git a/src/profile.h b/src/profile.h
index 4448a2a6..d6e20f38 100755
--- a/src/profile.h
+++ b/src/profile.h
@@ -73,5 +73,9 @@ bool btd_profile_add_custom_prop(const char *uuid, const char *type,
void *user_data);
bool btd_profile_remove_custom_prop(const char *uuid, const char *name);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+gboolean ext_profile_is_registered_as_client_role(struct btd_profile *p);
+#endif
+
void btd_profile_init(void);
void btd_profile_cleanup(void);
diff --git a/src/sdp-xml.c b/src/sdp-xml.c
index 0a3eb600..ec863b69 100755
--- a/src/sdp-xml.c
+++ b/src/sdp-xml.c
@@ -531,6 +531,11 @@ static void element_end(GMarkupParseContext *context,
struct context_data *ctx_data = user_data;
struct sdp_xml_data *elem;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (element_name == NULL)
+ return;
+#endif
+
if (!strcmp(element_name, "record"))
return;
diff --git a/src/service.c b/src/service.c
index 207ffaea..28a4db7f 100755
--- a/src/service.c
+++ b/src/service.c
@@ -171,6 +171,10 @@ int service_probe(struct btd_service *service)
void service_remove(struct btd_service *service)
{
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (service->profile == NULL)
+ return;
+#endif
change_state(service, BTD_SERVICE_STATE_DISCONNECTED, -ECONNABORTED);
change_state(service, BTD_SERVICE_STATE_UNAVAILABLE, 0);
service->profile->device_remove(service);
@@ -238,8 +242,11 @@ int btd_service_connect(struct btd_service *service)
struct btd_profile *profile = service->profile;
char addr[18];
int err;
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (profile != NULL && !profile->connect)
+#else
if (!profile->connect)
+#endif
return -ENOTSUP;
switch (service->state) {
@@ -373,9 +380,14 @@ bool btd_service_remove_state_cb(unsigned int id)
void btd_service_connecting_complete(struct btd_service *service, int err)
{
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
if (service->state != BTD_SERVICE_STATE_DISCONNECTED &&
service->state != BTD_SERVICE_STATE_CONNECTING)
return;
+#else
+ if (service->state != BTD_SERVICE_STATE_CONNECTING)
+ return;
+#endif
if (err == 0)
change_state(service, BTD_SERVICE_STATE_CONNECTED, 0);
diff --git a/src/shared/att.c b/src/shared/att.c
index 3071b51b..bb6a4375 100755
--- a/src/shared/att.c
+++ b/src/shared/att.c
@@ -68,6 +68,12 @@ struct bt_att {
bool in_req; /* There's a pending incoming request */
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ bdaddr_t bdaddr;
+ uint8_t bdaddr_type;
+ bool service_change_indication; /* Service changed indication status */
+#endif
+
uint8_t *buf;
uint16_t mtu;
@@ -209,6 +215,10 @@ static void destroy_att_send_op(void *data)
static void cancel_att_send_op(struct att_send_op *op)
{
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (op->callback)
+ op->callback(BT_ATT_OP_ERROR_RSP, NULL, 0, op->user_data);
+#endif
if (op->destroy)
op->destroy(op->user_data);
@@ -334,8 +344,14 @@ static struct att_send_op *create_att_send_op(struct bt_att *att,
* response from the remote end, then no callback should have been
* provided, since it will never be called.
*/
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (callback && type != ATT_OP_TYPE_REQ && type != ATT_OP_TYPE_IND
+ && type != ATT_OP_TYPE_CMD)
+ return NULL;
+#else
if (callback && type != ATT_OP_TYPE_REQ && type != ATT_OP_TYPE_IND)
return NULL;
+#endif
/* Similarly, if the operation does elicit a response then a callback
* must be provided.
@@ -479,12 +495,21 @@ static bool can_write_data(struct io *io, void *user_data)
case ATT_OP_TYPE_IND:
att->pending_ind = op;
break;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ case ATT_OP_TYPE_CMD:
+ if (op->callback)
+ op->callback(0, NULL, 0, op->user_data);
+ destroy_att_send_op(op);
+ return true;
+#endif
case ATT_OP_TYPE_RSP:
/* Set in_req to false to indicate that no request is pending */
att->in_req = false;
/* Fall through to the next case */
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
case ATT_OP_TYPE_CMD:
+#endif
case ATT_OP_TYPE_NOT:
case ATT_OP_TYPE_CONF:
case ATT_OP_TYPE_UNKNOWN:
@@ -645,6 +670,13 @@ static bool handle_error_rsp(struct bt_att *att, uint8_t *pdu,
att->pending_req = NULL;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (op->timeout_id) {
+ timeout_remove(op->timeout_id);
+ op->timeout_id = 0;
+ }
+#endif
+
/* Push operation back to request queue */
return queue_push_head(att->req_queue, op);
}
@@ -1515,3 +1547,50 @@ bool bt_att_has_crypto(struct bt_att *att)
return att->crypto ? true : false;
}
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+bool bt_att_set_remote_addr(struct bt_att *att,
+ const bdaddr_t *bdaddr, uint8_t bdaddr_type)
+{
+ if (!att)
+ return false;
+
+ bacpy(&att->bdaddr, bdaddr);
+ att->bdaddr_type = bdaddr_type;
+
+ return true;
+}
+
+bool bt_att_get_remote_addr(struct bt_att *att,
+ bdaddr_t *bdaddr, uint8_t *bdaddr_type)
+{
+ if (!att)
+ return false;
+
+ if (!bacmp(&att->bdaddr, BDADDR_ANY))
+ return false;
+
+ bacpy(bdaddr, &att->bdaddr);
+ *bdaddr_type = att->bdaddr_type;
+
+ return true;
+}
+
+bool bt_att_set_svc_changed_indication_registered(struct bt_att *att, bool value)
+{
+ if (!att)
+ return false;
+
+ att->service_change_indication = value;
+
+ return true;
+}
+
+bool bt_att_get_svc_changed_indication_registered(struct bt_att *att)
+{
+ if (!att)
+ return false;
+
+ return att->service_change_indication;
+}
+#endif
diff --git a/src/shared/att.h b/src/shared/att.h
index 7bffee7d..d05e7205 100755
--- a/src/shared/att.h
+++ b/src/shared/att.h
@@ -25,6 +25,9 @@
#include <stdint.h>
#include "src/shared/att-types.h"
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#include "lib/bluetooth.h"
+#endif
struct bt_att;
@@ -92,3 +95,11 @@ bool bt_att_set_local_key(struct bt_att *att, uint8_t sign_key[16],
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);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+bool bt_att_set_remote_addr(struct bt_att *att,
+ const bdaddr_t *bdaddr, uint8_t bdaddr_type);
+bool bt_att_get_remote_addr(struct bt_att *att,
+ bdaddr_t *bdaddr, uint8_t *bdaddr_type);
+bool bt_att_set_svc_changed_indication_registered(struct bt_att *att, bool value);
+bool bt_att_get_svc_changed_indication_registered(struct bt_att *att);
+#endif \ No newline at end of file
diff --git a/src/shared/btsnoop.c b/src/shared/btsnoop.c
index e20d1b38..d111e15f 100755
--- a/src/shared/btsnoop.c
+++ b/src/shared/btsnoop.c
@@ -25,6 +25,9 @@
#include <config.h>
#endif
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#include <stdio.h>
+#endif
#include <endian.h>
#include <fcntl.h>
#include <unistd.h>
@@ -73,6 +76,11 @@ struct btsnoop {
bool aborted;
bool pklg_format;
bool pklg_v2;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ char *path;
+ int16_t rotate_count;
+ ssize_t file_size;
+#endif
};
struct btsnoop *btsnoop_open(const char *path, unsigned long flags)
@@ -131,7 +139,12 @@ failed:
return NULL;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+struct btsnoop *btsnoop_create(const char *path, uint32_t format,
+ int16_t rotate_count, ssize_t file_size)
+#else
struct btsnoop *btsnoop_create(const char *path, uint32_t format)
+#endif
{
struct btsnoop *btsnoop;
struct btsnoop_hdr hdr;
@@ -162,9 +175,99 @@ struct btsnoop *btsnoop_create(const char *path, uint32_t format)
return NULL;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ 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_FEATURE_BLUEZ_MODIFY
+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->format);
+
+ 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)
@@ -183,6 +286,11 @@ void btsnoop_unref(struct btsnoop *btsnoop)
if (__sync_sub_and_fetch(&btsnoop->ref_count, 1))
return;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (btsnoop->path)
+ free(btsnoop->path);
+#endif
+
if (btsnoop->fd >= 0)
close(btsnoop->fd);
@@ -216,6 +324,16 @@ bool btsnoop_write(struct btsnoop *btsnoop, struct timeval *tv,
pkt.drops = htobe32(drops);
pkt.ts = htobe64(ts + 0x00E03AB44A676000ll);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ 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 3df8998a..25d1af17 100755
--- a/src/shared/btsnoop.h
+++ b/src/shared/btsnoop.h
@@ -99,7 +99,12 @@ struct btsnoop_opcode_user_logging {
struct btsnoop;
struct btsnoop *btsnoop_open(const char *path, unsigned long flags);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+struct btsnoop *btsnoop_create(const char *path, uint32_t format,
+ int16_t rotate_count, ssize_t file_size);
+#else
struct btsnoop *btsnoop_create(const char *path, uint32_t format);
+#endif
struct btsnoop *btsnoop_ref(struct btsnoop *btsnoop);
void btsnoop_unref(struct btsnoop *btsnoop);
diff --git a/src/shared/crypto.c b/src/shared/crypto.c
index 6de5514c..d8188f2f 100755
--- a/src/shared/crypto.c
+++ b/src/shared/crypto.c
@@ -213,7 +213,11 @@ static int alg_new(int fd, const void *keyval, socklen_t keylen)
return -1;
/* FIXME: This should use accept4() with SOCK_CLOEXEC */
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ return accept(fd, NULL, NULL);
+#else
return accept(fd, NULL, 0);
+#endif
}
static bool alg_encrypt(int fd, const void *inbuf, size_t inlen,
diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c
index 4386692f..025a5334 100755
--- a/src/shared/gatt-client.c
+++ b/src/shared/gatt-client.c
@@ -33,6 +33,9 @@
#include "src/shared/queue.h"
#include "src/shared/gatt-db.h"
#include "src/shared/gatt-client.h"
+#if defined TIZEN_FEATURE_BLUEZ_MODIFY
+#include "../log.h"
+#endif
#include <assert.h>
#include <limits.h>
@@ -112,6 +115,12 @@ struct bt_gatt_client {
struct bt_gatt_request *discovery_req;
unsigned int mtu_req_id;
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ char *device_name;
+ struct queue *pending_noti;
+ bool svc_changed_failed;
+#endif
};
struct request {
@@ -126,6 +135,26 @@ struct request {
void (*destroy)(void *);
};
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+struct noti {
+ uint8_t opcode;
+ void *pdu;
+ uint16_t length;
+};
+
+static void notify_cb(uint8_t opcode, const void *pdu, uint16_t length,
+ void *user_data);
+
+static void notification_free(void *data)
+{
+ struct noti *noti = data;
+
+ if (noti->pdu)
+ free(noti->pdu);
+ free(noti);
+}
+#endif
+
static struct request *request_ref(struct request *req)
{
__sync_fetch_and_add(&req->ref_count, 1);
@@ -341,11 +370,31 @@ static void discovery_op_complete(struct discovery_op *op, bool success,
{
/* Reset remaining range */
if (success) {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ util_debug(op->client->debug_callback, op->client->debug_data,
+ "op->start : %u, op->end : %u, op->last : %u",
+ op->start, op->end, op->last);
+
+ if (op->last != UINT16_MAX) {
+ if (op->start != op->last)
+ op->last++;
+
+ if (op->last <= op->end)
+ gatt_db_clear_range(op->client->db,
+ op->last, op->end);
+ }
+#else
if (op->last != UINT16_MAX)
gatt_db_clear_range(op->client->db, op->last + 1,
UINT16_MAX);
- } else
+#endif
+ } else {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ util_debug(op->client->debug_callback, op->client->debug_data,
+ "Fail to discover service. Clear DB [%d]", err);
+#endif
gatt_db_clear(op->client->db);
+ }
op->success = success;
op->complete_func(op, success, err);
@@ -368,6 +417,9 @@ static struct discovery_op *discovery_op_create(struct bt_gatt_client *client,
op->failure_func = failure_func;
op->start = start;
op->end = end;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ op->last = start;
+#endif
return op;
}
@@ -817,6 +869,56 @@ done:
discovery_op_complete(op, success, att_ecode);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void read_name_cb(bool success, uint8_t att_ecode, const uint8_t *value,
+ uint16_t length, void *user_data)
+{
+ struct bt_gatt_client *client = user_data;
+ char *name;
+
+ if (!success) {
+ util_debug(client->debug_callback, client->debug_data,
+ "read_name_cb failed");
+ return;
+ }
+
+ if (length == 0)
+ return;
+
+ name = malloc(length + 1);
+ if (!name)
+ return;
+
+ memcpy(name, value, length);
+ name[length] = '\0';
+
+ util_debug(client->debug_callback, client->debug_data,
+ "read_name_cb : %s", name);
+
+ if (client->device_name)
+ free(client->device_name);
+
+ client->device_name = name;
+}
+
+bool bt_gatt_request_att_mtu(struct bt_gatt_client *client, uint16_t mtu,
+ void *callback, void *user_data)
+{
+ if (!client || !client->ready)
+ return false;
+
+ /* Configure the MTU */
+ if(!bt_gatt_exchange_mtu(client->att,
+ MAX(BT_ATT_DEFAULT_LE_MTU, mtu),
+ callback,
+ user_data,
+ NULL)) {
+ return false;
+ }
+ return true;
+}
+#endif
+
static void discover_chrcs_cb(bool success, uint8_t att_ecode,
struct bt_gatt_result *result,
void *user_data)
@@ -874,6 +976,16 @@ static void discover_chrcs_cb(bool success, uint8_t att_ecode,
chrc_data->properties = properties;
chrc_data->uuid = uuid;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (!strcmp(uuid_str, "00002a00-0000-1000-8000-00805f9b34fb")) {
+ if (!bt_gatt_client_read_value(client, chrc_data->value_handle,
+ read_name_cb, client, NULL)) {
+ util_debug(client->debug_callback, client->debug_data,
+ "Failed to read value");
+ }
+ }
+#endif
+
queue_push_tail(op->pending_chrcs, chrc_data);
}
@@ -970,6 +1082,13 @@ static void discover_secondary_cb(bool success, uint8_t att_ecode,
start, end, uuid_str);
/* Store the service */
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (client->in_svc_chngd) {
+ util_debug(client->debug_callback, client->debug_data,
+ "In service changed, delete service first.");
+ gatt_db_clear_range(client->db, start, end);
+ }
+#endif
attr = gatt_db_insert_service(client->db, start, &uuid, false,
end - start + 1);
if (!attr) {
@@ -1052,7 +1171,11 @@ static void discover_primary_cb(bool success, uint8_t att_ecode,
"Primary service discovery failed."
" ATT ECODE: 0x%02x", att_ecode);
/* Reset error in case of not found */
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND) {
+#else
if (BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND) {
+#endif
success = true;
att_ecode = 0;
}
@@ -1077,6 +1200,13 @@ static void discover_primary_cb(bool success, uint8_t att_ecode,
"start: 0x%04x, end: 0x%04x, uuid: %s",
start, end, uuid_str);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (client->in_svc_chngd) {
+ util_debug(client->debug_callback, client->debug_data,
+ "In service changed, delete service first.");
+ gatt_db_clear_range(client->db, start, end);
+ }
+#endif
attr = gatt_db_insert_service(client->db, start, &uuid, true,
end - start + 1);
if (!attr) {
@@ -1185,6 +1315,9 @@ static void exchange_mtu_cb(bool success, uint8_t att_ecode, void *user_data)
bt_att_get_mtu(client->att));
discover:
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ op->success = false;
+#endif
client->discovery_req = bt_gatt_discover_all_primary_services(
client->att, NULL,
discover_primary_cb,
@@ -1208,6 +1341,33 @@ struct service_changed_op {
uint16_t end_handle;
};
+#if defined TIZEN_FEATURE_BLUEZ_MODIFY
+bool bt_gatt_discover_services(struct bt_gatt_client *client)
+{
+ struct discovery_op *op;
+
+ if (!client->ready)
+ return false;
+
+ op = new0(struct discovery_op, 1);
+ if (!op)
+ return false;
+
+ op->client = client;
+ gatt_db_unref(op->client->db);
+
+ if (bt_gatt_discover_all_primary_services(client->att, NULL,
+ discover_primary_cb,
+ discovery_op_ref(op),
+ discovery_op_unref))
+ return true;
+
+ discovery_op_unref(op);
+
+ return false;
+}
+#endif
+
static void process_service_changed(struct bt_gatt_client *client,
uint16_t start_handle,
uint16_t end_handle);
@@ -1424,6 +1584,23 @@ static void service_changed_register_cb(uint16_t att_ecode, void *user_data)
done:
notify_client_ready(client, success, att_ecode);
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (success) {
+ struct noti *noti;
+
+ while ((noti = queue_pop_head(client->pending_noti))) {
+ notify_cb(noti->opcode, noti->pdu,
+ noti->length, client);
+ notification_free(noti);
+ }
+ } else {
+ util_debug(client->debug_callback, client->debug_data,
+ "Remove all pending notifications");
+ queue_remove_all(client->pending_noti, NULL, NULL,
+ notification_free);
+ }
+#endif
}
static bool register_service_changed(struct bt_gatt_client *client)
@@ -1471,6 +1648,9 @@ static void service_changed_complete(struct discovery_op *op, bool success,
"Failed to discover services within changed range - "
"error: 0x%02x", att_ecode);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ client->svc_changed_failed = true;
+#endif
gatt_db_clear_range(client->db, start_handle, end_handle);
}
@@ -1509,6 +1689,11 @@ static void service_changed_failure(struct discovery_op *op)
{
struct bt_gatt_client *client = op->client;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ util_debug(client->debug_callback, client->debug_data,
+ "Failed to discover services");
+#endif
+
gatt_db_clear_range(client->db, op->start, op->end);
}
@@ -1549,8 +1734,14 @@ static void service_changed_cb(uint16_t value_handle, const uint8_t *value,
struct service_changed_op *op;
uint16_t start, end;
- if (length != 4)
+ if (length != 4) {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ util_debug(client->debug_callback, client->debug_data,
+ "Service changed is received with invalid length : %d",
+ length);
+#endif
return;
+ }
start = get_le16(value);
end = get_le16(value + 2);
@@ -1603,12 +1794,34 @@ fail:
done:
notify_client_ready(client, success, att_ecode);
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (success) {
+ struct noti *noti;
+
+ while ((noti = queue_pop_head(client->pending_noti))) {
+ notify_cb(noti->opcode, noti->pdu,
+ noti->length, client);
+ notification_free(noti);
+ }
+ } else {
+ util_debug(client->debug_callback, client->debug_data,
+ "Remove all pending notifications");
+ queue_remove_all(client->pending_noti, NULL, NULL,
+ notification_free);
+ }
+#endif
}
static void init_fail(struct discovery_op *op)
{
struct bt_gatt_client *client = op->client;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ util_debug(client->debug_callback, client->debug_data,
+ "GATT client init is failed");
+#endif
+
gatt_db_clear(client->db);
}
@@ -1707,6 +1920,10 @@ static void complete_unregister_notify(void *data)
if (notify_data->att_id) {
bt_att_cancel(notify_data->client->att, notify_data->att_id);
notify_data->att_id = 0;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ notify_data->chrc->ccc_write_id = 0;
+ __sync_sub_and_fetch(&notify_data->chrc->notify_count, 1);
+#endif
goto done;
}
@@ -1750,6 +1967,35 @@ static void notify_cb(uint8_t opcode, const void *pdu, uint16_t length,
struct bt_gatt_client *client = user_data;
struct pdu_data pdu_data;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (client->ready == false) {
+ struct noti *noti;
+
+ util_debug(client->debug_callback, client->debug_data,
+ "Client is not ready. pend notification.");
+
+ noti = new0(struct noti, 1);
+ if (!noti)
+ return;
+
+ noti->pdu = malloc(length);
+ if (!noti->pdu) {
+ free(noti);
+ return;
+ }
+
+ noti->opcode = opcode;
+ noti->length = length;
+ memcpy(noti->pdu, pdu, length);
+
+ util_debug(client->debug_callback, client->debug_data,
+ "Notification handle : %d", get_le16(pdu));
+
+ queue_push_tail(client->pending_noti, noti);
+ return;
+ }
+#endif
+
bt_gatt_client_ref(client);
memset(&pdu_data, 0, sizeof(pdu_data));
@@ -1784,6 +2030,16 @@ static void bt_gatt_client_free(struct bt_gatt_client *client)
bt_att_unref(client->att);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (client->in_svc_chngd || client->svc_changed_failed) {
+ util_debug(client->debug_callback, client->debug_data,
+ "Service changed is going. Clear DB");
+ gatt_db_clear(client->db);
+ }
+
+ queue_destroy(client->pending_noti, notification_free);
+#endif
+
gatt_db_unref(client->db);
queue_destroy(client->clones, NULL);
@@ -1792,6 +2048,13 @@ static void bt_gatt_client_free(struct bt_gatt_client *client)
queue_destroy(client->notify_chrcs, notify_chrc_free);
queue_destroy(client->pending_requests, request_unref);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (client->device_name) {
+ free(client->device_name);
+ client->device_name = NULL;
+ }
+#endif
+
if (client->parent) {
queue_remove(client->parent->clones, client);
bt_gatt_client_unref(client->parent);
@@ -1834,6 +2097,9 @@ static struct bt_gatt_client *gatt_client_new(struct gatt_db *db,
client->notify_list = queue_new();
client->notify_chrcs = queue_new();
client->pending_requests = queue_new();
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ client->pending_noti = queue_new();
+#endif
client->notify_id = bt_att_register(att, BT_ATT_OP_HANDLE_VAL_NOT,
notify_cb, client, NULL);
@@ -2443,6 +2709,93 @@ unsigned int bt_gatt_client_read_long_value(struct bt_gatt_client *client,
return req->id;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+struct write_cmd_op {
+ struct bt_gatt_client *client;
+ bt_gatt_client_callback_t callback;
+ void *user_data;
+ bt_gatt_destroy_func_t destroy;
+};
+
+static void destroy_write_cmd_op(void *data)
+{
+ struct write_cmd_op *op = data;
+
+ if (op->destroy)
+ op->destroy(op->user_data);
+
+ free(op);
+}
+
+static void write_cmd_cb(uint8_t opcode, const void *pdu, uint16_t length,
+ void *user_data)
+{
+ struct request *req = user_data;
+ struct write_cmd_op *op = req->data;
+ bool success = true;
+ uint8_t att_ecode = 0;
+
+ if (op->callback)
+ op->callback(success, att_ecode, op->user_data);
+}
+
+unsigned int bt_gatt_client_write_without_response_async(
+ struct bt_gatt_client *client,
+ uint16_t value_handle,
+ bool signed_write,
+ const uint8_t *value, uint16_t length,
+ bt_gatt_client_callback_t callback,
+ void *user_data,
+ bt_gatt_client_destroy_func_t destroy)
+{
+
+ uint8_t pdu[2 + length];
+ struct request *req;
+ struct write_cmd_op *op;
+ int security;
+ uint8_t opcode;
+
+ if (!client)
+ return 0;
+
+ op = new0(struct write_cmd_op, 1);
+ if (!op)
+ return 0;
+
+ req = request_create(client);
+ if (!req)
+ return 0;
+
+ op->callback = callback;
+ op->user_data = user_data;
+ op->destroy = destroy;
+
+ req->data = op;
+ req->destroy = destroy_write_cmd_op;
+
+ /* Only use signed write if unencrypted */
+ if (signed_write) {
+ security = bt_att_get_security(client->att);
+ opcode = security > BT_SECURITY_LOW ? BT_ATT_OP_WRITE_CMD :
+ BT_ATT_OP_SIGNED_WRITE_CMD;
+ } else
+ opcode = BT_ATT_OP_WRITE_CMD;
+
+ put_le16(value_handle, pdu);
+ memcpy(pdu + 2, value, length);
+
+ req->att_id = bt_att_send(client->att, opcode,
+ pdu, sizeof(pdu), write_cmd_cb, req, request_unref);
+ if (!req->att_id) {
+ op->destroy = NULL;
+ request_unref(req);
+ return 0;
+ }
+
+ return req->id;
+}
+#endif
+
unsigned int bt_gatt_client_write_without_response(
struct bt_gatt_client *client,
uint16_t value_handle,
@@ -3134,8 +3487,14 @@ unsigned int bt_gatt_client_register_notify(struct bt_gatt_client *client,
if (!client || !client->db || !chrc_value_handle || !callback)
return 0;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (client->in_svc_chngd)
+ util_debug(client->debug_callback, client->debug_data,
+ "register_notify in service changed handling");
+#else
if (client->in_svc_chngd)
return 0;
+#endif
return register_notify(client, chrc_value_handle, callback, notify,
user_data, destroy);
@@ -3176,3 +3535,21 @@ int bt_gatt_client_get_security(struct bt_gatt_client *client)
return bt_att_get_security(client->att);
}
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+char *bt_gatt_client_get_gap_device_name(struct bt_gatt_client *client)
+{
+ if (!client)
+ return NULL;
+
+ return client->device_name;
+}
+
+bool bt_gatt_client_svc_changed_received(struct bt_gatt_client *client)
+{
+ if (!client)
+ return false;
+
+ return client->in_svc_chngd;
+}
+#endif
diff --git a/src/shared/gatt-client.h b/src/shared/gatt-client.h
index aceb570e..58931c49 100755
--- a/src/shared/gatt-client.h
+++ b/src/shared/gatt-client.h
@@ -92,6 +92,17 @@ unsigned int bt_gatt_client_read_multiple(struct bt_gatt_client *client,
void *user_data,
bt_gatt_client_destroy_func_t destroy);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+unsigned int bt_gatt_client_write_without_response_async(
+ struct bt_gatt_client *client,
+ uint16_t value_handle,
+ bool signed_write,
+ const uint8_t *value, uint16_t length,
+ bt_gatt_client_callback_t callback,
+ void *user_data,
+ bt_gatt_client_destroy_func_t destroy);
+#endif
+
unsigned int bt_gatt_client_write_without_response(
struct bt_gatt_client *client,
uint16_t value_handle,
@@ -134,3 +145,14 @@ bool bt_gatt_client_unregister_notify(struct bt_gatt_client *client,
bool bt_gatt_client_set_security(struct bt_gatt_client *client, int level);
int bt_gatt_client_get_security(struct bt_gatt_client *client);
+
+#if defined TIZEN_FEATURE_BLUEZ_MODIFY
+bool bt_gatt_discover_services(struct bt_gatt_client *client);
+#endif
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+char *bt_gatt_client_get_gap_device_name(struct bt_gatt_client *client);
+bool bt_gatt_request_att_mtu(struct bt_gatt_client *client, uint16_t mtu,
+ void *callback, void *user_data);
+bool bt_gatt_client_svc_changed_received(struct bt_gatt_client *client);
+#endif
diff --git a/src/shared/gatt-db.c b/src/shared/gatt-db.c
index 513451f4..69554a9c 100755
--- a/src/shared/gatt-db.c
+++ b/src/shared/gatt-db.c
@@ -95,6 +95,10 @@ struct gatt_db_attribute {
uint32_t permissions;
uint16_t value_len;
uint8_t *value;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ bool notify_indicate;
+ bdaddr_t unicast_addr;
+#endif
gatt_db_read_t read_func;
gatt_db_write_t write_func;
@@ -719,6 +723,9 @@ service_insert_characteristic(struct gatt_db_service *service,
service->attributes[i] = new_attribute(service, handle, uuid, NULL, 0);
if (!service->attributes[i]) {
free(service->attributes[i - 1]);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ service->attributes[i - 1] = NULL;
+#endif
return NULL;
}
@@ -1784,3 +1791,34 @@ bool gatt_db_attribute_reset(struct gatt_db_attribute *attrib)
return true;
}
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+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(struct gatt_db_attribute *ccc,
+ const char *address)
+{
+ if (ccc)
+ str2ba(address, &ccc->unicast_addr);
+}
+
+const 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 134ec632..8ac0c32e 100755
--- a/src/shared/gatt-db.h
+++ b/src/shared/gatt-db.h
@@ -235,3 +235,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_FEATURE_BLUEZ_MODIFY
+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(struct gatt_db_attribute *ccc,
+ const char *address);
+
+const bdaddr_t *get_ccc_unicast_address(const struct gatt_db_attribute *ccc);
+#endif
diff --git a/src/shared/gatt-server.c b/src/shared/gatt-server.c
index 79e01c8d..f89ae01a 100755
--- a/src/shared/gatt-server.c
+++ b/src/shared/gatt-server.c
@@ -112,6 +112,12 @@ struct bt_gatt_server {
bt_gatt_server_debug_func_t debug_callback;
bt_gatt_server_destroy_func_t debug_destroy;
void *debug_data;
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ bt_gatt_server_mtu_changed_callback_t mtu_chngd_callback;
+ bt_gatt_server_destroy_func_t mtu_chngd_destroy;
+ void *mtu_chngd_data;
+#endif
};
static void bt_gatt_server_free(struct bt_gatt_server *server)
@@ -182,7 +188,11 @@ static bool encode_read_by_grp_type_rsp(struct gatt_db *db, struct queue *q,
int iter = 0;
uint16_t start_handle, end_handle;
struct iovec value;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ uint8_t data_val_len = 0;
+#else
uint8_t data_val_len;
+#endif
*len = 0;
@@ -524,7 +534,11 @@ static bool encode_find_info_rsp(struct gatt_db *db, struct queue *q,
uint16_t handle;
struct gatt_db_attribute *attr;
const bt_uuid_t *type;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ int uuid_len = 0, cur_uuid_len;
+#else
int uuid_len, cur_uuid_len;
+#endif
int iter = 0;
*len = 0;
@@ -789,14 +803,26 @@ static void write_cb(uint8_t opcode, const void *pdu,
goto error;
if (server->pending_write_op) {
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (opcode != BT_ATT_OP_WRITE_CMD) {
+#endif
ecode = BT_ATT_ERROR_UNLIKELY;
goto error;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ }
+#endif
}
op = new0(struct async_write_op, 1);
op->server = server;
op->opcode = opcode;
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (opcode != BT_ATT_OP_WRITE_CMD)
+ server->pending_write_op = op;
+#else
server->pending_write_op = op;
+#endif
if (gatt_db_attribute_write(attr, 0, pdu + 2, length - 2, opcode,
server->att,
@@ -809,6 +835,13 @@ static void write_cb(uint8_t opcode, const void *pdu,
ecode = BT_ATT_ERROR_UNLIKELY;
error:
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ util_debug(server->debug_callback, server->debug_data,
+ "Handling \"Write %s\" is failed : %d",
+ (opcode == BT_ATT_OP_WRITE_REQ) ? "Req" : "Cmd",
+ ecode);
+#endif
+
if (opcode == BT_ATT_OP_WRITE_CMD)
return;
@@ -912,6 +945,12 @@ static void handle_read_req(struct bt_gatt_server *server, uint8_t opcode,
ecode = BT_ATT_ERROR_UNLIKELY;
error:
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ util_debug(server->debug_callback, server->debug_data,
+ "Handling \"Read %sReq\" is failed : %d",
+ (opcode == BT_ATT_OP_READ_BLOB_REQ) ? "Blob" : "",
+ ecode);
+#endif
if (op)
async_read_op_destroy(op);
@@ -1369,6 +1408,7 @@ static void exchange_mtu_cb(uint8_t opcode, const void *pdu,
}
client_rx_mtu = get_le16(pdu);
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
final_mtu = MAX(MIN(client_rx_mtu, server->mtu), BT_ATT_DEFAULT_LE_MTU);
/* Respond with the server MTU */
@@ -1379,6 +1419,21 @@ static void exchange_mtu_cb(uint8_t opcode, const void *pdu,
/* Set MTU to be the minimum */
server->mtu = final_mtu;
bt_att_set_mtu(server->att, final_mtu);
+#else
+ final_mtu = MAX(MIN(client_rx_mtu, BT_ATT_MAX_LE_MTU), BT_ATT_DEFAULT_LE_MTU);
+
+ /* Set MTU to be the minimum */
+ server->mtu = final_mtu;
+ bt_att_set_mtu(server->att, final_mtu);
+
+ /* Respond with the server MTU */
+ put_le16(server->mtu, rsp_pdu);
+ bt_att_send(server->att, BT_ATT_OP_MTU_RSP, rsp_pdu, 2, NULL, NULL,
+ NULL);
+
+ if (server->mtu_chngd_callback)
+ server->mtu_chngd_callback(final_mtu, server->mtu_chngd_data);
+#endif
util_debug(server->debug_callback, server->debug_data,
"MTU exchange complete, with MTU: %u", final_mtu);
@@ -1633,3 +1688,23 @@ bool bt_gatt_server_send_indication(struct bt_gatt_server *server,
return result;
}
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+bool bt_gatt_server_set_mtu_changed(struct bt_gatt_server *server,
+ bt_gatt_server_mtu_changed_callback_t callback,
+ void *user_data,
+ bt_gatt_server_destroy_func_t destroy)
+{
+ if (!server)
+ return false;
+
+ if (server->mtu_chngd_destroy)
+ server->mtu_chngd_destroy(server->mtu_chngd_data);
+
+ server->mtu_chngd_callback = callback;
+ server->mtu_chngd_destroy = destroy;
+ server->mtu_chngd_data = user_data;
+
+ return true;
+}
+#endif
diff --git a/src/shared/gatt-server.h b/src/shared/gatt-server.h
index 0e480e1b..c61eabe8 100755
--- a/src/shared/gatt-server.h
+++ b/src/shared/gatt-server.h
@@ -50,3 +50,14 @@ bool bt_gatt_server_send_indication(struct bt_gatt_server *server,
bt_gatt_server_conf_func_t callback,
void *user_data,
bt_gatt_server_destroy_func_t destroy);
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+typedef void (*bt_gatt_server_mtu_changed_callback_t)(uint16_t mtu,
+ void *user_data);
+
+bool bt_gatt_server_set_mtu_changed(struct bt_gatt_server *server,
+ bt_gatt_server_mtu_changed_callback_t callback,
+ void *user_data,
+ bt_gatt_server_destroy_func_t destroy);
+
+#endif
diff --git a/src/storage.c b/src/storage.c
index 734a9e08..c478e037 100755
--- a/src/storage.c
+++ b/src/storage.c
@@ -144,6 +144,53 @@ int read_local_name(const bdaddr_t *bdaddr, char *name)
return 0;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+int write_device_characteristics(const bdaddr_t *sba, const bdaddr_t *dba,
+ uint8_t bdaddr_type, uint16_t handle,
+ const char *chars)
+{
+ char filename[PATH_MAX + 1], addr[18], key[25];
+
+ create_filename(filename, PATH_MAX, sba, "characteristics");
+
+ create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+ ba2str(dba, addr);
+ snprintf(key, sizeof(key), "%17s#%hhu#%04X", addr, bdaddr_type, handle);
+
+ return textfile_put(filename, key, chars);
+}
+
+char *read_device_characteristics(const bdaddr_t *sba, const bdaddr_t *dba,
+ uint8_t bdaddr_type, uint16_t handle)
+{
+ char filename[PATH_MAX + 1], addr[18], key[25];
+
+ create_filename(filename, PATH_MAX, sba, "characteristics");
+
+ ba2str(dba, addr);
+ snprintf(key, sizeof(key), "%17s#%hhu#%04X", addr, bdaddr_type, handle);
+
+ return textfile_get(filename, key);
+}
+
+int write_device_attribute(const bdaddr_t *sba, const bdaddr_t *dba,
+ uint8_t bdaddr_type, uint16_t handle,
+ const char *chars)
+{
+ char filename[PATH_MAX + 1], addr[18], key[25];
+
+ create_filename(filename, PATH_MAX, sba, "attributes");
+
+ create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+
+ ba2str(dba, addr);
+ snprintf(key, sizeof(key), "%17s#%hhu#%04X", addr, bdaddr_type, handle);
+
+ return textfile_put(filename, key, chars);
+}
+#endif
+
sdp_record_t *record_from_string(const char *str)
{
sdp_record_t *rec;
diff --git a/src/storage.h b/src/storage.h
index 1c0ad57e..979075b0 100755
--- a/src/storage.h
+++ b/src/storage.h
@@ -25,5 +25,15 @@ int read_discoverable_timeout(const char *src, int *timeout);
int read_pairable_timeout(const char *src, int *timeout);
int read_on_mode(const char *src, char *mode, int length);
int read_local_name(const bdaddr_t *bdaddr, char *name);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+int write_device_characteristics(const bdaddr_t *sba, const bdaddr_t *dba,
+ uint8_t bdaddr_type, uint16_t handle,
+ const char *chars);
+char *read_device_characteristics(const bdaddr_t *sba, const bdaddr_t *dba,
+ uint8_t bdaddr_type, uint16_t handle);
+int write_device_attribute(const bdaddr_t *sba, const bdaddr_t *dba,
+ uint8_t bdaddr_type, uint16_t handle,
+ const char *chars);
+#endif
sdp_record_t *record_from_string(const char *str);
sdp_record_t *find_record_in_list(sdp_list_t *recs, const char *uuid);
diff --git a/test/advertisement-example b/test/advertisement-example
new file mode 100755
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/test/exchange-business-cards b/test/exchange-business-cards
new file mode 100755
index 00000000..6805cf71
--- /dev/null
+++ b/test/exchange-business-cards
@@ -0,0 +1,19 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+bus = dbus.SessionBus()
+client = dbus.Interface(bus.get_object("org.bluez.obex", "/org/bluez/obex"),
+ "org.bluez.obex.Client")
+
+if (len(sys.argv) < 4):
+ print "Usage: %s <device> <clientfile> <file>" % (sys.argv[0])
+ sys.exit(1)
+
+print "Creating Session"
+path = client.CreateSession(sys.argv[1], { "Target": "OPP" })
+opp = dbus.Interface(bus.get_object("org.bluez.obex", path),
+ "org.bluez.obex.ObjectPush")
+
+opp.ExchangeBusinessCards(sys.argv[2], sys.argv[3])
diff --git a/test/get-managed-objects b/test/get-managed-objects
new file mode 100755
index 00000000..3156f658
--- /dev/null
+++ b/test/get-managed-objects
@@ -0,0 +1,29 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+import dbus
+
+bus = dbus.SystemBus()
+
+manager = dbus.Interface(bus.get_object("org.bluez", "/"),
+ "org.freedesktop.DBus.ObjectManager")
+
+objects = manager.GetManagedObjects()
+
+for path in objects.keys():
+ print("[ %s ]" % (path))
+
+ interfaces = objects[path]
+
+ for interface in interfaces.keys():
+ if interface in ["org.freedesktop.DBus.Introspectable",
+ "org.freedesktop.DBus.Properties"]:
+ continue
+
+ print(" %s" % (interface))
+
+ properties = interfaces[interface]
+
+ for key in properties.keys():
+ print(" %s = %s" % (key, properties[key]))
diff --git a/test/get-obex-capabilities b/test/get-obex-capabilities
new file mode 100755
index 00000000..e8afbad2
--- /dev/null
+++ b/test/get-obex-capabilities
@@ -0,0 +1,19 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+bus = dbus.SessionBus()
+client = dbus.Interface(bus.get_object("org.bluez.obex", "/org/bluez/obex"),
+ "org.bluez.obex.Client")
+
+if (len(sys.argv) < 3):
+ print "Usage: %s <device> <target>" % (sys.argv[0])
+ sys.exit(1)
+
+print "Creating Session"
+session_path = client.CreateSession(sys.argv[1], { "Target": sys.argv[2] })
+session = dbus.Interface(bus.get_object("org.bluez.obex", session_path),
+ "org.bluez.obex.Session")
+
+print session.GetCapabilities()
diff --git a/test/list-folders b/test/list-folders
new file mode 100755
index 00000000..7321a152
--- /dev/null
+++ b/test/list-folders
@@ -0,0 +1,39 @@
+#!/usr/bin/python
+
+import sys
+import dbus
+
+
+def list_folder(folder):
+ bus = dbus.SessionBus()
+ client = dbus.Interface(bus.get_object("org.bluez.obex",
+ "/org/bluez/obex"),
+ "org.bluez.obex.Client")
+
+ path = client.CreateSession(sys.argv[1], { "Target": "ftp" })
+
+ ftp = dbus.Interface(bus.get_object("org.bluez.obex", path),
+ "org.bluez.obex.FileTransfer")
+
+ if folder:
+ for node in folder.split("/"):
+ ftp.ChangeFolder(node)
+
+ for i in ftp.ListFolder():
+ if i["Type"] == "folder":
+ print "%s/" % (i["Name"])
+ else:
+ print "%s" % (i["Name"])
+
+
+if __name__ == '__main__':
+
+ if len(sys.argv) < 2:
+ print "Usage: %s <device> [folder]" % (sys.argv[0])
+ sys.exit(1)
+
+ folder = None
+ if len(sys.argv) == 3:
+ folder = sys.argv[2]
+
+ list_folder(folder)
diff --git a/test/simple-obex-agent b/test/simple-obex-agent
new file mode 100755
index 00000000..05ec4eda
--- /dev/null
+++ b/test/simple-obex-agent
@@ -0,0 +1,87 @@
+#!/usr/bin/python
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+import sys
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+try:
+ from gi.repository import GObject
+except ImportError:
+ import gobject as GObject
+
+BUS_NAME = 'org.bluez.obex'
+PATH = '/org/bluez/obex'
+AGENT_MANAGER_INTERFACE = 'org.bluez.obex.AgentManager1'
+AGENT_INTERFACE = 'org.bluez.obex.Agent1'
+TRANSFER_INTERFACE = 'org.bluez.obex.Transfer1'
+
+def ask(prompt):
+ try:
+ return raw_input(prompt)
+ except:
+ return input(prompt)
+
+class Agent(dbus.service.Object):
+ def __init__(self, conn=None, obj_path=None):
+ dbus.service.Object.__init__(self, conn, obj_path)
+ self.pending_auth = False
+
+ @dbus.service.method(AGENT_INTERFACE, in_signature="o",
+ out_signature="s")
+ def AuthorizePush(self, path):
+ transfer = dbus.Interface(bus.get_object(BUS_NAME, path),
+ 'org.freedesktop.DBus.Properties')
+ properties = transfer.GetAll(TRANSFER_INTERFACE);
+
+ self.pending_auth = True
+ auth = ask("Authorize (%s, %s) (Y/n):" % (path,
+ properties['Name']))
+
+ if auth == "n" or auth == "N":
+ self.pending_auth = False
+ raise dbus.DBusException(
+ "org.bluez.obex.Error.Rejected: "
+ "Not Authorized")
+
+ self.pending_auth = False
+
+ return properties['Name']
+
+ @dbus.service.method(AGENT_INTERFACE, in_signature="",
+ out_signature="")
+ def Cancel(self):
+ print("Authorization Canceled")
+ self.pending_auth = False
+
+if __name__ == '__main__':
+ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+ bus = dbus.SessionBus()
+ manager = dbus.Interface(bus.get_object(BUS_NAME, PATH),
+ AGENT_MANAGER_INTERFACE)
+
+ path = "/test/agent"
+ agent = Agent(bus, path)
+
+ mainloop = GObject.MainLoop()
+
+ manager.RegisterAgent(path)
+ print("Agent registered")
+
+ cont = True
+ while cont:
+ try:
+ mainloop.run()
+ except KeyboardInterrupt:
+ if agent.pending_auth:
+ agent.Cancel()
+ elif len(transfers) > 0:
+ for a in transfers:
+ a.cancel()
+ else:
+ cont = False
+
+ # manager.UnregisterAgent(path)
+ # print "Agent unregistered"
diff --git a/tools/bdaddr.1 b/tools/bdaddr.1
new file mode 100755
index 00000000..efb77d2e
--- /dev/null
+++ b/tools/bdaddr.1
@@ -0,0 +1,68 @@
+.TH BDADDR 1 "Sep 27 2005" BlueZ "Linux System Administration"
+.SH NAME
+bdaddr \- Utility for changing the Bluetooth device address
+.SH SYNOPSIS
+.B bdaddr
+.br
+.B bdaddr -h
+.br
+.B bdaddr [-i <dev>] [-r] [-t] [new bdaddr]
+
+.SH DESCRIPTION
+.LP
+.B
+bdaddr
+is used to query or set the local Bluetooth device address (BD_ADDR). If run
+with no arguments,
+.B
+bdaddr
+prints the chip manufacturer's name, and the current BD_ADDR. If the IEEE OUI
+index file "oui.txt" is installed on the system, the BD_ADDR owner will be
+displayed. If the optional [new bdaddr] argument is given, the device will be
+reprogrammed with that address. This can either be permanent or temporary, as
+specified by the -t flag. In both cases, the device must be reset before the
+new address will become active. This can be done with a 'soft' reset by
+specifying the -r flag, or a 'hard' reset by removing and replugging the
+device. A 'hard' reset will cause the address to revert to the current
+non-volatile value.
+.PP
+.B
+bdaddr
+uses manufacturer specific commands to set the address, and is therefore
+device specific. For this reason, not all devices are supported, and not all
+options are supported on all devices.
+Current supported manufacturers are:
+.B Ericsson, Cambridge Silicon Radio (CSR), Texas Instruments (TI), Zeevo
+and
+.B ST Microelectronics (ST)
+
+.SH OPTIONS
+.TP
+.BI -h
+Gives a list of possible commands.
+.TP
+.BI -i\ <dev>
+Specify a particular device to operate on. If not specified, default is the
+first available device.
+.TP
+.BI -r
+Reset device and make new BD_ADDR active.
+.B
+CSR
+devices only.
+.TP
+.BI -t
+Temporary change. Do not write to non-volatile memory.
+.B
+CSR
+devices only.
+.SH FILES
+.TP
+.I
+/usr/share/misc/oui.txt
+IEEE Organizationally Unique Identifier master file.
+Manually update from: http://standards.ieee.org/regauth/oui/oui.txt
+.SH AUTHORS
+Written by Marcel Holtmann <marcel@holtmann.org>,
+man page by Adam Laurie <adam@algroup.co.uk>
+.PP
diff --git a/tools/bneptest.c b/tools/bneptest.c
index 1404252d..c761e16a 100755
--- a/tools/bneptest.c
+++ b/tools/bneptest.c
@@ -327,7 +327,11 @@ static gboolean setup_bnep_cb(GIOChannel *chan, GIOCondition cond,
sk = g_io_channel_unix_get_fd(chan);
/* Reading BNEP_SETUP_CONNECTION_REQUEST_MSG */
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
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;
diff --git a/tools/btgatt-client.c b/tools/btgatt-client.c
index 4c8c9dda..31848f91 100755
--- a/tools/btgatt-client.c
+++ b/tools/btgatt-client.c
@@ -365,8 +365,13 @@ static void ready_cb(bool success, uint8_t att_ecode, void *user_data)
struct client *cli = user_data;
if (!success) {
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
PRLOG("GATT discovery procedures failed - error code: 0x%02x\n",
att_ecode);
+#else
+ PRLOG("GATT discovery procedures failed: %s (0x%02x)\n",
+ ecode_to_string(att_ecode), att_ecode);
+#endif
return;
}
@@ -480,7 +485,12 @@ static void read_multiple_cb(bool success, uint8_t att_ecode,
int i;
if (!success) {
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
PRLOG("\nRead multiple request failed: 0x%02x\n", att_ecode);
+#else
+ PRLOG("\nRead multiple request failed: %s (0x%02x)\n",
+ ecode_to_string(att_ecode), att_ecode);
+#endif
return;
}
@@ -1109,8 +1119,13 @@ static void notify_cb(uint16_t value_handle, const uint8_t *value,
static void register_notify_cb(uint16_t att_ecode, void *user_data)
{
if (att_ecode) {
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
PRLOG("Failed to register notify handler "
"- error code: 0x%02x\n", att_ecode);
+#else
+ PRLOG("Failed to register notify handler: %s (0x%02x)\n",
+ ecode_to_string(att_ecode), att_ecode);
+#endif
return;
}
diff --git a/tools/btsnoop.c b/tools/btsnoop.c
index 3eb8082d..c2a194b1 100755
--- a/tools/btsnoop.c
+++ b/tools/btsnoop.c
@@ -34,6 +34,10 @@
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#include <time.h>
+#include <sys/time.h>
+#endif
#include <getopt.h>
#include <endian.h>
#include <arpa/inet.h>
@@ -271,6 +275,118 @@ close_input:
for (i = 0; i < num_input; i++)
close(input_fd[i]);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define BT_SNOOP_TYPE_HCI_PREFIX "btsnoop_type_hci"
+#define MAX_SUPPORTED_ADAPTER 16
+
+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[MAX_SUPPORTED_ADAPTER] = { NULL };
+ 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_format(btsnoop_read_file);
+ if (type != BTSNOOP_FORMAT_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 || index >= MAX_SUPPORTED_ADAPTER)
+ 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_FORMAT_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;
+
+ case BTSNOOP_OPCODE_COMMAND_PKT:
+ case BTSNOOP_OPCODE_EVENT_PKT:
+ case BTSNOOP_OPCODE_ACL_TX_PKT:
+ case BTSNOOP_OPCODE_ACL_RX_PKT:
+ case BTSNOOP_OPCODE_SCO_TX_PKT:
+ case BTSNOOP_OPCODE_SCO_RX_PKT:
+ if (!btsnoop_write_file[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_FORMAT_HCI, -1, -1);
+ }
+
+ if (!btsnoop_write_file[index])
+ goto close_files;
+ btsnoop_write_hci(btsnoop_write_file[index], &tv, index, 0,
+ opcode, buf, pktlen);
+ break;
+ default:
+ printf("skip btmon opcode(%d)\n",opcode);
+ }
+ 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);
+}
+#endif
static void command_extract_eir(const char *input)
{
@@ -518,6 +634,9 @@ 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"
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ "\t-s, --split <input> Split btmon file into legacy btsnoop file(s)\n"
+#endif
"\t-h, --help Show help options\n");
}
@@ -525,12 +644,19 @@ static const struct option main_options[] = {
{ "merge", required_argument, NULL, 'm' },
{ "extract", required_argument, NULL, 'e' },
{ "type", required_argument, NULL, 't' },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ { "split", required_argument, NULL, 's' },
+#endif
{ "version", no_argument, NULL, 'v' },
{ "help", no_argument, NULL, 'h' },
{ }
};
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+enum { INVALID, MERGE, EXTRACT, SPLIT };
+#else
enum { INVALID, MERGE, EXTRACT };
+#endif
int main(int argc, char *argv[])
{
@@ -541,8 +667,11 @@ int main(int argc, char *argv[])
for (;;) {
int opt;
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ opt = getopt_long(argc, argv, "m:e:s:t:vh", main_options, NULL);
+#else
opt = getopt_long(argc, argv, "m:e:t:vh", main_options, NULL);
+#endif
if (opt < 0)
break;
@@ -555,6 +684,11 @@ int main(int argc, char *argv[])
command = EXTRACT;
input_path = optarg;
break;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ case 's':
+ command = SPLIT;
+ input_path = optarg;
+#endif
case 't':
type = optarg;
break;
@@ -600,6 +734,16 @@ int main(int argc, char *argv[])
fprintf(stderr, "extract type not supported\n");
break;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ case SPLIT:
+ if (argc - optind > 0) {
+ fprintf(stderr, "extra arguments not allowed\n");
+ return EXIT_FAILURE;
+ }
+
+ command_split(input_path);
+ break;
+#endif
default:
usage();
return EXIT_FAILURE;
diff --git a/tools/example.psr b/tools/example.psr
new file mode 100755
index 00000000..bbbec73a
--- /dev/null
+++ b/tools/example.psr
@@ -0,0 +1,12 @@
+// PSKEY_BDADDR
+&0001 = 0001 2821 005b 6789
+// PSKEY_ANA_FTRIM
+&01f6 = 0025
+// PSKEY_HOST_INTERFACE
+&01f9 = 0001
+// PSKEY_UART_BAUD_RATE
+&0204 = 01d8
+// PSKEY_ANA_FREQ
+&01fe = 0004
+// PSKEY_UART_CONFIG
+&0205 = 0006
diff --git a/tools/gatt-example b/tools/gatt-example
new file mode 100755
index 00000000..a6f5cbe1
--- /dev/null
+++ b/tools/gatt-example
@@ -0,0 +1,533 @@
+#!/usr/bin/python
+
+import dbus
+import dbus.exceptions
+import dbus.mainloop.glib
+import dbus.service
+
+import array
+import gobject
+
+from random import randint
+
+mainloop = None
+
+BLUEZ_SERVICE_NAME = 'org.bluez'
+GATT_MANAGER_IFACE = 'org.bluez.GattManager1'
+DBUS_OM_IFACE = 'org.freedesktop.DBus.ObjectManager'
+DBUS_PROP_IFACE = 'org.freedesktop.DBus.Properties'
+
+GATT_SERVICE_IFACE = 'org.bluez.GattService1'
+GATT_CHRC_IFACE = 'org.bluez.GattCharacteristic1'
+GATT_DESC_IFACE = 'org.bluez.GattDescriptor1'
+
+class InvalidArgsException(dbus.exceptions.DBusException):
+ _dbus_error_name = 'org.freedesktop.DBus.Error.InvalidArgs'
+
+class NotSupportedException(dbus.exceptions.DBusException):
+ _dbus_error_name = 'org.bluez.Error.NotSupported'
+
+class NotPermittedException(dbus.exceptions.DBusException):
+ _dbus_error_name = 'org.bluez.Error.NotPermitted'
+
+class InvalidValueLengthException(dbus.exceptions.DBusException):
+ _dbus_error_name = 'org.bluez.Error.InvalidValueLength'
+
+class FailedException(dbus.exceptions.DBusException):
+ _dbus_error_name = 'org.bluez.Error.Failed'
+
+
+class Service(dbus.service.Object):
+ PATH_BASE = '/org/bluez/example/service'
+
+ def __init__(self, bus, index, uuid, primary):
+ self.path = self.PATH_BASE + str(index)
+ self.bus = bus
+ self.uuid = uuid
+ self.primary = primary
+ self.characteristics = []
+ dbus.service.Object.__init__(self, bus, self.path)
+
+ def get_properties(self):
+ return {
+ GATT_SERVICE_IFACE: {
+ 'UUID': self.uuid,
+ 'Primary': self.primary,
+ 'Characteristics': dbus.Array(
+ self.get_characteristic_paths(),
+ signature='o')
+ }
+ }
+
+ def get_path(self):
+ return dbus.ObjectPath(self.path)
+
+ def add_characteristic(self, characteristic):
+ self.characteristics.append(characteristic)
+
+ def get_characteristic_paths(self):
+ result = []
+ for chrc in self.characteristics:
+ result.append(chrc.get_path())
+ return result
+
+ def get_characteristics(self):
+ return self.characteristics
+
+ @dbus.service.method(DBUS_PROP_IFACE,
+ in_signature='s',
+ out_signature='a{sv}')
+ def GetAll(self, interface):
+ if interface != GATT_SERVICE_IFACE:
+ raise InvalidArgsException()
+
+ return self.get_properties[GATT_SERVICE_IFACE]
+
+ @dbus.service.method(DBUS_OM_IFACE, out_signature='a{oa{sa{sv}}}')
+ def GetManagedObjects(self):
+ response = {}
+ print 'GetManagedObjects'
+
+ response[self.get_path()] = self.get_properties()
+ chrcs = self.get_characteristics()
+ for chrc in chrcs:
+ response[chrc.get_path()] = chrc.get_properties()
+ descs = chrc.get_descriptors()
+ for desc in descs:
+ response[desc.get_path()] = desc.get_properties()
+
+ return response
+
+
+class Characteristic(dbus.service.Object):
+ def __init__(self, bus, index, uuid, flags, service):
+ self.path = service.path + '/char' + str(index)
+ self.bus = bus
+ self.uuid = uuid
+ self.service = service
+ self.flags = flags
+ self.descriptors = []
+ dbus.service.Object.__init__(self, bus, self.path)
+
+ def get_properties(self):
+ return {
+ GATT_CHRC_IFACE: {
+ 'Service': self.service.get_path(),
+ 'UUID': self.uuid,
+ 'Flags': self.flags,
+ 'Descriptors': dbus.Array(
+ self.get_descriptor_paths(),
+ signature='o')
+ }
+ }
+
+ def get_path(self):
+ return dbus.ObjectPath(self.path)
+
+ def add_descriptor(self, descriptor):
+ self.descriptors.append(descriptor)
+
+ def get_descriptor_paths(self):
+ result = []
+ for desc in self.descriptors:
+ result.append(desc.get_path())
+ return result
+
+ def get_descriptors(self):
+ return self.descriptors
+
+ @dbus.service.method(DBUS_PROP_IFACE,
+ in_signature='s',
+ out_signature='a{sv}')
+ def GetAll(self, interface):
+ if interface != GATT_CHRC_IFACE:
+ raise InvalidArgsException()
+
+ return self.get_properties[GATT_CHRC_IFACE]
+
+ @dbus.service.method(GATT_CHRC_IFACE, out_signature='ay')
+ def ReadValue(self):
+ print 'Default ReadValue called, returning error'
+ raise NotSupportedException()
+
+ @dbus.service.method(GATT_CHRC_IFACE, in_signature='ay')
+ def WriteValue(self, value):
+ print 'Default WriteValue called, returning error'
+ raise NotSupportedException()
+
+ @dbus.service.method(GATT_CHRC_IFACE)
+ def StartNotify(self):
+ print 'Default StartNotify called, returning error'
+ raise NotSupportedException()
+
+ @dbus.service.method(GATT_CHRC_IFACE)
+ def StopNotify(self):
+ print 'Default StopNotify called, returning error'
+ raise NotSupportedException()
+
+ @dbus.service.signal(DBUS_PROP_IFACE,
+ signature='sa{sv}as')
+ def PropertiesChanged(self, interface, changed, invalidated):
+ pass
+
+
+class Descriptor(dbus.service.Object):
+ def __init__(self, bus, index, uuid, characteristic):
+ self.path = characteristic.path + '/desc' + str(index)
+ self.bus = bus
+ self.uuid = uuid
+ self.chrc = characteristic
+ dbus.service.Object.__init__(self, bus, self.path)
+
+ def get_properties(self):
+ return {
+ GATT_DESC_IFACE: {
+ 'Characteristic': self.chrc.get_path(),
+ 'UUID': self.uuid,
+ }
+ }
+
+ def get_path(self):
+ return dbus.ObjectPath(self.path)
+
+ @dbus.service.method(DBUS_PROP_IFACE,
+ in_signature='s',
+ out_signature='a{sv}')
+ def GetAll(self, interface):
+ if interface != GATT_DESC_IFACE:
+ raise InvalidArgsException()
+
+ return self.get_properties[GATT_CHRC_IFACE]
+
+ @dbus.service.method(GATT_DESC_IFACE, out_signature='ay')
+ def ReadValue(self):
+ print 'Default ReadValue called, returning error'
+ raise NotSupportedException()
+
+ @dbus.service.method(GATT_DESC_IFACE, in_signature='ay')
+ def WriteValue(self, value):
+ print 'Default WriteValue called, returning error'
+ raise NotSupportedException()
+
+
+class HeartRateService(Service):
+ """
+ Fake Heart Rate Service that simulates a fake heart beat and control point
+ behavior.
+
+ """
+ HR_UUID = '0000180d-0000-1000-8000-00805f9b34fb'
+
+ def __init__(self, bus, index):
+ Service.__init__(self, bus, index, self.HR_UUID, True)
+ self.add_characteristic(HeartRateMeasurementChrc(bus, 0, self))
+ self.add_characteristic(BodySensorLocationChrc(bus, 1, self))
+ self.add_characteristic(HeartRateControlPointChrc(bus, 2, self))
+ self.energy_expended = 0
+
+
+class HeartRateMeasurementChrc(Characteristic):
+ HR_MSRMT_UUID = '00002a37-0000-1000-8000-00805f9b34fb'
+
+ def __init__(self, bus, index, service):
+ Characteristic.__init__(
+ self, bus, index,
+ self.HR_MSRMT_UUID,
+ ['notify'],
+ service)
+ self.notifying = False
+ self.hr_ee_count = 0
+
+ def hr_msrmt_cb(self):
+ value = []
+ value.append(dbus.Byte(0x06))
+
+ value.append(dbus.Byte(randint(90, 130)))
+
+ if self.hr_ee_count % 10 == 0:
+ value[0] = dbus.Byte(value[0] | 0x08)
+ value.append(dbus.Byte(self.service.energy_expended & 0xff))
+ value.append(dbus.Byte((self.service.energy_expended >> 8) & 0xff))
+
+ self.service.energy_expended = \
+ min(0xffff, self.service.energy_expended + 1)
+ self.hr_ee_count += 1
+
+ print 'Updating value: ' + repr(value)
+
+ self.PropertiesChanged(GATT_CHRC_IFACE, { 'Value': value }, [])
+
+ return self.notifying
+
+ def _update_hr_msrmt_simulation(self):
+ print 'Update HR Measurement Simulation'
+
+ if not self.notifying:
+ return
+
+ gobject.timeout_add(1000, self.hr_msrmt_cb)
+
+ def StartNotify(self):
+ if self.notifying:
+ print 'Already notifying, nothing to do'
+ return
+
+ self.notifying = True
+ self._update_hr_msrmt_simulation()
+
+ def StopNotify(self):
+ if not self.notifying:
+ print 'Not notifying, nothing to do'
+ return
+
+ self.notifying = False
+ self._update_hr_msrmt_simulation()
+
+
+class BodySensorLocationChrc(Characteristic):
+ BODY_SNSR_LOC_UUID = '00002a38-0000-1000-8000-00805f9b34fb'
+
+ def __init__(self, bus, index, service):
+ Characteristic.__init__(
+ self, bus, index,
+ self.BODY_SNSR_LOC_UUID,
+ ['read'],
+ service)
+
+ def ReadValue(self):
+ # Return 'Chest' as the sensor location.
+ return [ 0x01 ]
+
+class HeartRateControlPointChrc(Characteristic):
+ HR_CTRL_PT_UUID = '00002a39-0000-1000-8000-00805f9b34fb'
+
+ def __init__(self, bus, index, service):
+ Characteristic.__init__(
+ self, bus, index,
+ self.HR_CTRL_PT_UUID,
+ ['write'],
+ service)
+
+ def WriteValue(self, value):
+ print 'Heart Rate Control Point WriteValue called'
+
+ if len(value) != 1:
+ raise InvalidValueLengthException()
+
+ byte = value[0]
+ print 'Control Point value: ' + repr(byte)
+
+ if byte != 1:
+ raise FailedException("0x80")
+
+ print 'Energy Expended field reset!'
+ self.service.energy_expended = 0
+
+
+class BatteryService(Service):
+ """
+ Fake Battery service that emulates a draining battery.
+
+ """
+ BATTERY_UUID = '180f'
+
+ def __init__(self, bus, index):
+ Service.__init__(self, bus, index, self.BATTERY_UUID, True)
+ self.add_characteristic(BatteryLevelCharacteristic(bus, 0, self))
+
+
+class BatteryLevelCharacteristic(Characteristic):
+ """
+ Fake Battery Level characteristic. The battery level is drained by 2 points
+ every 5 seconds.
+
+ """
+ BATTERY_LVL_UUID = '2a19'
+
+ def __init__(self, bus, index, service):
+ Characteristic.__init__(
+ self, bus, index,
+ self.BATTERY_LVL_UUID,
+ ['read', 'notify'],
+ service)
+ self.notifying = False
+ self.battery_lvl = 100
+ gobject.timeout_add(5000, self.drain_battery)
+
+ def notify_battery_level(self):
+ if not self.notifying:
+ return
+ self.PropertiesChanged(
+ GATT_CHRC_IFACE,
+ { 'Value': [dbus.Byte(self.battery_lvl)] }, [])
+
+ def drain_battery(self):
+ if self.battery_lvl > 0:
+ self.battery_lvl -= 2
+ if self.battery_lvl < 0:
+ self.battery_lvl = 0
+ print 'Battery Level drained: ' + repr(self.battery_lvl)
+ self.notify_battery_level()
+ return True
+
+ def ReadValue(self):
+ print 'Battery Level read: ' + repr(self.battery_lvl)
+ return [dbus.Byte(self.battery_lvl)]
+
+ def StartNotify(self):
+ if self.notifying:
+ print 'Already notifying, nothing to do'
+ return
+
+ self.notifying = True
+ self.notify_battery_level()
+
+ def StopNotify(self):
+ if not self.notifying:
+ print 'Not notifying, nothing to do'
+ return
+
+ self.notifying = False
+
+
+class TestService(Service):
+ """
+ Dummy test service that provides characteristics and descriptors that
+ exercise various API functionality.
+
+ """
+ TEST_SVC_UUID = '12345678-1234-5678-1234-56789abcdef0'
+
+ def __init__(self, bus, index):
+ Service.__init__(self, bus, index, self.TEST_SVC_UUID, False)
+ self.add_characteristic(TestCharacteristic(bus, 0, self))
+
+
+class TestCharacteristic(Characteristic):
+ """
+ Dummy test characteristic. Allows writing arbitrary bytes to its value, and
+ contains "extended properties", as well as a test descriptor.
+
+ """
+ TEST_CHRC_UUID = '12345678-1234-5678-1234-56789abcdef1'
+
+ def __init__(self, bus, index, service):
+ Characteristic.__init__(
+ self, bus, index,
+ self.TEST_CHRC_UUID,
+ ['read', 'write', 'writable-auxiliaries'],
+ service)
+ self.value = []
+ self.add_descriptor(TestDescriptor(bus, 0, self))
+ self.add_descriptor(
+ CharacteristicUserDescriptionDescriptor(bus, 1, self))
+
+ def ReadValue(self):
+ print 'TestCharacteristic Read: ' + repr(self.value)
+ return self.value
+
+ def WriteValue(self, value):
+ print 'TestCharacteristic Write: ' + repr(value)
+ self.value = value
+
+
+class TestDescriptor(Descriptor):
+ """
+ Dummy test descriptor. Returns a static value.
+
+ """
+ TEST_DESC_UUID = '12345678-1234-5678-1234-56789abcdef2'
+
+ def __init__(self, bus, index, characteristic):
+ Descriptor.__init__(
+ self, bus, index,
+ self.TEST_DESC_UUID,
+ characteristic)
+
+ def ReadValue(self):
+ return [
+ dbus.Byte('T'), dbus.Byte('e'), dbus.Byte('s'), dbus.Byte('t')
+ ]
+
+
+class CharacteristicUserDescriptionDescriptor(Descriptor):
+ """
+ Writable CUD descriptor.
+
+ """
+ CUD_UUID = '2901'
+
+ def __init__(self, bus, index, characteristic):
+ self.writable = 'writable-auxiliaries' in characteristic.flags
+ self.value = array.array('B', 'This is a characteristic for testing')
+ self.value = self.value.tolist()
+ Descriptor.__init__(
+ self, bus, index,
+ self.CUD_UUID,
+ characteristic)
+
+ def ReadValue(self):
+ return self.value
+
+ def WriteValue(self, value):
+ if not self.writable:
+ raise NotPermittedException()
+ self.value = value
+
+
+def register_service_cb():
+ print 'GATT service registered'
+
+
+def register_service_error_cb(error):
+ print 'Failed to register service: ' + str(error)
+ mainloop.quit()
+
+
+def find_adapter(bus):
+ remote_om = dbus.Interface(bus.get_object(BLUEZ_SERVICE_NAME, '/'),
+ DBUS_OM_IFACE)
+ objects = remote_om.GetManagedObjects()
+
+ for o, props in objects.iteritems():
+ if props.has_key(GATT_MANAGER_IFACE):
+ return o
+
+ return None
+
+def main():
+ global mainloop
+
+ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+ bus = dbus.SystemBus()
+
+ adapter = find_adapter(bus)
+ if not adapter:
+ print 'GattManager1 interface not found'
+ return
+
+ service_manager = dbus.Interface(
+ bus.get_object(BLUEZ_SERVICE_NAME, adapter),
+ GATT_MANAGER_IFACE)
+
+ hr_service = HeartRateService(bus, 0)
+ bat_service = BatteryService(bus, 1)
+ test_service = TestService(bus, 2)
+
+ mainloop = gobject.MainLoop()
+
+ service_manager.RegisterService(hr_service.get_path(), {},
+ reply_handler=register_service_cb,
+ error_handler=register_service_error_cb)
+ service_manager.RegisterService(bat_service.get_path(), {},
+ reply_handler=register_service_cb,
+ error_handler=register_service_error_cb)
+ service_manager.RegisterService(test_service.get_path(), {},
+ reply_handler=register_service_cb,
+ error_handler=register_service_error_cb)
+
+ mainloop.run()
+
+if __name__ == '__main__':
+ main()
diff --git a/tools/hciattach.c b/tools/hciattach.c
index fad176c9..0ba56e7c 100755
--- a/tools/hciattach.c
+++ b/tools/hciattach.c
@@ -27,6 +27,9 @@
#include <config.h>
#endif
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
+#define _GNU_SOURCE
+#endif
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
@@ -49,6 +52,7 @@
#include "src/shared/tty.h"
#include "hciattach.h"
+#include "../profile.h"
struct uart_t {
char *type;
@@ -62,8 +66,26 @@ struct uart_t {
char *bdaddr;
int (*init) (int fd, struct uart_t *u, struct termios *ti);
int (*post) (int fd, struct uart_t *u, struct termios *ti);
+
+/* TIZEN_FEATURE_BLUEZ_MODIFY */
+#if defined TIZEN_FEATURE_BLUEZ_MODIFY && defined __TI_PATCH__
+ uint16_t device_param;
+#endif
};
+#if defined TIZEN_FEATURE_BLUEZ_MODIFY && defined __TI_PATCH__
+ int firmware_path = 0;
+#endif
+
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#if defined(__TI_PATCH__) || 1
+#define TIOSETBRFPOWER 0x6000
+#define BRF_DEEP_SLEEP_OPCODE_BYTE_1 0x0c
+#define BRF_DEEP_SLEEP_OPCODE_BYTE_2 0xfd
+#define BRF_DEEP_SLEEP_OPCODE \
+ (BRF_DEEP_SLEEP_OPCODE_BYTE_1 | (BRF_DEEP_SLEEP_OPCODE_BYTE_2 << 8))
+#endif
+#endif
#define FLOW_CTL 0x0001
#define AMP_DEV 0x0002
#define ENABLE_PM 1
@@ -946,40 +968,78 @@ static int bcm2035(int fd, struct uart_t *u, struct termios *ti)
/* Set the baud rate */
memset(cmd, 0, sizeof(cmd));
memset(resp, 0, sizeof(resp));
- cmd[0] = HCI_COMMAND_PKT;
- cmd[1] = 0x18;
- cmd[2] = 0xfc;
- cmd[3] = 0x02;
- switch (u->speed) {
- case 57600:
- cmd[4] = 0x00;
- cmd[5] = 0xe6;
- break;
- case 230400:
- cmd[4] = 0x22;
- cmd[5] = 0xfa;
- break;
- case 460800:
- cmd[4] = 0x22;
- cmd[5] = 0xfd;
- break;
- case 921600:
- cmd[4] = 0x55;
- cmd[5] = 0xff;
- break;
- default:
- /* Default is 115200 */
+
+/* TIZEN_FEATURE_BLUEZ_MODIFY */
+#if defined(TIZEN_FEATURE_BLUEZ_MODIFY)
+ if (TIZEN_FEATURE_BLUEZ_BRCM_CHIP) {
+ cmd[0] = HCI_COMMAND_PKT;
+ cmd[1] = 0x18;
+ cmd[2] = 0xfc;
+ cmd[3] = 0x02;
+ switch (u->speed) {
+ case 57600:
+ cmd[4] = 0x00;
+ cmd[5] = 0xe6;
+ break;
+ case 230400:
+ cmd[4] = 0x22;
+ cmd[5] = 0xfa;
+ break;
+ case 460800:
+ cmd[4] = 0x22;
+ cmd[5] = 0xfd;
+ break;
+ case 921600:
+ cmd[4] = 0x55;
+ cmd[5] = 0xff;
+ break;
+ default:
+ /* Default is 115200 */
+ cmd[4] = 0x00;
+ cmd[5] = 0xf3;
+ break;
+ }
+ fprintf(stderr, "Baud rate parameters: DHBR=0x%2x,DLBR=0x%2x\n",
+ cmd[4], cmd[5]);
+
+ /* Send command */
+ if (write(fd, cmd, 6) != 6) {
+ fprintf(stderr, "Failed to write \"set baud rate\" command\n");
+ return -1;
+ }
+ } else
+#endif
+ {
+ cmd[0] = HCI_COMMAND_PKT;
+ cmd[1] = 0x18;
+ cmd[2] = 0xfc;
+
+ switch (u->speed) {
+ case 57600:
+ case 230400:
+ case 460800:
+ case 921600:
+ case 3000000:
+ break;
+ default:
+ break;
+ }
+
+ cmd[3] = 0x06;
cmd[4] = 0x00;
- cmd[5] = 0xf3;
- break;
- }
- fprintf(stderr, "Baud rate parameters: DHBR=0x%2x,DLBR=0x%2x\n",
- cmd[4], cmd[5]);
+ cmd[5] = 0x00;
+ cmd[6] = u->speed & 0xFF;
+ cmd[7] = (u->speed >> 8) & 0xFF;
+ cmd[8] = (u->speed >> 16) & 0xFF;
+ cmd[9] = (u->speed >> 24) & 0xFF;
- /* Send command */
- if (write(fd, cmd, 6) != 6) {
- fprintf(stderr, "Failed to write \"set baud rate\" command\n");
- return -1;
+ fprintf(stderr, "Set the baud rate %d : 0x%02x,0x%02x,0x%02x,0x%02x\n",u->speed,cmd[6],cmd[7],cmd[8],cmd[9] );
+
+ /* Send command */
+ if (write(fd, cmd, 10) != 10) {
+ fprintf(stderr, "Failed to write \"set baud rate\" command\n");
+ return -1;
+ }
}
if ((n = read_hci_event(fd, resp, 6)) < 0) {
@@ -990,10 +1050,21 @@ static int bcm2035(int fd, struct uart_t *u, struct termios *ti)
return 0;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY /*SPRD add Start*/
+static int init_sprd_config(int fd, struct uart_t *u, struct termios *ti)
+{
+
+ return sprd_config_init(fd, u->bdaddr, ti);
+}
+#endif
+
struct uart_t uart[] = {
{ "any", 0x0000, 0x0000, HCI_UART_H4, 115200, 115200,
FLOW_CTL, DISABLE_PM, NULL, NULL },
-
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY /*SPRD*/
+ { "sprd", 0x0000, 0x0000, HCI_UART_H4, 3000000, 3000000,
+ FLOW_CTL, DISABLE_PM, NULL, init_sprd_config },
+#endif
{ "ericsson", 0x0000, 0x0000, HCI_UART_H4, 57600, 115200,
FLOW_CTL, DISABLE_PM, NULL, ericsson },
@@ -1019,12 +1090,18 @@ struct uart_t uart[] = {
{ "swave", 0x0000, 0x0000, HCI_UART_H4, 115200, 115200,
FLOW_CTL, DISABLE_PM, NULL, swave },
+/* TIZEN_FEATURE_BLUEZ_MODIFY */
+#if defined TIZEN_FEATURE_BLUEZ_MODIFY && defined __TI_PATCH__
+ /* Texas Instruments BRF63xx modules */
+ { "texas", 0x0000, 0x0000, HCI_UART_LL, 115200,3000000, FLOW_CTL, NULL, texas, NULL/*texas_continue_script*/, BRF_DEEP_SLEEP_OPCODE},
+#else
/* Texas Instruments Bluelink (BRF) modules */
{ "texas", 0x0000, 0x0000, HCI_UART_LL, 115200, 115200,
FLOW_CTL, DISABLE_PM, NULL, texas, texas2 },
{ "texasalt", 0x0000, 0x0000, HCI_UART_LL, 115200, 115200,
FLOW_CTL, DISABLE_PM, NULL, texasalt, NULL },
+#endif
/* ST Microelectronics minikits based on STLC2410/STLC2415 */
{ "st", 0x0000, 0x0000, HCI_UART_H4, 57600, 115200,
@@ -1132,6 +1209,46 @@ static struct uart_t * get_by_type(char *type)
return NULL;
}
+#if defined TIZEN_FEATURE_BLUEZ_MODIFY
+static int enable_hci(char *dev, struct uart_t *u)
+{
+ int fd, i;
+ unsigned long flags = 0;
+
+ fd = open(dev, O_RDWR | O_NOCTTY);
+ if (fd < 0) {
+ fprintf(stderr, "Can't open serial port");
+ return -1;
+ }
+
+ tcflush(fd, TCIOFLUSH);
+
+ /* Set TTY to N_HCI line discipline */
+ i = N_HCI;
+ if (ioctl(fd, TIOCSETD, &i) < 0) {
+ fprintf(stderr, "Can't set line discipline");
+ close(fd);
+ return -1;
+ }
+
+ if (flags && ioctl(fd, HCIUARTSETFLAGS, flags) < 0) {
+ fprintf(stderr, "Can't set UART flags");
+ close(fd);
+ return -1;
+ }
+
+ tcflush(fd, TCIOFLUSH);
+
+ if (ioctl(fd, HCIUARTSETPROTO, u->proto) < 0) {
+ fprintf(stderr, "Can't set device");
+ close(fd);
+ return -1;
+ }
+
+ return fd;
+}
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
+
/* Initialize UART driver */
static int init_uart(char *dev, struct uart_t *u, int send_break, int raw)
{
@@ -1139,6 +1256,13 @@ static int init_uart(char *dev, struct uart_t *u, int send_break, int raw)
int fd, i;
unsigned long flags = 0;
+/* TIZEN_FEATURE_BLUEZ_MODIFY */
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#if defined(__TI_PATCH__) || 1
+ int power;
+#endif
+#endif
+
if (raw)
flags |= 1 << HCI_UART_RAW_DEVICE;
@@ -1160,11 +1284,16 @@ static int init_uart(char *dev, struct uart_t *u, int send_break, int raw)
cfmakeraw(&ti);
- ti.c_cflag |= CLOCAL;
- if (u->flags & FLOW_CTL)
- ti.c_cflag |= CRTSCTS;
- else
- ti.c_cflag &= ~CRTSCTS;
+/* TIZEN_FEATURE_BLUEZ_MODIFY */
+#if defined(TIZEN_FEATURE_BLUEZ_MODIFY)
+ if (!TIZEN_FEATURE_BLUEZ_BRCM_CHIP) {
+ ti.c_cflag |= CLOCAL;
+ if (u->flags & FLOW_CTL)
+ ti.c_cflag |= CRTSCTS;
+ else
+ ti.c_cflag &= ~CRTSCTS;
+ }
+#endif
if (tcsetattr(fd, TCSANOW, &ti) < 0) {
perror("Can't set port settings");
@@ -1184,6 +1313,24 @@ static int init_uart(char *dev, struct uart_t *u, int send_break, int raw)
usleep(500000);
}
+/* TIZEN_FEATURE_BLUEZ_MODIFY */
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#if defined(__TI_PATCH__)
+ /* Power up the BRF chip */
+ power = 1;
+ ioctl(fd, TIOSETBRFPOWER, &power);
+#else
+ if (TIZEN_FEATURE_BLUEZ_BRCM_CHIP) {
+ /* Power up the BRF chip */
+ power = 1;
+ ioctl(fd, TIOSETBRFPOWER, &power);
+ }
+#endif
+#ifdef __TI_PATCH__
+ usleep(500000);
+#endif
+#endif
+
if (u->init && u->init(fd, u, &ti) < 0)
goto fail;
@@ -1212,8 +1359,10 @@ static int init_uart(char *dev, struct uart_t *u, int send_break, int raw)
goto fail;
}
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
if (u->post && u->post(fd, u, &ti) < 0)
goto fail;
+#endif
return fd;
@@ -1226,33 +1375,69 @@ static void usage(void)
{
printf("hciattach - HCI UART driver initialization utility\n");
printf("Usage:\n");
+
+/* TIZEN_FEATURE_BLUEZ_MODIFY */
+#if defined(TIZEN_FEATURE_BLUEZ_MODIFY) && !defined(__TI_PATCH__)
+/* This commented code was present before bluez 5.25 upgrade
+ * printf("\thciattach [-n] [-p] [-b] [-g device_param] [-r] [-f] [-t timeout] [-s initial_speed] <tty> <type | id> [speed] [flow|noflow] [bdaddr]\n");*/
+ printf("\thciattach [-n] [-p] [-b] [-g device_param] [-r] [-f]"
+ " [-t timeout] [-s initial_speed]"
+ " <tty> <type | id> [speed] [flow|noflow]"
+ " [sleep|nosleep] [bdaddr]\n");
+#else
printf("\thciattach [-n] [-p] [-b] [-r] [-t timeout] [-s initial_speed]"
" <tty> <type | id> [speed] [flow|noflow]"
" [sleep|nosleep] [bdaddr]\n");
+#endif
printf("\thciattach -l\n");
}
int main(int argc, char *argv[])
{
struct uart_t *u = NULL;
+#if defined(TIZEN_FEATURE_BLUEZ_MODIFY)
int detach, printpid, raw, opt, i, n, ld, err;
+#else
+ int detach, printpid, opt, i, n, ld, err;
+#endif
int to = 10;
int init_speed = 0;
+#if defined(TIZEN_FEATURE_BLUEZ_MODIFY)
int send_break = 0;
+#endif
pid_t pid;
struct sigaction sa;
struct pollfd p;
sigset_t sigs;
char dev[PATH_MAX];
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#if defined(__TI_PATCH__) || 1
+/* TIZEN_FEATURE_BLUEZ_MODIFY */
+ int power;
+#endif
+#endif
+#ifdef __TI_PATCH__
+ uint16_t device_param = 0;
+ int reset_device = 0;
+ int bt_fd;
+#endif
detach = 1;
printpid = 0;
+#if defined(TIZEN_FEATURE_BLUEZ_MODIFY)
raw = 0;
-
+#endif
+#if defined(TIZEN_FEATURE_BLUEZ_MODIFY) && !defined(__TI_PATCH__)
+ while ((opt=getopt(argc, argv, "bnprft:g:s:l")) != EOF) {
+#else
while ((opt=getopt(argc, argv, "bnpt:s:lr")) != EOF) {
+#endif
switch(opt) {
case 'b':
- send_break = 1;
+#if defined(TIZEN_FEATURE_BLUEZ_MODIFY)
+ if (!TIZEN_FEATURE_BLUEZ_BRCM_CHIP)
+ send_break = 1;
+#endif
break;
case 'n':
@@ -1267,6 +1452,20 @@ int main(int argc, char *argv[])
to = atoi(optarg);
break;
+/* TIZEN_FEATURE_BLUEZ_MODIFY */
+#if defined(TIZEN_FEATURE_BLUEZ_MODIFY) && defined(__TI_PATCH__)
+ case 'g':
+ device_param = (uint16_t)strtol(optarg, NULL, 16);
+ break;
+
+ case 'r':
+ reset_device = 1;
+ break;
+
+ case 'f':
+ firmware_path = 1;
+ break;
+#endif
case 's':
init_speed = atoi(optarg);
break;
@@ -1279,7 +1478,10 @@ int main(int argc, char *argv[])
exit(0);
case 'r':
- raw = 1;
+#if defined(TIZEN_FEATURE_BLUEZ_MODIFY)
+ if (!TIZEN_FEATURE_BLUEZ_BRCM_CHIP)
+ raw = 1;
+#endif
break;
default:
@@ -1289,6 +1491,10 @@ int main(int argc, char *argv[])
}
n = argc - optind;
+/* TIZEN_FEATURE_BLUEZ_MODIFY */
+#if defined(TIZEN_FEATURE_BLUEZ_MODIFY) && defined(__TI_PATCH__)
+ if (!reset_device || (reset_device && n < 1))
+#endif
if (n < 2) {
usage();
exit(1);
@@ -1352,16 +1558,42 @@ int main(int argc, char *argv[])
break;
}
}
+/* TIZEN_FEATURE_BLUEZ_MODIFY */
+#if defined(TIZEN_FEATURE_BLUEZ_MODIFY) && defined(__TI_PATCH__)
+ if (reset_device)
+ {
+ // Reset row device
+ bt_fd = open(dev, O_RDWR | O_NOCTTY);
+ if (bt_fd< 0) {
+ perror("Can't open serial port");
+ return -1;
+ }
+ /* Power up the BRF chip */
+ power = 0;
+ ioctl(bt_fd, TIOSETBRFPOWER, &power);
+ return 0;
+ }
+#endif
if (!u) {
fprintf(stderr, "Unknown device type or id\n");
exit(1);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+// __hci_attach_log_init();
+#endif
+
/* If user specified a initial speed, use that instead of
the hardware's default */
if (init_speed)
u->init_speed = init_speed;
+#if defined(TIZEN_FEATURE_BLUEZ_MODIFY) && defined(__TI_PATCH__)
+ /* If user specified a device parameter, use that instead of
+ the hardware's default */
+ if (device_param)
+ u->device_param = device_param;
+#endif
memset(&sa, 0, sizeof(sa));
sa.sa_flags = SA_NOCLDSTOP;
@@ -1371,8 +1603,14 @@ int main(int argc, char *argv[])
/* 10 seconds should be enough for initialization */
alarm(to);
bcsp_max_retries = to;
-
+#if defined TIZEN_FEATURE_BLUEZ_MODIFY
+ if (TIZEN_FEATURE_BLUEZ_BRCM_CHIP)
+ n = enable_hci(dev, u);
+ else
+ n = init_uart(dev, u, send_break, raw);
+#else
n = init_uart(dev, u, send_break, raw);
+#endif
if (n < 0) {
perror("Can't initialize device");
exit(1);
@@ -1433,5 +1671,19 @@ int main(int argc, char *argv[])
exit(1);
}
+/* TIZEN_FEATURE_BLUEZ_MODIFY */
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#if defined(__TI_PATCH__)
+ /* Power down the BRF or BCMchip */
+ power = 0;
+ ioctl(n, TIOSETBRFPOWER, &power);
+#else
+ if (TIZEN_FEATURE_BLUEZ_BRCM_CHIP) {
+ /* Power down the BRF or BCMchip */
+ power = 0;
+ ioctl(n, TIOSETBRFPOWER, &power);
+ }
+#endif
+#endif
return 0;
}
diff --git a/tools/hciattach.h b/tools/hciattach.h
index 249aab49..9c949651 100755
--- a/tools/hciattach.h
+++ b/tools/hciattach.h
@@ -64,6 +64,9 @@ int stlc2500_init(int fd, bdaddr_t *bdaddr);
int bgb2xx_init(int dd, bdaddr_t *bdaddr);
int ath3k_init(int fd, int speed, int init_speed, char *bdaddr,
struct termios *ti);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+int sprd_config_init(int fd, char *bdaddr, struct termios *ti);
+#endif
int ath3k_post(int fd, int pm);
int qualcomm_init(int fd, int speed, struct termios *ti, const char *bdaddr);
int intel_init(int fd, int init_speed, int *speed, struct termios *ti);
diff --git a/tools/hciattach_bcm43xx.c b/tools/hciattach_bcm43xx.c
index 81f38cbb..af035380 100755
--- a/tools/hciattach_bcm43xx.c
+++ b/tools/hciattach_bcm43xx.c
@@ -43,8 +43,12 @@
#include "hciattach.h"
#ifndef FIRMWARE_DIR
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define FIRMWARE_DIR "/usr/etc/bluetooth"
+#else
#define FIRMWARE_DIR "/etc/firmware"
#endif
+#endif
#define FW_EXT ".hcd"
diff --git a/tools/hciattach_sprd.c b/tools/hciattach_sprd.c
new file mode 100755
index 00000000..6fd2954d
--- /dev/null
+++ b/tools/hciattach_sprd.c
@@ -0,0 +1,592 @@
+#include <linux/kernel.h>
+#include <assert.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <syslog.h>
+#include <termios.h>
+#include <time.h>
+#include <sys/time.h>
+#include <sys/poll.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+
+#include <sys/types.h>
+#include <dirent.h>
+
+#include <bluetooth/bluetooth.h>
+#include <bluetooth/hci.h>
+#include <bluetooth/hci_lib.h>
+
+#include "hciattach.h"
+#include <sys/stat.h>
+
+#include "hciattach_sprd.h"
+
+//#include <android/log.h>
+//#define DBG
+#ifdef DBG
+#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, "pskey_bt", __VA_ARGS__)
+#else
+#define LOGD(fmt, arg...) fprintf(stderr, "%s:%d()" fmt "\n", __FILE__,__LINE__, ## arg)
+#endif
+typedef unsigned char UINT8;
+
+#define UINT32_TO_STREAM(p, u32) {*(p)++ = (UINT8)(u32); *(p)++ = (UINT8)((u32) >> 8); *(p)++ = (UINT8)((u32) >> 16); *(p)++ = (UINT8)((u32) >> 24);}
+#define UINT24_TO_STREAM(p, u24) {*(p)++ = (UINT8)(u24); *(p)++ = (UINT8)((u24) >> 8); *(p)++ = (UINT8)((u24) >> 16);}
+#define UINT16_TO_STREAM(p, u16) {*(p)++ = (UINT8)(u16); *(p)++ = (UINT8)((u16) >> 8);}
+#define UINT8_TO_STREAM(p, u8) {*(p)++ = (UINT8)(u8);}
+#define INT8_TO_STREAM(p, u8) {*(p)++ = (INT8)(u8);}
+
+#define PSKEY_PRELOAD_SIZE 0x04
+#define PSKEY_PREAMBLE_SIZE 0xA2
+
+ // for bt mac addr
+#define BT_MAC_FILE_PATH "/csa/bluetooth/"
+#define DATMISC_MAC_ADDR_PATH BT_MAC_FILE_PATH".bd_addr"
+#define MAC_ADDR_BUF_LEN (strlen("FF:FF:FF:FF:FF:FF"))
+#define MAC_ADDR_FILE_LEN 25
+#define MAC_ADDR_LEN 6
+
+#define BD_ADDR_LEN 14
+#define BD_PREFIX "0002\n"
+
+#if 0
+#ifndef VENDOR_BTWRITE_PROC_NODE
+#define VENDOR_BTWRITE_PROC_NODE "/proc/bluetooth/sleep/btwrite"
+#endif
+#endif
+
+#define MAX_BT_TMP_PSKEY_FILE_LEN 2048
+
+typedef unsigned int UWORD32;
+typedef unsigned short UWORD16;
+typedef unsigned char UWORD8;
+
+#define down_bt_is_space(c) (((c) == '\n') || ((c) == ',') || ((c) == '\r') || ((c) == ' ') || ((c) == '{') || ((c) == '}'))
+#define down_bt_is_comma(c) (((c) == ','))
+#define down_bt_is_endc(c) (((c) == '}')) // indicate end of data
+
+/* Macros to swap byte order */
+#define SWAP_BYTE_ORDER_WORD(val) ((((val) & 0x000000FF) << 24) + \
+ (((val) & 0x0000FF00) << 8) + \
+ (((val) & 0x00FF0000) >> 8) + \
+ (((val) & 0xFF000000) >> 24))
+#define INLINE static __inline
+
+#ifndef LITTLE_ENDIAN
+#define LITTLE_ENDIAN
+#endif
+
+
+// pskey file structure default value
+static BT_PSKEY_CONFIG_T bt_para_setting={
+ .pskey_cmd = 0x001C0101,
+
+ .g_dbg_source_sink_syn_test_data = 0,
+ .g_sys_sleep_in_standby_supported = 0,
+ .g_sys_sleep_master_supported = 0,
+ .g_sys_sleep_slave_supported = 0,
+
+ .default_ahb_clk = 26000000,
+ .device_class = 0x001F00,
+ .win_ext = 30,
+
+ .g_aGainValue = {0x0000F600, 0x0000D000, 0x0000AA00, 0x00008400, 0x00004400, 0x00000A00},
+ .g_aPowerValue = {0x0FC80000, 0x0FF80000, 0x0FDA0000, 0x0FCC0000, 0x0FFC0000},
+
+ .feature_set = {0xFF, 0xFF, 0x8D, 0xFE, 0x9B, 0x7F, 0x79, 0x83, 0xFF, 0xA7, 0xFF, 0x7F, 0x00, 0xE0, 0xF7, 0x3E},
+ .device_addr = {0x6A, 0x6B, 0x8C, 0x8A, 0x8B, 0x8C},
+
+ .g_sys_sco_transmit_mode = 0, //true tramsmit by uart, otherwise by share memory
+ .g_sys_uart0_communication_supported = 1, //true use uart0, otherwise use uart1 for debug
+ .edr_tx_edr_delay = 5,
+ .edr_rx_edr_delay = 14,
+
+ .g_wbs_nv_117 = 0x0031,
+
+ .is_wdg_supported = 0,
+
+ .share_memo_rx_base_addr = 0,
+ //.share_memo_tx_base_addr = 0,
+ .g_wbs_nv_118 = 0x0066,
+ .g_nbv_nv_117 = 0x1063,
+
+ .share_memo_tx_packet_num_addr = 0,
+ .share_memo_tx_data_base_addr = 0,
+
+ .g_PrintLevel = 0xFFFFFFFF,
+
+ .share_memo_tx_block_length = 0,
+ .share_memo_rx_block_length = 0,
+ .share_memo_tx_water_mark = 0,
+ //.share_memo_tx_timeout_value = 0,
+ .g_nbv_nv_118 = 0x0E45,
+
+ .uart_rx_watermark = 48,
+ .uart_flow_control_thld = 63,
+ .comp_id = 0,
+ .pcm_clk_divd = 0x26,
+
+
+ .reserved = {0}
+};
+
+extern int getPskeyFromFile(void *pData);
+extern int bt_getPskeyFromFile(void *pData);
+
+static int create_mac_folder(void)
+{
+ DIR *dp;
+ int err;
+
+ dp = opendir(BT_MAC_FILE_PATH);
+ if (dp == NULL) {
+ if (mkdir(BT_MAC_FILE_PATH, 0755) < 0) {
+ err = -errno;
+ LOGD("%s: mkdir: %s(%d)",__FUNCTION__, strerror(-err), -err);
+ }
+ return -1;
+ }
+
+ closedir(dp);
+ return 0;
+}
+
+static void mac_rand(char *btmac)
+{
+ int ran;
+ int i;
+ unsigned int seed;
+ struct timeval tv;
+
+ memcpy(btmac, BD_PREFIX, 5);
+ i = gettimeofday(&tv, NULL);
+
+ if (i < 0) {
+ LOGD("Fail to call gettimeofday()");
+ seed = time(NULL);
+ } else
+ seed = (unsigned int)tv.tv_usec;
+
+ for (i = 5; i < BD_ADDR_LEN; i++) {
+ if (i == 7) {
+ btmac[i] = '\n';
+ continue;
+ }
+ ran = rand_r(&seed) % 16;
+ if (ran < 10)
+ ran += 0x30;
+ else
+ ran += 0x57;
+ btmac[i] = ran;
+ }
+ LOGD("Random number is\r\n");
+ for (i = 0; i < BD_ADDR_LEN; i++) {
+ LOGD("%c", btmac[i]);
+ }
+ LOGD("\r\n");
+}
+
+static void write_btmac2file(char *btmac)
+{
+ int fd;
+ int ret;
+ fd = open(DATMISC_MAC_ADDR_PATH, O_CREAT|O_RDWR|O_TRUNC, S_IRUSR|S_IWUSR);
+ LOGD("write_btmac2file open file, fd=%d", fd);
+ if(fd >= 0) {
+ if(chmod(DATMISC_MAC_ADDR_PATH,0666) != -1){
+ ret = write(fd, btmac, strlen(btmac));
+ if (ret < strlen(btmac)) {
+ LOGD("Fail to write %s", DATMISC_MAC_ADDR_PATH);
+ close(fd);
+ return;
+ }
+ }
+ close(fd);
+ }else{
+ LOGD("write bt mac to file failed!!");
+ }
+}
+
+uint8 ConvertHexToBin(
+ uint8 *hex_ptr, // in: the hexadecimal format string
+ uint16 length, // in: the length of hexadecimal string
+ uint8 *bin_ptr // out: pointer to the binary format string
+ ){
+ uint8 *dest_ptr = bin_ptr;
+ uint32 i = 0;
+ uint8 ch;
+
+ for(i=0; i<length; i+=2){
+ // the bit 8,7,6,5
+ ch = hex_ptr[i];
+ // digital 0 - 9
+ if (ch >= '0' && ch <= '9')
+ *dest_ptr =(uint8)((ch - '0') << 4);
+ // a - f
+ else if (ch >= 'a' && ch <= 'f')
+ *dest_ptr = (uint8)((ch - 'a' + 10) << 4);
+ // A - F
+ else if (ch >= 'A' && ch <= 'F')
+ *dest_ptr = (uint8)((ch -'A' + 10) << 4);
+ else{
+ return 0;
+ }
+
+ // the bit 1,2,3,4
+ ch = hex_ptr[i+1];
+ // digtial 0 - 9
+ if (ch >= '0' && ch <= '9')
+ *dest_ptr |= (uint8)(ch - '0');
+ // a - f
+ else if (ch >= 'a' && ch <= 'f')
+ *dest_ptr |= (uint8)(ch - 'a' + 10);
+ // A - F
+ else if (ch >= 'A' && ch <= 'F')
+ *dest_ptr |= (uint8)(ch -'A' + 10);
+ else{
+ return 0;
+ }
+
+ dest_ptr++;
+ }
+
+ return 1;
+}
+
+static int read_mac_address(char *file_name, uint8 *addr) {
+ char buf[MAC_ADDR_FILE_LEN] = {0};
+ uint32 addr_t[MAC_ADDR_LEN] = {0};
+ int i = 0;
+
+
+#if 1
+ int fd = open(file_name, O_RDONLY, 0666);
+ LOGD("%s read file: %s", __func__, file_name);
+ if (fd < 0) {
+ LOGD("%s open %s error reason: %s", __func__, file_name, strerror(errno));
+ return -1;
+ }
+ if (read(fd, buf, BD_ADDR_LEN) < 0) {
+ LOGD("%s read %s error reason: %s", __func__, file_name, strerror(errno));
+ goto done;
+ }
+ if (sscanf(buf, "%02X%02X\n%02X\n%02X%02X%02X", &addr_t[0], &addr_t[1], &addr_t[2], &addr_t[3], &addr_t[4], &addr_t[5]) < 0) {
+ LOGD("%s sscanf %s error reason: %s", __func__, file_name, strerror(errno));
+ goto done;
+ }
+
+ for (i = 0; i < MAC_ADDR_LEN; i++) {
+ addr[i] = addr_t[i] & 0xFF;
+ }
+ LOGD("%s %s addr: [%02X:%02X:%02X:%02X:%02X:%02X]", __func__, file_name, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
+
+done:
+ close(fd);
+#endif
+ return 0;
+}
+
+static void mac_address_stream_compose(uint8 *addr) {
+ uint8 tmp, i, j;
+ for (i = 0, j = MAC_ADDR_LEN - 1; (i < MAC_ADDR_LEN / 2) && (i != j); i++, j--) {
+ tmp = addr[i];
+ addr[i] = addr[j];
+ addr[j] = tmp;
+ }
+}
+
+#if 0
+/*
+ * random bluetooth mac address
+ */
+static void random_mac_addr(uint8 *addr) {
+ int fd, randseed, ret, mac_rd;
+ uint8 addr_t[MAC_ADDR_LEN] = {0};
+
+ LOGD("%s", __func__);
+ /* urandom seed build */
+ fd = open("/dev/urandom", O_RDONLY);
+ if (fd < 0){
+ LOGD("%s: open urandom fail", __func__);
+ } else {
+ ret = read(fd, &randseed, sizeof(randseed));
+ LOGD("%s urandom:0x%08X", __func__, randseed);
+ close(fd);
+ }
+
+ /* time seed build */
+ if (fd < 0 || ret < 0) {
+ struct timeval tt;
+ if (gettimeofday(&tt, (struct timezone *)0) > 0) {
+ randseed = (unsigned int) tt.tv_usec;
+ } else {
+ randseed = (unsigned int) time(NULL);
+ }
+ LOGD("urandom fail, using system time for randseed");
+ }
+
+ LOGD("%s: randseed = %u",__func__, randseed);
+ srand(randseed);
+ mac_rd = rand();
+
+ addr_t[0] = 0x40; /* FOR */
+ addr_t[1] = 0x45; /* SPRD */
+ addr_t[2] = 0xDA; /* ADDR */
+ addr_t[3] = (uint8)(mac_rd & 0xFF);
+ addr_t[4] = (uint8)((mac_rd >> 8) & 0xFF);
+ addr_t[5] = (uint8)((mac_rd >> 16) & 0xFF);
+
+ memcpy(addr, addr_t, MAC_ADDR_LEN);
+ LOGD("%s: MAC ADDR: [%02X:%02X:%02X:%02X:%02X:%02X]",__func__, addr_t[0], addr_t[1], addr_t[2], addr_t[3], addr_t[4], addr_t[5]);
+}
+#endif
+static void get_mac_address(uint8 *addr){
+ int ret = -1;
+ uint8 addr_t[6] = {0};
+ char bt_mac[BD_ADDR_LEN] = {0, };
+
+ LOGD("%s", __func__);
+ /* check misc mac file exist */
+ ret = access(DATMISC_MAC_ADDR_PATH, F_OK);
+ if (ret != 0) {
+ LOGD("%s %s miss", __func__, DATMISC_MAC_ADDR_PATH);
+
+ /* Try to make bt address file */
+ create_mac_folder();
+
+ mac_rand(bt_mac);
+ LOGD("bt random mac=%s",bt_mac);
+ write_btmac2file(bt_mac);
+
+ }
+
+ /* read mac file */
+ read_mac_address(DATMISC_MAC_ADDR_PATH, addr_t);
+
+ /* compose mac stream */
+ mac_address_stream_compose(addr_t);
+
+ memcpy(addr, addr_t, MAC_ADDR_LEN);
+
+}
+
+
+/*
+ * hci command preload stream, special order
+ */
+static void pskey_stream_compose(uint8 * buf, BT_PSKEY_CONFIG_T *bt_par) {
+ int i = 0;
+ uint8 *p = buf;
+
+ LOGD("%s", __func__);
+
+ UINT24_TO_STREAM(p, bt_par->pskey_cmd);
+ UINT8_TO_STREAM(p, (uint8)(PSKEY_PREAMBLE_SIZE & 0xFF));
+
+ UINT8_TO_STREAM(p, bt_par->g_dbg_source_sink_syn_test_data);
+ UINT8_TO_STREAM(p, bt_par->g_sys_sleep_in_standby_supported);
+ UINT8_TO_STREAM(p, bt_par->g_sys_sleep_master_supported);
+ UINT8_TO_STREAM(p, bt_par->g_sys_sleep_slave_supported);
+
+ UINT32_TO_STREAM(p, bt_par->default_ahb_clk);
+ UINT32_TO_STREAM(p, bt_par->device_class);
+ UINT32_TO_STREAM(p, bt_par->win_ext);
+
+ for (i = 0; i < 6; i++) {
+ UINT32_TO_STREAM(p, bt_par->g_aGainValue[i]);
+ }
+ for (i = 0; i < 5; i++) {
+ UINT32_TO_STREAM(p, bt_par->g_aPowerValue[i]);
+ }
+
+ for (i = 0; i < 16; i++) {
+ UINT8_TO_STREAM(p, bt_par->feature_set[i]);
+ }
+ for (i = 0; i < 6; i++) {
+ UINT8_TO_STREAM(p, bt_par->device_addr[i]);
+ }
+
+ UINT8_TO_STREAM(p, bt_par->g_sys_sco_transmit_mode);
+ UINT8_TO_STREAM(p, bt_par->g_sys_uart0_communication_supported);
+ UINT8_TO_STREAM(p, bt_par->edr_tx_edr_delay);
+ UINT8_TO_STREAM(p, bt_par->edr_rx_edr_delay);
+
+ UINT16_TO_STREAM(p, bt_par->g_wbs_nv_117);
+
+ UINT32_TO_STREAM(p, bt_par->is_wdg_supported);
+
+ UINT32_TO_STREAM(p, bt_par->share_memo_rx_base_addr);
+ //UINT32_TO_STREAM(p, bt_par->share_memo_tx_base_addr);
+ UINT16_TO_STREAM(p, bt_par->g_wbs_nv_118);
+ UINT16_TO_STREAM(p, bt_par->g_nbv_nv_117);
+
+ UINT32_TO_STREAM(p, bt_par->share_memo_tx_packet_num_addr);
+ UINT32_TO_STREAM(p, bt_par->share_memo_tx_data_base_addr);
+
+ UINT32_TO_STREAM(p, bt_par->g_PrintLevel);
+
+ UINT16_TO_STREAM(p, bt_par->share_memo_tx_block_length);
+ UINT16_TO_STREAM(p, bt_par->share_memo_rx_block_length);
+ UINT16_TO_STREAM(p, bt_par->share_memo_tx_water_mark);
+ //UINT16_TO_STREAM(p, bt_par->share_memo_tx_timeout_value);
+ UINT16_TO_STREAM(p, bt_par->g_nbv_nv_118);
+
+ UINT16_TO_STREAM(p, bt_par->uart_rx_watermark);
+ UINT16_TO_STREAM(p, bt_par->uart_flow_control_thld);
+ UINT32_TO_STREAM(p, bt_par->comp_id);
+ UINT16_TO_STREAM(p, bt_par->pcm_clk_divd);
+
+
+ for (i = 0; i < 8; i++) {
+ UINT32_TO_STREAM(p, bt_par->reserved[i]);
+ }
+}
+
+void sprd_get_pskey(BT_PSKEY_CONFIG_T * pskey_t) {
+ BT_PSKEY_CONFIG_T pskey;
+ uint8 buf[180] = {0};
+
+ LOGD("%s", __func__);
+ memset(&pskey, 0 , sizeof(BT_PSKEY_CONFIG_T));
+ if (bt_getPskeyFromFile(&pskey) < 0 ) {
+ LOGD("%s bt_getPskeyFromFile failed", __func__);
+ memcpy(pskey_t, &bt_para_setting, sizeof(BT_PSKEY_CONFIG_T));
+ return;
+ }
+
+ memset(buf, 0, PSKEY_PRELOAD_SIZE + PSKEY_PREAMBLE_SIZE);
+
+ /* get bluetooth mac address */
+ get_mac_address(pskey.device_addr);
+
+ /* compose pskey hci command pkt */
+ pskey_stream_compose(buf, &pskey);
+
+ memcpy(pskey_t, &pskey, sizeof(BT_PSKEY_CONFIG_T));
+}
+
+#define HCI_HDR_LEN 3
+
+int sprd_config_init(int fd, char *bdaddr, struct termios *ti)
+{
+ int ret = 0,r=0;
+ unsigned char resp[30] = {0};
+ BT_PSKEY_CONFIG_T bt_para_tmp;
+ uint8 data_tmp[30] = {'a'};
+ static int index = 0;
+ uint8 *buf = NULL;
+ uint8 hci_len = 0;
+ uint8 is_expected_hci_evt = 0;
+#if 0
+ char buffer;
+ int btsleep_fd_sprd = -1;
+#endif
+ LOGD("sprd_config_init");
+
+#if 0
+ uart_fd = open(UART_INFO_PATH, O_WRONLY);
+ if(uart_fd > 0)
+ {
+ buffer = '2';
+ if (write(uart_fd, &buffer, 1) < 0)
+ {
+ LOGD("%s write(%s) failed: %s (%d) 2", __func__,
+ UART_INFO_PATH, strerror(errno),errno);
+ }
+
+ close(uart_fd);
+ }
+#endif
+
+#if 0
+ btsleep_fd_sprd = open(VENDOR_BTWRITE_PROC_NODE, O_WRONLY);
+ if (btsleep_fd_sprd < 0)
+ {
+ LOGD("%s open(%s) for write failed: %s (%d)", __func__,
+ VENDOR_BTWRITE_PROC_NODE, strerror(errno), errno);
+ }
+ else
+ {
+ buffer = '1';
+ if (write(btsleep_fd_sprd, &buffer, 1) < 0)
+ {
+ LOGD("%s write(%s) failed: %s (%d)", __func__,
+ VENDOR_BTWRITE_PROC_NODE, strerror(errno),errno);
+ }
+ }
+#endif
+
+ ret = bt_getPskeyFromFile(&bt_para_tmp);
+ if (ret < 0) {
+ LOGD("init_sprd_config bt_getPskeyFromFile failed\n");
+ memcpy(&bt_para_tmp, &bt_para_setting, sizeof(BT_PSKEY_CONFIG_T));
+ }
+
+ buf = (uint8 *)malloc(PSKEY_PRELOAD_SIZE + PSKEY_PREAMBLE_SIZE);
+ if (buf == NULL) {
+ LOGD("%s alloc stream memory failed", __func__);
+ return -1;
+ }
+ memset(buf, 0, PSKEY_PRELOAD_SIZE + PSKEY_PREAMBLE_SIZE);
+
+ /* get bluetooth mac address */
+ get_mac_address(bt_para_tmp.device_addr);
+
+ /* compose pskey hci command pkt */
+ pskey_stream_compose(buf, &bt_para_tmp);
+
+ ret = write(fd, buf, PSKEY_PRELOAD_SIZE + PSKEY_PREAMBLE_SIZE);
+ LOGD("write pskey ret = %d", ret);
+
+ free(buf);
+ buf = NULL;
+
+ if (ret < 0) {
+ LOGD("%s write pskey stream failed", __func__);
+ return -1;
+ }
+
+ memset(data_tmp, 0xff, sizeof(data_tmp));
+ while (1) {
+ r = read(fd, resp, 1);
+
+ if (r <= 0)
+ return -1;
+ else{
+ data_tmp[index] = resp[0];
+ LOGD("recive from controller 0x%x", data_tmp[index]);
+ ++index;
+ }
+
+ if (index >= 6) {
+ hci_len = data_tmp[2]+HCI_HDR_LEN;
+
+ if ((data_tmp[0] == 0x04) && (data_tmp[1] == 0xe) &&
+ (data_tmp[2] == 0xa) &&(data_tmp[3] == 0x1) &&
+ (data_tmp[4] == 0xa0) &&(data_tmp[5] == 0xfc)) {
+ LOGD("read response ok \n");
+ is_expected_hci_evt = 1;
+ } else {
+ LOGD("this is not what we expect HCI evt\n");
+ is_expected_hci_evt = 0;
+ }
+
+ if (index == hci_len) {
+ index = 0;
+ memset(data_tmp, 0x0, sizeof(data_tmp));
+
+ if(is_expected_hci_evt)
+ break;
+ }
+ }
+ }
+ return 0;
+}
diff --git a/tools/hciattach_sprd.h b/tools/hciattach_sprd.h
new file mode 100755
index 00000000..6e59408d
--- /dev/null
+++ b/tools/hciattach_sprd.h
@@ -0,0 +1,82 @@
+#ifndef HCIATTACH_SPRD_H__
+#define HCIATTACH_SPRD_H__
+
+#define MAC_ERROR "FF:FF:FF:FF:FF:FF"
+
+#define BT_MAC_FILE "/productinfo/btmac.txt"
+//#define GET_BTMAC_ATCMD "AT+SNVM=0,401"
+//#define GET_BTPSKEY_ATCMD "AT+SNVM=0,415"
+//#define SET_BTMAC_ATCMD "AT+SNVM=1,401"
+#define BT_RAND_MAC_LENGTH 17
+
+// used to store BT pskey structure and default values
+#define BT_PSKEY_STRUCT_FILE "/system/lib/modules/pskey_bt.txt"
+//#define BT_PSKEY_FILE "/system/lib/modules/pskey_bt.txt"
+
+
+typedef unsigned char uint8;
+typedef unsigned int uint32;
+typedef unsigned short uint16;
+
+#define BT_ADDRESS_SIZE 6
+
+
+typedef struct SPRD_BT_PSKEY_INFO_T{
+ uint32 pskey_cmd;
+
+ uint8 g_dbg_source_sink_syn_test_data;
+ uint8 g_sys_sleep_in_standby_supported;
+ uint8 g_sys_sleep_master_supported;
+ uint8 g_sys_sleep_slave_supported;
+
+ uint32 default_ahb_clk;
+ uint32 device_class;
+ uint32 win_ext;
+
+ uint32 g_aGainValue[6];
+ uint32 g_aPowerValue[5];
+
+ uint8 feature_set[16];
+ uint8 device_addr[6];
+
+ uint8 g_sys_sco_transmit_mode; //true tramsmit by uart, otherwise by share memory
+ uint8 g_sys_uart0_communication_supported; //true use uart0, otherwise use uart1 for debug
+ uint8 edr_tx_edr_delay;
+ uint8 edr_rx_edr_delay;
+
+ uint16 g_wbs_nv_117;
+
+ uint32 is_wdg_supported;
+
+ uint32 share_memo_rx_base_addr;
+
+ // uint32 share_memo_tx_base_addr;
+ uint16 g_wbs_nv_118;
+ uint16 g_nbv_nv_117;
+
+ uint32 share_memo_tx_packet_num_addr;
+ uint32 share_memo_tx_data_base_addr;
+
+ uint32 g_PrintLevel;
+
+ uint16 share_memo_tx_block_length;
+ uint16 share_memo_rx_block_length;
+ uint16 share_memo_tx_water_mark;
+
+ //uint16 share_memo_tx_timeout_value;
+ uint16 g_nbv_nv_118;
+
+ uint16 uart_rx_watermark;
+ uint16 uart_flow_control_thld;
+ uint32 comp_id;
+ uint16 pcm_clk_divd;
+
+ uint32 reserved[8];
+}BT_PSKEY_CONFIG_T;
+
+
+#endif /* HCIATTACH_SPRD_H__ */
+
+
+
+
diff --git a/tools/hciattach_ti.c b/tools/hciattach_ti.c
index 828dd61b..b9626773 100755
--- a/tools/hciattach_ti.c
+++ b/tools/hciattach_ti.c
@@ -55,7 +55,13 @@
#define TI_MANUFACTURER_ID 13
+/* TIZEN_FEATURE_BLUEZ_MODIFY */
+#ifdef __TI_PATCH__
+#define FIRMWARE_DIRECTORY1 "/mnt/mmc/"
+#define FIRMWARE_DIRECTORY2 "/usr/etc/bluetooth/"
+#else
#define FIRMWARE_DIRECTORY "/lib/firmware/ti-connectivity/"
+#endif
#define ACTION_SEND_COMMAND 1
#define ACTION_WAIT_EVENT 2
@@ -197,7 +203,24 @@ static const char *get_firmware_name(const uint8_t *respond)
if (version & 0x8000)
maj_ver |= 0x0008;
+/* TIZEN_FEATURE_BLUEZ_MODIFY */
+#ifdef __TI_PATCH__
+ FILE *fp;
+ sprintf(firmware_file_name, FIRMWARE_DIRECTORY1 "TIInit_%d.%d.%d.bts", chip, maj_ver, min_ver);
+
+ if ((fp = fopen(firmware_file_name, "r")) == NULL ) {
+ extern int firmware_path;
+ if (firmware_path)
+ sprintf(firmware_file_name, FIRMWARE_DIRECTORY2 "TIInit_edutm_%d.%d.%d.bts", chip, maj_ver, min_ver);
+ else
+ sprintf(firmware_file_name, FIRMWARE_DIRECTORY2 "TIInit_%d.%d.%d.bts", chip, maj_ver, min_ver);
+ }
+ else {
+ fclose(fp);
+ }
+#else
sprintf(firmware_file_name, FIRMWARE_DIRECTORY "TIInit_%d.%d.%d.bts", chip, maj_ver, min_ver);
+#endif
return firmware_file_name;
}
diff --git a/tools/hciconfig.c b/tools/hciconfig.c
index 8a97cc4d..b4a3856f 100755
--- a/tools/hciconfig.c
+++ b/tools/hciconfig.c
@@ -93,7 +93,14 @@ static void print_pkt_type(struct hci_dev_info *di)
static void print_link_policy(struct hci_dev_info *di)
{
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ char *str;
+ str = hci_lptostr(di->link_policy);
+ printf("\tLink policy: %s\n", str);
+ bt_free(str);
+#else
printf("\tLink policy: %s\n", hci_lptostr(di->link_policy));
+#endif
}
static void print_link_mode(struct hci_dev_info *di)
diff --git a/tools/hcidump.c b/tools/hcidump.c
index af8f5925..b460daa3 100755
--- a/tools/hcidump.c
+++ b/tools/hcidump.c
@@ -59,10 +59,27 @@ enum {
/* Default options */
static int snap_len = SNAP_LEN;
static int mode = PARSE;
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
static char *dump_file = NULL;
+#endif
static char *pppdump_file = NULL;
static char *audio_file = NULL;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define DUMP_MAX_SIZE 10000000 /* 10MB */
+#define DUMP_MAX_COUNT 2
+#define NAME_MAX 255
+
+struct dump_file {
+ char *filename;
+ int postfix_width;
+ unsigned int max_size;
+ int max_count;
+};
+
+struct dump_file df = {NULL, 1, DUMP_MAX_SIZE, DUMP_MAX_COUNT};
+#endif
+
struct hcidump_hdr {
uint16_t len;
uint8_t in;
@@ -101,6 +118,10 @@ struct pktlog_hdr {
} __attribute__ ((packed));
#define PKTLOG_HDR_SIZE (sizeof(struct pktlog_hdr))
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static int open_new_dumpfile(unsigned long flags);
+#endif
+
static inline int read_n(int fd, char *buf, int len)
{
int t = 0, w;
@@ -148,6 +169,9 @@ static int process_frames(int dev, int sock, int fd, unsigned long flags)
char *buf;
char ctrl[100];
int len, hdr_size = HCIDUMP_HDR_SIZE;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ int written = 0;
+#endif
if (sock < 0)
return -1;
@@ -261,10 +285,28 @@ static int process_frames(int dev, int sock, int fd, unsigned long flags)
dh->ts_usec = htobl(frm.ts.tv_usec);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (mode == WRITE && df.max_size != 0 &&
+ written + frm.data_len + hdr_size > df.max_size) {
+ close(fd);
+ fd = open_new_dumpfile(flags);
+ if (fd < 0)
+ return -1;
+ written = 0;
+ }
+
+ len = write_n(fd, buf, frm.data_len + hdr_size);
+ if (len < 0) {
+ perror("Write error");
+ return -1;
+ }
+ written += len;
+#else
if (write_n(fd, buf, frm.data_len + hdr_size) < 0) {
perror("Write error");
return -1;
}
+#endif
break;
default:
@@ -399,8 +441,10 @@ static void read_dump(int fd)
if (err < 0)
goto failed;
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
if (!err)
goto done;
+#endif
frm.ptr = frm.data;
frm.len = frm.data_len;
@@ -516,6 +560,38 @@ static int open_file(char *file, int mode, unsigned long flags)
return fd;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static int open_new_dumpfile(unsigned long flags)
+{
+ char filename[NAME_MAX + 1];
+ char new_filename[NAME_MAX + 1];
+ int i;
+
+ if (df.max_count <= 1)
+ return open_file(df.filename, WRITE, flags);
+
+ for (i = df.max_count - 2; i >= 0; i--) {
+ if (i == 0) {
+ snprintf(filename, sizeof(filename), "%s", df.filename);
+ } else {
+ snprintf(filename, sizeof(filename), "%s.%0*d",
+ df.filename, df.postfix_width, i);
+ }
+
+ if (access(filename, F_OK) < 0)
+ continue;
+
+ snprintf(new_filename, sizeof(new_filename), "%s.%0*d",
+ df.filename, df.postfix_width, i + 1);
+
+ if (rename(filename, new_filename) < 0)
+ perror("rename failed");
+ }
+
+ return open_file(df.filename, WRITE, flags);
+}
+#endif
+
static int open_socket(int dev, unsigned long flags)
{
struct sockaddr_hci addr;
@@ -635,6 +711,10 @@ static void usage(void)
" -D, --pppdump=file Extract PPP traffic\n"
" -A, --audio=file Extract SCO audio data\n"
" -Y, --novendor No vendor commands or events\n"
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ " -s --dump-size=size Maximum save-dump's file size. The unit is million bytes. Use this with -w. (default: 1,000,000 bytes)\n"
+ " -c --dump-count=count Specified count's dump files will be generated at most. Use this with -w. (default: 4)\n"
+#endif
" -h, --help Give this help list\n"
" -v, --version Give version information\n"
" --usage Give a short usage message\n"
@@ -661,6 +741,10 @@ static struct option main_options[] = {
{ "pppdump", 1, 0, 'D' },
{ "audio", 1, 0, 'A' },
{ "novendor", 0, 0, 'Y' },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ { "dump-size", 1, 0, 's' },
+ { "dump-count", 1, 0, 'c' },
+#endif
{ "help", 0, 0, 'h' },
{ "version", 0, 0, 'v' },
{ 0 }
@@ -676,9 +760,15 @@ int main(int argc, char *argv[])
int opt, pppdump_fd = -1, audio_fd = -1;
uint16_t obex_port;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ while ((opt = getopt_long(argc, argv,
+ "i:l:p:m:w:r:taxXRC:H:O:P:S:D:A:Ys:c:hv",
+ main_options, NULL)) != -1) {
+#else
while ((opt = getopt_long(argc, argv,
"i:l:p:m:w:r:taxXRC:H:O:P:S:D:A:Yhv",
main_options, NULL)) != -1) {
+#endif
switch(opt) {
case 'i':
if (strcasecmp(optarg, "none") && strcasecmp(optarg, "system"))
@@ -701,12 +791,20 @@ int main(int argc, char *argv[])
case 'w':
mode = WRITE;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ df.filename = strdup(optarg);
+#else
dump_file = strdup(optarg);
+#endif
break;
case 'r':
mode = READ;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ df.filename = strdup(optarg);
+#else
dump_file = strdup(optarg);
+#endif
break;
case 't':
@@ -765,6 +863,21 @@ int main(int argc, char *argv[])
flags |= DUMP_NOVENDOR;
break;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ case 's':
+ df.max_size = atoi(optarg) * 1000000;
+ break;
+
+ case 'c':
+ {
+ int i;
+ df.max_count = atoi(optarg);
+ for (i = df.max_count / 10; i; i /= 10)
+ df.postfix_width++;
+ break;
+ }
+#endif
+
case 'v':
printf("%s\n", VERSION);
exit(0);
@@ -807,13 +920,24 @@ int main(int argc, char *argv[])
flags |= DUMP_VERBOSE;
init_parser(flags, filter, defpsm, defcompid,
pppdump_fd, audio_fd);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ read_dump(open_file(df.filename, mode, flags));
+#else
read_dump(open_file(dump_file, mode, flags));
+#endif
break;
case WRITE:
flags |= DUMP_BTSNOOP;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ printf("Maximum size of one file : %u, Rotated file count : %d",
+ df.max_size, df.max_count);
+ process_frames(device, open_socket(device, flags),
+ open_file(df.filename, mode, flags), flags);
+#else
process_frames(device, open_socket(device, flags),
open_file(dump_file, mode, flags), flags);
+#endif
break;
}
diff --git a/tools/hcitool.c b/tools/hcitool.c
index 02c4ebe1..0b6f4991 100755
--- a/tools/hcitool.c
+++ b/tools/hcitool.c
@@ -1098,6 +1098,66 @@ static void cmd_epinq(int dev_id, int argc, char **argv)
hci_close_dev(dd);
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+/* Send arbitrary ACL data */
+static struct option data_options[] = {
+ { "help", 0, 0, 'h' },
+ { 0, 0, 0, 0 }
+};
+
+static const char *data_help =
+ "Usage:\n"
+ "\tcmd <handle> <data>\n"
+ "Example:\n"
+ "\tcmd 0x0064 0x41 0x42 0x43 0x44\n";
+
+static void cmd_data(int dev_id, int argc, char **argv)
+{
+ unsigned char buf[HCI_MAX_ACL_SIZE], *ptr = buf;
+ struct hci_filter flt;
+ int i, opt, len, dd;
+ uint16_t handle;
+
+ for_each_opt(opt, data_options, NULL) {
+ switch (opt) {
+ default:
+ printf("%s", data_help);
+ return;
+ }
+ }
+ helper_arg(2, -1, &argc, &argv, data_help);
+
+ if (dev_id < 0)
+ dev_id = hci_get_route(NULL);
+
+ handle = atoi(argv[0]);
+
+ for (i = 1, len = 0; i < argc && len < (int) sizeof(buf); i++, len++)
+ *ptr++ = (uint8_t) strtol(argv[i], NULL, 16);
+
+ dd = hci_open_dev(dev_id);
+ if (dd < 0) {
+ perror("Device open failed");
+ exit(EXIT_FAILURE);
+ }
+
+ /* Setup filter */
+ hci_filter_clear(&flt);
+ hci_filter_all_events(&flt);
+ if (setsockopt(dd, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) {
+ perror("HCI filter setup failed");
+ exit(EXIT_FAILURE);
+ }
+
+ if (hci_send_data(dd, handle, len, buf) < 0) {
+ perror("Send failed");
+ exit(EXIT_FAILURE);
+ }
+
+ hci_close_dev(dd);
+}
+#endif
+
/* Send arbitrary HCI commands */
static struct option cmd_options[] = {
@@ -3388,6 +3448,9 @@ static struct {
{ "spinq", cmd_spinq, "Start periodic inquiry" },
{ "epinq", cmd_epinq, "Exit periodic inquiry" },
{ "cmd", cmd_cmd, "Submit arbitrary HCI commands" },
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ { "acl", cmd_data, "Submit arbitrary ACL data" },
+#endif
{ "con", cmd_con, "Display active connections" },
{ "cc", cmd_cc, "Create connection to remote device" },
{ "dc", cmd_dc, "Disconnect from remote device" },
diff --git a/tools/ibeacon.c b/tools/ibeacon.c
index 6208e8a9..9d48e66b 100755
--- a/tools/ibeacon.c
+++ b/tools/ibeacon.c
@@ -149,6 +149,7 @@ static void adv_tx_power_callback(const void *data, uint8_t size,
cmd.data[0] = 0x02; /* Field length */
cmd.data[1] = 0x01; /* Flags */
+ cmd.data[2] = 0x02; /* LE General Discoverable Mode */
cmd.data[2] |= 0x04; /* BR/EDR Not Supported */
cmd.data[3] = 0x1a; /* Field length */
diff --git a/tools/l2test.c b/tools/l2test.c
index 18194236..98f7353f 100755
--- a/tools/l2test.c
+++ b/tools/l2test.c
@@ -69,9 +69,17 @@ enum {
LSENDRECV,
CSENDRECV,
INFOREQ,
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ CONFIGREQ,
+#endif
PAIRING,
};
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#define L2CAP_DEFAULT_RETRANS_TO 2000 /* 2 seconds */
+#define L2CAP_DEFAULT_MONITOR_TO 12000 /* 12 seconds */
+#endif
+
static unsigned char *buf;
/* Default mtu */
@@ -95,6 +103,9 @@ static long buffer_size = 2048;
static bdaddr_t bdaddr;
static unsigned short psm = 0;
static unsigned short cid = 0;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static uint16_t dcid = 0x0000;
+#endif
/* Default number of frames to send (-1 = infinite) */
static int num_frames = -1;
@@ -1103,6 +1114,146 @@ static void multi_connect_mode(int argc, char *argv[])
}
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void l2cap_add_conf_opt(void **ptr, uint8_t type, uint8_t len, unsigned long val)
+{
+ l2cap_conf_opt *opt = *ptr;
+
+ printf("type 0x%2.2x len %u val 0x%lx \n", type, len, val);
+ opt->type = htobs(type);
+ opt->len = htobs(len);
+
+ switch (opt->len) {
+ case 1:
+ *((uint8_t *) opt->val) = val;
+ break;
+ case 2:
+ bt_put_le16(val, opt->val);
+ break;
+ case 4:
+ bt_put_le32(val, opt->val);
+ break;
+ default:
+ memcpy(opt->val, (void *) val, len);
+ break;
+ }
+
+ *ptr += L2CAP_CONF_OPT_SIZE + len;
+}
+
+static int l2cap_build_conf_req(void *data)
+{
+ l2cap_conf_req *req = data;
+ l2cap_conf_rfc rfc;
+ void *ptr = req->data;
+
+ req->dcid = htobs(dcid);
+ req->flags = htobs(0x0000);
+
+ switch (rfcmode) {
+ case L2CAP_MODE_BASIC:
+ rfc.mode = htobs(L2CAP_MODE_BASIC);
+ rfc.txwin_size = htobs(0);
+ rfc.max_transmit = htobs(0);
+ rfc.retrans_timeout = htobs(0);
+ rfc.monitor_timeout = htobs(0);
+ rfc.max_pdu_size = htobs(0);
+
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
+ (unsigned long) &rfc);
+
+ break;
+
+ case L2CAP_MODE_ERTM:
+ rfc.mode = htobs(L2CAP_MODE_ERTM);
+ rfc.txwin_size = htobs(txwin_size);
+ rfc.max_transmit = htobs(max_transmit);
+ rfc.retrans_timeout = htobs(L2CAP_DEFAULT_RETRANS_TO);
+ rfc.monitor_timeout = htobs(L2CAP_DEFAULT_MONITOR_TO);
+ rfc.max_pdu_size = htobs(imtu);
+
+ /* TODO: Enable FCS, FOC options if required */
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
+ (unsigned long) &rfc);
+ break;
+
+ case L2CAP_MODE_STREAMING:
+ rfc.mode = htobs(L2CAP_MODE_STREAMING);
+ rfc.txwin_size = htobs(txwin_size);
+ rfc.max_transmit = htobs(max_transmit);
+ rfc.retrans_timeout = htobs(L2CAP_DEFAULT_RETRANS_TO);
+ rfc.monitor_timeout = htobs(L2CAP_DEFAULT_MONITOR_TO);
+ rfc.max_pdu_size = htobs(imtu);
+
+ /* TODO: Enable FCS, FOC options if required */
+ l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc),
+ (unsigned long) &rfc);
+
+ break;
+ default:
+ return L2CAP_CONF_REQ_SIZE;
+ }
+ return ptr - data;
+}
+
+static void config_request(char *svr)
+{
+ unsigned char buf[48];
+ l2cap_cmd_hdr *cmd = (l2cap_cmd_hdr *) buf;
+ uint8_t *req_buf = (uint8_t *) (buf + L2CAP_CMD_HDR_SIZE);
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
+ uint16_t mtu;
+ uint32_t channels, mask = 0x0000;
+#endif
+ struct sockaddr_l2 addr;
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
+ int sk, err;
+#else
+ int sk;
+#endif
+ int data_len = 0;
+
+ sk = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP);
+ if (sk < 0) {
+ perror("Can't create socket");
+ return;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.l2_family = AF_BLUETOOTH;
+ bacpy(&addr.l2_bdaddr, &bdaddr);
+ addr.l2_bdaddr_type = bdaddr_type;
+
+ if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ perror("Can't bind socket");
+ goto failed;
+ }
+
+ memset(&addr, 0, sizeof(addr));
+ addr.l2_family = AF_BLUETOOTH;
+ str2ba(svr, &addr.l2_bdaddr);
+ addr.l2_bdaddr_type = bdaddr_type;
+
+ if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0 ) {
+ perror("Can't connect socket");
+ goto failed;
+ }
+
+ memset(buf, 0, sizeof(buf));
+ cmd->code = L2CAP_CONF_REQ;
+ cmd->ident = 141;
+ data_len = l2cap_build_conf_req(req_buf);
+ cmd->len = htobs(data_len);
+
+ if (send(sk, buf, L2CAP_CMD_HDR_SIZE + data_len, 0) < 0) {
+ perror("Can't send info request");
+ goto failed;
+ }
+failed:
+ close(sk);
+}
+#endif
+
static void info_request(char *svr)
{
unsigned char buf[48];
@@ -1314,6 +1465,9 @@ static void usage(void)
"\t-c connect, disconnect, connect, ...\n"
"\t-m multiple connects\n"
"\t-p trigger dedicated bonding\n"
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ "\t-o configuration request\n"
+#endif
"\t-z information request\n");
printf("Options:\n"
@@ -1343,6 +1497,9 @@ static void usage(void)
"\t[-M] become master\n"
"\t[-T] enable timestamps\n"
"\t[-V type] address type (help for list, default = bredr)\n"
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ "\t[-f DCID] destination CID\n"
+#endif
"\t[-e seq] initial sequence value (default = 0)\n");
}
@@ -1353,8 +1510,13 @@ int main(int argc, char *argv[])
bacpy(&bdaddr, BDADDR_ANY);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ while ((opt = getopt(argc, argv, "a:b:cde:f:g:i:mnopqrstuwxyz"
+ "AB:C:D:EF:GH:I:J:K:L:MN:O:P:Q:RSTUV:W:X:Y:Z:")) != EOF) {
+#else
while ((opt = getopt(argc, argv, "a:b:cde:g:i:mnpqrstuwxyz"
"AB:C:D:EF:GH:I:J:K:L:MN:O:P:Q:RSTUV:W:X:Y:Z:")) != EOF) {
+#endif
switch (opt) {
case 'r':
mode = RECV;
@@ -1383,6 +1545,13 @@ int main(int argc, char *argv[])
need_addr = 1;
break;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ case 'o':
+ mode = CONFIGREQ;
+ need_addr = 1;
+ break;
+#endif
+
case 'n':
mode = CONNECT;
need_addr = 1;
@@ -1560,6 +1729,13 @@ int main(int argc, char *argv[])
break;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ case 'f':
+ dcid = atoi(optarg);
+ printf("dcid %d", dcid);
+ break;
+#endif
+
case 'e':
seq_start = atoi(optarg);
break;
@@ -1669,6 +1845,12 @@ int main(int argc, char *argv[])
info_request(argv[optind]);
exit(0);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ case CONFIGREQ:
+ config_request(argv[optind]);
+ exit(0);
+#endif
+
case PAIRING:
do_pairing(argv[optind]);
exit(0);
diff --git a/tools/parse_companies.pl b/tools/parse_companies.pl
new file mode 100755
index 00000000..6dc358ee
--- /dev/null
+++ b/tools/parse_companies.pl
@@ -0,0 +1,59 @@
+#!/usr/bin/perl
+
+# parse companies from
+# https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers
+
+use strict;
+# use URI::Encode qw(uri_decode);
+
+my %known_entities = (
+ 'nbsp' => ' ',
+ 'eacute' => 'é',
+ 'auml' => 'ä',
+);
+
+# better to use URI::Encode if you have it
+sub uri_decode {
+ my $name = $_[0];
+ foreach my $entity (keys %known_entities) {
+ my $to = $known_entities{$entity};
+ $name =~ s/&$entity;/$to/g;
+ }
+ foreach my $entity (map { lc $_ } $name =~ /&([^;]+);/g) {
+ if ($entity ne 'amp') {
+ print "Unable to convert &$entity;, giving up\n";
+ exit 1;
+ }
+ }
+ $name =~ s/&amp;/&/ig;
+ $name =~ s/&nbsp;/ /ig;
+ return $name;
+}
+
+# never parse HTML with regex!
+# except when you should
+
+my $identifier;
+my $next_is_name = 0;
+
+while (<>) {
+ s/\xe2\x80\x8b//g; # kill zero width space
+
+ # grab identifier (in hex)
+ if (/\<td.*(0x[0-9A-F]{4})/i) {
+ $identifier = $1;
+ $next_is_name = 1;
+
+ # next <td> should be company name
+ } elsif ($next_is_name && m|\<td.*\>(.*)\</td\>|) {
+ my $name = uri_decode($1);
+ $name =~ s/^\s+//g; # kill leading
+ $name =~ s/\s+$//g; # and trailing space
+ my $id = hex($identifier);
+ if ($id != 65535) {
+ print "\tcase $id:\n";
+ print "\t\treturn \"$name\";\n";
+ }
+ $next_is_name = 0;
+ }
+}
diff --git a/tools/parser/l2cap.h b/tools/parser/l2cap.h
index 788aef00..6bd5b2ff 100755
--- a/tools/parser/l2cap.h
+++ b/tools/parser/l2cap.h
@@ -179,6 +179,18 @@ typedef struct {
} __attribute__ ((packed)) l2cap_conf_opt;
#define L2CAP_CONF_OPT_SIZE 2
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+typedef struct {
+ uint8_t mode;
+ uint8_t txwin_size;
+ uint8_t max_transmit;
+ uint16_t retrans_timeout;
+ uint16_t monitor_timeout;
+ uint16_t max_pdu_size;
+} __attribute__ ((packed)) l2cap_conf_rfc ;
+#define L2CAP_CONF_RFC_SIZE 9
+#endif
+
#define L2CAP_CONF_MTU 0x01
#define L2CAP_CONF_FLUSH_TO 0x02
#define L2CAP_CONF_QOS 0x03
diff --git a/tools/pskey_get.c b/tools/pskey_get.c
new file mode 100755
index 00000000..db4d2280
--- /dev/null
+++ b/tools/pskey_get.c
@@ -0,0 +1,384 @@
+#include <sys/stat.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#define LOG_TAG "pskey"
+// #include "cutils/log.h"
+
+#include "hciattach_sprd.h"
+//#include "bt_vendor_sprd.h"
+#define BT_PSKEY_TRACE_BUF_SIZE 256
+#define MAX_BOARD_TYPE_LEN 32
+
+#define _FILE_PARSE_DEBUG_
+#define CMD_ITEM_TABLE(ITEM, MEM_OFFSET, TYPE) { ITEM, (unsigned int)( &( ((BT_PSKEY_CONFIG_T *)(0))->MEM_OFFSET )), TYPE }
+#define ALOGI(fmt, arg...) fprintf(stderr, "%s:%d()" fmt "\n", __FILE__,__LINE__, ## arg)
+#define ALOGE(fmt, arg...) fprintf(stderr, "%s:%d()" fmt "\n", __FILE__,__LINE__, ## arg)
+
+#define PSKEY_PATH "/usr/lib/firmware/scx35_pikeavivaltove_3M_MARLIN_connectivity_configure.ini"
+
+typedef struct
+{
+ char item[64];
+ uint32 par[32];
+ int num;
+}cmd_par;
+
+typedef struct
+{
+ char *item;
+ unsigned int mem_offset;
+ int type;
+}cmd_par_table;
+
+static cmd_par_table g_pskey_table[] =
+{
+ CMD_ITEM_TABLE("pskey_cmd", pskey_cmd, 4),
+
+ CMD_ITEM_TABLE("g_dbg_source_sink_syn_test_data", g_dbg_source_sink_syn_test_data, 1),
+ CMD_ITEM_TABLE("g_sys_sleep_in_standby_supported", g_sys_sleep_in_standby_supported, 1),
+ CMD_ITEM_TABLE("g_sys_sleep_master_supported", g_sys_sleep_master_supported, 1),
+ CMD_ITEM_TABLE("g_sys_sleep_slave_supported", g_sys_sleep_slave_supported, 1),
+
+ CMD_ITEM_TABLE("default_ahb_clk", default_ahb_clk, 4),
+ CMD_ITEM_TABLE("device_class", device_class, 4),
+ CMD_ITEM_TABLE("win_ext", win_ext, 4),
+
+ CMD_ITEM_TABLE("g_aGainValue", g_aGainValue, 4),
+ CMD_ITEM_TABLE("g_aPowerValue", g_aPowerValue, 4),
+
+ CMD_ITEM_TABLE("feature_set", feature_set, 1),
+ CMD_ITEM_TABLE("device_addr", device_addr, 1),
+
+ CMD_ITEM_TABLE("g_sys_sco_transmit_mode", g_sys_sco_transmit_mode, 1), //true tramsmit by uart, otherwise by share memory
+ CMD_ITEM_TABLE("g_sys_uart0_communication_supported", g_sys_uart0_communication_supported, 1), //true use uart0, otherwise use uart1 for debug
+ CMD_ITEM_TABLE("edr_tx_edr_delay", edr_tx_edr_delay, 1),
+ CMD_ITEM_TABLE("edr_rx_edr_delay", edr_rx_edr_delay, 1),
+
+ CMD_ITEM_TABLE("g_wbs_nv_117", g_wbs_nv_117, 2),
+
+
+ CMD_ITEM_TABLE("is_wdg_supported", is_wdg_supported, 4),
+
+ CMD_ITEM_TABLE("share_memo_rx_base_addr", share_memo_rx_base_addr, 4),
+ //CMD_ITEM_TABLE("share_memo_tx_base_addr", share_memo_tx_base_addr, 4),
+
+ CMD_ITEM_TABLE("g_wbs_nv_118", g_wbs_nv_118, 2),
+ CMD_ITEM_TABLE("g_nbv_nv_117", g_nbv_nv_117, 2),
+
+
+ CMD_ITEM_TABLE("share_memo_tx_packet_num_addr", share_memo_tx_packet_num_addr, 4),
+ CMD_ITEM_TABLE("share_memo_tx_data_base_addr", share_memo_tx_data_base_addr, 4),
+
+ CMD_ITEM_TABLE("g_PrintLevel", g_PrintLevel, 4),
+
+ CMD_ITEM_TABLE("share_memo_tx_block_length", share_memo_tx_block_length, 2),
+ CMD_ITEM_TABLE("share_memo_rx_block_length", share_memo_rx_block_length, 2),
+ CMD_ITEM_TABLE("share_memo_tx_water_mark", share_memo_tx_water_mark, 2),
+ //CMD_ITEM_TABLE("share_memo_tx_timeout_value", share_memo_tx_timeout_value, 2),
+ CMD_ITEM_TABLE("g_nbv_nv_118", g_nbv_nv_118, 2),
+
+ CMD_ITEM_TABLE("uart_rx_watermark", uart_rx_watermark, 2),
+ CMD_ITEM_TABLE("uart_flow_control_thld", uart_flow_control_thld, 2),
+ CMD_ITEM_TABLE("comp_id", comp_id, 4),
+ CMD_ITEM_TABLE("pcm_clk_divd", pcm_clk_divd, 2),
+
+
+ CMD_ITEM_TABLE("bt_reserved", reserved, 4)
+};
+
+static int bt_getFileSize(char *file)
+{
+ struct stat temp;
+ stat(file, &temp);
+ return temp.st_size;
+}
+
+static int bt_find_type(char key)
+{
+ if( (key >= 'a' && key <= 'w') || (key >= 'y' && key <= 'z') || (key >= 'A' && key <= 'W') || (key >= 'Y' && key <= 'Z') || ('_' == key) )
+ return 1;
+ if( (key >= '0' && key <= '9') || ('-' == key) )
+ return 2;
+ if( ('x' == key) || ('X' == key) || ('.' == key) )
+ return 3;
+ if( (key == '\0') || ('\r' == key) || ('\n' == key) || ('#' == key) )
+ return 4;
+ return 0;
+}
+
+static void bt_getCmdOneline(unsigned char *str, cmd_par *cmd)
+{
+ int i, j, bufType, cType, flag;
+ char tmp[BT_PSKEY_TRACE_BUF_SIZE];
+ char c;
+ bufType = -1;
+ cType = 0;
+ flag = 0;
+ memset( cmd, 0, sizeof(cmd_par) );
+ for(i = 0, j = 0; ; i++)
+ {
+ c = str[i];
+ cType = bt_find_type(c);
+ if( (1 == cType) || ( 2 == cType) || (3 == cType) )
+ {
+ tmp[j] = c;
+ j++;
+ if(-1 == bufType)
+ {
+ if(2 == cType)
+ bufType = 2;
+ else
+ bufType = 1;
+ }
+ else if(2 == bufType)
+ {
+ if(1 == cType)
+ bufType = 1;
+ }
+ continue;
+ }
+ if(-1 != bufType)
+ {
+ tmp[j] = '\0';
+
+ if((1 == bufType) && (0 == flag) )
+ {
+ strcpy(cmd->item, tmp);
+ flag = 1;
+ }
+ else
+ {
+ /* compatible with HEX */
+ if (tmp[0] == '0' && (tmp[1] == 'x' || tmp[1] == 'X')) {
+ cmd->par[cmd->num] = strtoul(tmp, 0, 16) & 0xFFFFFFFF;
+ cmd->num++;
+ } else {
+ cmd->par[cmd->num] = strtoul(tmp, 0, 10) & 0xFFFFFFFF;
+ cmd->num++;
+ }
+ }
+ bufType = -1;
+ j = 0;
+ }
+ if(0 == cType )
+ continue;
+ if(4 == cType)
+ return;
+ }
+ return;
+}
+
+static int bt_getDataFromCmd(cmd_par_table *pTable, cmd_par *cmd, void *pData)
+{
+ int i;
+ unsigned char *p;
+ if( (1 != pTable->type) && (2 != pTable->type) && (4 != pTable->type) )
+ return -1;
+ p = (unsigned char *)(pData) + pTable->mem_offset;
+#ifdef _FILE_PARSE_DEBUG_
+ char tmp[BT_PSKEY_TRACE_BUF_SIZE] = {0};
+ char string[16] = {0};
+ sprintf(tmp, "###[pskey]%s, offset:%d, num:%d, value: ", pTable->item, pTable->mem_offset, cmd->num);
+ for(i=0; i<cmd->num; i++)
+ {
+ memset(string, 0, 16);
+ sprintf(string, "0x%x, ", cmd->par[i] );
+ strcat(tmp, string);
+ }
+ ALOGI("%s\n", tmp);
+#endif
+ for(i = 0; i < cmd->num; i++)
+ {
+ if(1 == pTable->type)
+ *((unsigned char *)p + i) = (unsigned char)(cmd->par[i]);
+ else if(2 == pTable->type)
+ *((unsigned short *)p + i) = (unsigned short)(cmd->par[i]);
+ else if(4 == pTable->type)
+ *( (unsigned int *)p + i) = (unsigned int)(cmd->par[i]);
+ else
+ ALOGE("%s, type err\n", __func__);
+ }
+ return 0;
+}
+
+static cmd_par_table *bt_cmd_table_match(cmd_par *cmd)
+{
+ int i;
+ cmd_par_table *pTable = NULL;
+ int len = sizeof(g_pskey_table) / sizeof(cmd_par_table);
+ if(NULL == cmd->item)
+ return NULL;
+ for(i = 0; i < len; i++)
+ {
+ if(NULL == g_pskey_table[i].item)
+ continue;
+ if( 0 != strcmp( g_pskey_table[i].item, cmd->item ) )
+ continue;
+ pTable = &g_pskey_table[i];
+ break;
+ }
+ return pTable;
+}
+
+
+static int bt_getDataFromBuf(void *pData, unsigned char *pBuf, int file_len)
+{
+ int i, p;
+ cmd_par cmd;
+ cmd_par_table *pTable = NULL;
+ if((NULL == pBuf) || (0 == file_len) || (NULL == pData) )
+ return -1;
+ for(i = 0, p = 0; i < file_len; i++)
+ {
+ if( ('\n' == *(pBuf + i)) || ( '\r' == *(pBuf + i)) || ( '\0' == *(pBuf + i) ) )
+ {
+ if(5 <= (i - p) )
+ {
+ bt_getCmdOneline((pBuf + p), &cmd);
+ pTable = bt_cmd_table_match(&cmd);
+ if(NULL != pTable)
+ {
+ bt_getDataFromCmd(pTable, &cmd, pData);
+ }
+ }
+ p = i + 1;
+ }
+
+ }
+ return 0;
+}
+
+static int bt_dumpPskey(BT_PSKEY_CONFIG_T *p)
+{
+ ALOGI("pskey_cmd: 0x%08X", p->pskey_cmd);
+
+ ALOGI("g_dbg_source_sink_syn_test_data: 0x%02X", p->g_dbg_source_sink_syn_test_data);
+ ALOGI("g_sys_sleep_in_standby_supported: 0x%02X", p->g_sys_sleep_in_standby_supported);
+ ALOGI("g_sys_sleep_master_supported: 0x%02X", p->g_sys_sleep_master_supported);
+ ALOGI("g_sys_sleep_slave_supported: 0x%02X", p->g_sys_sleep_slave_supported);
+
+ ALOGI("default_ahb_clk: %d", p->default_ahb_clk);
+ ALOGI("device_class: 0x%08X", p->device_class);
+ ALOGI("win_ext: 0x%08X", p->win_ext);
+
+ ALOGI("g_aGainValue: 0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X", p->g_aGainValue[0], p->g_aGainValue[1], p->g_aGainValue[2], p->g_aGainValue[3], p->g_aGainValue[4], p->g_aGainValue[5]);
+ ALOGI("g_aPowerValue: 0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X", p->g_aPowerValue[0], p->g_aPowerValue[1], p->g_aPowerValue[2], p->g_aPowerValue[3], p->g_aPowerValue[4]);
+
+
+ ALOGI("feature_set(0~7): 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X", p->feature_set[0], p->feature_set[1], p->feature_set[2],
+ p->feature_set[3], p->feature_set[4], p->feature_set[5], p->feature_set[6], p->feature_set[7]);
+ ALOGI("feature_set(8~15): 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X", p->feature_set[8], p->feature_set[9], p->feature_set[10],
+ p->feature_set[11], p->feature_set[12], p->feature_set[13], p->feature_set[14], p->feature_set[15]);
+ ALOGI("device_addr: 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X", p->device_addr[0], p->device_addr[1], p->device_addr[2], p->device_addr[3], p->device_addr[4], p->device_addr[5]);
+
+ ALOGI("g_sys_sco_transmit_mode: 0x%02X", p->g_sys_sco_transmit_mode);
+ ALOGI("g_sys_uart0_communication_supported: 0x%02X", p->g_sys_uart0_communication_supported);
+ ALOGI("edr_tx_edr_delay: %d", p->edr_tx_edr_delay);
+ ALOGI("edr_rx_edr_delay: %d", p->edr_rx_edr_delay);
+
+ ALOGI("g_wbs_nv_117 : 0x%04X", p->g_wbs_nv_117 );
+
+ ALOGI("is_wdg_supported: 0x%08X", p->is_wdg_supported);
+
+ ALOGI("share_memo_rx_base_addr: 0x%08X", p->share_memo_rx_base_addr);
+ //ALOGI("share_memo_tx_base_addr: 0x%08X", p->share_memo_tx_base_addr);
+ ALOGI("g_wbs_nv_118 : 0x%04X", p->g_wbs_nv_118 );
+ ALOGI("g_nbv_nv_117 : 0x%04X", p->g_nbv_nv_117 );
+
+
+ ALOGI("share_memo_tx_packet_num_addr: 0x%08X", p->share_memo_tx_packet_num_addr);
+ ALOGI("share_memo_tx_data_base_addr: 0x%08X", p->share_memo_tx_data_base_addr);
+
+ ALOGI("g_PrintLevel: 0x%08X", p->g_PrintLevel);
+
+ ALOGI("share_memo_tx_block_length: 0x%04X", p->share_memo_tx_block_length);
+ ALOGI("share_memo_rx_block_length: 0x%04X", p->share_memo_rx_block_length);
+ ALOGI("share_memo_tx_water_mark: 0x%04X", p->share_memo_tx_water_mark);
+ //ALOGI("share_memo_tx_timeout_value: 0x%04X", p->share_memo_tx_timeout_value);
+ ALOGI("g_nbv_nv_118 : 0x%04X", p->g_nbv_nv_118 );
+
+ ALOGI("uart_rx_watermark: %d", p->uart_rx_watermark);
+ ALOGI("uart_flow_control_thld: %d", p->uart_flow_control_thld);
+ ALOGI("comp_id: 0x%08X", p->comp_id);
+ ALOGI("pcm_clk_divd : 0x%04X", p->pcm_clk_divd );
+
+
+ ALOGI("reserved(0~7): 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X", p->reserved[0], p->reserved[1], p->reserved[2],
+ p->reserved[3], p->reserved[4], p->reserved[5], p->reserved[6], p->reserved[7]);
+ return 0;
+}
+#if 0
+static int bt_get_config_ver(unsigned char *pBuf, int len)
+{
+ int i, p;
+ cmd_par cmd;
+ int ret = -1;
+ for(i = 0, p = 0; i < len; i++)
+ {
+ if( ('\n' == *(pBuf + i)) || ( '\r' == *(pBuf + i)) || ( '\0' == *(pBuf + i) ) )
+ {
+ if(5 <= (i - p) )
+ {
+ bt_getCmdOneline((pBuf + p), &cmd);
+ if( 0 == strcmp(cmd.item, "version") )
+ {
+ ret = cmd.par[0];
+ break;
+ }
+ memset(&cmd, 0, sizeof(cmd_par) );
+ }
+ p = i + 1;
+ }
+
+ }
+ return ret;
+}
+#endif
+int bt_getPskeyFromFile(void *pData)
+{
+ int ret = -1;
+ int fd;
+ unsigned char *pBuf = NULL;
+ int len;
+
+ ALOGI("begin to bt_getPskeyFromFile");
+ fd = open(PSKEY_PATH, O_RDONLY, 0644);
+ if(-1 != fd)
+ {
+ len = bt_getFileSize(PSKEY_PATH);
+ pBuf = (unsigned char *)malloc(len);
+ ret = read(fd, pBuf, len);
+ if(-1 == ret)
+ {
+ ALOGE("%s read %s ret:%d\n", __FUNCTION__, PSKEY_PATH, ret);
+ free(pBuf);
+ close(fd);
+ return -1;
+ }
+ close(fd);
+ }
+ else
+ {
+ ALOGE("%s open %s ret:%d\n", __FUNCTION__, PSKEY_PATH, fd);
+ return -1;
+ }
+
+ ret = bt_getDataFromBuf(pData, pBuf, len);
+ if(-1 == ret)
+ {
+ free(pBuf);
+ return -1;
+ }
+ ALOGI("begin to dumpPskey");
+ bt_dumpPskey((BT_PSKEY_CONFIG_T *)pData);
+ free(pBuf);
+ return 0;
+}
+
+
diff --git a/tools/sdptool.c b/tools/sdptool.c
index b1cbcfdc..0881c6fa 100755
--- a/tools/sdptool.c
+++ b/tools/sdptool.c
@@ -1219,7 +1219,11 @@ static int add_sp(sdp_session_t *session, svc_info_t *si)
sdp_set_service_classes(&record, svclass_id);
sdp_uuid16_create(&profile.uuid, SERIAL_PORT_PROFILE_ID);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ profile.version = 0x0102;
+#else
profile.version = 0x0100;
+#endif
profiles = sdp_list_append(0, &profile);
sdp_set_profile_descs(&record, profiles);
diff --git a/tools/test-runner.c b/tools/test-runner.c
index 9b544260..57df7fb7 100755
--- a/tools/test-runner.c
+++ b/tools/test-runner.c
@@ -544,7 +544,11 @@ static const char *test_table[] = {
static void run_command(char *cmdname, char *home)
{
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ char *argv[10], *envp[3];
+#else
char *argv[9], *envp[3];
+#endif
int pos = 0, idx = 0;
int serial_fd;
pid_t pid, dbus_pid, daemon_pid;
diff --git a/tools/update_compids.sh b/tools/update_compids.sh
new file mode 100755
index 00000000..95c961d6
--- /dev/null
+++ b/tools/update_compids.sh
@@ -0,0 +1,57 @@
+#!/bin/bash
+# Download the list of company IDs from bluetooth.org and generate a diff which
+# can be applied to source tree to update bt_compidtostr(). Usage:
+#
+# 1) ./tools/update_compids.sh | git apply -p0
+# 2) Inspect changes to make sure they are sane
+# 3) git commit -m "lib: Update list of company identifiers" lib/bluetooth.c
+#
+# Requires html2text: http://www.mbayer.de/html2text/
+#
+set -e -u
+
+tmpdir=$(mktemp -d)
+trap "rm -rf $tmpdir" EXIT
+
+mkdir $tmpdir/lib
+cp lib/bluetooth.c $tmpdir/lib/bluetooth.c.orig
+cp lib/bluetooth.c $tmpdir/lib/bluetooth.c
+
+cd $tmpdir
+
+path=en-us/specification/assigned-numbers/company-identifiers
+# Use "iconv -c" to strip unwanted unicode characters
+# Fixups:
+# - strip <input> tags of type "checkbox" because html2text generates UTF-8 for
+# them in some distros even when using -ascii (e.g. Fedora)
+# - replace "&#160;" (non-breaking space) with whitespace manually, because
+# some versions incorrectly convert it into "\xC2\xA0"
+curl https://www.bluetooth.org/$path | iconv -c -f utf8 -t ascii | \
+ sed '/<input.*type="checkbox"/d; s/&#160;/ /g' | \
+ html2text -ascii -width 160 -o identifiers.txt >/dev/null
+
+# Some versions of html2text do not replace &amp; (e.g. Fedora)
+sed -i 's/&amp;/\&/g' identifiers.txt
+
+sed -n '/^const char \*bt_compidtostr(int compid)/,/^}/p' \
+ lib/bluetooth.c > old.c
+
+echo -e 'const char *bt_compidtostr(int compid)\n{\n\tswitch (compid) {' > new.c
+cat identifiers.txt |
+ perl -ne 'm/^(\d+)\s+0x[0-9a-f]+\s+(.*)/i &&
+ print "\tcase $1:\n\t\treturn \"$2\";\n"' >> new.c
+if ! grep -q "return \"" new.c; then
+ echo "ERROR: could not parse company IDs from bluetooth.org" >&2
+ exit 1
+fi
+if [ -n "$(tr -d '[:print:]\t\n' < new.c)" ]; then
+ echo -n "ERROR: invalid non-ASCII characters found while parsing" >&2
+ echo -n " company IDs. Please identify offending sequence and fix" >&2
+ echo " tools/update_compids.sh accordingly." >&2
+ exit 1
+fi
+echo -e '\tcase 65535:\n\t\treturn "internal use";' >> new.c
+echo -e '\tdefault:\n\t\treturn "not assigned";\n\t}\n}' >> new.c
+
+diff -Naur old.c new.c | patch -sp0 lib/bluetooth.c
+diff -Naur lib/bluetooth.c.orig lib/bluetooth.c
diff --git a/tools/valgrind.supp b/tools/valgrind.supp
new file mode 100755
index 00000000..9efb6f1d
--- /dev/null
+++ b/tools/valgrind.supp
@@ -0,0 +1,27 @@
+{
+ ecb_bind
+ Memcheck:Param
+ socketcall.bind(my_addr.sa_data)
+ fun:bind
+ fun:ecb_aes_setup
+}
+{
+ cmac_bind
+ Memcheck:Param
+ socketcall.bind(my_addr.sa_data)
+ fun:bind
+ fun:cmac_aes_setup
+}
+{
+ logging_open
+ Memcheck:Param
+ socketcall.bind(my_addr.rc_bdaddr)
+ fun:bind
+ fun:logging_open
+}
+{
+ bind
+ Memcheck:Param
+ socketcall.bind(my_addr.rc_channel)
+ fun:bind
+}