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 \
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
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 =
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
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
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
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
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 \
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 \
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
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
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
--- /dev/null
+/*
+ *
+ * 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;
+}
--- /dev/null
+/*
+ *
+ * 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);
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012 Texas Instruments, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <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;
+}
--- /dev/null
+/*
+ *
+ * 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);
--- /dev/null
+/*
+ *
+ * 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;
+}
--- /dev/null
+/*
+ *
+ * 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);
--- /dev/null
+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)
--- /dev/null
+/*
+ *
+ * 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;
+}
--- /dev/null
+/*
+ *
+ * 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);
--- /dev/null
+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));
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;
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
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
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);
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 |
* 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 *);
}
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);
}
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];
if (info->ccc_handle != NULL)
*info->ccc_handle = a->handle;
}
+#endif
*handle = h;
/* 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,
{
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);
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;
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)
{
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
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;
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)
"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>",
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)
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");
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
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}])
[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}])
[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}])
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.
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
--- /dev/null
+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);
+...
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
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
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.
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
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
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
"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
====================================
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
--- /dev/null
+Maintainer guidelines
+*********************
+
+This document is intended for the maintainers of the BlueZ project. It
+serves as basic guidelines for handling patch review and commit access.
+
+
+Rule 1: Keep the GIT tree clean and linear
+==========================================
+
+The bluetooth.git, bluetooth-next.git and bluez.git trees are not your
+private playground. The history is meant to be clean and linear.
+
+ - NO merges
+ - NO branches
+ - NO tags
+
+If anyone needs testing or work on a feature, clone the tree and do
+it in your own copy. The master trees are off limits.
+
+One advise to avoid any accidental errors in this area to set proper
+options in global ~/.gitconfig or local .git/config files.
+
+ [merge]
+ ff = only
+
+Violations of this rule are not acceptable. This rule is enforced. If
+in doubt ask one of the seasoned maintainers.
+
+
+Rule 2: Enforce clean commit messages
+=====================================
+
+The commit messages are required to be clean and follow style guidelines
+to be consistent.
+
+Commit messages should adhere to a 72 characters by line limit. That
+makes it easy to read them via git log in a terminal window. Exceptions
+to this rule are logs, trace or other verbatim copied information.
+
+Every commit requires full names and email addresses. No synonyms or
+nicknames are allowed. It is also important that the Outlook style
+names with lastname, firstname are not allowed. It is the maintainers
+job to ensure we get proper firstname lastname <email> authorship.
+
+It is also important that the committer itself uses a valid name and
+email address when committing patches. So ensure that either the
+global ~/.gitconfig or local .git/config provides proper values.
+
+ [user]
+ name = Peter Mustermann
+ email = peter@mustermann.de
+
+Commit messages for bluez.git shall not contain Signed-off-by
+signatures. They are not used in userspace and with that it is the
+maintainers job to ensure they do not get committed to the repository.
+
+For bluetooth.git and bluetooth-next.git The Signed-off-by process is
+used and the signatures are required.
+
+Tags like Change-Id generated from Gerrit are never acceptable. It is
+the maintainers job to ensure that these are not committed into the
+repositories.
+
+Violations of this rule create a mess in the tree that can not be
+reversed. If in doubt ask one of the seasoned maintainers.
+
+
+Rule 3: Enforce correct coding style
+====================================
+
+The coding style follows roughly the kernel coding style with any
+exceptions documented in doc/coding-style.txt.
+
+To ensure trivial white-space errors don't get committed, have the
+following in your .gitconfig:
+
+ [apply]
+ whitespace = error
+
+It can also be helpful to use the checkpatch.pl script coming with the
+Linux kernel to do some automated checking. Adding the following to your
+.git/hooks/pre-commit and .git/hooks/pre-applypatch is a simple way to
+do this:
+
+ exec git diff --cached | ~/src/linux/scripts/checkpatch.pl -q \
+ --no-tree --no-signoff --show-types \
+ --ignore CAMELCASE,NEW_TYPEDEFS,INITIALISED_STATIC -
+
+The above assumes that a kernel tree resides in ~/src/linux/.
+
+
+Rule 4: Pay extra attention to adding new files to the tree
+===========================================================
+
+New files that are added to the tree require several things to be
+verified first:
+
+ - Check that the names are acceptible with other maintainers
+ - Ensure that the file modes are correct
+ - Verify that the license & copyright headers are correct
+ - If the file is supposed to be part of the release 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.
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
==================================
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
--- /dev/null
+BlueZ D-Bus Out Of Band Pairing API description
+===============================================
+
+Copyright (C) 2011 Szymon Janc <szymon.janc@tieto.com> for ST-Ericsson
+
+Service org.bluez
+Interface org.bluez.OutOfBand
+Object path [variable prefix]/{hci0,hci1,...}
+
+Methods array{byte} hash, array{byte} randomizer ReadLocalData()
+
+ This method reads local OOB data from adapter. Return
+ value is pair of arrays 16 bytes each.
+
+ Note: This method will generate and return new local
+ OOB data.
+
+ Possible errors: org.bluez.Error.Failed
+ org.bluez.Error.InProgress
+
+ void AddRemoteData(string address, array{byte} hash,
+ array{byte} randomizer)
+
+ This method adds new Out Of Band data for
+ specified address. If data for specified address
+ already exists it will be overwritten with new one.
+
+ Possible errors: org.bluez.Error.Failed
+ org.bluez.Error.InvalidArguments
+
+ void RemoveRemoteData(string address)
+
+ This method removes Out Of Band data for specified
+ address. If data for specified address does not exist
+ nothing is removed.
+
+ Possible errors: org.bluez.Error.Failed
+ org.bluez.Error.InvalidArguments
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
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
====================
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
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,
#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...)
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);
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;
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);
}
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
{ }
};
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);
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)
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();
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)
{
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,
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)
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;
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);
#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
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";
}
{ "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 }
};
/* 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 = ∾
+ 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;
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;
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
} __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;
/* 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
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);
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);
} __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
"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];
}
#endif
#include <stdint.h>
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#include <bluetooth/bluetooth.h>
+#endif
#define GENERIC_AUDIO_UUID "00001203-0000-1000-8000-00805f9b34fb"
#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"
#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
#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
#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,
#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)
{
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);
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,
{ 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
{ }
};
}
}
+#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
{ }
};
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;
}
break;
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ setenv("PAGER", "cat", 0);
+#endif
open_pager();
switch (format) {
#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);
#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 { \
"\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");
}
{ "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' },
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;
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;
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();
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);
return "Intel";
case 15:
return "Broadcom";
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ default:
+ return "Unknown";
+#endif
}
return NULL;
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;
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;
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
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
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
#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"
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)
{
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;
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[] = {
{ "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
{ }
};
--- /dev/null
+/*
+ *
+ * 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);
+}
--- /dev/null
+/*
+ *
+ * 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);
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);
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",
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);
} 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") ||
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);
{
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);
{
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);
}
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",
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,
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,
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)
{ "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",
#include "dbus.h"
#include "transfer.h"
#include "session.h"
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#include "manager.h"
+#endif
#include "driver.h"
#include "transport.h"
guint process_id;
char *folder;
struct callback_data *callback;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ DBusMessage *message;
+ DBusConnection *connection;
+#endif
};
static GSList *sessions = NULL;
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;
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);
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)
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
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)
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;
}
{
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;
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);
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(®, forbid_chars, 0);
+
+ if (exp != 0)
+ return FALSE;
+
+ exp = regexec(®, name, size, match, 0);
+
+ regfree(®);
+
+ 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;
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);
--- /dev/null
+/*
+ *
+ * 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, ®,
+ 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)
+{
+}
--- /dev/null
+/*
+ *
+ * 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)
+{
+}
#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;
uint32_t find_handle;
struct cache cache;
struct pbap_object *obj;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ void *backend_data;
+#endif
};
struct pbap_object {
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);
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);
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
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;
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)
g_obex_apparam_get_uint8(apparam, SEARCHATTRIB_TAG,
¶m->searchattrib);
g_obex_apparam_get_uint8(apparam, FORMAT_TAG, ¶m->format);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ bmaxlistCount = g_obex_apparam_get_uint16(apparam, MAXLISTCOUNT_TAG,
+ ¶m->maxlistcount);
+#else
g_obex_apparam_get_uint16(apparam, MAXLISTCOUNT_TAG,
¶m->maxlistcount);
+#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */
g_obex_apparam_get_uint16(apparam, LISTSTARTOFFSET_TAG,
¶m->liststartoffset);
g_obex_apparam_get_uint64(apparam, FILTER_TAG, ¶m->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,
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)
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;
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;
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 */
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;
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);
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;
}
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;
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)
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)
--- /dev/null
+/*
+ *
+ * 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;
+}
--- /dev/null
+/*
+ *
+ * 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);
+}
--- /dev/null
+/*
+ * 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;
+}
--- /dev/null
+/*
+ *
+ * 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)
{ 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;
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)
{
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)
{
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
{ }
};
{ "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 },
{ }
};
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);
}
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) {
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
[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/'
+# 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/
#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
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.
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} .
%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} \
--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
%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-*
# 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
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}
%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
%{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
%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,-)
%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
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 ST-Ericsson SA
+ *
+ * Author: Szymon Janc <szymon.janc@tieto.com> for ST-Ericsson
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef 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
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;
#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
* 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);
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)
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;
--- /dev/null
+/*
+ * 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__ */
+
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ * Copyright (C) 2011 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <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)
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;
{
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);
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;
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;
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;
.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;
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)
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);
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;
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;
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);
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) {
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,
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) {
/* 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;
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,
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;
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;
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;
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;
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)
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,
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);
#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"
#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
#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
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);
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;
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:
}
}
+#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;
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;
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;
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)
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);
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);
}
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,
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;
(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);
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,
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);
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);
/* 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) {
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);
#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
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 {
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,
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;
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)
/* 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);
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;
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 |
AVRCP_FEATURE_CATEGORY_4 |
AVRCP_FEATURE_BROWSING |
AVRCP_FEATURE_PLAYER_SETTINGS );
-
+#endif
record = sdp_record_alloc();
if (!record)
return NULL;
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);
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);
return record;
}
+#endif
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;
}
GSList *l;
int attr;
int val;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ uint32_t *position_val = NULL;
+ GList *settings;
+#endif
if (player->sessions == NULL)
return;
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);
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));
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;
return "singletrack";
case AVRCP_REPEAT_MODE_ALL:
return "alltracks";
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
case AVRCP_REPEAT_MODE_GROUP:
return "group";
+#endif
}
break;
return "off";
case AVRCP_SCAN_ALL:
return "alltracks";
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
case AVRCP_SCAN_GROUP:
return "group";
+#endif
}
break;
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);
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)
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;
/*
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;
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;
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;
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)
.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,
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);
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)
{
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:
!session->controller->player)
break;
case AVRCP_EVENT_VOLUME_CHANGED:
+#endif
avrcp_register_notification(session, event);
break;
}
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;
}
session);
}
+#ifdef TIZEN_FEATURE_BLUEZ_AVRCP_TARGET
static void target_init(struct avrcp *session)
{
struct avrcp_server *server = session->server;
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) |
avrcp_connect_browsing(session);
}
+#endif
+#ifdef TIZEN_FEATURE_BLUEZ_AVRCP_CONTROL
static void controller_init(struct avrcp *session)
{
struct avrcp_player *player;
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);
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)
{
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)
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);
{
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);
}
control_unregister(service);
}
+#ifdef TIZEN_FEATURE_BLUEZ_AVRCP_TARGET
static void avrcp_target_server_remove(struct btd_profile *p,
struct btd_adapter *adapter)
{
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)
{
return 0;
}
+#endif
static struct btd_profile avrcp_target_profile = {
.name = "audio-avrcp-target",
.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)
control_unregister(service);
}
+#ifdef TIZEN_FEATURE_BLUEZ_AVRCP_CONTROL
static void avrcp_controller_server_remove(struct btd_profile *p,
struct btd_adapter *adapter)
{
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)
{
return 0;
}
+#endif
static struct btd_profile avrcp_controller_profile = {
.name = "avrcp-controller",
.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)
#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"
#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 */
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;
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)
if (endpoint->sep) {
a2dp_remove_sep(endpoint->sep);
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
return;
+#endif
}
info("Endpoint unregistered: sender=%s path=%s", endpoint->sender,
{
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,
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);
}
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;
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);
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);
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;
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;
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 ||
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)
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)
{
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);
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)
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);
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;
}
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;
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);
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;
}
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;
}
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));
}
dbus_message_iter_get_basic(iter, &value);
if (strcasecmp(key, "Duration") == 0) {
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
value /= 1000;
+#endif
mp->duration = value;
}
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)
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) {
} 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;
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;
}
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);
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
{ },
};
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)
{
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;
{ 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
{ }
};
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);
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,
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
}
}
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);
{
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));
DBG("Unable to get a session");
return -EIO;
}
+#endif
if (sink->connect_id > 0 || sink->disconnect_id > 0)
return -EBUSY;
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;
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);
--- /dev/null
+/*
+ *
+ * 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)
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;
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];
--- /dev/null
+/*
+ *
+ * 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)
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;
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;
}
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)
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
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);
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;
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;
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;
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)
{ }
};
+#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);
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)
{
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);
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;
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)
{
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);
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);
{
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",
.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;
}
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);
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,
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)
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 */
}
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);
}
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);
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
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;
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;
}
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);
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;
bnep_conndel(&session->dst_addr);
}
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
static int bnep_add_to_bridge(const char *devname, const char *bridge)
{
int ifindex;
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;
return err;
}
+#endif
static int bnep_del_from_bridge(const char *devname, const char *bridge)
{
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)
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) {
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)
failed_bridge:
bnep_del_from_bridge(iface, bridge);
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
failed_conn:
+#endif
bnep_conndel(addr);
return err;
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
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;
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';
if (nc->state == CONNECTED)
bnep_disconnect(nc->session);
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
bnep_free(nc->session);
nc->session = NULL;
-
+#endif
nc->state = DISCONNECTED;
}
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,
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);
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)
{
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;
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;
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)
{
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;
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)
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;
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,
{ 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
{ }
};
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,
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);
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012 Texas Instruments Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <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);
+}
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012 Texas Instruments Corporation
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+void imm_alert_register(struct btd_adapter *adapter);
+void imm_alert_unregister(struct btd_adapter *adapter);
+const char *imm_alert_get_level(struct btd_device *device);
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012 Texas Instruments Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <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);
+}
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012 Texas Instruments Corporation
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+void link_loss_register(struct btd_adapter *adapter);
+void link_loss_unregister(struct btd_adapter *adapter);
+const char *link_loss_get_alert_level(struct btd_device *device);
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ * Copyright (C) 2011 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <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)
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ * Copyright (C) 2011 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <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);
+}
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ * Copyright (C) 2011 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+int proximity_manager_init(GKeyFile *conf);
+void proximity_manager_exit(void);
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ * Copyright (C) 2011 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <errno.h>
+#include <fcntl.h>
+#include <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);
+}
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ * Copyright (C) 2011 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+struct enabled {
+ gboolean linkloss;
+ gboolean pathloss;
+ gboolean findme;
+};
+
+int monitor_register_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);
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ * Copyright (C) 2011 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <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);
+}
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ * Copyright (C) 2011 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#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
--- /dev/null
+/*
+ * 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)
--- /dev/null
+/*
+ * 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, ¶m, 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);
+}
--- /dev/null
+/*
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ * Copyright (C) 2011 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#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);
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 GSyC/LibreSoft, Universidad Rey Juan Carlos.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <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)
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ * Copyright (C) 2011 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <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)
#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
#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;
/* 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;
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 */
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 */
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 */
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;
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;
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);
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)
{
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);
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,
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;
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
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");
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)
adapter->devices = g_slist_append(adapter->devices, device);
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ adapter_print_devices(adapter);
+#endif
+
return device;
}
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)
{
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;
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)) {
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;
}
* 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;
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)
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,
}
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)
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
}
-static gboolean start_discovery_timeout(gpointer user_data)
+#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;
- struct mgmt_cp_start_service_discovery *sd_cp;
- uint8_t new_type;
+ const struct mgmt_cp_start_discovery *rp = param;
- DBG("");
+ if (!rp) {
+ error("Error ocurred in LEDiscovering, rp is NULL");
+ return;
+ }
- adapter->discovery_idle_timeout = 0;
+ DBG("status 0x%02x", status);
+ if (length < sizeof(*rp)) {
+ error("Wrong size of start discovery return parameters");
+ return;
+ }
- /* If we're doing filtered discovery, it must be quickly restarted */
- adapter->no_scan_restart_delay = !!adapter->current_discovery_filter;
+ DBG("Discovery Type 0x%02x", rp->type);
+ if (status == MGMT_STATUS_SUCCESS) {
+ adapter->discovery_type |= rp->type;
+ adapter->discovery_enable = 0x01;
- DBG("adapter->current_discovery_filter == %d",
- !!adapter->current_discovery_filter);
+ 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;
+
+ DBG("adapter->current_discovery_filter == %d",
+ !!adapter->current_discovery_filter);
new_type = get_scan_type(adapter);
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)
{
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)
{
*/
trigger_start_discovery(adapter, IDLE_DISCOV_TIMEOUT);
}
+#endif
static void discovering_callback(uint16_t index, uint16_t length,
const void *param, void *user_data)
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
* 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;
}
}
+#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)
{
trigger_passive_scanning(adapter);
}
}
+#endif
static int compare_sender(gconstpointer a, gconstpointer b)
{
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);
return false;
}
-static DBusMessage *start_discovery(DBusConnection *conn,
- DBusMessage *msg, void *user_data)
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+static void le_discovery_destroy(void *user_data)
{
- struct btd_adapter *adapter = user_data;
- const char *sender = dbus_message_get_sender(msg);
- struct watch_client *client;
- bool is_discovering;
+ struct watch_client *client = user_data;
+ struct btd_adapter *adapter = client->adapter;
- DBG("sender %s", sender);
+ DBG("owner %s", client->owner);
- if (!(adapter->current_settings & MGMT_SETTING_POWERED))
- return btd_error_not_ready(msg);
+ adapter->le_discovery_list = g_slist_remove(adapter->le_discovery_list,
+ client);
- is_discovering = get_discovery_client(adapter, sender, &client);
+ g_free(client->owner);
+ g_free(client);
/*
- * Every client can only start one discovery, if the client
- * already started a discovery then return an error.
+ * If there are other client discoveries in progress, then leave
+ * it active. If not, then make sure to stop the restart timeout.
*/
- if (is_discovering)
- return btd_error_busy(msg);
+ 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;
- /*
- * If there was pre-set filter, just reconnect it to discovery_list,
- * and trigger scan.
- */
- if (client) {
- adapter->set_filter_list = g_slist_remove(
- adapter->set_filter_list, client);
- adapter->discovery_list = g_slist_prepend(
- adapter->discovery_list, client);
- update_discovery_filter(adapter);
- return dbus_message_new_method_return(msg);
+ 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;
}
- client = g_new0(struct watch_client, 1);
+ if (adapter->temp_devices_timeout > 0) {
+ g_source_remove(adapter->temp_devices_timeout);
+ adapter->temp_devices_timeout = 0;
+ }
- client->adapter = adapter;
- client->owner = g_strdup(sender);
- client->discovery_filter = NULL;
- client->watch = g_dbus_add_disconnect_watch(dbus_conn, sender,
- discovery_disconnect, client,
- discovery_destroy);
- adapter->discovery_list = g_slist_prepend(adapter->discovery_list,
+ 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);
/*
- * Just trigger the discovery here. In case an already running
- * discovery in idle phase exists, it will be restarted right
- * away.
+ * 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.
*/
- update_discovery_filter(adapter);
+ if (adapter->le_discovery_list)
+ return;
- 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.
+ */
+ 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);
}
-static bool parse_uuids(DBusMessageIter *value, GSList **uuids)
+#ifdef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+static void addr_filter_params_free(gpointer data, gpointer user_data)
{
- DBusMessageIter arriter;
+ adapter_le_address_filter_params_t *params = data;
- if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_ARRAY)
- return false;
+ g_free(params);
+}
- dbus_message_iter_recurse(value, &arriter);
- while (dbus_message_iter_get_arg_type(&arriter) != DBUS_TYPE_INVALID) {
- bt_uuid_t uuid, u128;
- char uuidstr[MAX_LEN_UUID_STR + 1];
- char *uuid_param;
+static void uuid_filter_params_free(gpointer data, gpointer user_data)
+{
+ adapter_le_uuid_params_t *params = data;
- if (dbus_message_iter_get_arg_type(&arriter) !=
- DBUS_TYPE_STRING)
- return false;
+ g_free((char *)params->uuid);
+ g_free((char *)params->uuid_mask);
+ g_free(params);
+}
- dbus_message_iter_get_basic(&arriter, &uuid_param);
+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(¶ms->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, ¶ms->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(¶ms, 0, sizeof(params));
+
+ params.action = action;
+ params.index = filt_index;
+ params.feature = feat_seln;
+ params.filter_logic_type = filt_logic_type;
+ params.list_logic_type = list_logic_type;
+ params.delivery_mode = dely_mode;
+ params.rssi_high_threshold = rssi_high_thres;
+
+ if (params.delivery_mode == ON_FOUND) {
+ params.rssi_low_threshold = rssi_low_thres;
+ params.onfound_timeout = found_timeout;
+ params.onfound_timeout_cnt = found_timeout_cnt;
+ params.onlost_timeout = lost_timeout;
+ }
+
+#ifdef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+ if (ctlr_filter_support)
+#endif
+ err = adapter_le_set_scan_filter_params(¶ms);
+#ifdef TIZEN_FEATURE_PLATFROM_SCAN_FILTER
+ else
+ err = adapter_le_set_platform_scan_filter_params(adapter, ¶ms);
+#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)
+{
+ struct btd_adapter *adapter = user_data;
+ const char *sender = dbus_message_get_sender(msg);
+ struct watch_client *client;
+ bool is_discovering;
+
+ DBG("sender %s", sender);
+
+ if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+ return btd_error_not_ready(msg);
+
+ is_discovering = get_discovery_client(adapter, sender, &client);
+
+ /*
+ * Every client can only start one discovery, if the client
+ * already started a discovery then return an error.
+ */
+ if (is_discovering)
+ return btd_error_busy(msg);
+
+ /*
+ * If there was pre-set filter, just reconnect it to discovery_list,
+ * and trigger scan.
+ */
+ if (client) {
+ adapter->set_filter_list = g_slist_remove(
+ adapter->set_filter_list, client);
+ adapter->discovery_list = g_slist_prepend(
+ adapter->discovery_list, client);
+ update_discovery_filter(adapter);
+ return dbus_message_new_method_return(msg);
+ }
+
+ client = g_new0(struct watch_client, 1);
+
+ client->adapter = adapter;
+ client->owner = g_strdup(sender);
+ client->discovery_filter = NULL;
+ 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);
+
+#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
+ * away.
+ */
+ update_discovery_filter(adapter);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static bool parse_uuids(DBusMessageIter *value, GSList **uuids)
+{
+ DBusMessageIter arriter;
+
+ if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_ARRAY)
+ return false;
+
+ dbus_message_iter_recurse(value, &arriter);
+ while (dbus_message_iter_get_arg_type(&arriter) != DBUS_TYPE_INVALID) {
+ bt_uuid_t uuid, u128;
+ char uuidstr[MAX_LEN_UUID_STR + 1];
+ char *uuid_param;
+
+ if (dbus_message_iter_get_arg_type(&arriter) !=
+ DBUS_TYPE_STRING)
+ return false;
+
+ dbus_message_iter_get_basic(&arriter, &uuid_param);
if (bt_string_to_uuid(&uuid, uuid_param))
return false;
*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;
client = list->data;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ adapter->disc_type = BT_DISC_TYPE_BREDR_ONLY;
+#endif
cp.type = adapter->discovery_type;
/*
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);
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)
{
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,
break;
}
}
+#endif
memset(&cp, 0, sizeof(cp));
cp.val = mode;
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;
}
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);
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)
{
if (uuid == NULL)
continue;
- g_hash_table_add(uuids, uuid);
+ g_hash_table_add(uuids, uuid);
+ }
+
+ /* GATT services */
+ db = btd_gatt_database_get_db(adapter->database);
+ if (db)
+ gatt_db_foreach_service(db, NULL, add_gatt_uuid, uuids);
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_TYPE_STRING_AS_STRING, &entry);
+ g_hash_table_foreach(uuids, iter_append_uuid, &entry);
+ dbus_message_iter_close_container(iter, &entry);
+
+ g_hash_table_destroy(uuids);
+
+ return TRUE;
+}
+
+static gboolean property_exists_modalias(const GDBusPropertyTable *property,
+ void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+
+ return adapter->modalias ? TRUE : FALSE;
+}
+
+static gboolean property_get_modalias(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ const char *str = adapter->modalias ? : "";
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str);
+
+ return TRUE;
+}
+
+static int device_path_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct btd_device *device = a;
+ const char *path = b;
+ const char *dev_path = device_get_path(device);
+
+ 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);
}
+}
- /* GATT services */
- db = btd_gatt_database_get_db(adapter->database);
- if (db)
- gatt_db_foreach_service(db, NULL, add_gatt_uuid, uuids);
+static bool initialize_6lowpan(struct btd_adapter *adapter)
+{
+ struct mgmt_cp_enable_6lowpan cp;
- dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
- DBUS_TYPE_STRING_AS_STRING, &entry);
- g_hash_table_foreach(uuids, iter_append_uuid, &entry);
- dbus_message_iter_close_container(iter, &entry);
+ memset(&cp, 0, sizeof(cp));
- g_hash_table_destroy(uuids);
+ 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;
- return TRUE;
+ error("Failed to initialize BT 6Lowpan for index %u",
+ adapter->dev_id);
+ return false;
}
-static gboolean property_exists_modalias(const GDBusPropertyTable *property,
- void *user_data)
+static DBusMessage *adapter_initialize_ipsp(DBusConnection *conn,
+ DBusMessage *msg, void *data)
{
- struct btd_adapter *adapter = user_data;
+ struct btd_adapter *adapter = data;
+ dbus_bool_t err;
- return adapter->modalias ? TRUE : FALSE;
-}
+ DBG("Initialize IPSP");
-static gboolean property_get_modalias(const GDBusPropertyTable *property,
- DBusMessageIter *iter, void *user_data)
-{
- struct btd_adapter *adapter = user_data;
- const char *str = adapter->modalias ? : "";
+ if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+ return btd_error_not_ready(msg);
- dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str);
+ if (adapter->ipsp_intialized)
+ return btd_error_already_exists(msg);
- return TRUE;
+ /* 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 int device_path_cmp(gconstpointer a, gconstpointer b)
+static DBusMessage *adapter_deinitialize_ipsp(DBusConnection *conn,
+ DBusMessage *msg, void *data)
{
- const struct btd_device *device = a;
- const char *path = b;
- const char *dev_path = device_get_path(device);
+ struct btd_adapter *adapter = data;
+ dbus_bool_t err;
- return strcasecmp(dev_path, path);
+ 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)
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 },
{ "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
+
{ }
};
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);
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;
return 0;
}
+#if 0
static void set_privacy_complete(uint8_t status, uint16_t length,
const void *param, void *user_data)
{
return -1;
}
+#endif
static void load_link_keys_complete(uint8_t status, uint16_t length,
const void *param, void *user_data)
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;
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;
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);
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;
}
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, ¶m->bdaddr);
+ }
+}
+#endif
+
btd_device_set_temporary(device, false);
adapter->devices = g_slist_append(adapter->devices, device);
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);
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();
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)
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);
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;
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;
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;
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) {
/*
* 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);
}
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);
/*
* 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);
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);
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);
* 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))
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
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)
{
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 */
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
}
}
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
ADAPTER_INTERFACE, "Class");
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ advertiser_cleanup(adapter);
+#endif
g_dbus_emit_property_changed(dbus_conn, adapter->path,
ADAPTER_INTERFACE, "Powered");
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;
bacpy(&cp.addr.bdaddr, bdaddr);
cp.addr.type = BDADDR_BREDR;
- id = mgmt_reply(adapter->mgmt, MGMT_OP_PIN_CODE_NEG_REPLY,
- adapter->dev_id, sizeof(cp), &cp,
- NULL, NULL, NULL);
- } else {
- struct mgmt_cp_pin_code_reply cp;
+ id = mgmt_reply(adapter->mgmt, MGMT_OP_PIN_CODE_NEG_REPLY,
+ adapter->dev_id, sizeof(cp), &cp,
+ NULL, NULL, NULL);
+ } else {
+ struct mgmt_cp_pin_code_reply cp;
+
+ if (pin_len > 16)
+ return -EINVAL;
+
+ memset(&cp, 0, sizeof(cp));
+ bacpy(&cp.addr.bdaddr, bdaddr);
+ cp.addr.type = BDADDR_BREDR;
+ cp.pin_len = pin_len;
+ memcpy(cp.pin_code, pin, pin_len);
+
+ /* Since a pincode was requested, update the starting time to
+ * the point where the pincode is provided. */
+ device = btd_adapter_find_device(adapter, bdaddr, BDADDR_BREDR);
+ device_bonding_restart_timer(device);
+
+ id = mgmt_reply(adapter->mgmt, MGMT_OP_PIN_CODE_REPLY,
+ adapter->dev_id, sizeof(cp), &cp,
+ pincode_reply_complete, device, NULL);
+ }
+
+ if (id == 0)
+ return -EIO;
+
+ return 0;
+}
+
+int btd_adapter_confirm_reply(struct btd_adapter *adapter,
+ const bdaddr_t *bdaddr, uint8_t bdaddr_type,
+ gboolean success)
+{
+ struct mgmt_cp_user_confirm_reply cp;
+ uint16_t opcode;
+ char addr[18];
+
+ ba2str(bdaddr, addr);
+ DBG("hci%u addr %s success %d", adapter->dev_id, addr, success);
+
+ if (success)
+ opcode = MGMT_OP_USER_CONFIRM_REPLY;
+ else
+ opcode = MGMT_OP_USER_CONFIRM_NEG_REPLY;
+
+ memset(&cp, 0, sizeof(cp));
+ bacpy(&cp.addr.bdaddr, bdaddr);
+ cp.addr.type = bdaddr_type;
+
+ if (mgmt_reply(adapter->mgmt, opcode, adapter->dev_id, sizeof(cp), &cp,
+ NULL, NULL, NULL) > 0)
+ return 0;
+
+ return -EIO;
+}
+
+static void user_confirm_request_callback(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_user_confirm_request *ev = param;
+ struct btd_adapter *adapter = user_data;
+ struct btd_device *device;
+ char addr[18];
+ int err;
+
+ if (length < sizeof(*ev)) {
+ btd_error(adapter->dev_id,
+ "Too small user confirm request event");
+ return;
+ }
+
+ ba2str(&ev->addr.bdaddr, addr);
+ DBG("hci%u %s confirm_hint %u", adapter->dev_id, addr,
+ ev->confirm_hint);
+ device = btd_adapter_get_device(adapter, &ev->addr.bdaddr,
+ ev->addr.type);
+ if (!device) {
+ btd_error(adapter->dev_id,
+ "Unable to get device object for %s", addr);
+ 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) {
+ btd_error(adapter->dev_id,
+ "device_confirm_passkey: %s", strerror(-err));
+ btd_adapter_confirm_reply(adapter, &ev->addr.bdaddr,
+ ev->addr.type, FALSE);
+ }
+}
+
+int btd_adapter_passkey_reply(struct btd_adapter *adapter,
+ const bdaddr_t *bdaddr, uint8_t bdaddr_type,
+ uint32_t passkey)
+{
+ unsigned int id;
+ char addr[18];
+
+ ba2str(bdaddr, addr);
+ DBG("hci%u addr %s passkey %06u", adapter->dev_id, addr, passkey);
+
+ if (passkey == INVALID_PASSKEY) {
+ struct mgmt_cp_user_passkey_neg_reply cp;
+
+ memset(&cp, 0, sizeof(cp));
+ bacpy(&cp.addr.bdaddr, bdaddr);
+ cp.addr.type = bdaddr_type;
+
+ id = mgmt_reply(adapter->mgmt, MGMT_OP_USER_PASSKEY_NEG_REPLY,
+ adapter->dev_id, sizeof(cp), &cp,
+ NULL, NULL, NULL);
+ } else {
+ struct mgmt_cp_user_passkey_reply cp;
+
+ memset(&cp, 0, sizeof(cp));
+ bacpy(&cp.addr.bdaddr, bdaddr);
+ cp.addr.type = bdaddr_type;
+ cp.passkey = htobl(passkey);
+
+ id = mgmt_reply(adapter->mgmt, MGMT_OP_USER_PASSKEY_REPLY,
+ adapter->dev_id, sizeof(cp), &cp,
+ NULL, NULL, NULL);
+ }
+
+ if (id == 0)
+ return -EIO;
+
+ return 0;
+}
+
+static void user_passkey_request_callback(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_user_passkey_request *ev = param;
+ struct btd_adapter *adapter = user_data;
+ struct btd_device *device;
+ char addr[18];
+ int err;
+
+ if (length < sizeof(*ev)) {
+ btd_error(adapter->dev_id, "Too small passkey request 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) {
+ btd_error(adapter->dev_id,
+ "Unable to get device object for %s", addr);
+ 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,
+ "device_request_passkey: %s", strerror(-err));
+ btd_adapter_passkey_reply(adapter, &ev->addr.bdaddr,
+ ev->addr.type, INVALID_PASSKEY);
+ }
+}
+
+static void user_passkey_notify_callback(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
+{
+ const struct mgmt_ev_passkey_notify *ev = param;
+ struct btd_adapter *adapter = user_data;
+ struct btd_device *device;
+ uint32_t passkey;
+ char addr[18];
+ int err;
+
+ if (length < sizeof(*ev)) {
+ btd_error(adapter->dev_id, "Too small passkey notify 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) {
+ btd_error(adapter->dev_id,
+ "Unable to get device object for %s", addr);
+ return;
+ }
+
+ passkey = get_le32(&ev->passkey);
+
+ DBG("passkey %06u entered %u", passkey, ev->entered);
+
+ err = device_notify_passkey(device, ev->addr.type, passkey,
+ ev->entered);
+ if (err < 0)
+ btd_error(adapter->dev_id,
+ "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);
- if (pin_len > 16)
- return -EINVAL;
+ bt_addr = (char *)&addr;
+ link_type = ev->link_type;
- memset(&cp, 0, sizeof(cp));
- bacpy(&cp.addr.bdaddr, bdaddr);
- cp.addr.type = BDADDR_BREDR;
- cp.pin_len = pin_len;
- memcpy(cp.pin_code, pin, pin_len);
+ 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);
+}
- /* Since a pincode was requested, update the starting time to
- * the point where the pincode is provided. */
- device = btd_adapter_find_device(adapter, bdaddr, BDADDR_BREDR);
- device_bonding_restart_timer(device);
+void adapter_check_version(struct btd_adapter *adapter, uint8_t hci_ver)
+{
+ char *ver;
- id = mgmt_reply(adapter->mgmt, MGMT_OP_PIN_CODE_REPLY,
- adapter->dev_id, sizeof(cp), &cp,
- pincode_reply_complete, device, NULL);
+ 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 (id == 0)
- return -EIO;
+ if (adapter->version)
+ g_free(adapter->version);
- return 0;
+ adapter->version = g_strdup(ver);
}
-int btd_adapter_confirm_reply(struct btd_adapter *adapter,
- const bdaddr_t *bdaddr, uint8_t bdaddr_type,
- gboolean success)
+static void hardware_error_callback(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
{
- struct mgmt_cp_user_confirm_reply cp;
- uint16_t opcode;
- char addr[18];
-
- ba2str(bdaddr, addr);
- DBG("hci%u addr %s success %d", adapter->dev_id, addr, success);
+ const struct mgmt_ev_hardware_error *ev = param;
+ struct btd_adapter *adapter = user_data;
- if (success)
- opcode = MGMT_OP_USER_CONFIRM_REPLY;
- else
- opcode = MGMT_OP_USER_CONFIRM_NEG_REPLY;
+ if (length < sizeof(*ev)) {
+ error("Too small Hardware error event");
+ return;
+ }
- memset(&cp, 0, sizeof(cp));
- bacpy(&cp.addr.bdaddr, bdaddr);
- cp.addr.type = bdaddr_type;
+ error("Hardware error occurred : %d", ev->error_code);
+ g_dbus_emit_signal(dbus_conn, adapter->path,
+ ADAPTER_INTERFACE, "HardwareError",
+ DBUS_TYPE_INVALID);
+}
- if (mgmt_reply(adapter->mgmt, opcode, adapter->dev_id, sizeof(cp), &cp,
- NULL, NULL, NULL) > 0)
- return 0;
+static void tx_timeout_error_callback(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
- return -EIO;
+ error("Tx Timeout error occurred");
+ g_dbus_emit_signal(dbus_conn, adapter->path,
+ ADAPTER_INTERFACE, "TxTimeoutError",
+ DBUS_TYPE_INVALID);
}
-static void user_confirm_request_callback(uint16_t index, uint16_t length,
- const void *param, void *user_data)
+static void device_name_update_callback(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
{
- const struct mgmt_ev_user_confirm_request *ev = param;
+ const struct mgmt_ev_device_name_update *ev = param;
struct btd_adapter *adapter = user_data;
struct btd_device *device;
char addr[18];
- int err;
+ const uint8_t *eir_name;
+ struct eir_data eir_data;
if (length < sizeof(*ev)) {
- btd_error(adapter->dev_id,
- "Too small user confirm request event");
+ error("Name update error event");
return;
}
ba2str(&ev->addr.bdaddr, addr);
- DBG("hci%u %s confirm_hint %u", adapter->dev_id, addr,
- ev->confirm_hint);
+ DBG("hci%u %s", index, addr);
+
device = btd_adapter_get_device(adapter, &ev->addr.bdaddr,
ev->addr.type);
if (!device) {
- btd_error(adapter->dev_id,
- "Unable to get device object for %s", addr);
+ error("Unable to get device object for %s", addr);
return;
}
- err = device_confirm_passkey(device, ev->addr.type, btohl(ev->value),
- ev->confirm_hint);
- if (err < 0) {
- btd_error(adapter->dev_id,
- "device_confirm_passkey: %s", strerror(-err));
- btd_adapter_confirm_reply(adapter, &ev->addr.bdaddr,
- ev->addr.type, FALSE);
- }
+ 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);
}
-int btd_adapter_passkey_reply(struct btd_adapter *adapter,
- const bdaddr_t *bdaddr, uint8_t bdaddr_type,
- uint32_t passkey)
+static void multi_adv_state_change_callback(uint16_t index, uint16_t length,
+ const void *param, void *user_data)
{
- unsigned int id;
- char addr[18];
-
- ba2str(bdaddr, addr);
- DBG("hci%u addr %s passkey %06u", adapter->dev_id, addr, passkey);
+ const struct mgmt_ev_vendor_specific_multi_adv_state_changed *ev = param;
+ struct btd_adapter *adapter = user_data;
- if (passkey == INVALID_PASSKEY) {
- struct mgmt_cp_user_passkey_neg_reply cp;
+ if (length < sizeof(*ev)) {
+ error("Too small adv state change event");
+ return;
+ }
- memset(&cp, 0, sizeof(cp));
- bacpy(&cp.addr.bdaddr, bdaddr);
- cp.addr.type = bdaddr_type;
+ DBG("adv id %d, state change reason %d, connection_handle %x",
+ ev->adv_instance, ev->state_change_reason, ev->connection_handle);
- id = mgmt_reply(adapter->mgmt, MGMT_OP_USER_PASSKEY_NEG_REPLY,
- adapter->dev_id, sizeof(cp), &cp,
- NULL, NULL, NULL);
- } else {
- struct mgmt_cp_user_passkey_reply cp;
+ 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);
+}
- memset(&cp, 0, sizeof(cp));
- bacpy(&cp.addr.bdaddr, bdaddr);
- cp.addr.type = bdaddr_type;
- cp.passkey = htobl(passkey);
+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;
- id = mgmt_reply(adapter->mgmt, MGMT_OP_USER_PASSKEY_REPLY,
- adapter->dev_id, sizeof(cp), &cp,
- NULL, NULL, NULL);
+ if (length < sizeof(*ev)) {
+ error("Too small le conn update completed event");
+ return;
}
- if (id == 0)
- return -EIO;
-
- return 0;
+ 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 user_passkey_request_callback(uint16_t index, uint16_t length,
+static void bt_6lowpan_conn_state_change_callback(uint16_t index, uint16_t length,
const void *param, void *user_data)
{
- const struct mgmt_ev_user_passkey_request *ev = param;
+ const struct mgmt_ev_6lowpan_conn_state_changed *ev = param;
struct btd_adapter *adapter = user_data;
struct btd_device *device;
char addr[18];
- int err;
+ gboolean connected = 0;
if (length < sizeof(*ev)) {
- btd_error(adapter->dev_id, "Too small passkey request event");
+ btd_error(adapter->dev_id,
+ "Too small device connected event");
return;
}
ba2str(&ev->addr.bdaddr, addr);
- DBG("hci%u %s", index, addr);
- device = btd_adapter_get_device(adapter, &ev->addr.bdaddr,
+ 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);
+ "Unable to get device object for %s", addr);
return;
}
- err = device_request_passkey(device, ev->addr.type);
- if (err < 0) {
- btd_error(adapter->dev_id,
- "device_request_passkey: %s", strerror(-err));
- btd_adapter_passkey_reply(adapter, &ev->addr.bdaddr,
- ev->addr.type, INVALID_PASSKEY);
- }
+ if (ev->connected)
+ connected = TRUE;
+ else
+ connected = FALSE;
+
+ device_set_ipsp_connected(device, connected, ev->ifname);
}
-static void user_passkey_notify_callback(uint16_t index, uint16_t length,
+static void bt_le_data_length_changed_callback(uint16_t index, uint16_t length,
const void *param, void *user_data)
{
- const struct mgmt_ev_passkey_notify *ev = param;
+ const struct mgmt_ev_le_data_length_changed *ev = param;
struct btd_adapter *adapter = user_data;
struct btd_device *device;
- uint32_t passkey;
char addr[18];
- int err;
if (length < sizeof(*ev)) {
- btd_error(adapter->dev_id, "Too small passkey notify event");
+ btd_error(adapter->dev_id,
+ "Too small data length changed event");
return;
}
ba2str(&ev->addr.bdaddr, addr);
- DBG("hci%u %s", index, addr);
- device = btd_adapter_get_device(adapter, &ev->addr.bdaddr,
+ 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);
+ "Unable to get device object for %s", addr);
return;
}
- passkey = get_le32(&ev->passkey);
-
- DBG("passkey %06u entered %u", passkey, ev->entered);
-
- err = device_notify_passkey(device, ev->addr.type, passkey,
- ev->entered);
- if (err < 0)
- btd_error(adapter->dev_id,
- "device_notify_passkey: %s", strerror(-err));
+ 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)
{
/* 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));
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);
}
return -EBUSY;
}
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
suspend_discovery(adapter);
-
+#endif
return adapter_bonding_attempt(adapter, bdaddr, addr_type, io_cap);
}
{
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);
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,
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);
}
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");
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);
}
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");
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);
}
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");
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);
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);
}
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,
if (!ev->store_hint)
return;
- store_conn_param(adapter, &ev->addr.bdaddr, ev->addr.type,
- ev->min_interval, ev->max_interval,
- ev->latency, ev->timeout);
+#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)
+{
+ struct mgmt_cp_set_io_capability cp;
+
+ memset(&cp, 0, sizeof(cp));
+ cp.io_capability = io_cap;
+
+ if (mgmt_send(adapter->mgmt, MGMT_OP_SET_IO_CAPABILITY,
+ adapter->dev_id, sizeof(cp), &cp,
+ NULL, NULL, NULL) > 0)
+ return 0;
+
+ return -EIO;
+}
+
+int btd_adapter_add_remote_oob_data(struct btd_adapter *adapter,
+ const bdaddr_t *bdaddr,
+ uint8_t *hash, uint8_t *randomizer)
+{
+ struct mgmt_cp_add_remote_oob_data cp;
+ char addr[18];
+
+ ba2str(bdaddr, addr);
+ DBG("hci%d bdaddr %s", adapter->dev_id, addr);
+
+ memset(&cp, 0, sizeof(cp));
+ bacpy(&cp.addr.bdaddr, bdaddr);
+ memcpy(cp.hash192, hash, 16);
+
+ if (randomizer)
+ memcpy(cp.rand192, randomizer, 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 adapter_set_io_capability(struct btd_adapter *adapter, uint8_t io_cap)
+int btd_adapter_remove_remote_oob_data(struct btd_adapter *adapter,
+ const bdaddr_t *bdaddr)
{
- struct mgmt_cp_set_io_capability cp;
+ struct mgmt_cp_remove_remote_oob_data cp;
+ char addr[18];
+
+ ba2str(bdaddr, addr);
+ DBG("hci%d bdaddr %s", adapter->dev_id, addr);
memset(&cp, 0, sizeof(cp));
- cp.io_capability = io_cap;
+ bacpy(&cp.addr.bdaddr, bdaddr);
- if (mgmt_send(adapter->mgmt, MGMT_OP_SET_IO_CAPABILITY,
+ 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;
}
-int btd_adapter_add_remote_oob_data(struct btd_adapter *adapter,
- const bdaddr_t *bdaddr,
- uint8_t *hash, uint8_t *randomizer)
+#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", adapter->dev_id, addr);
+ DBG("hci%d bdaddr %s type %d", adapter->dev_id, addr, bdaddr_type);
memset(&cp, 0, sizeof(cp));
bacpy(&cp.addr.bdaddr, bdaddr);
- memcpy(cp.hash192, hash, 16);
+ cp.addr.type = bdaddr_type;
- if (randomizer)
- memcpy(cp.rand192, randomizer, 16);
+ 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,
return -EIO;
}
-int btd_adapter_remove_remote_oob_data(struct btd_adapter *adapter,
- const bdaddr_t *bdaddr)
+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", adapter->dev_id, 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,
return -EIO;
}
+#endif
bool btd_adapter_ssp_enabled(struct btd_adapter *adapter)
{
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,
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;
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,
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)
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,
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();
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)
{
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)
{
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);
(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");
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,
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));
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;
#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,
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);
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);
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);
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
--- /dev/null
+#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, ¶ms->broadcaster_addr);
+ cp.bdaddr_type = params->bdaddr_type;
+
+ ret = send_vsc_command(OCF_BCM_LE_SCAN_FILTER, (uint8_t *) &cp, sizeof(cp),
+ (uint8_t *) &rp, sizeof(rp));
+
+ if (ret < 0)
+ return FALSE;
+
+ if (HCI_SUCCESS != rp.status) {
+ DBG("Fail to send VSC :: sub[%x] - status [%x]", rp.subcode, rp.status);
+ return FALSE;
+ }
+
+ DBG("Scan Filter VSC :: sub[%x] - status [%x] Action [%x] Available space [%x]",
+ rp.subcode, rp.status, rp.action, rp.available_space);
+
+ return TRUE;
+}
+
+gboolean adapter_le_service_uuid_scan_filtering(gboolean is_solicited,
+ adapter_le_uuid_params_t *params)
+{
+ int ret;
+ adapter_le_vsc_cp_service_uuid_scan_filter cp;
+ adapter_le_vsc_rp_apcf_set_scan_filter rp;
+ uint8_t *p = cp.data;
+ int cp_len = UUID_SCAN_FILTER_HEADER_SIZE;
+
+ DBG("");
+
+ memset(&cp, 0, sizeof(cp));
+ cp.subcode = (is_solicited) ? SUB_CMD_LE_META_PF_SOL_UUID :
+ SUB_CMD_LE_META_PF_UUID;
+
+ cp.action= params ->action;
+ cp.filter_index = params->filter_index;
+
+ 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(¶ms.broadcaster_addr, &bd_addr);
+ params.bdaddr_type = addr_type;
+
+ ret = adapter_le_service_address_scan_filtering(¶ms);
+ break;
+ }
+
+ case TYPE_SERVICE_UUID:
+ case TYPE_SOLICIT_UUID: {
+ adapter_le_uuid_params_t params;
+ gboolean is_solicited = (filt_type == TYPE_SOLICIT_UUID) ? TRUE : FALSE;
+
+ if (uuid_len != UUID_16_LEN && uuid_len != UUID_32_LEN
+ && uuid_len != UUID_128_LEN) {
+ DBG("UUID length error");
+ return FALSE;
+ }
+
+ if (uuid_len != uuid_mask_len) {
+ DBG("Both UUID and UUID_MASK length shoule be samed");
+ return FALSE;
+ }
+
+ params.action = action;
+ params.filter_index = filter_index;
+ params.uuid = p_uuid;
+ params.uuid_mask = p_uuid_mask;
+ params.uuid_len = uuid_len;
+
+ ret = adapter_le_service_uuid_scan_filtering(is_solicited, ¶ms);
+ break;
+ }
+
+ case TYPE_LOCAL_NAME: {
+ adapter_le_local_name_params_t params;
+
+ params.action = action;
+ params.filter_index = filter_index;
+ params.local_name = string;
+ params.name_len = strlen(string);
+ ret = adapter_le_local_name_scan_filtering(¶ms);
+ break;
+ }
+
+ case TYPE_MANUFACTURER_DATA: {
+ adapter_le_manf_data_params_t params;
+
+ if (data_len == 0 || (data_len != mask_len)) {
+ DBG("parameter length error");
+ return FALSE;
+ }
+
+ params.action = action;
+ params.filter_index = filter_index;
+ params.company_id = company_id;
+ params.company_id_mask = company_id_mask;
+ params.man_data = p_data;
+ params.man_data_mask = p_mask;
+ params.man_data_len = data_len;
+
+ ret = adapter_le_manf_data_scan_filtering(¶ms);
+ break;
+ }
+
+ case TYPE_SERVICE_DATA: {
+ adapter_le_service_data_params_t params;
+
+ if (data_len == 0 || (data_len != mask_len)) {
+ DBG("parameter length error");
+ return FALSE;
+ }
+
+ params.action = action;
+ params.filter_index = filter_index;
+ params.service_data = p_data;
+ params.service_data_mask = p_mask;
+ params.service_data_len = data_len;
+
+ ret = adapter_le_service_data_scan_filtering(¶ms);
+ break;
+ }
+
+ default:
+ DBG("filter_type error");
+ ret = FALSE;
+ }
+
+ return ret;
+}
+
+gboolean adapter_le_clear_scan_filter_data(int client_if, int filter_index)
+{
+ int ret;
+ adapter_le_vsc_cp_service_data_scan_filter cp;
+ adapter_le_vsc_rp_apcf_set_scan_filter rp;
+
+ DBG("");
+
+ memset(&cp, 0, sizeof(cp));
+ cp.subcode = SUB_CMD_LE_META_PF_FEAT_SEL;
+ cp.action= 0x02; // (Add - 0x00, Delete - 0x01, Clear - 0x02)
+ cp.filter_index = filter_index;
+
+ ret = send_vsc_command(OCF_BCM_LE_SCAN_FILTER, (uint8_t *) &cp, sizeof(cp),
+ (uint8_t *) &rp, sizeof(rp));
+
+ if (ret < 0)
+ return FALSE;
+
+ if (HCI_SUCCESS != rp.status) {
+ DBG("Fail to send VSC :: sub[%x] - status [%x] Available space [%x]",
+ rp.subcode, rp.status, rp.available_space);
+ return FALSE;
+ }
+
+ DBG("Scan Filter VSC :: sub[%x] - status [%x] Action [%x] Available space [%x]",
+ rp.subcode, rp.status, rp.action, rp.available_space);
+ return TRUE;
+}
+
+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 */
--- /dev/null
+#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 */
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 Nokia Corporation
+ * Copyright (C) 2011 Marcel Holtmann <marcel@holtmann.org>
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+typedef void (*attio_connect_cb) (GAttrib *attrib, gpointer user_data);
+typedef void (*attio_disconnect_cb) (gpointer user_data);
+
+guint btd_device_add_attio_callback(struct btd_device *device,
+ attio_connect_cb cfunc,
+ attio_disconnect_cb dcfunc,
+ gpointer user_data);
+
+gboolean btd_device_remove_attio_callback(struct btd_device *device, guint id);
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)
{
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];
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;
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;
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);
}
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);
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);
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) {
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)
{
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
<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"/>
<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>
[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
#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
#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;
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;
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;
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;
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[] = {
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);
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;
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);
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);
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);
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");
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);
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)
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);
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);
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)
{
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;
ptr = device->name;
} else {
ba2str(&device->bdaddr, dstaddr);
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
g_strdelimit(dstaddr, ":", '-');
+#endif
ptr = dstaddr;
}
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)
{
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)
{
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)
connected = FALSE;
dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &connected);
+#endif
return TRUE;
}
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;
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) {
}
}
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (dev->pending == NULL)
+ return;
+#endif
pending = dev->pending->data;
l = find_service_with_profile(dev->pending, profile);
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);
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);
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;
}
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;
}
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;
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);
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);
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)
if (err == 0)
return NULL;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ if (dev->disconnect)
+#endif
dbus_message_unref(dev->disconnect);
dev->disconnect = NULL;
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();
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
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);
}
{
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)
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);
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);
* 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);
err = adapter_create_bonding(adapter, &device->bdaddr,
BDADDR_BREDR, io_cap);
}
+#endif
if (err < 0) {
bonding_request_free(device->bonding);
"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");
if (device->authr)
device_cancel_authentication(device, FALSE);
- reply = new_authentication_return(bonding->msg, status);
- g_dbus_send_message(dbus_conn, reply);
+ reply = new_authentication_return(bonding->msg, status);
+ g_dbus_send_message(dbus_conn, reply);
+
+ bonding_request_cancel(bonding);
+ bonding_request_free(bonding);
+}
+
+static DBusMessage *cancel_pairing(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct btd_device *device = data;
+ struct bonding_req *req = device->bonding;
+
+ DBG("");
+
+ if (!req)
+ return btd_error_does_not_exist(msg);
+
+ device_cancel_bonding(device, MGMT_STATUS_CANCELLED);
+
+ 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, ¶m.min,
+ DBUS_TYPE_UINT32, ¶m.max,
+ DBUS_TYPE_UINT32, ¶m.latency,
+ DBUS_TYPE_UINT32, ¶m.to_multiplier,
+ DBUS_TYPE_INVALID)) {
+ error("Invalid args");
+ return btd_error_invalid_args(msg);
+ }
+
+ if (setsockopt(fd, SOL_BLUETOOTH, BT_LE_CONN_PARAM,
+ ¶m, 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);
- bonding_request_cancel(bonding);
- bonding_request_free(bonding);
+ return reply;
}
-static DBusMessage *cancel_pairing(DBusConnection *conn, DBusMessage *msg,
- void *data)
+void device_set_conn_update_state(struct btd_device *device, bool state)
{
- struct btd_device *device = data;
- struct bonding_req *req = device->bonding;
-
- DBG("");
-
- if (!req)
- return btd_error_does_not_exist(msg);
+ if (!device)
+ return;
- device_cancel_bonding(device, MGMT_STATUS_CANCELLED);
+ device->pending_conn_update = state;
+}
- return dbus_message_new_method_return(msg);
+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) },
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
{ }
};
{ "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,
{ }
};
+#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;
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;
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;
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,
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.
*/
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);
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)
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);
}
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;
}
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,
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);
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)
{
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);
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;
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)
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);
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);
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;
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;
*/
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)
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)
{
/* 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;
}
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;
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);
g_dbus_emit_property_changed(dbus_conn, req->device->path,
DEVICE_INTERFACE, "UUIDs");
-send_reply:
+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);
}
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;
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;
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,
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)
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;
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) {
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");
return false;
}
}
+#endif
dev->att_mtu = MIN(mtu, BT_ATT_MAX_LE_MTU);
attrib = g_attrib_new(io, BT_ATT_DEFAULT_LE_MTU, false);
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);
*/
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;
}
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;
uuid_t uuid;
int err;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ DBG("");
+#endif
req = browse_request_new(device, msg);
if (!req)
return -EBUSY;
&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;
}
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;
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)
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;
device->rssi = rssi;
}
+#endif
g_dbus_emit_property_changed(dbus_conn, device->path,
DEVICE_INTERFACE, "RSSI");
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);
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;
}
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);
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;
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;
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);
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)
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();
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)
{
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)
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;
{
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
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,
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);
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);
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);
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);
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);
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,
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;
g_strstrip(utf8_name);
return g_strdup(utf8_name);
+#endif
}
static void eir_parse_msd(struct eir_data *eir, const uint8_t *data,
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;
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);
struct queue *services;
struct queue *all_notify_clients;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ guint wait_charcs_id;
+#endif
};
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);
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;
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;
}
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)
{
}
+#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)
{
* - 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,
if (chrc->write_op)
return NULL;
}
+#endif
if (chrc->props & BT_GATT_CHRC_PROP_WRITE) {
uint16_t mtu;
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;
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)
{
* 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,
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
{ }
};
{ "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,
{ }
};
+#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;
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);
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 "
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
{ }
};
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 "
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;
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:
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;
}
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)
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);
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)
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)
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
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;
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;
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();
{
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);
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);
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,
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) {
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;
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;
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;
}
g_io_channel_unref(io);
+
return true;
+#endif
}
static void gatt_ccc_read_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 *
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(¬ify_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,
+ ¬ify_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(¬ify_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, ¬ify_indicate);
}
+#endif
static void send_notification_to_device(void *data, void *user_data)
{
return op;
}
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
static void append_options(DBusMessageIter *iter, void *user_data)
{
struct pending_op *op = 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,
append_options(&dict, op);
dbus_message_iter_close_container(iter, &dict);
+#endif
}
static struct pending_op *send_read(struct btd_device *device,
{
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
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);
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, ¬ify_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;
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,
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)
{
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;
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;
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;
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;
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);
"ControllerMode",
"MultiProfile",
"Privacy",
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ "EnableLEPrivacy",
+#endif
};
GKeyFile *btd_get_main_conf(void)
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)
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,
--- /dev/null
+[General]
+
+# Default adaper name
+# %h - substituted for hostname
+# %d - substituted for adapter id
+# Defaults to 'BlueZ'
+#Name = %h-%d
+
+# Default device class. Only the major and minor device class bits are
+# considered. Defaults to '0x000000'.
+#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=
+
--- /dev/null
+[General]
+
+# Default adaper name
+# %h - substituted for hostname
+# %d - substituted for adapter id
+# Defaults to 'BlueZ'
+#Name = %h-%d
+
+# Default device class. Only the major and minor device class bits are
+# considered. Defaults to '0x000000'.
+#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=
+
--- /dev/null
+[General]
+
+# Default adaper name
+# %h - substituted for hostname
+# %d - substituted for adapter id
+# Defaults to 'BlueZ'
+#Name = %h-%d
+
+# Default device class. Only the major and minor device class bits are
+# considered. Defaults to '0x000000'.
+#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=
+
--- /dev/null
+[General]
+
+# Default adaper name
+# %h - substituted for hostname
+# %d - substituted for adapter id
+# Defaults to 'BlueZ'
+#Name = %h-%d
+
+# Default device class. Only the major and minor device class bits are
+# considered. Defaults to '0x000000'.
+#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
+
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 ST-Ericsson SA
+ *
+ * Author: Szymon Janc <szymon.janc@tieto.com> for ST-Ericsson
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#include "adapter.h"
+#include "oob.h"
+
+static oob_read_cb_t local_oob_read_cb = NULL;
+
+void oob_register_cb(oob_read_cb_t cb)
+{
+ local_oob_read_cb = cb;
+}
+
+#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);
+}
--- /dev/null
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2011 ST-Ericsson SA
+ *
+ * Author: Szymon Janc <szymon.janc@tieto.com> for ST-Ericsson
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+typedef void (*oob_read_cb_t) (struct btd_adapter *adapter, uint8_t *hash,
+ uint8_t *randomizer);
+
+void oob_register_cb(oob_read_cb_t cb);
+#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
#define BTD_PROFILE_PSM_AUTO -1
#define BTD_PROFILE_CHAN_AUTO -1
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+#define HID_DEVICE_INTR_PSM 17
+#define HID_DEVICE_CTRL_PSM 19
+#endif
+
#define HFP_HF_RECORD \
"<?xml version=\"1.0\" encoding=\"UTF-8\" ?> \
<record> \
</attribute> \
</record>"
+#ifdef TIZEN_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> \
<text value=\"%s\" /> \
</attribute> \
</record>"
+#endif
#define DUN_RECORD \
"<?xml version=\"1.0\" encoding=\"UTF-8\" ?> \
</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> \
<text value=\"%s\" /> \
</attribute> \
</record>"
+#endif
#define FTP_RECORD \
"<?xml version=\"1.0\" encoding=\"UTF-8\" ?> \
</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> \
<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\" ?> \
</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 {
GSList *conns;
GSList *connects;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ char *destination;
+ char *app_path;
+#endif
};
struct ext_io {
static GSList *profiles = NULL;
static GSList *ext_profiles = NULL;
+#ifdef TIZEN_BT_HID_DEVICE_ENABLE
+static int connect_io(struct ext_io *conn, const bdaddr_t *src,
+ const bdaddr_t *dst);
+#endif
+
void btd_profile_foreach(void (*func)(struct btd_profile *p, void *data),
void *data)
{
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;
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)
{
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",
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;
}
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;
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,
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,
.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",
.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)
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;
}
}
+#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)
}
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);
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) },
{ }
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);
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);
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;
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);
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) {
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);
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;
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);
* 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.
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:
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);
}
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
#include <stdint.h>
#include "src/shared/att-types.h"
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#include "lib/bluetooth.h"
+#endif
struct bt_att;
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
#include <config.h>
#endif
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+#include <stdio.h>
+#endif
#include <endian.h>
#include <fcntl.h>
#include <unistd.h>
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)
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;
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)
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);
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;
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);
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,
#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>
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 {
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);
{
/* 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);
op->failure_func = failure_func;
op->start = start;
op->end = end;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ op->last = start;
+#endif
return op;
}
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)
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);
}
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) {
"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;
}
"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) {
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,
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);
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)
"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);
}
{
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);
}
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);
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);
}
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(¬ify_data->chrc->notify_count, 1);
+#endif
goto done;
}
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));
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);
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);
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);
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,
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);
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
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,
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
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;
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;
}
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
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
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)
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;
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;
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,
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;
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);
}
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 */
/* 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);
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
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
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;
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);
--- /dev/null
+#!/usr/bin/python
+
+import dbus
+import dbus.exceptions
+import dbus.mainloop.glib
+import dbus.service
+
+import array
+import gobject
+
+from random import randint
+
+mainloop = None
+
+BLUEZ_SERVICE_NAME = 'org.bluez'
+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()
--- /dev/null
+#!/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])
--- /dev/null
+#!/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]))
--- /dev/null
+#!/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()
--- /dev/null
+#!/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)
--- /dev/null
+#!/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"
--- /dev/null
+.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
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;
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;
}
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;
}
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;
}
#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>
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)
{
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");
}
{ "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[])
{
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;
command = EXTRACT;
input_path = optarg;
break;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ case 's':
+ command = SPLIT;
+ input_path = optarg;
+#endif
case 't':
type = optarg;
break;
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;
--- /dev/null
+// PSKEY_BDADDR
+&0001 = 0001 2821 005b 6789
+// PSKEY_ANA_FTRIM
+&01f6 = 0025
+// PSKEY_HOST_INTERFACE
+&01f9 = 0001
+// PSKEY_UART_BAUD_RATE
+&0204 = 01d8
+// PSKEY_ANA_FREQ
+&01fe = 0004
+// PSKEY_UART_CONFIG
+&0205 = 0006
--- /dev/null
+#!/usr/bin/python
+
+import dbus
+import dbus.exceptions
+import dbus.mainloop.glib
+import dbus.service
+
+import array
+import gobject
+
+from random import randint
+
+mainloop = None
+
+BLUEZ_SERVICE_NAME = 'org.bluez'
+GATT_MANAGER_IFACE = 'org.bluez.GattManager1'
+DBUS_OM_IFACE = 'org.freedesktop.DBus.ObjectManager'
+DBUS_PROP_IFACE = 'org.freedesktop.DBus.Properties'
+
+GATT_SERVICE_IFACE = 'org.bluez.GattService1'
+GATT_CHRC_IFACE = 'org.bluez.GattCharacteristic1'
+GATT_DESC_IFACE = 'org.bluez.GattDescriptor1'
+
+class InvalidArgsException(dbus.exceptions.DBusException):
+ _dbus_error_name = 'org.freedesktop.DBus.Error.InvalidArgs'
+
+class NotSupportedException(dbus.exceptions.DBusException):
+ _dbus_error_name = 'org.bluez.Error.NotSupported'
+
+class NotPermittedException(dbus.exceptions.DBusException):
+ _dbus_error_name = 'org.bluez.Error.NotPermitted'
+
+class InvalidValueLengthException(dbus.exceptions.DBusException):
+ _dbus_error_name = 'org.bluez.Error.InvalidValueLength'
+
+class FailedException(dbus.exceptions.DBusException):
+ _dbus_error_name = 'org.bluez.Error.Failed'
+
+
+class Service(dbus.service.Object):
+ PATH_BASE = '/org/bluez/example/service'
+
+ def __init__(self, bus, index, uuid, primary):
+ self.path = self.PATH_BASE + str(index)
+ self.bus = bus
+ self.uuid = uuid
+ self.primary = primary
+ self.characteristics = []
+ dbus.service.Object.__init__(self, bus, self.path)
+
+ def get_properties(self):
+ return {
+ GATT_SERVICE_IFACE: {
+ 'UUID': self.uuid,
+ 'Primary': self.primary,
+ 'Characteristics': dbus.Array(
+ self.get_characteristic_paths(),
+ signature='o')
+ }
+ }
+
+ def get_path(self):
+ return dbus.ObjectPath(self.path)
+
+ def add_characteristic(self, characteristic):
+ self.characteristics.append(characteristic)
+
+ def get_characteristic_paths(self):
+ result = []
+ for chrc in self.characteristics:
+ result.append(chrc.get_path())
+ return result
+
+ def get_characteristics(self):
+ return self.characteristics
+
+ @dbus.service.method(DBUS_PROP_IFACE,
+ in_signature='s',
+ out_signature='a{sv}')
+ def GetAll(self, interface):
+ if interface != GATT_SERVICE_IFACE:
+ raise InvalidArgsException()
+
+ return self.get_properties[GATT_SERVICE_IFACE]
+
+ @dbus.service.method(DBUS_OM_IFACE, out_signature='a{oa{sa{sv}}}')
+ def GetManagedObjects(self):
+ response = {}
+ print 'GetManagedObjects'
+
+ response[self.get_path()] = self.get_properties()
+ chrcs = self.get_characteristics()
+ for chrc in chrcs:
+ response[chrc.get_path()] = chrc.get_properties()
+ descs = chrc.get_descriptors()
+ for desc in descs:
+ response[desc.get_path()] = desc.get_properties()
+
+ return response
+
+
+class Characteristic(dbus.service.Object):
+ def __init__(self, bus, index, uuid, flags, service):
+ self.path = service.path + '/char' + str(index)
+ self.bus = bus
+ self.uuid = uuid
+ self.service = service
+ self.flags = flags
+ self.descriptors = []
+ dbus.service.Object.__init__(self, bus, self.path)
+
+ def get_properties(self):
+ return {
+ GATT_CHRC_IFACE: {
+ 'Service': self.service.get_path(),
+ 'UUID': self.uuid,
+ 'Flags': self.flags,
+ 'Descriptors': dbus.Array(
+ self.get_descriptor_paths(),
+ signature='o')
+ }
+ }
+
+ def get_path(self):
+ return dbus.ObjectPath(self.path)
+
+ def add_descriptor(self, descriptor):
+ self.descriptors.append(descriptor)
+
+ def get_descriptor_paths(self):
+ result = []
+ for desc in self.descriptors:
+ result.append(desc.get_path())
+ return result
+
+ def get_descriptors(self):
+ return self.descriptors
+
+ @dbus.service.method(DBUS_PROP_IFACE,
+ in_signature='s',
+ out_signature='a{sv}')
+ def GetAll(self, interface):
+ if interface != GATT_CHRC_IFACE:
+ raise InvalidArgsException()
+
+ return self.get_properties[GATT_CHRC_IFACE]
+
+ @dbus.service.method(GATT_CHRC_IFACE, out_signature='ay')
+ def ReadValue(self):
+ print 'Default ReadValue called, returning error'
+ raise NotSupportedException()
+
+ @dbus.service.method(GATT_CHRC_IFACE, in_signature='ay')
+ def WriteValue(self, value):
+ print 'Default WriteValue called, returning error'
+ raise NotSupportedException()
+
+ @dbus.service.method(GATT_CHRC_IFACE)
+ def StartNotify(self):
+ print 'Default StartNotify called, returning error'
+ raise NotSupportedException()
+
+ @dbus.service.method(GATT_CHRC_IFACE)
+ def StopNotify(self):
+ print 'Default StopNotify called, returning error'
+ raise NotSupportedException()
+
+ @dbus.service.signal(DBUS_PROP_IFACE,
+ signature='sa{sv}as')
+ def PropertiesChanged(self, interface, changed, invalidated):
+ pass
+
+
+class Descriptor(dbus.service.Object):
+ def __init__(self, bus, index, uuid, characteristic):
+ self.path = characteristic.path + '/desc' + str(index)
+ self.bus = bus
+ self.uuid = uuid
+ self.chrc = characteristic
+ dbus.service.Object.__init__(self, bus, self.path)
+
+ def get_properties(self):
+ return {
+ GATT_DESC_IFACE: {
+ 'Characteristic': self.chrc.get_path(),
+ 'UUID': self.uuid,
+ }
+ }
+
+ def get_path(self):
+ return dbus.ObjectPath(self.path)
+
+ @dbus.service.method(DBUS_PROP_IFACE,
+ in_signature='s',
+ out_signature='a{sv}')
+ def GetAll(self, interface):
+ if interface != GATT_DESC_IFACE:
+ raise InvalidArgsException()
+
+ return self.get_properties[GATT_CHRC_IFACE]
+
+ @dbus.service.method(GATT_DESC_IFACE, out_signature='ay')
+ def ReadValue(self):
+ print 'Default ReadValue called, returning error'
+ raise NotSupportedException()
+
+ @dbus.service.method(GATT_DESC_IFACE, in_signature='ay')
+ def WriteValue(self, value):
+ print 'Default WriteValue called, returning error'
+ raise NotSupportedException()
+
+
+class HeartRateService(Service):
+ """
+ Fake Heart Rate Service that simulates a fake heart beat and control point
+ behavior.
+
+ """
+ HR_UUID = '0000180d-0000-1000-8000-00805f9b34fb'
+
+ def __init__(self, bus, index):
+ Service.__init__(self, bus, index, self.HR_UUID, True)
+ self.add_characteristic(HeartRateMeasurementChrc(bus, 0, self))
+ self.add_characteristic(BodySensorLocationChrc(bus, 1, self))
+ self.add_characteristic(HeartRateControlPointChrc(bus, 2, self))
+ self.energy_expended = 0
+
+
+class HeartRateMeasurementChrc(Characteristic):
+ HR_MSRMT_UUID = '00002a37-0000-1000-8000-00805f9b34fb'
+
+ def __init__(self, bus, index, service):
+ Characteristic.__init__(
+ self, bus, index,
+ self.HR_MSRMT_UUID,
+ ['notify'],
+ service)
+ self.notifying = False
+ self.hr_ee_count = 0
+
+ def hr_msrmt_cb(self):
+ value = []
+ value.append(dbus.Byte(0x06))
+
+ value.append(dbus.Byte(randint(90, 130)))
+
+ if self.hr_ee_count % 10 == 0:
+ value[0] = dbus.Byte(value[0] | 0x08)
+ value.append(dbus.Byte(self.service.energy_expended & 0xff))
+ value.append(dbus.Byte((self.service.energy_expended >> 8) & 0xff))
+
+ self.service.energy_expended = \
+ min(0xffff, self.service.energy_expended + 1)
+ self.hr_ee_count += 1
+
+ print 'Updating value: ' + repr(value)
+
+ self.PropertiesChanged(GATT_CHRC_IFACE, { 'Value': value }, [])
+
+ return self.notifying
+
+ def _update_hr_msrmt_simulation(self):
+ print 'Update HR Measurement Simulation'
+
+ if not self.notifying:
+ return
+
+ gobject.timeout_add(1000, self.hr_msrmt_cb)
+
+ def StartNotify(self):
+ if self.notifying:
+ print 'Already notifying, nothing to do'
+ return
+
+ self.notifying = True
+ self._update_hr_msrmt_simulation()
+
+ def StopNotify(self):
+ if not self.notifying:
+ print 'Not notifying, nothing to do'
+ return
+
+ self.notifying = False
+ self._update_hr_msrmt_simulation()
+
+
+class BodySensorLocationChrc(Characteristic):
+ BODY_SNSR_LOC_UUID = '00002a38-0000-1000-8000-00805f9b34fb'
+
+ def __init__(self, bus, index, service):
+ Characteristic.__init__(
+ self, bus, index,
+ self.BODY_SNSR_LOC_UUID,
+ ['read'],
+ service)
+
+ def ReadValue(self):
+ # Return 'Chest' as the sensor location.
+ return [ 0x01 ]
+
+class HeartRateControlPointChrc(Characteristic):
+ HR_CTRL_PT_UUID = '00002a39-0000-1000-8000-00805f9b34fb'
+
+ def __init__(self, bus, index, service):
+ Characteristic.__init__(
+ self, bus, index,
+ self.HR_CTRL_PT_UUID,
+ ['write'],
+ service)
+
+ def WriteValue(self, value):
+ print 'Heart Rate Control Point WriteValue called'
+
+ if len(value) != 1:
+ raise InvalidValueLengthException()
+
+ byte = value[0]
+ print 'Control Point value: ' + repr(byte)
+
+ if byte != 1:
+ raise FailedException("0x80")
+
+ print 'Energy Expended field reset!'
+ self.service.energy_expended = 0
+
+
+class BatteryService(Service):
+ """
+ Fake Battery service that emulates a draining battery.
+
+ """
+ BATTERY_UUID = '180f'
+
+ def __init__(self, bus, index):
+ Service.__init__(self, bus, index, self.BATTERY_UUID, True)
+ self.add_characteristic(BatteryLevelCharacteristic(bus, 0, self))
+
+
+class BatteryLevelCharacteristic(Characteristic):
+ """
+ Fake Battery Level characteristic. The battery level is drained by 2 points
+ every 5 seconds.
+
+ """
+ BATTERY_LVL_UUID = '2a19'
+
+ def __init__(self, bus, index, service):
+ Characteristic.__init__(
+ self, bus, index,
+ self.BATTERY_LVL_UUID,
+ ['read', 'notify'],
+ service)
+ self.notifying = False
+ self.battery_lvl = 100
+ gobject.timeout_add(5000, self.drain_battery)
+
+ def notify_battery_level(self):
+ if not self.notifying:
+ return
+ self.PropertiesChanged(
+ GATT_CHRC_IFACE,
+ { 'Value': [dbus.Byte(self.battery_lvl)] }, [])
+
+ def drain_battery(self):
+ if self.battery_lvl > 0:
+ self.battery_lvl -= 2
+ if self.battery_lvl < 0:
+ self.battery_lvl = 0
+ print 'Battery Level drained: ' + repr(self.battery_lvl)
+ self.notify_battery_level()
+ return True
+
+ def ReadValue(self):
+ print 'Battery Level read: ' + repr(self.battery_lvl)
+ return [dbus.Byte(self.battery_lvl)]
+
+ def StartNotify(self):
+ if self.notifying:
+ print 'Already notifying, nothing to do'
+ return
+
+ self.notifying = True
+ self.notify_battery_level()
+
+ def StopNotify(self):
+ if not self.notifying:
+ print 'Not notifying, nothing to do'
+ return
+
+ self.notifying = False
+
+
+class TestService(Service):
+ """
+ Dummy test service that provides characteristics and descriptors that
+ exercise various API functionality.
+
+ """
+ TEST_SVC_UUID = '12345678-1234-5678-1234-56789abcdef0'
+
+ def __init__(self, bus, index):
+ Service.__init__(self, bus, index, self.TEST_SVC_UUID, False)
+ self.add_characteristic(TestCharacteristic(bus, 0, self))
+
+
+class TestCharacteristic(Characteristic):
+ """
+ Dummy test characteristic. Allows writing arbitrary bytes to its value, and
+ contains "extended properties", as well as a test descriptor.
+
+ """
+ TEST_CHRC_UUID = '12345678-1234-5678-1234-56789abcdef1'
+
+ def __init__(self, bus, index, service):
+ Characteristic.__init__(
+ self, bus, index,
+ self.TEST_CHRC_UUID,
+ ['read', 'write', 'writable-auxiliaries'],
+ service)
+ self.value = []
+ self.add_descriptor(TestDescriptor(bus, 0, self))
+ self.add_descriptor(
+ CharacteristicUserDescriptionDescriptor(bus, 1, self))
+
+ def ReadValue(self):
+ print 'TestCharacteristic Read: ' + repr(self.value)
+ return self.value
+
+ def WriteValue(self, value):
+ print 'TestCharacteristic Write: ' + repr(value)
+ self.value = value
+
+
+class TestDescriptor(Descriptor):
+ """
+ Dummy test descriptor. Returns a static value.
+
+ """
+ TEST_DESC_UUID = '12345678-1234-5678-1234-56789abcdef2'
+
+ def __init__(self, bus, index, characteristic):
+ Descriptor.__init__(
+ self, bus, index,
+ self.TEST_DESC_UUID,
+ characteristic)
+
+ def ReadValue(self):
+ return [
+ dbus.Byte('T'), dbus.Byte('e'), dbus.Byte('s'), dbus.Byte('t')
+ ]
+
+
+class CharacteristicUserDescriptionDescriptor(Descriptor):
+ """
+ Writable CUD descriptor.
+
+ """
+ CUD_UUID = '2901'
+
+ def __init__(self, bus, index, characteristic):
+ self.writable = 'writable-auxiliaries' in characteristic.flags
+ self.value = array.array('B', 'This is a characteristic for testing')
+ self.value = self.value.tolist()
+ Descriptor.__init__(
+ self, bus, index,
+ self.CUD_UUID,
+ characteristic)
+
+ def ReadValue(self):
+ return self.value
+
+ def WriteValue(self, value):
+ if not self.writable:
+ raise NotPermittedException()
+ self.value = value
+
+
+def register_service_cb():
+ print 'GATT service registered'
+
+
+def register_service_error_cb(error):
+ print 'Failed to register service: ' + str(error)
+ mainloop.quit()
+
+
+def find_adapter(bus):
+ remote_om = dbus.Interface(bus.get_object(BLUEZ_SERVICE_NAME, '/'),
+ DBUS_OM_IFACE)
+ objects = remote_om.GetManagedObjects()
+
+ for o, props in objects.iteritems():
+ if props.has_key(GATT_MANAGER_IFACE):
+ return o
+
+ return None
+
+def main():
+ global mainloop
+
+ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+ bus = dbus.SystemBus()
+
+ adapter = find_adapter(bus)
+ if not adapter:
+ print 'GattManager1 interface not found'
+ return
+
+ service_manager = dbus.Interface(
+ bus.get_object(BLUEZ_SERVICE_NAME, adapter),
+ GATT_MANAGER_IFACE)
+
+ hr_service = HeartRateService(bus, 0)
+ bat_service = BatteryService(bus, 1)
+ test_service = TestService(bus, 2)
+
+ mainloop = gobject.MainLoop()
+
+ service_manager.RegisterService(hr_service.get_path(), {},
+ reply_handler=register_service_cb,
+ error_handler=register_service_error_cb)
+ service_manager.RegisterService(bat_service.get_path(), {},
+ reply_handler=register_service_cb,
+ error_handler=register_service_error_cb)
+ service_manager.RegisterService(test_service.get_path(), {},
+ reply_handler=register_service_cb,
+ error_handler=register_service_error_cb)
+
+ mainloop.run()
+
+if __name__ == '__main__':
+ main()
#include <config.h>
#endif
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
+#define _GNU_SOURCE
+#endif
#include <stdio.h>
#include <errno.h>
#include <fcntl.h>
#include "src/shared/tty.h"
#include "hciattach.h"
+#include "../profile.h"
struct uart_t {
char *type;
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
/* 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) {
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 },
{ "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,
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)
{
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;
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");
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;
goto fail;
}
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
if (u->post && u->post(fd, u, &ti) < 0)
goto fail;
+#endif
return fd;
{
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':
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;
exit(0);
case 'r':
- raw = 1;
+#if defined(TIZEN_FEATURE_BLUEZ_MODIFY)
+ if (!TIZEN_FEATURE_BLUEZ_BRCM_CHIP)
+ raw = 1;
+#endif
break;
default:
}
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);
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;
/* 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);
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;
}
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);
#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"
--- /dev/null
+#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;
+}
--- /dev/null
+#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__ */
+
+
+
+
#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
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;
}
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)
/* 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;
} __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;
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;
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:
if (err < 0)
goto failed;
+#ifndef TIZEN_FEATURE_BLUEZ_MODIFY
if (!err)
goto done;
+#endif
frm.ptr = frm.data;
frm.len = frm.data_len;
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;
" -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"
{ "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 }
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"))
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':
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);
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;
}
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[] = {
{ "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" },
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 */
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 */
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;
}
}
+#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];
"\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"
"\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");
}
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;
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;
break;
+#ifdef TIZEN_FEATURE_BLUEZ_MODIFY
+ case 'f':
+ dcid = atoi(optarg);
+ printf("dcid %d", dcid);
+ break;
+#endif
+
case 'e':
seq_start = atoi(optarg);
break;
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);
--- /dev/null
+#!/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/&/&/ig;
+ $name =~ s/ / /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;
+ }
+}
} __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
--- /dev/null
+#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;
+}
+
+
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);
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;
--- /dev/null
+#!/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 " " (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/ / /g' | \
+ html2text -ascii -width 160 -o identifiers.txt >/dev/null
+
+# Some versions of html2text do not replace & (e.g. Fedora)
+sed -i 's/&/\&/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
--- /dev/null
+{
+ 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
+}