From: h.sandeep Date: Thu, 2 Feb 2017 11:19:57 +0000 (+0530) Subject: Bluez 5.43 Upgrade: Apply Tizen patches X-Git-Tag: accepted/tizen/common/20170330.151746^0 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=refs%2Fchanges%2F79%2F121679%2F1;p=platform%2Fupstream%2Fbluez.git Bluez 5.43 Upgrade: Apply Tizen patches Change-Id: I5d5a9d9e2e7811af3dd3878b4f740e1f1a019d51 Signed-off-by: h.sandeep --- diff --git a/Makefile.am b/Makefile.am index ce7fd0f..7a8ad8d 100755 --- a/Makefile.am +++ b/Makefile.am @@ -182,6 +182,7 @@ src_bluetoothd_SOURCES = $(builtin_sources) \ src/agent.h src/agent.c \ src/error.h src/error.c \ src/adapter.h src/adapter.c \ + src/adapter_le_vsc_features.h src/adapter_le_vsc_features.c \ src/profile.h src/profile.c \ src/service.h src/service.c \ src/gatt-client.h src/gatt-client.c \ @@ -191,7 +192,7 @@ src_bluetoothd_SOURCES = $(builtin_sources) \ src_bluetoothd_LDADD = lib/libbluetooth-internal.la \ gdbus/libgdbus-internal.la \ src/libshared-glib.la \ - @BACKTRACE_LIBS@ @GLIB_LIBS@ @DBUS_LIBS@ -ldl -lrt + @BACKTRACE_LIBS@ @GLIB_LIBS@ @DBUS_LIBS@ @LIBXML_LIBS@ @INIPARSER_LIBS@ -ldl -lrt src_bluetoothd_LDFLAGS = $(AM_LDFLAGS) -Wl,--export-dynamic \ -Wl,--version-script=$(srcdir)/src/bluetooth.ver @@ -213,7 +214,7 @@ CLEANFILES += $(builtin_files) src/bluetooth.service man_MANS = src/bluetoothd.8 EXTRA_DIST += src/genbuiltin src/bluetooth.conf \ - src/main.conf profiles/network/network.conf \ + src/main_m.conf src/main_w.conf profiles/network/network.conf \ profiles/input/input.conf profiles/proximity/proximity.conf test_scripts = @@ -266,7 +267,7 @@ EXTRA_DIST += doc/btsnoop.txt EXTRA_DIST += tools/magic.btsnoop -AM_CFLAGS += @DBUS_CFLAGS@ @GLIB_CFLAGS@ +AM_CFLAGS += @DBUS_CFLAGS@ @GLIB_CFLAGS@ @LIBXML_CFLAGS@ @INIPARSER_CFLAGS@ AM_CPPFLAGS = -I$(builddir)/lib @@ -421,6 +422,16 @@ unit_test_gattrib_LDADD = lib/libbluetooth-internal.la \ src/libshared-glib.la \ @GLIB_LIBS@ @DBUS_LIBS@ -ldl -lrt +if MIDI +unit_tests += unit/test-midi +unit_test_midi_CFLAGS = $(AM_CFLAGS) @ALSA_CFLAGS@ -DMIDI_TEST +unit_test_midi_SOURCES = unit/test-midi.c \ + profiles/midi/libmidi.h \ + profiles/midi/libmidi.c +unit_test_midi_LDADD = src/libshared-glib.la \ + @GLIB_LIBS@ @ALSA_LIBS@ +endif + if MAINTAINER_MODE noinst_PROGRAMS += $(unit_tests) endif diff --git a/Makefile.obexd b/Makefile.obexd index 42135a9..05a3b6b 100755 --- a/Makefile.obexd +++ b/Makefile.obexd @@ -82,11 +82,13 @@ obexd_src_obexd_SOURCES = $(btio_sources) $(gobex_sources) \ obexd/src/map_ap.h obexd_src_obexd_LDADD = lib/libbluetooth-internal.la \ gdbus/libgdbus-internal.la \ - @ICAL_LIBS@ @DBUS_LIBS@ @GLIB_LIBS@ -ldl + @ICAL_LIBS@ @DBUS_LIBS@ @GLIB_LIBS@ \ + @LIBXML_LIBS@ @INIPARSER_LIBS@ -ldl obexd_src_obexd_LDFLAGS = -Wl,--export-dynamic obexd_src_obexd_CFLAGS = $(AM_CFLAGS) @GLIB_CFLAGS@ @DBUS_CFLAGS@ \ + @LIBXML_CFLAGS@ @INIPARSER_CFLAGS@ \ @ICAL_CFLAGS@ -DOBEX_PLUGIN_BUILTIN \ -DPLUGINDIR=\""$(obex_plugindir)"\" \ -fPIC -D_FILE_OFFSET_BITS=64 -pie diff --git a/Makefile.plugins b/Makefile.plugins index 53e80fe..d0b81d0 100755 --- a/Makefile.plugins +++ b/Makefile.plugins @@ -2,32 +2,46 @@ builtin_modules += hostname builtin_sources += plugins/hostname.c +if TIZEN_UNUSED_PLUGIN builtin_modules += wiimote builtin_sources += plugins/wiimote.c +endif +if AUTOPAIR builtin_modules += autopair builtin_sources += plugins/autopair.c +endif builtin_modules += policy builtin_sources += plugins/policy.c +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +builtin_modules += dbusoob +builtin_sources += plugins/dbusoob.c \ + src/oob.h src/oob.c +#endif + if MAINTAINER_MODE builtin_modules += gatt_example builtin_sources += plugins/gatt-example.c endif if EXPERIMENTAL +if TIZEN_UNUSED_PLUGIN builtin_modules += neard builtin_sources += plugins/neard.c +endif +if TIZEN_SAP_PLUGIN builtin_modules += sap builtin_sources += profiles/sap/main.c profiles/sap/manager.h \ profiles/sap/manager.c profiles/sap/server.h \ profiles/sap/server.c profiles/sap/sap.h \ profiles/sap/sap-u8500.c -noinst_LIBRARIES += profiles/sap/libsap.a -profiles_sap_libsap_a_SOURCES = profiles/sap/sap.h profiles/sap/sap-u8500.c +#noinst_LIBRARIES += profiles/sap/libsap.a +#profiles_sap_libsap_a_SOURCES = profiles/sap/sap.h profiles/sap/sap-u8500.c +endif endif builtin_modules += a2dp @@ -52,12 +66,18 @@ builtin_sources += profiles/network/manager.c \ profiles/network/connection.h \ profiles/network/connection.c +#if WEARABLE +#builtin_modules += +#builtin_sources += +#else +if TIZEN_HID_PLUGIN builtin_modules += input builtin_sources += profiles/input/manager.c \ profiles/input/server.h profiles/input/server.c \ profiles/input/device.h profiles/input/device.c \ profiles/input/hidp_defs.h +if TIZEN_UNUSED_PLUGIN builtin_modules += hog builtin_sources += profiles/input/hog.c profiles/input/uhid_copy.h \ profiles/input/hog-lib.c profiles/input/hog-lib.h \ @@ -67,8 +87,12 @@ builtin_sources += profiles/input/hog.c profiles/input/uhid_copy.h \ profiles/input/suspend.h profiles/input/suspend-none.c EXTRA_DIST += profiles/input/suspend-dummy.c +endif +endif +#endif if EXPERIMENTAL +if TIZEN_HEALTH_PLUGIN builtin_modules += health builtin_sources += profiles/health/mcap.h profiles/health/mcap.c \ profiles/health/hdp_main.c profiles/health/hdp_types.h \ @@ -77,7 +101,9 @@ builtin_sources += profiles/health/mcap.h profiles/health/mcap.c \ profiles/health/hdp.h profiles/health/hdp.c \ profiles/health/hdp_util.h profiles/health/hdp_util.c endif +endif +if TIZEN_HEALTH_PLUGIN builtin_modules += gap builtin_sources += profiles/gap/gas.c @@ -86,11 +112,52 @@ builtin_sources += profiles/scanparam/scan.c builtin_modules += deviceinfo builtin_sources += profiles/deviceinfo/deviceinfo.c +endif + +if EXPERIMENTAL +if TIZEN_UNUSED_PLUGIN +if TIZEN_PROXIMITY_PLUGIN +builtin_modules += proximity +builtin_sources += profiles/proximity/main.c profiles/proximity/manager.h \ + profiles/proximity/manager.c \ + profiles/proximity/monitor.h \ + profiles/proximity/monitor.c \ + profiles/proximity/reporter.h \ + profiles/proximity/reporter.c \ + profiles/proximity/linkloss.h \ + profiles/proximity/linkloss.c \ + profiles/proximity/immalert.h \ + profiles/proximity/immalert.c +endif + + +if TIZEN_TDS_PLUGIN +builtin_modules += tds +builtin_sources += profiles/tds/manager.c profiles/tds/tds.h \ + profiles/tds/tds.c +endif + +builtin_modules += alert +builtin_sources += profiles/alert/server.c + +builtin_modules += time +builtin_sources += profiles/time/server.c + +builtin_modules += thermometer +builtin_sources += profiles/thermometer/thermometer.c + +builtin_modules += heartrate +builtin_sources += profiles/heartrate/heartrate.c + +builtin_modules += cyclingspeed +builtin_sources += profiles/cyclingspeed/cyclingspeed.c +endif +endif if SIXAXIS plugin_LTLIBRARIES += plugins/sixaxis.la plugins_sixaxis_la_SOURCES = plugins/sixaxis.c plugins_sixaxis_la_LDFLAGS = $(AM_LDFLAGS) -module -avoid-version \ - -no-undefined @UDEV_LIBS@ -plugins_sixaxis_la_CFLAGS = $(AM_CFLAGS) -fvisibility=hidden @UDEV_CFLAGS@ + -no-undefined @UDEV_LIBS@ @LIBXML_LIBS@ @INIPARSER_LIBS@ +plugins_sixaxis_la_CFLAGS = $(AM_CFLAGS) -fvisibility=hidden @UDEV_CFLAGS@ @LIBXML_CFLAGS@ @INIPARSER_CFLAGS@ endif diff --git a/Makefile.tools b/Makefile.tools index 42f17fc..fe6220f 100755 --- a/Makefile.tools +++ b/Makefile.tools @@ -169,7 +169,7 @@ tools_hciattach_SOURCES = tools/hciattach.c tools/hciattach.h \ tools/hciattach_sprd.c \ tools/pskey_get.c \ tools/hciattach_bcm43xx.c -tools_hciattach_LDADD = lib/libbluetooth-internal.la +tools_hciattach_LDADD = lib/libbluetooth-internal.la @LIBXML_LIBS@ @INIPARSER_LIBS@ tools_hciconfig_SOURCES = tools/hciconfig.c tools/csr.h tools/csr.c tools_hciconfig_LDADD = lib/libbluetooth-internal.la diff --git a/android/bas.c b/android/bas.c new file mode 100755 index 0000000..dcbf9de --- /dev/null +++ b/android/bas.c @@ -0,0 +1,379 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2014 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can rebastribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is bastributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include + +#include "src/log.h" + +#include "lib/bluetooth.h" +#include "lib/sdp.h" +#include "lib/uuid.h" + +#include "src/shared/util.h" +#include "src/shared/queue.h" + +#include "attrib/gattrib.h" +#include "attrib/att.h" +#include "attrib/gatt.h" + +#include "android/bas.h" + +#define ATT_NOTIFICATION_HEADER_SIZE 3 +#define ATT_READ_RESPONSE_HEADER_SIZE 1 + +struct bt_bas { + int ref_count; + GAttrib *attrib; + struct gatt_primary *primary; + uint16_t handle; + uint16_t ccc_handle; + guint id; + struct queue *gatt_op; +}; + +struct gatt_request { + unsigned int id; + struct bt_bas *bas; + void *user_data; +}; + +static void destroy_gatt_req(struct gatt_request *req) +{ + queue_remove(req->bas->gatt_op, req); + bt_bas_unref(req->bas); + free(req); +} + +static void bas_free(struct bt_bas *bas) +{ + bt_bas_detach(bas); + + g_free(bas->primary); + queue_destroy(bas->gatt_op, (void *) destroy_gatt_req); + g_free(bas); +} + +struct bt_bas *bt_bas_new(void *primary) +{ + struct bt_bas *bas; + + bas = g_try_new0(struct bt_bas, 1); + if (!bas) + return NULL; + + bas->gatt_op = queue_new(); + if (!bas->gatt_op) { + bas_free(bas); + return NULL; + } + + if (primary) + bas->primary = g_memdup(primary, sizeof(*bas->primary)); + + return bt_bas_ref(bas); +} + +struct bt_bas *bt_bas_ref(struct bt_bas *bas) +{ + if (!bas) + return NULL; + + __sync_fetch_and_add(&bas->ref_count, 1); + + return bas; +} + +void bt_bas_unref(struct bt_bas *bas) +{ + if (!bas) + return; + + if (__sync_sub_and_fetch(&bas->ref_count, 1)) + return; + + bas_free(bas); +} + +static struct gatt_request *create_request(struct bt_bas *bas, + void *user_data) +{ + struct gatt_request *req; + + req = new0(struct gatt_request, 1); + if (!req) + return NULL; + + req->user_data = user_data; + req->bas = bt_bas_ref(bas); + + return req; +} + +static bool set_and_store_gatt_req(struct bt_bas *bas, + struct gatt_request *req, + unsigned int id) +{ + req->id = id; + return queue_push_head(bas->gatt_op, req); +} + +static void write_char(struct bt_bas *bas, GAttrib *attrib, uint16_t handle, + const uint8_t *value, size_t vlen, + GAttribResultFunc func, + gpointer user_data) +{ + struct gatt_request *req; + unsigned int id; + + req = create_request(bas, user_data); + if (!req) + return; + + id = gatt_write_char(attrib, handle, value, vlen, func, req); + + if (set_and_store_gatt_req(bas, req, id)) + return; + + error("bas: Could not write characteristic"); + g_attrib_cancel(attrib, id); + free(req); + +} + +static void read_char(struct bt_bas *bas, GAttrib *attrib, uint16_t handle, + GAttribResultFunc func, gpointer user_data) +{ + struct gatt_request *req; + unsigned int id; + + req = create_request(bas, user_data); + if (!req) + return; + + id = gatt_read_char(attrib, handle, func, req); + + if (set_and_store_gatt_req(bas, req, id)) + return; + + error("bas: Could not read characteristic"); + g_attrib_cancel(attrib, id); + free(req); +} + +static void discover_char(struct bt_bas *bas, GAttrib *attrib, + uint16_t start, uint16_t end, + bt_uuid_t *uuid, gatt_cb_t func, + gpointer user_data) +{ + struct gatt_request *req; + unsigned int id; + + req = create_request(bas, user_data); + if (!req) + return; + + id = gatt_discover_char(attrib, start, end, uuid, func, req); + + if (set_and_store_gatt_req(bas, req, id)) + return; + + error("bas: Could not discover characteristic"); + g_attrib_cancel(attrib, id); + free(req); +} + +static void discover_desc(struct bt_bas *bas, GAttrib *attrib, + uint16_t start, uint16_t end, bt_uuid_t *uuid, + gatt_cb_t func, gpointer user_data) +{ + struct gatt_request *req; + unsigned int id; + + req = create_request(bas, user_data); + if (!req) + return; + + id = gatt_discover_desc(attrib, start, end, uuid, func, req); + if (set_and_store_gatt_req(bas, req, id)) + return; + + error("bas: Could not discover descriptor"); + g_attrib_cancel(attrib, id); + free(req); +} + +static void notification_cb(const guint8 *pdu, guint16 len, gpointer user_data) +{ + DBG("Battery Level at %u", pdu[ATT_NOTIFICATION_HEADER_SIZE]); +} + +static void read_value_cb(guint8 status, const guint8 *pdu, guint16 len, + gpointer user_data) +{ + DBG("Battery Level at %u", pdu[ATT_READ_RESPONSE_HEADER_SIZE]); +} + +static void ccc_written_cb(guint8 status, const guint8 *pdu, + guint16 plen, gpointer user_data) +{ + struct gatt_request *req = user_data; + struct bt_bas *bas = req->user_data; + + destroy_gatt_req(req); + + if (status != 0) { + error("Write Scan Refresh CCC failed: %s", + att_ecode2str(status)); + return; + } + + DBG("Battery Level: notification enabled"); + + bas->id = g_attrib_register(bas->attrib, ATT_OP_HANDLE_NOTIFY, + bas->handle, notification_cb, bas, + NULL); +} + +static void write_ccc(struct bt_bas *bas, GAttrib *attrib, uint16_t handle, + void *user_data) +{ + uint8_t value[2]; + + put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value); + + write_char(bas, attrib, handle, value, sizeof(value), ccc_written_cb, + user_data); +} + +static void ccc_read_cb(guint8 status, const guint8 *pdu, guint16 len, + gpointer user_data) +{ + struct gatt_request *req = user_data; + struct bt_bas *bas = req->user_data; + + destroy_gatt_req(req); + + if (status != 0) { + error("Error reading CCC value: %s", att_ecode2str(status)); + return; + } + + write_ccc(bas, bas->attrib, bas->ccc_handle, bas); +} + +static void discover_descriptor_cb(uint8_t status, GSList *descs, + void *user_data) +{ + struct gatt_request *req = user_data; + struct bt_bas *bas = req->user_data; + struct gatt_desc *desc; + + destroy_gatt_req(req); + + if (status != 0) { + error("Discover descriptors failed: %s", att_ecode2str(status)); + return; + } + + /* There will be only one descriptor on list and it will be CCC */ + desc = descs->data; + bas->ccc_handle = desc->handle; + + read_char(bas, bas->attrib, desc->handle, ccc_read_cb, bas); +} + +static void bas_discovered_cb(uint8_t status, GSList *chars, void *user_data) +{ + struct gatt_request *req = user_data; + struct bt_bas *bas = req->user_data; + struct gatt_char *chr; + uint16_t start, end; + bt_uuid_t uuid; + + destroy_gatt_req(req); + + if (status) { + error("Battery: %s", att_ecode2str(status)); + return; + } + + chr = chars->data; + bas->handle = chr->value_handle; + + DBG("Battery handle: 0x%04x", bas->handle); + + read_char(bas, bas->attrib, bas->handle, read_value_cb, bas); + + start = chr->value_handle + 1; + end = bas->primary->range.end; + + bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID); + + discover_desc(bas, bas->attrib, start, end, &uuid, + discover_descriptor_cb, bas); +} + +bool bt_bas_attach(struct bt_bas *bas, void *attrib) +{ + if (!bas || bas->attrib || !bas->primary) + return false; + + bas->attrib = g_attrib_ref(attrib); + + if (bas->handle > 0) + return true; + + discover_char(bas, bas->attrib, bas->primary->range.start, + bas->primary->range.end, NULL, + bas_discovered_cb, bas); + + return true; +} + +static void cancel_gatt_req(struct gatt_request *req) +{ + if (g_attrib_cancel(req->bas->attrib, req->id)) + destroy_gatt_req(req); +} + +void bt_bas_detach(struct bt_bas *bas) +{ + if (!bas || !bas->attrib) + return; + + if (bas->id > 0) { + g_attrib_unregister(bas->attrib, bas->id); + bas->id = 0; + } + + queue_foreach(bas->gatt_op, (void *) cancel_gatt_req, NULL); + g_attrib_unref(bas->attrib); + bas->attrib = NULL; +} diff --git a/android/bas.h b/android/bas.h new file mode 100755 index 0000000..3e175b5 --- /dev/null +++ b/android/bas.h @@ -0,0 +1,32 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2014 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can rebastribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is bastributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +struct bt_bas; + +struct bt_bas *bt_bas_new(void *primary); + +struct bt_bas *bt_bas_ref(struct bt_bas *bas); +void bt_bas_unref(struct bt_bas *bas); + +bool bt_bas_attach(struct bt_bas *bas, void *gatt); +void bt_bas_detach(struct bt_bas *bas); diff --git a/android/dis.c b/android/dis.c new file mode 100755 index 0000000..75dbe3d --- /dev/null +++ b/android/dis.c @@ -0,0 +1,304 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2012 Texas Instruments, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include + +#include "src/log.h" + +#include "lib/bluetooth.h" +#include "lib/sdp.h" +#include "lib/uuid.h" + +#include "src/shared/util.h" +#include "src/shared/queue.h" + +#include "attrib/gattrib.h" +#include "attrib/att.h" +#include "attrib/gatt.h" + +#include "android/dis.h" + +#define PNP_ID_SIZE 7 + +struct bt_dis { + int ref_count; + uint16_t handle; + uint8_t source; + uint16_t vendor; + uint16_t product; + uint16_t version; + GAttrib *attrib; /* GATT connection */ + struct gatt_primary *primary; /* Primary details */ + bt_dis_notify notify; + void *notify_data; + struct queue *gatt_op; +}; + +struct characteristic { + struct gatt_char attr; /* Characteristic */ + struct bt_dis *d; /* deviceinfo where the char belongs */ +}; + +struct gatt_request { + unsigned int id; + struct bt_dis *dis; + void *user_data; +}; + +static void destroy_gatt_req(struct gatt_request *req) +{ + queue_remove(req->dis->gatt_op, req); + bt_dis_unref(req->dis); + free(req); +} + +static void dis_free(struct bt_dis *dis) +{ + bt_dis_detach(dis); + + g_free(dis->primary); + queue_destroy(dis->gatt_op, (void *) destroy_gatt_req); + g_free(dis); +} + +struct bt_dis *bt_dis_new(void *primary) +{ + struct bt_dis *dis; + + dis = g_try_new0(struct bt_dis, 1); + if (!dis) + return NULL; + + dis->gatt_op = queue_new(); + if (!dis->gatt_op) { + dis_free(dis); + return NULL; + } + + if (primary) + dis->primary = g_memdup(primary, sizeof(*dis->primary)); + + return bt_dis_ref(dis); +} + +struct bt_dis *bt_dis_ref(struct bt_dis *dis) +{ + if (!dis) + return NULL; + + __sync_fetch_and_add(&dis->ref_count, 1); + + return dis; +} + +void bt_dis_unref(struct bt_dis *dis) +{ + if (!dis) + return; + + if (__sync_sub_and_fetch(&dis->ref_count, 1)) + return; + + dis_free(dis); +} + +static struct gatt_request *create_request(struct bt_dis *dis, + void *user_data) +{ + struct gatt_request *req; + + req = new0(struct gatt_request, 1); + if (!req) + return NULL; + + req->user_data = user_data; + req->dis = bt_dis_ref(dis); + + return req; +} + +static bool set_and_store_gatt_req(struct bt_dis *dis, + struct gatt_request *req, + unsigned int id) +{ + req->id = id; + return queue_push_head(dis->gatt_op, req); +} + +static void read_pnpid_cb(guint8 status, const guint8 *pdu, guint16 len, + gpointer user_data) +{ + struct gatt_request *req = user_data; + struct bt_dis *dis = req->user_data; + uint8_t value[PNP_ID_SIZE]; + ssize_t vlen; + + destroy_gatt_req(req); + + if (status != 0) { + error("Error reading PNP_ID value: %s", att_ecode2str(status)); + return; + } + + vlen = dec_read_resp(pdu, len, value, sizeof(value)); + if (vlen < 0) { + error("Error reading PNP_ID: Protocol error"); + return; + } + + if (vlen < 7) { + error("Error reading PNP_ID: Invalid pdu length received"); + return; + } + + dis->source = value[0]; + dis->vendor = get_le16(&value[1]); + dis->product = get_le16(&value[3]); + dis->version = get_le16(&value[5]); + + DBG("source: 0x%02X vendor: 0x%04X product: 0x%04X version: 0x%04X", + dis->source, dis->vendor, dis->product, dis->version); + + if (dis->notify) + dis->notify(dis->source, dis->vendor, dis->product, + dis->version, dis->notify_data); +} + +static void read_char(struct bt_dis *dis, GAttrib *attrib, uint16_t handle, + GAttribResultFunc func, gpointer user_data) +{ + struct gatt_request *req; + unsigned int id; + + req = create_request(dis, user_data); + if (!req) + return; + + id = gatt_read_char(attrib, handle, func, req); + + if (set_and_store_gatt_req(dis, req, id)) + return; + + error("dis: Could not read characteristic"); + g_attrib_cancel(attrib, id); + free(req); +} + +static void discover_char(struct bt_dis *dis, GAttrib *attrib, + uint16_t start, uint16_t end, + bt_uuid_t *uuid, gatt_cb_t func, + gpointer user_data) +{ + struct gatt_request *req; + unsigned int id; + + req = create_request(dis, user_data); + if (!req) + return; + + id = gatt_discover_char(attrib, start, end, uuid, func, req); + + if (set_and_store_gatt_req(dis, req, id)) + return; + + error("dis: Could not send discover characteristic"); + g_attrib_cancel(attrib, id); + free(req); +} + +static void configure_deviceinfo_cb(uint8_t status, GSList *characteristics, + void *user_data) +{ + struct gatt_request *req = user_data; + struct bt_dis *d = req->user_data; + GSList *l; + + destroy_gatt_req(req); + + if (status != 0) { + error("Discover deviceinfo characteristics: %s", + att_ecode2str(status)); + return; + } + + for (l = characteristics; l; l = l->next) { + struct gatt_char *c = l->data; + + if (strcmp(c->uuid, PNPID_UUID) == 0) { + d->handle = c->value_handle; + read_char(d, d->attrib, d->handle, read_pnpid_cb, d); + break; + } + } +} + +bool bt_dis_attach(struct bt_dis *dis, void *attrib) +{ + struct gatt_primary *primary = dis->primary; + + if (dis->attrib || !primary) + return false; + + dis->attrib = g_attrib_ref(attrib); + + if (!dis->handle) + discover_char(dis, dis->attrib, primary->range.start, + primary->range.end, NULL, + configure_deviceinfo_cb, dis); + + return true; +} + +static void cancel_gatt_req(struct gatt_request *req) +{ + if (g_attrib_cancel(req->dis->attrib, req->id)) + destroy_gatt_req(req); +} + +void bt_dis_detach(struct bt_dis *dis) +{ + if (!dis->attrib) + return; + + queue_foreach(dis->gatt_op, (void *) cancel_gatt_req, NULL); + g_attrib_unref(dis->attrib); + dis->attrib = NULL; +} + +bool bt_dis_set_notification(struct bt_dis *dis, bt_dis_notify func, + void *user_data) +{ + if (!dis) + return false; + + dis->notify = func; + dis->notify_data = user_data; + + return true; +} diff --git a/android/dis.h b/android/dis.h new file mode 100755 index 0000000..faf27b3 --- /dev/null +++ b/android/dis.h @@ -0,0 +1,39 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2014 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +struct bt_dis; + +struct bt_dis *bt_dis_new(void *primary); + +struct bt_dis *bt_dis_ref(struct bt_dis *dis); +void bt_dis_unref(struct bt_dis *dis); + +bool bt_dis_attach(struct bt_dis *dis, void *gatt); +void bt_dis_detach(struct bt_dis *dis); + +typedef void (*bt_dis_notify) (uint8_t source, uint16_t vendor, + uint16_t product, uint16_t version, + void *user_data); + +bool bt_dis_set_notification(struct bt_dis *dis, bt_dis_notify func, + void *user_data); diff --git a/android/hog.c b/android/hog.c new file mode 100755 index 0000000..ff77bb3 --- /dev/null +++ b/android/hog.c @@ -0,0 +1,1511 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2014 Intel Corporation. + * Copyright (C) 2012 Marcel Holtmann + * 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 +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "lib/bluetooth.h" +#include "lib/sdp.h" +#include "lib/uuid.h" + +#include "src/shared/util.h" +#include "src/shared/uhid.h" +#include "src/shared/queue.h" +#include "src/log.h" + +#include "attrib/att.h" +#include "attrib/gattrib.h" +#include "attrib/gatt.h" + +#include "btio/btio.h" + +#include "android/scpp.h" +#include "android/dis.h" +#include "android/bas.h" +#include "android/hog.h" + +#define HOG_UUID "00001812-0000-1000-8000-00805f9b34fb" + +#define HOG_INFO_UUID 0x2A4A +#define HOG_REPORT_MAP_UUID 0x2A4B +#define HOG_REPORT_UUID 0x2A4D +#define HOG_PROTO_MODE_UUID 0x2A4E +#define HOG_CONTROL_POINT_UUID 0x2A4C + +#define HOG_REPORT_TYPE_INPUT 1 +#define HOG_REPORT_TYPE_OUTPUT 2 +#define HOG_REPORT_TYPE_FEATURE 3 + +#define HOG_PROTO_MODE_BOOT 0 +#define HOG_PROTO_MODE_REPORT 1 + +#define HOG_REPORT_MAP_MAX_SIZE 512 +#define HID_INFO_SIZE 4 +#define ATT_NOTIFICATION_HEADER_SIZE 3 + +struct bt_hog { + int ref_count; + char *name; + uint16_t vendor; + uint16_t product; + uint16_t version; + struct gatt_primary *primary; + GAttrib *attrib; + GSList *reports; + struct bt_uhid *uhid; + int uhid_fd; + gboolean has_report_id; + uint16_t bcdhid; + uint8_t bcountrycode; + uint16_t proto_mode_handle; + uint16_t ctrlpt_handle; + uint8_t flags; + unsigned int getrep_att; + uint16_t getrep_id; + unsigned int setrep_att; + uint16_t setrep_id; + struct bt_scpp *scpp; + struct bt_dis *dis; + struct queue *bas; + GSList *instances; + struct queue *gatt_op; +}; + +struct report { + struct bt_hog *hog; + uint8_t id; + uint8_t type; + uint16_t ccc_handle; + guint notifyid; + struct gatt_char *decl; + uint16_t len; + uint8_t *value; +}; + +struct gatt_request { + unsigned int id; + struct bt_hog *hog; + void *user_data; +}; + +static struct gatt_request *create_request(struct bt_hog *hog, + void *user_data) +{ + struct gatt_request *req; + + req = new0(struct gatt_request, 1); + if (!req) + return NULL; + + req->user_data = user_data; + req->hog = bt_hog_ref(hog); + + return req; +} + +static bool set_and_store_gatt_req(struct bt_hog *hog, + struct gatt_request *req, + unsigned int id) +{ + req->id = id; + return queue_push_head(hog->gatt_op, req); +} + +static void destroy_gatt_req(struct gatt_request *req) +{ + queue_remove(req->hog->gatt_op, req); + bt_hog_unref(req->hog); + free(req); +} + +static void write_char(struct bt_hog *hog, GAttrib *attrib, uint16_t handle, + const uint8_t *value, size_t vlen, + GAttribResultFunc func, + gpointer user_data) +{ + struct gatt_request *req; + unsigned int id; + + req = create_request(hog, user_data); + if (!req) + return; + + id = gatt_write_char(attrib, handle, value, vlen, func, req); + + if (set_and_store_gatt_req(hog, req, id)) + return; + + error("hog: Could not read char"); + g_attrib_cancel(attrib, id); + free(req); +} + +static void read_char(struct bt_hog *hog, GAttrib *attrib, uint16_t handle, + GAttribResultFunc func, gpointer user_data) +{ + struct gatt_request *req; + unsigned int id; + + req = create_request(hog, user_data); + if (!req) + return; + + id = gatt_read_char(attrib, handle, func, req); + + if (set_and_store_gatt_req(hog, req, id)) + return; + + error("hog: Could not read char"); + g_attrib_cancel(attrib, id); + free(req); +} + +static void discover_desc(struct bt_hog *hog, GAttrib *attrib, + uint16_t start, uint16_t end, gatt_cb_t func, + gpointer user_data) +{ + struct gatt_request *req; + unsigned int id; + + req = create_request(hog, user_data); + if (!req) + return; + + id = gatt_discover_desc(attrib, start, end, NULL, func, req); + if (set_and_store_gatt_req(hog, req, id)) + return; + + error("hog: Could not discover descriptors"); + g_attrib_cancel(attrib, id); + free(req); +} + +static void discover_char(struct bt_hog *hog, GAttrib *attrib, + uint16_t start, uint16_t end, + bt_uuid_t *uuid, gatt_cb_t func, + gpointer user_data) +{ + struct gatt_request *req; + unsigned int id; + + req = create_request(hog, user_data); + if (!req) + return; + + id = gatt_discover_char(attrib, start, end, uuid, func, req); + + if (set_and_store_gatt_req(hog, req, id)) + return; + + error("hog: Could not discover characteristic"); + g_attrib_cancel(attrib, id); + free(req); +} + +static void discover_primary(struct bt_hog *hog, GAttrib *attrib, + bt_uuid_t *uuid, gatt_cb_t func, + gpointer user_data) +{ + struct gatt_request *req; + unsigned int id; + + req = create_request(hog, user_data); + if (!req) + return; + + id = gatt_discover_primary(attrib, uuid, func, req); + + if (set_and_store_gatt_req(hog, req, id)) + return; + + error("hog: Could not send discover primary"); + g_attrib_cancel(attrib, id); + free(req); +} + +static void find_included(struct bt_hog *hog, GAttrib *attrib, + uint16_t start, uint16_t end, + gatt_cb_t func, gpointer user_data) +{ + struct gatt_request *req; + unsigned int id; + + req = create_request(hog, user_data); + if (!req) + return; + + id = gatt_find_included(attrib, start, end, func, req); + + if (set_and_store_gatt_req(hog, req, id)) + return; + + error("Could not find included"); + g_attrib_cancel(attrib, id); + free(req); +} + +static void report_value_cb(const guint8 *pdu, guint16 len, gpointer user_data) +{ + struct report *report = user_data; + struct bt_hog *hog = report->hog; + struct uhid_event ev; + uint8_t *buf; + int err; + + if (len < ATT_NOTIFICATION_HEADER_SIZE) { + error("Malformed ATT notification"); + return; + } + + pdu += ATT_NOTIFICATION_HEADER_SIZE; + len -= ATT_NOTIFICATION_HEADER_SIZE; + + memset(&ev, 0, sizeof(ev)); + ev.type = UHID_INPUT; + buf = ev.u.input.data; + + if (hog->has_report_id) { + buf[0] = report->id; + len = MIN(len, sizeof(ev.u.input.data) - 1); + memcpy(buf + 1, pdu, len); + ev.u.input.size = ++len; + } else { + len = MIN(len, sizeof(ev.u.input.data)); + memcpy(buf, pdu, len); + ev.u.input.size = len; + } + + err = bt_uhid_send(hog->uhid, &ev); + if (err < 0) { + error("bt_uhid_send: %s (%d)", strerror(-err), -err); + return; + } + + DBG("HoG report (%u bytes)", ev.u.input.size); +} + +static void report_ccc_written_cb(guint8 status, const guint8 *pdu, + guint16 plen, gpointer user_data) +{ + struct gatt_request *req = user_data; + struct report *report = req->user_data; + struct bt_hog *hog = report->hog; + + destroy_gatt_req(req); + + if (status != 0) { + error("Write report characteristic descriptor failed: %s", + att_ecode2str(status)); + return; + } + + report->notifyid = g_attrib_register(hog->attrib, + ATT_OP_HANDLE_NOTIFY, + report->decl->value_handle, + report_value_cb, report, NULL); + + DBG("Report characteristic descriptor written: notifications enabled"); +} + +static void write_ccc(struct bt_hog *hog, GAttrib *attrib, uint16_t handle, + void *user_data) +{ + uint8_t value[2]; + + put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value); + + write_char(hog, attrib, handle, value, sizeof(value), + report_ccc_written_cb, user_data); +} + +static void ccc_read_cb(guint8 status, const guint8 *pdu, guint16 len, + gpointer user_data) +{ + struct gatt_request *req = user_data; + struct report *report = req->user_data; + + destroy_gatt_req(req); + + if (status != 0) { + error("Error reading CCC value: %s", att_ecode2str(status)); + return; + } + + write_ccc(report->hog, report->hog->attrib, report->ccc_handle, report); +} + +static void report_reference_cb(guint8 status, const guint8 *pdu, + guint16 plen, gpointer user_data) +{ + struct gatt_request *req = user_data; + struct report *report = req->user_data; + + destroy_gatt_req(req); + + if (status != 0) { + error("Read Report Reference descriptor failed: %s", + att_ecode2str(status)); + return; + } + + if (plen != 3) { + error("Malformed ATT read response"); + return; + } + + report->id = pdu[1]; + report->type = pdu[2]; + DBG("Report ID: 0x%02x Report type: 0x%02x", pdu[1], pdu[2]); + + /* Enable notifications only for Input Reports */ + if (report->type == HOG_REPORT_TYPE_INPUT) + read_char(report->hog, report->hog->attrib, report->ccc_handle, + ccc_read_cb, report); +} + +static void external_report_reference_cb(guint8 status, const guint8 *pdu, + guint16 plen, gpointer user_data); + +static void discover_external_cb(uint8_t status, GSList *descs, void *user_data) +{ + struct gatt_request *req = user_data; + struct bt_hog *hog = req->user_data; + + destroy_gatt_req(req); + + if (status != 0) { + error("Discover external descriptors failed: %s", + att_ecode2str(status)); + return; + } + + for ( ; descs; descs = descs->next) { + struct gatt_desc *desc = descs->data; + + read_char(hog, hog->attrib, desc->handle, + external_report_reference_cb, + hog); + } +} + +static void discover_external(struct bt_hog *hog, GAttrib *attrib, + uint16_t start, uint16_t end, + gpointer user_data) +{ + bt_uuid_t uuid; + + if (start > end) + return; + + bt_uuid16_create(&uuid, GATT_EXTERNAL_REPORT_REFERENCE); + + discover_desc(hog, attrib, start, end, discover_external_cb, + user_data); +} + +static void discover_report_cb(uint8_t status, GSList *descs, void *user_data) +{ + struct gatt_request *req = user_data; + struct report *report = req->user_data; + struct bt_hog *hog = report->hog; + + destroy_gatt_req(req); + + if (status != 0) { + error("Discover report descriptors failed: %s", + att_ecode2str(status)); + return; + } + + for ( ; descs; descs = descs->next) { + struct gatt_desc *desc = descs->data; + + switch (desc->uuid16) { + case GATT_CLIENT_CHARAC_CFG_UUID: + report->ccc_handle = desc->handle; + break; + case GATT_REPORT_REFERENCE: + read_char(hog, hog->attrib, desc->handle, + report_reference_cb, report); + break; + } + } +} + +static void discover_report(struct bt_hog *hog, GAttrib *attrib, + uint16_t start, uint16_t end, + gpointer user_data) +{ + if (start > end) + return; + + discover_desc(hog, attrib, start, end, discover_report_cb, user_data); +} + +static void report_read_cb(guint8 status, const guint8 *pdu, guint16 len, + gpointer user_data) +{ + struct gatt_request *req = user_data; + struct report *report = req->user_data; + + destroy_gatt_req(req); + + if (status != 0) { + error("Error reading Report value: %s", att_ecode2str(status)); + return; + } + + if (report->value) + g_free(report->value); + + report->value = g_memdup(pdu, len); + report->len = len; +} + +static struct report *report_new(struct bt_hog *hog, struct gatt_char *chr) +{ + struct report *report; + + report = g_new0(struct report, 1); + report->hog = hog; + report->decl = g_memdup(chr, sizeof(*chr)); + hog->reports = g_slist_append(hog->reports, report); + + read_char(hog, hog->attrib, chr->value_handle, report_read_cb, report); + + return report; +} + +static void external_service_char_cb(uint8_t status, GSList *chars, + void *user_data) +{ + struct gatt_request *req = user_data; + struct bt_hog *hog = req->user_data; + struct gatt_primary *primary = hog->primary; + struct report *report; + GSList *l; + + destroy_gatt_req(req); + + if (status != 0) { + const char *str = att_ecode2str(status); + DBG("Discover external service characteristic failed: %s", str); + return; + } + + for (l = chars; l; l = g_slist_next(l)) { + struct gatt_char *chr, *next; + uint16_t start, end; + + chr = l->data; + next = l->next ? l->next->data : NULL; + + DBG("0x%04x UUID: %s properties: %02x", + chr->handle, chr->uuid, chr->properties); + + report = report_new(hog, chr); + start = chr->value_handle + 1; + end = (next ? next->handle - 1 : primary->range.end); + discover_report(hog, hog->attrib, start, end, report); + } +} + +static void external_report_reference_cb(guint8 status, const guint8 *pdu, + guint16 plen, gpointer user_data) +{ + struct gatt_request *req = user_data; + struct bt_hog *hog = req->user_data; + uint16_t uuid16; + bt_uuid_t uuid; + + destroy_gatt_req(req); + + if (status != 0) { + error("Read External Report Reference descriptor failed: %s", + att_ecode2str(status)); + return; + } + + if (plen != 3) { + error("Malformed ATT read response"); + return; + } + + uuid16 = get_le16(&pdu[1]); + DBG("External report reference read, external report characteristic " + "UUID: 0x%04x", uuid16); + + /* Do not discover if is not a Report */ + if (uuid16 != HOG_REPORT_UUID) + return; + + bt_uuid16_create(&uuid, uuid16); + discover_char(hog, hog->attrib, 0x0001, 0xffff, &uuid, + external_service_char_cb, hog); +} + +static int report_cmp(gconstpointer a, gconstpointer b) +{ + const struct report *ra = a, *rb = b; + + /* sort by type first.. */ + if (ra->type != rb->type) + return ra->type - rb->type; + + /* skip id check in case of report id 0 */ + if (!rb->id) + return 0; + + /* ..then by id */ + return ra->id - rb->id; +} + +static struct report *find_report(struct bt_hog *hog, uint8_t type, uint8_t id) +{ + struct report cmp; + GSList *l; + + cmp.type = type; + cmp.id = hog->has_report_id ? id : 0; + + l = g_slist_find_custom(hog->reports, &cmp, report_cmp); + + return l ? l->data : NULL; +} + +static struct report *find_report_by_rtype(struct bt_hog *hog, uint8_t rtype, + uint8_t id) +{ + uint8_t type; + + switch (rtype) { + case UHID_FEATURE_REPORT: + type = HOG_REPORT_TYPE_FEATURE; + break; + case UHID_OUTPUT_REPORT: + type = HOG_REPORT_TYPE_OUTPUT; + break; + case UHID_INPUT_REPORT: + type = HOG_REPORT_TYPE_INPUT; + break; + default: + return NULL; + } + + return find_report(hog, type, id); +} + +static void output_written_cb(guint8 status, const guint8 *pdu, + guint16 plen, gpointer user_data) +{ + struct gatt_request *req = user_data; + + destroy_gatt_req(req); + + if (status != 0) { + error("Write output report failed: %s", att_ecode2str(status)); + return; + } +} + +static void forward_report(struct uhid_event *ev, void *user_data) +{ + struct bt_hog *hog = user_data; + struct report *report; + void *data; + int size; + + report = find_report_by_rtype(hog, ev->u.output.rtype, + ev->u.output.data[0]); + if (!report) + return; + + data = ev->u.output.data; + size = ev->u.output.size; + if (hog->has_report_id && size > 0) { + data++; + --size; + } + + DBG("Sending report type %d ID %d to handle 0x%X", report->type, + report->id, report->decl->value_handle); + + if (hog->attrib == NULL) + return; + + if (report->decl->properties & GATT_CHR_PROP_WRITE) + write_char(hog, hog->attrib, report->decl->value_handle, + data, size, output_written_cb, hog); + else if (report->decl->properties & GATT_CHR_PROP_WRITE_WITHOUT_RESP) + gatt_write_cmd(hog->attrib, report->decl->value_handle, + data, size, NULL, NULL); +} + +static void get_feature(struct uhid_event *ev, void *user_data) +{ + struct bt_hog *hog = user_data; + struct report *report; + struct uhid_event rsp; + int err; + + memset(&rsp, 0, sizeof(rsp)); + rsp.type = UHID_FEATURE_ANSWER; + rsp.u.feature_answer.id = ev->u.feature.id; + + report = find_report_by_rtype(hog, ev->u.feature.rtype, + ev->u.feature.rnum); + if (!report) { + rsp.u.feature_answer.err = ENOTSUP; + goto done; + } + + if (!report->value) { + rsp.u.feature_answer.err = EIO; + goto done; + } + + rsp.u.feature_answer.size = report->len; + memcpy(rsp.u.feature_answer.data, report->value, report->len); + +done: + err = bt_uhid_send(hog->uhid, &rsp); + if (err < 0) + error("bt_uhid_send: %s", strerror(-err)); +} + +static void set_report_cb(guint8 status, const guint8 *pdu, + guint16 plen, gpointer user_data) +{ + struct bt_hog *hog = user_data; + struct uhid_event rsp; + int err; + + hog->setrep_att = 0; + + memset(&rsp, 0, sizeof(rsp)); + rsp.type = UHID_SET_REPORT_REPLY; + rsp.u.set_report_reply.id = hog->setrep_id; + rsp.u.set_report_reply.err = status; + + if (status != 0) + error("Error setting Report value: %s", att_ecode2str(status)); + + err = bt_uhid_send(hog->uhid, &rsp); + if (err < 0) + error("bt_uhid_send: %s", strerror(-err)); +} + +static void set_report(struct uhid_event *ev, void *user_data) +{ + struct bt_hog *hog = user_data; + struct report *report; + void *data; + int size; + int err; + + /* uhid never sends reqs in parallel; if there's a req, it timed out */ + if (hog->setrep_att) { + g_attrib_cancel(hog->attrib, hog->setrep_att); + hog->setrep_att = 0; + } + + hog->setrep_id = ev->u.set_report.id; + + report = find_report_by_rtype(hog, ev->u.set_report.rtype, + ev->u.set_report.rnum); + if (!report) { + err = ENOTSUP; + goto fail; + } + + data = ev->u.set_report.data; + size = ev->u.set_report.size; + if (hog->has_report_id && size > 0) { + data++; + --size; + } + + DBG("Sending report type %d ID %d to handle 0x%X", report->type, + report->id, report->decl->value_handle); + + if (hog->attrib == NULL) + return; + + hog->setrep_att = gatt_write_char(hog->attrib, + report->decl->value_handle, + data, size, set_report_cb, + hog); + if (!hog->setrep_att) { + err = ENOMEM; + goto fail; + } + + return; +fail: + /* cancel the request on failure */ + set_report_cb(err, NULL, 0, hog); +} + +static void get_report_cb(guint8 status, const guint8 *pdu, guint16 len, + gpointer user_data) +{ + struct bt_hog *hog = user_data; + struct uhid_event rsp; + int err; + + hog->getrep_att = 0; + + memset(&rsp, 0, sizeof(rsp)); + rsp.type = UHID_GET_REPORT_REPLY; + rsp.u.get_report_reply.id = hog->getrep_id; + + if (status != 0) { + error("Error reading Report value: %s", att_ecode2str(status)); + goto exit; + } + + if (len == 0) { + error("Error reading Report, length %d", len); + status = EIO; + goto exit; + } + + if (pdu[0] != 0x0b) { + error("Error reading Report, invalid response: %02x", pdu[0]); + status = EPROTO; + goto exit; + } + + --len; + ++pdu; + if (hog->has_report_id && len > 0) { + --len; + ++pdu; + } + + rsp.u.get_report_reply.size = len; + memcpy(rsp.u.get_report_reply.data, pdu, len); + +exit: + rsp.u.get_report_reply.err = status; + err = bt_uhid_send(hog->uhid, &rsp); + if (err < 0) + error("bt_uhid_send: %s", strerror(-err)); +} + +static void get_report(struct uhid_event *ev, void *user_data) +{ + struct bt_hog *hog = user_data; + struct report *report; + guint8 err; + + /* uhid never sends reqs in parallel; if there's a req, it timed out */ + if (hog->getrep_att) { + g_attrib_cancel(hog->attrib, hog->getrep_att); + hog->getrep_att = 0; + } + + hog->getrep_id = ev->u.get_report.id; + + report = find_report_by_rtype(hog, ev->u.get_report.rtype, + ev->u.get_report.rnum); + if (!report) { + err = ENOTSUP; + goto fail; + } + + hog->getrep_att = gatt_read_char(hog->attrib, + report->decl->value_handle, + get_report_cb, hog); + if (!hog->getrep_att) { + err = ENOMEM; + goto fail; + } + + return; + +fail: + /* cancel the request on failure */ + get_report_cb(err, NULL, 0, hog); +} + +static bool get_descriptor_item_info(uint8_t *buf, ssize_t blen, ssize_t *len, + bool *is_long) +{ + if (!blen) + return false; + + *is_long = (buf[0] == 0xfe); + + if (*is_long) { + if (blen < 3) + return false; + + /* + * long item: + * byte 0 -> 0xFE + * byte 1 -> data size + * byte 2 -> tag + * + data + */ + + *len = buf[1] + 3; + } else { + uint8_t b_size; + + /* + * short item: + * byte 0[1..0] -> data size (=0, 1, 2, 4) + * byte 0[3..2] -> type + * byte 0[7..4] -> tag + * + data + */ + + b_size = buf[0] & 0x03; + *len = (b_size ? 1 << (b_size - 1) : 0) + 1; + } + + /* item length should be no more than input buffer length */ + return *len <= blen; +} + +static char *item2string(char *str, uint8_t *buf, uint8_t len) +{ + char *p = str; + int i; + + /* + * Since long item tags are not defined except for vendor ones, we + * just ensure that short items are printed properly (up to 5 bytes). + */ + for (i = 0; i < 6 && i < len; i++) + p += sprintf(p, " %02x", buf[i]); + + /* + * If there are some data left, just add continuation mark to indicate + * this. + */ + if (i < len) + sprintf(p, " ..."); + + return str; +} + +static void report_map_read_cb(guint8 status, const guint8 *pdu, guint16 plen, + gpointer user_data) +{ + struct gatt_request *req = user_data; + struct bt_hog *hog = req->user_data; + uint8_t value[HOG_REPORT_MAP_MAX_SIZE]; + struct uhid_event ev; + ssize_t vlen; + char itemstr[20]; /* 5x3 (data) + 4 (continuation) + 1 (null) */ + int i, err; + GError *gerr = NULL; + + destroy_gatt_req(req); + + if (status != 0) { + error("Report Map read failed: %s", att_ecode2str(status)); + return; + } + + vlen = dec_read_resp(pdu, plen, value, sizeof(value)); + if (vlen < 0) { + error("ATT protocol error"); + return; + } + + DBG("Report MAP:"); + for (i = 0; i < vlen;) { + ssize_t ilen = 0; + bool long_item = false; + + if (get_descriptor_item_info(&value[i], vlen - i, &ilen, + &long_item)) { + /* Report ID is short item with prefix 100001xx */ + if (!long_item && (value[i] & 0xfc) == 0x84) + hog->has_report_id = TRUE; + + DBG("\t%s", item2string(itemstr, &value[i], ilen)); + + i += ilen; + } else { + error("Report Map parsing failed at %d", i); + + /* Just print remaining items at once and break */ + DBG("\t%s", item2string(itemstr, &value[i], vlen - i)); + break; + } + } + + /* create uHID device */ + memset(&ev, 0, sizeof(ev)); + ev.type = UHID_CREATE; + + bt_io_get(g_attrib_get_channel(hog->attrib), &gerr, + BT_IO_OPT_SOURCE, ev.u.create.phys, + BT_IO_OPT_DEST, ev.u.create.uniq, + BT_IO_OPT_INVALID); + if (gerr) { + error("Failed to connection details: %s", gerr->message); + g_error_free(gerr); + return; + } + + strcpy((char *) ev.u.create.name, hog->name); + ev.u.create.vendor = hog->vendor; + ev.u.create.product = hog->product; + ev.u.create.version = hog->version; + ev.u.create.country = hog->bcountrycode; + ev.u.create.bus = BUS_BLUETOOTH; + ev.u.create.rd_data = value; + ev.u.create.rd_size = vlen; + + err = bt_uhid_send(hog->uhid, &ev); + if (err < 0) { + error("bt_uhid_send: %s", strerror(-err)); + return; + } + + bt_uhid_register(hog->uhid, UHID_OUTPUT, forward_report, hog); + bt_uhid_register(hog->uhid, UHID_FEATURE, get_feature, hog); + bt_uhid_register(hog->uhid, UHID_GET_REPORT, get_report, hog); + bt_uhid_register(hog->uhid, UHID_SET_REPORT, set_report, hog); +} + +static void info_read_cb(guint8 status, const guint8 *pdu, guint16 plen, + gpointer user_data) +{ + struct gatt_request *req = user_data; + struct bt_hog *hog = req->user_data; + uint8_t value[HID_INFO_SIZE]; + ssize_t vlen; + + destroy_gatt_req(req); + + if (status != 0) { + error("HID Information read failed: %s", + att_ecode2str(status)); + return; + } + + vlen = dec_read_resp(pdu, plen, value, sizeof(value)); + if (vlen != 4) { + error("ATT protocol error"); + return; + } + + hog->bcdhid = get_le16(&value[0]); + hog->bcountrycode = value[2]; + hog->flags = value[3]; + + DBG("bcdHID: 0x%04X bCountryCode: 0x%02X Flags: 0x%02X", + hog->bcdhid, hog->bcountrycode, hog->flags); +} + +static void proto_mode_read_cb(guint8 status, const guint8 *pdu, guint16 plen, + gpointer user_data) +{ + struct gatt_request *req = user_data; + struct bt_hog *hog = req->user_data; + uint8_t value; + ssize_t vlen; + + destroy_gatt_req(req); + + if (status != 0) { + error("Protocol Mode characteristic read failed: %s", + att_ecode2str(status)); + return; + } + + vlen = dec_read_resp(pdu, plen, &value, sizeof(value)); + if (vlen < 0) { + error("ATT protocol error"); + return; + } + + if (value == HOG_PROTO_MODE_BOOT) { + uint8_t nval = HOG_PROTO_MODE_REPORT; + + DBG("HoG is operating in Boot Procotol Mode"); + + gatt_write_cmd(hog->attrib, hog->proto_mode_handle, &nval, + sizeof(nval), NULL, NULL); + } else if (value == HOG_PROTO_MODE_REPORT) + DBG("HoG is operating in Report Protocol Mode"); +} + +static void char_discovered_cb(uint8_t status, GSList *chars, void *user_data) +{ + struct gatt_request *req = user_data; + struct bt_hog *hog = req->user_data; + struct gatt_primary *primary = hog->primary; + bt_uuid_t report_uuid, report_map_uuid, info_uuid; + bt_uuid_t proto_mode_uuid, ctrlpt_uuid; + struct report *report; + GSList *l; + uint16_t info_handle = 0, proto_mode_handle = 0; + + destroy_gatt_req(req); + + if (status != 0) { + const char *str = att_ecode2str(status); + DBG("Discover all characteristics failed: %s", str); + return; + } + + bt_uuid16_create(&report_uuid, HOG_REPORT_UUID); + bt_uuid16_create(&report_map_uuid, HOG_REPORT_MAP_UUID); + bt_uuid16_create(&info_uuid, HOG_INFO_UUID); + bt_uuid16_create(&proto_mode_uuid, HOG_PROTO_MODE_UUID); + bt_uuid16_create(&ctrlpt_uuid, HOG_CONTROL_POINT_UUID); + + for (l = chars; l; l = g_slist_next(l)) { + struct gatt_char *chr, *next; + bt_uuid_t uuid; + uint16_t start, end; + + chr = l->data; + next = l->next ? l->next->data : NULL; + + DBG("0x%04x UUID: %s properties: %02x", + chr->handle, chr->uuid, chr->properties); + + bt_string_to_uuid(&uuid, chr->uuid); + + start = chr->value_handle + 1; + end = (next ? next->handle - 1 : primary->range.end); + + if (bt_uuid_cmp(&uuid, &report_uuid) == 0) { + report = report_new(hog, chr); + discover_report(hog, hog->attrib, start, end, report); + } else if (bt_uuid_cmp(&uuid, &report_map_uuid) == 0) { + read_char(hog, hog->attrib, chr->value_handle, + report_map_read_cb, hog); + discover_external(hog, hog->attrib, start, end, hog); + } else if (bt_uuid_cmp(&uuid, &info_uuid) == 0) + info_handle = chr->value_handle; + else if (bt_uuid_cmp(&uuid, &proto_mode_uuid) == 0) + proto_mode_handle = chr->value_handle; + else if (bt_uuid_cmp(&uuid, &ctrlpt_uuid) == 0) + hog->ctrlpt_handle = chr->value_handle; + } + + if (proto_mode_handle) { + hog->proto_mode_handle = proto_mode_handle; + read_char(hog, hog->attrib, proto_mode_handle, + proto_mode_read_cb, hog); + } + + if (info_handle) + read_char(hog, hog->attrib, info_handle, info_read_cb, hog); +} + +static void report_free(void *data) +{ + struct report *report = data; + + g_free(report->value); + g_free(report->decl); + g_free(report); +} + +static void cancel_gatt_req(struct gatt_request *req) +{ + if (g_attrib_cancel(req->hog->attrib, req->id)) + destroy_gatt_req(req); +} + +static void hog_free(void *data) +{ + struct bt_hog *hog = data; + + bt_hog_detach(hog); + + queue_destroy(hog->bas, (void *) bt_bas_unref); + g_slist_free_full(hog->instances, hog_free); + + bt_scpp_unref(hog->scpp); + bt_dis_unref(hog->dis); + bt_uhid_unref(hog->uhid); + g_slist_free_full(hog->reports, report_free); + g_free(hog->name); + g_free(hog->primary); + queue_destroy(hog->gatt_op, (void *) destroy_gatt_req); + g_free(hog); +} + +struct bt_hog *bt_hog_new_default(const char *name, uint16_t vendor, + uint16_t product, uint16_t version, + void *primary) +{ + return bt_hog_new(-1, name, vendor, product, version, primary); +} + +struct bt_hog *bt_hog_new(int fd, const char *name, uint16_t vendor, + uint16_t product, uint16_t version, + void *primary) +{ + struct bt_hog *hog; + + hog = g_try_new0(struct bt_hog, 1); + if (!hog) + return NULL; + + hog->gatt_op = queue_new(); + hog->bas = queue_new(); + + if (fd < 0) + hog->uhid = bt_uhid_new_default(); + else + hog->uhid = bt_uhid_new(fd); + + hog->uhid_fd = fd; + + if (!hog->gatt_op || !hog->bas || !hog->uhid) { + hog_free(hog); + return NULL; + } + + hog->name = g_strdup(name); + hog->vendor = vendor; + hog->product = product; + hog->version = version; + + if (primary) + hog->primary = g_memdup(primary, sizeof(*hog->primary)); + + return bt_hog_ref(hog); +} + +struct bt_hog *bt_hog_ref(struct bt_hog *hog) +{ + if (!hog) + return NULL; + + __sync_fetch_and_add(&hog->ref_count, 1); + + return hog; +} + +void bt_hog_unref(struct bt_hog *hog) +{ + if (!hog) + return; + + if (__sync_sub_and_fetch(&hog->ref_count, 1)) + return; + + hog_free(hog); +} + +static void find_included_cb(uint8_t status, GSList *services, void *user_data) +{ + struct gatt_request *req = user_data; + GSList *l; + + DBG(""); + + destroy_gatt_req(req); + + if (status) { + const char *str = att_ecode2str(status); + DBG("Find included failed: %s", str); + return; + } + + for (l = services; l; l = l->next) { + struct gatt_included *include = l->data; + + DBG("included: handle %x, uuid %s", + include->handle, include->uuid); + } +} + +static void hog_attach_scpp(struct bt_hog *hog, struct gatt_primary *primary) +{ + if (hog->scpp) { + bt_scpp_attach(hog->scpp, hog->attrib); + return; + } + + hog->scpp = bt_scpp_new(primary); + if (hog->scpp) + bt_scpp_attach(hog->scpp, hog->attrib); +} + +static void dis_notify(uint8_t source, uint16_t vendor, uint16_t product, + uint16_t version, void *user_data) +{ + struct bt_hog *hog = user_data; + + hog->vendor = vendor; + hog->product = product; + hog->version = version; +} + +static void hog_attach_dis(struct bt_hog *hog, struct gatt_primary *primary) +{ + if (hog->dis) { + bt_dis_attach(hog->dis, hog->attrib); + return; + } + + hog->dis = bt_dis_new(primary); + if (hog->dis) { + bt_dis_set_notification(hog->dis, dis_notify, hog); + bt_dis_attach(hog->dis, hog->attrib); + } +} + +static void hog_attach_bas(struct bt_hog *hog, struct gatt_primary *primary) +{ + struct bt_bas *instance; + + instance = bt_bas_new(primary); + if (!instance) + return; + + bt_bas_attach(instance, hog->attrib); + queue_push_head(hog->bas, instance); +} + +static void hog_attach_hog(struct bt_hog *hog, struct gatt_primary *primary) +{ + struct bt_hog *instance; + + if (!hog->primary) { + hog->primary = g_memdup(primary, sizeof(*primary)); + discover_char(hog, hog->attrib, primary->range.start, + primary->range.end, NULL, + char_discovered_cb, hog); + find_included(hog, hog->attrib, primary->range.start, + primary->range.end, find_included_cb, hog); + return; + } + + instance = bt_hog_new(hog->uhid_fd, hog->name, hog->vendor, + hog->product, hog->version, primary); + if (!instance) + return; + + find_included(instance, hog->attrib, primary->range.start, + primary->range.end, find_included_cb, instance); + + bt_hog_attach(instance, hog->attrib); + hog->instances = g_slist_append(hog->instances, instance); +} + +static void primary_cb(uint8_t status, GSList *services, void *user_data) +{ + struct gatt_request *req = user_data; + struct bt_hog *hog = req->user_data; + struct gatt_primary *primary; + GSList *l; + + DBG(""); + + destroy_gatt_req(req); + + if (status) { + const char *str = att_ecode2str(status); + DBG("Discover primary failed: %s", str); + return; + } + + if (!services) { + DBG("No primary service found"); + return; + } + + for (l = services; l; l = l->next) { + primary = l->data; + + if (strcmp(primary->uuid, SCAN_PARAMETERS_UUID) == 0) { + hog_attach_scpp(hog, primary); + continue; + } + + if (strcmp(primary->uuid, DEVICE_INFORMATION_UUID) == 0) { + hog_attach_dis(hog, primary); + continue; + } + + if (strcmp(primary->uuid, BATTERY_UUID) == 0) { + hog_attach_bas(hog, primary); + continue; + } + + if (strcmp(primary->uuid, HOG_UUID) == 0) + hog_attach_hog(hog, primary); + } +} + +bool bt_hog_attach(struct bt_hog *hog, void *gatt) +{ + struct gatt_primary *primary = hog->primary; + GSList *l; + + if (hog->attrib) + return false; + + hog->attrib = g_attrib_ref(gatt); + + if (!primary) { + discover_primary(hog, hog->attrib, NULL, primary_cb, hog); + return true; + } + + if (hog->scpp) + bt_scpp_attach(hog->scpp, gatt); + + if (hog->dis) + bt_dis_attach(hog->dis, gatt); + + queue_foreach(hog->bas, (void *) bt_bas_attach, gatt); + + for (l = hog->instances; l; l = l->next) { + struct bt_hog *instance = l->data; + + bt_hog_attach(instance, gatt); + } + + if (hog->reports == NULL) { + discover_char(hog, hog->attrib, primary->range.start, + primary->range.end, NULL, + char_discovered_cb, hog); + return true; + } + + for (l = hog->reports; l; l = l->next) { + struct report *r = l->data; + + r->notifyid = g_attrib_register(hog->attrib, + ATT_OP_HANDLE_NOTIFY, + r->decl->value_handle, + report_value_cb, r, NULL); + } + + return true; +} + +void bt_hog_detach(struct bt_hog *hog) +{ + GSList *l; + + if (!hog->attrib) + return; + + queue_foreach(hog->bas, (void *) bt_bas_detach, NULL); + + for (l = hog->instances; l; l = l->next) { + struct bt_hog *instance = l->data; + + bt_hog_detach(instance); + } + + for (l = hog->reports; l; l = l->next) { + struct report *r = l->data; + + if (r->notifyid > 0) { + g_attrib_unregister(hog->attrib, r->notifyid); + r->notifyid = 0; + } + } + + if (hog->scpp) + bt_scpp_detach(hog->scpp); + + if (hog->dis) + bt_dis_detach(hog->dis); + + queue_foreach(hog->gatt_op, (void *) cancel_gatt_req, NULL); + g_attrib_unref(hog->attrib); + hog->attrib = NULL; +} + +int bt_hog_set_control_point(struct bt_hog *hog, bool suspend) +{ + uint8_t value = suspend ? 0x00 : 0x01; + + if (hog->attrib == NULL) + return -ENOTCONN; + + if (hog->ctrlpt_handle == 0) + return -ENOTSUP; + + gatt_write_cmd(hog->attrib, hog->ctrlpt_handle, &value, + sizeof(value), NULL, NULL); + + return 0; +} + +int bt_hog_send_report(struct bt_hog *hog, void *data, size_t size, int type) +{ + struct report *report; + GSList *l; + + if (!hog) + return -EINVAL; + + if (!hog->attrib) + return -ENOTCONN; + + report = find_report(hog, type, 0); + if (!report) + return -ENOTSUP; + + DBG("hog: Write report, handle 0x%X", report->decl->value_handle); + + if (report->decl->properties & GATT_CHR_PROP_WRITE) + write_char(hog, hog->attrib, report->decl->value_handle, + data, size, output_written_cb, hog); + + if (report->decl->properties & GATT_CHR_PROP_WRITE_WITHOUT_RESP) + gatt_write_cmd(hog->attrib, report->decl->value_handle, + data, size, NULL, NULL); + + for (l = hog->instances; l; l = l->next) { + struct bt_hog *instance = l->data; + + bt_hog_send_report(instance, data, size, type); + } + + return 0; +} diff --git a/android/hog.h b/android/hog.h new file mode 100755 index 0000000..2a9b899 --- /dev/null +++ b/android/hog.h @@ -0,0 +1,41 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2014 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +struct bt_hog; + +struct bt_hog *bt_hog_new_default(const char *name, uint16_t vendor, + uint16_t product, uint16_t version, + void *primary); + +struct bt_hog *bt_hog_new(int fd, const char *name, uint16_t vendor, + uint16_t product, uint16_t version, + void *primary); + +struct bt_hog *bt_hog_ref(struct bt_hog *hog); +void bt_hog_unref(struct bt_hog *hog); + +bool bt_hog_attach(struct bt_hog *hog, void *gatt); +void bt_hog_detach(struct bt_hog *hog); + +int bt_hog_set_control_point(struct bt_hog *hog, bool suspend); +int bt_hog_send_report(struct bt_hog *hog, void *data, size_t size, int type); diff --git a/android/sco-ipc-api.txt b/android/sco-ipc-api.txt new file mode 100755 index 0000000..27d5ef2 --- /dev/null +++ b/android/sco-ipc-api.txt @@ -0,0 +1,37 @@ +Bluetooth SCO Audio Plugin +========================== + +The SCO Audio Plugin communicate through abstract socket name +"\0bluez_sco_socket". + + .----SCO----. .--Android--. + | Plugin | | Daemon | + | | Command | | + | | --------------------------> | | + | | | | + | | <-------------------------- | | + | | Response | | + | | | | + | | | | + | | | | + '-----------' '-----------' + + + SCO HAL Daemon + ---------------------------------------------------- + + call get_fd() --> Get SCO socket fd + return get_fd() <-- Return SCO socket fd and mtu + +SCO Audio Service (ID 0) +======================== + + Opcode 0x00 - Error response + + Response parameters: Status (1 octet) + + Opcode 0x01 - Get SCO fd command + + Command parameters: Remote address (6 octets) + Response parameters: MTU (2 octets) + File descriptor (inline) diff --git a/android/scpp.c b/android/scpp.c new file mode 100755 index 0000000..f8f81f3 --- /dev/null +++ b/android/scpp.c @@ -0,0 +1,371 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2012 Nordic Semiconductor Inc. + * Copyright (C) 2012 Instituto Nokia de Tecnologia - INdT + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include + +#include "src/log.h" + +#include "lib/bluetooth.h" +#include "lib/sdp.h" +#include "lib/uuid.h" + +#include "src/shared/util.h" +#include "src/shared/queue.h" + +#include "attrib/att.h" +#include "attrib/gattrib.h" +#include "attrib/gatt.h" + +#include "android/scpp.h" + +#define SCAN_INTERVAL_WIN_UUID 0x2A4F +#define SCAN_REFRESH_UUID 0x2A31 + +#define SCAN_INTERVAL 0x0060 +#define SCAN_WINDOW 0x0030 +#define SERVER_REQUIRES_REFRESH 0x00 + +struct bt_scpp { + int ref_count; + GAttrib *attrib; + struct gatt_primary *primary; + uint16_t interval; + uint16_t window; + uint16_t iwhandle; + uint16_t refresh_handle; + guint refresh_cb_id; + struct queue *gatt_op; +}; + +static void discover_char(struct bt_scpp *scpp, GAttrib *attrib, + uint16_t start, uint16_t end, + bt_uuid_t *uuid, gatt_cb_t func, + gpointer user_data) +{ + unsigned int id; + + id = gatt_discover_char(attrib, start, end, uuid, func, user_data); + + if (queue_push_head(scpp->gatt_op, UINT_TO_PTR(id))) + return; + + error("scpp: Could not discover characteristic"); + g_attrib_cancel(attrib, id); +} + +static void discover_desc(struct bt_scpp *scpp, GAttrib *attrib, + uint16_t start, uint16_t end, bt_uuid_t *uuid, + gatt_cb_t func, gpointer user_data) +{ + unsigned int id; + + id = gatt_discover_desc(attrib, start, end, uuid, func, user_data); + + if (queue_push_head(scpp->gatt_op, UINT_TO_PTR(id))) + return; + + error("scpp: Could not discover descriptor"); + g_attrib_cancel(attrib, id); +} + +static void write_char(struct bt_scpp *scan, GAttrib *attrib, uint16_t handle, + const uint8_t *value, size_t vlen, + GAttribResultFunc func, + gpointer user_data) +{ + unsigned int id; + + id = gatt_write_char(attrib, handle, value, vlen, func, user_data); + + if (queue_push_head(scan->gatt_op, UINT_TO_PTR(id))) + return; + + error("scpp: Could not read char"); + g_attrib_cancel(attrib, id); +} + +static void scpp_free(struct bt_scpp *scan) +{ + bt_scpp_detach(scan); + + g_free(scan->primary); + queue_destroy(scan->gatt_op, NULL); /* cleared in bt_scpp_detach */ + g_free(scan); +} + +struct bt_scpp *bt_scpp_new(void *primary) +{ + struct bt_scpp *scan; + + scan = g_try_new0(struct bt_scpp, 1); + if (!scan) + return NULL; + + scan->interval = SCAN_INTERVAL; + scan->window = SCAN_WINDOW; + + scan->gatt_op = queue_new(); + if (!scan->gatt_op) { + scpp_free(scan); + return NULL; + } + + if (primary) + scan->primary = g_memdup(primary, sizeof(*scan->primary)); + + return bt_scpp_ref(scan); +} + +struct bt_scpp *bt_scpp_ref(struct bt_scpp *scan) +{ + if (!scan) + return NULL; + + __sync_fetch_and_add(&scan->ref_count, 1); + + return scan; +} + +void bt_scpp_unref(struct bt_scpp *scan) +{ + if (!scan) + return; + + if (__sync_sub_and_fetch(&scan->ref_count, 1)) + return; + + scpp_free(scan); +} + +static void write_scan_params(GAttrib *attrib, uint16_t handle, + uint16_t interval, uint16_t window) +{ + uint8_t value[4]; + + put_le16(interval, &value[0]); + put_le16(window, &value[2]); + + gatt_write_cmd(attrib, handle, value, sizeof(value), NULL, NULL); +} + +static void refresh_value_cb(const uint8_t *pdu, uint16_t len, + gpointer user_data) +{ + struct bt_scpp *scan = user_data; + + DBG("Server requires refresh: %d", pdu[3]); + + if (pdu[3] == SERVER_REQUIRES_REFRESH) + write_scan_params(scan->attrib, scan->iwhandle, scan->interval, + scan->window); +} + +static void ccc_written_cb(guint8 status, const guint8 *pdu, + guint16 plen, gpointer user_data) +{ + struct bt_scpp *scan = user_data; + + if (status != 0) { + error("Write Scan Refresh CCC failed: %s", + att_ecode2str(status)); + return; + } + + DBG("Scan Refresh: notification enabled"); + + scan->refresh_cb_id = g_attrib_register(scan->attrib, + ATT_OP_HANDLE_NOTIFY, scan->refresh_handle, + refresh_value_cb, scan, NULL); +} + +static void write_ccc(struct bt_scpp *scan, GAttrib *attrib, uint16_t handle, + void *user_data) +{ + uint8_t value[2]; + + put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value); + + write_char(scan, attrib, handle, value, sizeof(value), ccc_written_cb, + user_data); +} + +static void discover_descriptor_cb(uint8_t status, GSList *descs, + void *user_data) +{ + struct bt_scpp *scan = user_data; + struct gatt_desc *desc; + + if (status != 0) { + error("Discover descriptors failed: %s", att_ecode2str(status)); + return; + } + + /* There will be only one descriptor on list and it will be CCC */ + desc = descs->data; + + write_ccc(scan, scan->attrib, desc->handle, scan); +} + +static void refresh_discovered_cb(uint8_t status, GSList *chars, + void *user_data) +{ + struct bt_scpp *scan = user_data; + struct gatt_char *chr; + uint16_t start, end; + bt_uuid_t uuid; + + if (status) { + error("Scan Refresh %s", att_ecode2str(status)); + return; + } + + if (!chars) { + DBG("Scan Refresh not supported"); + return; + } + + chr = chars->data; + + DBG("Scan Refresh handle: 0x%04x", chr->value_handle); + + start = chr->value_handle + 1; + end = scan->primary->range.end; + + if (start > end) + return; + + scan->refresh_handle = chr->value_handle; + + bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID); + + discover_desc(scan, scan->attrib, start, end, &uuid, + discover_descriptor_cb, user_data); +} + +static void iwin_discovered_cb(uint8_t status, GSList *chars, void *user_data) +{ + struct bt_scpp *scan = user_data; + struct gatt_char *chr; + + if (status) { + error("Discover Scan Interval Window: %s", + att_ecode2str(status)); + return; + } + + chr = chars->data; + scan->iwhandle = chr->value_handle; + + DBG("Scan Interval Window handle: 0x%04x", scan->iwhandle); + + write_scan_params(scan->attrib, scan->iwhandle, scan->interval, + scan->window); +} + +bool bt_scpp_attach(struct bt_scpp *scan, void *attrib) +{ + bt_uuid_t iwin_uuid, refresh_uuid; + + if (!scan || scan->attrib || !scan->primary) + return false; + + scan->attrib = g_attrib_ref(attrib); + + if (scan->iwhandle) + write_scan_params(scan->attrib, scan->iwhandle, scan->interval, + scan->window); + else { + bt_uuid16_create(&iwin_uuid, SCAN_INTERVAL_WIN_UUID); + discover_char(scan, scan->attrib, scan->primary->range.start, + scan->primary->range.end, &iwin_uuid, + iwin_discovered_cb, scan); + } + + if (scan->refresh_handle) + scan->refresh_cb_id = g_attrib_register(scan->attrib, + ATT_OP_HANDLE_NOTIFY, scan->refresh_handle, + refresh_value_cb, scan, NULL); + else { + bt_uuid16_create(&refresh_uuid, SCAN_REFRESH_UUID); + discover_char(scan, scan->attrib, scan->primary->range.start, + scan->primary->range.end, &refresh_uuid, + refresh_discovered_cb, scan); + } + + return true; +} + +static void cancel_gatt_req(void *data, void *user_data) +{ + unsigned int id = PTR_TO_UINT(data); + struct bt_scpp *scan = user_data; + + g_attrib_cancel(scan->attrib, id); +} + +void bt_scpp_detach(struct bt_scpp *scan) +{ + if (!scan || !scan->attrib) + return; + + if (scan->refresh_cb_id > 0) { + g_attrib_unregister(scan->attrib, scan->refresh_cb_id); + scan->refresh_cb_id = 0; + } + + queue_foreach(scan->gatt_op, cancel_gatt_req, scan); + g_attrib_unref(scan->attrib); + scan->attrib = NULL; +} + +bool bt_scpp_set_interval(struct bt_scpp *scan, uint16_t value) +{ + if (!scan) + return false; + + /* TODO: Check valid range */ + + scan->interval = value; + + return true; +} + +bool bt_scpp_set_window(struct bt_scpp *scan, uint16_t value) +{ + if (!scan) + return false; + + /* TODO: Check valid range */ + + scan->window = value; + + return true; +} diff --git a/android/scpp.h b/android/scpp.h new file mode 100755 index 0000000..048fb9f --- /dev/null +++ b/android/scpp.h @@ -0,0 +1,35 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2014 Intel Corporation. All rights reserved. + * + * + * This library is free software; you can rescpptribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is scpptributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +struct bt_scpp; + +struct bt_scpp *bt_scpp_new(void *primary); + +struct bt_scpp *bt_scpp_ref(struct bt_scpp *scan); +void bt_scpp_unref(struct bt_scpp *scan); + +bool bt_scpp_attach(struct bt_scpp *scan, void *gatt); +void bt_scpp_detach(struct bt_scpp *scan); + +bool bt_scpp_set_interval(struct bt_scpp *scan, uint16_t value); +bool bt_scpp_set_window(struct bt_scpp *scan, uint16_t value); diff --git a/android/socket-api.txt b/android/socket-api.txt new file mode 100755 index 0000000..9f622f9 --- /dev/null +++ b/android/socket-api.txt @@ -0,0 +1,61 @@ +Android Socket protocol for Bluetooth +===================================== + +Since Android switched from BlueZ (where sockets where nicely implemented) to +Bluedroid user space stack there is a need to emulate bluetooth sockets. + +Android Bluetooth Socket Hardware Abstraction Layer (HAL) bt_sock.h has +only 2 functions: + +static btsock_interface_t sock_if = { + sizeof(sock_if), + sock_listen, + sock_connect +}; + +with following parameters: + +sock_listen(btsock_type_t type, const char *service_name, + const uint8_t *uuid, int chan, int *sock_fd, int flags) +sock_connect(const bt_bdaddr_t *bdaddr, btsock_type_t type, + const uint8_t *uuid, int chan, int *sock_fd, int flags) + +socket type RFCOMM is only supported at the moment. uuid and channel used +to decide where to connect. + +sockfd is used to return socket fd to Android framework. It is used to inform +framework when remote device is connected. + +listen() +======== + +Listens on RFCOMM socket, socket channel is either found based on uuid or +channel parameter used directly. Returns sock_fd to Android framework. + +Through this sock_fd channel number as (int) needs to be written right after +listen() succeeds. + +When remote device is connected to this socket we shall send accept signal +through sock_fd + +connect() +========= + +Connects to remote device specified in bd_addr parameter. Socket channel is +found by SDP search of remote device by supplied uuid. Returns sock_fd to +Android framework. + +Through this sock_fd channel number as (int) needs to be written right after +connects() succeeds. + +When remote device is connected to this socket we shall send connect signal +through sock_fd + +The format of connect/accept signal is shown below: + +struct hal_sock_connect_signal { + short size; + uint8_t bdaddr[6]; + int channel; + int status; +} __attribute__((packed)); diff --git a/attrib/att.c b/attrib/att.c index 826b3c1..fef602c 100755 --- a/attrib/att.c +++ b/attrib/att.c @@ -129,11 +129,18 @@ struct att_data_list *att_data_list_alloc(uint16_t num, uint16_t len) return list; } - +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +void get_uuid(uint8_t type, const void *val, bt_uuid_t *uuid) +#else static void get_uuid(uint8_t type, const void *val, bt_uuid_t *uuid) +#endif { if (type == BT_UUID16) bt_uuid16_create(uuid, get_le16(val)); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + else if (type == BT_UUID32) + bt_uuid32_create(uuid, get_le32(val)); +#endif else { uint128_t u128; @@ -153,6 +160,10 @@ uint16_t enc_read_by_grp_req(uint16_t start, uint16_t end, bt_uuid_t *uuid, if (uuid->type == BT_UUID16) uuid_len = 2; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + else if (uuid->type == BT_UUID32) + uuid_len = 4; +#endif else if (uuid->type == BT_UUID128) uuid_len = 16; else @@ -187,6 +198,10 @@ uint16_t dec_read_by_grp_req(const uint8_t *pdu, size_t len, uint16_t *start, if (len == (min_len + 2)) type = BT_UUID16; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + else if (len == (min_len + 4)) + type = BT_UUID32; +#endif else if (len == (min_len + 16)) type = BT_UUID128; else diff --git a/attrib/att.h b/attrib/att.h index 2311aaf..c32f9ff 100755 --- a/attrib/att.h +++ b/attrib/att.h @@ -106,6 +106,26 @@ struct att_range { uint16_t end; }; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +void get_uuid(uint8_t type, const void *val, bt_uuid_t *uuid); + +static inline bt_uuid_t att_get_uuid(const void *ptr, uint8_t len) +{ + bt_uuid_t uuid; + if (len == 2) { + //return att_get_uuid16(ptr); + get_uuid(BT_UUID16, ptr, &uuid); + } else if (len == 4) { + //return att_get_uuid32(ptr); + get_uuid(BT_UUID32, ptr, &uuid); + } else { + //return att_get_uuid128(ptr); + get_uuid(BT_UUID128, ptr, &uuid); + } + return uuid; +} +#endif + struct att_data_list *att_data_list_alloc(uint16_t num, uint16_t len); void att_data_list_free(struct att_data_list *list); diff --git a/attrib/gatt-service.c b/attrib/gatt-service.c index 629d9cf..e434d3b 100755 --- a/attrib/gatt-service.c +++ b/attrib/gatt-service.c @@ -81,17 +81,26 @@ static GSList *parse_opts(gatt_option opt1, va_list args) while (opt != GATT_OPT_INVALID) { switch (opt) { case GATT_OPT_CHR_UUID16: +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + case GATT_OPT_DESC_UUID16: +#endif bt_uuid16_create(&info->uuid, va_arg(args, int)); /* characteristic declaration and value */ info->num_attrs += 2; break; case GATT_OPT_CHR_UUID: +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + case GATT_OPT_DESC_UUID: +#endif memcpy(&info->uuid, va_arg(args, bt_uuid_t *), sizeof(bt_uuid_t)); /* characteristic declaration and value */ info->num_attrs += 2; break; case GATT_OPT_CHR_PROPS: +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + case GATT_OPT_DESC_PROPS: +#endif info->props = va_arg(args, int); if (info->props & (GATT_CHR_PROP_NOTIFY | @@ -103,6 +112,9 @@ static GSList *parse_opts(gatt_option opt1, va_list args) * descriptor, but it is not supported yet. */ break; case GATT_OPT_CHR_VALUE_CB: +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + case GATT_OPT_DESC_VALUE_CB: +#endif cb = g_new0(struct attrib_cb, 1); cb->event = va_arg(args, attrib_event_t); cb->fn = va_arg(args, void *); @@ -130,7 +142,12 @@ static GSList *parse_opts(gatt_option opt1, va_list args) } opt = va_arg(args, gatt_option); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (opt == GATT_OPT_CHR_UUID16 || opt == GATT_OPT_CHR_UUID || + opt == GATT_OPT_DESC_UUID16 || opt == GATT_OPT_DESC_UUID) { +#else if (opt == GATT_OPT_CHR_UUID16 || opt == GATT_OPT_CHR_UUID) { +#endif info = g_new0(struct gatt_info, 1); l = g_slist_append(l, info); } @@ -268,6 +285,10 @@ static gboolean add_characteristic(struct btd_adapter *adapter, if (info->value_handle != NULL) *info->value_handle = a->handle; +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY +/* Since old attrib service implementation add descriptor by default + * if notification and indication properties are set, As per new gatt server implemenation + * CCCD are added by the application*/ /* client characteristic configuration descriptor */ if (info->props & (GATT_CHR_PROP_NOTIFY | GATT_CHR_PROP_INDICATE)) { uint8_t cfg_val[2]; @@ -283,6 +304,7 @@ static gboolean add_characteristic(struct btd_adapter *adapter, if (info->ccc_handle != NULL) *info->ccc_handle = a->handle; } +#endif *handle = h; diff --git a/attrib/gatt-service.h b/attrib/gatt-service.h index 728d3a8..5a8ac38 100755 --- a/attrib/gatt-service.h +++ b/attrib/gatt-service.h @@ -31,8 +31,20 @@ typedef enum { /* a uint16 value */ GATT_OPT_CHR_UUID16, +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + /* bt_uuid_t* value */ + GATT_OPT_DESC_UUID, + + /* a uint16 value */ + GATT_OPT_DESC_UUID16, +#endif + GATT_OPT_CHR_PROPS, GATT_OPT_CHR_VALUE_CB, +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + GATT_OPT_DESC_PROPS, + GATT_OPT_DESC_VALUE_CB, +#endif GATT_OPT_CHR_AUTHENTICATION, GATT_OPT_CHR_AUTHORIZATION, diff --git a/attrib/gatt.c b/attrib/gatt.c index 480f874..72457ce 100755 --- a/attrib/gatt.c +++ b/attrib/gatt.c @@ -185,6 +185,10 @@ static void put_uuid_le(const bt_uuid_t *uuid, void *dst) { if (uuid->type == BT_UUID16) put_le16(uuid->value.u16, dst); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + else if (uuid->type == BT_UUID32) + put_le32(uuid->value.u32, dst); +#endif else /* Convert from 128-bit BE to LE */ bswap_128(&uuid->value.u128, dst); @@ -197,6 +201,13 @@ static void get_uuid128(uint8_t type, const void *val, bt_uuid_t *uuid) bt_uuid16_create(&uuid16, get_le16(val)); bt_uuid_to_uuid128(&uuid16, uuid); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + } else if (type == BT_UUID32) { + bt_uuid_t uuid32; + + bt_uuid32_create(&uuid32, get_le32(val)); + bt_uuid_to_uuid128(&uuid32, uuid); +#endif } else { uint128_t u128; @@ -841,6 +852,45 @@ done: long_read->func(status, rpdu, rlen, long_read->user_data); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +guint gatt_read_char_by_offset(GAttrib *attrib, uint16_t handle, uint16_t offset, + GAttribResultFunc func, gpointer user_data) +{ + uint8_t *buf; + size_t buflen; + guint16 plen; + guint id; + struct read_long_data *long_read; + + long_read = g_try_new0(struct read_long_data, 1); + + if (long_read == NULL) + return 0; + + long_read->attrib = g_attrib_ref(attrib); + long_read->func = func; + long_read->user_data = user_data; + long_read->handle = handle; + + buf = g_attrib_get_buffer(attrib, &buflen); + if (offset > 0) + plen = enc_read_blob_req(handle, offset, buf, buflen); + else + plen = enc_read_req(handle, buf, buflen); + + id = g_attrib_send(attrib, 0, buf, plen, read_char_helper, + long_read, read_long_destroy); + if (id == 0) + g_free(long_read); + else { + __sync_fetch_and_add(&long_read->ref, 1); + long_read->id = id; + } + + return id; +} +#endif + guint gatt_read_char(GAttrib *attrib, uint16_t handle, GAttribResultFunc func, gpointer user_data) { diff --git a/attrib/gatt.h b/attrib/gatt.h index 63b2940..0d998c9 100755 --- a/attrib/gatt.h +++ b/attrib/gatt.h @@ -120,3 +120,8 @@ guint gatt_exchange_mtu(GAttrib *attrib, uint16_t mtu, GAttribResultFunc func, gboolean gatt_parse_record(const sdp_record_t *rec, uuid_t *prim_uuid, uint16_t *psm, uint16_t *start, uint16_t *end); + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +guint gatt_read_char_by_offset(GAttrib *attrib, uint16_t handle, uint16_t offset, + GAttribResultFunc func, gpointer user_data); +#endif diff --git a/attrib/interactive.c b/attrib/interactive.c index 7d4786a..62a25fa 100755 --- a/attrib/interactive.c +++ b/attrib/interactive.c @@ -559,7 +559,9 @@ static void cmd_char_desc(int argcp, char **argvp) static void cmd_read_hnd(int argcp, char **argvp) { int handle; - +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + int offset = 0; +#endif if (conn_state != STATE_CONNECTED) { failed("Disconnected\n"); return; @@ -575,8 +577,18 @@ static void cmd_read_hnd(int argcp, char **argvp) error("Invalid handle: %s\n", argvp[1]); return; } - +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (argcp > 2) { + offset = strtohandle(argvp[2]); + if (offset < 0) { + error("Invalid Offset: %s\n", argvp[2]); + return; + } + } + gatt_read_char_by_offset(attrib, handle, offset, char_read_cb, attrib); +#else gatt_read_char(attrib, handle, char_read_cb, attrib); +#endif } static void cmd_read_uuid(int argcp, char **argvp) @@ -796,8 +808,13 @@ static struct { "Characteristics Discovery" }, { "char-desc", cmd_char_desc, "[start hnd] [end hnd]", "Characteristics Descriptor Discovery" }, +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + { "char-read-hnd", cmd_read_hnd, " [offset]", + "Characteristics Value/Descriptor Read by handle" }, +#else { "char-read-hnd", cmd_read_hnd, "", "Characteristics Value/Descriptor Read by handle" }, +#endif { "char-read-uuid", cmd_read_uuid, " [start hnd] [end hnd]", "Characteristics Value/Descriptor Read by UUID" }, { "char-write-req", cmd_char_write, " ", diff --git a/client/main.c b/client/main.c index e1198a8..bbf4cd3 100755 --- a/client/main.c +++ b/client/main.c @@ -808,6 +808,9 @@ static void cmd_show(const char *arg) print_uuids(proxy); print_property(proxy, "Modalias"); print_property(proxy, "Discovering"); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + print_property(proxy, "Advertising"); +#endif } static void cmd_select(const char *arg) @@ -1368,6 +1371,9 @@ static void cmd_info(const char *arg) print_property(proxy, "LegacyPairing"); print_uuids(proxy); print_property(proxy, "Modalias"); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + print_property(proxy, "ManufacturerDataLen"); +#endif print_property(proxy, "ManufacturerData"); print_property(proxy, "ServiceData"); print_property(proxy, "RSSI"); diff --git a/configure.ac b/configure.ac index d469500..af589ab 100755 --- a/configure.ac +++ b/configure.ac @@ -34,13 +34,8 @@ AC_PROG_LIBTOOL if (test "$USE_MAINTAINER_MODE" = "yes"); then AC_CHECK_PROG(enable_coverage, [lcov], [yes], [no]) - AC_CHECK_PROG(enable_dbus_run_session, [dbus-run-session], [yes]) - AC_CHECK_PROG(enable_valgrind, [valgrind], [yes]) - AC_CHECK_HEADERS(valgrind/memcheck.h) fi AM_CONDITIONAL(COVERAGE, test "${enable_coverage}" = "yes") -AM_CONDITIONAL(DBUS_RUN_SESSION, test "${enable_dbus_run_session}" = "yes") -AM_CONDITIONAL(VALGRIND, test "${enable_valgrind}" = "yes") MISC_FLAGS @@ -79,6 +74,14 @@ PKG_CHECK_MODULES(DBUS, dbus-1 >= 1.6, dummy=yes, AC_SUBST(DBUS_CFLAGS) AC_SUBST(DBUS_LIBS) +PKG_CHECK_MODULES(LIBXML, libxml-2.0) +AC_SUBST(LIBXML_CFLAGS) +AC_SUBST(LIBXML_LIBS) + +PKG_CHECK_MODULES(INIPARSER, iniparser) +AC_SUBST(INIPARSER_CFLAGS) +AC_SUBST(INIPARSER_LIBS) + AC_ARG_WITH([dbusconfdir], AC_HELP_STRING([--with-dbusconfdir=DIR], [path to D-Bus configuration directory]), [path_dbusconfdir=${withval}]) @@ -180,6 +183,18 @@ AC_ARG_ENABLE(cups, AC_HELP_STRING([--disable-cups], [disable CUPS printer support]), [enable_cups=${enableval}]) AM_CONDITIONAL(CUPS, test "${enable_cups}" != "no") + +AC_ARG_ENABLE(midi, AC_HELP_STRING([--enable-midi], + [enable MIDI support]), [enable_midi=${enableval}]) +AM_CONDITIONAL(MIDI, test "${enable_midi}" = "no") + +if (test "${enable_midi}" = "yes"); then + PKG_CHECK_MODULES(ALSA, alsa, dummy=no, + AC_MSG_ERROR(ALSA lib is required for MIDI support)) + AC_SUBST(ALSA_CFLAGS) + AC_SUBST(ALSA_LIBS) +fi + AC_ARG_ENABLE(obex, AC_HELP_STRING([--disable-obex], [disable OBEX profile support]), [enable_obex=${enableval}]) @@ -245,6 +260,39 @@ AC_ARG_ENABLE(experimental, AC_HELP_STRING([--enable-experimental], [enable_experimental=${enableval}]) AM_CONDITIONAL(EXPERIMENTAL, test "${enable_experimental}" = "yes") +# Start of TIZEN_FEATURE_BLUEZ_MODIFY +AC_ARG_ENABLE(wearable, AC_HELP_STRING([--enable-wearable], + [enable wearable profile]), [enable_wearable=${enableval}]) +AM_CONDITIONAL(WEARABLE, test "${enable_wearable}" = "yes") + +AC_ARG_ENABLE(autopair, AC_HELP_STRING([--enable-autopair], + [Enable Autopair Plugin]), [enable_autopair=${enableval}]) +AM_CONDITIONAL(AUTOPAIR, test "${enable_autopair}" = "yes") + +AC_ARG_ENABLE(hid, AC_HELP_STRING([--enable-hid], + [Enable HID Plugin]), [enable_hid=${enableval}]) +AM_CONDITIONAL(TIZEN_HID_PLUGIN, test "${enable_hid}" = "yes") + +AM_CONDITIONAL(TIZEN_HEALTH_PLUGIN, test "${enable_health}" = "yes") + +AC_ARG_ENABLE(proximity, AC_HELP_STRING([--enable-proximity], + [Enable PROXIMITY Plugin]), [enable_proximity=${enableval}]) +AM_CONDITIONAL(TIZEN_PROXIMITY_PLUGIN, test "${enable_proximity}" = "yes") + +AC_ARG_ENABLE(tds, AC_HELP_STRING([--enable-tds], + [Enable TDS Plugin]), [enable_tds=${enableval}]) +AM_CONDITIONAL(TIZEN_TDS_PLUGIN, test "${enable_tds}" = "yes") + +AC_ARG_ENABLE(tizenunusedplugin, AC_HELP_STRING([--enable-tizenunusedplugin], + [Enable Unused Plugin]), [enable_tizenunusedplugin=${enableval}]) +AM_CONDITIONAL(TIZEN_UNUSED_PLUGIN, test "${enable_tizenunusedplugin}" = "yes") + +AC_ARG_ENABLE(sap, AC_HELP_STRING([--enable-sap], + [Enable SAP Plugin]), [enable_sap=${enableval}]) +AM_CONDITIONAL(TIZEN_SAP_PLUGIN, test "${enable_sap}" = "yes") + +# End of TIZEN_FEATURE_BLUEZ_MODIFY + AC_ARG_ENABLE(deprecated, AC_HELP_STRING([--enable-deprecated], [enable deprecated plugins (BLE services, ...)]), [enable_deprecated=${enableval}]) diff --git a/doc/adapter-api.txt b/doc/adapter-api.txt index 08de6cd..e37781e 100755 --- a/doc/adapter-api.txt +++ b/doc/adapter-api.txt @@ -109,6 +109,159 @@ Methods void StartDiscovery() org.bluez.Error.NotSupported org.bluez.Error.Failed +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + void StartCustomDiscovery(string pattern) + + This method starts the device discovery session with + parameter. The valid paramter strings are "BREDR", + "LE" or "LE_BREDR" which will perform the inquiry for + appropriate types. This includes an inquiry procedure + and remote device name resolving. Use StopDiscovery + to release the sessions acquired. + + This process will start creating Device objects as + new devices are discovered. + + Possible errors: org.bluez.Error.NotReady + org.bluez.Error.Failed + + void StartLEDiscovery() + + This method starts the LE device discovery session. + General discovery and active scan is default. + Use StopLEDiscovery to release the sessions + acquired. + + This process will start emitting DeviceFound and + PropertyChanged "LEDiscovering" signals. + + Possible errors: org.bluez.Error.NotReady + org.bluez.Error.Failed + + void StopLEDiscovery() + + This method will cancel any previous StartLEDiscovery + transaction. + + Note that a discovery procedure is shared between all + discovery sessions thus calling StopLEDiscovery will only + release a single session. + + Possible errors: org.bluez.Error.NotReady + org.bluez.Error.Failed + org.bluez.Error.NotAuthorized + + void SetAdvertising(boolean enable) + + This method is used to set LE advertising on a + controller that supports it. + + This process will emit PropertyChanged "Advertising" + signal. + + Possible errors: org.bluez.Error.NotReady + org.bluez.Error.InvalidArguments + org.bluez.Error.Failed + + void SetAdvertisingParameters(uint32 interval_min, + uint32 interval_max, uint32 filter_policy, + uint32 type) + + This method allows for setting the Low Energy + advertising interval and advertising filter policy. + It is only supported on controller with LE support. + + Possible errors: org.bluez.Error.NotReady + org.bluez.Error.Failed + + void SetAdvertisingData(array{byte} value) + + This method is used to set LE advertising data on a + controller that supports it. + + Possible errors: org.bluez.Error.NotReady + org.bluez.Error.InvalidArguments + org.bluez.Error.Failed + + void SetScanParameters(uint32 type, uint32 interval, uint32 window) + + This method allows for setting the Low Energy + scan interval and window. + It is only supported on controller with LE support. + + Possible errors: org.bluez.Error.NotReady + org.bluez.Error.Failed + + void SetScanRespData(array{byte} value) + + This method is used to set LE scan response data on + a controller that supports it. + + Possible errors: org.bluez.Error.NotReady + org.bluez.Error.InvalidArguments + org.bluez.Error.Failed + + void AddDeviceWhiteList(string address, uint32 address_type) + + This method is used to add LE device to White List for given + address. + + Possible errors: org.bluez.Error.NotReady + org.bluez.Error.InvalidArguments + org.bluez.Error.Failed + + void RemoveDeviceWhiteList(string address, uint32 address_type) + + This method is used to remove LE device to White List for given + address. + + Possible errors: org.bluez.Error.NotReady + org.bluez.Error.InvalidArguments + org.bluez.Error.Failed + + void ClearDeviceWhiteList() + + This method is used to clear LE device to White List + + Possible errors: org.bluez.Error.NotReady + org.bluez.Error.Failed + + void SetLePrivacy(boolean enable_privacy) + + This method is used to set/reset LE privacy feature for the local + adapter when it supports the feature. + + Possible errors: org.bluez.Error.NotReady + org.bluez.Error.InvalidArguments + org.bluez.Error.Failed + + void SetLeStaticRandomAddress(boolean enable_random_address) + + This method is used to set/reset LE static random address for the local + adapter when it supports the feature. + + Possible errors: org.bluez.Error.NotReady + org.bluez.Error.InvalidArguments + org.bluez.Error.Failed + + void SetManufacturerData(array{byte} value) + + This method is used to set Manufacturer data on a + controller that supports it. + + Possible errors: org.bluez.Error.NotReady + org.bluez.Error.InvalidArguments + org.bluez.Error.Failed + + void CreateDevice(string address) + + Creates a new object path for a remote device. This + method will connect to the remote device and retrieve + all SDP records. + + Possible errors: org.bluez.Error.InvalidArguments +#endif + Properties string Address [readonly] The Bluetooth device address. @@ -219,3 +372,12 @@ Properties string Address [readonly] Local Device ID information in modalias format used by the kernel and udev. +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + boolean LEDiscovering [readonly] + + Indicates that a device LE discovery procedure is active. + + string Version [readonly] + + The Bluetooth version. +#endif diff --git a/doc/coding-style.txt b/doc/coding-style.txt new file mode 100755 index 0000000..f0bf880 --- /dev/null +++ b/doc/coding-style.txt @@ -0,0 +1,279 @@ +BlueZ coding style +****************** + +Every project has its coding style, and BlueZ is not an exception. This +document describes the preferred coding style for BlueZ code, in order to keep +some level of consistency among developers so that code can be easily +understood and maintained. + +First of all, BlueZ coding style must follow every rule for Linux kernel +(http://www.kernel.org/doc/Documentation/CodingStyle). There also exists a tool +named checkpatch.pl to help you check the compliance with it. Just type +"checkpatch.pl --no-tree patch_name" to check your patch. In theory, you need +to clean up all the warnings and errors except this one: "ERROR: Missing +Signed-off-by: line(s)". BlueZ does not used Signed-Off lines, so including +them is actually an error. In certain circumstances one can ignore the 80 +character per line limit. This is generally only allowed if the alternative +would make the code even less readable. + +Besides the kernel coding style above, BlueZ has special flavors for its own. +Some of them are mandatory (marked as 'M'), while some others are optional +(marked as 'O'), but generally preferred. + +M1: Blank line before and after an if/while/do/for statement +============================================================ + +There should be a blank line before if statement unless the if is nested and +not preceded by an expression or variable declaration. + +Example: +1) +a = 1; +if (b) { // wrong + +2) +a = 1 + +if (b) { +} +a = 2; // wrong + +3) +if (a) { + if (b) // correct + +4) +b = 2; + +if (a) { // correct + +} + +b = 3; + +The only exception to this rule applies when a variable is being checked for +errors as such: + +err = stat(filename, &st); +if (err || !S_ISDIR(st.st_mode)) + return; + +M2: Multiple line comment +========================= + +If your comment has more than one line, please start it from the second line. + +Example: +/* + * first line comment // correct + * ... + * last line comment + */ + + +M3: Space before and after operator +=================================== + +There should be a space before and after each operator. + +Example: +a + b; // correct + + +M4: Wrap long lines +=================== + +If your condition in if, while, for statement or a function declaration is too +long to fit in one line, the new line needs to be indented not aligned with the +body. + +Example: +1) +if ((adapter->supported_settings & MGMT_SETTING_SSP) && + !(adapter->current_settings & MGMT_SETTING_SSP)) // wrong + +2) +if ((adapter->supported_settings & MGMT_SETTING_SSP) && + !(adapter->current_settings & MGMT_SETTING_SSP)) + +3) +void btd_adapter_register_pin_cb(struct btd_adapter *adapter, + btd_adapter_pin_cb_t cb) // wrong + +4) +void btd_adapter_register_pin_cb(struct btd_adapter *adapter, + btd_adapter_pin_cb_t cb) + +The referred style for line wrapping is to indent as far as possible to the +right without hitting the 80 columns limit. + +M5: Space when doing type casting +================================= + +There should be a space between new type and variable. + +Example: +1) +a = (int *)b; // wrong +2) +a = (int *) b; // correct + + +M6: Don't initialize variable unnecessarily +=========================================== + +When declaring a variable, try not to initialize it unless necessary. + +Example: +int i = 1; // wrong + +for (i = 0; i < 3; i++) { +} + +M7: Follow the order of include header elements +=============================================== + +When writing an include header the various elements should be in the following +order: + - #includes + - forward declarations + - #defines + - enums + - typedefs + - function declarations and inline function definitions + +M8: Internal headers must not use include guards +================================================ + +Any time when creating a new header file with non-public API, that header +must not contain include guards. + +M9: Naming of enums +=================== + +Enums must have a descriptive name. The enum type should be small caps and +it should not be typedef-ed. Enum contents should be in CAPITAL letters and +prefixed by the enum type name. + +Example: + +enum animal_type { + ANIMAL_TYPE_FOUR_LEGS, + ANIMAL_TYPE_EIGHT_LEGS, + ANIMAL_TYPE_TWO_LEGS, +}; + +If the enum contents have values (e.g. from specification) the formatting +should be as follows: + +enum animal_type { + ANIMAL_TYPE_FOUR_LEGS = 4, + ANIMAL_TYPE_EIGHT_LEGS = 8, + ANIMAL_TYPE_TWO_LEGS = 2, +}; + +M10: Enum as switch variable +============================ + +If the variable of a switch is an enum, you must include all values in +switch body even if providing default. This is enforced by compiler option +enabling extra warning in such case. The reason for this is to ensure that if +later on enum is modified and one forget to change the switch accordingly, the +compiler will complain the new added type hasn't been handled. + +Example: + +enum animal_type { + ANIMAL_TYPE_FOUR_LEGS = 4, + ANIMAL_TYPE_EIGHT_LEGS = 8, + ANIMAL_TYPE_TWO_LEGS = 2, +}; + +enum animal_type t; + +switch (t) { // OK +case ANIMAL_TYPE_FOUR_LEGS: + ... + break; +case ANIMAL_TYPE_EIGHT_LEGS: + ... + break; +case ANIMAL_TYPE_TWO_LEGS: + ... + break; +default: + break; +} + +switch (t) { // Wrong +case ANIMAL_TYPE_FOUR_LEGS: + ... + break; +case ANIMAL_TYPE_TWO_LEGS: + ... + break; +default: + break; +} + +However if the enum comes from an external header file outside BlueZ, such as +Android headers, we cannot make any assumption of how the enum is defined and +this rule might not apply. + +M11: Always use parenthesis with sizeof +======================================= + +The expression argument to the sizeof operator should always be in +parenthesis, too. + +Example: +1) +memset(stuff, 0, sizeof(*stuff)); + +2) +memset(stuff, 0, sizeof *stuff); // Wrong + +M12: Use void if function has no parameters +=========================================== + +A function with no parameters must use void in the parameter list. + +Example: +1) +void foo(void) +{ +} + +2) +void foo() // Wrong +{ +} + +O1: Try to avoid complex if body +================================ + +It's better not to have a complicated statement for if. You may judge its +contrary condition and return | break | continue | goto ASAP. + +Example: +1) +if (device) { // worse + memset(&eir_data, 0, sizeof(eir_data)); + if (eir_len > 0) + eir_parse(&eir_data, ev->eir, eir_len); + ... +} else { + error("Unable to get device object for %s", addr); + return; +} + +2) +if (!device) { + error("Unable to get device object for %s", addr); + return; +} + +memset(&eir_data, 0, sizeof(eir_data)); +if (eir_len > 0) + eir_parse(&eir_data, ev->eir, eir_len); +... diff --git a/doc/device-api.txt b/doc/device-api.txt index 13b2881..d03906c 100755 --- a/doc/device-api.txt +++ b/doc/device-api.txt @@ -81,7 +81,11 @@ Methods void Connect() org.bluez.Error.InvalidArguments org.bluez.Error.NotSupported +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + void Pair(uint8 conn_type) +#else void Pair() +#endif This method will connect to the remote device, initiate pairing and then retrieve all SDP records @@ -99,6 +103,14 @@ Methods void Connect() In case there is no application agent and also no default agent present, this method will fail. +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + conn_type represents the type of the connection to be + used for pairing. + 0xFF -> BR/EDR and LE + 0x00 -> BR/EDR only + 0x01 -> LE only +#endif + Possible errors: org.bluez.Error.InvalidArguments org.bluez.Error.Failed org.bluez.Error.AlreadyExists @@ -116,6 +128,29 @@ Methods void Connect() Possible errors: org.bluez.Error.DoesNotExist org.bluez.Error.Failed +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + void ConnectLe() + + This is a generic method to connect GATT of + the remote device supporting LE and have been + flagged as auto-connectable on our side. + + Possible errors: org.bluez.Error.NotReady + org.bluez.Error.Failed + org.bluez.Error.InProgress + org.bluez.Error.AlreadyConnected + + + void DisconnectLe() + + This method disconnects a specific remote device LE + connection by terminating the low-level ACL connection. + The use of this method should be restricted to administrator + use. + + Possible errors: org.bluez.Error.NotConnected +#endif + Properties string Address [readonly] The Bluetooth device address of the remote device. @@ -154,7 +189,11 @@ Properties string Address [readonly] Indicates if the remote device is paired. +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + byte Connected [readonly] +#else boolean Connected [readonly] +#endif Indicates if the remote device is currently connected. A PropertiesChanged signal indicate changes to this @@ -234,3 +273,11 @@ Properties string Address [readonly] array{byte} AdvertisingFlags [readonly, experimental] The Advertising Data Flags of the remote device. + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + boolean GattConnected [readonly] + + Indicates if the remote LE device is currently connected. + A PropertiesChanged signal indicate changes to this + status. +#endif \ No newline at end of file diff --git a/doc/gatt-api.txt b/doc/gatt-api.txt index 6c98b87..b29f599 100755 --- a/doc/gatt-api.txt +++ b/doc/gatt-api.txt @@ -45,6 +45,16 @@ Properties string UUID [read-only] belongs to. Only present on services from remote devices. +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + array{object} Characteristics [read-only] + + Array of object paths representing the characteristics + of this service. This property is set only when the + characteristic discovery has been completed, however the + characteristic objects will become available via + ObjectManager as soon as they get discovered. +#endif + array{object} Includes [read-only]: Not implemented Array of object paths representing the included @@ -153,6 +163,22 @@ Properties string UUID [read-only] "secure-read" (Server only) "secure-write" (Server only) +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + array{object} Descriptors [read-only] + + Array of object paths representing the descriptors + of this service. This property is set only when the + descriptor discovery has been completed, however the + descriptor objects will become available via + ObjectManager as soon as they get discovered. + + string Unicast [read-only] + + 12:34:56:78:9A:BC remote device address, if address is set then + notifications or indications shall be sent to only "XX_XX_XX_XX_XX_XX" + device otherwise notification or indication shall be multicasted. +#endif + Characteristic Descriptors hierarchy ==================================== @@ -342,3 +368,21 @@ Methods void RegisterApplication(object application, dict options) Possible errors: org.bluez.Error.InvalidArguments org.bluez.Error.DoesNotExist + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + GetService(string uuid) + + This Reads the service, characterstics and descriptors + that has been previously registered. The string uuid parameter + must match the same value that has been used + on registration. + + The return values includes, + Key objectpath + ---- ----------- + Service /serviceX + CharacteristicsX /serviceX/CharacterisitcX + DescriptorX /serviceX/CharacterisitcX/DescriptorX + + Possible errors: org.bluez.Error.InvalidArguments +#endif diff --git a/doc/maintainer-guidelines.txt b/doc/maintainer-guidelines.txt new file mode 100755 index 0000000..21162d4 --- /dev/null +++ b/doc/maintainer-guidelines.txt @@ -0,0 +1,114 @@ +Maintainer guidelines +********************* + +This document is intended for the maintainers of the BlueZ project. It +serves as basic guidelines for handling patch review and commit access. + + +Rule 1: Keep the GIT tree clean and linear +========================================== + +The bluetooth.git, bluetooth-next.git and bluez.git trees are not your +private playground. The history is meant to be clean and linear. + + - NO merges + - NO branches + - NO tags + +If anyone needs testing or work on a feature, clone the tree and do +it in your own copy. The master trees are off limits. + +One advise to avoid any accidental errors in this area to set proper +options in global ~/.gitconfig or local .git/config files. + + [merge] + ff = only + +Violations of this rule are not acceptable. This rule is enforced. If +in doubt ask one of the seasoned maintainers. + + +Rule 2: Enforce clean commit messages +===================================== + +The commit messages are required to be clean and follow style guidelines +to be consistent. + +Commit messages should adhere to a 72 characters by line limit. That +makes it easy to read them via git log in a terminal window. Exceptions +to this rule are logs, trace or other verbatim copied information. + +Every commit requires full names and email addresses. No synonyms or +nicknames are allowed. It is also important that the Outlook style +names with lastname, firstname are not allowed. It is the maintainers +job to ensure we get proper firstname lastname authorship. + +It is also important that the committer itself uses a valid name and +email address when committing patches. So ensure that either the +global ~/.gitconfig or local .git/config provides proper values. + + [user] + name = Peter Mustermann + email = peter@mustermann.de + +Commit messages for bluez.git shall not contain Signed-off-by +signatures. They are not used in userspace and with that it is the +maintainers job to ensure they do not get committed to the repository. + +For bluetooth.git and bluetooth-next.git The Signed-off-by process is +used and the signatures are required. + +Tags like Change-Id generated from Gerrit are never acceptable. It is +the maintainers job to ensure that these are not committed into the +repositories. + +Violations of this rule create a mess in the tree that can not be +reversed. If in doubt ask one of the seasoned maintainers. + + +Rule 3: Enforce correct coding style +==================================== + +The coding style follows roughly the kernel coding style with any +exceptions documented in doc/coding-style.txt. + +To ensure trivial white-space errors don't get committed, have the +following in your .gitconfig: + + [apply] + whitespace = error + +It can also be helpful to use the checkpatch.pl script coming with the +Linux kernel to do some automated checking. Adding the following to your +.git/hooks/pre-commit and .git/hooks/pre-applypatch is a simple way to +do this: + + exec git diff --cached | ~/src/linux/scripts/checkpatch.pl -q \ + --no-tree --no-signoff --show-types \ + --ignore CAMELCASE,NEW_TYPEDEFS,INITIALISED_STATIC - + +The above assumes that a kernel tree resides in ~/src/linux/. + + +Rule 4: Pay extra attention to adding new files to the tree +=========================================================== + +New files that are added to the tree require several things to be +verified first: + + - Check that the names are acceptible with other maintainers + - Ensure that the file modes are correct + - Verify that the license & copyright headers are correct + - If the file is supposed to be part of the release tarball, + make sure that it gets picked up by 'make dist' (particularly + important for documentation or other files that are not code) + + +Rule 5: Keep the mailing list in sync with the commit process +============================================================= + +When applying patches, be sure to send a response to the mailing list as +soon as the code has been pushed to the upstream tree. Usually this +means one email per patch, however patch-sets may only have one response +covering the entire set. If applying a subset of a patch-set clearly +state what was applied in your response. diff --git a/doc/mgmt-api.txt b/doc/mgmt-api.txt index 34270dd..6690752 100755 --- a/doc/mgmt-api.txt +++ b/doc/mgmt-api.txt @@ -1907,6 +1907,68 @@ Load Identity Resolving Keys Command Possible errors: Invalid Parameters Invalid Index +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +Generate Identity Resolving Key Command +======================================= + + Command Code: 0x00F6 + Controller Index: + 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: + 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: + Command Parameters: Resolvable Private Address (6 Octets) + Return Parameters: + + This command is used to set the random address to local controller. + If local device supports LE Privacy then this command will be called to set + its Random address while active scanning remote devices and in the case of + advertising itself. + + This command intern calls the HCI Set Random address command from kernel + space to set the RPA to controller. + + This command generates a Command Complete event on success + or a Command Status event on failure. + + Possible errors: Failed + Invalid Index + +#endif Get Connection Information Command ================================== diff --git a/doc/network-api.txt b/doc/network-api.txt index 109da28..0fb633c 100755 --- a/doc/network-api.txt +++ b/doc/network-api.txt @@ -74,3 +74,17 @@ Methods void Register(string uuid, string bridge) All servers will be automatically unregistered when the calling application terminates. + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + void Disconnect(string address) + + Disconnect the device from the network device. + + Possible errors: org.bluez.Error.Failed + org.bluez.Error.NotConnected + + dict GetProperties(string address) + + Returns all properties of the specified device. + See the properties section for available properties. +#endif diff --git a/doc/oob-api.txt b/doc/oob-api.txt new file mode 100755 index 0000000..d838712 --- /dev/null +++ b/doc/oob-api.txt @@ -0,0 +1,38 @@ +BlueZ D-Bus Out Of Band Pairing API description +=============================================== + +Copyright (C) 2011 Szymon Janc for ST-Ericsson + +Service org.bluez +Interface org.bluez.OutOfBand +Object path [variable prefix]/{hci0,hci1,...} + +Methods array{byte} hash, array{byte} randomizer ReadLocalData() + + This method reads local OOB data from adapter. Return + value is pair of arrays 16 bytes each. + + Note: This method will generate and return new local + OOB data. + + Possible errors: org.bluez.Error.Failed + org.bluez.Error.InProgress + + void AddRemoteData(string address, array{byte} hash, + array{byte} randomizer) + + This method adds new Out Of Band data for + specified address. If data for specified address + already exists it will be overwritten with new one. + + Possible errors: org.bluez.Error.Failed + org.bluez.Error.InvalidArguments + + void RemoveRemoteData(string address) + + This method removes Out Of Band data for specified + address. If data for specified address does not exist + nothing is removed. + + Possible errors: org.bluez.Error.Failed + org.bluez.Error.InvalidArguments diff --git a/doc/profile-api.txt b/doc/profile-api.txt index ec18034..60eee2b 100755 --- a/doc/profile-api.txt +++ b/doc/profile-api.txt @@ -91,7 +91,100 @@ Object path /org/bluez Possible errors: org.bluez.Error.InvalidArguments org.bluez.Error.AlreadyExists +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + void RegisterProfile2(object profile, string uuid, string destination, + string path,dict options) + This registers a profile implementation. + + If an application disconects/exits, its registered profile + will not be removed and bluez launches an application through + dbus activation when profile is connected. + + HFP HS UUID: 0000111e-0000-1000-8000-00805f9b34fb + + Default RFCOMM channel is 6. And this requires + authentication. + + string Destination + + Application bus name + + string Path + + Applicatoin path name + + Available options: + + string Name + + Human readable name for the profile + + string Service + + The primary service class UUID + (if different from the actual + profile UUID) + + string Role + + For asymmetric profiles that do not + have UUIDs available to uniquely + identify each side this + parameter allows specifying the + precise local role. + + Possible values: "client", "server" + + uint16 Channel + + RFCOMM channel number that is used + for client and server UUIDs. + + If applicable it will be used in the + SDP record as well. + + uint16 PSM + + PSM number that is used for client + and server UUIDs. + + If applicable it will be used in the + SDP record as well. + + boolean RequireAuthentication + + Pairing is required before connections + will be established. No devices will + be connected if not paired. + + boolean RequireAuthorization + + Request authorization before any + connection will be established. + + boolean AutoConnect + + In case of a client UUID this will + force connection of the RFCOMM or + L2CAP channels when a remote device + is connected. + + string ServiceRecord + + Provide a manual SDP record. + + uint16 Version + + Profile version (for SDP record) + + uint16 Features + + Profile features (for SDP record) + + Possible errors: org.bluez.Error.InvalidArguments + org.bluez.Error.AlreadyExists +#endif void UnregisterProfile(object profile) This unregisters the profile that has been previously diff --git a/doc/settings-storage.txt b/doc/settings-storage.txt index 6a708b4..bbe525a 100755 --- a/doc/settings-storage.txt +++ b/doc/settings-storage.txt @@ -81,13 +81,23 @@ Settings file contains one [General] group with adapter info like: 0 = disable timer, i.e. stay discoverable forever +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + LocalIrk String Identity resolving key for local + adapter. This key value is used to + generate the RPA to suport privacy feature. + If value is NULL, i.e IRK to be generated + for this adapter. +#endif + Sample: [General] Name=My PC Discoverable=false Pairable=true DiscoverableTimeout=0 - +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + LocalIrk="" +#endif Identity file format ==================== @@ -294,3 +304,13 @@ Long term key) related to a remote device. Counter Integer Signing counter Authenticated Boolean True if the key is authenticated + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +[IdentityResolvingKey] group contains: + + Key String Identity Resolving key in hexadecimal format + + IdentityAddress String Identity Address of the device + + IdentityAddressType String Type of Identity Address of the device +#endif diff --git a/gdbus/gdbus.h b/gdbus/gdbus.h index e37385f..e40dd44 100755 --- a/gdbus/gdbus.h +++ b/gdbus/gdbus.h @@ -277,6 +277,12 @@ gboolean g_dbus_emit_signal_valist(DBusConnection *connection, const char *path, const char *interface, const char *name, int type, va_list args); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +gboolean g_dbus_emit_signal_to_dest(DBusConnection *connection, + const char *dest, const char *path, + const char *interface, const char *name, int type, ...); +#endif + guint g_dbus_add_service_watch(DBusConnection *connection, const char *name, GDBusWatchFunction connect, GDBusWatchFunction disconnect, diff --git a/gdbus/object.c b/gdbus/object.c index afb4587..bd80060 100755 --- a/gdbus/object.c +++ b/gdbus/object.c @@ -33,7 +33,24 @@ #include "gdbus.h" +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#if 0 +#include +static void gdbus_dbg(const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + + vsyslog(LOG_DEBUG, format, ap); + + va_end(ap); +} +#endif +#else #define info(fmt...) +#endif + #define error(fmt...) #define debug(fmt...) @@ -88,6 +105,12 @@ static int global_flags = 0; static struct generic_data *root; static GSList *pending = NULL; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#define ADAPTER_INTERFACE "org.bluez.Adapter1" + +static char *adapter_path = NULL; +#endif + static gboolean process_changes(gpointer user_data); static void process_properties_from_interface(struct generic_data *data, struct interface_data *iface); @@ -663,6 +686,13 @@ static gboolean remove_interface(struct generic_data *data, const char *name) data->interfaces = g_slist_remove(data->interfaces, iface); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (g_strcmp0(iface->name, ADAPTER_INTERFACE) == 0) { + g_free(adapter_path); + adapter_path = NULL; + } +#endif + if (iface->destroy) { iface->destroy(iface->user_data); iface->user_data = NULL; @@ -1075,7 +1105,12 @@ static DBusHandlerResult generic_message(DBusConnection *connection, if (check_privilege(connection, message, method, iface->user_data) == TRUE) return DBUS_HANDLER_RESULT_HANDLED; - +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#if 0 + gdbus_dbg("%s: %s.%s()", dbus_message_get_path(message), + iface->name, method->name); +#endif +#endif return process_message(connection, message, method, iface->user_data); } @@ -1165,9 +1200,36 @@ static DBusMessage *get_objects(DBusConnection *connection, return reply; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static DBusMessage *default_adapter(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + DBusMessage *reply; + + if (!adapter_path) + return g_dbus_create_error(msg, + "org.bluez.Error" ".NoSuchAdapter", + "No such adapter"); + + reply = dbus_message_new_method_return(msg); + if (!reply) + return NULL; + + dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &adapter_path, + DBUS_TYPE_INVALID); + + return reply; +} +#endif + static const GDBusMethodTable manager_methods[] = { { GDBUS_METHOD("GetManagedObjects", NULL, GDBUS_ARGS({ "objects", "a{oa{sa{sv}}}" }), get_objects) }, +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + { GDBUS_METHOD("DefaultAdapter", + NULL, GDBUS_ARGS({ "adapter", "o" }), + default_adapter) }, +#endif { } }; @@ -1355,6 +1417,11 @@ gboolean g_dbus_register_interface(DBusConnection *connection, return FALSE; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (g_strcmp0(name, ADAPTER_INTERFACE) == 0) + adapter_path = g_strdup(path); +#endif + if (!add_interface(data, name, methods, signals, properties, user_data, destroy)) { object_path_unref(connection, path); @@ -1615,6 +1682,65 @@ gboolean g_dbus_emit_signal(DBusConnection *connection, return result; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static gboolean g_dbus_emit_signal_valist_to_dest(DBusConnection *connection, + const char *dest, const char *path, const char *interface, + const char *name, int type, va_list args) +{ + DBusMessage *signal; + dbus_bool_t ret; + const GDBusArgInfo *args_info; + + if (!check_signal(connection, path, interface, name, &args_info)) + return FALSE; + + signal = dbus_message_new_signal(path, interface, name); + if (signal == NULL) { + error("Unable to allocate new %s.%s signal", interface, name); + return FALSE; + } + + ret = dbus_message_append_args_valist(signal, type, args); + if (!ret) + goto fail; + + if (g_dbus_args_have_signature(args_info, signal) == FALSE) { + error("%s.%s: got unexpected signature '%s'", interface, name, + dbus_message_get_signature(signal)); + ret = FALSE; + goto fail; + } + + ret = dbus_message_set_destination(signal, dest); + if (!ret) + error("Fail to set destination"); + + return g_dbus_send_message(connection, signal); + +fail: + dbus_message_unref(signal); + + return ret; +} + +gboolean g_dbus_emit_signal_to_dest(DBusConnection *connection, + const char *dest, const char *path, + const char *interface, const char *name, int type, ...) +{ + va_list args; + gboolean result; + + va_start(args, type); + + result = g_dbus_emit_signal_valist_to_dest(connection, dest, path, + interface, name, type, args); + + va_end(args); + + return result; +} +#endif + gboolean g_dbus_emit_signal_valist(DBusConnection *connection, const char *path, const char *interface, const char *name, int type, va_list args) diff --git a/gobex/gobex-apparam.c b/gobex/gobex-apparam.c index 4328172..6f7a952 100755 --- a/gobex/gobex-apparam.c +++ b/gobex/gobex-apparam.c @@ -104,8 +104,10 @@ GObexApparam *g_obex_apparam_decode(const void *data, gsize size) GHashTable *tags; gsize count = 0; +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY if (size < 2) return NULL; +#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */ apparam = g_obex_apparam_new(); @@ -166,6 +168,13 @@ gssize g_obex_apparam_encode(GObexApparam *apparam, void *buf, gsize len) return count; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +void g_obex_apparam_remove_all(GObexApparam *apparam) +{ + g_hash_table_remove_all(apparam->tags); +} +#endif + GObexApparam *g_obex_apparam_set_bytes(GObexApparam *apparam, guint8 id, const void *value, gsize len) { diff --git a/gobex/gobex-apparam.h b/gobex/gobex-apparam.h index 6c08609..701fd43 100755 --- a/gobex/gobex-apparam.h +++ b/gobex/gobex-apparam.h @@ -30,6 +30,10 @@ typedef struct _GObexApparam GObexApparam; GObexApparam *g_obex_apparam_decode(const void *data, gsize size); gssize g_obex_apparam_encode(GObexApparam *apparam, void *buf, gsize size); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +void g_obex_apparam_remove_all(GObexApparam *apparam); +#endif + GObexApparam *g_obex_apparam_set_bytes(GObexApparam *apparam, guint8 id, const void *value, gsize size); GObexApparam *g_obex_apparam_set_uint8(GObexApparam *apparam, guint8 id, diff --git a/gobex/gobex-header.c b/gobex/gobex-header.c index c594999..c6634c7 100755 --- a/gobex/gobex-header.c +++ b/gobex/gobex-header.c @@ -389,6 +389,11 @@ GObexHeader *g_obex_header_new_unicode(guint8 id, const char *str) GObexHeader *header; gsize len; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + gunichar2 *utf16; + glong utf16_len; +#endif + g_obex_debug(G_OBEX_DEBUG_HEADER, "header 0x%02x", G_OBEX_HDR_ENC(id)); if (G_OBEX_HDR_ENC(id) != G_OBEX_HDR_ENC_UNICODE) @@ -401,9 +406,15 @@ GObexHeader *g_obex_header_new_unicode(guint8 id, const char *str) len = g_utf8_strlen(str, -1); header->vlen = len; +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY header->hlen = len == 0 ? 3 : 3 + ((len + 1) * 2); header->v.string = g_strdup(str); - +#else + header->v.string = g_strdup(str); + utf16_len = utf8_to_utf16(&utf16, header->v.string); + header->hlen = len == 0 ? 3 : 3 + utf16_len; + g_free(utf16); +#endif g_obex_debug(G_OBEX_DEBUG_HEADER, "%s", header->v.string); return header; diff --git a/gobex/gobex.c b/gobex/gobex.c index 42175fc..61ddc66 100755 --- a/gobex/gobex.c +++ b/gobex/gobex.c @@ -571,7 +571,12 @@ static guint8 *digest_response(const guint8 *nonce) return result; g_checksum_update(md5, nonce, NONCE_LEN); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + g_checksum_update(md5, (guint8 *) ":0000", 5); +#else g_checksum_update(md5, (guint8 *) ":BlueZ", 6); +#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */ + size = NONCE_LEN; g_checksum_get_digest(md5, result, &size); diff --git a/lib/bluetooth.h b/lib/bluetooth.h index eb27926..f70ff09 100755 --- a/lib/bluetooth.h +++ b/lib/bluetooth.h @@ -120,6 +120,16 @@ struct bt_voice { #define BT_SNDMTU 12 #define BT_RCVMTU 13 +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#define BT_LE_CONN_PARAM 14 +struct le_conn_param { + uint16_t min; + uint16_t max; + uint16_t latency; + uint16_t to_multiplier; +}; +#endif + #define BT_VOICE_TRANSPARENT 0x0003 #define BT_VOICE_CVSD_16BIT 0x0060 diff --git a/lib/hci.c b/lib/hci.c index a3f5d96..4dd92b8 100755 --- a/lib/hci.c +++ b/lib/hci.c @@ -160,8 +160,13 @@ char *hci_bustostr(int bus) return "SPI"; case HCI_I2C: return "I2C"; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + case HCI_SMD: + return "QC_SMD"; +#else case HCI_SMD: return "SMD"; +#endif default: return "Unknown"; } @@ -594,7 +599,12 @@ static hci_map commands_map[] = { { "LE Receiver Test", 228 }, { "LE Transmitter Test", 229 }, { "LE Test End", 230 }, +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + { "LE Read Maximum Data Length", 231 }, + { "Reserved", 232 }, +#else { "Reserved", 231 }, +#endif { NULL } }; @@ -1086,6 +1096,38 @@ int hci_close_dev(int dd) /* HCI functions that require open device * dd - Device descriptor returned by hci_open_dev. */ +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +int hci_send_data(int dd, uint16_t handle, uint8_t len, void *data) +{ + uint8_t type = HCI_ACLDATA_PKT; + hci_acl_hdr ac; + struct iovec iv[3]; + int ivn; + + ac.handle = htobs(handle); + ac.dlen= htobs(len); + + iv[0].iov_base = &type; + iv[0].iov_len = 1; + iv[1].iov_base = ∾ + iv[1].iov_len = HCI_ACL_HDR_SIZE; + ivn = 2; + + if (len) { + iv[2].iov_base = data; + iv[2].iov_len = len; + ivn = 3; + } + + while (writev(dd, iv, ivn) < 0) { + if (errno == EAGAIN || errno == EINTR) + continue; + return -1; + } + return 0; +} +#endif + int hci_send_cmd(int dd, uint16_t ogf, uint16_t ocf, uint8_t plen, void *param) { uint8_t type = HCI_COMMAND_PKT; @@ -1604,7 +1646,11 @@ int hci_write_local_name(int dd, const char *name, int to) struct hci_request rq; memset(&cp, 0, sizeof(cp)); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + strncpy((char *) cp.name, name, sizeof(cp.name) - 1); +#else strncpy((char *) cp.name, name, sizeof(cp.name)); +#endif memset(&rq, 0, sizeof(rq)); rq.ogf = OGF_HOST_CTL; @@ -3119,3 +3165,132 @@ int hci_le_read_remote_features(int dd, uint16_t handle, uint8_t *features, int return 0; } + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +int hci_le_read_maximum_data_length( + int dd, uint8_t *status, uint16_t *tx_octets, + uint16_t *tx_time, uint16_t *rx_octets, + uint16_t *rx_time, int to) +{ + le_read_maximum_data_length_rp rp; + struct hci_request rq; + + memset(&rq, 0, sizeof(rq)); + memset(&rp, 0, sizeof(rp)); + + rq.ogf = OGF_LE_CTL; + rq.ocf = OCF_LE_READ_MAXIMUM_DATA_LENGTH; + rq.rparam = &rp; + rq.rlen = LE_READ_MAXIMUM_DATA_LENGTH_SIZE; + + if (hci_send_req(dd, &rq, to) < 0) + return -1; + + if (rp.status) { + errno = EIO; + return -1; + } + + *tx_octets = rp.max_tx_octets; + *tx_time = rp.max_tx_time; + *rx_octets = rp.max_rx_octets; + *rx_time = rp.max_rx_time; + *status = rp.status; + return 0; +} + +int hci_le_write_host_suggested_data_length( + int dd, uint16_t *def_tx_octets, + uint16_t *def_tx_time, int to) +{ + le_write_host_suggested_data_length_cp cp; + struct hci_request rq; + uint8_t status; + + memset(&cp, 0, sizeof(cp)); + cp.def_tx_octets = *def_tx_octets; + cp.def_tx_time = *def_tx_time; + + memset(&rq, 0, sizeof(rq)); + rq.ogf = OGF_LE_CTL; + rq.ocf = OCF_LE_WRITE_HOST_SUGGESTED_DATA_LENGTH; + rq.cparam = &cp; + rq.clen = LE_WRITE_HOST_SUGGESTED_DATA_LENGTH_CP_SIZE; + rq.rparam = &status; + rq.rlen = 1; + + if (hci_send_req(dd, &rq, to) < 0) + return -1; + + if (status) { + errno = EIO; + return -1; + } + + return 0; +} + +int hci_le_read_host_suggested_data_length( + int dd, uint8_t *status, uint16_t *def_tx_octets, + uint16_t *def_tx_time, int to) +{ + le_read_host_suggested_data_length_rp rp; + struct hci_request rq; + + memset(&rp, 0, sizeof(rp)); + memset(&rq, 0, sizeof(rq)); + + rq.ogf = OGF_LE_CTL; + rq.ocf = OCF_LE_READ_HOST_SUGGESTED_DATA_LENGTH; + rq.rparam = &rp; + rq.rlen = LE_READ_HOST_SUGGESTED_DATA_LENGTH_SIZE; + + if (hci_send_req(dd, &rq, to) < 0) + return -1; + + if (rp.status) { + errno = EIO; + return -1; + } + + *def_tx_octets = rp.def_tx_octets; + *def_tx_time = rp.def_tx_time; + *status = rp.status; + return 0; +} + +int hci_le_set_data_length( + int dd, const bdaddr_t *bdaddr, uint16_t *max_tx_octets, + uint16_t *max_tx_time, int to) +{ + le_set_data_length_cp cp; + le_set_data_length_rp rp; + struct hci_request rq; + + memset(&cp, 0, sizeof(cp)); + memset(&rp, 0, sizeof(rp)); + + bacpy(&cp.bdaddr, bdaddr); + cp.max_tx_octets = *max_tx_octets; + cp.max_tx_time = *max_tx_time; + + memset(&rq, 0, sizeof(rq)); + rq.ogf = OGF_LE_CTL; + rq.ocf = OCF_LE_SET_DATA_LENGTH; + rq.cparam = &cp; + rq.clen = LE_SET_DATA_LENGTH_CP_SIZE; + rq.rparam = &rp; + rq.rlen = LE_SET_DATA_LENGTH_RP_SIZE; + + if (hci_send_req(dd, &rq, to) < 0) + return -1; + + if (rp.status) { + errno = EIO; + return -1; + } + + return 0; +} + +#endif diff --git a/lib/hci.h b/lib/hci.h index 794333b..061580b 100755 --- a/lib/hci.h +++ b/lib/hci.h @@ -1710,6 +1710,52 @@ typedef struct { } __attribute__ ((packed)) le_test_end_rp; #define LE_TEST_END_RP_SIZE 3 +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#define OCF_LE_READ_MAXIMUM_DATA_LENGTH 0x002F +typedef struct { + uint8_t status; + uint16_t max_tx_octets; + uint16_t max_tx_time; + uint16_t max_rx_octets; + uint16_t max_rx_time; +} __attribute__ ((packed)) +le_read_maximum_data_length_rp; +#define LE_READ_MAXIMUM_DATA_LENGTH_SIZE 9 + +#define OCF_LE_WRITE_HOST_SUGGESTED_DATA_LENGTH 0x0030 +typedef struct { + uint16_t def_tx_octets; + uint16_t def_tx_time; +} __attribute__ ((packed)) +le_write_host_suggested_data_length_cp; +#define LE_WRITE_HOST_SUGGESTED_DATA_LENGTH_CP_SIZE 4 + +#define OCF_LE_READ_HOST_SUGGESTED_DATA_LENGTH 0x0024 +typedef struct { + uint8_t status; + uint16_t def_tx_octets; + uint16_t def_tx_time; +} __attribute__ ((packed)) +le_read_host_suggested_data_length_rp; +#define LE_READ_HOST_SUGGESTED_DATA_LENGTH_SIZE 5 + +#define OCF_LE_SET_DATA_LENGTH 0x0022 +typedef struct { + bdaddr_t bdaddr; + uint16_t max_tx_octets; + uint16_t max_tx_time; +} __attribute__ ((packed)) +le_set_data_length_cp; +#define LE_SET_DATA_LENGTH_CP_SIZE 10 + +typedef struct { + uint16_t handle; + uint8_t status; +} __attribute__ ((packed)) +le_set_data_length_rp; +#define LE_SET_DATA_LENGTH_RP_SIZE 3 +#endif + #define OCF_LE_ADD_DEVICE_TO_RESOLV_LIST 0x0027 typedef struct { uint8_t bdaddr_type; @@ -1744,6 +1790,17 @@ typedef struct { /* Vendor specific commands */ #define OGF_VENDOR_CMD 0x3f +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#define BRCM_QOS_PRIORITY_NORMAL 0x00 +#define BRCM_QOS_PRIORITY_HIGH 0xFF +#define BROADCOM_QOS_CMD 0xFC57 /* Only for bcm4329/bcm4330/bcm4334 chipsets */ +typedef struct { + uint16_t handle; + uint8_t priority; +} __attribute__ ((__packed__)) broadcom_qos_cp; +#define BROADCOM_QOS_CP_SIZE 3 +#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */ + /* ---- HCI Events ---- */ #define EVT_INQUIRY_COMPLETE 0x01 diff --git a/lib/hci_lib.h b/lib/hci_lib.h index 55aeb17..ff79599 100755 --- a/lib/hci_lib.h +++ b/lib/hci_lib.h @@ -50,6 +50,9 @@ struct hci_version { int hci_open_dev(int dev_id); int hci_close_dev(int dd); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +int hci_send_data(int dd, uint16_t handle, uint8_t len, void *data); +#endif int hci_send_cmd(int dd, uint16_t ogf, uint16_t ocf, uint8_t plen, void *param); int hci_send_req(int dd, struct hci_request *req, int timeout); @@ -141,7 +144,18 @@ int hci_le_clear_resolving_list(int dd, int to); int hci_le_read_resolving_list_size(int dd, uint8_t *size, int to); int hci_le_set_address_resolution_enable(int dev_id, uint8_t enable, int to); int hci_le_read_remote_features(int dd, uint16_t handle, uint8_t *features, int to); - +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +int hci_le_read_maximum_data_length( + int dd, uint8_t *status, uint16_t *tx_octets, + uint16_t *tx_time, uint16_t *rx_octets, + uint16_t *rx_time, int to ); +int hci_le_write_host_suggested_data_length(int dd, uint16_t *def_tx_octets, + uint16_t *def_tx_time, int to); +int hci_le_read_host_suggested_data_length(int dd, uint8_t *status, + uint16_t *def_tx_octets, uint16_t *def_tx_time, int to); +int hci_le_set_data_length(int dd, const bdaddr_t *bdaddr, + uint16_t *max_tx_octets, uint16_t *max_tx_time, int to); +#endif int hci_for_each_dev(int flag, int(*func)(int dd, int dev_id, long arg), long arg); int hci_get_route(bdaddr_t *bdaddr); diff --git a/lib/l2cap.h b/lib/l2cap.h index 5ce94c4..62b8c33 100755 --- a/lib/l2cap.h +++ b/lib/l2cap.h @@ -182,6 +182,18 @@ typedef struct { } __attribute__ ((packed)) l2cap_conf_opt; #define L2CAP_CONF_OPT_SIZE 2 +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +typedef struct { + uint8_t mode; + uint8_t txwin_size; + uint8_t max_transmit; + uint16_t retrans_timeout; + uint16_t monitor_timeout; + uint16_t max_pdu_size; +} __attribute__ ((packed)) l2cap_conf_rfc ; +#define L2CAP_CONF_RFC_SIZE 9 +#endif + #define L2CAP_CONF_MTU 0x01 #define L2CAP_CONF_FLUSH_TO 0x02 #define L2CAP_CONF_QOS 0x03 diff --git a/lib/mgmt.h b/lib/mgmt.h index 798a05e..2a7b787 100755 --- a/lib/mgmt.h +++ b/lib/mgmt.h @@ -900,21 +900,361 @@ static const char *mgmt_status[] = { "Permission Denied", }; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + +#define TIZEN_OP_CODE_BASE 0xff00 +#define TIZEN_EV_BASE 0xff00 + +#define MGMT_MAX_ADVERTISING_LENGTH 31 + +#define MGMT_MAX_EIR_MANUFACTURER_DATA_LENGTH 100 + +#define MGMT_IRK_SIZE 16 + +#define MGMT_OP_SET_ADVERTISING_PARAMS (TIZEN_OP_CODE_BASE + 0x01) +struct mgmt_cp_set_advertising_params { + uint16_t interval_min; + uint16_t interval_max; + uint8_t filter_policy; + uint8_t type; +} __packed; + +#define MGMT_OP_SET_ADVERTISING_DATA (TIZEN_OP_CODE_BASE + 0x02) +struct mgmt_cp_set_advertising_data { + uint8_t data[MGMT_MAX_ADVERTISING_LENGTH]; +} __packed; + +#define MGMT_OP_SET_SCAN_RSP_DATA (TIZEN_OP_CODE_BASE + 0x03) +struct mgmt_cp_set_scan_rsp_data { + uint8_t data[MGMT_MAX_ADVERTISING_LENGTH]; +} __packed; + +#define MGMT_OP_ADD_DEV_WHITE_LIST (TIZEN_OP_CODE_BASE + 0x04) +struct mgmt_cp_add_dev_white_list { + uint8_t bdaddr_type; + bdaddr_t bdaddr; +} __packed; + +#define MGMT_OP_REMOVE_DEV_FROM_WHITE_LIST (TIZEN_OP_CODE_BASE + 0x05) +struct mgmt_cp_remove_dev_white_list { + uint8_t bdaddr_type; + bdaddr_t bdaddr; +} __packed; + +#define MGMT_OP_CLEAR_DEV_WHITE_LIST (TIZEN_OP_CODE_BASE + 0x06) + +/* BEGIN TIZEN_Bluetooth :: RSSI monitoring */ +#define MGMT_OP_SET_RSSI_ENABLE (TIZEN_OP_CODE_BASE + 0x07) +struct mgmt_cp_set_enable_rssi { + int8_t low_th; + int8_t in_range_th; + int8_t high_th; + bdaddr_t bdaddr; + int8_t link_type; +} __packed; + +struct mgmt_cc_rsp_enable_rssi { + uint8_t status; + uint8_t le_ext_opcode; + bdaddr_t bt_address; + int8_t link_type; +} __packed; + +#define MGMT_OP_GET_RAW_RSSI (TIZEN_OP_CODE_BASE + 0x08) +struct mgmt_cp_get_raw_rssi { + bdaddr_t bt_address; + uint8_t link_type; +} __packed; +struct mgmt_cc_rp_get_raw_rssi { + uint8_t status; + int8_t rssi_dbm; + uint8_t link_type; + bdaddr_t bt_address; +} __packed; + +#define MGMT_OP_SET_RSSI_DISABLE (TIZEN_OP_CODE_BASE + 0x09) +struct mgmt_cp_disable_rssi { + bdaddr_t bdaddr; + int8_t link_type; +} __packed; +struct mgmt_cc_rp_disable_rssi { + uint8_t status; + uint8_t le_ext_opcode; + bdaddr_t bt_address; + int8_t link_type; +} __packed; +/* END TIZEN_Bluetooth :: RSSI monitoring */ + +#define MGMT_OP_START_LE_DISCOVERY (TIZEN_OP_CODE_BASE + 0x0a) +struct mgmt_cp_start_le_discovery { + uint8_t type; +} __packed; + +#define MGMT_OP_STOP_LE_DISCOVERY (TIZEN_OP_CODE_BASE + 0x0b) +struct mgmt_cp_stop_le_discovery { + uint8_t type; +} __packed; + +#define MGMT_OP_LE_CONN_UPDATE (TIZEN_OP_CODE_BASE + 0x0d) +struct mgmt_cp_le_conn_update { + uint16_t interval_min; + uint16_t interval_max; + uint16_t latency; + uint16_t supervision_time_out; + bdaddr_t bdaddr; +} __packed; + +#define MGMT_OP_SET_MANUFACTURER_DATA (TIZEN_OP_CODE_BASE + 0x0e) +struct mgmt_cp_set_manufacturer_data { + uint8_t data[MGMT_MAX_EIR_MANUFACTURER_DATA_LENGTH]; +} __packed; + +#define MGMT_OP_LE_SET_SCAN_PARAMS (TIZEN_OP_CODE_BASE + 0x0f) +struct mgmt_cp_le_set_scan_params { + uint8_t type; /* le scan type */ + uint16_t interval; + uint16_t window; +} __packed; + +#define MGMT_SCO_ROLE_HANDSFREE 0x00 +#define MGMT_SCO_ROLE_AUDIO_GATEWAY 0x01 +#define MGMT_OP_SET_VOICE_SETTING (TIZEN_OP_CODE_BASE + 0x10) +struct mgmt_cp_set_voice_setting { + bdaddr_t bdaddr; + uint8_t sco_role; + uint16_t voice_setting; +} __packed; + +#define MGMT_OP_GET_ADV_TX_POWER (TIZEN_OP_CODE_BASE + 0x11) +struct mgmt_rp_get_adv_tx_power { + int8_t adv_tx_power; +} __packed; + +#define MGMT_OP_ENABLE_6LOWPAN (TIZEN_OP_CODE_BASE + 0x12) +struct mgmt_cp_enable_6lowpan { + uint8_t enable_6lowpan; +} __packed; + +#define MGMT_OP_CONNECT_6LOWPAN (TIZEN_OP_CODE_BASE + 0x13) +struct mgmt_cp_connect_6lowpan { + struct mgmt_addr_info addr; +} __packed; + +#define MGMT_OP_DISCONNECT_6LOWPAN (TIZEN_OP_CODE_BASE + 0x14) +struct mgmt_cp_disconnect_6lowpan { + struct mgmt_addr_info addr; +} __packed; + +#define MGMT_OP_LE_READ_MAXIMUM_DATA_LENGTH (TIZEN_OP_CODE_BASE + 0x15) +struct mgmt_rp_le_read_maximum_data_length { + uint8_t status; + uint16_t max_tx_octets; + uint16_t max_tx_time; + uint16_t max_rx_octets; + uint16_t max_rx_time; +} __packed; + +#define MGMT_OP_LE_WRITE_HOST_SUGGESTED_DATA_LENGTH (TIZEN_OP_CODE_BASE + 0x16) +struct mgmt_cp_le_write_host_suggested_data_length { + uint16_t def_tx_octets; + uint16_t def_tx_time; +} __packed; + +#define MGMT_OP_LE_READ_HOST_SUGGESTED_DATA_LENGTH (TIZEN_OP_CODE_BASE + 0x17) +struct mgmt_rp_le_read_host_suggested_data_length { + uint8_t status; + uint16_t def_tx_octets; + uint16_t def_tx_time; +} __packed; + +#define MGMT_OP_LE_SET_DATA_LENGTH (TIZEN_OP_CODE_BASE + 0x18) +struct mgmt_cp_le_set_data_length { + bdaddr_t bdaddr; + uint16_t max_tx_octets; + uint16_t max_tx_time; +} __packed; +#define MGMT_LE_SET_DATA_LENGTH_SIZE 10 + +#define MGMT_OP_SET_IRK (TIZEN_OP_CODE_BASE + 0x19) +struct mgmt_cp_set_irk { + uint8_t irk[16]; +} __packed; + +#define MGMT_OP_SET_DEV_RPA_RES_SUPPORT (TIZEN_OP_CODE_BASE + 0x1a) +struct mgmt_cp_set_dev_rpa_res_support { + struct mgmt_addr_info addr; + uint8_t res_support; +} __packed; + +/* BEGIN TIZEN_Bluetooth :: name update changes */ +#define MGMT_EV_DEVICE_NAME_UPDATE (TIZEN_EV_BASE + 0x01) +struct mgmt_ev_device_name_update { + struct mgmt_addr_info addr; + uint16_t eir_len; + uint8_t eir[0]; +} __packed; +/* END TIZEN_Bluetooth :: name update changes */ + +/* BEGIN TIZEN_Bluetooth :: Add handling of hardware error event */ +#define MGMT_EV_HARDWARE_ERROR (TIZEN_EV_BASE + 0x02) +struct mgmt_ev_hardware_error{ + uint8_t error_code; +} __packed; +/* END TIZEN_Bluetooth */ + +/* BEGIN TIZEN_Bluetooth :: HCI TX Timeout Error */ +#define MGMT_EV_TX_TIMEOUT_ERROR (TIZEN_EV_BASE + 0x03) +/* END TIZEN_Bluetooth */ + +/* BEGIN TIZEN_Bluetooth :: Add handling of RSSI Events */ +#define MGMT_EV_RSSI_ALERT (TIZEN_EV_BASE + 0x04) +struct mgmt_ev_vendor_specific_rssi_alert { + bdaddr_t bdaddr; + int8_t link_type; + int8_t alert_type; + int8_t rssi_dbm; +} __packed; + +#define MGMT_EV_RAW_RSSI (TIZEN_EV_BASE + 0x05) + +#define MGMT_EV_RSSI_ENABLED (TIZEN_EV_BASE + 0x06) + +#define MGMT_EV_RSSI_DISABLED (TIZEN_EV_BASE + 0x07) +/* END TIZEN_Bluetooth :: Handling of RSSI Events */ + +/* BEGIN TIZEN_Bluetooth :: Add LE connection update Events */ +#define MGMT_EV_CONN_UPDATED (TIZEN_EV_BASE + 0x08) +struct mgmt_ev_conn_updated { + struct mgmt_addr_info addr; + uint16_t conn_interval; + uint16_t conn_latency; + uint16_t supervision_timeout; +} __packed; + +#define MGMT_EV_CONN_UPDATE_FAILED (TIZEN_EV_BASE + 0x09) +struct mgmt_ev_conn_update_failed { + struct mgmt_addr_info addr; + uint8_t status; +} __packed; +/* END TIZEN_Bluetooth :: Add LE connection update Events */ + +#define MGMT_EV_LE_DEVICE_FOUND (TIZEN_EV_BASE + 0x0a) +struct mgmt_ev_le_device_found { + struct mgmt_addr_info addr; + int8_t rssi; + uint32_t flags; + int8_t adv_type; + uint16_t eir_len; + uint8_t eir[0]; +} __packed; + +#define MGMT_EV_MULTI_ADV_STATE_CHANGED (TIZEN_EV_BASE + 0x0b) +struct mgmt_ev_vendor_specific_multi_adv_state_changed { + uint8_t adv_instance; + uint8_t state_change_reason; + int16_t connection_handle; +} __packed; + +#define MGMT_EV_6LOWPAN_CONN_STATE_CHANGED (TIZEN_EV_BASE + 0x0c) +struct mgmt_ev_6lowpan_conn_state_changed { + struct mgmt_addr_info addr; + uint8_t connected; + uint8_t ifname[16]; +} __packed; + + +#define MGMT_EV_LE_DATA_LENGTH_CHANGED (TIZEN_EV_BASE + 0x0d) +struct mgmt_ev_le_data_length_changed { + struct mgmt_addr_info addr; + int16_t max_tx_octets; + int16_t max_tx_time; + int16_t max_rx_octets; + int16_t max_rx_time; +} __packed; + +static const char *mgmt_tizen_op[] = { + "<0x0000>", + "Set Advertising Parameters", + "Set Advertising Data", + "Set Scan Response Data", + "Add Device White List", + "Remove Device White List", + "Clear Device White List", + "Set RSSI Enable", + "Get Raw RSSI", + "Set RSSI Disable", + "Start LE Discovery", + "Stop LE Discovery", + "Disable LE Auto Connect", + "LE Connection Update", + "Set Manufacturer Data", + "LE Set Scan Parameters", + "Set Voice Setting", + "Get Adv Tx Power", +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + "Enable BT 6LOWPAN", + "Connect BT 6LOWPAN", + "Disconnect BT 6LOWPAN" +#endif +}; + +static const char *mgmt_tizen_ev[] = { + "<0x0000>", + "Device Name Update", + "Hardware Error", + "Tx TimeOut Error", + "RSSI Alert", + "Raw RSSI", + "RSSI Enabled", + "RSSI Disabled", + "LE Connection Updated", + "LE Connection Update Failed", + "LE Device Found", + "Multi Adv State Change", +}; +#endif /* End of TIZEN_FEATURE_BLUEZ_MODIFY */ + #ifndef NELEM #define NELEM(x) (sizeof(x) / sizeof((x)[0])) #endif static inline const char *mgmt_opstr(uint16_t op) { +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (op >= NELEM(mgmt_op)) { + uint16_t tizen_op = op - TIZEN_OP_CODE_BASE; + + if (tizen_op > 0 && + tizen_op < NELEM(mgmt_tizen_op)) + return mgmt_tizen_op[tizen_op]; + + return ""; + } +#else if (op >= NELEM(mgmt_op)) return ""; +#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 ""; + } +#else if (ev >= NELEM(mgmt_ev)) return ""; +#endif + return mgmt_ev[ev]; } diff --git a/lib/uuid.h b/lib/uuid.h index 2dcfe9e..393fbe8 100755 --- a/lib/uuid.h +++ b/lib/uuid.h @@ -30,6 +30,9 @@ extern "C" { #endif #include +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#include +#endif #define GENERIC_AUDIO_UUID "00001203-0000-1000-8000-00805f9b34fb" @@ -88,6 +91,9 @@ extern "C" { #define HDP_SINK_UUID "00001402-0000-1000-8000-00805f9b34fb" #define HID_UUID "00001124-0000-1000-8000-00805f9b34fb" +#ifdef TIZEN_BT_HID_DEVICE_ENABLE +#define HID_DEVICE_UUID "00001124-0000-1000-8000-00805f9b43bf" +#endif #define DUN_GW_UUID "00001103-0000-1000-8000-00805f9b34fb" @@ -106,6 +112,12 @@ extern "C" { #define OBEX_MNS_UUID "00001133-0000-1000-8000-00805f9b34fb" #define OBEX_MAP_UUID "00001134-0000-1000-8000-00805f9b34fb" +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +/* Samsung Accessary Protocol UUIDs */ +#define WEARABLE_OLD_SAP_UUID "a49eb41e-cb06-495c-9f4f-aa80a90cdf4a" +#define WEARABLE_NEW_SAP_UUID "a49eb41e-cb06-495c-9f4f-bb80a90cdf00" +#endif + /* GATT UUIDs section */ #define GATT_PRIM_SVC_UUID 0x2800 #define GATT_SND_SVC_UUID 0x2801 @@ -127,6 +139,9 @@ extern "C" { #define GATT_CHARAC_SOFTWARE_REVISION_STRING 0x2A28 #define GATT_CHARAC_MANUFACTURER_NAME_STRING 0x2A29 #define GATT_CHARAC_PNP_ID 0x2A50 +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#define GATT_CHARAC_CENTRAL_RPA_RESOLUTION 0x2AA6 +#endif /* GATT Characteristic Descriptors */ #define GATT_CHARAC_EXT_PROPER_UUID 0x2900 @@ -139,6 +154,11 @@ extern "C" { #define GATT_EXTERNAL_REPORT_REFERENCE 0x2907 #define GATT_REPORT_REFERENCE 0x2908 +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +/* GATT Service UUIDs : Defined by SIG */ +#define GATT_IPSP_UUID 0x1820 +#endif + typedef struct { enum { BT_UUID_UNSPEC = 0, diff --git a/monitor/broadcom.c b/monitor/broadcom.c index a3c3443..80edbeb 100755 --- a/monitor/broadcom.c +++ b/monitor/broadcom.c @@ -35,6 +35,9 @@ #include "ll.h" #include "vendor.h" #include "broadcom.h" +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#include "uuid.h" +#endif static void print_status(uint8_t status) { @@ -93,6 +96,599 @@ static void launch_ram_cmd(const void *data, uint8_t size) print_field("Address: 0x%8.8x", addr); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static void set_advt_param_multi_subcmd(const void *data, uint8_t size) +{ + uint8_t adv_instance = get_u8(data + size - 2); + int8_t tx_power = *((int8_t *)(data + size - 1)); + + print_le_set_adv_parameters_cmd(data, size - 2); + + print_field("Advertising Instance: %u", adv_instance); + print_field("TX Power: %d", tx_power); +} + +static void set_advt_data_subcmd(const void *data, uint8_t size) +{ + uint8_t adv_instance = get_u8(data + size - 1); + + print_le_set_adv_data_cmd(data, size - 1); + + print_field("Advertising Instance: %u", adv_instance); +} + +static void set_scan_rsp_data_multi_subcmd(const void *data, uint8_t size) +{ + uint8_t adv_instance = get_u8(data + size - 1); + + print_le_set_scan_rsp_data_cmd(data, size - 1); + + print_field("Advertising Instance: %u", adv_instance); +} + +static void set_random_addr_multi_subcmd(const void *data, uint8_t size) +{ + uint8_t adv_instance = get_u8(data + size - 1); + + print_le_set_random_address_cmd(data, size - 1); + + print_field("Advertising Instance: %u", adv_instance); +} + +static void set_adv_enable_multi_subcmd(const void *data, uint8_t size) +{ + uint8_t adv_instance = get_u8(data + size - 1); + + print_le_set_adv_enable_cmd(data, size - 1); + + print_field("Advertising Instance: %u", adv_instance); +} + +static void enable_custom_feature_subcmd(const void *data, uint8_t size) +{ + uint8_t enable = get_u8(data); + const char *str; + + switch (enable) { + case 0x00: + str = "Disable"; + break; + case 0x01: + str = "Enable"; + break; + default: + str = "Reserved"; + break; + } + + print_field("%s offloaded RPA feature (0x%2.2x)", str, enable); +} + +static void add_irk_to_list_subcmd(const void *data, uint8_t size) +{ + uint8_t addr_type = get_u8(data + 16); + const uint8_t *addr = data + 17; + const char *str; + + print_field("LE IRK (1st byte LSB)"); + packet_hexdump(data, 16); + + switch (addr_type) { + case 0x00: + str = "Public Address"; + break; + case 0x01: + str = "Random Address"; + break; + default: + str = "Reserved"; + break; + } + + print_field("Address type : %s (0x%2.2x)", str, addr_type); + print_field("Address : %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X", + addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); +} + +static void remove_irk_from_list_subcmd(const void *data, uint8_t size) +{ + uint8_t addr_type = get_u8(data); + const uint8_t *addr = data + 1; + const char *str; + + switch (addr_type) { + case 0x00: + str = "Public Address"; + break; + case 0x01: + str = "Random Address"; + break; + default: + str = "Reserved"; + break; + } + + print_field("Address type : %s (0x%2.2x)", str, addr_type); + print_field("Address : %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X", + addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); +} + +static void read_irk_list_entry_subcmd(const void *data, uint8_t size) +{ + uint8_t index = get_u8(data); + + print_field("LE Read IRK List entry index : %u", index); +} + +static void apcf_enable_subcmd(const void *data, uint8_t size) +{ + uint8_t enable = get_u8(data); + const char *str; + + switch (enable) { + case 0x00: + str = "Disable"; + break; + case 0x01: + str = "Enable"; + break; + default: + str = "Reserved"; + break; + } + + print_field("%s APCF feature (0x%2.2x)", str, enable); +} + +static const struct { + uint8_t bit; + const char *str; +} apcf_feature_table[] = { + { 0, "Broadcast Address filter" }, + { 1, "Service Data Change filter" }, + { 2, "Service UUID check" }, + { 3, "Service Solicitation UUID check" }, + { 4, "Local Name check" }, + { 5, "Manufacturer Data check" }, + { 6, "Service Data check" }, + { } +}; + +static void print_apcf_feature(const char *label, uint16_t feature) +{ + int i; + uint16_t mask; + + mask = feature; + + print_field("%s", label); + + for (i = 0; apcf_feature_table[i].str; i++) { + if (feature & (1 << apcf_feature_table[i].bit)) { + print_field(" %s", apcf_feature_table[i].str); + mask &= ~(1 << apcf_feature_table[i].bit); + } + } + + if (mask) + print_field(" Unknown features (0x%4.4x)", mask); +} + +static void apcf_set_filtering_param_subcmd(const void *data, uint8_t size) +{ + uint8_t add = get_u8(data); + uint8_t index = get_u8(data + 1); + uint16_t feature_selection = get_le16(data + 2); + uint16_t list_logic = get_le16(data + 4); + uint16_t filter_logic = get_u8(data + 6); + uint8_t rssi_high = get_u8(data + 8); + uint8_t delivery_mode = get_u8(data + 9); + uint16_t onfound_timeout = get_le16(data + 10); + uint8_t onfound_timeout_cnt = get_u8(data + 12); + uint8_t rssi_low = get_u8(data + 13); + uint16_t onlost_timeout = get_le16(data + 14); + uint16_t no_of_tracking_entries; + const char *str; + + switch (add) { + case 0x00: + str = "Add"; + break; + case 0x01: + str = "Delete"; + break; + case 0x02: + str = "Clear"; + break; + default: + str = "Reserved"; + break; + } + + print_field("Action : %s for filter [%d]", str, index); + + print_apcf_feature("Feature Selection", feature_selection); + print_apcf_feature("List Logic Type (OR)", ~list_logic); + print_apcf_feature("List Logic Type (AND)", list_logic); + print_apcf_feature("Filter Logic Type (OR)", ~(filter_logic << 3)); + print_apcf_feature("Filter Logic Type (AND)", filter_logic << 3); + print_field("RSSI High Threshold : %d dBm", rssi_high); + + switch (delivery_mode) { + case 0x00: + str = "Immediate"; + break; + case 0x01: + str = "On Found"; + break; + case 0x02: + str = "Batched"; + break; + default: + str = "Reserved"; + break; + } + + print_field("Delivery_mode : %s", str); + print_field("On Found Timeout : %d miliseconds", onfound_timeout); + print_field("On Found Timeout Count : %d", onfound_timeout_cnt); + print_field("RSSI Low Threshold : %d dBm", rssi_low); + print_field("On Lost Timeout : %d miliseconds", onlost_timeout); + + if (size >= 18) { + no_of_tracking_entries = get_le16(data + 16); + print_field("Number of Tracking Entries : %d", + no_of_tracking_entries); + } +} + +static void apcf_broadcaster_addr_subcmd(const void *data, uint8_t size) +{ + uint8_t add = get_u8(data); + uint8_t index = get_u8(data + 1); + uint8_t type = get_u8(data + 7); + char *str; + + switch (add) { + case 0x00: + str = "Add"; + break; + case 0x01: + str = "Delete"; + break; + case 0x02: + str = "Clear"; + break; + default: + str = "Reserved"; + break; + } + + print_field("Action : %s for filter [%d]", str, index); + packet_print_addr("Address", data + 2, type == 0x00 ? false : true); +} + +static void apcf_service_uuid_subcmd(const void *data, uint8_t size) +{ + uint8_t add = get_u8(data); + uint8_t index = get_u8(data + 1); + char *str; + const uint8_t *uuid; + uint16_t uuid16; + uint32_t uuid32; + + + switch (add) { + case 0x00: + str = "Add"; + break; + case 0x01: + str = "Delete"; + break; + case 0x02: + str = "Clear"; + break; + default: + str = "Reserved"; + break; + } + + print_field("Action : %s for filter [%d]", str, index); + + switch ((size - 2) / 2) { + case 2: + uuid16 = get_le16(data + 2); + print_field(" UUID : %s (0x%4.4x)", + uuid16_to_str(uuid16), uuid16); + + uuid16 = get_le16(data + 4); + print_field(" UUID Mask: %s (0x%4.4x)", + uuid16_to_str(uuid16), uuid16); + break; + case 4: + uuid32 = get_le32(data + 2); + print_field(" UUID :%s (0x%8.8x)", + uuid32_to_str(uuid32), uuid32); + + uuid32 = get_le32(data + 6); + print_field(" UUID Mask:%s (0x%8.8x)", + uuid32_to_str(uuid32), uuid32); + break; + case 16: + uuid = data + 2; + print_field(" UUID :%8.8x-%4.4x-%4.4x-%4.4x-%8.8x%4.4x", + get_le32(&uuid[12]), get_le16(&uuid[10]), + get_le16(&uuid[8]), get_le16(&uuid[6]), + get_le32(&uuid[2]), get_le16(&uuid[0])); + uuid = data + 18; + print_field(" UUID :%8.8x-%4.4x-%4.4x-%4.4x-%8.8x%4.4x", + get_le32(&uuid[12]), get_le16(&uuid[10]), + get_le16(&uuid[8]), get_le16(&uuid[6]), + get_le32(&uuid[2]), get_le16(&uuid[0])); + break; + default: + print_field("Invalid UUIDs"); + packet_hexdump(data + 2, size - 2); + break; + } + + return; +} + +static void apcf_service_solicitation_uuid_subcmd(const void *data, uint8_t size) +{ + apcf_service_uuid_subcmd(data, size); +} + +static void apcf_local_name_subcmd(const void *data, uint8_t size) +{ + uint8_t add = get_u8(data); + uint8_t index = get_u8(data + 1); + char *str; + char name[30] = { 0 }; + + switch (add) { + case 0x00: + str = "Add"; + break; + case 0x01: + str = "Delete"; + break; + case 0x02: + str = "Clear"; + break; + default: + str = "Reserved"; + break; + } + + print_field("Action : %s for filter [%d]", str, index); + + memcpy(name, data + 2, size - 2 < 29 ? size - 2 : 29); + print_field("Local Name : %s", name); +} + +static void apcf_manufacturer_data_subcmd(const void *data, uint8_t size) +{ + uint8_t add = get_u8(data); + uint8_t index = get_u8(data + 1); + char *str; + + switch (add) { + case 0x00: + str = "Add"; + break; + case 0x01: + str = "Delete"; + break; + case 0x02: + str = "Clear"; + break; + default: + str = "Reserved"; + break; + } + + print_field("Action : %s for filter [%d]", str, index); + + print_field("Manufacturer data"); + packet_hexdump(data + 2, (size - 2 ) / 2); + + print_field("Manufacturer data Mask"); + packet_hexdump(data + 2 + (size - 2) / 2, (size - 2 ) / 2); +} + +static void apcf_service_data_subcmd(const void *data, uint8_t size) +{ + uint8_t add = get_u8(data); + uint8_t index = get_u8(data + 1); + char *str; + + switch (add) { + case 0x00: + str = "Add"; + break; + case 0x01: + str = "Delete"; + break; + case 0x02: + str = "Clear"; + break; + default: + str = "Reserved"; + break; + } + + print_field("Action : %s for filter [%d]", str, index); + + print_field("Service data"); + packet_hexdump(data + 2, (size - 2 ) / 2); + + print_field("Service data Mask"); + packet_hexdump(data + 2 + (size - 2) / 2, (size - 2 ) / 2); +} + +struct subcmd_data { + uint8_t subcmd; + const char *str; + void (*cmd_func) (const void *data, uint8_t size); + uint8_t cmd_size; + bool cmd_fixed; +}; + +static void print_subcmd(const struct subcmd_data *subcmd_data, + const void *data, uint8_t size) +{ + const char *subcmd_color; + + if (subcmd_data->cmd_func) + subcmd_color = COLOR_BLUE; + else + subcmd_color = COLOR_WHITE_BG; + + print_indent(6, subcmd_color, "", subcmd_data->str, COLOR_OFF, + " (0x%2.2x)", subcmd_data->subcmd); + + if (!subcmd_data->cmd_func) { + packet_hexdump(data, size); + return; + } + + if (subcmd_data->cmd_fixed) { + if (size != subcmd_data->cmd_size) { + print_text(COLOR_ERROR, "invalid packet size"); + packet_hexdump(data, size); + return; + } + } else { + if (size < subcmd_data->cmd_size) { + print_text(COLOR_ERROR, "too short packet"); + packet_hexdump(data, size); + return; + } + } + + subcmd_data->cmd_func(data, size); +} + +static const struct subcmd_data le_multi_advt_table[] = { + { 0x01, "LE Set Advertising Parameters Multi Sub Command", + set_advt_param_multi_subcmd, 23, true }, + { 0x02, "LE Set Advertising Data Multi Sub Command", + set_advt_data_subcmd, 33, false }, + { 0x03, "LE Set Scan Response Data Multi Sub Command", + set_scan_rsp_data_multi_subcmd, 33, false }, + { 0x04, "LE Set Random Address Multi Sub Command", + set_random_addr_multi_subcmd, 7, true }, + { 0x05, "LE Set Advertise Enable Multi Sub Command", + set_adv_enable_multi_subcmd, 2, true }, + { } +}; + +static void le_multi_advt_cmd(const void *data, uint8_t size) +{ + uint8_t subcmd = *((const uint8_t *)data); + struct subcmd_data unknown; + const struct subcmd_data *subcmd_data = &unknown; + int i; + + unknown.subcmd = subcmd; + unknown.str = "Unknown Sub Command"; + unknown.cmd_func = NULL; + unknown.cmd_size = 0; + unknown.cmd_fixed = true; + + for (i = 0; le_multi_advt_table[i].str; i++) { + if (le_multi_advt_table[i].subcmd == subcmd) { + subcmd_data = &le_multi_advt_table[i]; + break; + } + } + + print_subcmd(subcmd_data, data + 1, size - 1); +} + +static const struct subcmd_data le_rpa_offload_table[] = { + { 0x01, "Enable customer specific feature", + enable_custom_feature_subcmd, 1, true }, + { 0x02, "Add IRK to the list", + add_irk_to_list_subcmd, 23, true }, + { 0x03, "Remove IRK from the list", + remove_irk_from_list_subcmd, 7, true }, + { 0x04, "Clear IRK list", + null_cmd, 0, true }, + { 0x05, "Read IRK list entry", + read_irk_list_entry_subcmd, 1, true }, + { } +}; + +static void le_rpa_offload_cmd(const void *data, uint8_t size) +{ + uint8_t subcmd = *((const uint8_t *)data); + struct subcmd_data unknown; + const struct subcmd_data *subcmd_data = &unknown; + int i; + + unknown.subcmd = subcmd; + unknown.str = "Unknown Sub Command"; + unknown.cmd_func = NULL; + unknown.cmd_size = 0; + unknown.cmd_fixed = true; + + for (i = 0; le_rpa_offload_table[i].str; i++) { + if (le_rpa_offload_table[i].subcmd == subcmd) { + subcmd_data = &le_rpa_offload_table[i]; + break; + } + } + + print_subcmd(subcmd_data, data + 1, size - 1); +} + +static const struct subcmd_data le_apcf_table[] = { + { 0x00, "APCF Enable", + apcf_enable_subcmd, 1, true }, + { 0x01, "APCF Set Filtering Parameters", + apcf_set_filtering_param_subcmd, 15, false }, + { 0x02, "APCF Broadcaster Address", + apcf_broadcaster_addr_subcmd, 9, true }, + { 0x03, "APCF Service UUID", + apcf_service_uuid_subcmd, 2, false }, + { 0x04, "APCF Service Solicitation UUID", + apcf_service_solicitation_uuid_subcmd, 2, false }, + { 0x05, "APCF Local Name", + apcf_local_name_subcmd, 2, false }, + { 0x06, "APCF Manufacturer Data", + apcf_manufacturer_data_subcmd, 2, false }, + { 0x07, "APCF Service Data", + apcf_service_data_subcmd, 2, false }, + { } +}; + +static void le_apcf_cmd(const void *data, uint8_t size) +{ + uint8_t subcmd = *((const uint8_t *)data); + struct subcmd_data unknown; + const struct subcmd_data *subcmd_data = &unknown; + int i; + + unknown.subcmd = subcmd; + unknown.str = "Unknown Sub Command"; + unknown.cmd_func = NULL; + unknown.cmd_size = 0; + unknown.cmd_fixed = true; + + for (i = 0; le_apcf_table[i].str; i++) { + if (le_apcf_table[i].subcmd == subcmd) { + subcmd_data = &le_apcf_table[i]; + break; + } + } + + print_subcmd(subcmd_data, data + 1, size - 1); +} +#endif + static void read_vid_pid_rsp(const void *data, uint8_t size) { uint8_t status = get_u8(data); @@ -132,6 +728,97 @@ static void read_verbose_version_info_rsp(const void *data, uint8_t size) print_field("Build number: %u (0x%4.4x)", build_num, build_num); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static void get_vendor_capabilities_rsp(const void *data, uint8_t size) +{ + uint8_t status = get_u8(data); + uint8_t max_advt_instances = get_u8(data + 1); + uint8_t offloaded_resolution_of_private_address = get_u8(data + 2); + uint16_t total_scan_results_storage = get_le16(data + 3); + uint8_t max_irk_list_sz = get_u8(data + 5); + uint8_t filtering_support = get_u8(data + 6); + uint8_t max_filter = get_u8(data + 7); + uint8_t activity_energy_info_support = get_u8(data + 8); + uint8_t onlost_follow_per_filter = get_u8(data + 9); + + print_status(status); + print_field("The Number of advertisement instances supported: %u", + max_advt_instances); + print_field("BT chip capability of RPA: %s", + offloaded_resolution_of_private_address ? + "Capable" : "Not Capable"); + print_field("Storage for scan results: %u bytes", + total_scan_results_storage); + print_field("The Number of IRK entries supported: %u", max_irk_list_sz); + print_field("Support Filtering in BT chip: %s", + filtering_support ? "Supported" : "Not Supported"); + print_field("The Number of filters supported: %u", max_filter); + print_field("Supports reporting of activity and energy info: %s", + activity_energy_info_support ? + "Capable" : "Not Capable"); + print_field("The Number of advertisers that can be analysed for " + "onlost per filter: %u", onlost_follow_per_filter); +} + +static void le_multi_advt_rsp(const void *data, uint8_t size) +{ + uint8_t status = get_u8(data); + uint8_t subcmd = get_u8(data + 1); + int i; + const char *str = "Unknown Sub Command"; + + print_status(status); + + for (i = 0; le_multi_advt_table[i].str; i++) { + if (le_multi_advt_table[i].subcmd == subcmd) { + str = le_multi_advt_table[i].str; + break; + } + } + + print_field("Multi Advertise OPcode: %s (%u)", str, subcmd); +} + +static void le_rpa_offload_rsp(const void *data, uint8_t size) +{ + uint8_t status = get_u8(data); + uint8_t subcmd = get_u8(data + 1); + int i; + const char *str = "Unknown Sub Command"; + + print_status(status); + + for (i = 0; le_rpa_offload_table[i].str; i++) { + if (le_rpa_offload_table[i].subcmd == subcmd) { + str = le_rpa_offload_table[i].str; + break; + } + } + + print_field("RPA Offload OPcode: %s (%u)", str, subcmd); +} + +static void le_apcf_rsp(const void *data, uint8_t size) +{ + uint8_t status = get_u8(data); + uint8_t subcmd = get_u8(data + 1); + int i; + const char *str = "Unknown Sub Command"; + + print_status(status); + + for (i = 0; le_apcf_table[i].str; i++) { + if (le_apcf_table[i].subcmd == subcmd) { + str = le_apcf_table[i].str; + break; + } + } + + print_field("Advertising Packet Content Filter OPcode: %s (%u)", + str, subcmd); +} +#endif + static const struct vendor_ocf vendor_ocf_table[] = { { 0x001, "Write BD ADDR", write_bd_addr_cmd, 6, true, @@ -157,6 +844,25 @@ static const struct vendor_ocf vendor_ocf_table[] = { { 0x079, "Read Verbose Config Version Info", null_cmd, 0, true, read_verbose_version_info_rsp, 7, true }, +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + { 0x0153, "LE Get Vendor Capabilities", + null_cmd, 0, true, + get_vendor_capabilities_rsp, 10, false }, + { 0x0154, "LE Multi Advertise", + le_multi_advt_cmd, 1, false, + le_multi_advt_rsp, 2, true }, + { 0x0155, "LE RPA Offload", + le_rpa_offload_cmd, 1, false, + le_rpa_offload_rsp, 2, false }, +#if 0 + { 0x0156, "LE Batch Scan", + le_batch_scan_cmd, 1, false, + le_batch_scan_rsp, 2, true }, +#endif + { 0x0157, "LE APCF", + le_apcf_cmd, 1, false, + le_apcf_rsp, 2, false }, +#endif { } }; @@ -234,7 +940,59 @@ void broadcom_lm_diag(const void *data, uint8_t size) } } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +struct hci_vse_sec_brcm_link_loss_dbg_info{ + unsigned char linklost_status; + unsigned char conn_handle; + char trans_pwr; + char rssi; + unsigned char ch_map[10]; + unsigned char lmp_cmd[4]; +} __packed; + +static void linkloss_evt(const void *data, uint8_t size) +{ + + struct hci_vse_sec_brcm_link_loss_dbg_info *ev = (void *) data; + char *status = NULL; + switch (ev->linklost_status) { + case 0: + status = "BT_Link_Supervision_Timeout"; + break; + case 1: + status = "LE_Link_Supervision_Timeout"; + break; + case 2: + status = "BT_LMP_Timeout_Local"; + break; + case 3: + status = "BT_LMP_Timeout_Remote"; + break; + case 4: + status = "LE_LMP_Timeout"; + break; + case 5: + status = "Page_Timeout"; + break; + default : + break; + } + + print_field("Status:%s,Handle:%02x,Trans_Pwr:%d,RSSI:%d" + " Ch_map:%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x" + " LMP_cmd:0x%x%x%x%x", + status, ev->conn_handle, ev->trans_pwr, ev->rssi, + ev->ch_map[0], ev->ch_map[1], ev->ch_map[2], ev->ch_map[3], + ev->ch_map[4], ev->ch_map[5], ev->ch_map[6], ev->ch_map[7], + ev->ch_map[8], ev->ch_map[9], ev->lmp_cmd[0], ev->lmp_cmd[1], + ev->lmp_cmd[2], ev->lmp_cmd[3]); +} +#endif + static const struct vendor_evt vendor_evt_table[] = { +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + { 0x76, "SEC Link Loss", linkloss_evt, 18, true }, +#endif { } }; diff --git a/monitor/control.c b/monitor/control.c index 9bbdc37..4262e43 100755 --- a/monitor/control.c +++ b/monitor/control.c @@ -1364,9 +1364,18 @@ int control_tty(const char *path, unsigned int speed) return 0; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +bool control_writer(const char *path, int16_t rotate_count, ssize_t file_size) +#else bool control_writer(const char *path) +#endif { +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + btsnoop_file = btsnoop_create(path, BTSNOOP_FORMAT_MONITOR, + rotate_count, file_size); +#else btsnoop_file = btsnoop_create(path, BTSNOOP_FORMAT_MONITOR); +#endif return !!btsnoop_file; } @@ -1396,6 +1405,9 @@ void control_reader(const char *path) break; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + setenv("PAGER", "cat", 0); +#endif open_pager(); switch (format) { diff --git a/monitor/control.h b/monitor/control.h index 630a852..cd66441 100755 --- a/monitor/control.h +++ b/monitor/control.h @@ -24,7 +24,11 @@ #include +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +bool control_writer(const char *path, int16_t rotate_count, ssize_t file_size); +#else bool control_writer(const char *path); +#endif void control_reader(const char *path); void control_server(const char *path); int control_tty(const char *path, unsigned int speed); diff --git a/monitor/display.h b/monitor/display.h index b85f37b..1f54c6c 100755 --- a/monitor/display.h +++ b/monitor/display.h @@ -48,7 +48,11 @@ bool use_color(void); #define COLOR_INFO COLOR_OFF #define COLOR_DEBUG COLOR_WHITE +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#define FALLBACK_TERMINAL_WIDTH 130 +#else #define FALLBACK_TERMINAL_WIDTH 80 +#endif #define print_indent(indent, color1, prefix, title, color2, fmt, args...) \ do { \ diff --git a/monitor/main.c b/monitor/main.c index f9bca22..57830ad 100755 --- a/monitor/main.c +++ b/monitor/main.c @@ -70,6 +70,10 @@ static void usage(void) "\t-T, --date Show time and date information\n" "\t-S, --sco Dump SCO traffic\n" "\t-E, --ellisys [ip] Send Ellisys HCI Injection\n" +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + "\t-C, --count Save traces by rotation\n" + "\t-W, --size Save traces at most size\n" +#endif "\t-h, --help Show help options\n"); } @@ -86,6 +90,10 @@ static const struct option main_options[] = { { "date", no_argument, NULL, 'T' }, { "sco", no_argument, NULL, 'S' }, { "ellisys", required_argument, NULL, 'E' }, +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + { "count", required_argument, NULL, 'C' }, + { "size", required_argument, NULL, 'W' }, +#endif { "todo", no_argument, NULL, '#' }, { "version", no_argument, NULL, 'v' }, { "help", no_argument, NULL, 'h' }, @@ -102,6 +110,10 @@ int main(int argc, char *argv[]) const char *tty = NULL; unsigned int tty_speed = B115200; unsigned short ellisys_port = 0; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + int16_t rotate_count = -1; + ssize_t file_size = -1; +#endif const char *str; int exit_status; sigset_t mask; @@ -113,8 +125,13 @@ int main(int argc, char *argv[]) for (;;) { int opt; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + opt = getopt_long(argc, argv, "d:r:w:a:s:p:i:tTSE:C:W:vh", + main_options, NULL); +#else opt = getopt_long(argc, argv, "d:r:w:a:s:p:i:tTSE:vh", main_options, NULL); +#endif if (opt < 0) break; @@ -171,6 +188,14 @@ int main(int argc, char *argv[]) ellisys_server = optarg; ellisys_port = 24352; break; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + case 'C': + rotate_count = atoi(optarg); + break; + case 'W': + file_size = atoll(optarg); + break; +#endif case '#': packet_todo(); lmp_todo(); @@ -221,10 +246,18 @@ int main(int argc, char *argv[]) return EXIT_SUCCESS; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (writer_path && !control_writer(writer_path, + rotate_count, file_size)) { + printf("Failed to open '%s'\n", writer_path); + return EXIT_FAILURE; + } +#else if (writer_path && !control_writer(writer_path)) { printf("Failed to open '%s'\n", writer_path); return EXIT_FAILURE; } +#endif if (ellisys_server) ellisys_enable(ellisys_server, ellisys_port); diff --git a/monitor/packet.c b/monitor/packet.c index 6272562..259b805 100755 --- a/monitor/packet.c +++ b/monitor/packet.c @@ -7413,6 +7413,10 @@ static const char *current_vendor_str(void) return "Intel"; case 15: return "Broadcom"; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + default: + return "Unknown"; +#endif } return NULL; @@ -7432,6 +7436,10 @@ static const struct vendor_ocf *current_vendor_ocf(uint16_t ocf) return intel_vendor_ocf(ocf); case 15: return broadcom_vendor_ocf(ocf); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + default: + return broadcom_vendor_ocf(ocf); +#endif } return NULL; @@ -7451,6 +7459,10 @@ static const struct vendor_evt *current_vendor_evt(uint8_t evt) return intel_vendor_evt(evt); case 15: return broadcom_vendor_evt(evt); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + default: + return broadcom_vendor_evt(evt); +#endif } return NULL; @@ -11695,3 +11707,30 @@ void packet_todo(void) printf("\t%s\n", le_meta_event_table[i].str); } } + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +void print_le_set_adv_parameters_cmd(const void *data, uint8_t size) +{ + le_set_adv_parameters_cmd(data, size); +} + +void print_le_set_random_address_cmd(const void *data, uint8_t size) +{ + le_set_random_address_cmd(data, size); +} + +void print_le_set_adv_data_cmd(const void *data, uint8_t size) +{ + le_set_adv_data_cmd(data, size); +} + +void print_le_set_scan_rsp_data_cmd(const void *data, uint8_t size) +{ + le_set_scan_rsp_data_cmd(data, size); +} + +void print_le_set_adv_enable_cmd(const void *data, uint8_t size) +{ + le_set_adv_enable_cmd(data, size); +} +#endif diff --git a/monitor/packet.h b/monitor/packet.h index 354f4fe..20e1718 100755 --- a/monitor/packet.h +++ b/monitor/packet.h @@ -99,3 +99,11 @@ void packet_ctrl_event(struct timeval *tv, struct ucred *cred, uint16_t index, const void *data, uint16_t size); void packet_todo(void); + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +void print_le_set_adv_parameters_cmd(const void *data, uint8_t size); +void print_le_set_random_address_cmd(const void *data, uint8_t size); +void print_le_set_adv_data_cmd(const void *data, uint8_t size); +void print_le_set_scan_rsp_data_cmd(const void *data, uint8_t size); +void print_le_set_adv_enable_cmd(const void *data, uint8_t size); +#endif diff --git a/obexd/client/bluetooth.c b/obexd/client/bluetooth.c index e35124a..a755b8c 100755 --- a/obexd/client/bluetooth.c +++ b/obexd/client/bluetooth.c @@ -186,10 +186,21 @@ static void search_callback(uint8_t type, uint16_t status, protos = NULL; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + /* Use obex over l2cap only if obex over rfcomm is not there */ + if (ch == -1) { + data = sdp_data_get(rec, 0x0200); + /* PSM must be odd and lsb of upper byte must be 0 */ + if (data != NULL && (data->val.uint16 & 0x0101) == + 0x0001) + ch = data->val.uint16; + } +#else data = sdp_data_get(rec, 0x0200); /* PSM must be odd and lsb of upper byte must be 0 */ if (data != NULL && (data->val.uint16 & 0x0101) == 0x0001) ch = data->val.uint16; +#endif /* Cache the sdp record associated with the service that we * attempt to connect. This allows reading its application diff --git a/obexd/client/manager.c b/obexd/client/manager.c index fbcad6d..cc472e4 100755 --- a/obexd/client/manager.c +++ b/obexd/client/manager.c @@ -46,7 +46,9 @@ #include "sync.h" #include "map.h" #include "manager.h" - +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#include "mns-tizen.h" +#endif #define CLIENT_INTERFACE "org.bluez.obex.Client1" #define ERROR_INTERFACE "org.bluez.obex.Error" #define CLIENT_PATH "/org/bluez/obex" @@ -64,11 +66,21 @@ static void shutdown_session(struct obc_session *session) obc_session_unref(session); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +void release_session(struct obc_session *session) +{ + DBG("+"); + sessions = g_slist_remove(sessions, session); + shutdown_session(session); + DBG("-"); +} +#else static void release_session(struct obc_session *session) { sessions = g_slist_remove(sessions, session); shutdown_session(session); } +#endif static void unregister_session(void *data) { @@ -154,7 +166,10 @@ static int parse_device_dict(DBusMessageIter *iter, static struct obc_session *find_session(const char *path) { GSList *l; - +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if(!path) + return NULL; +#endif for (l = sessions; l; l = l->next) { struct obc_session *session = l->data; @@ -238,9 +253,16 @@ static DBusMessage *remove_session(DBusConnection *connection, ERROR_INTERFACE ".NotAuthorized", "Not Authorized"); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + obc_session_update(session, message, connection); +#endif release_session(session); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + return NULL; +#else return dbus_message_new_method_return(message); +#endif } static const GDBusMethodTable client_methods[] = { @@ -265,6 +287,9 @@ static struct obc_module { { "pbap", pbap_init, pbap_exit }, { "sync", sync_init, sync_exit }, { "map", map_init, map_exit }, +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + { "mns", mns_init, mns_exit }, +#endif { } }; diff --git a/obexd/client/mns-tizen.c b/obexd/client/mns-tizen.c new file mode 100755 index 0000000..c6da1eb --- /dev/null +++ b/obexd/client/mns-tizen.c @@ -0,0 +1,271 @@ +/* + * + * OBEX Client + * + * Copyright (C) 2012 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include + +#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(""); + g_string_append_printf(buf, ""); + + return g_string_free(buf, FALSE); +} + +static DBusMessage *send_event(DBusConnection *connection, + DBusMessage *message, void *user_data) +{ + struct mns_data *mns = user_data; + struct obc_transfer *transfer; + GObexApparam *apparam; + gchar *event_type; + gchar *folder; + gchar *old_folder; + gchar *msg_type; + gchar *buf; + guint64 handle; + GError *err; + DBusMessage *reply; + + if (dbus_message_get_args(message, NULL, + DBUS_TYPE_STRING, &event_type, + DBUS_TYPE_UINT64, &handle, + DBUS_TYPE_STRING, &folder, + DBUS_TYPE_STRING, &old_folder, + DBUS_TYPE_STRING, &msg_type, + DBUS_TYPE_INVALID) == FALSE) + return g_dbus_create_error(message, + "org.openobex.Error.InvalidArguments", + NULL); + + buf = generate_event_report(event_type, handle, folder, + old_folder, msg_type); + if (!buf) + return g_dbus_create_error(message, + "org.openobex.Error.InvalidArguments", NULL); + + transfer = obc_transfer_put("x-bt/MAP-event-report", NULL, NULL, + buf, strlen(buf), &err); + + g_free(buf); + + if (transfer == NULL) + goto fail; + + /* Obexd currently supports single SDP for MAS */ + apparam = g_obex_apparam_set_uint8(NULL, MAP_AP_MASINSTANCEID, 0); + + obc_transfer_set_apparam(transfer, apparam); + + if (obc_session_queue(mns->session, transfer, NULL, NULL, &err)) + return dbus_message_new_method_return(message); + +fail: + reply = g_dbus_create_error(message, ERROR_INF ".Failed", "%s", + err->message); + g_error_free(err); + return reply; +} + +static GDBusMethodTable mns_methods[] = { + { GDBUS_ASYNC_METHOD("SendEvent", + GDBUS_ARGS({ "event_type", "s" }, { "handle", "t" }, + { "folder", "s" }, { "old_folder", "s" }, + { "msg_type", "s" }), + NULL, + send_event) }, + { } +}; + +static void mns_free(void *data) +{ + struct mns_data *mns = data; + + obc_session_unref(mns->session); + g_free(mns); +} + +static int mns_probe(struct obc_session *session) +{ + struct mns_data *mns; + const char *path; + + path = obc_session_get_path(session); + + DBG("%s", path); + + mns = g_try_new0(struct mns_data, 1); + if (!mns) + return -ENOMEM; + + mns->session = obc_session_ref(session); + + if (!g_dbus_register_interface(conn, path, MNS_INTERFACE, mns_methods, + NULL, NULL, mns, mns_free)) { + mns_free(mns); + return -ENOMEM; + } + + return 0; +} + +static void mns_remove(struct obc_session *session) +{ + const char *path = obc_session_get_path(session); + + DBG("%s", path); + + g_dbus_unregister_interface(conn, path, MNS_INTERFACE); +} + +static struct obc_driver mns = { + .service = "MNS", + .uuid = MNS_UUID, + .target = OBEX_MNS_UUID, + .target_len = OBEX_MNS_UUID_LEN, + .probe = mns_probe, + .remove = mns_remove +}; + +int mns_init(void) +{ + int err; + + DBG(""); + + conn = dbus_bus_get(DBUS_BUS_SESSION, NULL); + if (!conn) + return -EIO; + + err = obc_driver_register(&mns); + if (err < 0) { + dbus_connection_unref(conn); + conn = NULL; + return err; + } + + return 0; +} + +void mns_exit(void) +{ + DBG(""); + + dbus_connection_unref(conn); + conn = NULL; + + obc_driver_unregister(&mns); +} diff --git a/obexd/client/mns-tizen.h b/obexd/client/mns-tizen.h new file mode 100755 index 0000000..e4ee5e5 --- /dev/null +++ b/obexd/client/mns-tizen.h @@ -0,0 +1,24 @@ +/* + * + * OBEX Client + * + * Copyright (C) 2012 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +int mns_init(void); +void mns_exit(void); diff --git a/obexd/client/opp.c b/obexd/client/opp.c index 92785f6..9a6cf34 100755 --- a/obexd/client/opp.c +++ b/obexd/client/opp.c @@ -55,16 +55,26 @@ static DBusMessage *opp_send_file(DBusConnection *connection, char *filename; char *basename; GError *err = NULL; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + char *mimetype = NULL; +#endif if (dbus_message_get_args(message, NULL, DBUS_TYPE_STRING, &filename, +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + DBUS_TYPE_STRING, &mimetype, +#endif DBUS_TYPE_INVALID) == FALSE) return g_dbus_create_error(message, ERROR_INTERFACE ".InvalidArguments", NULL); basename = g_path_get_basename(filename); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + transfer = obc_transfer_put(mimetype, basename, filename, NULL, 0, &err); +#else transfer = obc_transfer_put(NULL, basename, filename, NULL, 0, &err); +#endif g_free(basename); @@ -123,7 +133,11 @@ static DBusMessage *opp_exchange_business_cards(DBusConnection *connection, static const GDBusMethodTable opp_methods[] = { { GDBUS_METHOD("SendFile", +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + GDBUS_ARGS({ "sourcefile", "s" }, { "mimetype", "s" }), +#else GDBUS_ARGS({ "sourcefile", "s" }), +#endif GDBUS_ARGS({ "transfer", "o" }, { "properties", "a{sv}" }), opp_send_file) }, { GDBUS_METHOD("PullBusinessCard", diff --git a/obexd/client/pbap.c b/obexd/client/pbap.c index 1ab34a7..db46a80 100755 --- a/obexd/client/pbap.c +++ b/obexd/client/pbap.c @@ -218,7 +218,11 @@ static char *build_phonebook_path(const char *location, const char *item) internal = TRUE; } else if (!g_ascii_strncasecmp(location, "sim", 3)) { if (strlen(location) == 3) +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + tmp = g_strdup("SIM1"); +#else tmp = g_strdup("sim1"); +#endif else tmp = g_ascii_strup(location, 4); @@ -227,6 +231,11 @@ static char *build_phonebook_path(const char *location, const char *item) } else return NULL; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (!g_ascii_strcasecmp(item, "nil")) + return path; +#endif + if (!g_ascii_strcasecmp(item, "pb") || !g_ascii_strcasecmp(item, "ich") || !g_ascii_strcasecmp(item, "och") || @@ -358,7 +367,11 @@ static void read_return_apparam(struct obc_transfer *transfer, g_obex_apparam_get_uint16(apparam, PHONEBOOKSIZE_TAG, phone_book_size); +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY + g_obex_apparam_get_uint16(apparam, NEWMISSEDCALLS_TAG, +#else g_obex_apparam_get_uint8(apparam, NEWMISSEDCALLS_TAG, +#endif new_missed_calls); read_version(pbap, apparam); @@ -493,8 +506,14 @@ static GObexApparam *parse_offset(GObexApparam *apparam, DBusMessageIter *iter) { guint16 num; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16 && + dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT32) + return NULL; +#else if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16) return NULL; +#endif dbus_message_iter_get_basic(iter, &num); @@ -506,8 +525,14 @@ static GObexApparam *parse_max_count(GObexApparam *apparam, { guint16 num; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16 && + dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT32) + return NULL; +#else if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16) return NULL; +#endif dbus_message_iter_get_basic(iter, &num); @@ -737,9 +762,16 @@ static DBusMessage *pbap_select(DBusConnection *connection, } request = pending_request_new(pbap, message); - - obc_session_setpath(pbap->session, path, pbap_setpath_cb, request, - &err); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (pbap->path == NULL || strlen(pbap->path) == 0) + obc_session_setpath(pbap->session, path + 1, pbap_setpath_cb, request, + &err); + else + obc_session_setpath(pbap->session, path, pbap_setpath_cb, request, + &err); +#else + obc_session_setpath(pbap->session, path, pbap_setpath_cb, request, &err); +#endif if (err != NULL) { DBusMessage *reply; reply = g_dbus_create_error(message, ERROR_INTERFACE ".Failed", @@ -870,6 +902,9 @@ static DBusMessage *pbap_list(DBusConnection *connection, struct pbap_data *pbap = user_data; GObexApparam *apparam; DBusMessageIter args; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + const char *pb_folder; +#endif if (!pbap->path) return g_dbus_create_error(message, @@ -878,6 +913,15 @@ static DBusMessage *pbap_list(DBusConnection *connection, dbus_message_iter_init(message, &args); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_STRING) + return g_dbus_create_error(message, + ERROR_INTERFACE ".InvalidArguments", NULL); + + dbus_message_iter_get_basic(&args, &pb_folder); + dbus_message_iter_next(&args); +#endif + apparam = g_obex_apparam_set_uint16(NULL, MAXLISTCOUNT_TAG, DEFAULT_COUNT); apparam = g_obex_apparam_set_uint16(apparam, LISTSTARTOFFSET_TAG, @@ -889,7 +933,11 @@ static DBusMessage *pbap_list(DBusConnection *connection, ERROR_INTERFACE ".InvalidArguments", NULL); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + return pull_vcard_listing(pbap, message, pb_folder, apparam); +#else return pull_vcard_listing(pbap, message, "", apparam); +#endif } static GObexApparam *parse_attribute(GObexApparam *apparam, const char *field) @@ -1049,7 +1097,11 @@ static const GDBusMethodTable pbap_methods[] = { { "properties", "a{sv}" }), pbap_pull_vcard) }, { GDBUS_ASYNC_METHOD("List", - GDBUS_ARGS({ "filters", "a{sv}" }), +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + GDBUS_ARGS({ "folder", "s" }, {"filters", "a{sv}" }), +#else + GDBUS_ARGS({"filters", "a{sv}" }), +#endif GDBUS_ARGS({ "vcard_listing", "a(ss)" }), pbap_list) }, { GDBUS_ASYNC_METHOD("Search", diff --git a/obexd/client/session.c b/obexd/client/session.c index 5f981bf..6c7c337 100755 --- a/obexd/client/session.c +++ b/obexd/client/session.c @@ -43,6 +43,9 @@ #include "dbus.h" #include "transfer.h" #include "session.h" +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#include "manager.h" +#endif #include "driver.h" #include "transport.h" @@ -114,6 +117,10 @@ struct obc_session { guint process_id; char *folder; struct callback_data *callback; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + DBusMessage *message; + DBusConnection *connection; +#endif }; static GSList *sessions = NULL; @@ -139,6 +146,17 @@ struct obc_session *obc_session_ref(struct obc_session *session) return session; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +void obc_session_update(struct obc_session *session, DBusMessage *message, + DBusConnection *connection) +{ + DBG("+"); + session->message = dbus_message_ref(message); + session->connection = dbus_connection_ref(connection); + DBG("-"); +} +#endif + static void session_unregistered(struct obc_session *session) { char *path; @@ -263,6 +281,18 @@ static void disconnect_complete(GObex *obex, GError *err, GObexPacket *rsp, if (err) error("%s", err->message); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (session->message) { + /* Dbus reply need to be done */ + DBG("Dbus reply for remove_session"); + g_dbus_send_reply(session->connection, session->message, + DBUS_TYPE_INVALID); + dbus_message_unref(session->message); + dbus_connection_unref(session->connection); + session->message = NULL; + session->connection = NULL; + } +#endif /* Disconnect transport */ if (session->id > 0 && session->transport != NULL) { session->transport->disconnect(session->id); @@ -350,8 +380,11 @@ static void session_disconnected(GObex *obex, GError *err, gpointer user_data) if (err) error("%s", err->message); - +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + release_session(session); +#else obc_session_shutdown(session); +#endif } static void transport_func(GIOChannel *io, GError *err, gpointer user_data) diff --git a/obexd/client/session.h b/obexd/client/session.h index b561b7e..2ce9f3f 100755 --- a/obexd/client/session.h +++ b/obexd/client/session.h @@ -80,3 +80,8 @@ guint obc_session_delete(struct obc_session *session, const char *file, GError **err); void obc_session_cancel(struct obc_session *session, guint id, gboolean remove); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +void release_session(struct obc_session *session); +void obc_session_update(struct obc_session *session, DBusMessage *message, + DBusConnection *connection); +#endif diff --git a/obexd/client/transfer.c b/obexd/client/transfer.c index 092e72f..a5ecd13 100755 --- a/obexd/client/transfer.c +++ b/obexd/client/transfer.c @@ -787,12 +787,26 @@ static gboolean report_progress(gpointer data) if (transfer->transferred == transfer->progress) return TRUE; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (transfer->transferred == transfer->size) { + transfer->progress_id = 0; + if(transfer->progress == 0) { + transfer->progress = transfer->transferred; + transfer_set_status(transfer, TRANSFER_STATUS_ACTIVE); + } + + return FALSE; + } + + transfer->progress = transfer->transferred; +#else transfer->progress = transfer->transferred; if (transfer->transferred == transfer->size) { transfer->progress_id = 0; return FALSE; } +#endif if (transfer->status != TRANSFER_STATUS_ACTIVE && transfer->status != TRANSFER_STATUS_SUSPENDED) @@ -884,8 +898,13 @@ static gboolean transfer_start_put(struct obc_transfer *transfer, GError **err) if (transfer->path == NULL) return TRUE; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + transfer->progress_id = g_timeout_add(10, report_progress, + transfer); +#else transfer->progress_id = g_timeout_add_seconds(1, report_progress, transfer); +#endif return TRUE; } diff --git a/obexd/plugins/ftp.c b/obexd/plugins/ftp.c index 3ee18a6..85756a0 100755 --- a/obexd/plugins/ftp.c +++ b/obexd/plugins/ftp.c @@ -170,6 +170,13 @@ int ftp_chkput(struct obex_session *os, void *user_data) { struct ftp_session *ftp = user_data; const char *name = obex_get_name(os); +#if 0 +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + char *folder; + int32_t time; + int err; +#endif +#endif char *path; int ret; @@ -184,6 +191,15 @@ int ftp_chkput(struct obex_session *os, void *user_data) if (obex_get_size(os) == OBJECT_SIZE_DELETE) return 0; +#if 0 +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + time = 0; + err = manager_request_authorization(os, time, &folder, &name); + if (err < 0) + return -EPERM; +#endif +#endif + path = g_build_filename(ftp->folder, name, NULL); ret = obex_put_stream_start(os, path); @@ -219,6 +235,36 @@ int ftp_put(struct obex_session *os, void *user_data) return 0; } +#if 0 +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static gboolean is_valid_name(const char *name) +{ + char *forbid_chars = "[*\"<>;?|\\^:/]"; + int exp; + regex_t reg; + regmatch_t match[1]; + size_t size = 1; + + if (name[0] == '.') + return FALSE; + + exp = regcomp(®, 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; @@ -273,6 +319,16 @@ int ftp_setpath(struct obex_session *os, void *user_data) return -EPERM; } +#if 0 +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + /* Check if the folder name is valid or not */ + if (!is_valid_name(name)) { + error("Set path failed: Invalid folder name!"); + return -EINVAL; + } +#endif +#endif + fullname = g_build_filename(ftp->folder, name, NULL); DBG("Fullname: %s", fullname); diff --git a/obexd/plugins/messages-tizen.c b/obexd/plugins/messages-tizen.c new file mode 100755 index 0000000..e59c37b --- /dev/null +++ b/obexd/plugins/messages-tizen.c @@ -0,0 +1,1547 @@ +/* + * + * OBEX Server + * + * Copyright (C) 2012 Samsung Electronics Co., Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include "log.h" +#include "messages.h" +#include "../../profile.h" + +#include + +#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) +{ +} diff --git a/obexd/plugins/messages-tracker.c b/obexd/plugins/messages-tracker.c new file mode 100755 index 0000000..60f3a80 --- /dev/null +++ b/obexd/plugins/messages-tracker.c @@ -0,0 +1,345 @@ +/* + * + * OBEX Server + * + * Copyright (C) 2010-2011 Nokia Corporation + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include "messages.h" + +struct message_folder { + char *name; + GSList *subfolders; + char *query; +}; + +struct session { + char *cwd; + struct message_folder *folder; + char *name; + uint16_t max; + uint16_t offset; + void *user_data; + void (*folder_list_cb)(void *session, int err, uint16_t size, + const char *name, void *user_data); +}; + +static struct message_folder *folder_tree = NULL; + +static struct message_folder *get_folder(const char *folder) +{ + GSList *folders = folder_tree->subfolders; + struct message_folder *last = NULL; + char **path; + int i; + + if (g_strcmp0(folder, "/") == 0) + return folder_tree; + + path = g_strsplit(folder, "/", 0); + + for (i = 1; path[i] != NULL; i++) { + gboolean match_found = FALSE; + GSList *l; + + for (l = folders; l != NULL; l = g_slist_next(l)) { + struct message_folder *folder = l->data; + + if (g_strcmp0(folder->name, path[i]) == 0) { + match_found = TRUE; + last = l->data; + folders = folder->subfolders; + break; + } + } + + if (!match_found) { + g_strfreev(path); + return NULL; + } + } + + g_strfreev(path); + + return last; +} + +static struct message_folder *create_folder(const char *name, const char *query) +{ + struct message_folder *folder = g_new0(struct message_folder, 1); + + folder->name = g_strdup(name); + folder->query = g_strdup(query); + + return folder; +} + +static void destroy_folder_tree(void *root) +{ + struct message_folder *folder = root; + GSList *tmp, *next; + + if (folder == NULL) + return; + + g_free(folder->name); + g_free(folder->query); + + tmp = folder->subfolders; + while (tmp != NULL) { + next = g_slist_next(tmp); + destroy_folder_tree(tmp->data); + tmp = next; + } + + g_slist_free(folder->subfolders); + g_free(folder); +} + +static void create_folder_tree(void) +{ + struct message_folder *parent, *child; + + folder_tree = create_folder("/", "FILTER (!BOUND(?msg))"); + + parent = create_folder("telecom", "FILTER (!BOUND(?msg))"); + folder_tree->subfolders = g_slist_append(folder_tree->subfolders, + parent); + + child = create_folder("msg", "FILTER (!BOUND(?msg))"); + parent->subfolders = g_slist_append(parent->subfolders, child); + + parent = child; + + child = create_folder("inbox", "?msg nmo:isSent \"false\" ; " + "nmo:isDeleted \"false\" ; " + "nmo:isDraft \"false\". "); + parent->subfolders = g_slist_append(parent->subfolders, child); + + child = create_folder("sent", "?msg nmo:isDeleted \"false\" ; " + "nmo:isSent \"true\" . "); + parent->subfolders = g_slist_append(parent->subfolders, child); + + child = create_folder("deleted", "?msg nmo:isDeleted \"true\" . "); + parent->subfolders = g_slist_append(parent->subfolders, child); +} + +int messages_init(void) +{ + create_folder_tree(); + + return 0; +} + +void messages_exit(void) +{ + destroy_folder_tree(folder_tree); +} + +int messages_connect(void **s) +{ + struct session *session = g_new0(struct session, 1); + + session->cwd = g_strdup("/"); + session->folder = folder_tree; + + *s = session; + + return 0; +} + +void messages_disconnect(void *s) +{ + struct session *session = s; + + g_free(session->cwd); + g_free(session); +} + +int messages_set_notification_registration(void *session, + void (*send_event)(void *session, + const struct messages_event *event, void *user_data), + void *user_data) +{ + return -ENOSYS; +} + +int messages_set_folder(void *s, const char *name, gboolean cdup) +{ + struct session *session = s; + char *newrel = NULL; + char *newabs; + char *tmp; + + if (name && (strchr(name, '/') || strcmp(name, "..") == 0)) + return -EBADR; + + if (cdup) { + if (session->cwd[0] == 0) + return -ENOENT; + + newrel = g_path_get_dirname(session->cwd); + + /* We use empty string for indication of the root directory */ + if (newrel[0] == '.' && newrel[1] == 0) + newrel[0] = 0; + } + + tmp = newrel; + if (!cdup && (!name || name[0] == 0)) + newrel = g_strdup(""); + else + newrel = g_build_filename(newrel ? newrel : session->cwd, name, + NULL); + g_free(tmp); + + if (newrel[0] != '/') + newabs = g_build_filename("/", newrel, NULL); + else + newabs = g_strdup(newrel); + + session->folder = get_folder(newabs); + if (session->folder == NULL) { + g_free(newrel); + g_free(newabs); + + return -ENOENT; + } + + g_free(newrel); + g_free(session->cwd); + session->cwd = newabs; + + return 0; +} + +static gboolean async_get_folder_listing(void *s) +{ + struct session *session = s; + gboolean count = FALSE; + int folder_count = 0; + char *path = NULL; + struct message_folder *folder; + GSList *dir; + + if (session->name && strchr(session->name, '/') != NULL) + goto done; + + path = g_build_filename(session->cwd, session->name, NULL); + + if (path == NULL || strlen(path) == 0) + goto done; + + folder = get_folder(path); + + if (folder == NULL) + goto done; + + if (session->max == 0) { + session->max = 0xffff; + session->offset = 0; + count = TRUE; + } + + for (dir = folder->subfolders; dir && + (folder_count - session->offset) < session->max; + folder_count++, dir = g_slist_next(dir)) { + struct message_folder *dir_data = dir->data; + + if (count == FALSE && session->offset <= folder_count) + session->folder_list_cb(session, -EAGAIN, 0, + dir_data->name, session->user_data); + } + + done: + session->folder_list_cb(session, 0, folder_count, NULL, + session->user_data); + + g_free(path); + g_free(session->name); + + return FALSE; +} + +int messages_get_folder_listing(void *s, const char *name, + uint16_t max, uint16_t offset, + messages_folder_listing_cb callback, + void *user_data) +{ + struct session *session = s; + session->name = g_strdup(name); + session->max = max; + session->offset = offset; + session->folder_list_cb = callback; + session->user_data = user_data; + + g_idle_add_full(G_PRIORITY_DEFAULT_IDLE, async_get_folder_listing, + session, NULL); + + return 0; +} + +int messages_get_messages_listing(void *session, const char *name, + uint16_t max, uint16_t offset, + uint8_t subject_len, + const struct messages_filter *filter, + messages_get_messages_listing_cb callback, + void *user_data) +{ + return -ENOSYS; +} + +int messages_get_message(void *session, const char *handle, + unsigned long flags, + messages_get_message_cb callback, + void *user_data) +{ + return -ENOSYS; +} + +int messages_update_inbox(void *session, messages_status_cb callback, + void *user_data) +{ + return -ENOSYS; +} + +int messages_set_read(void *session, const char *handle, uint8_t value, + messages_status_cb callback, void *user_data) +{ + return -ENOSYS; +} + +int messages_set_delete(void *session, const char *handle, uint8_t value, + messages_status_cb callback, + void *user_data) +{ + return -ENOSYS; +} + +void messages_abort(void *session) +{ +} diff --git a/obexd/plugins/pbap.c b/obexd/plugins/pbap.c index ad93208..68a2139 100755 --- a/obexd/plugins/pbap.c +++ b/obexd/plugins/pbap.c @@ -65,6 +65,10 @@ #define PHONEBOOKSIZE_TAG 0X08 #define NEWMISSEDCALLS_TAG 0X09 +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#define PBAP_MAXLISTCOUNT_MAX_VALUE 65535 +#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */ + struct cache { gboolean valid; uint32_t index; @@ -85,6 +89,9 @@ struct pbap_session { uint32_t find_handle; struct cache cache; struct pbap_object *obj; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + void *backend_data; +#endif }; struct pbap_object { @@ -194,11 +201,18 @@ static void phonebook_size_result(const char *buffer, size_t bufsize, phonebooksize); pbap->obj->firstpacket = TRUE; - +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY if (missed > 0) { +#else + if (pbap->params->required_missedcall_call_header == TRUE) { +#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */ DBG("missed %d", missed); +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY pbap->obj->apparam = g_obex_apparam_set_uint16( +#else + pbap->obj->apparam = g_obex_apparam_set_uint8( +#endif pbap->obj->apparam, NEWMISSEDCALLS_TAG, missed); @@ -231,13 +245,20 @@ static void query_result(const char *buffer, size_t bufsize, int vcards, else pbap->obj->buffer = g_string_append_len(pbap->obj->buffer, buffer, bufsize); - +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY if (missed > 0) { +#else + if (pbap->params->required_missedcall_call_header == TRUE) { +#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */ DBG("missed %d", missed); pbap->obj->firstpacket = TRUE; +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY pbap->obj->apparam = g_obex_apparam_set_uint16( +#else + pbap->obj->apparam = g_obex_apparam_set_uint8( +#endif pbap->obj->apparam, NEWMISSEDCALLS_TAG, missed); @@ -373,9 +394,27 @@ static int generate_response(void *user_data) pbap->obj->apparam, PHONEBOOKSIZE_TAG, size); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (pbap->params->required_missedcall_call_header == TRUE) { + //DBG("missed %d", missed); + pbap->obj->apparam = g_obex_apparam_set_uint8( + pbap->obj->apparam, + NEWMISSEDCALLS_TAG, + pbap->params->new_missed_calls); + } +#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */ return 0; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (pbap->params->required_missedcall_call_header == TRUE) { + pbap->obj->firstpacket = TRUE; + pbap->obj->apparam = g_obex_apparam_set_uint8( + pbap->obj->apparam, + NEWMISSEDCALLS_TAG, + pbap->params->new_missed_calls); + } +#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */ /* * Don't free the sorted list content: this list contains @@ -406,12 +445,18 @@ static int generate_response(void *user_data) return 0; } +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY static void cache_ready_notify(void *user_data) +#else +static void cache_ready_notify(void *user_data, unsigned int new_missed_call) +#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */ { struct pbap_session *pbap = user_data; DBG(""); - +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + pbap->params->new_missed_calls = new_missed_call; +#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */ phonebook_req_finalize(pbap->obj->request); pbap->obj->request = NULL; @@ -445,10 +490,27 @@ static void cache_entry_done(void *user_data) obex_object_set_io_flags(pbap->obj, G_IO_ERR, ret); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static void cache_clear_notify(void *user_data) +{ + struct pbap_session *pbap = user_data; + + if (pbap == NULL) + return; + + pbap->cache.valid = FALSE; + pbap->cache.index = 0; + cache_clear(&pbap->cache); +} +#endif + static struct apparam_field *parse_aparam(const uint8_t *buffer, uint32_t hlen) { GObexApparam *apparam; struct apparam_field *param; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + gboolean bmaxlistCount = FALSE; +#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */ apparam = g_obex_apparam_decode(buffer, hlen); if (apparam == NULL) @@ -466,13 +528,27 @@ static struct apparam_field *parse_aparam(const uint8_t *buffer, uint32_t hlen) g_obex_apparam_get_uint8(apparam, SEARCHATTRIB_TAG, ¶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, @@ -493,10 +569,28 @@ static void *pbap_connect(struct obex_session *os, int *err) pbap->folder = g_strdup("/"); pbap->find_handle = PHONEBOOK_INVALID_HANDLE; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + int error; + + error = phonebook_connect(&pbap->backend_data); + if (error < 0) { + if (err) + *err = error; + goto failed; + } +#endif + if (err) *err = 0; return pbap; + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +failed: + g_free(pbap); + + return NULL; +#endif } static int pbap_get(struct obex_session *os, void *user_data) @@ -612,6 +706,13 @@ static void pbap_disconnect(struct obex_session *os, void *user_data) manager_unregister_session(os); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (!pbap) + return; + + phonebook_disconnect(pbap->backend_data); +#endif + if (pbap->obj) pbap->obj->session = NULL; @@ -676,6 +777,13 @@ static void *vobject_pull_open(const char *name, int oflag, mode_t mode, ret = -EBADR; goto fail; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (strcmp(name, "/telecom/mch.vcf") == 0) + pbap->params->required_missedcall_call_header = TRUE; + + DBG("[%s] - required_missedcall_call_header [%d] ", + name,pbap->params->required_missedcall_call_header); +#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */ if (pbap->params->maxlistcount == 0) cb = phonebook_size_result; @@ -746,6 +854,13 @@ static void *vobject_list_open(const char *name, int oflag, mode_t mode, ret = -EPERM; goto fail; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (strcmp(name, "/telecom/mch") == 0) + pbap->params->required_missedcall_call_header = TRUE; + + DBG("[%s] - required_missedcall_call_header [%d] ", + name,pbap->params->required_missedcall_call_header); +#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */ /* PullvCardListing always get the contacts from the cache */ @@ -757,6 +872,10 @@ static void *vobject_list_open(const char *name, int oflag, mode_t mode, cache_ready_notify, pbap, &ret); if (ret == 0) obj = vobject_create(pbap, request); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + phonebook_set_cache_notification(pbap->backend_data, + cache_clear_notify, pbap); +#endif } if (ret < 0) goto fail; @@ -782,7 +901,11 @@ static void *vobject_vcard_open(const char *name, int oflag, mode_t mode, struct pbap_session *pbap = context; const char *id; uint32_t handle; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + int ret = 0; +#else int ret; +#endif void *request; DBG("name %s context %p valid %d", name, context, pbap->cache.valid); @@ -799,8 +922,18 @@ static void *vobject_vcard_open(const char *name, int oflag, mode_t mode, if (pbap->cache.valid == FALSE) { pbap->find_handle = handle; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + request = phonebook_create_cache(pbap->folder, + (phonebook_entry_cb)cache_entry_notify, + (phonebook_cache_ready_cb)cache_entry_done, pbap, &ret); +#else request = phonebook_create_cache(pbap->folder, cache_entry_notify, cache_entry_done, pbap, &ret); +#endif +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + phonebook_set_cache_notification(pbap->backend_data, + cache_clear_notify, pbap); +#endif goto done; } @@ -841,8 +974,15 @@ static ssize_t vobject_pull_get_next_header(void *object, void *buf, size_t mtu, if (obj->firstpacket) { obj->firstpacket = FALSE; - +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + gsize count = 0; + count = g_obex_apparam_encode(obj->apparam, buf, mtu); + DBG("APPARAM Processed remove tags"); + g_obex_apparam_remove_all(obj->apparam); + return count; +#else return g_obex_apparam_encode(obj->apparam, buf, mtu); +#endif } return 0; @@ -891,11 +1031,22 @@ static ssize_t vobject_list_get_next_header(void *object, void *buf, size_t mtu, return -EAGAIN; *hi = G_OBEX_HDR_APPARAM; - +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY if (pbap->params->maxlistcount == 0) return g_obex_apparam_encode(obj->apparam, buf, mtu); return 0; +#else + if (obj->apparam != NULL) { + gsize count = 0; + count = g_obex_apparam_encode(obj->apparam, buf, mtu); + DBG("APPARAM Processed remove tags"); + g_obex_apparam_remove_all(obj->apparam); + return count; + } + else + return 0; +#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */ } static ssize_t vobject_list_read(void *object, void *buf, size_t count) diff --git a/obexd/plugins/phonebook-dummy.c b/obexd/plugins/phonebook-dummy.c index 29ae889..322be4d 100755 --- a/obexd/plugins/phonebook-dummy.c +++ b/obexd/plugins/phonebook-dummy.c @@ -524,6 +524,11 @@ void *phonebook_get_entry(const char *folder, const char *id, filename = g_build_filename(root_folder, folder, id, NULL); fd = open(filename, O_RDONLY); + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + g_free(filename); +#endif + if (fd < 0) { DBG("open(): %s(%d)", strerror(errno), errno); if (err) diff --git a/obexd/plugins/phonebook-ebook.c b/obexd/plugins/phonebook-ebook.c new file mode 100755 index 0000000..c422585 --- /dev/null +++ b/obexd/plugins/phonebook-ebook.c @@ -0,0 +1,708 @@ +/* + * + * OBEX Server + * + * Copyright (C) 2009-2010 Intel Corporation + * Copyright (C) 2007-2010 Marcel Holtmann + * + * + * 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 +#endif + +#include +#include + +#include +#include + +#include "lib/bluetooth.h" + +#include "obexd/src/log.h" +#include "obexd/src/obex.h" +#include "obexd/src/service.h" +#include "phonebook.h" + +#define QUERY_FN "(contains \"family_name\" \"%s\")" +#define QUERY_NAME "(contains \"given_name\" \"%s\")" +#define QUERY_PHONE "(contains \"phone\" \"%s\")" + +struct query_context { + const struct apparam_field *params; + phonebook_cb contacts_cb; + phonebook_entry_cb entry_cb; + phonebook_cache_ready_cb ready_cb; + EBookQuery *query; + unsigned int count; + GString *buf; + char *id; + unsigned queued_calls; + void *user_data; + GSList *ebooks; + gboolean canceled; +}; + +static char *attribute_mask[] = { +/* 0 */ "VERSION", + "FN", + "N", + "PHOTO", + "BDAY", + "ADR", + "LABEL", + "TEL", +/* 8 */ "EMAIL", + "MAILER", + "TZ", + "GEO", + "TITLE", + "ROLE", + "LOGO", + "AGENT", +/* 16 */ "ORG", + "NOTE", + "REV", + "SOUND", + "URL", + "UID", + "KEY", + "NICKNAME", +/* 24 */ "CATEGORIES", + "PROID", + "CLASS", + "SORT-STRING", +/* 28 */ "X-IRMC-CALL-DATETIME", + NULL + +}; + +static void close_ebooks(GSList *ebooks) +{ + g_slist_free_full(ebooks, g_object_unref); +} + +static void free_query_context(struct query_context *data) +{ + g_free(data->id); + + if (data->buf != NULL) + g_string_free(data->buf, TRUE); + + if (data->query != NULL) + e_book_query_unref(data->query); + + close_ebooks(data->ebooks); + + g_free(data); +} + +static char *evcard_to_string(EVCard *evcard, unsigned int format, + uint64_t filter) +{ + EVCard *evcard2; + GList *l; + char *vcard; + + if (!filter) + return e_vcard_to_string(evcard, EVC_FORMAT_VCARD_30); + /* XXX There is no support for VCARD 2.1 at this time */ + + /* + * Mandatory attributes for vCard 2.1 are VERSION ,N and TEL. + * Mandatory attributes for vCard 3.0 are VERSION, N, FN and TEL + */ + filter = format == EVC_FORMAT_VCARD_30 ? filter | 0x87: filter | 0x85; + + l = e_vcard_get_attributes(evcard); + evcard2 = e_vcard_new(); + for (; l; l = g_list_next(l)) { + EVCardAttribute *attrib = l->data; + const char *name; + int i; + + if (!attrib) + continue; + + name = e_vcard_attribute_get_name(attrib); + + for (i = 0; attribute_mask[i] != NULL; i++) { + if (!(filter & (1 << i))) + continue; + if (g_strcmp0(name, attribute_mask[i]) != 0) + continue; + + e_vcard_add_attribute(evcard2, + e_vcard_attribute_copy(attrib)); + } + } + + vcard = e_vcard_to_string(evcard2, format); + g_object_unref(evcard2); + + return vcard; +} + +static void ebookpull_cb(EBook *book, const GError *gerr, GList *contacts, + void *user_data) +{ + struct query_context *data = user_data; + GList *l; + unsigned int count, maxcount; + + data->queued_calls--; + + if (data->canceled) + goto canceled; + + if (gerr != NULL) { + error("E-Book query failed: %s", gerr->message); + goto done; + } + + DBG(""); + + /* + * When MaxListCount is zero, PCE wants to know the number of used + * indexes in the phonebook of interest. All other parameters that + * may be present in the request shall be ignored. + */ + maxcount = data->params->maxlistcount; + if (maxcount == 0) { + data->count += g_list_length(contacts); + goto done; + } + + l = g_list_nth(contacts, data->params->liststartoffset); + + for (count = 0; l && count + data->count < maxcount; l = g_list_next(l), + count++) { + EContact *contact = E_CONTACT(l->data); + EVCard *evcard = E_VCARD(contact); + char *vcard; + + vcard = evcard_to_string(evcard, EVC_FORMAT_VCARD_30, + data->params->filter); + + data->buf = g_string_append(data->buf, vcard); + data->buf = g_string_append(data->buf, "\r\n"); + g_free(vcard); + } + + DBG("collected %d vcards", count); + + data->count += count; + + g_list_free_full(contacts, g_object_unref); + +done: + if (data->queued_calls == 0) { + GString *buf = data->buf; + data->buf = NULL; + + data->contacts_cb(buf->str, buf->len, data->count, + 0, TRUE, data->user_data); + + g_string_free(buf, TRUE); + + } + + return; + +canceled: + if (data->queued_calls == 0) + free_query_context(data); +} + +static void ebook_entry_cb(EBook *book, const GError *gerr, + EContact *contact, void *user_data) +{ + struct query_context *data = user_data; + EVCard *evcard; + char *vcard; + size_t len; + + data->queued_calls--; + + if (data->canceled) + goto done; + + if (gerr != NULL) { + error("E-Book query failed: %s", gerr->message); + goto done; + } + + DBG(""); + + evcard = E_VCARD(contact); + + vcard = evcard_to_string(evcard, EVC_FORMAT_VCARD_30, + data->params->filter); + + len = vcard ? strlen(vcard) : 0; + + data->count++; + data->contacts_cb(vcard, len, 1, 0, TRUE, data->user_data); + + g_free(vcard); + g_object_unref(contact); + + return; + +done: + if (data->queued_calls == 0) { + if (data->count == 0) + data->contacts_cb(NULL, 0, 1, 0, TRUE, + data->user_data); + else if (data->canceled) + free_query_context(data); + } +} + +static char *evcard_name_attribute_to_string(EVCard *evcard) +{ + EVCardAttribute *attrib; + GList *l; + GString *name = NULL; + + attrib = e_vcard_get_attribute(evcard, EVC_N); + if (!attrib) + return NULL; + + for (l = e_vcard_attribute_get_values(attrib); l; l = l->next) { + const char *value = l->data; + + if (!strlen(value)) + continue; + + if (!name) + name = g_string_new(value); + else { + name = g_string_append(name, ";"); + name = g_string_append(name, l->data); + } + } + + if (!name) + return NULL; + + return g_string_free(name, FALSE); +} + +static void cache_cb(EBook *book, const GError *gerr, GList *contacts, + void *user_data) +{ + struct query_context *data = user_data; + GList *l; + + data->queued_calls--; + + if (data->canceled) + goto canceled; + + if (gerr != NULL) { + error("E-Book operation failed: %s", gerr->message); + goto done; + } + + DBG(""); + + for (l = contacts; l; l = g_list_next(l)) { + EContact *contact = E_CONTACT(l->data); + EVCard *evcard = E_VCARD(contact); + EVCardAttribute *attrib; + char *uid, *tel, *name; + + name = evcard_name_attribute_to_string(evcard); + if (!name) + continue; + + attrib = e_vcard_get_attribute(evcard, EVC_UID); + if (!attrib) + continue; + + uid = e_vcard_attribute_get_value(attrib); + if (!uid) + continue; + + attrib = e_vcard_get_attribute(evcard, EVC_TEL); + if (attrib) + tel = e_vcard_attribute_get_value(attrib); + else + tel = g_strdup(""); + + data->entry_cb(uid, PHONEBOOK_INVALID_HANDLE, name, NULL, + tel, data->user_data); + + g_free(name); + g_free(uid); + g_free(tel); + } + + g_list_free_full(contacts, g_object_unref); + +done: + if (data->queued_calls == 0) + data->ready_cb(data->user_data); + + return; + +canceled: + if (data->queued_calls == 0) + free_query_context(data); +} + +static GSList *traverse_sources(GSList *ebooks, GSList *sources, + char **default_src) { + GError *gerr = NULL; + + for (; sources != NULL; sources = g_slist_next(sources)) { + char *uri; + ESource *source = E_SOURCE(sources->data); + EBook *ebook = e_book_new(source, &gerr); + + if (ebook == NULL) { + error("Can't create user's address book: %s", + gerr->message); + g_clear_error(&gerr); + continue; + } + + uri = e_source_get_uri(source); + if (g_strcmp0(*default_src, uri) == 0) { + g_free(uri); + continue; + } + g_free(uri); + + if (e_book_open(ebook, FALSE, &gerr) == FALSE) { + error("Can't open e-book address book: %s", + gerr->message); + g_object_unref(ebook); + g_clear_error(&gerr); + continue; + } + + if (*default_src == NULL) + *default_src = e_source_get_uri(source); + + DBG("%s address book opened", e_source_peek_name(source)); + + ebooks = g_slist_append(ebooks, ebook); + } + + return ebooks; +} + +int phonebook_init(void) +{ + g_type_init(); + + return 0; +} + +static GSList *open_ebooks(void) +{ + GError *gerr = NULL; + ESourceList *src_list; + GSList *list; + char *default_src = NULL; + GSList *ebooks = NULL; + + if (e_book_get_addressbooks(&src_list, &gerr) == FALSE) { + error("Can't list user's address books: %s", gerr->message); + g_error_free(gerr); + return NULL; + } + + list = e_source_list_peek_groups(src_list); + while (list != NULL) { + ESourceGroup *group = E_SOURCE_GROUP(list->data); + GSList *sources = e_source_group_peek_sources(group); + + ebooks = traverse_sources(ebooks, sources, &default_src); + + list = list->next; + } + + g_free(default_src); + g_object_unref(src_list); + + return ebooks; +} + +void phonebook_exit(void) +{ +} + +char *phonebook_set_folder(const char *current_folder, + const char *new_folder, uint8_t flags, int *err) +{ + gboolean root, child; + char *fullname = NULL, *tmp1, *tmp2, *base; + int ret = 0, len; + + root = (g_strcmp0("/", current_folder) == 0); + child = (new_folder && strlen(new_folder) != 0); + + /* Evolution back-end will support /telecom/pb folder only */ + + switch (flags) { + case 0x02: + /* Go back to root */ + if (!child) { + fullname = g_strdup("/"); + goto done; + } + + /* Go down 1 level */ + fullname = g_build_filename(current_folder, new_folder, NULL); + if (strcmp(PB_TELECOM_FOLDER, fullname) != 0 && + strcmp(PB_CONTACTS_FOLDER, fullname) != 0) { + g_free(fullname); + fullname = NULL; + ret = -ENOENT; + } + + break; + case 0x03: + /* Go up 1 level */ + if (root) { + /* Already root */ + ret = -EBADR; + goto done; + } + + /* + * Removing one level of the current folder. Current folder + * contains AT LEAST one level since it is not at root folder. + * Use glib utility functions to handle invalid chars in the + * folder path properly. + */ + tmp1 = g_path_get_basename(current_folder); + tmp2 = g_strrstr(current_folder, tmp1); + len = tmp2 - (current_folder + 1); + + g_free(tmp1); + + if (len == 0) + base = g_strdup("/"); + else + base = g_strndup(current_folder, len); + + /* Return one level only */ + if (!child) { + fullname = base; + goto done; + } + + fullname = g_build_filename(base, new_folder, NULL); + if (strcmp(fullname, PB_TELECOM_FOLDER) != 0 && + strcmp(fullname, PB_CONTACTS_FOLDER) != 0) { + g_free(fullname); + fullname = NULL; + ret = -ENOENT; + } + + g_free(base); + + break; + default: + ret = -EBADR; + break; + } + +done: + if (err) + *err = ret; + + return fullname; +} + +void phonebook_req_finalize(void *request) +{ + struct query_context *data = request; + + if (data->queued_calls == 0) + free_query_context(data); + else + data->canceled = TRUE; +} + +void *phonebook_pull(const char *name, const struct apparam_field *params, + phonebook_cb cb, void *user_data, int *err) +{ + struct query_context *data; + + if (g_strcmp0(PB_CONTACTS, name) != 0) { + if (err) + *err = -ENOENT; + + return NULL; + } + + data = g_new0(struct query_context, 1); + data->contacts_cb = cb; + data->params = params; + data->user_data = user_data; + data->buf = g_string_new(""); + data->query = e_book_query_any_field_contains(""); + data->ebooks = open_ebooks(); + + if (err) + *err = data->ebooks == NULL ? -EIO : 0; + + return data; +} + +int phonebook_pull_read(void *request) +{ + struct query_context *data = request; + GSList *l; + + if (!data) + return -ENOENT; + + for (l = data->ebooks; l != NULL; l = g_slist_next(l)) { + EBook *ebook = l->data; + + if (e_book_is_opened(ebook) == FALSE) + continue; + + if (e_book_get_contacts_async(ebook, data->query, + ebookpull_cb, data) == TRUE) + data->queued_calls++; + } + + if (data->queued_calls == 0) + return -ENOENT; + + return 0; +} + +void *phonebook_get_entry(const char *folder, const char *id, + const struct apparam_field *params, + phonebook_cb cb, void *user_data, int *err) +{ + struct query_context *data; + GSList *l; + + data = g_new0(struct query_context, 1); + data->contacts_cb = cb; + data->params = params; + data->user_data = user_data; + data->id = g_strdup(id); + data->ebooks = open_ebooks(); + + for (l = data->ebooks; l != NULL; l = g_slist_next(l)) { + EBook *ebook = l->data; + + if (e_book_is_opened(ebook) == FALSE) + continue; + + if (e_book_get_contact_async(ebook, data->id, + ebook_entry_cb, data) == TRUE) + data->queued_calls++; + } + + if (err) + *err = (data->queued_calls == 0 ? -ENOENT : 0); + + return data; +} + +void *phonebook_create_cache(const char *name, phonebook_entry_cb entry_cb, + phonebook_cache_ready_cb ready_cb, void *user_data, int *err) +{ + struct query_context *data; + EBookQuery *query; + GSList *l; + EContact *me; + EVCard *evcard; + GError *gerr = NULL; + EBook *eb; + EVCardAttribute *attrib; + char *uid, *tel, *cname; + + if (g_strcmp0(PB_CONTACTS_FOLDER, name) != 0) { + if (err) + *err = -ENOENT; + + return NULL; + } + + DBG(""); + + query = e_book_query_any_field_contains(""); + + data = g_new0(struct query_context, 1); + data->entry_cb = entry_cb; + data->ready_cb = ready_cb; + data->user_data = user_data; + data->query = query; + data->ebooks = open_ebooks(); + + /* Add 0.vcf */ + if (e_book_get_self(&me, &eb, &gerr) == FALSE) { + g_error_free(gerr); + goto next; + } + + evcard = E_VCARD(me); + + cname = evcard_name_attribute_to_string(evcard); + if (!cname) + cname = g_strdup(""); + + attrib = e_vcard_get_attribute(evcard, EVC_UID); + uid = e_vcard_attribute_get_value(attrib); + if (!uid) + uid = g_strdup(""); + + attrib = e_vcard_get_attribute(evcard, EVC_TEL); + if (attrib) + tel = e_vcard_attribute_get_value(attrib); + else + tel = g_strdup(""); + + data->entry_cb(uid, 0, cname, NULL, tel, data->user_data); + + data->count++; + + g_free(cname); + g_free(uid); + g_free(tel); + g_object_unref(eb); + +next: + for (l = data->ebooks; l != NULL; l = g_slist_next(l)) { + EBook *ebook = l->data; + + if (e_book_is_opened(ebook) == FALSE) + continue; + + if (e_book_get_contacts_async(ebook, query, + cache_cb, data) == TRUE) + data->queued_calls++; + } + + if (err) + *err = (data->queued_calls == 0 ? -ENOENT : 0); + + return data; +} diff --git a/obexd/plugins/phonebook-tizen.c b/obexd/plugins/phonebook-tizen.c new file mode 100755 index 0000000..dcb6179 --- /dev/null +++ b/obexd/plugins/phonebook-tizen.c @@ -0,0 +1,813 @@ +/* + * + * OBEX Server + * + * Copyright (c) 2000-2016 Samsung Electronics Co., Ltd. All rights reserved. + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "log.h" +#include "phonebook.h" + +#include +#include + +#define PHONEBOOK_DESTINATION "org.bluez.pb_agent" +#define PHONEBOOK_PATH "/org/bluez/pb_agent" +#define PHONEBOOK_INTERFACE "org.bluez.PbAgent" + +#define QUERY_GET_PHONEBOOK_FOLDER_LIST "GetPhonebookFolderList" +#define QUERY_GET_PHONEBOOK_SIZE "GetPhonebookSize" +#define QUERY_GET_PHONEBOOK "GetPhonebook" +#define QUERY_GET_PHONEBOOK_LIST "GetPhonebookList" +#define QUERY_GET_PHONEBOOK_ENTRY "GetPhonebookEntry" +#define QUERY_DESTROY_AGENT "DestroyAgent" + +static GPtrArray *folder_list = NULL; + +struct phonebook_data { + phonebook_cb cb; + DBusPendingCallNotifyFunction reply_cb; + DBusPendingCall *call; + void *user_data; + const struct apparam_field *params; + + phonebook_entry_cb entry_cb; + phonebook_cache_ready_cb ready_cb; + + char *req_name; +}; + +struct phonebook_session { + DBusConnection *connection; + phonebook_cache_clear_cb clear_cb; + unsigned int clear_id; + + void *user_data; +}; + +static gboolean folder_is_valid(const gchar *folder, + gboolean leaf_only) +{ + int i; + int folder_len; + + if (folder_list == NULL || folder == NULL) + return FALSE; + + folder_len = strlen(folder); + + for (i = 0 ; i < folder_list->len; i++) { + char *str = (char *)g_ptr_array_index(folder_list, i); + + if (folder_len > strlen(str)) + continue; + + if (g_strcmp0(str, folder) == 0) + return TRUE; + + if (leaf_only == TRUE) + continue; + + if (strncmp(str, folder, folder_len) == 0) { + + if (*(folder + folder_len - 1) == '/') + return TRUE; /* folder is ended with '/' */ + + if (*(str + folder_len) == '/') + return TRUE; /* folder is matched */ + } + } + + return FALSE; +} + +static DBusPendingCall* phonebook_request(struct phonebook_data *data, + const gchar *method, + DBusPendingCallNotifyFunction function, + int first_arg_type, + ...) +{ + DBusConnection *connection; + DBusPendingCall *call; + DBusMessage *message; + + va_list args; + + DBG("%s\n", method); + + if (!method) { + error("Can't get method name"); + return NULL; + } + + connection = g_dbus_setup_bus(DBUS_BUS_SYSTEM, NULL, NULL); + if (!connection) { + error("Can't get on session bus"); + return NULL; + } + + message = dbus_message_new_method_call(PHONEBOOK_DESTINATION, + PHONEBOOK_PATH, + PHONEBOOK_INTERFACE, + method); + if (!message) { + dbus_connection_unref(connection); + error("Can't Allocate new message"); + return NULL; + } + + va_start(args, first_arg_type); + dbus_message_append_args_valist(message, first_arg_type, args); + va_end(args); + + if (dbus_connection_send_with_reply(connection, message, &call, -1) == FALSE) { + dbus_message_unref(message); + dbus_connection_unref(connection); + DBG("-"); + return NULL; + } + dbus_pending_call_set_notify(call, function, data, NULL); + + dbus_message_unref(message); + dbus_connection_unref(connection); + + DBG("-"); + return call; +} + +static void get_phonebook_size_reply(DBusPendingCall *call, void *user_data) +{ + DBusMessage *reply = dbus_pending_call_steal_reply(call); + struct phonebook_data *s_data = user_data; + DBusError derr; + uint32_t phonebook_size; + + unsigned int new_missed_call = 0; + + DBG(""); + dbus_pending_call_unref(s_data->call); + s_data->call = NULL; + + if (!reply) { + DBG("Reply Error\n"); + return; + } + + dbus_error_init(&derr); + if (dbus_set_error_from_message(&derr, reply)) { + error("Replied with an error: %s, %s", derr.name, derr.message); + dbus_error_free(&derr); + phonebook_size = 0; + } else { + dbus_message_get_args(reply, NULL, + DBUS_TYPE_UINT32, &phonebook_size, + DBUS_TYPE_UINT32, &new_missed_call, + DBUS_TYPE_INVALID); + DBG("phonebooksize:%d\n", phonebook_size); + } + + s_data->cb(NULL, 0, phonebook_size, new_missed_call, TRUE, s_data->user_data); + + dbus_message_unref(reply); +} + +static void get_phonebook_reply(DBusPendingCall *call, void *user_data) +{ + DBusMessage *reply = dbus_pending_call_steal_reply(call); + struct phonebook_data *s_data = user_data; + DBusError derr; + GString *buffer; + + unsigned int new_missed_call = 0; + + uint32_t count = 0; + + DBG(""); + dbus_pending_call_unref(s_data->call); + s_data->call = NULL; + + if (!reply) { + DBG("Reply Error\n"); + return; + } + + buffer = g_string_new(""); + + dbus_error_init(&derr); + if (dbus_set_error_from_message(&derr, reply)) { + error("Replied with an error: %s, %s", derr.name, derr.message); + dbus_error_free(&derr); + } else { + DBusMessageIter iter; + + dbus_message_iter_init(reply, &iter); + + if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_ARRAY) { + DBusMessageIter recurse_iter; + + dbus_message_iter_recurse(&iter, &recurse_iter); + while (dbus_message_iter_get_arg_type(&recurse_iter) == DBUS_TYPE_STRING) { + gchar *str; + + dbus_message_iter_get_basic(&recurse_iter, &str); + dbus_message_iter_next(&recurse_iter); + + g_string_append(buffer, str); + + DBG("str:\n%s\n", str); + + count++; + } + dbus_message_iter_next(&iter); + } + + if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_UINT32) + dbus_message_iter_get_basic(&iter, &new_missed_call); + + DBG("new_missed_call %d\n", new_missed_call); + } + + s_data->cb(buffer->str, buffer->len, count, new_missed_call, 1, s_data->user_data); + + g_string_free(buffer, TRUE); + dbus_message_unref(reply); +} + + +static void get_phonebook_list_reply(DBusPendingCall *call, void *user_data) +{ + DBusMessage *reply = dbus_pending_call_steal_reply(call); + DBusMessageIter iter, iter_struct, entry; + struct phonebook_data *data = user_data; + DBusError derr; + uint32_t handle = 0; + const char *name = NULL; + const char *tel = NULL; + char id[10]; + unsigned int new_missed_call = 0; + + dbus_pending_call_unref(data->call); + data->call = NULL; + if (!reply) { + DBG("Reply Error\n"); + return; + } + + dbus_error_init(&derr); + if (dbus_set_error_from_message(&derr, reply)) { + error("Replied with an error: %s, %s", derr.name, derr.message); + dbus_error_free(&derr); + } else { + + dbus_message_iter_init(reply, &iter); + + dbus_message_iter_recurse(&iter, &iter_struct); + + while (dbus_message_iter_get_arg_type(&iter_struct) == + DBUS_TYPE_STRUCT) { + dbus_message_iter_recurse(&iter_struct, &entry); + + dbus_message_iter_get_basic(&entry, &name); + dbus_message_iter_next(&entry); + dbus_message_iter_get_basic(&entry, &tel); + dbus_message_iter_next(&entry); + dbus_message_iter_get_basic(&entry, &handle); + + /* + DBG("[handle:%d name:%s tel:%s]\n", handle, name, tel); + */ + + snprintf(id, sizeof(id), "%d.vcf", handle); + + data->entry_cb(id, handle, name, NULL, tel, + data->user_data); + + dbus_message_iter_next(&iter_struct); + } + + dbus_message_iter_next(&iter); + if (dbus_message_iter_get_arg_type(&iter) == DBUS_TYPE_UINT32) { + dbus_message_iter_get_basic(&iter, &new_missed_call); + } + + DBG("new_missed_call %d\n", new_missed_call); + } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + data->ready_cb(data->user_data, new_missed_call); +#else + data->ready_cb(data->user_data); +#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */ + + dbus_message_unref(reply); +} + +static void get_phonebook_entry_reply(DBusPendingCall *call, void *user_data) +{ + DBusMessage *reply = dbus_pending_call_steal_reply(call); + struct phonebook_data *s_data = user_data; + DBusError derr; + const char *phonebook_entry; + + DBG(""); + dbus_pending_call_unref(s_data->call); + s_data->call = NULL; + + if (!reply) { + DBG("Reply Error\n"); + return; + } + + dbus_error_init(&derr); + if (dbus_set_error_from_message(&derr, reply)) { + error("Replied with an error: %s, %s", derr.name, derr.message); + dbus_error_free(&derr); + } else { + dbus_message_get_args(reply, NULL, DBUS_TYPE_STRING, + &phonebook_entry, DBUS_TYPE_INVALID); + DBG("phonebook_entry:[%s]\n", phonebook_entry); + } + + s_data->cb(phonebook_entry, strlen(phonebook_entry), 1, 0, TRUE, + s_data->user_data); + + dbus_message_unref(reply); +} + +static gboolean clear_signal(DBusConnection *conn, DBusMessage *msg, + void *user_data) +{ + struct phonebook_session *session; + + if (user_data == NULL) + return FALSE; + + DBG(""); + session = user_data; + + session->clear_cb(session->user_data); + + g_dbus_remove_watch(session->connection, session->clear_id); + session->clear_id = 0; + + return TRUE; +} + +static gboolean phonebook_folder_list_init(struct phonebook_session *session) +{ + DBusMessage *message; + DBusMessage *reply; + + DBusMessageIter iter; + DBusMessageIter recurse_iter; + + DBusError error; + + if (session->connection == NULL) { + session->connection = g_dbus_setup_bus(DBUS_BUS_SYSTEM, + NULL, NULL); + } + + if (session->connection == NULL) { + DBG("Can't get on s bus"); + return FALSE; + } + + message = dbus_message_new_method_call(PHONEBOOK_DESTINATION, + PHONEBOOK_PATH, + PHONEBOOK_INTERFACE, + QUERY_GET_PHONEBOOK_FOLDER_LIST); + + if (message == NULL) { + DBG("Can't allocate dbus message"); + return FALSE; + } + + dbus_error_init(&error); + + reply = dbus_connection_send_with_reply_and_block(session->connection, + message, -1, &error); + + if (!reply) { + if (dbus_error_is_set(&error) == TRUE) { + DBG("%s", error.message); + dbus_error_free(&error); + } else { + DBG("Failed to get contacts"); + } + dbus_message_unref(message); + return FALSE; + } + + dbus_message_iter_init(reply, &iter); + + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY) { + dbus_message_unref(message); + dbus_message_unref(reply); + return FALSE; + } + + if (folder_list) { + g_ptr_array_free(folder_list, TRUE); + folder_list = NULL; + } + + folder_list = g_ptr_array_new(); + + dbus_message_iter_recurse(&iter, &recurse_iter); + + while (dbus_message_iter_get_arg_type(&recurse_iter) + == DBUS_TYPE_STRING) { + gchar *str; + + dbus_message_iter_get_basic(&recurse_iter, &str); + DBG("folder list %s\n", str); + g_ptr_array_add(folder_list, g_strdup(str)); + + dbus_message_iter_next(&recurse_iter); + } + + dbus_message_unref(message); + dbus_message_unref(reply); + return TRUE; +} + +int phonebook_init(void) +{ + return 0; +} + +void phonebook_exit(void) +{ +} + +int phonebook_connect(void **user_data) +{ + struct phonebook_session *session; + gboolean ret; + + DBG(""); + + session = g_new0(struct phonebook_session, 1); + + *user_data = session; + + ret = phonebook_folder_list_init(session); + + if (ret) + return 0; + + return -1; +} + +void phonebook_disconnect(void *user_data) +{ + struct phonebook_session *session; + DBusMessage *message; + DBG(""); + session = user_data; + + if (folder_list) { + g_ptr_array_free(folder_list, TRUE); + folder_list = NULL; + } + + if (!session->connection) { + g_free(session); + return; + } + + + if (session->clear_id) + g_dbus_remove_watch(session->connection, session->clear_id); + + message = dbus_message_new_method_call(PHONEBOOK_DESTINATION, + PHONEBOOK_PATH, + PHONEBOOK_INTERFACE, + QUERY_DESTROY_AGENT); + + if (message) { + if (dbus_connection_send(session->connection, message, + NULL) == FALSE) + error("Could not send dbus message"); + + dbus_message_unref(message); + } + + dbus_connection_unref(session->connection); + g_free(session); +} + +char *phonebook_set_folder(const char *current_folder, + const char *new_folder, uint8_t flags, int *err) +{ + char *tmp1, *tmp2, *base, *path = NULL; + gboolean root, child; + int ret = 0; + int len; + + root = (g_strcmp0("/", current_folder) == 0); + child = (new_folder && strlen(new_folder) != 0); + + switch (flags) { + case 0x02: + /* Go back to root */ + if (!child) { + path = g_strdup("/"); + goto done; + } + + path = g_build_filename(current_folder, new_folder, NULL); + break; + case 0x03: + /* Go up 1 level */ + if (root) { + /* Already root */ + path = g_strdup("/"); + goto done; + } + + /* + * Removing one level of the current folder. Current folder + * contains AT LEAST one level since it is not at root folder. + * Use glib utility functions to handle invalid chars in the + * folder path properly. + */ + tmp1 = g_path_get_basename(current_folder); + tmp2 = g_strrstr(current_folder, tmp1); + len = tmp2 - (current_folder + 1); + + g_free(tmp1); + + if (len == 0) + base = g_strdup("/"); + else + base = g_strndup(current_folder, len); + + /* Return: one level only */ + if (!child) { + path = base; + goto done; + } + + path = g_build_filename(base, new_folder, NULL); + g_free(base); + + break; + default: + ret = -EBADR; + break; + } + +done: + if (path && !folder_is_valid(path, FALSE)) + ret = -ENOENT; + + if (ret < 0) { + g_free(path); + path = NULL; + } + + if (err) + *err = ret; + + DBG("path : %s\n", path); + + return path; +} + +void phonebook_req_finalize(void *request) +{ + struct phonebook_data *data = request; + + DBG(""); + + if (!data) + return; + + if (data->call) { + dbus_pending_call_cancel(data->call); + dbus_pending_call_unref(data->call); + } + g_free(data->req_name); + g_free(data); +} + +void *phonebook_pull(const char *name, const struct apparam_field *params, + phonebook_cb cb, void *user_data, int *err) +{ + struct phonebook_data *data; + char *folder; + + DBG("name %s", name); + + if (!g_str_has_suffix(name, ".vcf")) { + if (err) + *err = -ENOENT; + + return NULL; + } + + folder = g_strndup(name, strlen(name) - 4); + + if (!folder_is_valid(folder, TRUE)) { + if (err) + *err = -ENOENT; + + g_free(folder); + return NULL; + } + + g_free(folder); + + data = g_new0(struct phonebook_data, 1); + data->params = params; + data->user_data = user_data; + data->cb = cb; + data->req_name = g_strdup(name); + + if (err) + *err = 0; + + return data; +} + +int phonebook_pull_read(void *request) +{ + struct phonebook_data *data = request; + + DBG("name %s", data->req_name); + + if (data->params->maxlistcount == 0) { + data->call = phonebook_request(data, + QUERY_GET_PHONEBOOK_SIZE, + get_phonebook_size_reply, + DBUS_TYPE_STRING, &data->req_name, + DBUS_TYPE_INVALID); + return 0; + } + + data->call = phonebook_request(data, + QUERY_GET_PHONEBOOK, + get_phonebook_reply, + DBUS_TYPE_STRING, &data->req_name, + DBUS_TYPE_UINT64, &data->params->filter, + DBUS_TYPE_BYTE, &data->params->format, + DBUS_TYPE_UINT16, &data->params->maxlistcount, + DBUS_TYPE_UINT16, &data->params->liststartoffset, + DBUS_TYPE_INVALID); + + return 0; +} + +void *phonebook_get_entry(const char *folder, const char *id, + const struct apparam_field *params, phonebook_cb cb, + void *user_data, int *err) +{ + struct phonebook_data *data; + + DBG(""); + if (!g_str_has_suffix(id, ".vcf")) { + if (err) + *err = -ENOENT; + + return NULL; + } + + if (!folder_is_valid(folder, TRUE)) { + if (err) + *err = - ENOENT; + + return NULL; + } + + DBG("folder %s id %s", folder, id); + + data = g_new0(struct phonebook_data, 1); + data->params = params; + data->user_data = user_data; + data->cb = cb; + + data->call = phonebook_request(data, + QUERY_GET_PHONEBOOK_ENTRY, + get_phonebook_entry_reply, + DBUS_TYPE_STRING, &folder, + DBUS_TYPE_STRING, &id, + DBUS_TYPE_UINT64, &data->params->filter, + DBUS_TYPE_BYTE, &data->params->format, + DBUS_TYPE_INVALID); + + if (*err) { + if (data->call) + *err = 0; + else { + *err = -ENOENT; + g_free(data); + data = NULL; + } + } + + return data; +} + +void *phonebook_create_cache(const char *name, phonebook_entry_cb entry_cb, + phonebook_cache_ready_cb ready_cb, void *user_data, int *err) +{ + struct phonebook_data *data; + + if (!folder_is_valid(name, TRUE)) { + if (err) + *err = - ENOENT; + + return NULL; + } + + DBG("name %s", name); + + data = g_new0(struct phonebook_data, 1); + data->user_data = user_data; + data->entry_cb = entry_cb; + data->ready_cb = ready_cb; + + data->call = phonebook_request(data, + QUERY_GET_PHONEBOOK_LIST, + get_phonebook_list_reply, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_INVALID); + + if (*err) { + if (data->call) + *err = 0; + else { + *err = -ENOENT; + g_free(data); + data = NULL; + } + } + + return data; +} + +void phonebook_set_cache_notification(void *session, + phonebook_cache_clear_cb clear_cb, + void *user_data) +{ + struct phonebook_session *s = session; + + DBG(""); + s->clear_cb = clear_cb; + + if (s->connection == NULL) { + s->connection = g_dbus_setup_bus(DBUS_BUS_SYSTEM, + NULL, NULL); + + if (s->connection == NULL) { + error("Can't get on s bus"); + return; + } + } + + s->user_data = user_data; + + if (s->clear_id) { + g_dbus_remove_watch(s->connection, s->clear_id); + s->clear_id = 0; + } + + s->clear_id = g_dbus_add_signal_watch(s->connection, + NULL, PHONEBOOK_PATH, PHONEBOOK_INTERFACE, + "clear", clear_signal, + s, NULL); +} diff --git a/obexd/plugins/phonebook-tracker.c b/obexd/plugins/phonebook-tracker.c new file mode 100755 index 0000000..0743629 --- /dev/null +++ b/obexd/plugins/phonebook-tracker.c @@ -0,0 +1,1718 @@ +/* + * Phonebook access through D-Bus vCard and call history service + * + * Copyright (C) 2010 Nokia Corporation + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "obexd/src/log.h" +#include "obexd/src/obex.h" +#include "obexd/src/service.h" +#include "obexd/src/mimetype.h" +#include "phonebook.h" +#include "vcard.h" + +#define TRACKER_SERVICE "org.freedesktop.Tracker1" +#define TRACKER_RESOURCES_PATH "/org/freedesktop/Tracker1/Resources" +#define TRACKER_RESOURCES_INTERFACE "org.freedesktop.Tracker1.Resources" + +#define TRACKER_DEFAULT_CONTACT_ME "http://www.semanticdesktop.org/ontologies/2007/03/22/nco#default-contact-me" +#define AFFILATION_HOME "Home" +#define AFFILATION_WORK "Work" +#define ADDR_FIELD_AMOUNT 7 +#define PULL_QUERY_COL_AMOUNT 23 +#define COUNT_QUERY_COL_AMOUNT 1 + +#define COL_PHONE_AFF 0 /* work/home phone numbers */ +#define COL_FULL_NAME 1 +#define COL_FAMILY_NAME 2 +#define COL_GIVEN_NAME 3 +#define COL_ADDITIONAL_NAME 4 +#define COL_NAME_PREFIX 5 +#define COL_NAME_SUFFIX 6 +#define COL_ADDR_AFF 7 /* addresses from affilation */ +#define COL_BIRTH_DATE 8 +#define COL_NICKNAME 9 +#define COL_URL 10 +#define COL_PHOTO 11 +#define COL_ORG_ROLE 12 +#define COL_UID 13 +#define COL_TITLE 14 +#define COL_AFF_TYPE 15 +#define COL_ORG_NAME 16 +#define COL_ORG_DEPARTMENT 17 +#define COL_EMAIL_AFF 18 /* email's from affilation (work/home) */ +#define COL_DATE 19 +#define COL_SENT 20 +#define COL_ANSWERED 21 +#define CONTACTS_ID_COL 22 +#define CONTACT_ID_PREFIX "urn:uuid:" +#define CALL_ID_PREFIX "message:" + +#define FAX_NUM_TYPE "http://www.semanticdesktop.org/ontologies/2007/03/22/nco#FaxNumber" +#define MOBILE_NUM_TYPE "http://www.semanticdesktop.org/ontologies/2007/03/22/nco#CellPhoneNumber" + +#define MAIN_DELIM "\30" /* Main delimiter between phones, addresses, emails*/ +#define SUB_DELIM "\31" /* Delimiter used in telephone number strings*/ +#define ADDR_DELIM "\37" /* Delimiter used for address data fields */ +#define MAX_FIELDS 100 /* Max amount of fields to be concatenated at once*/ +#define VCARDS_PART_COUNT 50 /* amount of vcards sent at once to PBAP core */ +#define QUERY_OFFSET_FORMAT "%s OFFSET %d" + +#define CONTACTS_QUERY_ALL \ +"SELECT " \ +"(SELECT GROUP_CONCAT(fn:concat(rdf:type(?aff_number)," \ +"\"\31\", nco:phoneNumber(?aff_number)), \"\30\")" \ +"WHERE {" \ +" ?_role nco:hasPhoneNumber ?aff_number" \ +"}) " \ +"nco:fullname(?_contact) " \ +"nco:nameFamily(?_contact) " \ +"nco:nameGiven(?_contact) " \ +"nco:nameAdditional(?_contact) " \ +"nco:nameHonorificPrefix(?_contact) " \ +"nco:nameHonorificSuffix(?_contact) " \ +"(SELECT GROUP_CONCAT(fn:concat(" \ +"tracker:coalesce(nco:pobox(?aff_addr), \"\"), \"\37\"," \ +"tracker:coalesce(nco:extendedAddress(?aff_addr), \"\"), \"\37\"," \ +"tracker:coalesce(nco:streetAddress(?aff_addr), \"\"), \"\37\"," \ +"tracker:coalesce(nco:locality(?aff_addr), \"\"), \"\37\"," \ +"tracker:coalesce(nco:region(?aff_addr), \"\"), \"\37\"," \ +"tracker:coalesce(nco:postalcode(?aff_addr), \"\"), \"\37\"," \ +"tracker:coalesce(nco:country(?aff_addr), \"\"), " \ +"\"\31\", rdfs:label(?_role) ), " \ +"\"\30\") " \ +"WHERE {" \ +"?_role nco:hasPostalAddress ?aff_addr" \ +"}) " \ +"nco:birthDate(?_contact) " \ +"(SELECT " \ +" ?nick " \ +" WHERE { " \ +" { " \ +" ?_contact nco:nickname ?nick " \ +" } UNION { " \ +" ?_contact nco:hasAffiliation ?role . " \ +" ?role nco:hasIMAddress ?im . " \ +" ?im nco:imNickname ?nick " \ +" } " \ +" } " \ +") " \ +"(SELECT GROUP_CONCAT(fn:concat( " \ + "?url_val, \"\31\", tracker:coalesce(rdfs:label(?_role), \"\") "\ + "), \"\30\") " \ + "WHERE {" \ + "?_role nco:url ?url_val . " \ +"})" \ +"nie:url(nco:photo(?_contact)) " \ +"nco:role(?_role) " \ +"nco:contactUID(?_contact) " \ +"nco:title(?_role) " \ +"rdfs:label(?_role) " \ +"nco:fullname(nco:org(?_role))" \ +"nco:department(?_role) " \ +"(SELECT GROUP_CONCAT(fn:concat(?emailaddress,\"\31\"," \ + "tracker:coalesce(rdfs:label(?_role), \"\"))," \ + "\"\30\") " \ + "WHERE { " \ + "?_role nco:hasEmailAddress " \ + " [ nco:emailAddress ?emailaddress ] " \ + "}) " \ +"\"NOTACALL\" \"false\" \"false\" " \ +"?_contact " \ +"WHERE {" \ +" ?_contact a nco:PersonContact ." \ +" OPTIONAL {?_contact nco:hasAffiliation ?_role .}" \ +"}" \ +"ORDER BY tracker:id(?_contact)" + +#define CONTACTS_QUERY_ALL_LIST \ + "SELECT ?c nco:nameFamily(?c) " \ + "nco:nameGiven(?c) nco:nameAdditional(?c) " \ + "nco:nameHonorificPrefix(?c) nco:nameHonorificSuffix(?c) " \ + "(SELECT " \ + "?nick " \ + "WHERE { " \ + "{ " \ + "?c nco:nickname ?nick " \ + "} UNION { " \ + "?c nco:hasAffiliation ?role . " \ + "?role nco:hasIMAddress ?im . " \ + "?im nco:imNickname ?nick " \ + "} " \ + "} " \ + ") " \ + "nco:phoneNumber(?h) " \ + "WHERE { " \ + "?c a nco:PersonContact . " \ + "OPTIONAL { ?c nco:hasPhoneNumber ?h . } " \ + "OPTIONAL { " \ + "?c nco:hasAffiliation ?a . " \ + "?a nco:hasPhoneNumber ?h . " \ + "} " \ + "} GROUP BY ?c" + +#define CALLS_CONSTRAINTS(CONSTRAINT) \ +" WHERE { " \ + "?_call a nmo:Call . " \ + "?_unb_contact a nco:Contact . " \ + "?_unb_contact nco:hasPhoneNumber ?_cpn . " \ +CONSTRAINT \ + "OPTIONAL { " \ + "{ SELECT ?_contact ?_no ?_role ?_number " \ + "count(?_contact) as ?cnt " \ + "WHERE { " \ + "?_contact a nco:PersonContact . " \ + "{ " \ + "?_contact nco:hasAffiliation ?_role . "\ + "?_role nco:hasPhoneNumber ?_number . " \ + "} UNION { " \ + "?_contact nco:hasPhoneNumber ?_number" \ + "} " \ + "?_number maemo:localPhoneNumber ?_no . " \ + "} GROUP BY ?_no } " \ + "FILTER(?cnt = 1) " \ + "?_cpn maemo:localPhoneNumber ?_no . " \ + "} " \ +"} " + +#define CALLS_LIST(CONSTRAINT) \ +"SELECT ?_call nco:nameFamily(?_contact) " \ + "nco:nameGiven(?_contact) nco:nameAdditional(?_contact) " \ + "nco:nameHonorificPrefix(?_contact) " \ + "nco:nameHonorificSuffix(?_contact) " \ + "(SELECT " \ + "?nick " \ + "WHERE { " \ + "{ " \ + "?_contact nco:nickname ?nick " \ + "} UNION { " \ + "?_contact nco:hasAffiliation ?role . " \ + "?role nco:hasIMAddress ?im . " \ + "?im nco:imNickname ?nick " \ + "} " \ + "} " \ + ") " \ + "nco:phoneNumber(?_cpn) " \ +CALLS_CONSTRAINTS(CONSTRAINT) \ +"ORDER BY DESC(nmo:sentDate(?_call)) " + +#define CALLS_QUERY(CONSTRAINT) \ +"SELECT " \ +"(SELECT fn:concat(rdf:type(?role_number)," \ + "\"\31\", nco:phoneNumber(?role_number))" \ + "WHERE {" \ + "{" \ + " ?_role nco:hasPhoneNumber ?role_number " \ + " FILTER (?role_number = ?_number)" \ + "} UNION { " \ + "?_unb_contact nco:hasPhoneNumber ?role_number . " \ + " FILTER (!bound(?_role)) " \ + "}" \ +"} GROUP BY nco:phoneNumber(?role_number) ) " \ + "nco:fullname(?_contact) " \ + "nco:nameFamily(?_contact) " \ + "nco:nameGiven(?_contact) " \ + "nco:nameAdditional(?_contact) " \ + "nco:nameHonorificPrefix(?_contact) " \ + "nco:nameHonorificSuffix(?_contact) " \ +"(SELECT GROUP_CONCAT(fn:concat(" \ + "tracker:coalesce(nco:pobox(?aff_addr), \"\"), \"\37\"," \ + "tracker:coalesce(nco:extendedAddress(?aff_addr), \"\"), \"\37\","\ + "tracker:coalesce(nco:streetAddress(?aff_addr), \"\"), \"\37\","\ + "tracker:coalesce(nco:locality(?aff_addr), \"\"), \"\37\"," \ + "tracker:coalesce(nco:region(?aff_addr), \"\"), \"\37\"," \ + "tracker:coalesce(nco:postalcode(?aff_addr), \"\"), \"\37\"," \ + "tracker:coalesce(nco:country(?aff_addr), \"\"), " \ + "\"\31\", rdfs:label(?c_role) ), " \ + "\"\30\") " \ + "WHERE {" \ + "?_contact nco:hasAffiliation ?c_role . " \ + "?c_role nco:hasPostalAddress ?aff_addr" \ + "}) " \ + "nco:birthDate(?_contact) " \ +"(SELECT " \ + "?nick " \ + "WHERE { " \ + " { " \ + " ?_contact nco:nickname ?nick " \ + " } UNION { " \ + " ?_contact nco:hasAffiliation ?role . " \ + " ?role nco:hasIMAddress ?im . " \ + " ?im nco:imNickname ?nick " \ + " } " \ + " } " \ + ") " \ +"(SELECT GROUP_CONCAT(fn:concat(?url_value, \"\31\", " \ + "tracker:coalesce(rdfs:label(?c_role), \"\")), \"\30\") " \ + "WHERE {" \ + "?_contact nco:hasAffiliation ?c_role . " \ + "?c_role nco:url ?url_value . " \ +"})" \ + "nie:url(nco:photo(?_contact)) " \ + "nco:role(?_role) " \ + "nco:contactUID(?_contact) " \ + "nco:title(?_role) " \ + "rdfs:label(?_role) " \ + "nco:fullname(nco:org(?_role)) " \ + "nco:department(?_role) " \ +"(SELECT GROUP_CONCAT(fn:concat(?emailaddress,\"\31\"," \ + "tracker:coalesce(rdfs:label(?c_role), \"\"))," \ + "\"\30\") " \ + "WHERE { " \ + "?_contact nco:hasAffiliation ?c_role . " \ + "?c_role nco:hasEmailAddress " \ + " [ nco:emailAddress ?emailaddress ] " \ + "}) " \ + "nmo:receivedDate(?_call) " \ + "nmo:isSent(?_call) " \ + "nmo:isAnswered(?_call) " \ + "?_call " \ +CALLS_CONSTRAINTS(CONSTRAINT) \ +"ORDER BY DESC(nmo:sentDate(?_call)) " + +#define MISSED_CONSTRAINT \ +"?_call nmo:from ?_unb_contact . " \ +"?_call nmo:isSent false . " \ +"?_call nmo:isAnswered false . " + +#define INCOMING_CONSTRAINT \ +"?_call nmo:from ?_unb_contact . " \ +"?_call nmo:isSent false . " \ +"?_call nmo:isAnswered true . " + +#define OUTGOING_CONSTRAINT \ +"?_call nmo:to ?_unb_contact . " \ +"?_call nmo:isSent true . " + +#define COMBINED_CONSTRAINT \ +"{ " \ +" ?_call nmo:from ?_unb_contact . " \ +" ?_call nmo:isSent false " \ +"} UNION { " \ +" ?_call nmo:to ?_unb_contact . " \ +" ?_call nmo:isSent true " \ +"} " + +#define CALL_URI_CONSTRAINT \ +COMBINED_CONSTRAINT \ +"FILTER (?_call = <%s>) " + +#define MISSED_CALLS_QUERY CALLS_QUERY(MISSED_CONSTRAINT) +#define MISSED_CALLS_LIST CALLS_LIST(MISSED_CONSTRAINT) +#define INCOMING_CALLS_QUERY CALLS_QUERY(INCOMING_CONSTRAINT) +#define INCOMING_CALLS_LIST CALLS_LIST(INCOMING_CONSTRAINT) +#define OUTGOING_CALLS_QUERY CALLS_QUERY(OUTGOING_CONSTRAINT) +#define OUTGOING_CALLS_LIST CALLS_LIST(OUTGOING_CONSTRAINT) +#define COMBINED_CALLS_QUERY CALLS_QUERY(COMBINED_CONSTRAINT) +#define COMBINED_CALLS_LIST CALLS_LIST(COMBINED_CONSTRAINT) +#define CONTACT_FROM_CALL_QUERY CALLS_QUERY(CALL_URI_CONSTRAINT) + +#define CONTACTS_QUERY_FROM_URI \ +"SELECT " \ +"(SELECT GROUP_CONCAT(fn:concat(rdf:type(?aff_number)," \ +"\"\31\", nco:phoneNumber(?aff_number)), \"\30\")" \ +"WHERE {" \ +" ?_role nco:hasPhoneNumber ?aff_number" \ +"}) " \ +"nco:fullname(<%s>) " \ +"nco:nameFamily(<%s>) " \ +"nco:nameGiven(<%s>) " \ +"nco:nameAdditional(<%s>) " \ +"nco:nameHonorificPrefix(<%s>) " \ +"nco:nameHonorificSuffix(<%s>) " \ +"(SELECT GROUP_CONCAT(fn:concat(" \ +"tracker:coalesce(nco:pobox(?aff_addr), \"\"), \"\37\"," \ +"tracker:coalesce(nco:extendedAddress(?aff_addr), \"\"), \"\37\"," \ +"tracker:coalesce(nco:streetAddress(?aff_addr), \"\"), \"\37\"," \ +"tracker:coalesce(nco:locality(?aff_addr), \"\"), \"\37\"," \ +"tracker:coalesce(nco:region(?aff_addr), \"\"), \"\37\"," \ +"tracker:coalesce(nco:postalcode(?aff_addr), \"\"), \"\37\"," \ +"tracker:coalesce(nco:country(?aff_addr), \"\"), " \ +"\"\31\", rdfs:label(?_role) ), " \ +"\"\30\") " \ +"WHERE {" \ +"?_role nco:hasPostalAddress ?aff_addr" \ +"}) " \ +"nco:birthDate(<%s>) " \ +"(SELECT " \ +" ?nick " \ +" WHERE { " \ +" { " \ +" ?_contact nco:nickname ?nick " \ +" } UNION { " \ +" ?_contact nco:hasAffiliation ?role . " \ +" ?role nco:hasIMAddress ?im . " \ +" ?im nco:imNickname ?nick " \ +" } " \ +" FILTER (?_contact = <%s>)" \ +" } " \ +") " \ +"(SELECT GROUP_CONCAT(fn:concat( " \ + "?url_val, \"\31\", tracker:coalesce(rdfs:label(?_role), \"\") "\ + "), \"\30\") " \ + "WHERE {" \ + "?_role nco:url ?url_val . " \ +"})" \ +"nie:url(nco:photo(<%s>)) " \ +"nco:role(?_role) " \ +"nco:contactUID(<%s>) " \ +"nco:title(?_role) " \ +"rdfs:label(?_role) " \ +"nco:fullname(nco:org(?_role))" \ +"nco:department(?_role) " \ +"(SELECT GROUP_CONCAT(fn:concat(?emailaddress,\"\31\"," \ + "tracker:coalesce(rdfs:label(?_role), \"\"))," \ + "\"\30\") " \ + "WHERE { " \ + "?_role nco:hasEmailAddress " \ + " [ nco:emailAddress ?emailaddress ] " \ + "}) " \ +"\"NOTACALL\" \"false\" \"false\" " \ +"<%s> " \ +"WHERE {" \ +" <%s> a nco:PersonContact ." \ +" OPTIONAL {<%s> nco:hasAffiliation ?_role .}" \ +"}" + +#define CONTACTS_OTHER_QUERY_FROM_URI \ + "SELECT fn:concat(\"TYPE_OTHER\", \"\31\", nco:phoneNumber(?t))"\ + "\"\" \"\" \"\" \"\" \"\" \"\" \"\" \"\" " \ + "\"\" \"\" \"\" \"\" \"\" \"\" \"\" \"\" \"\" \"\" " \ + " \"NOTACALL\" \"false\" \"false\" <%s> " \ + "WHERE { " \ + "<%s> a nco:Contact . " \ + "OPTIONAL { <%s> nco:hasPhoneNumber ?t . } " \ + "} " + +#define CONTACTS_COUNT_QUERY \ + "SELECT COUNT(?c) " \ + "WHERE {" \ + "?c a nco:PersonContact ." \ + "}" + +#define MISSED_CALLS_COUNT_QUERY \ + "SELECT COUNT(?call) WHERE {" \ + "?c a nco:Contact ;" \ + "nco:hasPhoneNumber ?h ." \ + "?call a nmo:Call ;" \ + "nmo:isSent false ;" \ + "nmo:from ?c ;" \ + "nmo:isAnswered false ." \ + "}" + +#define INCOMING_CALLS_COUNT_QUERY \ + "SELECT COUNT(?call) WHERE {" \ + "?c a nco:Contact ;" \ + "nco:hasPhoneNumber ?h ." \ + "?call a nmo:Call ;" \ + "nmo:isSent false ;" \ + "nmo:from ?c ;" \ + "nmo:isAnswered true ." \ + "}" + +#define OUTGOING_CALLS_COUNT_QUERY \ + "SELECT COUNT(?call) WHERE {" \ + "?c a nco:Contact ;" \ + "nco:hasPhoneNumber ?h ." \ + "?call a nmo:Call ;" \ + "nmo:isSent true ;" \ + "nmo:to ?c ." \ + "}" + +#define COMBINED_CALLS_COUNT_QUERY \ + "SELECT COUNT(?call) WHERE {" \ + "{" \ + "?c a nco:Contact ;" \ + "nco:hasPhoneNumber ?h ." \ + "?call a nmo:Call ;" \ + "nmo:isSent true ;" \ + "nmo:to ?c ." \ + "}UNION {" \ + "?c a nco:Contact ;" \ + "nco:hasPhoneNumber ?h ." \ + "?call a nmo:Call ;" \ + "nmo:from ?c ." \ + "}" \ + "}" + +#define NEW_MISSED_CALLS_COUNT_QUERY \ + "SELECT COUNT(?call) WHERE {" \ + "?c a nco:Contact ;" \ + "nco:hasPhoneNumber ?h ." \ + "?call a nmo:Call ;" \ + "nmo:isSent false ;" \ + "nmo:from ?c ;" \ + "nmo:isAnswered false ;" \ + "nmo:isRead false ." \ + "}" + +typedef int (*reply_list_foreach_t) (const char **reply, int num_fields, + void *user_data); + +typedef void (*add_field_t) (struct phonebook_contact *contact, + const char *value, int type); + +struct pending_reply { + reply_list_foreach_t callback; + void *user_data; + int num_fields; +}; + +struct contact_data { + char *id; + struct phonebook_contact *contact; +}; + +struct phonebook_data { + phonebook_cb cb; + void *user_data; + int index; + gboolean vcardentry; + const struct apparam_field *params; + GSList *contacts; + phonebook_cache_ready_cb ready_cb; + phonebook_entry_cb entry_cb; + int newmissedcalls; + GCancellable *query_canc; + char *req_name; + int vcard_part_count; + int tracker_index; +}; + +struct phonebook_index { + GArray *phonebook; + int index; +}; + +static TrackerSparqlConnection *connection = NULL; + +static const char *name2query(const char *name) +{ + if (g_str_equal(name, PB_CONTACTS)) + return CONTACTS_QUERY_ALL; + else if (g_str_equal(name, PB_CALLS_INCOMING)) + return INCOMING_CALLS_QUERY; + else if (g_str_equal(name, PB_CALLS_OUTGOING)) + return OUTGOING_CALLS_QUERY; + else if (g_str_equal(name, PB_CALLS_MISSED)) + return MISSED_CALLS_QUERY; + else if (g_str_equal(name, PB_CALLS_COMBINED)) + return COMBINED_CALLS_QUERY; + + return NULL; +} + +static const char *name2count_query(const char *name) +{ + if (g_str_equal(name, PB_CONTACTS)) + return CONTACTS_COUNT_QUERY; + else if (g_str_equal(name, PB_CALLS_INCOMING)) + return INCOMING_CALLS_COUNT_QUERY; + else if (g_str_equal(name, PB_CALLS_OUTGOING)) + return OUTGOING_CALLS_COUNT_QUERY; + else if (g_str_equal(name, PB_CALLS_MISSED)) + return MISSED_CALLS_COUNT_QUERY; + else if (g_str_equal(name, PB_CALLS_COMBINED)) + return COMBINED_CALLS_COUNT_QUERY; + + return NULL; +} + +static gboolean folder_is_valid(const char *folder) +{ + if (folder == NULL) + return FALSE; + + if (g_str_equal(folder, "/")) + return TRUE; + else if (g_str_equal(folder, PB_TELECOM_FOLDER)) + return TRUE; + else if (g_str_equal(folder, PB_CONTACTS_FOLDER)) + return TRUE; + else if (g_str_equal(folder, PB_CALLS_INCOMING_FOLDER)) + return TRUE; + else if (g_str_equal(folder, PB_CALLS_OUTGOING_FOLDER)) + return TRUE; + else if (g_str_equal(folder, PB_CALLS_MISSED_FOLDER)) + return TRUE; + else if (g_str_equal(folder, PB_CALLS_COMBINED_FOLDER)) + return TRUE; + + return FALSE; +} + +static const char *folder2query(const char *folder) +{ + if (g_str_equal(folder, PB_CONTACTS_FOLDER)) + return CONTACTS_QUERY_ALL_LIST; + else if (g_str_equal(folder, PB_CALLS_INCOMING_FOLDER)) + return INCOMING_CALLS_LIST; + else if (g_str_equal(folder, PB_CALLS_OUTGOING_FOLDER)) + return OUTGOING_CALLS_LIST; + else if (g_str_equal(folder, PB_CALLS_MISSED_FOLDER)) + return MISSED_CALLS_LIST; + else if (g_str_equal(folder, PB_CALLS_COMBINED_FOLDER)) + return COMBINED_CALLS_LIST; + + return NULL; +} + +static const char **string_array_from_cursor(TrackerSparqlCursor *cursor, + int array_len) +{ + const char **result; + int i; + + result = g_new0(const char *, array_len); + + for (i = 0; i < array_len; ++i) { + TrackerSparqlValueType type; + + type = tracker_sparql_cursor_get_value_type(cursor, i); + + if (type == TRACKER_SPARQL_VALUE_TYPE_BLANK_NODE || + type == TRACKER_SPARQL_VALUE_TYPE_UNBOUND) + /* For null/unbound type filling result part with ""*/ + result[i] = ""; + else + /* Filling with string representation of content*/ + result[i] = tracker_sparql_cursor_get_string(cursor, i, + NULL); + } + + return result; +} + +static void update_cancellable(struct phonebook_data *pdata, + GCancellable *canc) +{ + if (pdata->query_canc) + g_object_unref(pdata->query_canc); + + pdata->query_canc = canc; +} + +static void async_query_cursor_next_cb(GObject *source, GAsyncResult *result, + gpointer user_data) +{ + struct pending_reply *pending = user_data; + TrackerSparqlCursor *cursor = TRACKER_SPARQL_CURSOR(source); + GCancellable *cancellable; + GError *error = NULL; + gboolean success; + const char **node; + int err; + + success = tracker_sparql_cursor_next_finish( + TRACKER_SPARQL_CURSOR(source), + result, &error); + + if (!success) { + if (error) { + DBG("cursor_next error: %s", error->message); + g_error_free(error); + } else + /* When tracker_sparql_cursor_next_finish ends with + * failure and no error is set, that means end of + * results returned by query */ + pending->callback(NULL, 0, pending->user_data); + + goto failed; + } + + node = string_array_from_cursor(cursor, pending->num_fields); + err = pending->callback(node, pending->num_fields, pending->user_data); + g_free(node); + + /* Fetch next result only if processing current chunk ended with + * success. Sometimes during processing data, we are able to determine + * if there is no need to get more data from tracker - by example + * stored amount of data parts is big enough for sending and we might + * want to suspend processing or just some error occurred. */ + if (!err) { + cancellable = g_cancellable_new(); + update_cancellable(pending->user_data, cancellable); + tracker_sparql_cursor_next_async(cursor, cancellable, + async_query_cursor_next_cb, + pending); + return; + } + +failed: + g_object_unref(cursor); + g_free(pending); +} + +static int query_tracker(const char *query, int num_fields, + reply_list_foreach_t callback, void *user_data) +{ + struct pending_reply *pending; + GCancellable *cancellable; + TrackerSparqlCursor *cursor; + GError *error = NULL; + + DBG(""); + + if (connection == NULL) + connection = tracker_sparql_connection_get_direct( + NULL, &error); + + if (!connection) { + if (error) { + DBG("direct-connection error: %s", error->message); + g_error_free(error); + } + + return -EINTR; + } + + cancellable = g_cancellable_new(); + update_cancellable(user_data, cancellable); + cursor = tracker_sparql_connection_query(connection, query, + cancellable, &error); + + if (cursor == NULL) { + if (error) { + DBG("connection_query error: %s", error->message); + g_error_free(error); + } + + g_object_unref(cancellable); + + return -EINTR; + } + + pending = g_new0(struct pending_reply, 1); + pending->callback = callback; + pending->user_data = user_data; + pending->num_fields = num_fields; + + /* Now asynchronously going through each row of results - callback + * async_query_cursor_next_cb will be called ALWAYS, even if async + * request was canceled */ + tracker_sparql_cursor_next_async(cursor, cancellable, + async_query_cursor_next_cb, + pending); + + return 0; +} + +static char *iso8601_utc_to_localtime(const char *datetime) +{ + time_t time; + struct tm tm, *local; + char localdate[32]; + int nr; + + memset(&tm, 0, sizeof(tm)); + + nr = sscanf(datetime, "%04u-%02u-%02uT%02u:%02u:%02u", + &tm.tm_year, &tm.tm_mon, &tm.tm_mday, + &tm.tm_hour, &tm.tm_min, &tm.tm_sec); + if (nr < 6) { + /* Invalid time format */ + error("sscanf(): %s (%d)", strerror(errno), errno); + return g_strdup(""); + } + + /* Time already in localtime */ + if (!g_str_has_suffix(datetime, "Z")) { + strftime(localdate, sizeof(localdate), "%Y%m%dT%H%M%S", &tm); + return g_strdup(localdate); + } + + tm.tm_year -= 1900; /* Year since 1900 */ + tm.tm_mon--; /* Months since January, values 0-11 */ + + time = mktime(&tm); + time -= timezone; + + local = localtime(&time); + + strftime(localdate, sizeof(localdate), "%Y%m%dT%H%M%S", local); + + return g_strdup(localdate); +} + +static void set_call_type(struct phonebook_contact *contact, + const char *datetime, const char *is_sent, + const char *is_answered) +{ + gboolean sent, answered; + + if (g_strcmp0(datetime, "NOTACALL") == 0) { + contact->calltype = CALL_TYPE_NOT_A_CALL; + return; + } + + sent = g_str_equal(is_sent, "true"); + answered = g_str_equal(is_answered, "true"); + + if (sent == FALSE) { + if (answered == FALSE) + contact->calltype = CALL_TYPE_MISSED; + else + contact->calltype = CALL_TYPE_INCOMING; + } else + contact->calltype = CALL_TYPE_OUTGOING; + + /* Tracker gives time in the ISO 8601 format, UTC time */ + contact->datetime = iso8601_utc_to_localtime(datetime); +} + +static gboolean contact_matches(struct contact_data *c_data, const char *id, + const char *datetime) +{ + char *localtime; + int cmp_ret; + + if (g_strcmp0(c_data->id, id) != 0) + return FALSE; + + /* id is equal and not call history entry => contact matches */ + if (c_data->contact->calltype == CALL_TYPE_NOT_A_CALL) + return TRUE; + + /* for call history entries have to compare also timestamps of calls */ + localtime = iso8601_utc_to_localtime(datetime); + cmp_ret = g_strcmp0(c_data->contact->datetime, localtime); + g_free(localtime); + + return (cmp_ret == 0) ? TRUE : FALSE; +} + +static struct phonebook_contact *find_contact(GSList *contacts, const char *id, + const char *datetime) +{ + GSList *l; + + for (l = contacts; l; l = l->next) { + struct contact_data *c_data = l->data; + + if (contact_matches(c_data, id, datetime)) + return c_data->contact; + } + + return NULL; +} + +static struct phonebook_field *find_field(GSList *fields, const char *value, + int type) +{ + GSList *l; + + for (l = fields; l; l = l->next) { + struct phonebook_field *field = l->data; + /* Returning phonebook number if phone values and type values + * are equal */ + if (g_strcmp0(field->text, value) == 0 && field->type == type) + return field; + } + + return NULL; +} + +static void add_phone_number(struct phonebook_contact *contact, + const char *phone, int type) +{ + struct phonebook_field *number; + + if (phone == NULL || strlen(phone) == 0) + return; + + /* Not adding number if there is already added with the same value */ + if (find_field(contact->numbers, phone, type)) + return; + + number = g_new0(struct phonebook_field, 1); + number->text = g_strdup(phone); + number->type = type; + + contact->numbers = g_slist_append(contact->numbers, number); +} + +static void add_email(struct phonebook_contact *contact, const char *address, + int type) +{ + struct phonebook_field *email; + + if (address == NULL || strlen(address) == 0) + return; + + /* Not adding email if there is already added with the same value */ + if (find_field(contact->emails, address, type)) + return; + + email = g_new0(struct phonebook_field, 1); + email->text = g_strdup(address); + email->type = type; + + contact->emails = g_slist_append(contact->emails, email); +} + +static gboolean addr_matches(struct phonebook_addr *a, struct phonebook_addr *b) +{ + GSList *la, *lb; + + if (a->type != b->type) + return FALSE; + + for (la = a->fields, lb = b->fields; la && lb; + la = la->next, lb = lb->next) { + char *field_a = la->data; + char *field_b = lb->data; + + if (g_strcmp0(field_a, field_b) != 0) + return FALSE; + } + + return TRUE; +} + +/* generates phonebook_addr struct from tracker address data string. */ +static struct phonebook_addr *gen_addr(const char *address, int type) +{ + struct phonebook_addr *addr; + GSList *fields = NULL; + char **addr_parts; + int i; + + /* This test handles cases when address points to empty string + * (or address is NULL pointer) or string containing only six + * separators. It indicates that none of address fields is present + * and there is no sense to create dummy phonebook_addr struct */ + if (address == NULL || strlen(address) < ADDR_FIELD_AMOUNT) + return NULL; + + addr_parts = g_strsplit(address, ADDR_DELIM, ADDR_FIELD_AMOUNT); + + for (i = 0; i < ADDR_FIELD_AMOUNT; ++i) + fields = g_slist_append(fields, g_strdup(addr_parts[i])); + + g_strfreev(addr_parts); + + addr = g_new0(struct phonebook_addr, 1); + addr->fields = fields; + addr->type = type; + + return addr; +} + +static void add_address(struct phonebook_contact *contact, + const char *address, int type) +{ + struct phonebook_addr *addr; + GSList *l; + + addr = gen_addr(address, type); + if (addr == NULL) + return; + + /* Not adding address if there is already added with the same value. + * These type of checks have to be done because sometimes tracker + * returns results for contact data in more than 1 row - then the same + * address may be returned more than once in query results */ + for (l = contact->addresses; l; l = l->next) { + struct phonebook_addr *tmp = l->data; + + if (addr_matches(tmp, addr)) { + phonebook_addr_free(addr); + return; + } + } + + contact->addresses = g_slist_append(contact->addresses, addr); +} + +static void add_url(struct phonebook_contact *contact, const char *url_val, + int type) +{ + struct phonebook_field *url; + + if (url_val == NULL || strlen(url_val) == 0) + return; + + /* Not adding url if there is already added with the same value */ + if (find_field(contact->urls, url_val, type)) + return; + + url = g_new0(struct phonebook_field, 1); + + url->text = g_strdup(url_val); + url->type = type; + + contact->urls = g_slist_append(contact->urls, url); +} + +static GString *gen_vcards(GSList *contacts, + const struct apparam_field *params) +{ + GSList *l; + GString *vcards; + + vcards = g_string_new(NULL); + + /* Generating VCARD string from contacts and freeing used contacts */ + for (l = contacts; l; l = l->next) { + struct contact_data *c_data = l->data; + phonebook_add_contact(vcards, c_data->contact, + params->filter, params->format); + } + + return vcards; +} + +static int pull_contacts_size(const char **reply, int num_fields, + void *user_data) +{ + struct phonebook_data *data = user_data; + + if (num_fields < 0) { + data->cb(NULL, 0, num_fields, 0, TRUE, data->user_data); + return -EINTR; + } + + if (reply != NULL) { + data->index = atoi(reply[0]); + return 0; + } + + data->cb(NULL, 0, data->index, data->newmissedcalls, TRUE, + data->user_data); + + return 0; + /* + * phonebook_data is freed in phonebook_req_finalize. Useful in + * cases when call is terminated. + */ +} + +static void add_affiliation(char **field, const char *value) +{ + if (strlen(*field) > 0 || value == NULL || strlen(value) == 0) + return; + + g_free(*field); + + *field = g_strdup(value); +} + +static void contact_init(struct phonebook_contact *contact, + const char **reply) +{ + if (reply[COL_FAMILY_NAME][0] == '\0' && + reply[COL_GIVEN_NAME][0] == '\0' && + reply[COL_ADDITIONAL_NAME][0] == '\0' && + reply[COL_NAME_PREFIX][0] == '\0' && + reply[COL_NAME_SUFFIX][0] == '\0') { + if (reply[COL_FULL_NAME][0] != '\0') + contact->family = g_strdup(reply[COL_FULL_NAME]); + else + contact->family = g_strdup(reply[COL_NICKNAME]); + } else { + contact->family = g_strdup(reply[COL_FAMILY_NAME]); + contact->given = g_strdup(reply[COL_GIVEN_NAME]); + contact->additional = g_strdup(reply[COL_ADDITIONAL_NAME]); + contact->prefix = g_strdup(reply[COL_NAME_PREFIX]); + contact->suffix = g_strdup(reply[COL_NAME_SUFFIX]); + } + contact->fullname = g_strdup(reply[COL_FULL_NAME]); + contact->birthday = g_strdup(reply[COL_BIRTH_DATE]); + contact->nickname = g_strdup(reply[COL_NICKNAME]); + contact->photo = g_strdup(reply[COL_PHOTO]); + contact->company = g_strdup(reply[COL_ORG_NAME]); + contact->department = g_strdup(reply[COL_ORG_DEPARTMENT]); + contact->role = g_strdup(reply[COL_ORG_ROLE]); + contact->uid = g_strdup(reply[COL_UID]); + contact->title = g_strdup(reply[COL_TITLE]); + + set_call_type(contact, reply[COL_DATE], reply[COL_SENT], + reply[COL_ANSWERED]); +} + +static enum phonebook_number_type get_phone_type(const char *affilation) +{ + if (g_strcmp0(AFFILATION_HOME, affilation) == 0) + return TEL_TYPE_HOME; + else if (g_strcmp0(AFFILATION_WORK, affilation) == 0) + return TEL_TYPE_WORK; + + return TEL_TYPE_OTHER; +} + +static void add_aff_number(struct phonebook_contact *contact, + const char *pnumber, const char *aff_type) +{ + char **num_parts; + char *type, *number; + + /* For phone taken directly from contacts data, phone number string + * is represented as number type and number string - those strings are + * separated by SUB_DELIM string */ + num_parts = g_strsplit(pnumber, SUB_DELIM, 2); + + if (!num_parts) + return; + + if (num_parts[0]) + type = num_parts[0]; + else + goto failed; + + if (num_parts[1]) + number = num_parts[1]; + else + goto failed; + + if (g_strrstr(type, FAX_NUM_TYPE)) + add_phone_number(contact, number, TEL_TYPE_FAX); + else if (g_strrstr(type, MOBILE_NUM_TYPE)) + add_phone_number(contact, number, TEL_TYPE_MOBILE); + else + /* if this is no fax/mobile phone, then adding phone number + * type based on type of the affilation field */ + add_phone_number(contact, number, get_phone_type(aff_type)); + +failed: + g_strfreev(num_parts); +} + +static void contact_add_numbers(struct phonebook_contact *contact, + const char **reply) +{ + char **aff_numbers; + int i; + + /* Filling phone numbers from contact's affilation */ + aff_numbers = g_strsplit(reply[COL_PHONE_AFF], MAIN_DELIM, MAX_FIELDS); + + if (aff_numbers) + for (i = 0; aff_numbers[i]; ++i) + add_aff_number(contact, aff_numbers[i], + reply[COL_AFF_TYPE]); + + g_strfreev(aff_numbers); +} + +static enum phonebook_field_type get_field_type(const char *affilation) +{ + if (g_strcmp0(AFFILATION_HOME, affilation) == 0) + return FIELD_TYPE_HOME; + else if (g_strcmp0(AFFILATION_WORK, affilation) == 0) + return FIELD_TYPE_WORK; + + return FIELD_TYPE_OTHER; +} + +static void add_aff_field(struct phonebook_contact *contact, + const char *aff_email, add_field_t add_field_cb) +{ + char **email_parts; + char *type, *email; + + /* Emails from affilation data, are represented as real email + * string and affilation type - those strings are separated by + * SUB_DELIM string */ + email_parts = g_strsplit(aff_email, SUB_DELIM, 2); + + if (!email_parts) + return; + + if (email_parts[0]) + email = email_parts[0]; + else + goto failed; + + if (email_parts[1]) + type = email_parts[1]; + else + goto failed; + + add_field_cb(contact, email, get_field_type(type)); + +failed: + g_strfreev(email_parts); +} + +static void contact_add_emails(struct phonebook_contact *contact, + const char **reply) +{ + char **aff_emails; + int i; + + /* Emails from affilation */ + aff_emails = g_strsplit(reply[COL_EMAIL_AFF], MAIN_DELIM, MAX_FIELDS); + + if (aff_emails) + for (i = 0; aff_emails[i] != NULL; ++i) + add_aff_field(contact, aff_emails[i], add_email); + + g_strfreev(aff_emails); +} + +static void contact_add_addresses(struct phonebook_contact *contact, + const char **reply) +{ + char **aff_addr; + int i; + + /* Addresses from affilation */ + aff_addr = g_strsplit(reply[COL_ADDR_AFF], MAIN_DELIM, MAX_FIELDS); + + if (aff_addr) + for (i = 0; aff_addr[i] != NULL; ++i) + add_aff_field(contact, aff_addr[i], add_address); + + g_strfreev(aff_addr); +} + +static void contact_add_urls(struct phonebook_contact *contact, + const char **reply) +{ + char **aff_url; + int i; + + /* Addresses from affilation */ + aff_url = g_strsplit(reply[COL_URL], MAIN_DELIM, MAX_FIELDS); + + if (aff_url) + for (i = 0; aff_url[i] != NULL; ++i) + add_aff_field(contact, aff_url[i], add_url); + + g_strfreev(aff_url); +} + +static void contact_add_organization(struct phonebook_contact *contact, + const char **reply) +{ + /* Adding fields connected by nco:hasAffiliation - they may be in + * separate replies */ + add_affiliation(&contact->title, reply[COL_TITLE]); + add_affiliation(&contact->company, reply[COL_ORG_NAME]); + add_affiliation(&contact->department, reply[COL_ORG_DEPARTMENT]); + add_affiliation(&contact->role, reply[COL_ORG_ROLE]); +} + +static void free_data_contacts(struct phonebook_data *data) +{ + GSList *l; + + /* freeing contacts */ + for (l = data->contacts; l; l = l->next) { + struct contact_data *c_data = l->data; + + g_free(c_data->id); + phonebook_contact_free(c_data->contact); + g_free(c_data); + } + + g_slist_free(data->contacts); + data->contacts = NULL; +} + +static void send_pull_part(struct phonebook_data *data, + const struct apparam_field *params, gboolean lastpart) +{ + GString *vcards; + + DBG(""); + vcards = gen_vcards(data->contacts, params); + data->cb(vcards->str, vcards->len, g_slist_length(data->contacts), + data->newmissedcalls, lastpart, data->user_data); + + if (!lastpart) + free_data_contacts(data); + g_string_free(vcards, TRUE); +} + +static int pull_contacts(const char **reply, int num_fields, void *user_data) +{ + struct phonebook_data *data = user_data; + const struct apparam_field *params = data->params; + struct phonebook_contact *contact; + struct contact_data *contact_data; + int last_index, i; + gboolean cdata_present = FALSE, part_sent = FALSE; + static char *temp_id = NULL; + + if (num_fields < 0) { + data->cb(NULL, 0, num_fields, 0, TRUE, data->user_data); + goto fail; + } + + DBG("reply %p", reply); + data->tracker_index++; + + if (reply == NULL) + goto done; + + /* Trying to find contact in recently added contacts. It is needed for + * contacts that have more than one telephone number filled */ + contact = find_contact(data->contacts, reply[CONTACTS_ID_COL], + reply[COL_DATE]); + + /* If contact is already created then adding only new phone numbers */ + if (contact) { + cdata_present = TRUE; + goto add_numbers; + } + + /* We are doing a PullvCardEntry, no need for those checks */ + if (data->vcardentry) + goto add_entry; + + /* Last four fields are always present, ignoring them */ + for (i = 0; i < num_fields - 4; i++) { + if (reply[i][0] != '\0') + break; + } + + if (i == num_fields - 4 && !g_str_equal(reply[CONTACTS_ID_COL], + TRACKER_DEFAULT_CONTACT_ME)) + return 0; + + if (g_strcmp0(temp_id, reply[CONTACTS_ID_COL])) { + data->index++; + g_free(temp_id); + temp_id = g_strdup(reply[CONTACTS_ID_COL]); + + /* Incrementing counter for vcards in current part of data, + * but only if liststartoffset has been already reached */ + if (data->index > params->liststartoffset) + data->vcard_part_count++; + } + + if (data->vcard_part_count > VCARDS_PART_COUNT) { + DBG("Part of vcard data ready for sending..."); + data->vcard_part_count = 0; + /* Sending part of data to PBAP core - more data can be still + * fetched, so marking lastpart as FALSE */ + send_pull_part(data, params, FALSE); + + /* Later, after adding contact data, need to return -EINTR to + * stop fetching more data for this request. Data will be + * downloaded again from this point, when phonebook_pull_read + * will be called again with current request as a parameter*/ + part_sent = TRUE; + } + + last_index = params->liststartoffset + params->maxlistcount; + + if (data->index <= params->liststartoffset) + return 0; + + /* max number of results achieved - need send vcards data that was + * already collected and stop further data processing (these operations + * will be invoked in "done" section) */ + if (data->index > last_index && params->maxlistcount > 0) { + DBG("Maxlistcount achieved"); + goto done; + } + +add_entry: + contact = g_new0(struct phonebook_contact, 1); + contact_init(contact, reply); + +add_numbers: + contact_add_numbers(contact, reply); + contact_add_emails(contact, reply); + contact_add_addresses(contact, reply); + contact_add_urls(contact, reply); + contact_add_organization(contact, reply); + + DBG("contact %p", contact); + + /* Adding contacts data to wrapper struct - this data will be used to + * generate vcard list */ + if (!cdata_present) { + contact_data = g_new0(struct contact_data, 1); + contact_data->contact = contact; + contact_data->id = g_strdup(reply[CONTACTS_ID_COL]); + data->contacts = g_slist_append(data->contacts, contact_data); + } + + if (part_sent) + return -EINTR; + + return 0; + +done: + /* Processing is end, this is definitely last part of transmission + * (marking lastpart as TRUE) */ + send_pull_part(data, params, TRUE); + +fail: + g_free(temp_id); + temp_id = NULL; + + return -EINTR; + /* + * phonebook_data is freed in phonebook_req_finalize. Useful in + * cases when call is terminated. + */ +} + +static int add_to_cache(const char **reply, int num_fields, void *user_data) +{ + struct phonebook_data *data = user_data; + char *formatted; + int i; + + if (reply == NULL || num_fields < 0) + goto done; + + /* the first element is the URI, always not empty */ + for (i = 1; i < num_fields; i++) { + if (reply[i][0] != '\0') + break; + } + + if (i == num_fields && + !g_str_equal(reply[0], TRACKER_DEFAULT_CONTACT_ME)) + return 0; + + if (i == 7) + formatted = g_strdup(reply[7]); + else if (i == 6) + formatted = g_strdup(reply[6]); + else + formatted = g_strdup_printf("%s;%s;%s;%s;%s", + reply[1], reply[2], reply[3], reply[4], + reply[5]); + + /* The owner vCard must have the 0 handle */ + if (strcmp(reply[0], TRACKER_DEFAULT_CONTACT_ME) == 0) + data->entry_cb(reply[0], 0, formatted, "", + reply[6], data->user_data); + else + data->entry_cb(reply[0], PHONEBOOK_INVALID_HANDLE, formatted, + "", reply[6], data->user_data); + + g_free(formatted); + + return 0; + +done: + if (num_fields <= 0) + data->ready_cb(data->user_data); + + return -EINTR; + /* + * phonebook_data is freed in phonebook_req_finalize. Useful in + * cases when call is terminated. + */ +} + +int phonebook_init(void) +{ + g_thread_init(NULL); + g_type_init(); + + return 0; +} + +void phonebook_exit(void) +{ +} + +char *phonebook_set_folder(const char *current_folder, const char *new_folder, + uint8_t flags, int *err) +{ + char *tmp1, *tmp2, *base, *path = NULL; + gboolean root, child; + int ret = 0; + int len; + + root = (g_strcmp0("/", current_folder) == 0); + child = (new_folder && strlen(new_folder) != 0); + + switch (flags) { + case 0x02: + /* Go back to root */ + if (!child) { + path = g_strdup("/"); + goto done; + } + + path = g_build_filename(current_folder, new_folder, NULL); + break; + case 0x03: + /* Go up 1 level */ + if (root) { + /* Already root */ + path = g_strdup("/"); + goto done; + } + + /* + * Removing one level of the current folder. Current folder + * contains AT LEAST one level since it is not at root folder. + * Use glib utility functions to handle invalid chars in the + * folder path properly. + */ + tmp1 = g_path_get_basename(current_folder); + tmp2 = g_strrstr(current_folder, tmp1); + len = tmp2 - (current_folder + 1); + + g_free(tmp1); + + if (len == 0) + base = g_strdup("/"); + else + base = g_strndup(current_folder, len); + + /* Return: one level only */ + if (!child) { + path = base; + goto done; + } + + path = g_build_filename(base, new_folder, NULL); + g_free(base); + + break; + default: + ret = -EBADR; + break; + } + +done: + if (path && !folder_is_valid(path)) + ret = -ENOENT; + + if (ret < 0) { + g_free(path); + path = NULL; + } + + if (err) + *err = ret; + + return path; +} + +static int pull_newmissedcalls(const char **reply, int num_fields, + void *user_data) +{ + struct phonebook_data *data = user_data; + reply_list_foreach_t pull_cb; + int col_amount, err; + const char *query; + int nmissed; + + if (num_fields < 0) { + data->cb(NULL, 0, num_fields, 0, TRUE, data->user_data); + + return -EINTR; + } + + if (reply != NULL) { + nmissed = atoi(reply[0]); + data->newmissedcalls = + nmissed <= UINT8_MAX ? nmissed : UINT8_MAX; + DBG("newmissedcalls %d", data->newmissedcalls); + + return 0; + } + + if (data->params->maxlistcount == 0) { + query = name2count_query(PB_CALLS_MISSED); + col_amount = COUNT_QUERY_COL_AMOUNT; + pull_cb = pull_contacts_size; + } else { + query = name2query(PB_CALLS_MISSED); + col_amount = PULL_QUERY_COL_AMOUNT; + pull_cb = pull_contacts; + } + + err = query_tracker(query, col_amount, pull_cb, data); + if (err < 0) { + data->cb(NULL, 0, err, 0, TRUE, data->user_data); + + return -EINTR; + } + + return 0; +} + +void phonebook_req_finalize(void *request) +{ + struct phonebook_data *data = request; + + DBG(""); + + if (!data) + return; + + /* canceling asynchronous operation on tracker if any is active */ + if (data->query_canc) { + g_cancellable_cancel(data->query_canc); + g_object_unref(data->query_canc); + } + + free_data_contacts(data); + g_free(data->req_name); + g_free(data); +} + +void *phonebook_pull(const char *name, const struct apparam_field *params, + phonebook_cb cb, void *user_data, int *err) +{ + struct phonebook_data *data; + + DBG("name %s", name); + + data = g_new0(struct phonebook_data, 1); + data->params = params; + data->user_data = user_data; + data->cb = cb; + data->req_name = g_strdup(name); + + if (err) + *err = 0; + + return data; +} + +int phonebook_pull_read(void *request) +{ + struct phonebook_data *data = request; + reply_list_foreach_t pull_cb; + const char *query; + char *offset_query; + int col_amount; + int ret; + + if (!data) + return -ENOENT; + + data->newmissedcalls = 0; + + if (g_strcmp0(data->req_name, PB_CALLS_MISSED) == 0 && + data->tracker_index == 0) { + /* new missed calls amount should be counted only once - it + * will be done during generating first part of results of + * missed calls history */ + query = NEW_MISSED_CALLS_COUNT_QUERY; + col_amount = COUNT_QUERY_COL_AMOUNT; + pull_cb = pull_newmissedcalls; + } else if (data->params->maxlistcount == 0) { + query = name2count_query(data->req_name); + col_amount = COUNT_QUERY_COL_AMOUNT; + pull_cb = pull_contacts_size; + } else { + query = name2query(data->req_name); + col_amount = PULL_QUERY_COL_AMOUNT; + pull_cb = pull_contacts; + } + + if (query == NULL) + return -ENOENT; + + if (pull_cb == pull_contacts && data->tracker_index > 0) { + /* Adding offset to pull query to download next parts of data + * from tracker (phonebook_pull_read may be called many times + * from PBAP core to fetch data partially) */ + offset_query = g_strdup_printf(QUERY_OFFSET_FORMAT, query, + data->tracker_index); + ret = query_tracker(offset_query, col_amount, pull_cb, data); + + g_free(offset_query); + + return ret; + } + + return query_tracker(query, col_amount, pull_cb, data); +} + +void *phonebook_get_entry(const char *folder, const char *id, + const struct apparam_field *params, + phonebook_cb cb, void *user_data, int *err) +{ + struct phonebook_data *data; + char *query; + int ret; + + DBG("folder %s id %s", folder, id); + + data = g_new0(struct phonebook_data, 1); + data->user_data = user_data; + data->params = params; + data->cb = cb; + data->vcardentry = TRUE; + + if (g_str_has_prefix(id, CONTACT_ID_PREFIX) == TRUE || + g_strcmp0(id, TRACKER_DEFAULT_CONTACT_ME) == 0) + query = g_strdup_printf(CONTACTS_QUERY_FROM_URI, id, id, id, id, + id, id, id, id, id, id, id, id, id); + else if (g_str_has_prefix(id, CALL_ID_PREFIX) == TRUE) + query = g_strdup_printf(CONTACT_FROM_CALL_QUERY, id); + else + query = g_strdup_printf(CONTACTS_OTHER_QUERY_FROM_URI, + id, id, id); + + ret = query_tracker(query, PULL_QUERY_COL_AMOUNT, pull_contacts, data); + if (err) + *err = ret; + + g_free(query); + + return data; +} + +void *phonebook_create_cache(const char *name, phonebook_entry_cb entry_cb, + phonebook_cache_ready_cb ready_cb, void *user_data, int *err) +{ + struct phonebook_data *data; + const char *query; + int ret; + + DBG("name %s", name); + + query = folder2query(name); + if (query == NULL) { + if (err) + *err = -ENOENT; + return NULL; + } + + data = g_new0(struct phonebook_data, 1); + data->entry_cb = entry_cb; + data->ready_cb = ready_cb; + data->user_data = user_data; + + ret = query_tracker(query, 8, add_to_cache, data); + if (err) + *err = ret; + + return data; +} diff --git a/obexd/plugins/syncevolution.c b/obexd/plugins/syncevolution.c new file mode 100755 index 0000000..854505a --- /dev/null +++ b/obexd/plugins/syncevolution.c @@ -0,0 +1,483 @@ +/* + * + * OBEX Server + * + * Copyright (C) 2007-2010 Intel Corporation + * Copyright (C) 2007-2010 Marcel Holtmann + * + * + * 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 +#endif + +#include +#include +#include +#include + +#include +#include + +#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 "\ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ +" + +#define SYNCE_BUS_NAME "org.syncevolution" +#define SYNCE_PATH "/org/syncevolution/Server" +#define SYNCE_SERVER_INTERFACE "org.syncevolution.Server" +#define SYNCE_CONN_INTERFACE "org.syncevolution.Connection" + +struct synce_context { + struct obex_session *os; + DBusConnection *dbus_conn; + char *conn_obj; + unsigned int reply_watch; + unsigned int abort_watch; + GString *buffer; + int lasterr; + char *id; +}; + +static void append_dict_entry(DBusMessageIter *dict, const char *key, + int type, void *val) +{ + DBusMessageIter entry; + + dbus_message_iter_open_container(dict, DBUS_TYPE_DICT_ENTRY, + NULL, &entry); + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &key); + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &val); + dbus_message_iter_close_container(dict, &entry); +} + +static gboolean reply_signal(DBusConnection *conn, DBusMessage *msg, + void *data) +{ + struct synce_context *context = data; + const char *path = dbus_message_get_path(msg); + DBusMessageIter iter, array_iter; + char *value; + int length; + + if (strcmp(context->conn_obj, path) != 0) { + obex_object_set_io_flags(context, G_IO_ERR, -EPERM); + context->lasterr = -EPERM; + return FALSE; + } + + dbus_message_iter_init(msg, &iter); + + dbus_message_iter_recurse(&iter, &array_iter); + dbus_message_iter_get_fixed_array(&array_iter, &value, &length); + + context->buffer = g_string_new_len(value, length); + obex_object_set_io_flags(context, G_IO_IN, 0); + context->lasterr = 0; + + return TRUE; +} + +static gboolean abort_signal(DBusConnection *conn, DBusMessage *msg, + void *data) +{ + struct synce_context *context = data; + + obex_object_set_io_flags(context, G_IO_ERR, -EPERM); + context->lasterr = -EPERM; + + return TRUE; +} + +static void connect_cb(DBusPendingCall *call, void *user_data) +{ + struct synce_context *context = user_data; + DBusConnection *conn; + DBusMessage *reply; + DBusError err; + char *path; + + conn = context->dbus_conn; + + reply = dbus_pending_call_steal_reply(call); + + dbus_error_init(&err); + if (dbus_message_get_args(reply, &err, DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID) == FALSE) { + error("%s", err.message); + dbus_error_free(&err); + goto failed; + } + + DBG("Got conn object %s from syncevolution", path); + context->conn_obj = g_strdup(path); + + context->reply_watch = g_dbus_add_signal_watch(conn, NULL, path, + SYNCE_CONN_INTERFACE, "Reply", + reply_signal, context, NULL); + + context->abort_watch = g_dbus_add_signal_watch(conn, NULL, path, + SYNCE_CONN_INTERFACE, "Abort", + abort_signal, context, NULL); + + dbus_message_unref(reply); + + return; + +failed: + obex_object_set_io_flags(context, G_IO_ERR, -EPERM); + context->lasterr = -EPERM; +} + +static void process_cb(DBusPendingCall *call, void *user_data) +{ + struct synce_context *context = user_data; + DBusMessage *reply; + DBusError derr; + + reply = dbus_pending_call_steal_reply(call); + dbus_error_init(&derr); + if (dbus_set_error_from_message(&derr, reply)) { + error("process_cb(): syncevolution replied with an error:" + " %s, %s", derr.name, derr.message); + dbus_error_free(&derr); + + obex_object_set_io_flags(context, G_IO_ERR, -EPERM); + context->lasterr = -EPERM; + goto done; + } + + obex_object_set_io_flags(context, G_IO_OUT, 0); + context->lasterr = 0; + +done: + dbus_message_unref(reply); +} + +static void *synce_connect(struct obex_session *os, int *err) +{ + DBusConnection *conn; + struct synce_context *context; + char *address; + + manager_register_session(os); + + conn = manager_dbus_get_connection(); + if (!conn) + goto failed; + + context = g_new0(struct synce_context, 1); + context->dbus_conn = conn; + context->lasterr = -EAGAIN; + context->os = os; + + if (obex_getpeername(os, &address) == 0) { + context->id = g_strdup_printf("%s+%d", address, + SYNCEVOLUTION_CHANNEL); + g_free(address); + } + + if (err) + *err = 0; + + return context; + +failed: + if (err) + *err = -EPERM; + + return NULL; +} + +static int synce_put(struct obex_session *os, void *user_data) +{ + return 0; +} + +static int synce_get(struct obex_session *os, void *user_data) +{ + return obex_get_stream_start(os, NULL); +} + +static void close_cb(DBusPendingCall *call, void *user_data) +{ + DBusMessage *reply; + DBusError derr; + + reply = dbus_pending_call_steal_reply(call); + dbus_error_init(&derr); + if (dbus_set_error_from_message(&derr, reply)) { + error("close_cb(): syncevolution replied with an error:" + " %s, %s", derr.name, derr.message); + dbus_error_free(&derr); + } + + dbus_message_unref(reply); +} + +static void synce_disconnect(struct obex_session *os, void *user_data) +{ + struct synce_context *context = user_data; + + g_free(context); +} + +static void *synce_open(const char *name, int oflag, mode_t mode, + void *user_data, size_t *size, int *err) +{ + struct synce_context *context = user_data; + + if (err) + *err = context ? 0 : -EFAULT; + + return user_data; +} + +static int synce_close(void *object) +{ + struct synce_context *context = object; + DBusMessage *msg; + const char *error; + gboolean normal; + DBusPendingCall *call; + + if (!context->conn_obj) + goto done; + + msg = dbus_message_new_method_call(SYNCE_BUS_NAME, context->conn_obj, + SYNCE_CONN_INTERFACE, "Close"); + if (!msg) + goto failed; + + normal = TRUE; + error = "none"; + dbus_message_append_args(msg, DBUS_TYPE_BOOLEAN, &normal, + DBUS_TYPE_STRING, &error, DBUS_TYPE_INVALID); + + g_dbus_send_message_with_reply(context->dbus_conn, msg, &call, -1); + dbus_pending_call_set_notify(call, close_cb, NULL, NULL); + dbus_message_unref(msg); + dbus_pending_call_unref(call); + +failed: + g_dbus_remove_watch(context->dbus_conn, context->reply_watch); + context->reply_watch = 0; + g_dbus_remove_watch(context->dbus_conn, context->abort_watch); + context->abort_watch = 0; + + g_free(context->conn_obj); + context->conn_obj = NULL; + +done: + dbus_connection_unref(context->dbus_conn); + g_free(context); + return 0; +} + +static ssize_t synce_read(void *object, void *buf, size_t count) +{ + struct synce_context *context = object; + DBusConnection *conn; + char transport[36], transport_description[24]; + const char *session; + DBusMessage *msg; + DBusMessageIter iter, dict; + gboolean authenticate; + DBusPendingCall *call; + + if (context->buffer) + return string_read(context->buffer, buf, count); + + conn = manager_dbus_get_connection(); + if (conn == NULL) + return -EPERM; + + msg = dbus_message_new_method_call(SYNCE_BUS_NAME, SYNCE_PATH, + SYNCE_SERVER_INTERFACE, "Connect"); + if (!msg) + return -EPERM; + + dbus_message_iter_init_append(msg, &iter); + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_STRING_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); + + append_dict_entry(&dict, "id", DBUS_TYPE_STRING, context->id); + + snprintf(transport, sizeof(transport), "%s.obexd", OBEXD_SERVICE); + append_dict_entry(&dict, "transport", DBUS_TYPE_STRING, transport); + + snprintf(transport_description, sizeof(transport_description), + "version %s", VERSION); + append_dict_entry(&dict, "transport_description", DBUS_TYPE_STRING, + transport_description); + + dbus_message_iter_close_container(&iter, &dict); + + authenticate = FALSE; + session = ""; + dbus_message_append_args(msg, DBUS_TYPE_BOOLEAN, &authenticate, + DBUS_TYPE_STRING, &session, DBUS_TYPE_INVALID); + + if (!g_dbus_send_message_with_reply(conn, msg, &call, -1)) { + error("D-Bus call to %s failed.", SYNCE_SERVER_INTERFACE); + dbus_message_unref(msg); + return -EPERM; + } + + dbus_pending_call_set_notify(call, connect_cb, context, NULL); + + dbus_pending_call_unref(call); + dbus_message_unref(msg); + + return -EAGAIN; +} + +static ssize_t synce_write(void *object, const void *buf, size_t count) +{ + struct synce_context *context = object; + DBusMessage *msg; + DBusMessageIter iter, array_iter; + DBusPendingCall *call; + const char *type = obex_get_type(context->os); + + if (context->lasterr == 0) + return count; + + if (!context->conn_obj) + return -EFAULT; + + msg = dbus_message_new_method_call(SYNCE_BUS_NAME, context->conn_obj, + SYNCE_CONN_INTERFACE, "Process"); + if (!msg) + return -EFAULT; + + dbus_message_iter_init_append(msg, &iter); + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + DBUS_TYPE_BYTE_AS_STRING, &array_iter); + + dbus_message_iter_append_fixed_array(&array_iter, DBUS_TYPE_BYTE, + &buf, count); + dbus_message_iter_close_container(&iter, &array_iter); + + dbus_message_append_args(msg, DBUS_TYPE_STRING, &type, + DBUS_TYPE_INVALID); + + if (!g_dbus_send_message_with_reply(context->dbus_conn, msg, + &call, -1)) { + error("D-Bus call to %s failed.", SYNCE_CONN_INTERFACE); + dbus_message_unref(msg); + return -EPERM; + } + + dbus_pending_call_set_notify(call, process_cb, context, NULL); + + dbus_message_unref(msg); + dbus_pending_call_unref(call); + + return -EAGAIN; +} + +static struct obex_mime_type_driver synce_driver = { + .target = SYNCML_TARGET, + .target_size = SYNCML_TARGET_SIZE, + .open = synce_open, + .close = synce_close, + .read = synce_read, + .write = synce_write, +}; + +static struct obex_service_driver synce = { + .name = "OBEX server for SyncML, using SyncEvolution", + .service = OBEX_SYNCEVOLUTION, + .channel = SYNCEVOLUTION_CHANNEL, + .secure = TRUE, + .record = SYNCEVOLUTION_RECORD, + .target = SYNCML_TARGET, + .target_size = SYNCML_TARGET_SIZE, + .get = synce_get, + .put = synce_put, + .connect = synce_connect, + .disconnect = synce_disconnect, +}; + +static int synce_init(void) +{ + int err; + + err = obex_mime_type_driver_register(&synce_driver); + if (err < 0) + return err; + + return obex_service_driver_register(&synce); +} + +static void synce_exit(void) +{ + obex_service_driver_unregister(&synce); + obex_mime_type_driver_unregister(&synce_driver); +} + +OBEX_PLUGIN_DEFINE(syncevolution, synce_init, synce_exit) diff --git a/obexd/src/main.c b/obexd/src/main.c index c774cda..e2df4ef 100755 --- a/obexd/src/main.c +++ b/obexd/src/main.c @@ -179,6 +179,14 @@ static GOptionEntry options[] = { { NULL }, }; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +void obex_option_set_root_folder(const char *root) +{ + g_free(option_root); + option_root = g_strdup(root); +} +#endif + gboolean obex_option_auto_accept(void) { return option_autoaccept; diff --git a/obexd/src/manager.c b/obexd/src/manager.c index f84384a..90c5114 100755 --- a/obexd/src/manager.c +++ b/obexd/src/manager.c @@ -190,6 +190,52 @@ static DBusMessage *unregister_agent(DBusConnection *conn, return dbus_message_new_method_return(msg); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static DBusMessage *set_root(DBusConnection *conn, DBusMessage *msg, + const char *root, void *data) +{ + DBG("new_root: %s", root); + + /* Change the option root path (using in filesystem) */ + obex_option_set_root_folder(root); + + return dbus_message_new_method_return(msg); +} + +static DBusMessage *set_property(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + DBusMessageIter iter; + DBusMessageIter sub; + const char *property; + + if (!dbus_message_iter_init(msg, &iter)) + return invalid_args(msg); + + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) + return invalid_args(msg); + + dbus_message_iter_get_basic(&iter, &property); + dbus_message_iter_next(&iter); + + if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT) + return invalid_args(msg); + dbus_message_iter_recurse(&iter, &sub); + + if (g_str_equal("Root", property)) { + const char *root; + + if (dbus_message_iter_get_arg_type(&sub) != DBUS_TYPE_STRING) + return invalid_args(msg); + dbus_message_iter_get_basic(&sub, &root); + + return set_root(conn, msg, root, data); + } + + return invalid_args(msg); +} +#endif + static gboolean get_source(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { @@ -440,6 +486,79 @@ static gboolean transfer_get_filename(const GDBusPropertyTable *property, return TRUE; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static gboolean transfer_operation_exists(const GDBusPropertyTable *property, + void *data) +{ + struct obex_transfer *transfer = data; + struct obex_session *session = transfer->session; + + if (session->cmd == G_OBEX_OP_PUT && + session->size != OBJECT_SIZE_DELETE) + return TRUE; + else if (session->cmd == G_OBEX_OP_GET) + return TRUE; + else + return FALSE; +} + +static gboolean transfer_get_operation(const GDBusPropertyTable *property, + DBusMessageIter *iter, void *data) +{ + struct obex_transfer *transfer = data; + struct obex_session *session = transfer->session; + const char *operation; + + if (session->cmd == G_OBEX_OP_PUT && + session->size != OBJECT_SIZE_DELETE) + operation = "PUT"; + else if (session->cmd == G_OBEX_OP_GET) + operation = "GET"; + else + return FALSE; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &operation); + + return TRUE; +} + +static gboolean transfer_address_exists(const GDBusPropertyTable *property, + void *data) +{ + struct obex_transfer *transfer = data; + struct obex_session *session = transfer->session; + char *address; + int err; + + err = obex_getpeername(session, &address); + if (err < 0) + return FALSE; + + g_free(address); + + return TRUE; +} + +static gboolean transfer_get_address(const GDBusPropertyTable *property, + DBusMessageIter *iter, void *data) +{ + struct obex_transfer *transfer = data; + struct obex_session *session = transfer->session; + char *address; + int err; + + err = obex_getpeername(session, &address); + if (err < 0) + return FALSE; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &address); + g_free(address); + + return TRUE; +} + +#endif + static gboolean transfer_get_transferred(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { @@ -457,6 +576,10 @@ static const GDBusMethodTable manager_methods[] = { GDBUS_ARGS({ "agent", "o" }), NULL, register_agent) }, { GDBUS_METHOD("UnregisterAgent", GDBUS_ARGS({ "agent", "o" }), NULL, unregister_agent) }, +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + { GDBUS_METHOD("SetProperty", + GDBUS_ARGS({ "property", "sv" }), NULL, set_property) }, +#endif { } }; @@ -474,6 +597,12 @@ static const GDBusPropertyTable transfer_properties[] = { { "Time", "t", transfer_get_time, NULL, transfer_time_exists }, { "Filename", "s", transfer_get_filename, NULL, transfer_filename_exists }, +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + { "Operation", "s", transfer_get_operation, NULL, + transfer_operation_exists }, + { "Address", "s", transfer_get_address, NULL, + transfer_address_exists }, +#endif { "Transferred", "t", transfer_get_transferred }, { } }; diff --git a/obexd/src/obex.c b/obexd/src/obex.c index 788bffc..fee2461 100755 --- a/obexd/src/obex.c +++ b/obexd/src/obex.c @@ -642,7 +642,17 @@ static void parse_name(struct obex_session *os, GObexPacket *req) if (!g_obex_header_get_unicode(hdr, &name)) return; - +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + DBG("Obex Session For: %s", os->service->name); + if (name && g_strcmp0(os->service->name, "Object Push server") == 0) { + char *new_name; + new_name = strrchr(name, '/'); + if (new_name) { + name = new_name + 1; + DBG("FileName %s", name); + } + } +#endif os->name = g_strdup(name); DBG("NAME: %s", os->name); } @@ -773,7 +783,11 @@ int obex_put_stream_start(struct obex_session *os, const char *filename) int err; os->object = os->driver->open(filename, O_WRONLY | O_CREAT | O_TRUNC, +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + 0644, os->service_data, +#else 0600, os->service_data, +#endif os->size != OBJECT_SIZE_UNKNOWN ? (size_t *) &os->size : NULL, &err); if (os->object == NULL) { diff --git a/obexd/src/obexd.h b/obexd/src/obexd.h index 42c3c4d..ddf5ba0 100755 --- a/obexd/src/obexd.h +++ b/obexd/src/obexd.h @@ -41,3 +41,6 @@ gboolean obex_option_auto_accept(void); const char *obex_option_root_folder(void); gboolean obex_option_symlinks(void); const char *obex_option_capability(void); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +void obex_option_set_root_folder(const char *root); +#endif diff --git a/obexd/src/org.bluez.obex.service b/obexd/src/org.bluez.obex.service index a538088..3abb555 100755 --- a/obexd/src/org.bluez.obex.service +++ b/obexd/src/org.bluez.obex.service @@ -1,4 +1,3 @@ [D-BUS Service] Name=org.bluez.obex -Exec=/bin/false -SystemdService=dbus-org.bluez.obex.service +Exec=/bin/sh -c 'exec /usr/libexec/bluetooth/obexd -d --noplugin=ftp,syncevolution,pcsuite,irmc --symlinks -r /opt/usr/home/owner/media/' diff --git a/packaging/bluez.spec b/packaging/bluez.spec index ff12594..42d5729 100755 --- a/packaging/bluez.spec +++ b/packaging/bluez.spec @@ -1,10 +1,14 @@ +# Do not create provides fro extension-tm1 because the main package +# should anchor any reverse-dependencies +%global __provides_exclude_from ^(.*\\.tm1)$ + #%define with_libcapng --enable-capng %define _libpath /usr/lib %define upgrade_script_path /usr/share/upgrade/scripts Name: bluez Summary: Bluetooth Stack for Linux -Version: 5.37 -Release: 2 +Version: 5.43 +Release: 1 Group: Network & Connectivity/Bluetooth License: GPL-2.0+ and LGPL-2.1+ and Apache-2.0 URL: http://www.bluez.org/ @@ -23,6 +27,8 @@ Source1001: bluez.manifest #Requires: dbus >= 0.60 #BuildRequires: pkgconfig(libudev) BuildRequires: pkgconfig(dbus-1) +BuildRequires: pkgconfig(iniparser) +BuildRequires: pkgconfig(libxml-2.0) #BuildRequires: pkgconfig(glib-2.0) #BuildRequires: pkgconfig(ncurses) #BuildRequires: flex @@ -45,6 +51,8 @@ BuildRequires: pkg-config BuildRequires: readline-devel BuildRequires: udev BuildRequires: pkgconfig(libtzplatform-config) +Requires: %{name}-compat = %{version}-%{release} +Recommends: %{name}-profile_common = %{version}-%{release} %description The Bluetooth stack for Linux. @@ -87,6 +95,43 @@ Requires: python-gobject Contains a few tools for testing various bluetooth functions. The BLUETOOTH trademarks are owned by Bluetooth SIG, Inc., U.S.A. +%package profile_common +Summary: Modified bluez for mobile/common profile +Provides: %{name}-compat = %{version}-%{release} +Provides: %{name}-profile_mobile = %{version}-%{release} +Provides: %{name}-profile_ivi = %{version}-%{release} +Conflicts: %{name}-profile_wearable +Conflicts: %{name}-profile_tv +%description profile_common +Bluez default service script for Tizen (mobile/common profile) + +%package profile_wearable +Summary: Modified bluez for wearable profile +Provides: %{name}-compat = %{version}-%{release} +Conflicts: %{name}-profile_common +Conflicts: %{name}-profile_tv +%description profile_wearable +Bluez modified service script for Tizen wearable + +%package profile_tv +Summary: Modified bluez for wearable profile +Provides: %{name}-compat = %{version}-%{release} +Conflicts: %{name}-profile_common +Conflicts: %{name}-profile_wearable +%description profile_tv +Bluez modified service script for Tizen TV + +%ifarch %{ix86} || %{arm} +%package -n libbluetooth-extension-TM1 +Summary: Extension for mobile TM1 +Requires: libbluetooth = %{version}-%{release} +%description -n libbluetooth-extension-TM1 +Bluez default service script for Tizen mobile TM1 +When you want to uninstall this while keeping libbluetooth, you need +to reinstall libbluetooth after uninstalling this because this package +overwrites some contents of libbluetooth. +%endif + %prep %setup -q cp %{SOURCE1001} . @@ -94,20 +139,72 @@ cp %{SOURCE1001} . %build autoreconf -fiv -%if "%{?tizen_target_name}" == "TM1" || "%{?profile}" == "ivi" -%if "%{?tizen_target_name}" == "TM1" -export CFLAGS="${CFLAGS} -DTIZEN_FEATURE_BLUEZ_SPRD_QOS -DTIZEN_FEATURE_BLUEZ_SPRD_PAGE_SCAN" -%endif -%else -export CFLAGS="${CFLAGS} -DTIZEN_FEATURE_BLUEZ_BRCM_CHIP" -%endif +#if "{?profile}" == "ivi" +# TIZEN_FEATURE_BLUEZ_BRCM_CHIP: only in tools/hciattach.c ==> RUNTIME! (profile.h) +#endif -%if "%{?profile}" == "wearable" -export CFLAGS="${CFLAGS} -DTIZEN_FEATURE_BLUEZ_SMS_ONLY -DTIZEN_FEATURE_BLUEZ_BRCM_QOS -DTIZEN_FEATURE_BLUEZ_ROLE_CHANGE -DTIZEN_FEATURE_BLUEZ_CONFIRM_ONLY" -%endif +#if "{?profile}" == "wearable" +# TIZEN_FEATURE_BLUEZ_SMS_ONLY: only in obexd/plugins/messages-tizen.c ==> RUNTIME! (profile.h) +# TIZEN_FEATURE_BLUEZ_BRCM_QOS: only in profiles/audio/avdtp.c ==> RUNTIME! (profile.h) +# TIZEN_FEATURE_BLUEZ_ROLE_CHANGE: only in profiles/audio/avdtp.c ==> RUNTIME! (profile.h) +# TIZEN_FEATURE_BLUEZ_CONFIRM_ONLY: only in src/device.c ==> RUNTIME! (profile.h) +#endif export LDFLAGS=" -lncurses -Wl,--as-needed " export CFLAGS+=" -DTIZEN_FEATURE_BLUEZ_MODIFY -DTIZEN_FEATURE_BLUEZ_PBAP_SIM -DTIZEN_FEATURE_BLUEZ_AVRCP_TARGET" +export CFLAGS_DEFAULT="$CFLAGS" + +%ifarch %{ix86} || %{arm} +# extension-TM1 +export CFLAGS="$CFLAGS_DEFAULT -DTIZEN_FEATURE_BLUEZ_SPRD_QOS -DTIZEN_FEATURE_BLUEZ_SPRD_PAGE_SCAN" +# TIZEN_FEATURE_BLUEZ_SPRD_QOS: only in profiles/audio/avdtp.c +# TIZEN_FEATURE_BLUEZ_SPRD_PAGE_SCAN: src/adapter.c + +%reconfigure --disable-static \ + --sysconfdir=%{_sysconfdir} \ + --localstatedir=%{_localstatedir} \ + --with-systemdsystemunitdir=%{_libpath}/systemd/system \ + --with-systemduserunitdir=%{_libpath}/systemd/user \ + --libexecdir=%{_libexecdir} \ + --enable-debug \ + --enable-pie \ + --enable-serial \ + --enable-input \ + --enable-usb=no \ + --enable-tools \ + --disable-bccmd \ + --enable-pcmcia=no \ + --enable-hid2hci=no \ + --enable-alsa=no \ + --enable-gstreamer=no \ + --disable-dfutool \ + --disable-cups \ + --enable-health=yes \ + --enable-proximity=yes \ + --enable-tds=yes \ + --enable-dbusoob \ + --enable-test \ + --with-telephony=tizen \ + --enable-obex \ + --enable-library \ + --enable-gatt \ + --enable-experimental \ + --enable-autopair=no \ + --enable-hid=yes \ + --enable-tizenunusedplugin=no + + +make %{?_smp_mflags} all V=1 + +mkdir -p tm1 + +%make_install +cp -a %{buildroot}%{_libdir}/libbluetooth.so* tm1/ +%endif + +# non-extension-TM1 +export CFLAGS="$CFLAGS_DEFAULT" + %reconfigure --disable-static \ --sysconfdir=%{_sysconfdir} \ --localstatedir=%{_localstatedir} \ @@ -140,12 +237,15 @@ export CFLAGS+=" -DTIZEN_FEATURE_BLUEZ_MODIFY -DTIZEN_FEATURE_BLUEZ_PBAP_SIM -DT --enable-autopair=no \ %if "%{?profile}" == "wearable" --enable-wearable \ -%else - --enable-network \ %endif --enable-hid=yes \ --enable-tizenunusedplugin=no +# The if/endif for wearable above if only for PRODUCT optimization +# enable-wearable disables "TIZEN_HID_PLUGIN / TIZEN_UNUSED_PLUGIN" (input / hog) +# TIZEN_UNUSED_PLUGIN is "no" regardless of enable-wearable. +# TIZEN_HID_PLUGIN only matters. +# enable-network is not used in configure. make %{?_smp_mflags} all V=1 @@ -154,6 +254,11 @@ make check %install %make_install +%ifarch %{ix86} || %{arm} +pushd tm1 +for FILE in libbluetooth.so*; do mv "$FILE" "%{buildroot}%{_libdir}/$FILE.tm1"; done +popd +%endif # bluez-test rm -rvf $RPM_BUILD_ROOT/%{_libdir}/gstreamer-* @@ -163,11 +268,9 @@ install --mode=0644 -D %{S:7} $RPM_BUILD_ROOT/%{_sysconfdir}/modprobe.d/50-bluet # no idea why this is suddenly necessary... install --mode 0755 -d $RPM_BUILD_ROOT/var/lib/bluetooth -%if "%{?profile}" == "wearable" -install -D -m 0644 src/main_w.conf %{buildroot}%{_sysconfdir}/bluetooth/main.conf -%else +install -D -m 0644 src/main_w.conf %{buildroot}%{_sysconfdir}/bluetooth/main.conf.wearable install -D -m 0644 src/main_m.conf %{buildroot}%{_sysconfdir}/bluetooth/main.conf -%endif + #install -D -m 0644 src/bluetooth.conf %{buildroot}%{_sysconfdir}/dbus-1/system.d/bluetooth.conf #install -D -m 0644 profiles/audio/audio.conf %{buildroot}%{_sysconfdir}/bluetooth/audio.conf @@ -192,11 +295,11 @@ install -D -m 0755 attrib/gatttool $RPM_BUILD_ROOT/%{_bindir}/ install -D -m 0755 tools/obexctl %{buildroot}%{_bindir}/obexctl #test -%if "%{?profile}" == "tv" +#if "tv" mkdir -p %{buildroot}%{_libpath}/systemd/system/multi-user.target.wants/ ln -sf bluetooth.service %{buildroot}%{_libpath}/systemd/system/dbus-org.bluez.service ln -sf ../bluetooth-frwk.service %{buildroot}%{_libpath}/systemd/system/multi-user.target.wants/bluetooth.service -%endif +#endif mkdir -p %{buildroot}%{upgrade_script_path} cp -f packaging/500.bluez_upgrade.sh %{buildroot}%{upgrade_script_path} @@ -205,12 +308,19 @@ cp -f packaging/500.bluez_upgrade.sh %{buildroot}%{upgrade_script_path} %postun -n libbluetooth -p /sbin/ldconfig +%ifarch %{ix86} || %{arm} +%post -n libbluetooth-extension-TM1 +pushd %{_libdir} +for FILE in libbluetooth.so*.tm1; do mv "$FILE" "${FILE%.tm1}"; done +popd +/sbin/ldconfig +%endif + %files %manifest %{name}.manifest %defattr(-, root, root) %license COPYING #%{_sysconfdir}/bluetooth/audio.conf -%{_sysconfdir}/bluetooth/main.conf #%{_sysconfdir}/bluetooth/network.conf #%{_sysconfdir}/bluetooth/rfcomm.conf #%{_sysconfdir}/dbus-1/system.d/bluetooth.conf @@ -234,16 +344,9 @@ cp -f packaging/500.bluez_upgrade.sh %{buildroot}%{upgrade_script_path} %{upgrade_script_path}/500.bluez_upgrade.sh #test -2 -%if "%{?profile}" == "tv" -%{_libpath}/systemd/system/bluetooth.service -%{_libpath}/systemd/system/multi-user.target.wants/bluetooth.service -%{_libpath}/systemd/system/dbus-org.bluez.service -%{_datadir}/dbus-1/system-services/org.bluez.service -%else %exclude /%{_libpath}/systemd/system/bluetooth.service %exclude %{_libpath}/systemd/system/multi-user.target.wants/bluetooth.service %exclude /%{_datadir}/dbus-1/system-services/org.bluez.service -%endif %config %{_sysconfdir}/dbus-1/system.d/bluetooth.conf %dir /var/lib/bluetooth %dir %{_sysconfdir}/modprobe.d @@ -263,8 +366,16 @@ cp -f packaging/500.bluez_upgrade.sh %{buildroot}%{upgrade_script_path} %manifest %{name}.manifest %defattr(-, root, root) %{_libdir}/libbluetooth.so.* +%exclude %{_libdir}/libbluetooth.so*.tm1 %license COPYING +%ifarch %{ix86} || %{arm} +%files -n libbluetooth-extension-TM1 +%manifest %{name}.manifest +%defattr(-, root, root) +%{_libdir}/libbluetooth.so*.tm1 +%endif + %files -n obexd %manifest %{name}.manifest %defattr(-,root,root,-) @@ -298,4 +409,21 @@ cp -f packaging/500.bluez_upgrade.sh %{buildroot}%{upgrade_script_path} %docs_package +%post profile_wearable +ln -sf main.conf.wearable %{_sysconfdir}/bluetooth/main.conf +%preun profile_wearable +rm %{_sysconfdir}/bluetooth/main.conf +%files profile_wearable +%{_sysconfdir}/bluetooth/main.conf.wearable + +%files profile_tv +%{_sysconfdir}/bluetooth/main.conf +%{_libpath}/systemd/system/bluetooth.service +%{_libpath}/systemd/system/multi-user.target.wants/bluetooth.service +%{_libpath}/systemd/system/dbus-org.bluez.service +%{_datadir}/dbus-1/system-services/org.bluez.service + +%files profile_common +%{_sysconfdir}/bluetooth/main.conf + %changelog diff --git a/plugins/dbusoob.c b/plugins/dbusoob.c new file mode 100755 index 0000000..8948d5d --- /dev/null +++ b/plugins/dbusoob.c @@ -0,0 +1,273 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2011 ST-Ericsson SA + * + * Author: Szymon Janc 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 +#endif + +#include +#include "gdbus/gdbus.h" + +#include +#include +#include + +#include "src/plugin.h" +#include "src/log.h" +#include "src/dbus-common.h" +#include "src/adapter.h" +#include "src/device.h" +#include "src/eir.h" +#include "src/agent.h" +#include "src/hcid.h" +#include "src/error.h" + +#define OOB_INTERFACE "org.bluez.OutOfBand" + +struct oob_request { + struct btd_adapter *adapter; + DBusMessage *msg; +}; + +static GSList *oob_requests = NULL; +static DBusConnection *connection = NULL; + +static gint oob_request_cmp(gconstpointer a, gconstpointer b) +{ + const struct oob_request *data = a; + const struct btd_adapter *adapter = b; + + return data->adapter != adapter; +} + +static struct oob_request *find_oob_request(struct btd_adapter *adapter) +{ + GSList *match; + + match = g_slist_find_custom(oob_requests, adapter, oob_request_cmp); + + if (match) + return match->data; + + return NULL; +} + +static void read_local_data_complete(struct btd_adapter *adapter, + const uint8_t *hash192, const uint8_t *randomizer192, + const uint8_t *hash256, const uint8_t *randomizer256, + void *user_data) +{ + struct DBusMessage *reply; + struct oob_request *oob_request; + + oob_request = find_oob_request(adapter); + if (!oob_request) + return; + + if ((hash192 && randomizer192) || (hash256 && randomizer256)) + reply = g_dbus_create_reply(oob_request->msg, + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &hash192, 16, + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &randomizer192, 16, + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, + &hash256, hash256 ? 16 : 0, + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, + &randomizer256, randomizer256 ? 16 : 0, + DBUS_TYPE_INVALID); + else + reply = btd_error_failed(oob_request->msg, + "Failed to read local OOB data."); + + oob_requests = g_slist_remove(oob_requests, oob_request); + dbus_message_unref(oob_request->msg); + g_free(oob_request); + + if (!reply) { + error("Couldn't allocate D-Bus message"); + return; + } + + if (!g_dbus_send_message(connection, reply)) + error("D-Bus send failed"); +} + +static DBusMessage *read_local_data(DBusConnection *conn, DBusMessage *msg, + void *data) +{ + struct btd_adapter *adapter = data; + struct oob_request *oob_request; + struct oob_handler *handler; + + if (find_oob_request(adapter)) + return btd_error_in_progress(msg); + + if (btd_adapter_read_local_oob_data(adapter)) + return btd_error_failed(msg, "Request failed."); + + oob_request = g_new(struct oob_request, 1); + oob_request->adapter = adapter; + oob_requests = g_slist_append(oob_requests, oob_request); + oob_request->msg = dbus_message_ref(msg); + + handler = g_new0(struct oob_handler, 1); + handler->read_local_cb = read_local_data_complete; + + btd_adapter_set_oob_handler(oob_request->adapter, handler); + + return NULL; +} + +static DBusMessage *add_remote_data(DBusConnection *conn, DBusMessage *msg, + void *data) +{ + struct btd_adapter *adapter = data; + const char *addr = NULL; + uint8_t *hash192 = NULL; + uint8_t *randomizer192 = NULL; + int32_t h192_len = 0; + int32_t r192_len = 0; + uint8_t *hash256 = NULL; + uint8_t *randomizer256 = NULL; + int32_t h256_len = 0; + int32_t r256_len = 0; + bdaddr_t bdaddr; + uint8_t addr_type = 0; + bool valid_len; + + if (!dbus_message_get_args(msg, NULL, + DBUS_TYPE_STRING, &addr, + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &hash192, &h192_len, + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &randomizer192, &r192_len, + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &hash256, &h256_len, + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &randomizer256, &r256_len, + DBUS_TYPE_INVALID)) + return btd_error_invalid_args(msg); + + valid_len = (h192_len == 16 && r192_len == 16) || + (h256_len == 16 && r256_len == 16); + + if (!valid_len || bachk(addr)) + return btd_error_invalid_args(msg); + + str2ba(addr, &bdaddr); + + if (btd_adapter_add_remote_oob_ext_data(adapter, &bdaddr, addr_type, + hash192, randomizer192, + hash256, randomizer256)) + return btd_error_failed(msg, "Request failed"); + + return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); +} + +static DBusMessage *remove_remote_data(DBusConnection *conn, DBusMessage *msg, + void *data) +{ + struct btd_adapter *adapter = data; + const char *addr; + bdaddr_t bdaddr; + uint8_t addr_type = 0; + + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &addr, + DBUS_TYPE_INVALID)) + return btd_error_invalid_args(msg); + + if (bachk(addr)) + return btd_error_invalid_args(msg); + + str2ba(addr, &bdaddr); + + if (btd_adapter_remove_remote_oob_ext_data(adapter, &bdaddr, addr_type)) + return btd_error_failed(msg, "Request failed"); + + return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); +} + +static const GDBusMethodTable oob_methods[] = { + { GDBUS_METHOD("AddRemoteData", + GDBUS_ARGS({ "address", "s" }, + { "hash192", "ay" }, { "randomizer192", "ay" }, + { "hash256", "ay" }, { "randomizer256", "ay" }), + NULL, + add_remote_data) }, + { GDBUS_METHOD("RemoveRemoteData", + GDBUS_ARGS({ "address", "s" }), NULL, + remove_remote_data) }, + { GDBUS_ASYNC_METHOD("ReadLocalData", + NULL, GDBUS_ARGS( + {"hash192", "ay" }, { "randomizer192", "ay" }, + {"hash256", "ay" }, { "randomizer256", "ay" }), + read_local_data) }, + { } +}; + +static int oob_probe(struct btd_adapter *adapter) +{ + const char *path = adapter_get_path(adapter); + + DBG("dbusoob probe"); + DBG("adapter path: %s", path); + + if (!g_dbus_register_interface(connection, path, OOB_INTERFACE, + oob_methods, NULL, NULL, adapter, NULL)) { + error("OOB interface init failed on path %s", path); + return -EIO; + } + + return 0; +} + +static void oob_remove(struct btd_adapter *adapter) +{ + read_local_data_complete(adapter, NULL, NULL, NULL, NULL, NULL); + + g_dbus_unregister_interface(connection, adapter_get_path(adapter), + OOB_INTERFACE); +} + +static struct btd_adapter_driver oob_driver = { + .name = "oob", + .probe = oob_probe, + .remove = oob_remove, +}; + +static int dbusoob_init(void) +{ + DBG("Setup dbusoob plugin"); + + connection = btd_get_dbus_connection(); + + return btd_register_adapter_driver(&oob_driver); +} + +static void dbusoob_exit(void) +{ + DBG("Cleanup dbusoob plugin"); + + btd_unregister_adapter_driver(&oob_driver); +} + +BLUETOOTH_PLUGIN_DEFINE(dbusoob, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, + dbusoob_init, dbusoob_exit) +#endif diff --git a/plugins/neard.c b/plugins/neard.c index cabcf34..51e276e 100755 --- a/plugins/neard.c +++ b/plugins/neard.c @@ -259,9 +259,16 @@ static DBusMessage *create_request_oob_reply(struct btd_adapter *adapter, return reply; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static void read_local_complete(struct btd_adapter *adapter, + const uint8_t *hash, const uint8_t *randomizer, + const uint8_t *hash256, const uint8_t *randomizer256, + void *user_data) +#else static void read_local_complete(struct btd_adapter *adapter, const uint8_t *hash, const uint8_t *randomizer, void *user_data) +#endif { DBusMessage *msg = user_data; DBusMessage *reply; diff --git a/plugins/policy.c b/plugins/policy.c index c9a7c84..9fdee00 100755 --- a/plugins/policy.c +++ b/plugins/policy.c @@ -45,12 +45,21 @@ #include "src/profile.h" #include "src/hcid.h" +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#define CONTROL_CONNECT_TIMEOUT 4 +#define TARGET_CONNECT_TIMEOUT 1 +#else #define CONTROL_CONNECT_TIMEOUT 2 +#endif #define SOURCE_RETRY_TIMEOUT 2 #define SINK_RETRY_TIMEOUT SOURCE_RETRY_TIMEOUT #define CT_RETRY_TIMEOUT 1 #define TG_RETRY_TIMEOUT CT_RETRY_TIMEOUT +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#define SOURCE_RETRIES 0 +#else #define SOURCE_RETRIES 1 +#endif #define SINK_RETRIES SOURCE_RETRIES #define CT_RETRIES 1 #define TG_RETRIES CT_RETRIES @@ -287,7 +296,15 @@ static void sink_cb(struct btd_service *service, btd_service_state_t old_state, * immediatelly otherwise set timer */ if (old_state == BTD_SERVICE_STATE_CONNECTING) +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + /* Set timer as most of the devices initiate + * avrcp connection immediately; irrespective of local + * or remote initiated a2dp connection + */ + policy_set_ct_timer(data, CONTROL_CONNECT_TIMEOUT); +#else policy_connect(data, controller); +#endif else if (btd_service_get_state(controller) != BTD_SERVICE_STATE_CONNECTED) policy_set_ct_timer(data, CONTROL_CONNECT_TIMEOUT); @@ -317,8 +334,15 @@ static void policy_set_tg_timer(struct policy_data *data, int timeout) if (data->tg_timer > 0) g_source_remove(data->tg_timer); + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + data->tg_timer = g_timeout_add_seconds(TARGET_CONNECT_TIMEOUT, + policy_connect_tg, + data); +#else data->tg_timer = g_timeout_add_seconds(timeout, policy_connect_tg, data); +#endif } static gboolean policy_connect_source(gpointer user_data) @@ -386,9 +410,13 @@ static void source_cb(struct btd_service *service, if (data->tg_timer > 0) { g_source_remove(data->tg_timer); data->tg_timer = 0; +#if defined TIZEN_FEATURE_BLUEZ_MODIFY && defined BT_QUALIFICATION + } +#else } else if (btd_service_get_state(target) != BTD_SERVICE_STATE_DISCONNECTED) policy_disconnect(data, target); +#endif break; case BTD_SERVICE_STATE_CONNECTING: break; diff --git a/profile.h b/profile.h new file mode 100755 index 0000000..697303d --- /dev/null +++ b/profile.h @@ -0,0 +1,164 @@ +/* + * Copyright (c) 2016 Samsung Electronics Co., Ltd All Rights Reserved + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifndef __TIZEN_PROFILE_H__ +#define __TIZEN_PROFILE_H__ + +#include + +#include +#include +#include +#include + +#define MODEL_CONFIG_FILE "/etc/config/model-config.xml" +#define TYPE_FIELD "string" +#define FEATURE_TAG "platform" +#define MODEL_CONFIG_TAG "model-config" + +typedef enum { + TIZEN_PROFILE_UNKNOWN = 0, + TIZEN_PROFILE_MOBILE = 0x1, + TIZEN_PROFILE_WEARABLE = 0x2, + TIZEN_PROFILE_TV = 0x4, + TIZEN_PROFILE_IVI = 0x8, + TIZEN_PROFILE_COMMON = 0x10, +} tizen_profile_t; + +static tizen_profile_t profile = TIZEN_PROFILE_UNKNOWN; + +static inline int __get_profile_from_model_config_xml(const char *field, char **value) +{ + char *node_name = NULL; + char *node_value = NULL; + xmlNode *cur_node = NULL; + xmlNodePtr cur_ptr = NULL; + xmlNodePtr model_ptr = NULL; + xmlDocPtr xml_doc = NULL; + + xml_doc = xmlParseFile(MODEL_CONFIG_FILE); + if (xml_doc == NULL) + return -1; + + cur_ptr = xmlDocGetRootElement(xml_doc); + if (cur_ptr == NULL) { + xmlFreeDoc(xml_doc); + return -1; + } + + for (cur_node = cur_ptr; cur_node; cur_node = cur_node->next) { + if (!xmlStrcmp(cur_ptr->name, (const xmlChar*)MODEL_CONFIG_TAG)) + break; + } + + if (cur_ptr == NULL) { + xmlFreeDoc(xml_doc); + return -1; + } + + cur_ptr = cur_ptr->xmlChildrenNode; + for (cur_node = cur_ptr; cur_node; cur_node = cur_node->next) { + if (!xmlStrcmp(cur_node->name, (const xmlChar*)FEATURE_TAG)) { + model_ptr = cur_node; + break; + } + } + + if (model_ptr == NULL) { + xmlFreeDoc(xml_doc); + return -1; + } + + if (model_ptr) { + cur_ptr = model_ptr->xmlChildrenNode; + + for (cur_node = cur_ptr; cur_node; cur_node = cur_node->next) { + if (cur_node->type == XML_ELEMENT_NODE) { + node_name = (char *)xmlGetProp(cur_node, (const xmlChar*)"name"); + + if (!strncmp(node_name, field, strlen(node_name))) { + node_value = (char *)xmlNodeListGetString(xml_doc, cur_node->xmlChildrenNode, 1); + if (node_value) { + *value = strdup(node_value); + free(node_name); + free(node_value); + break; + } + } + free(node_name); + } + } + } + + xmlFreeDoc(xml_doc); + return 0; +} + +static inline tizen_profile_t _get_tizen_profile(void) +{ + char *profile_name = NULL; + + if (__builtin_expect(profile != TIZEN_PROFILE_UNKNOWN, 1)) + return profile; + + if (__get_profile_from_model_config_xml("tizen.org/feature/profile", + &profile_name) < 0) { + profile = TIZEN_PROFILE_MOBILE; + return profile; + } + + if (profile_name == NULL) { + profile = TIZEN_PROFILE_MOBILE; + return profile; + } + + switch (*profile_name) { + case 'm': + case 'M': + profile = TIZEN_PROFILE_MOBILE; + break; + case 'w': + case 'W': + profile = TIZEN_PROFILE_WEARABLE; + break; + case 't': + case 'T': + profile = TIZEN_PROFILE_TV; + break; + case 'i': + case 'I': + profile = TIZEN_PROFILE_IVI; + break; + default: /* common or unknown ==> ALL ARE COMMON */ + profile = TIZEN_PROFILE_COMMON; + } + free(profile_name); + + return profile; +} + +#define TIZEN_FEATURE_BLUEZ_BRCM_CHIP ((_get_tizen_profile()) == TIZEN_PROFILE_IVI) +#define TIZEN_FEATURE_BLUEZ_SMS_ONLY ((_get_tizen_profile()) == TIZEN_PROFILE_WEARABLE) +#define TIZEN_FEATURE_BLUEZ_BRCM_QOS ((_get_tizen_profile()) == TIZEN_PROFILE_WEARABLE) +#define TIZEN_FEATURE_BLUEZ_ROLE_CHANGE ((_get_tizen_profile()) == TIZEN_PROFILE_WEARABLE) +#define TIZEN_FEATURE_BLUEZ_CONFIRM_ONLY ((_get_tizen_profile()) == TIZEN_PROFILE_WEARABLE) + + +#endif /* __TIZEN_PROFILE_H__ */ + diff --git a/profiles/alert/server.c b/profiles/alert/server.c new file mode 100755 index 0000000..2f6e3cd --- /dev/null +++ b/profiles/alert/server.c @@ -0,0 +1,1044 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2011 Nokia Corporation + * Copyright (C) 2011 Marcel Holtmann + * + * + * 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 +#endif + +#include +#include +#include +#include +#include +#include + +#include + +#include "lib/bluetooth.h" +#include "lib/hci.h" +#include "lib/hci_lib.h" +#include "lib/sdp.h" +#include "lib/uuid.h" + +#include "gdbus/gdbus.h" + +#include "src/plugin.h" +#include "src/dbus-common.h" +#include "attrib/att.h" +#include "src/adapter.h" +#include "src/device.h" +#include "attrib/att-database.h" +#include "src/log.h" +#include "attrib/gatt-service.h" +#include "attrib/gattrib.h" +#include "src/attrib-server.h" +#include "attrib/gatt.h" +#include "src/profile.h" +#include "src/error.h" +#include "src/textfile.h" +#include "src/attio.h" + +#define PHONE_ALERT_STATUS_SVC_UUID 0x180E +#define ALERT_NOTIF_SVC_UUID 0x1811 + +#define ALERT_STATUS_CHR_UUID 0x2A3F +#define RINGER_CP_CHR_UUID 0x2A40 +#define RINGER_SETTING_CHR_UUID 0x2A41 + +#define ALERT_NOTIF_CP_CHR_UUID 0x2A44 +#define UNREAD_ALERT_CHR_UUID 0x2A45 +#define NEW_ALERT_CHR_UUID 0x2A46 +#define SUPP_NEW_ALERT_CAT_CHR_UUID 0x2A47 +#define SUPP_UNREAD_ALERT_CAT_CHR_UUID 0x2A48 + +#define ALERT_OBJECT_PATH "/org/bluez" +#define ALERT_INTERFACE "org.bluez.Alert1" +#define ALERT_AGENT_INTERFACE "org.bluez.AlertAgent1" + +/* Maximum length for "Text String Information" */ +#define NEW_ALERT_MAX_INFO_SIZE 18 +/* Maximum length for New Alert Characteristic Value */ +#define NEW_ALERT_CHR_MAX_VALUE_SIZE (NEW_ALERT_MAX_INFO_SIZE + 2) + +enum { + ENABLE_NEW_INCOMING, + ENABLE_UNREAD_CAT, + DISABLE_NEW_INCOMING, + DISABLE_UNREAD_CAT, + NOTIFY_NEW_INCOMING, + NOTIFY_UNREAD_CAT, +}; + +enum { + RINGER_SILENT_MODE = 1, + RINGER_MUTE_ONCE, + RINGER_CANCEL_SILENT_MODE, +}; + +/* Ringer Setting characteristic values */ +enum { + RINGER_SILENT, + RINGER_NORMAL, +}; + +enum notify_type { + NOTIFY_RINGER_SETTING = 0, + NOTIFY_ALERT_STATUS, + NOTIFY_NEW_ALERT, + NOTIFY_UNREAD_ALERT, + NOTIFY_SIZE, +}; + +struct alert_data { + const char *category; + char *srv; + char *path; + guint watcher; +}; + +struct alert_adapter { + struct btd_adapter *adapter; + uint16_t supp_new_alert_cat_handle; + uint16_t supp_unread_alert_cat_handle; + uint16_t hnd_ccc[NOTIFY_SIZE]; + uint16_t hnd_value[NOTIFY_SIZE]; +}; + +struct notify_data { + struct alert_adapter *al_adapter; + enum notify_type type; + uint8_t *value; + size_t len; +}; + +struct notify_callback { + struct notify_data *notify_data; + struct btd_device *device; + guint id; +}; + +static GSList *registered_alerts = NULL; +static GSList *alert_adapters = NULL; +static uint8_t ringer_setting = RINGER_NORMAL; +static uint8_t alert_status = 0; + +static const char * const anp_categories[] = { + "simple", + "email", + "news", + "call", + "missed-call", + "sms-mms", + "voice-mail", + "schedule", + "high-priority", + "instant-message", +}; + +static const char * const pasp_categories[] = { + "ringer", + "vibrate", + "display", +}; + +static int adapter_cmp(gconstpointer a, gconstpointer b) +{ + const struct alert_adapter *al_adapter = a; + const struct btd_adapter *adapter = b; + + return al_adapter->adapter == adapter ? 0 : -1; +} + +static struct alert_adapter *find_alert_adapter(struct btd_adapter *adapter) +{ + GSList *l = g_slist_find_custom(alert_adapters, adapter, adapter_cmp); + + return l ? l->data : NULL; +} + +static void alert_data_destroy(gpointer user_data) +{ + struct alert_data *alert = user_data; + + if (alert->watcher) + g_dbus_remove_watch(btd_get_dbus_connection(), alert->watcher); + + g_free(alert->srv); + g_free(alert->path); + g_free(alert); +} + +static void alert_release(gpointer user_data) +{ + struct alert_data *alert = user_data; + DBusMessage *msg; + + msg = dbus_message_new_method_call(alert->srv, alert->path, + ALERT_AGENT_INTERFACE, + "Release"); + if (msg) + g_dbus_send_message(btd_get_dbus_connection(), msg); + + alert_data_destroy(alert); +} + +static void alert_destroy(gpointer user_data) +{ + DBG(""); + + g_slist_free_full(registered_alerts, alert_release); + registered_alerts = NULL; +} + +static const char *valid_category(const char *category) +{ + unsigned i; + + for (i = 0; i < G_N_ELEMENTS(anp_categories); i++) { + if (g_str_equal(anp_categories[i], category)) + return anp_categories[i]; + } + + for (i = 0; i < G_N_ELEMENTS(pasp_categories); i++) { + if (g_str_equal(pasp_categories[i], category)) + return pasp_categories[i]; + } + + return NULL; +} + +static struct alert_data *get_alert_data_by_category(const char *category) +{ + GSList *l; + struct alert_data *alert; + + for (l = registered_alerts; l; l = g_slist_next(l)) { + alert = l->data; + if (g_str_equal(alert->category, category)) + return alert; + } + + return NULL; +} + +static gboolean registered_category(const char *category) +{ + struct alert_data *alert; + + alert = get_alert_data_by_category(category); + if (alert) + return TRUE; + + return FALSE; +} + +static gboolean pasp_category(const char *category) +{ + unsigned i; + + for (i = 0; i < G_N_ELEMENTS(pasp_categories); i++) + if (g_str_equal(category, pasp_categories[i])) + return TRUE; + + return FALSE; +} + +static gboolean valid_description(const char *category, + const char *description) +{ + if (!pasp_category(category)) { + if (strlen(description) >= NEW_ALERT_MAX_INFO_SIZE) + return FALSE; + + return TRUE; + } + + if (g_str_equal(description, "active") || + g_str_equal(description, "not active")) + return TRUE; + + if (g_str_equal(category, "ringer")) + if (g_str_equal(description, "enabled") || + g_str_equal(description, "disabled")) + return TRUE; + + return FALSE; +} + +static gboolean valid_count(const char *category, uint16_t count) +{ + if (!pasp_category(category) && count > 0 && count <= 255) + return TRUE; + + if (pasp_category(category) && count == 1) + return TRUE; + + return FALSE; +} + +static void update_supported_categories(gpointer data, gpointer user_data) +{ + struct alert_adapter *al_adapter = data; + struct btd_adapter *adapter = al_adapter->adapter; + uint8_t value[2]; + unsigned int i; + + memset(value, 0, sizeof(value)); + + for (i = 0; i < G_N_ELEMENTS(anp_categories); i++) { + if (registered_category(anp_categories[i])) + hci_set_bit(i, value); + } + + attrib_db_update(adapter, al_adapter->supp_new_alert_cat_handle, NULL, + value, sizeof(value), NULL); + + /* FIXME: For now report all registered categories as supporting unread + * status, until it is known which ones should be supported */ + attrib_db_update(adapter, al_adapter->supp_unread_alert_cat_handle, + NULL, value, sizeof(value), NULL); +} + +static void watcher_disconnect(DBusConnection *conn, void *user_data) +{ + struct alert_data *alert = user_data; + + DBG("Category %s was disconnected", alert->category); + + registered_alerts = g_slist_remove(registered_alerts, alert); + alert_data_destroy(alert); + + g_slist_foreach(alert_adapters, update_supported_categories, NULL); +} + +static gboolean is_notifiable_device(struct btd_device *device, uint16_t ccc) +{ + char *filename; + GKeyFile *key_file; + char handle[6]; + char *str; + uint16_t val; + gboolean result; + + sprintf(handle, "%hu", ccc); + + filename = btd_device_get_storage_path(device, "ccc"); + if (!filename) { + warn("Unable to get ccc storage path for device"); + return FALSE; + } + + key_file = g_key_file_new(); + g_key_file_load_from_file(key_file, filename, 0, NULL); + + str = g_key_file_get_string(key_file, handle, "Value", NULL); + if (!str) { + result = FALSE; + goto end; + } + + val = strtol(str, NULL, 16); + if (!(val & 0x0001)) { + result = FALSE; + goto end; + } + + result = TRUE; +end: + g_free(str); + g_free(filename); + g_key_file_free(key_file); + + return result; +} + +static void destroy_notify_callback(guint8 status, const guint8 *pdu, guint16 len, + gpointer user_data) +{ + struct notify_callback *cb = user_data; + + DBG("status=%#x", status); + + btd_device_remove_attio_callback(cb->device, cb->id); + btd_device_unref(cb->device); + g_free(cb->notify_data->value); + g_free(cb->notify_data); + g_free(cb); +} + +static void attio_connected_cb(GAttrib *attrib, gpointer user_data) +{ + struct notify_callback *cb = user_data; + struct notify_data *nd = cb->notify_data; + enum notify_type type = nd->type; + struct alert_adapter *al_adapter = nd->al_adapter; + size_t len; + uint8_t *pdu = g_attrib_get_buffer(attrib, &len); + + + switch (type) { + case NOTIFY_RINGER_SETTING: + len = enc_notification(al_adapter->hnd_value[type], + &ringer_setting, sizeof(ringer_setting), + pdu, len); + break; + case NOTIFY_ALERT_STATUS: + len = enc_notification(al_adapter->hnd_value[type], + &alert_status, sizeof(alert_status), + pdu, len); + break; + case NOTIFY_NEW_ALERT: + case NOTIFY_UNREAD_ALERT: + len = enc_notification(al_adapter->hnd_value[type], + nd->value, nd->len, pdu, len); + break; + case NOTIFY_SIZE: + default: + DBG("Unknown type, could not send notification"); + goto end; + } + + DBG("Send notification for handle: 0x%04x, ccc: 0x%04x", + al_adapter->hnd_value[type], + al_adapter->hnd_ccc[type]); + + g_attrib_send(attrib, 0, pdu, len, destroy_notify_callback, cb, NULL); + + return; + +end: + btd_device_remove_attio_callback(cb->device, cb->id); + btd_device_unref(cb->device); + g_free(cb->notify_data->value); + g_free(cb->notify_data); + g_free(cb); +} + +static void filter_devices_notify(struct btd_device *device, void *user_data) +{ + struct notify_data *notify_data = user_data; + struct alert_adapter *al_adapter = notify_data->al_adapter; + enum notify_type type = notify_data->type; + struct notify_callback *cb; + + if (!is_notifiable_device(device, al_adapter->hnd_ccc[type])) + return; + + cb = g_new0(struct notify_callback, 1); + cb->notify_data = notify_data; + cb->device = btd_device_ref(device); + cb->id = btd_device_add_attio_callback(device, + attio_connected_cb, NULL, cb); +} + +static void notify_devices(struct alert_adapter *al_adapter, + enum notify_type type, uint8_t *value, size_t len) +{ + struct notify_data *notify_data; + + notify_data = g_new0(struct notify_data, 1); + notify_data->al_adapter = al_adapter; + notify_data->type = type; + notify_data->value = g_memdup(value, len); + notify_data->len = len; + + btd_adapter_for_each_device(al_adapter->adapter, filter_devices_notify, + notify_data); +} + +static void pasp_notification(enum notify_type type) +{ + GSList *it; + struct alert_adapter *al_adapter; + + for (it = alert_adapters; it; it = g_slist_next(it)) { + al_adapter = it->data; + + notify_devices(al_adapter, type, NULL, 0); + } +} + +static DBusMessage *register_alert(DBusConnection *conn, DBusMessage *msg, + void *data) +{ + const char *sender = dbus_message_get_sender(msg); + char *path; + const char *category; + const char *c; + struct alert_data *alert; + + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &c, + DBUS_TYPE_OBJECT_PATH, &path, DBUS_TYPE_INVALID)) + return btd_error_invalid_args(msg); + + category = valid_category(c); + if (!category) { + DBG("Invalid category: %s", c); + return btd_error_invalid_args(msg); + } + + if (registered_category(category)) { + DBG("Category %s already registered", category); + return dbus_message_new_method_return(msg); + } + + alert = g_new0(struct alert_data, 1); + alert->srv = g_strdup(sender); + alert->path = g_strdup(path); + alert->category = category; + alert->watcher = g_dbus_add_disconnect_watch(conn, alert->srv, + watcher_disconnect, alert, NULL); + + if (alert->watcher == 0) { + alert_data_destroy(alert); + DBG("Could not register disconnect watcher"); + return btd_error_failed(msg, + "Could not register disconnect watcher"); + } + + registered_alerts = g_slist_append(registered_alerts, alert); + + g_slist_foreach(alert_adapters, update_supported_categories, NULL); + + DBG("RegisterAlert(\"%s\", \"%s\")", alert->category, alert->path); + + return dbus_message_new_method_return(msg); +} + +static void update_new_alert(gpointer data, gpointer user_data) +{ + struct alert_adapter *al_adapter = data; + struct btd_adapter *adapter = al_adapter->adapter; + uint8_t *value = user_data; + + attrib_db_update(adapter, al_adapter->hnd_value[NOTIFY_NEW_ALERT], NULL, + &value[1], value[0], NULL); + + notify_devices(al_adapter, NOTIFY_NEW_ALERT, &value[1], value[0]); +} + +static void update_phone_alerts(const char *category, const char *description) +{ + unsigned int i; + + if (g_str_equal(category, "ringer")) { + if (g_str_equal(description, "enabled")) { + ringer_setting = RINGER_NORMAL; + pasp_notification(NOTIFY_RINGER_SETTING); + return; + } else if (g_str_equal(description, "disabled")) { + ringer_setting = RINGER_SILENT; + pasp_notification(NOTIFY_RINGER_SETTING); + return; + } + } + + for (i = 0; i < G_N_ELEMENTS(pasp_categories); i++) { + if (g_str_equal(pasp_categories[i], category)) { + if (g_str_equal(description, "active")) { + alert_status |= (1 << i); + pasp_notification(NOTIFY_ALERT_STATUS); + } else if (g_str_equal(description, "not active")) { + alert_status &= ~(1 << i); + pasp_notification(NOTIFY_ALERT_STATUS); + } + break; + } + } +} + +static DBusMessage *new_alert(DBusConnection *conn, DBusMessage *msg, + void *data) +{ + const char *sender = dbus_message_get_sender(msg); + const char *category, *description; + struct alert_data *alert; + uint16_t count; + unsigned int i; + size_t dlen; + + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &category, + DBUS_TYPE_UINT16, &count, DBUS_TYPE_STRING, + &description, DBUS_TYPE_INVALID)) + return btd_error_invalid_args(msg); + + alert = get_alert_data_by_category(category); + if (!alert) { + DBG("Category %s not registered", category); + return btd_error_invalid_args(msg); + } + + if (!g_str_equal(alert->srv, sender)) { + DBG("Sender %s is not registered in category %s", sender, + category); + return btd_error_invalid_args(msg); + } + + if (!valid_description(category, description)) { + DBG("Description %s is invalid for %s category", + description, category); + return btd_error_invalid_args(msg); + } + + if (!valid_count(category, count)) { + DBG("Count %d is invalid for %s category", count, category); + return btd_error_invalid_args(msg); + } + + dlen = strlen(description); + + for (i = 0; i < G_N_ELEMENTS(anp_categories); i++) { + uint8_t value[NEW_ALERT_CHR_MAX_VALUE_SIZE + 1]; + uint8_t *ptr = value; + + if (!g_str_equal(anp_categories[i], category)) + continue; + + memset(value, 0, sizeof(value)); + + *ptr++ = 2; /* Attribute value size */ + *ptr++ = i; /* Category ID (mandatory) */ + *ptr++ = count; /* Number of New Alert (mandatory) */ + /* Text String Information (optional) */ + strncpy((char *) ptr, description, + NEW_ALERT_MAX_INFO_SIZE - 1); + + if (dlen > 0) + *value += dlen + 1; + + g_slist_foreach(alert_adapters, update_new_alert, value); + } + + if (pasp_category(category)) + update_phone_alerts(category, description); + + DBG("NewAlert(\"%s\", %d, \"%s\")", category, count, description); + + return dbus_message_new_method_return(msg); +} + +static int agent_ringer_mute_once(void) +{ + struct alert_data *alert; + DBusMessage *msg; + + alert = get_alert_data_by_category("ringer"); + if (!alert) { + DBG("Category ringer is not registered"); + return -EINVAL; + } + + msg = dbus_message_new_method_call(alert->srv, alert->path, + ALERT_AGENT_INTERFACE, "MuteOnce"); + if (!msg) + return -ENOMEM; + + dbus_message_set_no_reply(msg, TRUE); + g_dbus_send_message(btd_get_dbus_connection(), msg); + + return 0; +} + +static int agent_ringer_set_ringer(const char *mode) +{ + struct alert_data *alert; + DBusMessage *msg; + + alert = get_alert_data_by_category("ringer"); + if (!alert) { + DBG("Category ringer is not registered"); + return -EINVAL; + } + + msg = dbus_message_new_method_call(alert->srv, alert->path, + ALERT_AGENT_INTERFACE, "SetRinger"); + if (!msg) + return -ENOMEM; + + dbus_message_append_args(msg, DBUS_TYPE_STRING, &mode, + DBUS_TYPE_INVALID); + + dbus_message_set_no_reply(msg, TRUE); + g_dbus_send_message(btd_get_dbus_connection(), msg); + + return 0; +} + +static void update_unread_alert(gpointer data, gpointer user_data) +{ + struct alert_adapter *al_adapter = data; + struct btd_adapter *adapter = al_adapter->adapter; + uint8_t *value = user_data; + + attrib_db_update(adapter, + al_adapter->hnd_value[NOTIFY_UNREAD_ALERT], NULL, value, + 2, NULL); + + notify_devices(al_adapter, NOTIFY_UNREAD_ALERT, value, 2); +} + +static DBusMessage *unread_alert(DBusConnection *conn, DBusMessage *msg, + void *data) +{ + const char *sender = dbus_message_get_sender(msg); + struct alert_data *alert; + const char *category; + unsigned int i; + uint16_t count; + + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &category, + DBUS_TYPE_UINT16, &count, + DBUS_TYPE_INVALID)) + return btd_error_invalid_args(msg); + + alert = get_alert_data_by_category(category); + if (!alert) { + DBG("Category %s not registered", category); + return btd_error_invalid_args(msg); + } + + if (!valid_count(category, count)) { + DBG("Count %d is invalid for %s category", count, category); + return btd_error_invalid_args(msg); + } + + if (!g_str_equal(alert->srv, sender)) { + DBG("Sender %s is not registered in category %s", sender, + category); + return btd_error_invalid_args(msg); + } + + for (i = 0; i < G_N_ELEMENTS(anp_categories); i++) { + if (g_str_equal(anp_categories[i], category)) { + uint8_t value[2]; + + value[0] = i; /* Category ID */ + value[1] = count; /* Unread count */ + + g_slist_foreach(alert_adapters, update_unread_alert, + value); + } + } + + DBG("category %s, count %d", category, count); + + return dbus_message_new_method_return(msg); +} + +static uint8_t ringer_cp_write(struct attribute *a, + struct btd_device *device, + gpointer user_data) +{ + DBG("a = %p", a); + + if (a->len > 1) { + DBG("Invalid command size (%zu)", a->len); + return 0; + } + + switch (a->data[0]) { + case RINGER_SILENT_MODE: + DBG("Silent Mode"); + agent_ringer_set_ringer("disabled"); + break; + case RINGER_MUTE_ONCE: + DBG("Mute Once"); + agent_ringer_mute_once(); + break; + case RINGER_CANCEL_SILENT_MODE: + DBG("Cancel Silent Mode"); + agent_ringer_set_ringer("enabled"); + break; + default: + DBG("Invalid command (0x%02x)", a->data[0]); + } + + return 0; +} + +static uint8_t alert_status_read(struct attribute *a, + struct btd_device *device, + gpointer user_data) +{ + struct btd_adapter *adapter = user_data; + + DBG("a = %p", a); + + if (a->data == NULL || a->data[0] != alert_status) + attrib_db_update(adapter, a->handle, NULL, &alert_status, + sizeof(alert_status), NULL); + + return 0; +} + +static uint8_t ringer_setting_read(struct attribute *a, + struct btd_device *device, + gpointer user_data) +{ + struct btd_adapter *adapter = user_data; + + DBG("a = %p", a); + + if (a->data == NULL || a->data[0] != ringer_setting) + attrib_db_update(adapter, a->handle, NULL, &ringer_setting, + sizeof(ringer_setting), NULL); + + return 0; +} + +static void register_phone_alert_service(struct alert_adapter *al_adapter) +{ + bt_uuid_t uuid; + + bt_uuid16_create(&uuid, PHONE_ALERT_STATUS_SVC_UUID); + + /* Phone Alert Status Service */ + gatt_service_add(al_adapter->adapter, GATT_PRIM_SVC_UUID, &uuid, + /* Alert Status characteristic */ + GATT_OPT_CHR_UUID16, ALERT_STATUS_CHR_UUID, + GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ | + GATT_CHR_PROP_NOTIFY, + GATT_OPT_CHR_VALUE_CB, ATTRIB_READ, + alert_status_read, al_adapter->adapter, + GATT_OPT_CCC_GET_HANDLE, + &al_adapter->hnd_ccc[NOTIFY_ALERT_STATUS], + GATT_OPT_CHR_VALUE_GET_HANDLE, + &al_adapter->hnd_value[NOTIFY_ALERT_STATUS], + /* Ringer Control Point characteristic */ + GATT_OPT_CHR_UUID16, RINGER_CP_CHR_UUID, + GATT_OPT_CHR_PROPS, GATT_CHR_PROP_WRITE_WITHOUT_RESP, + GATT_OPT_CHR_VALUE_CB, ATTRIB_WRITE, + ringer_cp_write, NULL, + /* Ringer Setting characteristic */ + GATT_OPT_CHR_UUID16, RINGER_SETTING_CHR_UUID, + GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ | + GATT_CHR_PROP_NOTIFY, + GATT_OPT_CHR_VALUE_CB, ATTRIB_READ, + ringer_setting_read, al_adapter->adapter, + GATT_OPT_CCC_GET_HANDLE, + &al_adapter->hnd_ccc[NOTIFY_RINGER_SETTING], + GATT_OPT_CHR_VALUE_GET_HANDLE, + &al_adapter->hnd_value[NOTIFY_RINGER_SETTING], + GATT_OPT_INVALID); +} + +static uint8_t supp_new_alert_cat_read(struct attribute *a, + struct btd_device *device, + gpointer user_data) +{ + struct btd_adapter *adapter = user_data; + uint8_t value[] = { 0x00, 0x00 }; + + DBG("a = %p", a); + + if (a->data == NULL) + attrib_db_update(adapter, a->handle, NULL, value, sizeof(value), + NULL); + + return 0; +} + +static uint8_t supp_unread_alert_cat_read(struct attribute *a, + struct btd_device *device, + gpointer user_data) +{ + struct btd_adapter *adapter = user_data; + uint8_t value[] = { 0x00, 0x00 }; + + DBG("a = %p", a); + + if (a->data == NULL) + attrib_db_update(adapter, a->handle, NULL, value, sizeof(value), + NULL); + + return 0; +} + +static uint8_t alert_notif_cp_write(struct attribute *a, + struct btd_device *device, + gpointer user_data) +{ + DBG("a = %p", a); + + if (a->len < 2) + return 0; + + switch (a->data[0]) { + case ENABLE_NEW_INCOMING: + DBG("ENABLE_NEW_INCOMING: 0x%02x", a->data[1]); + break; + case ENABLE_UNREAD_CAT: + DBG("ENABLE_UNREAD_CAT: 0x%02x", a->data[1]); + break; + case DISABLE_NEW_INCOMING: + DBG("DISABLE_NEW_INCOMING: 0x%02x", a->data[1]); + break; + case DISABLE_UNREAD_CAT: + DBG("DISABLE_UNREAD_CAT: 0x%02x", a->data[1]); + break; + case NOTIFY_NEW_INCOMING: + DBG("NOTIFY_NEW_INCOMING: 0x%02x", a->data[1]); + break; + case NOTIFY_UNREAD_CAT: + DBG("NOTIFY_UNREAD_CAT: 0x%02x", a->data[1]); + break; + default: + DBG("0x%02x 0x%02x", a->data[0], a->data[1]); + } + + return 0; +} + +static void register_alert_notif_service(struct alert_adapter *al_adapter) +{ + bt_uuid_t uuid; + + bt_uuid16_create(&uuid, ALERT_NOTIF_SVC_UUID); + + /* Alert Notification Service */ + gatt_service_add(al_adapter->adapter, GATT_PRIM_SVC_UUID, &uuid, + /* Supported New Alert Category */ + GATT_OPT_CHR_UUID16, SUPP_NEW_ALERT_CAT_CHR_UUID, + GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ, + GATT_OPT_CHR_VALUE_CB, ATTRIB_READ, + supp_new_alert_cat_read, al_adapter->adapter, + GATT_OPT_CHR_VALUE_GET_HANDLE, + &al_adapter->supp_new_alert_cat_handle, + /* New Alert */ + GATT_OPT_CHR_UUID16, NEW_ALERT_CHR_UUID, + GATT_OPT_CHR_PROPS, GATT_CHR_PROP_NOTIFY, + GATT_OPT_CCC_GET_HANDLE, + &al_adapter->hnd_ccc[NOTIFY_NEW_ALERT], + GATT_OPT_CHR_VALUE_GET_HANDLE, + &al_adapter->hnd_value[NOTIFY_NEW_ALERT], + /* Supported Unread Alert Category */ + GATT_OPT_CHR_UUID16, SUPP_UNREAD_ALERT_CAT_CHR_UUID, + GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ, + GATT_OPT_CHR_VALUE_CB, ATTRIB_READ, + supp_unread_alert_cat_read, al_adapter->adapter, + GATT_OPT_CHR_VALUE_GET_HANDLE, + &al_adapter->supp_unread_alert_cat_handle, + /* Unread Alert Status */ + GATT_OPT_CHR_UUID16, UNREAD_ALERT_CHR_UUID, + GATT_OPT_CHR_PROPS, GATT_CHR_PROP_NOTIFY, + GATT_OPT_CCC_GET_HANDLE, + &al_adapter->hnd_ccc[NOTIFY_UNREAD_ALERT], + GATT_OPT_CHR_VALUE_GET_HANDLE, + &al_adapter->hnd_value[NOTIFY_UNREAD_ALERT], + /* Alert Notification Control Point */ + GATT_OPT_CHR_UUID16, ALERT_NOTIF_CP_CHR_UUID, + GATT_OPT_CHR_PROPS, GATT_CHR_PROP_WRITE, + GATT_OPT_CHR_VALUE_CB, ATTRIB_WRITE, + alert_notif_cp_write, NULL, + GATT_OPT_INVALID); +} + +static int alert_server_probe(struct btd_profile *p, + struct btd_adapter *adapter) +{ + struct alert_adapter *al_adapter; + + al_adapter = g_new0(struct alert_adapter, 1); + al_adapter->adapter = btd_adapter_ref(adapter); + + alert_adapters = g_slist_append(alert_adapters, al_adapter); + + register_phone_alert_service(al_adapter); + register_alert_notif_service(al_adapter); + + return 0; +} + +static void alert_server_remove(struct btd_profile *p, + struct btd_adapter *adapter) +{ + struct alert_adapter *al_adapter; + + al_adapter = find_alert_adapter(adapter); + if (!al_adapter) + return; + + alert_adapters = g_slist_remove(alert_adapters, al_adapter); + btd_adapter_unref(al_adapter->adapter); + + g_free(al_adapter); +} + +static struct btd_profile alert_profile = { + .name = "gatt-alert-server", + .adapter_probe = alert_server_probe, + .adapter_remove = alert_server_remove, +}; + +static const GDBusMethodTable alert_methods[] = { + { GDBUS_METHOD("RegisterAlert", + GDBUS_ARGS({ "category", "s" }, + { "agent", "o" }), NULL, + register_alert) }, + { GDBUS_METHOD("NewAlert", + GDBUS_ARGS({ "category", "s" }, + { "count", "q" }, + { "description", "s" }), NULL, + new_alert) }, + { GDBUS_METHOD("UnreadAlert", + GDBUS_ARGS({ "category", "s" }, { "count", "q" }), NULL, + unread_alert) }, + { } +}; + +static int alert_server_init(void) +{ + if (!g_dbus_register_interface(btd_get_dbus_connection(), + ALERT_OBJECT_PATH, ALERT_INTERFACE, + alert_methods, NULL, NULL, NULL, + alert_destroy)) { + error("D-Bus failed to register %s interface", + ALERT_INTERFACE); + return -EIO; + } + + return btd_profile_register(&alert_profile); +} + +static void alert_server_exit(void) +{ + btd_profile_unregister(&alert_profile); + + g_dbus_unregister_interface(btd_get_dbus_connection(), + ALERT_OBJECT_PATH, ALERT_INTERFACE); +} + +static int alert_init(void) +{ + return alert_server_init(); +} + +static void alert_exit(void) +{ + alert_server_exit(); +} + +BLUETOOTH_PLUGIN_DEFINE(alert, VERSION, + BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, + alert_init, alert_exit) diff --git a/profiles/audio/a2dp.c b/profiles/audio/a2dp.c index db0736d..5877219 100755 --- a/profiles/audio/a2dp.c +++ b/profiles/audio/a2dp.c @@ -72,6 +72,9 @@ struct a2dp_sep { struct avdtp *session; struct avdtp_stream *stream; guint suspend_timer; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + gboolean remote_suspended; +#endif gboolean delay_reporting; gboolean locked; gboolean suspending; @@ -207,11 +210,21 @@ static void finalize_setup_errno(struct a2dp_setup *s, int err, { GSourceFunc finalize; va_list args; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + struct avdtp_error *avdtp_err; +#else struct avdtp_error avdtp_err; +#endif if (err < 0) { +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + avdtp_err = g_new(struct avdtp_error, 1); + avdtp_error_init(avdtp_err, AVDTP_ERRNO, -err); + s->err = avdtp_err; +#else avdtp_error_init(&avdtp_err, AVDTP_ERRNO, -err); s->err = &avdtp_err; +#endif } va_start(args, cb1); @@ -420,6 +433,13 @@ static void stream_state_changed(struct avdtp_stream *stream, return; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (new_state == AVDTP_STATE_STREAMING && sep->suspend_timer) { + g_source_remove(sep->suspend_timer); + sep->suspend_timer = 0; + } +#endif + if (new_state != AVDTP_STATE_IDLE) return; @@ -844,12 +864,25 @@ static gboolean start_ind(struct avdtp *session, struct avdtp_local_sep *sep, else DBG("Source %p: Start_Ind", sep); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (!a2dp_sep->locked) { + a2dp_sep->session = avdtp_ref(session); + if(a2dp_sep->remote_suspended == FALSE) + a2dp_sep->suspend_timer = g_timeout_add_seconds(SUSPEND_TIMEOUT, + (GSourceFunc) suspend_timeout, + a2dp_sep); + else + a2dp_sep->remote_suspended = FALSE; + } +#else + if (!a2dp_sep->locked) { a2dp_sep->session = avdtp_ref(session); a2dp_sep->suspend_timer = g_timeout_add_seconds(SUSPEND_TIMEOUT, (GSourceFunc) suspend_timeout, a2dp_sep); } +#endif if (!a2dp_sep->starting) return TRUE; @@ -903,6 +936,10 @@ static gboolean suspend_ind(struct avdtp *session, struct avdtp_local_sep *sep, else DBG("Source %p: Suspend_Ind", sep); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + a2dp_sep->remote_suspended = TRUE; +#endif + if (a2dp_sep->suspend_timer) { g_source_remove(a2dp_sep->suspend_timer); a2dp_sep->suspend_timer = 0; @@ -1207,7 +1244,11 @@ static struct avdtp_sep_ind endpoint_ind = { .delayreport = endpoint_delayreport_ind, }; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static sdp_record_t *a2dp_record(uint8_t type, gboolean sink_enabled) +#else static sdp_record_t *a2dp_record(uint8_t type) +#endif { sdp_list_t *svclass_id, *pfseq, *apseq, *root; uuid_t root_uuid, l2cap_uuid, avdtp_uuid, a2dp_uuid; @@ -1216,7 +1257,22 @@ static sdp_record_t *a2dp_record(uint8_t type) sdp_record_t *record; sdp_data_t *psm, *version, *features; uint16_t lp = AVDTP_UUID; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + uint16_t a2dp_ver, avdtp_ver, feat; + if (sink_enabled) { + DBG("A2DP record for Sink role"); + a2dp_ver = 0x0102; + avdtp_ver = 0x0103; + feat = 0x0002; + } else { + DBG("A2DP record for Source role"); + a2dp_ver = 0x0102; + avdtp_ver = 0x0103; + feat = 0x0001; + } +#else uint16_t a2dp_ver = 0x0103, avdtp_ver = 0x0103, feat = 0x000f; +#endif record = sdp_record_alloc(); if (!record) @@ -1400,6 +1456,13 @@ struct avdtp *a2dp_avdtp_get(struct btd_device *device) return NULL; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (chan->auth_id) { + DBG("auth is already going..."); + return NULL; + } +#endif + if (chan->session) return avdtp_ref(chan->session); @@ -1524,6 +1587,18 @@ static void confirm_cb(GIOChannel *io, gpointer data) if (!device) goto drop; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +{ + gboolean restricted = FALSE; + + restricted = device_is_profile_restricted(device, A2DP_SINK_UUID); + if (restricted) { + DBG("A2DP is restricted"); + goto drop; + } +} +#endif + chan = queue_find(server->channels, match_by_device, device); if (chan) { struct a2dp_setup *setup; @@ -1564,13 +1639,34 @@ static bool a2dp_server_listen(struct a2dp_server *server) if (server->io) return true; - server->io = bt_io_listen(NULL, confirm_cb, server, NULL, &err, +#if defined(TIZEN_FEATURE_BLUEZ_MODIFY) + if (btd_adapter_get_a2dp_role(server->adapter) == BLUETOOTH_A2DP_SINK_ROLE) { + server->io = bt_io_listen(NULL, confirm_cb, server, NULL, &err, BT_IO_OPT_SOURCE_BDADDR, btd_adapter_get_address(server->adapter), BT_IO_OPT_PSM, AVDTP_PSM, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, + BT_IO_OPT_IMTU, 895, BT_IO_OPT_MASTER, true, BT_IO_OPT_INVALID); + } else { + server->io = bt_io_listen(NULL, confirm_cb, server, NULL, &err, + BT_IO_OPT_SOURCE_BDADDR, + btd_adapter_get_address(server->adapter), + BT_IO_OPT_PSM, AVDTP_PSM, + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, + BT_IO_OPT_MASTER, true, + BT_IO_OPT_INVALID); + } +#else + server->io = bt_io_listen(NULL, confirm_cb, server, NULL, &err, + BT_IO_OPT_SOURCE_BDADDR, + btd_adapter_get_address(server->adapter), + BT_IO_OPT_PSM, AVDTP_PSM, + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, + BT_IO_OPT_MASTER, true, + BT_IO_OPT_INVALID); +#endif if (server->io) return true; @@ -1697,7 +1793,14 @@ struct a2dp_sep *a2dp_add_sep(struct btd_adapter *adapter, uint8_t type, if (*record_id != 0) goto add; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (btd_adapter_get_a2dp_role(adapter) == BLUETOOTH_A2DP_SINK_ROLE) + record = a2dp_record(type, true); + else + record = a2dp_record(type, false); +#else record = a2dp_record(type); +#endif if (!record) { error("Unable to allocate new service record"); a2dp_unregister_sep(sep); @@ -1796,8 +1899,17 @@ done: static struct a2dp_sep *a2dp_find_sep(struct avdtp *session, GSList *list, const char *sender) { +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + struct a2dp_sep *selected_sep = NULL; +#endif + for (; list; list = list->next) { struct a2dp_sep *sep = list->data; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + struct avdtp_remote_sep *rsep; + struct avdtp_media_codec_capability *cap; + struct avdtp_service_capability *service; +#endif /* Use sender's endpoint if available */ if (sender) { @@ -1811,14 +1923,35 @@ static struct a2dp_sep *a2dp_find_sep(struct avdtp *session, GSList *list, continue; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + rsep = avdtp_find_remote_sep(session, sep->lsep); + if (rsep == NULL) + continue; + + service = avdtp_get_codec(rsep); + cap = (struct avdtp_media_codec_capability *) service->data; + + if (cap->media_codec_type != A2DP_CODEC_VENDOR) { + selected_sep = sep; + continue; + } +#else if (avdtp_find_remote_sep(session, sep->lsep) == NULL) continue; +#endif return sep; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (selected_sep) + return selected_sep; + else + return NULL; +#else return NULL; +#endif } static struct a2dp_sep *a2dp_select_sep(struct avdtp *session, uint8_t type, diff --git a/profiles/audio/avctp.c b/profiles/audio/avctp.c index 2a43d32..c223b01 100755 --- a/profiles/audio/avctp.c +++ b/profiles/audio/avctp.c @@ -321,16 +321,31 @@ static void send_key(int fd, uint16_t key, int pressed) static gboolean auto_release(gpointer user_data) { struct avctp *session = user_data; - - session->key.timer = 0; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + uint16_t op = session->key.op; +#endif DBG("AV/C: key press timeout"); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (op != KEY_FASTFORWARD && op != KEY_REWIND) { + session->key.timer = 0; + send_key(session->uinput, op, 0); + } else { + return TRUE; + } +#else + session->key.timer = 0; send_key(session->uinput, session->key.op, 0); +#endif return FALSE; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +extern void avrcp_stop_position_timer(void); +#endif + static void handle_press(struct avctp *session, uint16_t op) { if (session->key.timer > 0) { @@ -339,8 +354,9 @@ static void handle_press(struct avctp *session, uint16_t op) /* Only auto release if keys are different */ if (session->key.op == op) goto done; - +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY send_key(session->uinput, session->key.op, 0); +#endif } session->key.op = op; @@ -361,6 +377,9 @@ static void handle_release(struct avctp *session, uint16_t op) send_key(session->uinput, op, 0); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +extern void avrcp_stop_position_timer(void); +#endif static size_t handle_panel_passthrough(struct avctp *session, uint8_t transaction, uint8_t *code, @@ -415,9 +434,13 @@ static size_t handle_panel_passthrough(struct avctp *session, break; } - if (pressed) + if (pressed) { handle_press(session, key_map[i].uinput); - else +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (key_map[i].avc == AVC_REWIND) + avrcp_stop_position_timer(); +#endif + } else handle_release(session, key_map[i].uinput); break; @@ -791,7 +814,11 @@ static gboolean process_queue(void *user_data) return FALSE; chan->p = p; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + p->timeout = g_timeout_add_seconds(5, req_timeout, chan); +#else p->timeout = g_timeout_add_seconds(2, req_timeout, chan); +#endif return FALSE; @@ -1368,9 +1395,15 @@ static void avctp_control_confirm(struct avctp *session, GIOChannel *chan, src = btd_adapter_get_address(device_get_adapter(dev)); dst = device_get_address(dev); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + session->auth_id = btd_request_authorization(src, dst, + AVRCP_TARGET_UUID, + auth_cb, session); +#else session->auth_id = btd_request_authorization(src, dst, AVRCP_REMOTE_UUID, auth_cb, session); +#endif if (session->auth_id == 0) goto drop; @@ -1434,6 +1467,16 @@ static void avctp_confirm_cb(GIOChannel *chan, gpointer data) if (!device) return; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + char name[10]; + device_get_name(device, name, sizeof(name)); + DBG("name : %s", name); + if (g_str_equal(name, "PLT_M50")) { + DBG("Don't accept avrcp connection with this headset"); + return; + } +#endif + session = avctp_get_internal(device); if (session == NULL) return; @@ -1690,13 +1733,20 @@ static gboolean repeat_timeout(gpointer user_data) struct avctp *session = user_data; avctp_passthrough_release(session, session->key.op); +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY avctp_passthrough_press(session, session->key.op); return TRUE; +#else + return FALSE; +#endif } static void release_pressed(struct avctp *session) { +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (session->key.op != AVC_FAST_FORWARD && session->key.op != AVC_REWIND) +#endif avctp_passthrough_release(session, session->key.op); if (session->key.timer > 0) @@ -1748,6 +1798,23 @@ int avctp_send_passthrough(struct avctp *session, uint8_t op) return avctp_passthrough_press(session, op); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +int avctp_send_release_passthrough(struct avctp *session, uint8_t op) +{ + DBG("+"); + + if (op != AVC_FAST_FORWARD && op != AVC_REWIND) + return FALSE; + + /* Auto release if key pressed */ + if (session->key.timer > 0) + g_source_remove(session->key.timer); + session->key.timer = 0; + + DBG("-"); + return avctp_passthrough_release(session, op); +} +#endif int avctp_send_vendordep(struct avctp *session, uint8_t transaction, uint8_t code, uint8_t subunit, diff --git a/profiles/audio/avctp.h b/profiles/audio/avctp.h index 68a2735..90316ad 100755 --- a/profiles/audio/avctp.h +++ b/profiles/audio/avctp.h @@ -173,6 +173,9 @@ unsigned int avctp_register_browsing_pdu_handler(struct avctp *session, gboolean avctp_unregister_browsing_pdu_handler(unsigned int id); int avctp_send_passthrough(struct avctp *session, uint8_t op); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +int avctp_send_release_passthrough(struct avctp *session, uint8_t op); +#endif int avctp_send_vendordep(struct avctp *session, uint8_t transaction, uint8_t code, uint8_t subunit, uint8_t *operands, size_t operand_count); diff --git a/profiles/audio/avdtp.c b/profiles/audio/avdtp.c index 4ec9cca..bc5a735 100755 --- a/profiles/audio/avdtp.c +++ b/profiles/audio/avdtp.c @@ -41,6 +41,11 @@ #include "lib/sdp_lib.h" #include "lib/uuid.h" +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#include +#include +#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */ + #include "btio/btio.h" #include "src/log.h" #include "src/shared/util.h" @@ -48,9 +53,14 @@ #include "src/adapter.h" #include "src/device.h" +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#include "src/service.h" +#endif + #include "avdtp.h" #include "sink.h" #include "source.h" +#include "../../profile.h" #define AVDTP_PSM 25 @@ -85,7 +95,11 @@ static unsigned int seids; #define AVDTP_MSG_TYPE_ACCEPT 0x02 #define AVDTP_MSG_TYPE_REJECT 0x03 +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#define REQ_TIMEOUT 10 +#else #define REQ_TIMEOUT 6 +#endif #define ABORT_TIMEOUT 2 #define DISCONNECT_TIMEOUT 1 #define START_TIMEOUT 1 @@ -830,13 +844,23 @@ static void handle_transport_connect(struct avdtp *session, GIOChannel *io, goto proceed; DBG("sk %d, omtu %d, send buffer size %d", sk, omtu, buf_size); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + min_buf_size = omtu * 10; +#else min_buf_size = omtu * 2; +#endif if (buf_size < min_buf_size) { DBG("send buffer size to be increassed to %d", min_buf_size); set_send_buffer_size(sk, min_buf_size); } - +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + else { + DBG("send buffer size to be decreassed to %d", + min_buf_size); + set_send_buffer_size(sk, min_buf_size); + } +#endif proceed: if (!stream->open_acp && sep->cfm && sep->cfm->open) sep->cfm->open(session, sep, stream, NULL, sep->user_data); @@ -940,11 +964,182 @@ static void handle_unanswered_req(struct avdtp *session, pending_req_free(req); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static gboolean send_broadcom_a2dp_qos(const bdaddr_t *dst, gboolean qos_high) +{ + int dd; + int err = 0; + struct hci_conn_info_req *cr; + broadcom_qos_cp cp; + + dd = hci_open_dev(0); + + if (dd < 0) + return FALSE; + + cr = g_malloc0(sizeof(*cr) + sizeof(struct hci_conn_info)); + + cr->type = ACL_LINK; + bacpy(&cr->bdaddr, dst); + + err = ioctl(dd, HCIGETCONNINFO, cr); + if (err < 0) { + error("Fail to get HCIGETCOINFO"); + g_free(cr); + hci_close_dev(dd); + return FALSE; + } + + cp.handle = cr->conn_info->handle; + DBG("Handle %d", cp.handle); + g_free(cr); + + if (qos_high) + cp.priority = BRCM_QOS_PRIORITY_HIGH; + else + cp.priority = BRCM_QOS_PRIORITY_NORMAL; + + if (hci_send_cmd(dd, OGF_VENDOR_CMD, BROADCOM_QOS_CMD, + BROADCOM_QOS_CP_SIZE, &cp) < 0) { + hci_close_dev(dd); + return FALSE; + } + DBG("Send Broadcom Qos Patch %s", qos_high ? "High" : "Low"); + + hci_close_dev(dd); + + return TRUE; +} + +#ifdef TIZEN_FEATURE_BLUEZ_SPRD_QOS +static gboolean send_sprd_a2dp_qos(bdaddr_t *dst, gboolean qos_high) +{ + int dd; + int err = 0; + struct hci_conn_info_req *cr; + qos_setup_cp cp; + + dd = hci_open_dev(0); + + cr = g_malloc0(sizeof(*cr) + sizeof(struct hci_conn_info)); + + cr->type = ACL_LINK; + bacpy(&cr->bdaddr, dst); + + err = ioctl(dd, HCIGETCONNINFO, cr); + if (err < 0) { + error("Fail to get HCIGETCOINFO"); + g_free(cr); + hci_close_dev(dd); + return FALSE; + } + + cp.handle = cr->conn_info->handle; + cp.flags = 0x00; + DBG("Handle %d", cp.handle); + g_free(cr); + + if (qos_high) { + cp.qos.service_type = 0x02; + cp.qos.token_rate = 0X000000C8; + cp.qos.peak_bandwidth = 0X000000C8; + cp.qos.latency = 0x00000001; + cp.qos.delay_variation = 0xFFFFFFFF; + } else { + cp.qos.service_type = 0x01; + cp.qos.token_rate = 0X00000000; + cp.qos.peak_bandwidth = 0X00000000; + cp.qos.latency = 0x00000001; + cp.qos.delay_variation = 0xFFFFFFFF; + } + + if (hci_send_cmd(dd, OGF_LINK_POLICY, OCF_QOS_SETUP, + QOS_SETUP_CP_SIZE, &cp) < 0) { + hci_close_dev(dd); + return FALSE; + } + DBG("Send Spreadtrum Qos Patch %s", qos_high ? "High" : "Low"); + + hci_close_dev(dd); + + return TRUE; +} +#endif /* TIZEN_FEATURE_BLUEZ_SPRD_QOS */ + +static gboolean fix_role_to_master(const bdaddr_t *dst, gboolean fix_to_master) +{ + int dd; + int err = 0; + struct hci_conn_info_req *cr; + switch_role_cp sr_cp; + write_link_policy_cp lp_cp; + + dd = hci_open_dev(0); + if (dd < 0) { + error("hci_open_dev is failed"); + return FALSE; + } + + cr = g_malloc0(sizeof(*cr) + sizeof(struct hci_conn_info)); + + cr->type = ACL_LINK; + bacpy(&cr->bdaddr, dst); + err = ioctl(dd, HCIGETCONNINFO, cr); + if (err < 0) { + error("Fail to get HCIGETCOINFO : %d", err); + g_free(cr); + hci_close_dev(dd); + return FALSE; + } + + if (!(cr->conn_info->link_mode & HCI_LM_MASTER) && fix_to_master) { + DBG("Need to role switch"); + + bacpy(&sr_cp.bdaddr, dst); + sr_cp.role = 0x00; /* 0x00 : Master, 0x01 : Slave */ + if (hci_send_cmd(dd, OGF_LINK_POLICY, OCF_SWITCH_ROLE, + SWITCH_ROLE_CP_SIZE, &sr_cp) < 0) { + error("switch role is failed"); + g_free(cr); + hci_close_dev(dd); + return FALSE; + } + } + + lp_cp.handle = cr->conn_info->handle; + DBG("Handle %d", lp_cp.handle); + g_free(cr); + + lp_cp.policy = fix_to_master ? 0x00 : HCI_LP_SNIFF | HCI_LP_RSWITCH; + DBG("Request link policy : 0x%X", lp_cp.policy); + + if (hci_send_cmd(dd, OGF_LINK_POLICY, OCF_WRITE_LINK_POLICY, + WRITE_LINK_POLICY_CP_SIZE, &lp_cp) < 0) { + error("write link policy is failed : %d", lp_cp.policy); + hci_close_dev(dd); + return FALSE; + } + + hci_close_dev(dd); + + return TRUE; +} +#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */ + static void avdtp_sep_set_state(struct avdtp *session, struct avdtp_local_sep *sep, avdtp_state_t state) { struct avdtp_stream *stream = sep->stream; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + const bdaddr_t *dst; +#if defined(TIZEN_FEATURE_BLUEZ_SPRD_QOS) + dst = device_get_address(session->device); +#else + if (TIZEN_FEATURE_BLUEZ_BRCM_QOS || TIZEN_FEATURE_BLUEZ_ROLE_CHANGE) + dst = device_get_address(session->device); +#endif +#endif avdtp_state_t old_state; struct avdtp_error err, *err_ptr = NULL; GSList *l; @@ -975,12 +1170,38 @@ static void avdtp_sep_set_state(struct avdtp *session, break; case AVDTP_STATE_OPEN: stream->starting = FALSE; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (TIZEN_FEATURE_BLUEZ_BRCM_QOS) { + send_broadcom_a2dp_qos(dst, FALSE); + } else { +#if defined(TIZEN_FEATURE_BLUEZ_SPRD_QOS) + if (old_state == AVDTP_STATE_STREAMING) + send_sprd_a2dp_qos(dst, FALSE); +#endif + } + if (TIZEN_FEATURE_BLUEZ_ROLE_CHANGE) + fix_role_to_master(dst, FALSE); +#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */ + break; case AVDTP_STATE_STREAMING: if (stream->start_timer) { g_source_remove(stream->start_timer); stream->start_timer = 0; } + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (TIZEN_FEATURE_BLUEZ_BRCM_QOS) { + send_broadcom_a2dp_qos(dst, TRUE); + } else { +#if defined(TIZEN_FEATURE_BLUEZ_SPRD_QOS) + if (old_state == AVDTP_STATE_OPEN) + send_sprd_a2dp_qos(dst, TRUE); +#endif + } + if (TIZEN_FEATURE_BLUEZ_ROLE_CHANGE) + fix_role_to_master(dst, TRUE); +#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */ stream->open_acp = FALSE; break; case AVDTP_STATE_CLOSING: @@ -1020,7 +1241,11 @@ static void avdtp_sep_set_state(struct avdtp *session, } } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +void finalize_discovery(struct avdtp *session, int err) +#else static void finalize_discovery(struct avdtp *session, int err) +#endif { struct discover_callback *discover = session->discover; struct avdtp_error avdtp_err; @@ -1104,12 +1329,20 @@ static void avdtp_free(void *data) static void connection_lost(struct avdtp *session, int err) { char address[18]; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + struct btd_service *service; +#endif session = avdtp_ref(session); ba2str(device_get_address(session->device), address); DBG("Disconnected from %s", address); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + service = btd_device_get_service(session->device, A2DP_SINK_UUID); + if (service) + btd_service_connecting_complete(service, -err); +#endif g_slist_foreach(session->streams, (GFunc) release_stream, session); session->streams = NULL; @@ -1117,14 +1350,48 @@ static void connection_lost(struct avdtp *session, int err) avdtp_set_state(session, AVDTP_SESSION_STATE_DISCONNECTED); - avdtp_unref(session); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + DBG("%p: ref=%d", session, session->ref); + if (err != EIO && session->ref > 0) /* link loss*/ + return; +#endif + + avdtp_free(session); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static gboolean disconnect_acl_timeout(gpointer user_data) +{ + struct btd_device *device = user_data; + + DBG(""); + + btd_device_disconnect(device); + + return FALSE; +} +#endif + static gboolean disconnect_timeout(gpointer user_data) { struct avdtp *session = user_data; struct btd_service *service; gboolean stream_setup; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + struct btd_device *device = NULL; + struct btd_adapter *adapter = NULL; + const bdaddr_t *bdaddr = NULL; + + DBG(""); +#endif + +/* Fix : REVERSE_INULL */ +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (session->device == NULL) { + error("session device NOT found"); + return FALSE; + } +#endif session->dc_timer = 0; @@ -1143,19 +1410,83 @@ static gboolean disconnect_timeout(gpointer user_data) return FALSE; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (session->device) { + adapter = device_get_adapter(session->device); + bdaddr = device_get_address(session->device); + if (adapter && bdaddr) + device = btd_adapter_find_device(adapter, bdaddr, BDADDR_BREDR); + if (!device) + error("device is NOT found"); + } +#endif + connection_lost(session, ETIMEDOUT); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (device) + g_timeout_add(100, + disconnect_acl_timeout, + device); +#endif + return FALSE; } +#if defined TIZEN_FEATURE_BLUEZ_MODIFY +static void set_disconnect_timer_for_sink(struct avdtp *session, gboolean disconn) +{ + char name[6]; + + if (session->dc_timer) + remove_disconnect_timer(session); + + device_get_name(session->device, name, sizeof(name)); + DBG("name : [%s]", name); + if (g_str_equal(name, "VW BT") || g_str_equal(name, "VW MI") || + g_str_equal(name, "Seat ")) { + session->dc_timer = g_timeout_add_seconds(3, disconnect_timeout, + session); + } else if (g_str_equal(name, "CAR M")) { + session->dc_timer = g_timeout_add(200, disconnect_timeout, + session); + } else { + if (disconn == TRUE) + session->dc_timer = g_timeout_add(100, + disconnect_timeout, + session); + else + session->dc_timer = g_timeout_add_seconds(DISCONNECT_TIMEOUT, + disconnect_timeout, + session); + } +} +#endif + static void set_disconnect_timer(struct avdtp *session) { +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + char name[6]; +#endif if (session->dc_timer) remove_disconnect_timer(session); - session->dc_timer = g_timeout_add_seconds(DISCONNECT_TIMEOUT, - disconnect_timeout, - session); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + device_get_name(session->device, name, sizeof(name)); + DBG("name : [%s]", name); + if (g_str_equal(name, "VW BT") || g_str_equal(name, "VW MI") || + g_str_equal(name, "Seat ")) { + session->dc_timer = g_timeout_add_seconds(3, disconnect_timeout, + session); + } else if (g_str_equal(name, "CAR M")) { + session->dc_timer = g_timeout_add(200, disconnect_timeout, + session); + } else { + session->dc_timer = g_timeout_add_seconds(DISCONNECT_TIMEOUT, + disconnect_timeout, + session); + } +#endif } void avdtp_unref(struct avdtp *session) @@ -1164,6 +1495,10 @@ void avdtp_unref(struct avdtp *session) return; session->ref--; +#if defined TIZEN_FEATURE_BLUEZ_MODIFY + struct btd_adapter *adapter; + adapter = avdtp_get_adapter(session); +#endif DBG("%p: ref=%d", session, session->ref); @@ -1172,7 +1507,14 @@ void avdtp_unref(struct avdtp *session) switch (session->state) { case AVDTP_SESSION_STATE_CONNECTED: +#if defined TIZEN_FEATURE_BLUEZ_MODIFY + if (btd_adapter_get_a2dp_role(adapter) == BLUETOOTH_A2DP_SINK_ROLE) + set_disconnect_timer_for_sink(session, TRUE); + else + set_disconnect_timer(session); +#else set_disconnect_timer(session); +#endif break; case AVDTP_SESSION_STATE_CONNECTING: connection_lost(session, ECONNABORTED); @@ -2163,6 +2505,21 @@ static gboolean session_cb(GIOChannel *chan, GIOCondition cond, } if (session->in.message_type == AVDTP_MSG_TYPE_COMMAND) { +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + struct btd_service *service; + + service = btd_device_get_service(session->device, A2DP_SINK_UUID); + if (service != NULL) { + DBG("A2dp state %d", btd_service_get_state( + btd_device_get_service(session->device, A2DP_SINK_UUID))); + + if (btd_service_get_state(btd_device_get_service(session->device, + A2DP_SINK_UUID)) == BTD_SERVICE_STATE_DISCONNECTING) { + DBG("avdtp:%p , disconnect timer is going on", session); + return FALSE; + } + } +#endif if (!avdtp_parse_cmd(session, session->in.transaction, session->in.signal_id, session->in.buf, @@ -2272,6 +2629,10 @@ static void avdtp_connect_cb(GIOChannel *chan, GError *err, gpointer user_data) struct avdtp *session = user_data; char address[18]; int err_no = EIO; +#if defined TIZEN_FEATURE_BLUEZ_MODIFY + struct btd_adapter *adapter; + adapter = avdtp_get_adapter(session); +#endif if (err) { err_no = err->code; @@ -2320,8 +2681,16 @@ static void avdtp_connect_cb(GIOChannel *chan, GError *err, gpointer user_data) (GIOFunc) session_cb, session, NULL); - if (session->stream_setup) + if (session->stream_setup) { +#if defined TIZEN_FEATURE_BLUEZ_MODIFY + if (btd_adapter_get_a2dp_role(adapter) == BLUETOOTH_A2DP_SINK_ROLE) + set_disconnect_timer_for_sink(session, FALSE); + else + set_disconnect_timer(session); +#else set_disconnect_timer(session); +#endif + } } else if (session->pending_open) handle_transport_connect(session, chan, session->imtu, session->omtu); @@ -2385,10 +2754,26 @@ static GIOChannel *l2cap_connect(struct avdtp *session) GError *err = NULL; GIOChannel *io; const bdaddr_t *src; +#if defined TIZEN_FEATURE_BLUEZ_MODIFY + struct btd_adapter *adapter; + adapter = avdtp_get_adapter(session); +#endif src = btd_adapter_get_address(device_get_adapter(session->device)); - io = bt_io_connect(avdtp_connect_cb, session, +#if defined TIZEN_FEATURE_BLUEZ_MODIFY + if (btd_adapter_get_a2dp_role(adapter) == BLUETOOTH_A2DP_SINK_ROLE) { + io = bt_io_connect(avdtp_connect_cb, session, + NULL, &err, + BT_IO_OPT_SOURCE_BDADDR, src, + BT_IO_OPT_DEST_BDADDR, + device_get_address(session->device), + BT_IO_OPT_PSM, AVDTP_PSM, + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, + BT_IO_OPT_IMTU, 895, + BT_IO_OPT_INVALID); + } else { + io = bt_io_connect(avdtp_connect_cb, session, NULL, &err, BT_IO_OPT_SOURCE_BDADDR, src, BT_IO_OPT_DEST_BDADDR, @@ -2396,6 +2781,18 @@ static GIOChannel *l2cap_connect(struct avdtp *session) BT_IO_OPT_PSM, AVDTP_PSM, BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, BT_IO_OPT_INVALID); + } +#else + io = bt_io_connect(avdtp_connect_cb, session, + NULL, &err, + BT_IO_OPT_SOURCE_BDADDR, src, + BT_IO_OPT_DEST_BDADDR, + device_get_address(session->device), + BT_IO_OPT_PSM, AVDTP_PSM, + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_MEDIUM, + BT_IO_OPT_INVALID); +#endif + if (!io) { error("%s", err->message); g_error_free(err); @@ -2782,6 +3179,12 @@ static gboolean avdtp_abort_resp(struct avdtp *session, struct seid_rej *resp, int size) { struct avdtp_local_sep *sep = stream->lsep; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (!sep) { + error("Error in getting sep"); + return FALSE; + } +#endif avdtp_sep_set_state(session, sep, AVDTP_STATE_ABORTING); @@ -3403,11 +3806,44 @@ int avdtp_start(struct avdtp *session, struct avdtp_stream *stream) /* If timer already active wait it */ if (stream->start_timer) return 0; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + else { + char address[18]; + uint32_t timeout_sec = START_TIMEOUT; + + ba2str(device_get_address(session->device), address); + /* For Bose headset (Bose AE2w) 2 seconds timeout is required to avoid AVDTP ABORT_CMD */ + if (!strncasecmp(address, "00:0C:8A", 8)) + timeout_sec = 2; + /* For Gear Circle, HS3000 headset, this headset doesn't initiate start command and + * when we add timer for 1 second so idle may trigger callback after 1.2 sec or + * 1.5 sec. So, don't timer for this headset.*/ + if (!strncasecmp(address, "10:92:66", 8) || + !strncasecmp(address, "A8:9F:BA", 8) || + !strncasecmp(address, "00:26:B4", 8)) { + start_timeout(stream); + return 0; + } + /* Here we can't use Mac address as there are changing so check for name */ + char name[10]; + device_get_name(session->device, name, sizeof(name)); + DBG("name : %s", name); + if (g_str_equal(name, "HS3000")) { + start_timeout(stream); + return 0; + } + stream->start_timer = g_timeout_add_seconds(timeout_sec, + start_timeout, + stream); + return 0; + } +#else stream->start_timer = g_timeout_add_seconds(START_TIMEOUT, start_timeout, stream); return 0; +#endif } if (stream->close_int == TRUE) { diff --git a/profiles/audio/avdtp.h b/profiles/audio/avdtp.h index 621a6e3..54591aa 100755 --- a/profiles/audio/avdtp.h +++ b/profiles/audio/avdtp.h @@ -230,6 +230,10 @@ int avdtp_discover(struct avdtp *session, avdtp_discover_cb_t cb, gboolean avdtp_has_stream(struct avdtp *session, struct avdtp_stream *stream); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +void finalize_discovery(struct avdtp *session, int err); +#endif + unsigned int avdtp_stream_add_cb(struct avdtp *session, struct avdtp_stream *stream, avdtp_stream_state_cb cb, void *data); diff --git a/profiles/audio/avrcp.c b/profiles/audio/avrcp.c index 51a89b1..4d24510 100755 --- a/profiles/audio/avrcp.c +++ b/profiles/audio/avrcp.c @@ -137,9 +137,13 @@ #define AVRCP_CHARSET_UTF8 106 #define AVRCP_BROWSING_TIMEOUT 1 +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#define AVRCP_CT_VERSION 0x0103 +#define AVRCP_TG_VERSION 0x0103 +#else #define AVRCP_CT_VERSION 0x0106 #define AVRCP_TG_VERSION 0x0105 - +#endif #define AVRCP_SCOPE_MEDIA_PLAYER_LIST 0x00 #define AVRCP_SCOPE_MEDIA_PLAYER_VFS 0x01 #define AVRCP_SCOPE_SEARCH 0x02 @@ -275,6 +279,9 @@ struct avrcp { uint8_t transaction; uint8_t transaction_events[AVRCP_EVENT_LAST + 1]; struct pending_pdu *pending_pdu; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + uint32_t playback_status_id; +#endif }; struct passthrough_handler { @@ -292,6 +299,15 @@ struct control_pdu_handler { static GSList *servers = NULL; static unsigned int avctp_id = 0; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#ifdef TIZEN_FEATURE_BLUEZ_AVRCP_TARGET +static uint16_t adapter_avrcp_tg_ver = 0; +#endif +#ifdef TIZEN_FEATURE_BLUEZ_AVRCP_CONTROL +static uint16_t adapter_avrcp_ct_ver = 0; +#endif +#endif + /* Default feature bit mask for media player as per avctp.c:key_map */ static const uint8_t features[16] = { 0xF8, 0xBF, 0xFF, 0xBF, 0x1F, @@ -306,6 +322,13 @@ static uint32_t company_ids[] = { static void avrcp_register_notification(struct avrcp *session, uint8_t event); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static GList *player_list_settings(struct avrcp_player *player); +void avrcp_stop_position_timer(void); +unsigned int pos_timer_id = 0; +#endif + +#ifdef TIZEN_FEATURE_BLUEZ_AVRCP_CONTROL static sdp_record_t *avrcp_ct_record(void) { sdp_list_t *svclass_id, *pfseq, *apseq, *apseq1, *root; @@ -315,12 +338,23 @@ static sdp_record_t *avrcp_ct_record(void) sdp_record_t *record; sdp_data_t *psm[2], *version, *features; uint16_t lp = AVCTP_CONTROL_PSM, ap = AVCTP_BROWSING_PSM; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + uint16_t avctp_ver = 0x0104; + uint16_t feat = 0; +#ifdef ENABLE_AVRCP_CATEGORY1 + feat = AVRCP_FEATURE_CATEGORY_1; +#endif +#ifdef ENABLE_AVRCP_CATEGORY2 + feat = feat | AVRCP_FEATURE_CATEGORY_2; +#endif +#else uint16_t avctp_ver = 0x0103; uint16_t feat = ( AVRCP_FEATURE_CATEGORY_1 | AVRCP_FEATURE_CATEGORY_2 | AVRCP_FEATURE_CATEGORY_3 | AVRCP_FEATURE_CATEGORY_4 | AVRCP_FEATURE_BROWSING); +#endif record = sdp_record_alloc(); if (!record) @@ -371,6 +405,9 @@ static sdp_record_t *avrcp_ct_record(void) /* Bluetooth Profile Descriptor List */ sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID); profile[0].version = AVRCP_CT_VERSION; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + adapter_avrcp_ct_ver = AVRCP_CT_VERSION; +#endif pfseq = sdp_list_append(NULL, &profile[0]); sdp_set_profile_descs(record, pfseq); @@ -396,7 +433,9 @@ static sdp_record_t *avrcp_ct_record(void) return record; } +#endif +#ifdef TIZEN_FEATURE_BLUEZ_AVRCP_TARGET static sdp_record_t *avrcp_tg_record(void) { sdp_list_t *svclass_id, *pfseq, *apseq, *root, *apseq_browsing; @@ -405,9 +444,23 @@ static sdp_record_t *avrcp_tg_record(void) sdp_list_t *aproto_control, *proto_control[2]; sdp_record_t *record; sdp_data_t *psm_control, *version, *features, *psm_browsing; - sdp_list_t *aproto_browsing, *proto_browsing[2] = {0}; +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY + sdp_list_t *aproto_browsing; +#endif + sdp_list_t *proto_browsing[2] = {0}; uint16_t lp = AVCTP_CONTROL_PSM; uint16_t lp_browsing = AVCTP_BROWSING_PSM; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + uint16_t avctp_ver = 0x0104; + uint16_t feat = 0; +#ifdef ENABLE_AVRCP_CATEGORY1 + feat = AVRCP_FEATURE_CATEGORY_1 | + AVRCP_FEATURE_PLAYER_SETTINGS; +#endif +#ifdef ENABLE_AVRCP_CATEGORY2 + feat = feat | AVRCP_FEATURE_CATEGORY_2; +#endif +#else uint16_t avctp_ver = 0x0103; uint16_t feat = ( AVRCP_FEATURE_CATEGORY_1 | AVRCP_FEATURE_CATEGORY_2 | @@ -415,7 +468,7 @@ static sdp_record_t *avrcp_tg_record(void) AVRCP_FEATURE_CATEGORY_4 | AVRCP_FEATURE_BROWSING | AVRCP_FEATURE_PLAYER_SETTINGS ); - +#endif record = sdp_record_alloc(); if (!record) return NULL; @@ -453,12 +506,17 @@ static sdp_record_t *avrcp_tg_record(void) proto_browsing[1] = sdp_list_append(proto_browsing[1], version); apseq_browsing = sdp_list_append(apseq_browsing, proto_browsing[1]); +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY aproto_browsing = sdp_list_append(NULL, apseq_browsing); sdp_set_add_access_protos(record, aproto_browsing); +#endif /* Bluetooth Profile Descriptor List */ sdp_uuid16_create(&profile[0].uuid, AV_REMOTE_PROFILE_ID); profile[0].version = AVRCP_TG_VERSION; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + adapter_avrcp_tg_ver = AVRCP_TG_VERSION; +#endif pfseq = sdp_list_append(NULL, &profile[0]); sdp_set_profile_descs(record, pfseq); @@ -471,7 +529,9 @@ static sdp_record_t *avrcp_tg_record(void) sdp_list_free(proto_browsing[0], NULL); sdp_list_free(proto_browsing[1], NULL); sdp_list_free(apseq_browsing, NULL); +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY sdp_list_free(aproto_browsing, NULL); +#endif free(psm_control); free(version); @@ -485,6 +545,7 @@ static sdp_record_t *avrcp_tg_record(void) return record; } +#endif static unsigned int attr_get_max_val(uint8_t attr) { @@ -492,9 +553,17 @@ static unsigned int attr_get_max_val(uint8_t attr) case AVRCP_ATTRIBUTE_EQUALIZER: return AVRCP_EQUALIZER_ON; case AVRCP_ATTRIBUTE_REPEAT_MODE: +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + return AVRCP_REPEAT_MODE_ALL; +#else return AVRCP_REPEAT_MODE_GROUP; +#endif case AVRCP_ATTRIBUTE_SHUFFLE: +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + return AVRCP_SHUFFLE_ALL; +#else return AVRCP_SHUFFLE_GROUP; +#endif case AVRCP_ATTRIBUTE_SCAN: return AVRCP_SCAN_GROUP; } @@ -673,6 +742,10 @@ void avrcp_player_event(struct avrcp_player *player, uint8_t id, GSList *l; int attr; int val; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + uint32_t *position_val = NULL; + GList *settings; +#endif if (player->sessions == NULL) return; @@ -712,6 +785,24 @@ void avrcp_player_event(struct avrcp_player *player, uint8_t id, break; case AVRCP_EVENT_SETTINGS_CHANGED: size = 2; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + settings = player_list_settings(player); + pdu->params[1] = g_list_length(settings); + for (; settings; settings = settings->next) { + const char *key = settings->data; + + attr = attr_to_val(key); + if (attr < 0) + continue; + + val = player_get_setting(player, attr); + if (val < 0) + continue; + + pdu->params[size++] = attr; + pdu->params[size++] = val; + } +#else pdu->params[1] = 1; attr = attr_to_val(data); @@ -724,7 +815,19 @@ void avrcp_player_event(struct avrcp_player *player, uint8_t id, pdu->params[size++] = attr; pdu->params[size++] = val; +#endif /* __TIZEN__PATCH__ */ break; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + case AVRCP_EVENT_PLAYBACK_POS_CHANGED: + size = 5; + position_val = (uint32_t *) data; + *position_val = (*position_val & 0x000000ff) << 24 | + (*position_val & 0x0000ff00) << 8 | + (*position_val & 0x00ff0000) >> 8 | + (*position_val & 0xff000000) >> 24; + memcpy(&pdu->params[1], position_val, sizeof(uint32_t)); + break; +#endif case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED: size = 5; memcpy(&pdu->params[1], &player->id, sizeof(uint16_t)); @@ -740,6 +843,17 @@ void avrcp_player_event(struct avrcp_player *player, uint8_t id, done: pdu->params_len = htons(size); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (id == AVRCP_EVENT_PLAYBACK_POS_CHANGED && + pos_timer_id > 0) { + /* Remove the timer function which was added for register notification. + * As we are sending changed event eariler then time interval. + */ + DBG("Removing the timer function added by register notification"); + g_source_remove(pos_timer_id); + pos_timer_id = 0; + } +#endif for (l = player->sessions; l; l = l->next) { struct avrcp *session = l->data; @@ -920,8 +1034,10 @@ static const char *attrval_to_str(uint8_t attr, uint8_t value) return "singletrack"; case AVRCP_REPEAT_MODE_ALL: return "alltracks"; +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY case AVRCP_REPEAT_MODE_GROUP: return "group"; +#endif } break; @@ -933,8 +1049,10 @@ static const char *attrval_to_str(uint8_t attr, uint8_t value) return "off"; case AVRCP_SCAN_ALL: return "alltracks"; +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY case AVRCP_SCAN_GROUP: return "group"; +#endif } break; @@ -1426,6 +1544,16 @@ static GList *player_list_settings(struct avrcp_player *player) return player->cb->list_settings(player->user_data); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static uint32_t player_get_playback_position(struct avrcp_player *player) +{ + if (player == NULL) + return UINT32_MAX; + + return player->cb->get_position(player->user_data); +} +#endif + static bool avrcp_handle_play(struct avrcp *session) { struct avrcp_player *player = target_get_player(session); @@ -1507,6 +1635,33 @@ static bool handle_passthrough(struct avctp *conn, uint8_t op, bool pressed, return handler->func(session); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +void avrcp_stop_position_timer(void) +{ + if (pos_timer_id > 0) { + DBG("Removing position timer id"); + g_source_remove(pos_timer_id); + pos_timer_id = 0; + } +} +gboolean send_playback_position_event(gpointer user_data) +{ + struct avrcp_player *player = user_data; + uint32_t playback_position; + uint8_t play_status; + + play_status = player_get_status(player); + if (play_status != AVRCP_PLAY_STATUS_PLAYING) + return FALSE; + + playback_position = player_get_playback_position(player); + pos_timer_id = 0; + avrcp_player_event(player, AVRCP_EVENT_PLAYBACK_POS_CHANGED, + &playback_position); + return FALSE; +} +#endif + static uint8_t avrcp_handle_register_notification(struct avrcp *session, struct avrcp_header *pdu, uint8_t transaction) @@ -1515,6 +1670,11 @@ static uint8_t avrcp_handle_register_notification(struct avrcp *session, struct btd_device *dev = session->dev; uint16_t len = ntohs(pdu->params_len); uint64_t uid; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + uint32_t playback_interval; + uint32_t playback_position; + uint8_t play_status; +#endif GList *settings; /* @@ -1586,6 +1746,40 @@ static uint8_t avrcp_handle_register_notification(struct avrcp *session, len = 2; break; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + case AVRCP_EVENT_PLAYBACK_POS_CHANGED: + len = 5; + + /* time interval in seconds at which the change in playback position + shall be notified */ + memcpy(&playback_interval, &pdu->params[1], sizeof(uint32_t)); + playback_interval = ((playback_interval>>24)&0xff) | + ((playback_interval<<8)&0xff0000) | + ((playback_interval>>8)&0xff00) | + ((playback_interval<<24)&0xff000000); + + play_status = player_get_status(player); + + if (play_status != AVRCP_PLAY_STATUS_PLAYING) { + DBG("Play Pos Changed Event is skipped(%d)", play_status); + } else { + DBG("Playback interval : %d secs", playback_interval); + pos_timer_id = g_timeout_add_seconds( + playback_interval, + send_playback_position_event, player); + } + + /* retrieve current playback position for interim response */ + playback_position = player_get_playback_position(player); + playback_position = (playback_position & 0x000000ff) << 24 | + (playback_position & 0x0000ff00) << 8 | + (playback_position & 0x00ff0000) >> 8 | + (playback_position & 0xff000000) >> 24; + memcpy(&pdu->params[1], &playback_position, sizeof(uint32_t)); + + break; +#endif + default: /* All other events are not supported yet */ goto err; @@ -2918,6 +3112,26 @@ static int ct_press(struct avrcp_player *player, uint8_t op) return 0; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static int ct_release(struct avrcp_player *player, uint8_t op) +{ + DBG("+"); + int err; + struct avrcp *session; + + session = player->sessions->data; + if (session == NULL) + return -ENOTCONN; + + err = avctp_send_release_passthrough(session->conn, op); + if (err < 0) + return err; + + DBG("-"); + return 0; +} +#endif + static int ct_play(struct media_player *mp, void *user_data) { struct avrcp_player *player = user_data; @@ -2953,6 +3167,43 @@ static int ct_previous(struct media_player *mp, void *user_data) return ct_press(player, AVC_BACKWARD); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static int ct_press_fast_forward(struct media_player *mp, void *user_data) +{ + DBG("+"); + struct avrcp_player *player = user_data; + + DBG("-"); + return ct_press(player, AVC_FAST_FORWARD); +} + +static int ct_release_fast_forward(struct media_player *mp, void *user_data) +{ + DBG("+"); + struct avrcp_player *player = user_data; + + DBG("-"); + return ct_release(player, AVC_FAST_FORWARD); +} + +static int ct_press_rewind(struct media_player *mp, void *user_data) +{ + DBG("+"); + struct avrcp_player *player = user_data; + + DBG("-"); + return ct_press(player, AVC_REWIND); +} + +static int ct_release_rewind(struct media_player *mp, void *user_data) +{ + DBG("+"); + struct avrcp_player *player = user_data; + + DBG("-"); + return ct_release(player, AVC_REWIND); +} +#else static int ct_fast_forward(struct media_player *mp, void *user_data) { struct avrcp_player *player = user_data; @@ -2966,6 +3217,7 @@ static int ct_rewind(struct media_player *mp, void *user_data) return ct_press(player, AVC_REWIND); } +#endif static int ct_list_items(struct media_player *mp, const char *name, uint32_t start, uint32_t end, void *user_data) @@ -3274,8 +3526,15 @@ static const struct media_player_callback ct_cbs = { .stop = ct_stop, .next = ct_next, .previous = ct_previous, +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + .press_fast_forward = ct_press_fast_forward, + .release_fast_forward = ct_release_fast_forward, + .press_rewind = ct_press_rewind, + .release_rewind = ct_release_rewind, +#else .fast_forward = ct_fast_forward, .rewind = ct_rewind, +#endif .list_items = ct_list_items, .change_folder = ct_change_folder, .search = ct_search, @@ -3397,6 +3656,10 @@ static void player_destroy(gpointer data) if (player->destroy) player->destroy(player->user_data); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + avrcp_stop_position_timer(); +#endif + if (player->changed_id > 0) g_source_remove(player->changed_id); @@ -3724,8 +3987,42 @@ static void avrcp_register_notification(struct avrcp *session, uint8_t event) avrcp_handle_event, session); } -static gboolean avrcp_get_capabilities_resp(struct avctp *conn, uint8_t code, - uint8_t subunit, uint8_t transaction, +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static char *avrcp_event_to_string(uint8_t event) +{ + + switch (event) { + case AVRCP_EVENT_STATUS_CHANGED: + return "AVRCP EVENT STATUS CHANGED"; + case AVRCP_EVENT_TRACK_CHANGED: + return "AVRCP EVENT TRACK CHANGED"; + case AVRCP_EVENT_SETTINGS_CHANGED: + return "AVRCP EVENT SETTINGS CHANGED"; + case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED: + return "AVRCP EVENT ADDRESSED PLAYER CHANGED"; + case AVRCP_EVENT_UIDS_CHANGED: + return "AVRCP EVENT UIDS CHANGED"; + case AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED: + return "AVRCP EVENT AVAILABLE PLAYERS CHANGED"; + case AVRCP_EVENT_VOLUME_CHANGED: + return "AVRCP EVENT VOLUME CHANGED"; + default: + return "Unknown Event"; + } +} + +static gboolean avrcp_get_playback_status(gpointer user_data) +{ + struct avrcp *session = user_data; + + avrcp_get_play_status(session); + + return TRUE; +} +#endif + +static gboolean avrcp_get_capabilities_resp(struct avctp *conn, + uint8_t code, uint8_t subunit, uint8_t *operands, size_t operand_count, void *user_data) { @@ -3751,12 +4048,15 @@ static gboolean avrcp_get_capabilities_resp(struct avctp *conn, uint8_t code, uint8_t event = pdu->params[1 + count]; events |= (1 << event); - +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + DBG("Supported Event %s", avrcp_event_to_string(event)); +#endif switch (event) { case AVRCP_EVENT_STATUS_CHANGED: case AVRCP_EVENT_TRACK_CHANGED: case AVRCP_EVENT_PLAYBACK_POS_CHANGED: case AVRCP_EVENT_SETTINGS_CHANGED: +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY case AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED: case AVRCP_EVENT_UIDS_CHANGED: case AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED: @@ -3765,6 +4065,7 @@ static gboolean avrcp_get_capabilities_resp(struct avctp *conn, uint8_t code, !session->controller->player) break; case AVRCP_EVENT_VOLUME_CHANGED: +#endif avrcp_register_notification(session, event); break; } @@ -3781,7 +4082,12 @@ static gboolean avrcp_get_capabilities_resp(struct avctp *conn, uint8_t code, if (!(events & (1 << AVRCP_EVENT_STATUS_CHANGED))) avrcp_get_element_attributes(session); - +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if ((events & (1 << AVRCP_EVENT_STATUS_CHANGED)) == 0) { + session->playback_status_id = g_timeout_add_seconds(1, + avrcp_get_playback_status, session); + } +#endif return FALSE; } @@ -3893,6 +4199,7 @@ static void avrcp_connect_browsing(struct avrcp *session) session); } +#ifdef TIZEN_FEATURE_BLUEZ_AVRCP_TARGET static void target_init(struct avrcp *session) { struct avrcp_server *server = session->server; @@ -3919,13 +4226,23 @@ static void target_init(struct avrcp *session) session->supported_events |= (1 << AVRCP_EVENT_STATUS_CHANGED) | (1 << AVRCP_EVENT_TRACK_CHANGED) | +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY (1 << AVRCP_EVENT_TRACK_REACHED_START) | (1 << AVRCP_EVENT_TRACK_REACHED_END) | +#endif +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + (1 << AVRCP_EVENT_PLAYBACK_POS_CHANGED) | +#endif (1 << AVRCP_EVENT_SETTINGS_CHANGED); if (target->version < 0x0104) return; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (adapter_avrcp_tg_ver < 0x0104) + return; +#endif + session->supported_events |= (1 << AVRCP_EVENT_ADDRESSED_PLAYER_CHANGED) | (1 << AVRCP_EVENT_AVAILABLE_PLAYERS_CHANGED) | @@ -3940,7 +4257,9 @@ static void target_init(struct avrcp *session) avrcp_connect_browsing(session); } +#endif +#ifdef TIZEN_FEATURE_BLUEZ_AVRCP_CONTROL static void controller_init(struct avrcp *session) { struct avrcp_player *player; @@ -3955,6 +4274,11 @@ static void controller_init(struct avrcp *session) DBG("%p version 0x%04x", controller, controller->version); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if ((controller->version >= 0x0104) && (adapter_avrcp_ct_ver >= 0x0104)) + session->supported_events |= (1 << AVRCP_EVENT_VOLUME_CHANGED); +#endif + service = btd_device_get_service(session->dev, AVRCP_TARGET_UUID); btd_service_connecting_complete(service, 0); @@ -3973,11 +4297,17 @@ static void controller_init(struct avrcp *session) if (controller->version < 0x0104) return; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (adapter_avrcp_ct_ver < 0x0104) + return; +#endif + if (!(controller->features & AVRCP_FEATURE_BROWSING)) return; avrcp_connect_browsing(session); } +#endif static void session_init_control(struct avrcp *session) { @@ -3991,12 +4321,14 @@ static void session_init_control(struct avrcp *session) handle_vendordep_pdu, session); session->control_handlers = control_handlers; - +#ifdef TIZEN_FEATURE_BLUEZ_AVRCP_CONTROL if (btd_device_get_service(session->dev, AVRCP_TARGET_UUID) != NULL) controller_init(session); - +#endif +#ifdef TIZEN_FEATURE_BLUEZ_AVRCP_TARGET if (btd_device_get_service(session->dev, AVRCP_REMOTE_UUID) != NULL) target_init(session); +#endif } static void controller_destroy(struct avrcp *session) @@ -4030,6 +4362,14 @@ static void session_destroy(struct avrcp *session, int err) server->sessions = g_slist_remove(server->sessions, session); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (session->playback_status_id > 0) { + DBG("Removing the timer for playback status polling"); + g_source_remove(session->playback_status_id); + session->playback_status_id = 0; + } +#endif + session_abort_pending_pdu(session); service = btd_device_get_service(session->dev, AVRCP_TARGET_UUID); @@ -4350,9 +4690,19 @@ static int avrcp_connect(struct btd_service *service) { struct btd_device *dev = btd_service_get_device(service); const char *path = device_get_path(dev); - +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + char name[10]; +#endif DBG("path %s", path); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + device_get_name(dev, name, sizeof(name)); + DBG("name : %s", name); + if (g_str_equal(name, "PLT_M50")) { + DBG("Don't initiate avrcp connection with this headset"); + return -ENOTSUP; + } +#endif return control_connect(service); } @@ -4380,6 +4730,7 @@ static void avrcp_target_remove(struct btd_service *service) control_unregister(service); } +#ifdef TIZEN_FEATURE_BLUEZ_AVRCP_TARGET static void avrcp_target_server_remove(struct btd_profile *p, struct btd_adapter *adapter) { @@ -4399,7 +4750,9 @@ static void avrcp_target_server_remove(struct btd_profile *p, if (server->ct_record_id == 0) avrcp_server_unregister(server); } +#endif +#ifdef TIZEN_FEATURE_BLUEZ_AVRCP_TARGET static int avrcp_target_server_probe(struct btd_profile *p, struct btd_adapter *adapter) { @@ -4434,6 +4787,7 @@ done: return 0; } +#endif static struct btd_profile avrcp_target_profile = { .name = "audio-avrcp-target", @@ -4444,9 +4798,10 @@ static struct btd_profile avrcp_target_profile = { .connect = avrcp_connect, .disconnect = avrcp_disconnect, - +#ifdef TIZEN_FEATURE_BLUEZ_AVRCP_TARGET .adapter_probe = avrcp_target_server_probe, .adapter_remove = avrcp_target_server_remove, +#endif }; static int avrcp_controller_probe(struct btd_service *service) @@ -4463,6 +4818,7 @@ static void avrcp_controller_remove(struct btd_service *service) control_unregister(service); } +#ifdef TIZEN_FEATURE_BLUEZ_AVRCP_CONTROL static void avrcp_controller_server_remove(struct btd_profile *p, struct btd_adapter *adapter) { @@ -4482,7 +4838,9 @@ static void avrcp_controller_server_remove(struct btd_profile *p, if (server->tg_record_id == 0) avrcp_server_unregister(server); } +#endif +#ifdef TIZEN_FEATURE_BLUEZ_AVRCP_CONTROL static int avrcp_controller_server_probe(struct btd_profile *p, struct btd_adapter *adapter) { @@ -4517,6 +4875,7 @@ done: return 0; } +#endif static struct btd_profile avrcp_controller_profile = { .name = "avrcp-controller", @@ -4527,9 +4886,10 @@ static struct btd_profile avrcp_controller_profile = { .connect = avrcp_connect, .disconnect = avrcp_disconnect, - +#ifdef TIZEN_FEATURE_BLUEZ_AVRCP_CONTROL .adapter_probe = avrcp_controller_server_probe, .adapter_remove = avrcp_controller_server_remove, +#endif }; static int avrcp_init(void) diff --git a/profiles/audio/media.c b/profiles/audio/media.c index 23d1561..e7a16d7 100755 --- a/profiles/audio/media.c +++ b/profiles/audio/media.c @@ -44,6 +44,10 @@ #include "src/dbus-common.h" #include "src/profile.h" +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#include "src/service.h" +#endif + #include "src/uuid-helper.h" #include "src/log.h" #include "src/error.h" @@ -54,13 +58,29 @@ #include "transport.h" #include "a2dp.h" #include "avrcp.h" +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#include "sink.h" +#endif #define MEDIA_INTERFACE "org.bluez.Media1" #define MEDIA_ENDPOINT_INTERFACE "org.bluez.MediaEndpoint1" #define MEDIA_PLAYER_INTERFACE "org.mpris.MediaPlayer2.Player" +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#define A2DP_SINK_ROLE "sink" +#define A2DP_SOURCE_ROLE "source" +#endif + #define REQUEST_TIMEOUT (3 * 1000) /* 3 seconds */ +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#define SINK_SUSPEND_TIMEOUT 4 /* 4 seconds */ + +unsigned int suspend_timer_id = 0; +static gboolean a2dp_sink_support = false; +static gboolean a2dp_source_support = true; +#endif + struct media_adapter { struct btd_adapter *btd_adapter; GSList *endpoints; /* Endpoints list */ @@ -102,6 +122,9 @@ struct media_player { guint watch; guint properties_watch; guint seek_watch; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + guint sink_watch; +#endif char *status; uint32_t position; uint32_t duration; @@ -117,6 +140,26 @@ struct media_player { static GSList *adapters = NULL; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static gboolean set_avrcp_status = FALSE; +static gboolean send_track_changed_event = FALSE; + +gboolean current_delay_reporting = false; +struct media_endpoint *source_endpoint = NULL; +struct media_endpoint *sink_endpoint = NULL; + +static int media_set_sink_callback(struct btd_device *device, + struct media_player *mp); +static void media_sink_state_changed_cb(struct btd_service *service, + sink_state_t old_state, + sink_state_t new_state, + void *user_data); +void media_stop_suspend_timer(void); +struct media_player *media_adapter_get_player(struct media_adapter *adapter); +static struct media_adapter *find_adapter(struct btd_device *device); +static uint32_t get_position(void *user_data); +#endif + static void endpoint_request_free(struct endpoint_request *request) { if (request->call) @@ -199,7 +242,9 @@ static void media_endpoint_remove(struct media_endpoint *endpoint) if (endpoint->sep) { a2dp_remove_sep(endpoint->sep); +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY return; +#endif } info("Endpoint unregistered: sender=%s path=%s", endpoint->sender, @@ -228,6 +273,9 @@ static void clear_configuration(struct media_endpoint *endpoint, { DBusMessage *msg; const char *path; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + struct media_player *mp; +#endif msg = dbus_message_new_method_call(endpoint->sender, endpoint->path, MEDIA_ENDPOINT_INTERFACE, @@ -243,6 +291,14 @@ static void clear_configuration(struct media_endpoint *endpoint, g_dbus_send_message(btd_get_dbus_connection(), msg); done: endpoint->transports = g_slist_remove(endpoint->transports, transport); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if ((mp = media_adapter_get_player(endpoint->adapter))) + if (mp->sink_watch) { + sink_remove_state_cb(mp->sink_watch); + mp->sink_watch = 0; + } + media_stop_suspend_timer(); +#endif media_transport_destroy(transport); } @@ -405,6 +461,141 @@ static struct media_transport *find_device_transport( return match->data; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +struct media_player * media_adapter_get_player(struct media_adapter * adapter) +{ + GSList *l; + DBG(" "); + + for (l = adapter->players; l; l = l->next) { + struct media_player *mp = l->data; + if (mp != NULL) + return mp; + } + return NULL; +} + +void media_stop_suspend_timer(void) +{ + if (suspend_timer_id > 0) { + DBG("Removing sink suspend timer"); + g_source_remove(suspend_timer_id); + suspend_timer_id = 0; + } +} + +gboolean media_reset_mp_status(gpointer user_data) +{ + struct media_player *mp = user_data; + DBG(" "); + + /* PlayBackStatus already reset; so return */ + if (g_strcmp0(mp->status, "playing") != 0) + return FALSE; + + mp->position = get_position(mp); + g_timer_start(mp->timer); + + g_free(mp->status); + mp->status = g_strdup("paused"); + suspend_timer_id = 0; + avrcp_player_event(mp->player, AVRCP_EVENT_STATUS_CHANGED, mp->status); + + return FALSE; +} + +static void media_sink_state_changed_cb(struct btd_service *service, + sink_state_t old_state, + sink_state_t new_state, + void *user_data) +{ + struct media_player *mp = user_data; + DBG(" "); + + /* Check if A2DP streaming is suspended */ + if ((old_state == SINK_STATE_PLAYING) && + (new_state == SINK_STATE_CONNECTED)) { + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + struct btd_device *device = btd_service_get_device(service); + char name[20] = {0,}; +#endif + + /* Check AVRCP play back status */ + if (g_strcmp0(mp->status, "playing") != 0) + return; + + media_stop_suspend_timer(); + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + device_get_name(device, name, sizeof(name)); + DBG("Name : %s", name); + + if (g_str_has_prefix(name, "LG HBS") != TRUE) { +#endif + /* PlayBackStatus is still PLAYING; start a timer */ + suspend_timer_id = g_timeout_add_seconds(SINK_SUSPEND_TIMEOUT, + media_reset_mp_status, mp); + DBG("SINK SUSPEND TIMEOUT started"); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + } +#endif + } + + /* Check if A2DP streaming is started */ + if ((old_state == SINK_STATE_CONNECTED) && + (new_state == SINK_STATE_PLAYING)) { + + struct btd_device *device = btd_service_get_device(service); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + char name[20] = {0,}; +#else + char name[20]; +#endif + + media_stop_suspend_timer(); + + /* NULL packet streaming during initial connection */ + if (set_avrcp_status == FALSE) { + set_avrcp_status = TRUE; + return; + } + + /* Check for BMW, Audi, VW car kit */ + device_get_name(device, name, sizeof(name)); + DBG("Name : %s", name); + if ((g_str_has_prefix(name, "BMW") == TRUE) || + (g_str_has_prefix(name, "Audi") == TRUE) || + (g_str_has_prefix(name, "VW BT") == TRUE)) { + + /* Check AVRCP play back status */ + if (g_strcmp0(mp->status, "playing") == 0) + return; + + g_free(mp->status); + mp->status = g_strdup("playing"); + avrcp_player_event(mp->player, + AVRCP_EVENT_STATUS_CHANGED, mp->status); + } + } +} + +static int media_set_sink_callback(struct btd_device *device, + struct media_player *mp) +{ + struct btd_service *service; + DBG(" "); + + service = btd_device_get_service(device, A2DP_SINK_UUID); + if (service == NULL) + return -EINVAL; + + mp->sink_watch = sink_add_state_cb(service, media_sink_state_changed_cb, mp); + + return 0; +} +#endif + struct a2dp_config_data { struct a2dp_setup *setup; a2dp_endpoint_config_t cb; @@ -423,6 +614,10 @@ static gboolean set_configuration(struct media_endpoint *endpoint, const char *path; DBusMessageIter iter; struct media_transport *transport; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + struct media_adapter *adapter; + struct media_player *mp; +#endif transport = find_device_transport(endpoint, device); @@ -443,6 +638,13 @@ static gboolean set_configuration(struct media_endpoint *endpoint, return FALSE; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + set_avrcp_status = FALSE; + adapter = find_adapter(device); + if ((mp = media_adapter_get_player(adapter))) + media_set_sink_callback(device, mp); +#endif + endpoint->transports = g_slist_append(endpoint->transports, transport); dbus_message_iter_init_append(msg, &iter); @@ -600,7 +802,11 @@ static gboolean endpoint_init_a2dp_source(struct media_endpoint *endpoint, endpoint->sep = a2dp_add_sep(endpoint->adapter->btd_adapter, AVDTP_SEP_TYPE_SOURCE, endpoint->codec, delay_reporting, &a2dp_endpoint, +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + endpoint, NULL, err); +#else endpoint, a2dp_destroy_endpoint, err); +#endif if (endpoint->sep == NULL) return FALSE; @@ -614,7 +820,11 @@ static gboolean endpoint_init_a2dp_sink(struct media_endpoint *endpoint, endpoint->sep = a2dp_add_sep(endpoint->adapter->btd_adapter, AVDTP_SEP_TYPE_SINK, endpoint->codec, delay_reporting, &a2dp_endpoint, +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + endpoint, NULL, err); +#else endpoint, a2dp_destroy_endpoint, err); +#endif if (endpoint->sep == NULL) return FALSE; @@ -744,13 +954,31 @@ static struct media_endpoint *media_endpoint_create(struct media_adapter *adapte endpoint->adapter = adapter; - if (strcasecmp(uuid, A2DP_SOURCE_UUID) == 0) + if (strcasecmp(uuid, A2DP_SOURCE_UUID) == 0) { +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + source_endpoint = endpoint; + if (btd_adapter_get_a2dp_role(adapter->btd_adapter) == BLUETOOTH_A2DP_SINK_ROLE) + return endpoint; + else + succeeded = endpoint_init_a2dp_source(endpoint, + delay_reporting, err); +#else succeeded = endpoint_init_a2dp_source(endpoint, delay_reporting, err); - else if (strcasecmp(uuid, A2DP_SINK_UUID) == 0) +#endif + } else if (strcasecmp(uuid, A2DP_SINK_UUID) == 0) { +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + sink_endpoint = endpoint; + if (btd_adapter_get_a2dp_role(adapter->btd_adapter) == BLUETOOTH_A2DP_SOURCE_ROLE) + return endpoint; + else + succeeded = endpoint_init_a2dp_sink(endpoint, + delay_reporting, err); +#else succeeded = endpoint_init_a2dp_sink(endpoint, delay_reporting, err); - else if (strcasecmp(uuid, HFP_AG_UUID) == 0 || +#endif + } else if (strcasecmp(uuid, HFP_AG_UUID) == 0 || strcasecmp(uuid, HSP_AG_UUID) == 0) succeeded = TRUE; else if (strcasecmp(uuid, HFP_HS_UUID) == 0 || @@ -787,6 +1015,36 @@ static struct media_endpoint *media_endpoint_create(struct media_adapter *adapte return endpoint; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static int parse_a2dp_uuid(DBusMessageIter *props, const char **uuid) +{ + gboolean has_uuid = FALSE; + + while (dbus_message_iter_get_arg_type(props) == DBUS_TYPE_DICT_ENTRY) { + const char *key; + DBusMessageIter value, entry; + int var; + + dbus_message_iter_recurse(props, &entry); + dbus_message_iter_get_basic(&entry, &key); + + dbus_message_iter_next(&entry); + dbus_message_iter_recurse(&entry, &value); + + var = dbus_message_iter_get_arg_type(&value); + if (strcasecmp(key, "UUID") == 0) { + if (var != DBUS_TYPE_STRING) + return -EINVAL; + dbus_message_iter_get_basic(&value, uuid); + has_uuid = TRUE; + } + dbus_message_iter_next(props); + } + + return has_uuid ? 0 : -EINVAL; +} +#endif + static int parse_properties(DBusMessageIter *props, const char **uuid, gboolean *delay_reporting, uint8_t *codec, uint8_t **capabilities, int *size) @@ -837,6 +1095,42 @@ static int parse_properties(DBusMessageIter *props, const char **uuid, return (has_uuid && has_codec) ? 0 : -EINVAL; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static DBusMessage *a2dp_select_role(DBusConnection *conn, DBusMessage *msg, + void *data) +{ + struct media_adapter *adapter = data; + DBusMessageIter args, props; + const char *a2dp_role; + gboolean ret; + int err; + + if (!dbus_message_get_args(msg, NULL, + DBUS_TYPE_STRING, &a2dp_role, + DBUS_TYPE_INVALID)) + return btd_error_invalid_args(msg); + + if (!g_strcmp0(a2dp_role, A2DP_SINK_ROLE)) { + btd_adapter_set_a2dp_role(adapter->btd_adapter, BLUETOOTH_A2DP_SINK_ROLE); + a2dp_remove_sep(source_endpoint->sep); + ret = endpoint_init_a2dp_sink(sink_endpoint, current_delay_reporting, NULL); + if (!ret) + DBG("could not init a2dp sink"); + } else if (!g_strcmp0(a2dp_role, A2DP_SOURCE_ROLE)) { + btd_adapter_set_a2dp_role(adapter->btd_adapter, BLUETOOTH_A2DP_SOURCE_ROLE); + a2dp_remove_sep(sink_endpoint->sep); + ret = endpoint_init_a2dp_source(source_endpoint, current_delay_reporting, NULL); + if (!ret) + DBG("could not init a2dp source"); + } else { + DBG("invalid a2dp role"); + return btd_error_invalid_args(msg); + } + + return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); +} +#endif + static DBusMessage *register_endpoint(DBusConnection *conn, DBusMessage *msg, void *data) { @@ -848,7 +1142,15 @@ static DBusMessage *register_endpoint(DBusConnection *conn, DBusMessage *msg, uint8_t *capabilities; int size = 0; int err; - +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (btd_adapter_get_a2dp_role(adapter->btd_adapter) == BLUETOOTH_A2DP_SINK_ROLE) { + a2dp_sink_support = true; + a2dp_source_support = false; + } else { + a2dp_sink_support = false; + a2dp_source_support = true; + } +#endif sender = dbus_message_get_sender(msg); dbus_message_iter_init(msg, &args); @@ -866,7 +1168,9 @@ static DBusMessage *register_endpoint(DBusConnection *conn, DBusMessage *msg, if (parse_properties(&props, &uuid, &delay_reporting, &codec, &capabilities, &size) < 0) return btd_error_invalid_args(msg); - +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + current_delay_reporting = delay_reporting; +#endif if (media_endpoint_create(adapter, sender, path, uuid, delay_reporting, codec, capabilities, size, &err) == NULL) { if (err == -EPROTONOSUPPORT) @@ -961,6 +1265,13 @@ static void media_player_free(gpointer data) if (mp->settings) g_hash_table_unref(mp->settings); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + media_stop_suspend_timer(); + + if (mp->sink_watch) + sink_remove_state_cb(mp->sink_watch); +#endif + g_timer_destroy(mp->timer); g_free(mp->sender); g_free(mp->path); @@ -1126,6 +1437,11 @@ static uint64_t get_uid(void *user_data) if (mp->track == NULL) return UINT64_MAX; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (!g_hash_table_lookup(mp->track, "Title")) + return UINT64_MAX; +#endif + return 0; } @@ -1304,6 +1620,9 @@ static void media_player_exit(DBusConnection *connection, void *user_data) static gboolean set_status(struct media_player *mp, DBusMessageIter *iter) { const char *value; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + uint32_t playback_position; +#endif if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) return FALSE; @@ -1321,26 +1640,49 @@ static gboolean set_status(struct media_player *mp, DBusMessageIter *iter) mp->status = g_strdup(value); avrcp_player_event(mp->player, AVRCP_EVENT_STATUS_CHANGED, mp->status); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (strcasecmp(mp->status, "reverse-seek") != 0 && + strcasecmp(mp->status, "playing") != 0) { + playback_position = get_position(mp); + avrcp_player_event(mp->player, AVRCP_EVENT_PLAYBACK_POS_CHANGED, + &playback_position); + } +#endif return TRUE; } static gboolean set_position(struct media_player *mp, DBusMessageIter *iter) { +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + uint32_t value; +#else uint64_t value; const char *status; +#endif +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + uint32_t playback_position; +#endif +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_INT64) - return FALSE; - + return FALSE; +#else + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT32) + return FALSE; +#endif dbus_message_iter_get_basic(iter, &value); +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY value /= 1000; - +#endif + DBG("Value %d", value); +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY if (value > get_position(mp)) status = "forward-seek"; else status = "reverse-seek"; +#endif mp->position = value; g_timer_start(mp->timer); @@ -1350,6 +1692,12 @@ static gboolean set_position(struct media_player *mp, DBusMessageIter *iter) if (!mp->position) { avrcp_player_event(mp->player, AVRCP_EVENT_TRACK_REACHED_START, NULL); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + playback_position = get_position(mp); + avrcp_player_event(mp->player, AVRCP_EVENT_PLAYBACK_POS_CHANGED, + &playback_position); +#endif + return TRUE; } @@ -1360,11 +1708,23 @@ static gboolean set_position(struct media_player *mp, DBusMessageIter *iter) if (mp->position == UINT32_MAX || mp->position >= mp->duration) { avrcp_player_event(mp->player, AVRCP_EVENT_TRACK_REACHED_END, NULL); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + playback_position = get_position(mp); + avrcp_player_event(mp->player, AVRCP_EVENT_PLAYBACK_POS_CHANGED, + &playback_position); +#endif return TRUE; } +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY /* Send a status change to force resync the position */ avrcp_player_event(mp->player, AVRCP_EVENT_STATUS_CHANGED, status); +#endif +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + playback_position = get_position(mp); + avrcp_player_event(mp->player, AVRCP_EVENT_PLAYBACK_POS_CHANGED, + &playback_position); +#endif return TRUE; } @@ -1372,6 +1732,15 @@ static gboolean set_position(struct media_player *mp, DBusMessageIter *iter) static void set_metadata(struct media_player *mp, const char *key, const char *value) { +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + const char *current_value = NULL; + + current_value = g_hash_table_lookup(mp->track, key); + + if ((g_strcmp0(value, current_value) != 0) && + (send_track_changed_event == FALSE)) + send_track_changed_event = TRUE; +#endif DBG("%s=%s", key, value); g_hash_table_replace(mp->track, g_strdup(key), g_strdup(value)); } @@ -1431,7 +1800,9 @@ static gboolean parse_int64_metadata(struct media_player *mp, const char *key, dbus_message_iter_get_basic(iter, &value); if (strcasecmp(key, "Duration") == 0) { +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY value /= 1000; +#endif mp->duration = value; } @@ -1472,6 +1843,9 @@ static gboolean parse_player_metadata(struct media_player *mp, int ctype; gboolean title = FALSE; uint64_t uid; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + uint32_t playback_position; +#endif ctype = dbus_message_iter_get_arg_type(iter); if (ctype != DBUS_TYPE_ARRAY) @@ -1479,11 +1853,13 @@ static gboolean parse_player_metadata(struct media_player *mp, dbus_message_iter_recurse(iter, &dict); +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY if (mp->track != NULL) g_hash_table_unref(mp->track); mp->track = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); +#endif while ((ctype = dbus_message_iter_get_arg_type(&dict)) != DBUS_TYPE_INVALID) { @@ -1521,6 +1897,11 @@ static gboolean parse_player_metadata(struct media_player *mp, } else if (strcasecmp(key, "mpris:length") == 0) { if (!parse_int64_metadata(mp, "Duration", &var)) return FALSE; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + } else if (strcasecmp(key, "xesam:totalTracks") == 0) { + if (!parse_int32_metadata(mp, "NumberOfTracks", &var)) + return FALSE; +#endif } else if (strcasecmp(key, "xesam:trackNumber") == 0) { if (!parse_int32_metadata(mp, "TrackNumber", &var)) return FALSE; @@ -1534,13 +1915,25 @@ static gboolean parse_player_metadata(struct media_player *mp, g_hash_table_insert(mp->track, g_strdup("Title"), g_strdup("")); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (send_track_changed_event) { + uid = get_uid(mp); + avrcp_player_event(mp->player, + AVRCP_EVENT_TRACK_CHANGED, &uid); + send_track_changed_event = FALSE; + + playback_position = get_position(mp); + avrcp_player_event(mp->player, + AVRCP_EVENT_PLAYBACK_POS_CHANGED, &playback_position); + } +#else mp->position = 0; g_timer_start(mp->timer); uid = get_uid(mp); avrcp_player_event(mp->player, AVRCP_EVENT_TRACK_CHANGED, &uid); avrcp_player_event(mp->player, AVRCP_EVENT_TRACK_REACHED_START, NULL); - +#endif return TRUE; } @@ -1791,6 +2184,10 @@ static struct media_player *media_player_create(struct media_adapter *adapter, mp->settings = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + mp->track = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, + g_free); +#endif adapter->players = g_slist_append(adapter->players, mp); @@ -1871,6 +2268,10 @@ static const GDBusMethodTable media_methods[] = { NULL, register_player) }, { GDBUS_METHOD("UnregisterPlayer", GDBUS_ARGS({ "player", "o" }), NULL, unregister_player) }, +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + { GDBUS_METHOD("SelectRole", + GDBUS_ARGS({ "role", "s" }), NULL, a2dp_select_role) }, +#endif { }, }; diff --git a/profiles/audio/player.c b/profiles/audio/player.c index 7944b49..f39b9ae 100755 --- a/profiles/audio/player.c +++ b/profiles/audio/player.c @@ -534,7 +534,83 @@ static DBusMessage *media_player_previous(DBusConnection *conn, return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static DBusMessage *media_player_press_fast_forward(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + DBG("+"); + struct media_player *mp = data; + struct player_callback *cb = mp->cb; + int err; + + if (cb->cbs->press_fast_forward == NULL) + return btd_error_not_supported(msg); + + err = cb->cbs->press_fast_forward(mp, cb->user_data); + if (err < 0) + return btd_error_failed(msg, strerror(-err)); + + DBG("-"); + return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); +} + +static DBusMessage *media_player_release_fast_forward(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + DBG("+"); + struct media_player *mp = data; + struct player_callback *cb = mp->cb; + int err; + + if (cb->cbs->release_fast_forward == NULL) + return btd_error_not_supported(msg); + + err = cb->cbs->release_fast_forward(mp, cb->user_data); + if (err < 0) + return btd_error_failed(msg, strerror(-err)); + + DBG("-"); + return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); +} + +static DBusMessage *media_player_press_rewind(DBusConnection *conn, DBusMessage *msg, + void *data) +{ + DBG("+"); + struct media_player *mp = data; + struct player_callback *cb = mp->cb; + int err; + + if (cb->cbs->press_rewind == NULL) + return btd_error_not_supported(msg); + + err = cb->cbs->press_rewind(mp, cb->user_data); + if (err < 0) + return btd_error_failed(msg, strerror(-err)); + + DBG("-"); + return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); +} + +static DBusMessage *media_player_release_rewind(DBusConnection *conn, DBusMessage *msg, + void *data) +{ + DBG("+"); + struct media_player *mp = data; + struct player_callback *cb = mp->cb; + int err; + + if (cb->cbs->release_rewind == NULL) + return btd_error_not_supported(msg); + + err = cb->cbs->release_rewind(mp, cb->user_data); + if (err < 0) + return btd_error_failed(msg, strerror(-err)); + DBG("-"); + return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); +} +#else static DBusMessage *media_player_fast_forward(DBusConnection *conn, DBusMessage *msg, void *data) { @@ -568,7 +644,7 @@ static DBusMessage *media_player_rewind(DBusConnection *conn, DBusMessage *msg, return g_dbus_create_reply(msg, DBUS_TYPE_INVALID); } - +#endif static void parse_folder_list(gpointer data, gpointer user_data) { struct media_item *item = data; @@ -725,8 +801,15 @@ static const GDBusMethodTable media_player_methods[] = { { GDBUS_METHOD("Stop", NULL, NULL, media_player_stop) }, { GDBUS_METHOD("Next", NULL, NULL, media_player_next) }, { GDBUS_METHOD("Previous", NULL, NULL, media_player_previous) }, +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + { GDBUS_METHOD("PressFastForward", NULL, NULL, media_player_press_fast_forward) }, + { GDBUS_METHOD("ReleaseFastForward", NULL, NULL, media_player_release_fast_forward) }, + { GDBUS_METHOD("PressRewind", NULL, NULL, media_player_press_rewind) }, + { GDBUS_METHOD("ReleaseRewind", NULL, NULL, media_player_release_rewind) }, +#else { GDBUS_METHOD("FastForward", NULL, NULL, media_player_fast_forward) }, { GDBUS_METHOD("Rewind", NULL, NULL, media_player_rewind) }, +#endif { } }; @@ -1328,18 +1411,37 @@ void media_player_set_metadata(struct media_player *mp, struct media_item *item, const char *key, void *data, size_t len) { +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + char *value; + char *end, *temp; +#else char *value, *curval; +#endif value = g_strndup(data, len); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + temp = value; + while (g_utf8_validate(temp, -1, (const gchar **)&end) == FALSE) { + temp = g_utf8_find_next_char(end, NULL); + if (temp == NULL) { + *end = '\0'; + break; + } + strcpy(end, temp); + temp = end; + } +#endif + DBG("%s: %s", key, value); +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY curval = g_hash_table_lookup(mp->track, key); if (g_strcmp0(curval, value) == 0) { g_free(value); return; } - +#endif if (mp->process_id == 0) { g_hash_table_remove_all(mp->track); mp->process_id = g_idle_add(process_metadata_changed, mp); diff --git a/profiles/audio/player.h b/profiles/audio/player.h index 54e395a..21eab49 100755 --- a/profiles/audio/player.h +++ b/profiles/audio/player.h @@ -52,8 +52,15 @@ struct media_player_callback { int (*stop) (struct media_player *mp, void *user_data); int (*next) (struct media_player *mp, void *user_data); int (*previous) (struct media_player *mp, void *user_data); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + int (*press_fast_forward) (struct media_player *mp, void *user_data); + int (*release_fast_forward) (struct media_player *mp, void *user_data); + int (*press_rewind) (struct media_player *mp, void *user_data); + int (*release_rewind) (struct media_player *mp, void *user_data); +#else int (*fast_forward) (struct media_player *mp, void *user_data); int (*rewind) (struct media_player *mp, void *user_data); +#endif int (*list_items) (struct media_player *mp, const char *name, uint32_t start, uint32_t end, void *user_data); int (*change_folder) (struct media_player *mp, const char *path, diff --git a/profiles/audio/sink.c b/profiles/audio/sink.c index 7cac210..332d127 100755 --- a/profiles/audio/sink.c +++ b/profiles/audio/sink.c @@ -106,9 +106,17 @@ static void sink_set_state(struct sink *sink, sink_state_t new_state) if (new_state != SINK_STATE_DISCONNECTED) return; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + btd_service_disconnecting_complete(service, 0); +#endif + if (sink->session) { avdtp_unref(sink->session); sink->session = NULL; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + sink->connect_id = 0; + sink->disconnect_id = 0; +#endif } } @@ -148,7 +156,9 @@ static void stream_state_changed(struct avdtp_stream *stream, switch (new_state) { case AVDTP_STATE_IDLE: +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY btd_service_disconnecting_complete(sink->service, 0); +#endif if (sink->disconnect_id > 0) { a2dp_cancel(sink->disconnect_id); @@ -274,6 +284,7 @@ int sink_connect(struct btd_service *service) { struct sink *sink = btd_service_get_user_data(service); +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY if (!sink->session) sink->session = a2dp_avdtp_get(btd_service_get_device(service)); @@ -281,6 +292,7 @@ int sink_connect(struct btd_service *service) DBG("Unable to get a session"); return -EIO; } +#endif if (sink->connect_id > 0 || sink->disconnect_id > 0) return -EBUSY; @@ -291,6 +303,16 @@ int sink_connect(struct btd_service *service) if (sink->stream_state >= AVDTP_STATE_OPEN) return -EALREADY; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (!sink->session) + sink->session = a2dp_avdtp_get(btd_service_get_device(service)); + + if (!sink->session) { + DBG("Unable to get a session"); + return -EIO; + } +#endif + if (!sink_setup_stream(service, NULL)) { DBG("Failed to create a stream"); return -EIO; @@ -309,8 +331,16 @@ static void sink_free(struct btd_service *service) avdtp_stream_remove_cb(sink->session, sink->stream, sink->cb_id); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (sink->session) { + /* We need to clear the avdtp discovery procedure */ + finalize_discovery(sink->session, ECANCELED); + avdtp_unref(sink->session); + } +#else if (sink->session) avdtp_unref(sink->session); +#endif if (sink->connect_id > 0) { btd_service_connecting_complete(sink->service, -ECANCELED); diff --git a/profiles/cyclingspeed/cyclingspeed.c b/profiles/cyclingspeed/cyclingspeed.c new file mode 100755 index 0000000..e447725 --- /dev/null +++ b/profiles/cyclingspeed/cyclingspeed.c @@ -0,0 +1,1266 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2012 Tieto Poland + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#include + +#include "lib/bluetooth.h" +#include "lib/sdp.h" +#include "lib/uuid.h" + +#include "gdbus/gdbus.h" + +#include "src/plugin.h" +#include "src/adapter.h" +#include "src/device.h" +#include "src/profile.h" +#include "src/service.h" +#include "src/dbus-common.h" +#include "src/shared/util.h" +#include "src/error.h" +#include "attrib/gattrib.h" +#include "attrib/att.h" +#include "attrib/gatt.h" +#include "src/attio.h" +#include "src/log.h" + +/* min length for ATT indication or notification: opcode (1b) + handle (2b) */ +#define ATT_HDR_LEN 3 + +#define ATT_TIMEOUT 30 + +#define CYCLINGSPEED_INTERFACE "org.bluez.CyclingSpeed1" +#define CYCLINGSPEED_MANAGER_INTERFACE "org.bluez.CyclingSpeedManager1" +#define CYCLINGSPEED_WATCHER_INTERFACE "org.bluez.CyclingSpeedWatcher1" + +#define WHEEL_REV_SUPPORT 0x01 +#define CRANK_REV_SUPPORT 0x02 +#define MULTI_SENSOR_LOC_SUPPORT 0x04 + +#define WHEEL_REV_PRESENT 0x01 +#define CRANK_REV_PRESENT 0x02 + +#define SET_CUMULATIVE_VALUE 0x01 +#define START_SENSOR_CALIBRATION 0x02 +#define UPDATE_SENSOR_LOC 0x03 +#define REQUEST_SUPPORTED_SENSOR_LOC 0x04 +#define RESPONSE_CODE 0x10 + +#define RSP_SUCCESS 0x01 +#define RSP_NOT_SUPPORTED 0x02 +#define RSP_INVALID_PARAM 0x03 +#define RSP_FAILED 0x04 + +struct csc; + +struct controlpoint_req { + struct csc *csc; + uint8_t opcode; + guint timeout; + GDBusPendingReply reply_id; + DBusMessage *msg; + + uint8_t pending_location; +}; + +struct csc_adapter { + struct btd_adapter *adapter; + GSList *devices; /* list of registered devices */ + GSList *watchers; +}; + +struct csc { + struct btd_device *dev; + struct csc_adapter *cadapter; + + GAttrib *attrib; + guint attioid; + /* attio id for measurement characteristics value notifications */ + guint attio_measurement_id; + /* attio id for SC Control Point characteristics value indications */ + guint attio_controlpoint_id; + + struct att_range *svc_range; + + uint16_t measurement_ccc_handle; + uint16_t controlpoint_val_handle; + + uint16_t feature; + gboolean has_location; + uint8_t location; + uint8_t num_locations; + uint8_t *locations; + + struct controlpoint_req *pending_req; +}; + +struct watcher { + struct csc_adapter *cadapter; + guint id; + char *srv; + char *path; +}; + +struct measurement { + struct csc *csc; + + bool has_wheel_rev; + uint32_t wheel_rev; + uint16_t last_wheel_time; + + bool has_crank_rev; + uint16_t crank_rev; + uint16_t last_crank_time; +}; + +struct characteristic { + struct csc *csc; + char uuid[MAX_LEN_UUID_STR + 1]; +}; + +static GSList *csc_adapters = NULL; + +static const char * const location_enum[] = { + "other", "top-of-shoe", "in-shoe", "hip", "front-wheel", "left-crank", + "right-crank", "left-pedal", "right-pedal", "front-hub", + "rear-dropout", "chainstay", "rear-wheel", "rear-hub" +}; + +static const char *location2str(uint8_t value) +{ + if (value < G_N_ELEMENTS(location_enum)) + return location_enum[value]; + + info("Body Sensor Location [%d] is RFU", value); + + return location_enum[0]; +} + +static int str2location(const char *location) +{ + size_t i; + + for (i = 0; i < G_N_ELEMENTS(location_enum); i++) + if (!strcmp(location_enum[i], location)) + return i; + + return -1; +} + +static int cmp_adapter(gconstpointer a, gconstpointer b) +{ + const struct csc_adapter *cadapter = a; + const struct btd_adapter *adapter = b; + + if (adapter == cadapter->adapter) + return 0; + + return -1; +} + +static int cmp_device(gconstpointer a, gconstpointer b) +{ + const struct csc *csc = a; + const struct btd_device *dev = b; + + if (dev == csc->dev) + return 0; + + return -1; +} + +static int cmp_watcher(gconstpointer a, gconstpointer b) +{ + const struct watcher *watcher = a; + const struct watcher *match = b; + int ret; + + ret = g_strcmp0(watcher->srv, match->srv); + if (ret != 0) + return ret; + + return g_strcmp0(watcher->path, match->path); +} + +static struct csc_adapter *find_csc_adapter(struct btd_adapter *adapter) +{ + GSList *l = g_slist_find_custom(csc_adapters, adapter, cmp_adapter); + + if (!l) + return NULL; + + return l->data; +} + +static void destroy_watcher(gpointer user_data) +{ + struct watcher *watcher = user_data; + + g_free(watcher->path); + g_free(watcher->srv); + g_free(watcher); +} + +static struct watcher *find_watcher(GSList *list, const char *sender, + const char *path) +{ + struct watcher *match; + GSList *l; + + match = g_new0(struct watcher, 1); + match->srv = g_strdup(sender); + match->path = g_strdup(path); + + l = g_slist_find_custom(list, match, cmp_watcher); + destroy_watcher(match); + + if (l != NULL) + return l->data; + + return NULL; +} + +static void remove_watcher(gpointer user_data) +{ + struct watcher *watcher = user_data; + + g_dbus_remove_watch(btd_get_dbus_connection(), watcher->id); +} + +static void destroy_csc_adapter(gpointer user_data) +{ + struct csc_adapter *cadapter = user_data; + + g_slist_free_full(cadapter->watchers, remove_watcher); + + g_free(cadapter); +} + +static void destroy_csc(gpointer user_data) +{ + struct csc *csc = user_data; + + if (csc->attioid > 0) + btd_device_remove_attio_callback(csc->dev, csc->attioid); + + if (csc->attrib != NULL) { + g_attrib_unregister(csc->attrib, csc->attio_measurement_id); + g_attrib_unregister(csc->attrib, csc->attio_controlpoint_id); + g_attrib_unref(csc->attrib); + } + + btd_device_unref(csc->dev); + g_free(csc->svc_range); + g_free(csc->locations); + g_free(csc); +} + +static void char_write_cb(guint8 status, const guint8 *pdu, guint16 len, + gpointer user_data) +{ + char *msg = user_data; + + if (status != 0) + error("%s failed", msg); + + g_free(msg); +} + +static gboolean controlpoint_timeout(gpointer user_data) +{ + struct controlpoint_req *req = user_data; + + if (req->opcode == UPDATE_SENSOR_LOC) { + g_dbus_pending_property_error(req->reply_id, + ERROR_INTERFACE ".Failed", + "Operation failed (timeout)"); + } else if (req->opcode == SET_CUMULATIVE_VALUE) { + DBusMessage *reply; + + reply = btd_error_failed(req->msg, + "Operation failed (timeout)"); + + g_dbus_send_message(btd_get_dbus_connection(), reply); + + dbus_message_unref(req->msg); + } + + req->csc->pending_req = NULL; + g_free(req); + + return FALSE; +} + +static void controlpoint_write_cb(guint8 status, const guint8 *pdu, guint16 len, + gpointer user_data) +{ + struct controlpoint_req *req = user_data; + + if (status == 0) { + req->timeout = g_timeout_add_seconds(ATT_TIMEOUT, + controlpoint_timeout, + req); + return; + } + + error("SC Control Point write failed (opcode=%d)", req->opcode); + + if (req->opcode == UPDATE_SENSOR_LOC) { + g_dbus_pending_property_error(req->reply_id, + ERROR_INTERFACE ".Failed", + "Operation failed (%d)", status); + } else if (req->opcode == SET_CUMULATIVE_VALUE) { + DBusMessage *reply; + + reply = btd_error_failed(req->msg, "Operation failed"); + + g_dbus_send_message(btd_get_dbus_connection(), reply); + + dbus_message_unref(req->msg); + } + + req->csc->pending_req = NULL; + g_free(req); +} + +static void read_supported_locations(struct csc *csc) +{ + struct controlpoint_req *req; + + req = g_new0(struct controlpoint_req, 1); + req->csc = csc; + req->opcode = REQUEST_SUPPORTED_SENSOR_LOC; + + csc->pending_req = req; + + gatt_write_char(csc->attrib, csc->controlpoint_val_handle, + &req->opcode, sizeof(req->opcode), + controlpoint_write_cb, req); +} + +static void read_feature_cb(guint8 status, const guint8 *pdu, guint16 len, + gpointer user_data) +{ + struct csc *csc = user_data; + uint8_t value[2]; + ssize_t vlen; + + if (status) { + error("CSC Feature read failed: %s", att_ecode2str(status)); + return; + } + + vlen = dec_read_resp(pdu, len, value, sizeof(value)); + if (vlen < 0) { + error("Protocol error"); + return; + } + + if (vlen != sizeof(value)) { + error("Invalid value length for CSC Feature"); + return; + } + + csc->feature = get_le16(value); + + if ((csc->feature & MULTI_SENSOR_LOC_SUPPORT) + && (csc->locations == NULL)) + read_supported_locations(csc); +} + +static void read_location_cb(guint8 status, const guint8 *pdu, + guint16 len, gpointer user_data) +{ + struct csc *csc = user_data; + uint8_t value; + ssize_t vlen; + + if (status) { + error("Sensor Location read failed: %s", att_ecode2str(status)); + return; + } + + vlen = dec_read_resp(pdu, len, &value, sizeof(value)); + if (vlen < 0) { + error("Protocol error"); + return; + } + + if (vlen != sizeof(value)) { + error("Invalid value length for Sensor Location"); + return; + } + + csc->has_location = TRUE; + csc->location = value; + + g_dbus_emit_property_changed(btd_get_dbus_connection(), + device_get_path(csc->dev), + CYCLINGSPEED_INTERFACE, "Location"); +} + +static void discover_desc_cb(guint8 status, GSList *descs, gpointer user_data) +{ + struct characteristic *ch = user_data; + struct gatt_desc *desc; + uint8_t attr_val[2]; + char *msg = NULL; + + if (status != 0) { + error("Discover %s descriptors failed: %s", ch->uuid, + att_ecode2str(status)); + goto done; + } + + /* There will be only one descriptor on list and it will be CCC */ + desc = descs->data; + + if (g_strcmp0(ch->uuid, CSC_MEASUREMENT_UUID) == 0) { + ch->csc->measurement_ccc_handle = desc->handle; + + if (g_slist_length(ch->csc->cadapter->watchers) == 0) { + put_le16(0x0000, attr_val); + msg = g_strdup("Disable measurement"); + } else { + put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, + attr_val); + msg = g_strdup("Enable measurement"); + } + } else if (g_strcmp0(ch->uuid, SC_CONTROL_POINT_UUID) == 0) { + put_le16(GATT_CLIENT_CHARAC_CFG_IND_BIT, attr_val); + msg = g_strdup("Enable SC Control Point indications"); + } else { + goto done; + } + + gatt_write_char(ch->csc->attrib, desc->handle, attr_val, + sizeof(attr_val), char_write_cb, msg); + +done: + g_free(ch); +} + +static void discover_desc(struct csc *csc, struct gatt_char *c, + struct gatt_char *c_next) +{ + struct characteristic *ch; + uint16_t start, end; + bt_uuid_t uuid; + + start = c->value_handle + 1; + + if (c_next != NULL) { + if (start == c_next->handle) + return; + end = c_next->handle - 1; + } else if (c->value_handle != csc->svc_range->end) { + end = csc->svc_range->end; + } else { + return; + } + + ch = g_new0(struct characteristic, 1); + ch->csc = csc; + memcpy(ch->uuid, c->uuid, sizeof(c->uuid)); + + bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID); + + gatt_discover_desc(csc->attrib, start, end, &uuid, discover_desc_cb, + ch); +} + +static void update_watcher(gpointer data, gpointer user_data) +{ + struct watcher *w = data; + struct measurement *m = user_data; + struct csc *csc = m->csc; + const char *path = device_get_path(csc->dev); + DBusMessageIter iter; + DBusMessageIter dict; + DBusMessage *msg; + + msg = dbus_message_new_method_call(w->srv, w->path, + CYCLINGSPEED_WATCHER_INTERFACE, "MeasurementReceived"); + if (msg == NULL) + return; + + dbus_message_iter_init_append(msg, &iter); + + dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH , &path); + + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); + + if (m->has_wheel_rev) { + dict_append_entry(&dict, "WheelRevolutions", + DBUS_TYPE_UINT32, &m->wheel_rev); + dict_append_entry(&dict, "LastWheelEventTime", + DBUS_TYPE_UINT16, &m->last_wheel_time); + } + + if (m->has_crank_rev) { + dict_append_entry(&dict, "CrankRevolutions", + DBUS_TYPE_UINT16, &m->crank_rev); + dict_append_entry(&dict, "LastCrankEventTime", + DBUS_TYPE_UINT16, &m->last_crank_time); + } + + dbus_message_iter_close_container(&iter, &dict); + + dbus_message_set_no_reply(msg, TRUE); + g_dbus_send_message(btd_get_dbus_connection(), msg); +} + +static void process_measurement(struct csc *csc, const uint8_t *pdu, + uint16_t len) +{ + struct measurement m; + uint8_t flags; + + flags = *pdu; + + pdu++; + len--; + + memset(&m, 0, sizeof(m)); + + if ((flags & WHEEL_REV_PRESENT) && (csc->feature & WHEEL_REV_SUPPORT)) { + if (len < 6) { + error("Wheel revolutions data fields missing"); + return; + } + + m.has_wheel_rev = true; + m.wheel_rev = get_le32(pdu); + m.last_wheel_time = get_le16(pdu + 4); + pdu += 6; + len -= 6; + } + + if ((flags & CRANK_REV_PRESENT) && (csc->feature & CRANK_REV_SUPPORT)) { + if (len < 4) { + error("Crank revolutions data fields missing"); + return; + } + + m.has_crank_rev = true; + m.crank_rev = get_le16(pdu); + m.last_crank_time = get_le16(pdu + 2); + pdu += 4; + len -= 4; + } + + /* Notify all registered watchers */ + m.csc = csc; + g_slist_foreach(csc->cadapter->watchers, update_watcher, &m); +} + +static void measurement_notify_handler(const uint8_t *pdu, uint16_t len, + gpointer user_data) +{ + struct csc *csc = user_data; + + /* should be at least opcode (1b) + handle (2b) */ + if (len < 3) { + error("Invalid PDU received"); + return; + } + + process_measurement(csc, pdu + 3, len - 3); +} + +static void controlpoint_property_reply(struct controlpoint_req *req, + uint8_t code) +{ + switch (code) { + case RSP_SUCCESS: + g_dbus_pending_property_success(req->reply_id); + break; + + case RSP_NOT_SUPPORTED: + g_dbus_pending_property_error(req->reply_id, + ERROR_INTERFACE ".NotSupported", + "Feature is not supported"); + break; + + case RSP_INVALID_PARAM: + g_dbus_pending_property_error(req->reply_id, + ERROR_INTERFACE ".InvalidArguments", + "Invalid arguments in method call"); + break; + + case RSP_FAILED: + g_dbus_pending_property_error(req->reply_id, + ERROR_INTERFACE ".Failed", + "Operation failed"); + break; + + default: + g_dbus_pending_property_error(req->reply_id, + ERROR_INTERFACE ".Failed", + "Operation failed (%d)", code); + break; + } +} + +static void controlpoint_method_reply(struct controlpoint_req *req, + uint8_t code) +{ + DBusMessage *reply; + + switch (code) { + case RSP_SUCCESS: + reply = dbus_message_new_method_return(req->msg); + break; + case RSP_NOT_SUPPORTED: + reply = btd_error_not_supported(req->msg); + break; + case RSP_INVALID_PARAM: + reply = btd_error_invalid_args(req->msg); + break; + case RSP_FAILED: + reply = btd_error_failed(req->msg, "Failed"); + break; + default: + reply = btd_error_failed(req->msg, "Unknown error"); + break; + } + + g_dbus_send_message(btd_get_dbus_connection(), reply); + + dbus_message_unref(req->msg); +} + +static void controlpoint_ind_handler(const uint8_t *pdu, uint16_t len, + gpointer user_data) +{ + struct csc *csc = user_data; + struct controlpoint_req *req = csc->pending_req; + uint8_t opcode; + uint8_t req_opcode; + uint8_t rsp_code; + uint8_t *opdu; + uint16_t olen; + size_t plen; + + if (len < ATT_HDR_LEN) { + error("Invalid PDU received"); + return; + } + + /* skip ATT header */ + pdu += ATT_HDR_LEN; + len -= ATT_HDR_LEN; + + if (len < 1) { + error("Op Code missing"); + goto done; + } + + opcode = *pdu; + pdu++; + len--; + + if (opcode != RESPONSE_CODE) { + DBG("Unsupported Op Code received (%d)", opcode); + goto done; + } + + if (len < 2) { + error("Invalid Response Code PDU received"); + goto done; + } + + req_opcode = *pdu; + rsp_code = *(pdu + 1); + pdu += 2; + len -= 2; + + if (req == NULL || req->opcode != req_opcode) { + DBG("Indication received without pending request"); + goto done; + } + + switch (req->opcode) { + case SET_CUMULATIVE_VALUE: + controlpoint_method_reply(req, rsp_code); + break; + + case REQUEST_SUPPORTED_SENSOR_LOC: + if (rsp_code == RSP_SUCCESS) { + csc->num_locations = len; + csc->locations = g_memdup(pdu, len); + } else { + error("Failed to read Supported Sendor Locations"); + } + break; + + case UPDATE_SENSOR_LOC: + csc->location = req->pending_location; + + controlpoint_property_reply(req, rsp_code); + + g_dbus_emit_property_changed(btd_get_dbus_connection(), + device_get_path(csc->dev), + CYCLINGSPEED_INTERFACE, "Location"); + break; + } + + csc->pending_req = NULL; + g_source_remove(req->timeout); + g_free(req); + +done: + opdu = g_attrib_get_buffer(csc->attrib, &plen); + olen = enc_confirmation(opdu, plen); + if (olen > 0) + g_attrib_send(csc->attrib, 0, opdu, olen, NULL, NULL, NULL); +} + +static void discover_char_cb(uint8_t status, GSList *chars, void *user_data) +{ + struct csc *csc = user_data; + uint16_t feature_val_handle = 0; + + if (status) { + error("Discover CSCS characteristics: %s", + att_ecode2str(status)); + return; + } + + for (; chars; chars = chars->next) { + struct gatt_char *c = chars->data; + struct gatt_char *c_next = + (chars->next ? chars->next->data : NULL); + + if (g_strcmp0(c->uuid, CSC_MEASUREMENT_UUID) == 0) { + csc->attio_measurement_id = + g_attrib_register(csc->attrib, + ATT_OP_HANDLE_NOTIFY, c->value_handle, + measurement_notify_handler, csc, NULL); + + discover_desc(csc, c, c_next); + } else if (g_strcmp0(c->uuid, CSC_FEATURE_UUID) == 0) { + feature_val_handle = c->value_handle; + } else if (g_strcmp0(c->uuid, SENSOR_LOCATION_UUID) == 0) { + DBG("Sensor Location supported"); + gatt_read_char(csc->attrib, c->value_handle, + read_location_cb, csc); + } else if (g_strcmp0(c->uuid, SC_CONTROL_POINT_UUID) == 0) { + DBG("SC Control Point supported"); + csc->controlpoint_val_handle = c->value_handle; + + csc->attio_controlpoint_id = g_attrib_register( + csc->attrib, ATT_OP_HANDLE_IND, + c->value_handle, + controlpoint_ind_handler, csc, NULL); + + discover_desc(csc, c, c_next); + } + } + + if (feature_val_handle > 0) + gatt_read_char(csc->attrib, feature_val_handle, + read_feature_cb, csc); +} + +static void enable_measurement(gpointer data, gpointer user_data) +{ + struct csc *csc = data; + uint16_t handle = csc->measurement_ccc_handle; + uint8_t value[2]; + char *msg; + + if (csc->attrib == NULL || !handle) + return; + + put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value); + msg = g_strdup("Enable measurement"); + + gatt_write_char(csc->attrib, handle, value, sizeof(value), + char_write_cb, msg); +} + +static void disable_measurement(gpointer data, gpointer user_data) +{ + struct csc *csc = data; + uint16_t handle = csc->measurement_ccc_handle; + uint8_t value[2]; + char *msg; + + if (csc->attrib == NULL || !handle) + return; + + put_le16(0x0000, value); + msg = g_strdup("Disable measurement"); + + gatt_write_char(csc->attrib, handle, value, sizeof(value), + char_write_cb, msg); +} + +static void attio_connected_cb(GAttrib *attrib, gpointer user_data) +{ + struct csc *csc = user_data; + + DBG(""); + + csc->attrib = g_attrib_ref(attrib); + + gatt_discover_char(csc->attrib, csc->svc_range->start, + csc->svc_range->end, NULL, + discover_char_cb, csc); +} + +static void attio_disconnected_cb(gpointer user_data) +{ + struct csc *csc = user_data; + + DBG(""); + + if (csc->attio_measurement_id > 0) { + g_attrib_unregister(csc->attrib, csc->attio_measurement_id); + csc->attio_measurement_id = 0; + } + + if (csc->attio_controlpoint_id > 0) { + g_attrib_unregister(csc->attrib, csc->attio_controlpoint_id); + csc->attio_controlpoint_id = 0; + } + + g_attrib_unref(csc->attrib); + csc->attrib = NULL; +} + +static void watcher_exit_cb(DBusConnection *conn, void *user_data) +{ + struct watcher *watcher = user_data; + struct csc_adapter *cadapter = watcher->cadapter; + + DBG("cycling watcher [%s] disconnected", watcher->path); + + cadapter->watchers = g_slist_remove(cadapter->watchers, watcher); + g_dbus_remove_watch(conn, watcher->id); + + if (g_slist_length(cadapter->watchers) == 0) + g_slist_foreach(cadapter->devices, disable_measurement, 0); +} + +static DBusMessage *register_watcher(DBusConnection *conn, DBusMessage *msg, + void *data) +{ + struct csc_adapter *cadapter = data; + struct watcher *watcher; + const char *sender = dbus_message_get_sender(msg); + char *path; + + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID)) + return btd_error_invalid_args(msg); + + watcher = find_watcher(cadapter->watchers, sender, path); + if (watcher != NULL) + return btd_error_already_exists(msg); + + watcher = g_new0(struct watcher, 1); + watcher->cadapter = cadapter; + watcher->id = g_dbus_add_disconnect_watch(conn, sender, watcher_exit_cb, + watcher, destroy_watcher); + watcher->srv = g_strdup(sender); + watcher->path = g_strdup(path); + + if (g_slist_length(cadapter->watchers) == 0) + g_slist_foreach(cadapter->devices, enable_measurement, 0); + + cadapter->watchers = g_slist_prepend(cadapter->watchers, watcher); + + DBG("cycling watcher [%s] registered", path); + + return dbus_message_new_method_return(msg); +} + +static DBusMessage *unregister_watcher(DBusConnection *conn, DBusMessage *msg, + void *data) +{ + struct csc_adapter *cadapter = data; + struct watcher *watcher; + const char *sender = dbus_message_get_sender(msg); + char *path; + + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID)) + return btd_error_invalid_args(msg); + + watcher = find_watcher(cadapter->watchers, sender, path); + if (watcher == NULL) + return btd_error_does_not_exist(msg); + + cadapter->watchers = g_slist_remove(cadapter->watchers, watcher); + g_dbus_remove_watch(conn, watcher->id); + + if (g_slist_length(cadapter->watchers) == 0) + g_slist_foreach(cadapter->devices, disable_measurement, 0); + + DBG("cycling watcher [%s] unregistered", path); + + return dbus_message_new_method_return(msg); +} + +static const GDBusMethodTable cyclingspeed_manager_methods[] = { + { GDBUS_METHOD("RegisterWatcher", + GDBUS_ARGS({ "agent", "o" }), NULL, + register_watcher) }, + { GDBUS_METHOD("UnregisterWatcher", + GDBUS_ARGS({ "agent", "o" }), NULL, + unregister_watcher) }, + { } +}; + +static int csc_adapter_probe(struct btd_profile *p, struct btd_adapter *adapter) +{ + struct csc_adapter *cadapter; + + cadapter = g_new0(struct csc_adapter, 1); + cadapter->adapter = adapter; + + if (!g_dbus_register_interface(btd_get_dbus_connection(), + adapter_get_path(adapter), + CYCLINGSPEED_MANAGER_INTERFACE, + cyclingspeed_manager_methods, + NULL, NULL, cadapter, + destroy_csc_adapter)) { + error("D-Bus failed to register %s interface", + CYCLINGSPEED_MANAGER_INTERFACE); + destroy_csc_adapter(cadapter); + return -EIO; + } + + csc_adapters = g_slist_prepend(csc_adapters, cadapter); + + return 0; +} + +static void csc_adapter_remove(struct btd_profile *p, + struct btd_adapter *adapter) +{ + struct csc_adapter *cadapter; + + cadapter = find_csc_adapter(adapter); + if (cadapter == NULL) + return; + + csc_adapters = g_slist_remove(csc_adapters, cadapter); + + g_dbus_unregister_interface(btd_get_dbus_connection(), + adapter_get_path(cadapter->adapter), + CYCLINGSPEED_MANAGER_INTERFACE); +} + +static gboolean property_get_location(const GDBusPropertyTable *property, + DBusMessageIter *iter, void *data) +{ + struct csc *csc = data; + const char *loc; + + if (!csc->has_location) + return FALSE; + + loc = location2str(csc->location); + + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &loc); + + return TRUE; +} + +static void property_set_location(const GDBusPropertyTable *property, + DBusMessageIter *iter, + GDBusPendingPropertySet id, void *data) +{ + struct csc *csc = data; + char *loc; + int loc_val; + uint8_t att_val[2]; + struct controlpoint_req *req; + + if (csc->pending_req != NULL) { + g_dbus_pending_property_error(id, + ERROR_INTERFACE ".InProgress", + "Operation already in progress"); + return; + } + + if (!(csc->feature & MULTI_SENSOR_LOC_SUPPORT)) { + g_dbus_pending_property_error(id, + ERROR_INTERFACE ".NotSupported", + "Feature is not supported"); + return; + } + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) { + g_dbus_pending_property_error(id, + ERROR_INTERFACE ".InvalidArguments", + "Invalid arguments in method call"); + return; + } + + dbus_message_iter_get_basic(iter, &loc); + + loc_val = str2location(loc); + + if (loc_val < 0) { + g_dbus_pending_property_error(id, + ERROR_INTERFACE ".InvalidArguments", + "Invalid arguments in method call"); + return; + } + + req = g_new(struct controlpoint_req, 1); + req->csc = csc; + req->reply_id = id; + req->opcode = UPDATE_SENSOR_LOC; + req->pending_location = loc_val; + + csc->pending_req = req; + + att_val[0] = UPDATE_SENSOR_LOC; + att_val[1] = loc_val; + + gatt_write_char(csc->attrib, csc->controlpoint_val_handle, att_val, + sizeof(att_val), controlpoint_write_cb, req); +} + +static gboolean property_exists_location(const GDBusPropertyTable *property, + void *data) +{ + struct csc *csc = data; + + return csc->has_location; +} + +static gboolean property_get_locations(const GDBusPropertyTable *property, + DBusMessageIter *iter, void *data) +{ + struct csc *csc = data; + DBusMessageIter entry; + int i; + + if (!(csc->feature & MULTI_SENSOR_LOC_SUPPORT)) + return FALSE; + + dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, + DBUS_TYPE_STRING_AS_STRING, &entry); + for (i = 0; i < csc->num_locations; i++) { + char *loc = g_strdup(location2str(csc->locations[i])); + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &loc); + g_free(loc); + } + + dbus_message_iter_close_container(iter, &entry); + + return TRUE; +} + +static gboolean property_exists_locations(const GDBusPropertyTable *property, + void *data) +{ + struct csc *csc = data; + + return !!(csc->feature & MULTI_SENSOR_LOC_SUPPORT); +} + +static gboolean property_get_wheel_rev_sup(const GDBusPropertyTable *property, + DBusMessageIter *iter, void *data) +{ + struct csc *csc = data; + dbus_bool_t val; + + val = !!(csc->feature & WHEEL_REV_SUPPORT); + dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &val); + + return TRUE; +} + +static gboolean property_get_multi_loc_sup(const GDBusPropertyTable *property, + DBusMessageIter *iter, void *data) +{ + struct csc *csc = data; + dbus_bool_t val; + + val = !!(csc->feature & MULTI_SENSOR_LOC_SUPPORT); + dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &val); + + return TRUE; +} + +static const GDBusPropertyTable cyclingspeed_device_properties[] = { + { "Location", "s", property_get_location, property_set_location, + property_exists_location }, + { "SupportedLocations", "as", property_get_locations, NULL, + property_exists_locations }, + { "WheelRevolutionDataSupported", "b", property_get_wheel_rev_sup }, + { "MultipleLocationsSupported", "b", property_get_multi_loc_sup }, + { } +}; + +static DBusMessage *set_cumulative_wheel_rev(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + struct csc *csc = data; + dbus_uint32_t value; + struct controlpoint_req *req; + uint8_t att_val[5]; /* uint8 opcode + uint32 value */ + + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT32, &value, + DBUS_TYPE_INVALID)) + return btd_error_invalid_args(msg); + + if (csc->pending_req != NULL) + return btd_error_in_progress(msg); + + req = g_new(struct controlpoint_req, 1); + req->csc = csc; + req->opcode = SET_CUMULATIVE_VALUE; + req->msg = dbus_message_ref(msg); + + csc->pending_req = req; + + att_val[0] = SET_CUMULATIVE_VALUE; + put_le32(value, att_val + 1); + + gatt_write_char(csc->attrib, csc->controlpoint_val_handle, att_val, + sizeof(att_val), controlpoint_write_cb, req); + + return NULL; +} + +static const GDBusMethodTable cyclingspeed_device_methods[] = { + { GDBUS_ASYNC_METHOD("SetCumulativeWheelRevolutions", + GDBUS_ARGS({ "value", "u" }), NULL, + set_cumulative_wheel_rev) }, + { } +}; + +static int csc_device_probe(struct btd_service *service) +{ + struct btd_device *device = btd_service_get_device(service); + struct btd_adapter *adapter; + struct csc_adapter *cadapter; + struct csc *csc; + struct gatt_primary *prim; + + prim = btd_device_get_primary(device, CYCLING_SC_UUID); + if (prim == NULL) + return -EINVAL; + + adapter = device_get_adapter(device); + + cadapter = find_csc_adapter(adapter); + if (cadapter == NULL) + return -1; + + csc = g_new0(struct csc, 1); + csc->dev = btd_device_ref(device); + csc->cadapter = cadapter; + + if (!g_dbus_register_interface(btd_get_dbus_connection(), + device_get_path(device), + CYCLINGSPEED_INTERFACE, + cyclingspeed_device_methods, + NULL, + cyclingspeed_device_properties, + csc, destroy_csc)) { + error("D-Bus failed to register %s interface", + CYCLINGSPEED_INTERFACE); + destroy_csc(csc); + return -EIO; + } + + csc->svc_range = g_new0(struct att_range, 1); + csc->svc_range->start = prim->range.start; + csc->svc_range->end = prim->range.end; + + cadapter->devices = g_slist_prepend(cadapter->devices, csc); + + csc->attioid = btd_device_add_attio_callback(device, attio_connected_cb, + attio_disconnected_cb, csc); + + return 0; +} + +static void csc_device_remove(struct btd_service *service) +{ + struct btd_device *device = btd_service_get_device(service); + struct btd_adapter *adapter; + struct csc_adapter *cadapter; + struct csc *csc; + GSList *l; + + adapter = device_get_adapter(device); + + cadapter = find_csc_adapter(adapter); + if (cadapter == NULL) + return; + + l = g_slist_find_custom(cadapter->devices, device, cmp_device); + if (l == NULL) + return; + + csc = l->data; + + cadapter->devices = g_slist_remove(cadapter->devices, csc); + + g_dbus_unregister_interface(btd_get_dbus_connection(), + device_get_path(device), + CYCLINGSPEED_INTERFACE); +} + +static struct btd_profile cscp_profile = { + .name = "Cycling Speed and Cadence GATT Driver", + .remote_uuid = CYCLING_SC_UUID, + + .adapter_probe = csc_adapter_probe, + .adapter_remove = csc_adapter_remove, + + .device_probe = csc_device_probe, + .device_remove = csc_device_remove, +}; + +static int cyclingspeed_init(void) +{ + return btd_profile_register(&cscp_profile); +} + +static void cyclingspeed_exit(void) +{ + btd_profile_unregister(&cscp_profile); +} + +BLUETOOTH_PLUGIN_DEFINE(cyclingspeed, VERSION, + BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, + cyclingspeed_init, cyclingspeed_exit) diff --git a/profiles/gap/gas.c b/profiles/gap/gas.c index 47c8c25..ac9d4ac 100755 --- a/profiles/gap/gas.c +++ b/profiles/gap/gas.c @@ -154,6 +154,40 @@ static void handle_appearance(struct gas *gas, uint16_t value_handle) DBG("Failed to send request to read appearance"); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static void read_rpa_res_characteristic_value_cb(bool success, uint8_t att_ecode, + const uint8_t *value, uint16_t length, + void *user_data) +{ + struct gas *gas = user_data; + uint8_t rpa_res_support; + + if (!success) { + DBG("Reading RPA Resolution Char Value failed with ATT error: %u", att_ecode); + return; + } + + /* The RPA Resolution Char Value value is a 8-bit unsigned integer */ + if (length != 1) { + DBG("Malformed RPA resolution char value"); + return; + } + + rpa_res_support = *value; + + DBG("GAP RPA Resolution Char Value: %d", rpa_res_support); + + device_set_rpa_res_char_value(gas->device, rpa_res_support); +} + +static void handle_rpa_res_characteristic_value(struct gas *gas, uint16_t value_handle) +{ + if (!bt_gatt_client_read_value(gas->client, value_handle, + read_rpa_res_characteristic_value_cb, gas, NULL)) + DBG("Failed to send request to read RPA resolution Char Value"); +} +#endif + static bool uuid_cmp(uint16_t u16, const bt_uuid_t *uuid) { bt_uuid_t lhs; @@ -180,6 +214,10 @@ static void handle_characteristic(struct gatt_db_attribute *attr, handle_device_name(gas, value_handle); else if (uuid_cmp(GATT_CHARAC_APPEARANCE, &uuid)) handle_appearance(gas, value_handle); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + else if (uuid_cmp(GATT_CHARAC_CENTRAL_RPA_RESOLUTION, &uuid)) + handle_rpa_res_characteristic_value(gas, value_handle); +#endif else { char uuid_str[MAX_LEN_UUID_STR]; diff --git a/profiles/heartrate/heartrate.c b/profiles/heartrate/heartrate.c new file mode 100755 index 0000000..9e8c499 --- /dev/null +++ b/profiles/heartrate/heartrate.c @@ -0,0 +1,870 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2012 Tieto Poland + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include +#include + +#include "lib/bluetooth.h" +#include "lib/sdp.h" +#include "lib/uuid.h" + +#include "gdbus/gdbus.h" + +#include "src/plugin.h" +#include "src/adapter.h" +#include "src/dbus-common.h" +#include "src/device.h" +#include "src/profile.h" +#include "src/shared/util.h" +#include "src/service.h" +#include "src/error.h" +#include "attrib/gattrib.h" +#include "attrib/att.h" +#include "attrib/gatt.h" +#include "src/attio.h" +#include "src/log.h" + +#define HEART_RATE_INTERFACE "org.bluez.HeartRate1" +#define HEART_RATE_MANAGER_INTERFACE "org.bluez.HeartRateManager1" +#define HEART_RATE_WATCHER_INTERFACE "org.bluez.HeartRateWatcher1" + +#define HR_VALUE_FORMAT 0x01 +#define SENSOR_CONTACT_DETECTED 0x02 +#define SENSOR_CONTACT_SUPPORT 0x04 +#define ENERGY_EXP_STATUS 0x08 +#define RR_INTERVAL 0x10 + +struct heartrate_adapter { + struct btd_adapter *adapter; + GSList *devices; + GSList *watchers; +}; + +struct heartrate { + struct btd_device *dev; + struct heartrate_adapter *hradapter; + GAttrib *attrib; + guint attioid; + guint attionotid; + + struct att_range *svc_range; /* primary svc range */ + + uint16_t measurement_ccc_handle; + uint16_t hrcp_val_handle; + + gboolean has_location; + uint8_t location; +}; + +struct watcher { + struct heartrate_adapter *hradapter; + guint id; + char *srv; + char *path; +}; + +struct measurement { + struct heartrate *hr; + uint16_t value; + gboolean has_energy; + uint16_t energy; + gboolean has_contact; + gboolean contact; + uint16_t num_interval; + uint16_t *interval; +}; + +static GSList *heartrate_adapters = NULL; + +static const char * const location_enum[] = { + "other", + "chest", + "wrist", + "finger", + "hand", + "earlobe", + "foot", +}; + +static const char *location2str(uint8_t value) +{ + if (value < G_N_ELEMENTS(location_enum)) + return location_enum[value]; + + error("Body Sensor Location [%d] is RFU", value); + + return NULL; +} + +static int cmp_adapter(gconstpointer a, gconstpointer b) +{ + const struct heartrate_adapter *hradapter = a; + const struct btd_adapter *adapter = b; + + if (adapter == hradapter->adapter) + return 0; + + return -1; +} + +static int cmp_device(gconstpointer a, gconstpointer b) +{ + const struct heartrate *hr = a; + const struct btd_device *dev = b; + + if (dev == hr->dev) + return 0; + + return -1; +} + +static int cmp_watcher(gconstpointer a, gconstpointer b) +{ + const struct watcher *watcher = a; + const struct watcher *match = b; + int ret; + + ret = g_strcmp0(watcher->srv, match->srv); + if (ret != 0) + return ret; + + return g_strcmp0(watcher->path, match->path); +} + +static struct heartrate_adapter * +find_heartrate_adapter(struct btd_adapter *adapter) +{ + GSList *l = g_slist_find_custom(heartrate_adapters, adapter, + cmp_adapter); + if (!l) + return NULL; + + return l->data; +} + +static void destroy_watcher(gpointer user_data) +{ + struct watcher *watcher = user_data; + + g_free(watcher->path); + g_free(watcher->srv); + g_free(watcher); +} + +static struct watcher *find_watcher(GSList *list, const char *sender, + const char *path) +{ + struct watcher *match; + GSList *l; + + match = g_new0(struct watcher, 1); + match->srv = g_strdup(sender); + match->path = g_strdup(path); + + l = g_slist_find_custom(list, match, cmp_watcher); + destroy_watcher(match); + + if (l != NULL) + return l->data; + + return NULL; +} + +static void destroy_heartrate(gpointer user_data) +{ + struct heartrate *hr = user_data; + + if (hr->attioid > 0) + btd_device_remove_attio_callback(hr->dev, hr->attioid); + + if (hr->attrib != NULL) { + g_attrib_unregister(hr->attrib, hr->attionotid); + g_attrib_unref(hr->attrib); + } + + btd_device_unref(hr->dev); + g_free(hr->svc_range); + g_free(hr); +} + +static void remove_watcher(gpointer user_data) +{ + struct watcher *watcher = user_data; + + g_dbus_remove_watch(btd_get_dbus_connection(), watcher->id); +} + +static void destroy_heartrate_adapter(gpointer user_data) +{ + struct heartrate_adapter *hradapter = user_data; + + g_slist_free_full(hradapter->watchers, remove_watcher); + + g_free(hradapter); +} + +static void read_sensor_location_cb(guint8 status, const guint8 *pdu, + guint16 len, gpointer user_data) +{ + struct heartrate *hr = user_data; + uint8_t value; + ssize_t vlen; + + if (status != 0) { + error("Body Sensor Location read failed: %s", + att_ecode2str(status)); + return; + } + + vlen = dec_read_resp(pdu, len, &value, sizeof(value)); + if (vlen < 0) { + error("Protocol error"); + return; + } + + if (vlen != sizeof(value)) { + error("Invalid length for Body Sensor Location"); + return; + } + + hr->has_location = TRUE; + hr->location = value; +} + +static void char_write_cb(guint8 status, const guint8 *pdu, guint16 len, + gpointer user_data) +{ + char *msg = user_data; + + if (status != 0) + error("%s failed", msg); + + g_free(msg); +} + +static void update_watcher(gpointer data, gpointer user_data) +{ + struct watcher *w = data; + struct measurement *m = user_data; + struct heartrate *hr = m->hr; + const char *path = device_get_path(hr->dev); + DBusMessageIter iter; + DBusMessageIter dict; + DBusMessage *msg; + + msg = dbus_message_new_method_call(w->srv, w->path, + HEART_RATE_WATCHER_INTERFACE, "MeasurementReceived"); + if (msg == NULL) + return; + + dbus_message_iter_init_append(msg, &iter); + + dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH , &path); + + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); + + dict_append_entry(&dict, "Value", DBUS_TYPE_UINT16, &m->value); + + if (m->has_energy) + dict_append_entry(&dict, "Energy", DBUS_TYPE_UINT16, + &m->energy); + + if (m->has_contact) + dict_append_entry(&dict, "Contact", DBUS_TYPE_BOOLEAN, + &m->contact); + + if (m->num_interval > 0) + dict_append_array(&dict, "Interval", DBUS_TYPE_UINT16, + &m->interval, m->num_interval); + + dbus_message_iter_close_container(&iter, &dict); + + dbus_message_set_no_reply(msg, TRUE); + g_dbus_send_message(btd_get_dbus_connection(), msg); +} + +static void process_measurement(struct heartrate *hr, const uint8_t *pdu, + uint16_t len) +{ + struct measurement m; + uint8_t flags; + + flags = *pdu; + + pdu++; + len--; + + memset(&m, 0, sizeof(m)); + + if (flags & HR_VALUE_FORMAT) { + if (len < 2) { + error("Heart Rate Measurement field missing"); + return; + } + + m.value = get_le16(pdu); + pdu += 2; + len -= 2; + } else { + if (len < 1) { + error("Heart Rate Measurement field missing"); + return; + } + + m.value = *pdu; + pdu++; + len--; + } + + if (flags & ENERGY_EXP_STATUS) { + if (len < 2) { + error("Energy Expended field missing"); + return; + } + + m.has_energy = TRUE; + m.energy = get_le16(pdu); + pdu += 2; + len -= 2; + } + + if (flags & RR_INTERVAL) { + int i; + + if (len == 0 || (len % 2 != 0)) { + error("RR-Interval field malformed"); + return; + } + + m.num_interval = len / 2; + m.interval = g_new(uint16_t, m.num_interval); + + for (i = 0; i < m.num_interval; pdu += 2, i++) + m.interval[i] = get_le16(pdu); + } + + if (flags & SENSOR_CONTACT_SUPPORT) { + m.has_contact = TRUE; + m.contact = !!(flags & SENSOR_CONTACT_DETECTED); + } + + /* Notify all registered watchers */ + m.hr = hr; + g_slist_foreach(hr->hradapter->watchers, update_watcher, &m); + + g_free(m.interval); +} + +static void notify_handler(const uint8_t *pdu, uint16_t len, gpointer user_data) +{ + struct heartrate *hr = user_data; + + /* should be at least opcode (1b) + handle (2b) */ + if (len < 3) { + error("Invalid PDU received"); + return; + } + + process_measurement(hr, pdu + 3, len - 3); +} + +static void discover_ccc_cb(uint8_t status, GSList *descs, void *user_data) +{ + struct heartrate *hr = user_data; + struct gatt_desc *desc; + uint8_t attr_val[2]; + char *msg; + + if (status != 0) { + error("Discover Heart Rate Measurement descriptors failed: %s", + att_ecode2str(status)); + return; + } + + /* There will be only one descriptor on list and it will be CCC */ + desc = descs->data; + + hr->measurement_ccc_handle = desc->handle; + + if (g_slist_length(hr->hradapter->watchers) == 0) { + put_le16(0x0000, attr_val); + msg = g_strdup("Disable measurement"); + } else { + put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, attr_val); + msg = g_strdup("Enable measurement"); + } + + gatt_write_char(hr->attrib, desc->handle, attr_val, sizeof(attr_val), + char_write_cb, msg); +} + +static void discover_measurement_ccc(struct heartrate *hr, + struct gatt_char *c, struct gatt_char *c_next) +{ + uint16_t start, end; + bt_uuid_t uuid; + + start = c->value_handle + 1; + + if (c_next != NULL) { + if (start == c_next->handle) + return; + end = c_next->handle - 1; + } else if (c->value_handle != hr->svc_range->end) { + end = hr->svc_range->end; + } else { + return; + } + + bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID); + + gatt_discover_desc(hr->attrib, start, end, &uuid, discover_ccc_cb, hr); +} + +static void discover_char_cb(uint8_t status, GSList *chars, void *user_data) +{ + struct heartrate *hr = user_data; + + if (status) { + error("Discover HRS characteristics failed: %s", + att_ecode2str(status)); + return; + } + + for (; chars; chars = chars->next) { + struct gatt_char *c = chars->data; + + if (g_strcmp0(c->uuid, HEART_RATE_MEASUREMENT_UUID) == 0) { + struct gatt_char *c_next = + (chars->next ? chars->next->data : NULL); + + hr->attionotid = g_attrib_register(hr->attrib, + ATT_OP_HANDLE_NOTIFY, + c->value_handle, + notify_handler, hr, NULL); + + discover_measurement_ccc(hr, c, c_next); + } else if (g_strcmp0(c->uuid, BODY_SENSOR_LOCATION_UUID) == 0) { + DBG("Body Sensor Location supported"); + + gatt_read_char(hr->attrib, c->value_handle, + read_sensor_location_cb, hr); + } else if (g_strcmp0(c->uuid, + HEART_RATE_CONTROL_POINT_UUID) == 0) { + DBG("Heart Rate Control Point supported"); + hr->hrcp_val_handle = c->value_handle; + } + } +} + +static void enable_measurement(gpointer data, gpointer user_data) +{ + struct heartrate *hr = data; + uint16_t handle = hr->measurement_ccc_handle; + uint8_t value[2]; + char *msg; + + if (hr->attrib == NULL || !handle) + return; + + put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value); + msg = g_strdup("Enable measurement"); + + gatt_write_char(hr->attrib, handle, value, sizeof(value), + char_write_cb, msg); +} + +static void disable_measurement(gpointer data, gpointer user_data) +{ + struct heartrate *hr = data; + uint16_t handle = hr->measurement_ccc_handle; + uint8_t value[2]; + char *msg; + + if (hr->attrib == NULL || !handle) + return; + + put_le16(0x0000, value); + msg = g_strdup("Disable measurement"); + + gatt_write_char(hr->attrib, handle, value, sizeof(value), + char_write_cb, msg); +} + +static void attio_connected_cb(GAttrib *attrib, gpointer user_data) +{ + struct heartrate *hr = user_data; + + DBG(""); + + hr->attrib = g_attrib_ref(attrib); + + gatt_discover_char(hr->attrib, hr->svc_range->start, hr->svc_range->end, + NULL, discover_char_cb, hr); +} + +static void attio_disconnected_cb(gpointer user_data) +{ + struct heartrate *hr = user_data; + + DBG(""); + + if (hr->attionotid > 0) { + g_attrib_unregister(hr->attrib, hr->attionotid); + hr->attionotid = 0; + } + + g_attrib_unref(hr->attrib); + hr->attrib = NULL; +} + +static void watcher_exit_cb(DBusConnection *conn, void *user_data) +{ + struct watcher *watcher = user_data; + struct heartrate_adapter *hradapter = watcher->hradapter; + + DBG("heartrate watcher [%s] disconnected", watcher->path); + + hradapter->watchers = g_slist_remove(hradapter->watchers, watcher); + g_dbus_remove_watch(conn, watcher->id); + + if (g_slist_length(hradapter->watchers) == 0) + g_slist_foreach(hradapter->devices, disable_measurement, 0); +} + +static DBusMessage *register_watcher(DBusConnection *conn, DBusMessage *msg, + void *data) +{ + struct heartrate_adapter *hradapter = data; + struct watcher *watcher; + const char *sender = dbus_message_get_sender(msg); + char *path; + + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID)) + return btd_error_invalid_args(msg); + + watcher = find_watcher(hradapter->watchers, sender, path); + if (watcher != NULL) + return btd_error_already_exists(msg); + + watcher = g_new0(struct watcher, 1); + watcher->hradapter = hradapter; + watcher->id = g_dbus_add_disconnect_watch(conn, sender, watcher_exit_cb, + watcher, destroy_watcher); + watcher->srv = g_strdup(sender); + watcher->path = g_strdup(path); + + if (g_slist_length(hradapter->watchers) == 0) + g_slist_foreach(hradapter->devices, enable_measurement, 0); + + hradapter->watchers = g_slist_prepend(hradapter->watchers, watcher); + + DBG("heartrate watcher [%s] registered", path); + + return dbus_message_new_method_return(msg); +} + +static DBusMessage *unregister_watcher(DBusConnection *conn, DBusMessage *msg, + void *data) +{ + struct heartrate_adapter *hradapter = data; + struct watcher *watcher; + const char *sender = dbus_message_get_sender(msg); + char *path; + + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID)) + return btd_error_invalid_args(msg); + + watcher = find_watcher(hradapter->watchers, sender, path); + if (watcher == NULL) + return btd_error_does_not_exist(msg); + + hradapter->watchers = g_slist_remove(hradapter->watchers, watcher); + g_dbus_remove_watch(conn, watcher->id); + + if (g_slist_length(hradapter->watchers) == 0) + g_slist_foreach(hradapter->devices, disable_measurement, 0); + + DBG("heartrate watcher [%s] unregistered", path); + + return dbus_message_new_method_return(msg); +} + +static const GDBusMethodTable heartrate_manager_methods[] = { + { GDBUS_METHOD("RegisterWatcher", + GDBUS_ARGS({ "agent", "o" }), NULL, + register_watcher) }, + { GDBUS_METHOD("UnregisterWatcher", + GDBUS_ARGS({ "agent", "o" }), NULL, + unregister_watcher) }, + { } +}; + +static gboolean property_get_location(const GDBusPropertyTable *property, + DBusMessageIter *iter, void *data) +{ + struct heartrate *hr = data; + char *loc; + + if (!hr->has_location) + return FALSE; + + loc = g_strdup(location2str(hr->location)); + + if (loc == NULL) + return FALSE; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &loc); + + g_free(loc); + + return TRUE; +} + +static gboolean property_exists_location(const GDBusPropertyTable *property, + void *data) +{ + struct heartrate *hr = data; + + if (!hr->has_location || location2str(hr->location) == NULL) + return FALSE; + + return TRUE; +} + +static gboolean property_get_reset_supported(const GDBusPropertyTable *property, + DBusMessageIter *iter, void *data) +{ + struct heartrate *hr = data; + dbus_bool_t has_reset = !!hr->hrcp_val_handle; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &has_reset); + + return TRUE; +} + +static DBusMessage *hrcp_reset(DBusConnection *conn, DBusMessage *msg, + void *data) +{ + struct heartrate *hr = data; + uint8_t value; + char *vmsg; + + if (!hr->hrcp_val_handle) + return btd_error_not_supported(msg); + + if (!hr->attrib) + return btd_error_not_available(msg); + + value = 0x01; + vmsg = g_strdup("Reset Control Point"); + gatt_write_char(hr->attrib, hr->hrcp_val_handle, &value, + sizeof(value), char_write_cb, vmsg); + + DBG("Energy Expended Value has been reset"); + + return dbus_message_new_method_return(msg); +} + +static const GDBusMethodTable heartrate_device_methods[] = { + { GDBUS_METHOD("Reset", NULL, NULL, hrcp_reset) }, + { } +}; + +static const GDBusPropertyTable heartrate_device_properties[] = { + { "Location", "s", property_get_location, NULL, + property_exists_location }, + { "ResetSupported", "b", property_get_reset_supported }, + { } +}; + +static int heartrate_adapter_register(struct btd_adapter *adapter) +{ + struct heartrate_adapter *hradapter; + + hradapter = g_new0(struct heartrate_adapter, 1); + hradapter->adapter = adapter; + + if (!g_dbus_register_interface(btd_get_dbus_connection(), + adapter_get_path(adapter), + HEART_RATE_MANAGER_INTERFACE, + heartrate_manager_methods, + NULL, NULL, hradapter, + destroy_heartrate_adapter)) { + error("D-Bus failed to register %s interface", + HEART_RATE_MANAGER_INTERFACE); + destroy_heartrate_adapter(hradapter); + return -EIO; + } + + heartrate_adapters = g_slist_prepend(heartrate_adapters, hradapter); + + return 0; +} + +static void heartrate_adapter_unregister(struct btd_adapter *adapter) +{ + struct heartrate_adapter *hradapter; + + hradapter = find_heartrate_adapter(adapter); + if (hradapter == NULL) + return; + + heartrate_adapters = g_slist_remove(heartrate_adapters, hradapter); + + g_dbus_unregister_interface(btd_get_dbus_connection(), + adapter_get_path(hradapter->adapter), + HEART_RATE_MANAGER_INTERFACE); +} + +static int heartrate_device_register(struct btd_device *device, + struct gatt_primary *prim) +{ + struct btd_adapter *adapter; + struct heartrate_adapter *hradapter; + struct heartrate *hr; + + adapter = device_get_adapter(device); + + hradapter = find_heartrate_adapter(adapter); + + if (hradapter == NULL) + return -1; + + hr = g_new0(struct heartrate, 1); + hr->dev = btd_device_ref(device); + hr->hradapter = hradapter; + + if (!g_dbus_register_interface(btd_get_dbus_connection(), + device_get_path(device), + HEART_RATE_INTERFACE, + heartrate_device_methods, + NULL, + heartrate_device_properties, + hr, destroy_heartrate)) { + error("D-Bus failed to register %s interface", + HEART_RATE_INTERFACE); + destroy_heartrate(hr); + return -EIO; + } + + hr->svc_range = g_new0(struct att_range, 1); + hr->svc_range->start = prim->range.start; + hr->svc_range->end = prim->range.end; + + hradapter->devices = g_slist_prepend(hradapter->devices, hr); + + hr->attioid = btd_device_add_attio_callback(device, attio_connected_cb, + attio_disconnected_cb, hr); + + return 0; +} + +static void heartrate_device_unregister(struct btd_device *device) +{ + struct btd_adapter *adapter; + struct heartrate_adapter *hradapter; + struct heartrate *hr; + GSList *l; + + adapter = device_get_adapter(device); + + hradapter = find_heartrate_adapter(adapter); + if (hradapter == NULL) + return; + + l = g_slist_find_custom(hradapter->devices, device, cmp_device); + if (l == NULL) + return; + + hr = l->data; + + hradapter->devices = g_slist_remove(hradapter->devices, hr); + + g_dbus_unregister_interface(btd_get_dbus_connection(), + device_get_path(device), HEART_RATE_INTERFACE); +} + +static int heartrate_adapter_probe(struct btd_profile *p, + struct btd_adapter *adapter) +{ + return heartrate_adapter_register(adapter); +} + +static void heartrate_adapter_remove(struct btd_profile *p, + struct btd_adapter *adapter) +{ + heartrate_adapter_unregister(adapter); +} + +static int heartrate_device_probe(struct btd_service *service) +{ + struct btd_device *device = btd_service_get_device(service); + struct gatt_primary *prim; + + prim = btd_device_get_primary(device, HEART_RATE_UUID); + if (prim == NULL) + return -EINVAL; + + return heartrate_device_register(device, prim); +} + +static void heartrate_device_remove(struct btd_service *service) +{ + struct btd_device *device = btd_service_get_device(service); + + heartrate_device_unregister(device); +} + +static struct btd_profile hrp_profile = { + .name = "Heart Rate GATT Driver", + .remote_uuid = HEART_RATE_UUID, + + .device_probe = heartrate_device_probe, + .device_remove = heartrate_device_remove, + + .adapter_probe = heartrate_adapter_probe, + .adapter_remove = heartrate_adapter_remove, +}; + +static int heartrate_init(void) +{ + return btd_profile_register(&hrp_profile); +} + +static void heartrate_exit(void) +{ + btd_profile_unregister(&hrp_profile); +} + +BLUETOOTH_PLUGIN_DEFINE(heartrate, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, + heartrate_init, heartrate_exit) diff --git a/profiles/input/device.c b/profiles/input/device.c index a494ea2..ff553cb 100755 --- a/profiles/input/device.c +++ b/profiles/input/device.c @@ -87,6 +87,9 @@ struct input_device { uint8_t report_req_pending; guint report_req_timer; uint32_t report_rsp_id; +#ifdef TIZEN_BT_HID_DEVICE_ENABLE + char *role; +#endif }; static int idle_timeout = 0; @@ -333,8 +336,12 @@ static gboolean intr_watch_cb(GIOChannel *chan, GIOCondition cond, gpointer data btd_service_disconnecting_complete(idev->service, 0); /* Enter the auto-reconnect mode if needed */ +#ifdef TIZEN_BT_HID_DEVICE_ENABLE + if (idev->role == NULL) + input_device_enter_reconnect_mode(idev); +#else input_device_enter_reconnect_mode(idev); - +#endif return FALSE; } @@ -1002,10 +1009,21 @@ cleanup: static bool is_connected(struct input_device *idev) { +#ifdef TIZEN_BT_HID_DEVICE_ENABLE + if (idev->role == NULL) { + if (idev->uhid) + return (idev->intr_io != NULL && idev->ctrl_io != NULL); + else + return ioctl_is_connected(idev); + } else { + return (idev->intr_io != NULL && idev->ctrl_io != NULL); + } +#else if (idev->uhid) return (idev->intr_io != NULL && idev->ctrl_io != NULL); else return ioctl_is_connected(idev); +#endif } static int connection_disconnect(struct input_device *idev, uint32_t flags) @@ -1019,6 +1037,10 @@ static int connection_disconnect(struct input_device *idev, uint32_t flags) if (idev->ctrl_io) g_io_channel_shutdown(idev->ctrl_io, TRUE, NULL); +#ifdef TIZEN_BT_HID_DEVICE_ENABLE + if (idev->role != NULL) + btd_service_disconnecting_complete(idev->service, 0); +#endif if (idev->uhid) return 0; else @@ -1031,10 +1053,17 @@ static int input_device_connected(struct input_device *idev) if (idev->intr_io == NULL || idev->ctrl_io == NULL) return -ENOTCONN; - +#ifdef TIZEN_BT_HID_DEVICE_ENABLE + if (idev->role == NULL) { + err = hidp_add_connection(idev); + if (err < 0) + return err; + } +#else err = hidp_add_connection(idev); if (err < 0) return err; +#endif btd_service_connecting_complete(idev->service, 0); @@ -1052,11 +1081,19 @@ static void interrupt_connect_cb(GIOChannel *chan, GError *conn_err, err = -EIO; goto failed; } - +#ifdef TIZEN_BT_HID_DEVICE_ENABLE + if (idev->role == NULL) { + err = input_device_connected(idev); + if (err < 0) + goto failed; + } else { + btd_service_connecting_complete(idev->service, 0); + } +#else err = input_device_connected(idev); if (err < 0) goto failed; - +#endif if (idev->uhid) cond |= G_IO_IN; @@ -1230,7 +1267,9 @@ int input_device_connect(struct btd_service *service) DBG(""); idev = btd_service_get_user_data(service); - +#ifdef TIZEN_BT_HID_DEVICE_ENABLE + DBG("Role=%s", idev->role); +#endif if (idev->ctrl_io) return -EBUSY; @@ -1251,7 +1290,9 @@ int input_device_disconnect(struct btd_service *service) flags = device_is_temporary(idev->device) ? (1 << HIDP_VIRTUAL_CABLE_UNPLUG) : 0; - +#ifdef TIZEN_BT_HID_DEVICE_ENABLE + DBG("Role=%s", idev->role); +#endif err = connection_disconnect(idev, flags); if (err < 0) return err; @@ -1328,6 +1369,27 @@ static struct input_device *input_device_new(struct btd_service *service) return idev; } +#ifdef TIZEN_BT_HID_DEVICE_ENABLE +static struct input_device *input_device_role_new(struct btd_service *service) +{ + struct btd_device *device = btd_service_get_device(service); + const char *path = device_get_path(device); + struct btd_adapter *adapter = device_get_adapter(device); + struct input_device *idev; + + idev = g_new0(struct input_device, 1); + bacpy(&idev->src, btd_adapter_get_address(adapter)); + bacpy(&idev->dst, device_get_address(device)); + idev->service = btd_service_ref(service); + idev->device = btd_device_ref(device); + idev->path = g_strdup(path); + idev->role = g_strdup("device"); + idev->disable_sdp = 0; + idev->uhid = NULL; + return idev; +} +#endif + static gboolean property_get_reconnect_mode( const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) @@ -1345,6 +1407,37 @@ static const GDBusPropertyTable input_properties[] = { { } }; +#ifdef TIZEN_BT_HID_DEVICE_ENABLE +static DBusMessage *hid_device_fd(DBusConnection *conn, + DBusMessage *msg, void *user_data) +{ + struct input_device *idev = user_data; + GError *gerr = NULL; + DBusMessage *reply; + int ctrl_fd = -1; + int intr_fd = -1; + if (idev->ctrl_io == NULL || idev->intr_io == NULL) { + DBG("Return error reply"); + reply = g_dbus_create_error(msg, ERROR_INTERFACE ".InputError", + "%s", "NotConnected"); + g_error_free(gerr); + } else { + ctrl_fd = g_io_channel_unix_get_fd(idev->ctrl_io); + intr_fd = g_io_channel_unix_get_fd(idev->intr_io); + reply = g_dbus_create_reply(msg, DBUS_TYPE_UNIX_FD, + &ctrl_fd, DBUS_TYPE_UNIX_FD, &intr_fd ,DBUS_TYPE_INVALID); + } + + return reply; +} +static const GDBusMethodTable input_device_methods[] = { + { GDBUS_ASYNC_METHOD("GetFD", + NULL, GDBUS_ARGS({ "fd", "h" } , {"fd", "h"}), + hid_device_fd) }, + { } +}; +#endif + int input_device_register(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); @@ -1381,6 +1474,34 @@ int input_device_register(struct btd_service *service) return 0; } +#ifdef TIZEN_BT_HID_DEVICE_ENABLE +int input_device_role_register(struct btd_service *service) +{ + struct btd_device *device = btd_service_get_device(service); + const char *path = device_get_path(device); + struct input_device *idev; + + DBG("%s", path); + + idev = input_device_role_new(service); + if (!idev) + return -EINVAL; + if (g_dbus_register_interface(btd_get_dbus_connection(), + idev->path, INPUT_INTERFACE, + input_device_methods, NULL, + NULL, idev, + NULL) == FALSE) { + error("Unable to register %s interface", INPUT_INTERFACE); + input_device_free(idev); + return -EINVAL; + } + btd_service_set_user_data(service, idev); + + return 0; +} + +#endif + static struct input_device *find_device(const bdaddr_t *src, const bdaddr_t *dst) { @@ -1398,6 +1519,25 @@ static struct input_device *find_device(const bdaddr_t *src, return btd_service_get_user_data(service); } +#ifdef TIZEN_BT_HID_DEVICE_ENABLE +static struct input_device *find_device_role(const bdaddr_t *src, + const bdaddr_t *dst) +{ + struct btd_device *device; + struct btd_service *service; + + device = btd_adapter_find_device(adapter_find(src), dst, BDADDR_BREDR); + if (device == NULL) + return NULL; + + service = btd_device_get_service(device, HID_DEVICE_UUID); + if (service == NULL) + return NULL; + + return btd_service_get_user_data(service); +} +#endif + void input_device_unregister(struct btd_service *service) { struct btd_device *device = btd_service_get_device(service); @@ -1412,6 +1552,19 @@ void input_device_unregister(struct btd_service *service) input_device_free(idev); } +#ifdef TIZEN_BT_HID_DEVICE_ENABLE +void input_device_role_unregister(struct btd_service *service) +{ + struct btd_device *device = btd_service_get_device(service); + const char *path = device_get_path(device); + struct input_device *idev = btd_service_get_user_data(service); + + DBG("%s", path); + + input_device_free(idev); +} +#endif + static int input_device_connadd(struct input_device *idev) { int err; @@ -1443,6 +1596,16 @@ bool input_device_exists(const bdaddr_t *src, const bdaddr_t *dst) return false; } +#ifdef TIZEN_BT_HID_DEVICE_ENABLE +bool input_device_role_exists(const bdaddr_t *src, const bdaddr_t *dst) +{ + if (find_device_role(src, dst)) + return true; + + return false; +} +#endif + int input_device_set_channel(const bdaddr_t *src, const bdaddr_t *dst, int psm, GIOChannel *io) { @@ -1480,6 +1643,58 @@ int input_device_set_channel(const bdaddr_t *src, const bdaddr_t *dst, int psm, return 0; } +#ifdef TIZEN_BT_HID_DEVICE_ENABLE +int input_device_role_set_channel(const bdaddr_t *src, const bdaddr_t *dst, int psm, + GIOChannel *io) +{ + struct input_device *idev = find_device_role(src, dst); + GIOCondition cond = G_IO_HUP | G_IO_ERR | G_IO_NVAL; + + DBG("idev %p psm %d", idev, psm); + + if (!idev) + return -ENOENT; + + switch (psm) { + case L2CAP_PSM_HIDP_CTRL: + if (idev->ctrl_io) + return -EALREADY; + idev->ctrl_io = g_io_channel_ref(io); + idev->ctrl_watch = g_io_add_watch(idev->ctrl_io, cond, + ctrl_watch_cb, idev); + break; + case L2CAP_PSM_HIDP_INTR: + if (idev->intr_io) + return -EALREADY; + idev->intr_io = g_io_channel_ref(io); + idev->intr_watch = g_io_add_watch(idev->intr_io, cond, + intr_watch_cb, idev); + break; + } + if (idev->intr_io && idev->ctrl_io) { + btd_service_connecting_complete(idev->service, 0); + } + return 0; +} + +int input_device_role_close_channels(const bdaddr_t *src, const bdaddr_t *dst) +{ + struct input_device *idev = find_device(src, dst); + + if (!idev) + return -ENOENT; + + if (idev->intr_io) + g_io_channel_shutdown(idev->intr_io, TRUE, NULL); + + if (idev->ctrl_io) + g_io_channel_shutdown(idev->ctrl_io, TRUE, NULL); + + return 0; +} + +#endif + int input_device_close_channels(const bdaddr_t *src, const bdaddr_t *dst) { struct input_device *idev = find_device(src, dst); diff --git a/profiles/input/device.h b/profiles/input/device.h index 51a9aee..20aba31 100755 --- a/profiles/input/device.h +++ b/profiles/input/device.h @@ -33,6 +33,15 @@ void input_enable_userspace_hid(bool state); int input_device_register(struct btd_service *service); void input_device_unregister(struct btd_service *service); +#ifdef TIZEN_BT_HID_DEVICE_ENABLE +int input_device_role_register(struct btd_service *service); +void input_device_role_unregister(struct btd_service *service); +bool input_device_role_exists(const bdaddr_t *src, const bdaddr_t *dst); +int input_device_role_set_channel(const bdaddr_t *src, const bdaddr_t *dst, int psm, + GIOChannel *io); +int input_device_role_close_channels(const bdaddr_t *src, const bdaddr_t *dst); +#endif + bool input_device_exists(const bdaddr_t *src, const bdaddr_t *dst); int input_device_set_channel(const bdaddr_t *src, const bdaddr_t *dst, int psm, GIOChannel *io); diff --git a/profiles/input/manager.c b/profiles/input/manager.c index 1d31b06..2aaeb15 100755 --- a/profiles/input/manager.c +++ b/profiles/input/manager.c @@ -53,6 +53,19 @@ static void hid_server_remove(struct btd_profile *p, { server_stop(btd_adapter_get_address(adapter)); } +#ifdef TIZEN_BT_HID_DEVICE_ENABLE +static int hid_device_probe(struct btd_profile *p, struct btd_adapter *adapter) +{ + DBG("hid device probe"); + return server_start(btd_adapter_get_address(adapter)); +} + +static void hid_device_remove(struct btd_profile *p, + struct btd_adapter *adapter) +{ + server_stop(btd_adapter_get_address(adapter)); +} +#endif static struct btd_profile input_profile = { .name = "input-hid", @@ -70,6 +83,24 @@ static struct btd_profile input_profile = { .adapter_remove = hid_server_remove, }; +#ifdef TIZEN_BT_HID_DEVICE_ENABLE +static struct btd_profile input_device_profile = { + .name = "hid-device", + .local_uuid = HID_DEVICE_UUID, + .remote_uuid = HID_DEVICE_UUID, + + .auto_connect = false, + .connect = input_device_connect, + .disconnect = input_device_disconnect, + + .device_probe = input_device_role_register, + .device_remove = input_device_role_unregister, + + .adapter_probe = hid_device_probe, + .adapter_remove = hid_device_remove, +}; +#endif + static GKeyFile *load_config_file(const char *file) { GKeyFile *keyfile; @@ -117,7 +148,9 @@ static int input_init(void) } btd_profile_register(&input_profile); - +#ifdef TIZEN_BT_HID_DEVICE_ENABLE + btd_profile_register(&input_device_profile); +#endif if (config) g_key_file_free(config); @@ -127,6 +160,9 @@ static int input_init(void) static void input_exit(void) { btd_profile_unregister(&input_profile); +#ifdef TIZEN_BT_HID_DEVICE_ENABLE + btd_profile_unregister(&input_device_profile); +#endif } BLUETOOTH_PLUGIN_DEFINE(input, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, diff --git a/profiles/input/server.c b/profiles/input/server.c index eb3fcf8..2ee3b9b 100755 --- a/profiles/input/server.c +++ b/profiles/input/server.c @@ -57,6 +57,9 @@ struct input_server { GIOChannel *ctrl; GIOChannel *intr; struct confirm_data *confirm; +#ifdef TIZEN_BT_HID_DEVICE_ENABLE + char *role; +#endif }; static int server_cmp(gconstpointer s, gconstpointer user_data) @@ -182,7 +185,14 @@ static void connect_event_cb(GIOChannel *chan, GError *err, gpointer data) sixaxis_browse_sdp(&src, &dst, chan, psm); return; } - +#ifdef TIZEN_BT_HID_DEVICE_ENABLE + if (ret == -ENOENT) { + DBG("Connection request for device role"); + ret = input_device_role_set_channel(&src, &dst, psm, chan); + if (ret == 0) + return; + } +#endif error("Refusing input device connect: %s (%d)", strerror(-ret), -ret); /* Send unplug virtual cable to unknown devices */ @@ -208,8 +218,15 @@ static void auth_callback(DBusError *derr, void *user_data) } if (!input_device_exists(&server->src, &confirm->dst) && - !dev_is_sixaxis(&server->src, &confirm->dst)) + !dev_is_sixaxis(&server->src, &confirm->dst)) { +#ifdef TIZEN_BT_HID_DEVICE_ENABLE + if (!input_device_role_exists(&server->src, &confirm->dst)) { + return; + } +#else return; +#endif + } if (!bt_io_accept(confirm->io, connect_event_cb, server, NULL, &err)) { error("bt_io_accept: %s", err->message); @@ -260,8 +277,15 @@ static void confirm_event_cb(GIOChannel *chan, gpointer user_data) } if (!input_device_exists(&src, &dst) && !dev_is_sixaxis(&src, &dst)) { +#ifdef TIZEN_BT_HID_DEVICE_ENABLE + if (!input_device_role_exists(&src, &dst)) { + error("Refusing connection from %s: unknown device", addr); + goto drop; + } +#else error("Refusing connection from %s: unknown device", addr); goto drop; +#endif } server->confirm = g_new0(struct confirm_data, 1); @@ -344,3 +368,59 @@ void server_stop(const bdaddr_t *src) servers = g_slist_remove(servers, server); g_free(server); } + +#ifdef TIZEN_BT_HID_DEVICE_ENABLE +int server_device_start(const bdaddr_t *src) +{ + struct input_server *server; + GError *err = NULL; + + server = g_new0(struct input_server, 1); + bacpy(&server->src, src); + + server->ctrl = bt_io_listen(connect_event_cb, NULL, + server, NULL, &err, + BT_IO_OPT_SOURCE_BDADDR, src, + BT_IO_OPT_PSM, L2CAP_PSM_HIDP_CTRL, + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, + BT_IO_OPT_INVALID); + if (!server->ctrl) { + error("Failed to listen on control channel"); + } + + server->intr = bt_io_listen(NULL, confirm_event_cb, + server, NULL, &err, + BT_IO_OPT_SOURCE_BDADDR, src, + BT_IO_OPT_PSM, L2CAP_PSM_HIDP_INTR, + BT_IO_OPT_SEC_LEVEL, BT_IO_SEC_LOW, + BT_IO_OPT_INVALID); + if (!server->intr) { + error("Failed to listen on interrupt channel"); + } + server->role = strdup("device"); + servers = g_slist_append(servers, server); + + return 0; +} + +void server_device_stop(const bdaddr_t *src) +{ + struct input_server *server; + GSList *l; + + l = g_slist_find_custom(servers, src, server_cmp); + if (!l) + return; + + server = l->data; + + g_io_channel_shutdown(server->intr, TRUE, NULL); + g_io_channel_unref(server->intr); + + g_io_channel_shutdown(server->ctrl, TRUE, NULL); + g_io_channel_unref(server->ctrl); + g_free(server->role); + servers = g_slist_remove(servers, server); + g_free(server); +} +#endif diff --git a/profiles/network/bnep.c b/profiles/network/bnep.c index 9bf0b18..86d1eb9 100755 --- a/profiles/network/bnep.c +++ b/profiles/network/bnep.c @@ -54,6 +54,19 @@ static int ctl; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +/* Compatibility with old ioctls */ +#define OLD_BNEPCONADD 1 +#define OLD_BNEPCONDEL 2 +#define OLD_BNEPGETCONLIST 3 +#define OLD_BNEPGETCONINFO 4 + +static unsigned long bnepconnadd; +static unsigned long bnepconndel; +static unsigned long bnepgetconnlist; +static unsigned long bnepgetconninfo; +#endif + struct __service_16 { uint16_t dst; uint16_t src; @@ -88,7 +101,30 @@ int bnep_init(void) return err; } - +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +/* Temporary ioctl compatibility hack */ +{ + struct bnep_connlist_req req; + struct bnep_conninfo ci[1]; + + req.cnum = 1; + req.ci = ci; + + if (!ioctl(ctl, BNEPGETCONNLIST, &req)) { + /* New ioctls */ + bnepconnadd = BNEPCONNADD; + bnepconndel = BNEPCONNDEL; + bnepgetconnlist = BNEPGETCONNLIST; + bnepgetconninfo = BNEPGETCONNINFO; + } else { + /* Old ioctls */ + bnepconnadd = OLD_BNEPCONADD; + bnepconndel = OLD_BNEPCONDEL; + bnepgetconnlist = OLD_BNEPGETCONLIST; + bnepgetconninfo = OLD_BNEPGETCONINFO; + } +} +#endif return 0; } @@ -179,6 +215,11 @@ static int bnep_if_down(const char *devname) sk = socket(AF_INET, SOCK_DGRAM, 0); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (sk < 0) + return -1; +#endif + memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, devname, IF_NAMESIZE - 1); @@ -420,11 +461,12 @@ void bnep_disconnect(struct bnep *session) if (!session) return; +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY if (session->watch > 0) { g_source_remove(session->watch); session->watch = 0; } - +#endif if (session->io) { g_io_channel_unref(session->io); session->io = NULL; @@ -434,6 +476,7 @@ void bnep_disconnect(struct bnep *session) bnep_conndel(&session->dst_addr); } +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY static int bnep_add_to_bridge(const char *devname, const char *bridge) { int ifindex; @@ -448,7 +491,15 @@ static int bnep_add_to_bridge(const char *devname, const char *bridge) sk = socket(AF_INET, SOCK_STREAM, 0); if (sk < 0) return -1; - +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + err = ioctl(sk, SIOCBRADDBR, bridge); + if (err < 0) + { + info("bridge create err: %d", err); + close(sk); + return -errno; + } +#endif memset(&ifr, 0, sizeof(ifr)); strncpy(ifr.ifr_name, bridge, IFNAMSIZ - 1); ifr.ifr_ifindex = ifindex; @@ -465,6 +516,7 @@ static int bnep_add_to_bridge(const char *devname, const char *bridge) return err; } +#endif static int bnep_del_from_bridge(const char *devname, const char *bridge) { @@ -602,6 +654,20 @@ static uint16_t bnep_setup_decode(int sk, struct bnep_setup_conn_req *req, return BNEP_CONN_INVALID_DST; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +int bnep_if_down_wrapper(const char *devname) +{ + bnep_if_down(devname); + return 0; +} + +int bnep_conndel_wrapper(const bdaddr_t *dst) +{ + bnep_conndel(dst); + return 0; +} +#endif + static int bnep_server_add_legacy(int sk, uint16_t dst, char *bridge, char *iface, const bdaddr_t *addr, uint8_t *setup_data, int len) @@ -622,12 +688,14 @@ static int bnep_server_add_legacy(int sk, uint16_t dst, char *bridge, goto reply; } +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY err = bnep_add_to_bridge(iface, bridge); if (err < 0) { bnep_conndel(addr); rsp = BNEP_CONN_NOT_ALLOWED; goto reply; } +#endif err = bnep_if_up(iface); if (err < 0) { @@ -697,9 +765,11 @@ int bnep_server_add(int sk, char *bridge, char *iface, const bdaddr_t *addr, goto failed; } +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY err = bnep_add_to_bridge(iface, bridge); if (err < 0) goto failed_conn; +#endif err = bnep_if_up(iface); if (err < 0) @@ -710,7 +780,9 @@ int bnep_server_add(int sk, char *bridge, char *iface, const bdaddr_t *addr, failed_bridge: bnep_del_from_bridge(iface, bridge); +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY failed_conn: +#endif bnep_conndel(addr); return err; diff --git a/profiles/network/bnep.h b/profiles/network/bnep.h index e9f4c1c..d34ed03 100755 --- a/profiles/network/bnep.h +++ b/profiles/network/bnep.h @@ -40,3 +40,7 @@ void bnep_disconnect(struct bnep *session); int bnep_server_add(int sk, char *bridge, char *iface, const bdaddr_t *addr, uint8_t *setup_data, int len); void bnep_server_delete(char *bridge, char *iface, const bdaddr_t *addr); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +int bnep_if_down_wrapper(const char *devname); +int bnep_conndel_wrapper(const bdaddr_t *dst); +#endif diff --git a/profiles/network/connection.c b/profiles/network/connection.c index 5305ace..53d35fe 100755 --- a/profiles/network/connection.c +++ b/profiles/network/connection.c @@ -123,12 +123,14 @@ static void bnep_disconn_cb(gpointer data) DBusConnection *conn = btd_get_dbus_connection(); const char *path = device_get_path(nc->peer->device); +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY g_dbus_emit_property_changed(conn, path, NETWORK_PEER_INTERFACE, "Connected"); g_dbus_emit_property_changed(conn, path, NETWORK_PEER_INTERFACE, "Interface"); g_dbus_emit_property_changed(conn, path, NETWORK_PEER_INTERFACE, "UUID"); +#endif device_remove_disconnect_watch(nc->peer->device, nc->dc_id); nc->dc_id = 0; @@ -137,6 +139,14 @@ static void bnep_disconn_cb(gpointer data) info("%s disconnected", nc->dev); nc->state = DISCONNECTED; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + g_dbus_emit_property_changed(conn, path, + NETWORK_PEER_INTERFACE, "Connected"); + g_dbus_emit_property_changed(conn, path, + NETWORK_PEER_INTERFACE, "Interface"); + g_dbus_emit_property_changed(conn, path, + NETWORK_PEER_INTERFACE, "UUID"); +#endif memset(nc->dev, 0, sizeof(nc->dev)); strncpy(nc->dev, BNEP_INTERFACE, 16); nc->dev[15] = '\0'; @@ -179,9 +189,10 @@ static void cancel_connection(struct network_conn *nc, int err) if (nc->state == CONNECTED) bnep_disconnect(nc->session); +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY bnep_free(nc->session); nc->session = NULL; - +#endif nc->state = DISCONNECTED; } @@ -226,6 +237,9 @@ static void bnep_conn_cb(char *iface, int err, void *data) conn = btd_get_dbus_connection(); path = device_get_path(nc->peer->device); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + nc->state = CONNECTED; +#endif g_dbus_emit_property_changed(conn, path, NETWORK_PEER_INTERFACE, "Connected"); g_dbus_emit_property_changed(conn, path, @@ -233,7 +247,9 @@ static void bnep_conn_cb(char *iface, int err, void *data) g_dbus_emit_property_changed(conn, path, NETWORK_PEER_INTERFACE, "UUID"); +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY nc->state = CONNECTED; +#endif nc->dc_id = device_add_disconnect_watch(nc->peer->device, disconnect_cb, nc, NULL); diff --git a/profiles/network/server.c b/profiles/network/server.c index e69ffaf..19206a1 100755 --- a/profiles/network/server.c +++ b/profiles/network/server.c @@ -86,6 +86,11 @@ struct network_server { static GSList *adapters = NULL; static gboolean security = TRUE; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static gboolean server_disconnected_cb(GIOChannel *chan, + GIOCondition cond, gpointer user_data); +#endif + static struct network_adapter *find_adapter(GSList *list, struct btd_adapter *adapter) { @@ -151,6 +156,38 @@ static struct network_server *find_server_by_uuid(GSList *list, return NULL; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static struct network_session *find_session(GSList *list, GIOChannel *io) +{ + GSList *l; + + for (l = list; l; l = l->next) { + struct network_session *session = l->data; + + if (session && session->io == io) + return session; + } + + return NULL; +} + +static struct network_session *find_session_by_addr(GSList *list, + bdaddr_t dst_addr) +{ + GSList *l; + + for (l = list; l; l = l->next) { + struct network_session *session = l->data; + + if (!bacmp(&session->dst, &dst_addr)) + return session; + + } + + return NULL; +} +#endif + static sdp_record_t *server_record_new(const char *name, uint16_t id) { sdp_list_t *svclass, *pfseq, *apseq, *root, *aproto; @@ -160,8 +197,13 @@ static sdp_record_t *server_record_new(const char *name, uint16_t id) sdp_data_t *v, *p; uint16_t psm = BNEP_PSM, version = 0x0100; uint16_t security_desc = (security ? 0x0001 : 0x0000); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + uint16_t net_access_type = 0x000a; + uint32_t max_net_access_rate = 0x001312d0; +#else uint16_t net_access_type = 0xfffe; uint32_t max_net_access_rate = 0; +#endif const char *desc = "Network service"; sdp_record_t *record; @@ -303,6 +345,56 @@ static void setup_destroy(void *user_data) session_free(setup); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static gboolean server_disconnected_cb(GIOChannel *chan, + GIOCondition cond, gpointer user_data) +{ + struct network_server *ns = NULL; + struct network_session *session = NULL; + char address[20] = {0}; + const char* paddr = address; + char *name_str = NULL; + + info("server_disconnected_cb"); + + if (!user_data) + return FALSE; + + ns = (struct network_server *) user_data; + + session = find_session(ns->sessions, chan); + if (session) { + name_str = g_strdup(session->dev); + ba2str(&session->dst, address); + } else { + info("Session is not exist!"); + name_str = g_strdup("bnep"); + } + + g_dbus_emit_signal(btd_get_dbus_connection(), + adapter_get_path(ns->na->adapter), + NETWORK_SERVER_INTERFACE, "PeerDisconnected", + DBUS_TYPE_STRING, &name_str, + DBUS_TYPE_STRING, &paddr, + DBUS_TYPE_INVALID); + + if (session) { + ns->sessions = g_slist_remove(ns->sessions, session); + session_free(session); + } + + if (g_slist_length(ns->sessions) == 0 && + name_str != NULL) { + bnep_if_down_wrapper(name_str); + ns->sessions = NULL; + } + + g_free(name_str); + + return FALSE; +} +#endif + static gboolean bnep_setup(GIOChannel *chan, GIOCondition cond, gpointer user_data) { @@ -381,6 +473,30 @@ static gboolean bnep_setup(GIOChannel *chan, packet, n) < 0) error("BNEP server cannot be added"); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +if (ns) { + /* Emit connected signal to BT application */ + const gchar *adapter_path = adapter_get_path(na->adapter); + const char *pdev = na->setup->dev; + char address[24] = { 0 }; + char *paddr = address; + + ba2str(&na->setup->dst, paddr); + + ns->sessions = g_slist_append(ns->sessions, na->setup); + + g_dbus_emit_signal(btd_get_dbus_connection(), adapter_path, + NETWORK_SERVER_INTERFACE, "PeerConnected", + DBUS_TYPE_STRING, &pdev, + DBUS_TYPE_STRING, &paddr, + DBUS_TYPE_INVALID); + + na->setup->watch = g_io_add_watch_full(chan, G_PRIORITY_DEFAULT, + G_IO_HUP | G_IO_ERR | G_IO_NVAL, + server_disconnected_cb, ns, NULL); +} +#endif + na->setup = NULL; return FALSE; @@ -515,9 +631,11 @@ static void server_remove_sessions(struct network_server *ns) bnep_server_delete(ns->bridge, session->dev, &session->dst); } +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY g_slist_free_full(ns->sessions, session_free); ns->sessions = NULL; +#endif } static void server_disconnect(DBusConnection *conn, void *user_data) @@ -590,6 +708,11 @@ static DBusMessage *unregister_server(DBusConnection *conn, if (!ns) return btd_error_failed(msg, "Invalid UUID"); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (!ns->record_id) + return btd_error_not_available(msg); +#endif + reply = dbus_message_new_method_return(msg); if (!reply) return NULL; @@ -645,6 +768,94 @@ static void path_unregister(void *data) adapter_free(na); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static DBusMessage *disconnect_device(DBusConnection *conn, DBusMessage *msg, + void *data) +{ + struct network_adapter *na = data; + struct network_server *ns; + struct network_session *session; + const char *addr = NULL; + bdaddr_t dst_addr; + + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &addr, + DBUS_TYPE_INVALID)) + return btd_error_invalid_args(msg); + + ns = find_server(na->servers, BNEP_SVC_NAP); + + str2ba(addr, &dst_addr); + session = find_session_by_addr(ns->sessions, dst_addr); + + if (session == NULL) + return btd_error_failed(msg, "No active session"); + + if (session->io == NULL) + return btd_error_not_connected(msg); + + bnep_if_down_wrapper(session->dev); + bnep_conndel_wrapper(&dst_addr); + + return dbus_message_new_method_return(msg); +} + +static DBusMessage *get_properties(DBusConnection *conn, + DBusMessage *msg, void *data) +{ + struct network_adapter *na = data; + struct network_server *ns; + struct network_session *session; + const char *addr = NULL; + bdaddr_t dst_addr; + DBusMessage *reply; + DBusMessageIter iter; + DBusMessageIter dict; + dbus_bool_t connected; + const char *property; + + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &addr, + DBUS_TYPE_INVALID)) + return btd_error_invalid_args(msg); + + reply = dbus_message_new_method_return(msg); + if (!reply) + return NULL; + + dbus_message_iter_init_append(reply, &iter); + + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); + + ns = find_server(na->servers, BNEP_SVC_NAP); + + str2ba(addr, &dst_addr); + session = find_session_by_addr(ns->sessions, dst_addr); + + connected = (session && session->io) ? TRUE : FALSE; + dict_append_entry(&dict, "Connected", DBUS_TYPE_BOOLEAN, &connected); + + /* Interface */ + property = session ? session->dev : ""; + dict_append_entry(&dict, "Interface", DBUS_TYPE_STRING, &property); + + dbus_message_iter_close_container(&iter, &dict); + + return reply; +} +#endif + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static GDBusSignalTable server_signals[] = { + { GDBUS_SIGNAL("PeerConnected", + GDBUS_ARGS({ "device", "s" }, { "address", "s" })) }, + { GDBUS_SIGNAL("PeerDisconnected", + GDBUS_ARGS({ "device", "s" }, { "address", "s" })) }, + { } +}; +#endif + static const GDBusMethodTable server_methods[] = { { GDBUS_METHOD("Register", GDBUS_ARGS({ "uuid", "s" }, { "bridge", "s" }), NULL, @@ -652,6 +863,15 @@ static const GDBusMethodTable server_methods[] = { { GDBUS_METHOD("Unregister", GDBUS_ARGS({ "uuid", "s" }), NULL, unregister_server) }, +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + { GDBUS_METHOD("Disconnect", + GDBUS_ARGS({ "address", "s" }), NULL, + disconnect_device) }, + { GDBUS_METHOD("GetProperties", + GDBUS_ARGS({ "address", "s" }), + GDBUS_ARGS({ "properties", "a{sv}" }), + get_properties) }, +#endif { } }; @@ -709,6 +929,7 @@ int server_register(struct btd_adapter *adapter, uint16_t id) if (g_slist_length(na->servers) > 0) goto done; +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY if (!g_dbus_register_interface(btd_get_dbus_connection(), path, NETWORK_SERVER_INTERFACE, server_methods, NULL, NULL, na, @@ -718,6 +939,20 @@ int server_register(struct btd_adapter *adapter, uint16_t id) server_free(ns); return -1; } +#else + ns->sessions = NULL; + + if (!g_dbus_register_interface(btd_get_dbus_connection(), + path, NETWORK_SERVER_INTERFACE, + server_methods, server_signals, + NULL, + na, path_unregister)) { + error("D-Bus failed to register %s interface", + NETWORK_SERVER_INTERFACE); + server_free(ns); + return -1; + } +#endif DBG("Registered interface %s on path %s", NETWORK_SERVER_INTERFACE, path); diff --git a/profiles/proximity/immalert.c b/profiles/proximity/immalert.c new file mode 100755 index 0000000..adf9140 --- /dev/null +++ b/profiles/proximity/immalert.c @@ -0,0 +1,458 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2012 Texas Instruments Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include + +#include + +#include "lib/bluetooth.h" +#include "lib/sdp.h" +#include "lib/uuid.h" + +#include "gdbus/gdbus.h" + +#include "src/log.h" +#include "src/adapter.h" +#include "attrib/gattrib.h" +#include "attrib/att.h" +#include "attrib/gatt.h" +#include "attrib/att-database.h" +#include "attrib/gatt-service.h" +#include "src/attrib-server.h" +#include "src/device.h" +#include "src/profile.h" +#include "src/attio.h" +#include "src/dbus-common.h" + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#include "src/shared/queue.h" +#include "src/shared/gatt-db.h" +#include "src/shared/att.h" + #include "btio/btio.h" +#include "src/gatt-database.h" +#endif + +#include "reporter.h" +#include "immalert.h" + +struct imm_alert_adapter { + struct btd_adapter *adapter; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + struct gatt_db_attribute *imservice; +#endif + GSList *connected_devices; +}; + +struct connected_device { + struct btd_device *device; + struct imm_alert_adapter *adapter; + uint8_t alert_level; + guint callback_id; +}; + +static GSList *imm_alert_adapters; + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static bool get_dest_info(struct bt_att *att, bdaddr_t *dst, uint8_t *dst_type) +{ + GIOChannel *io = NULL; + GError *gerr = NULL; + + io = g_io_channel_unix_new(bt_att_get_fd(att)); + if (!io) + return false; + + bt_io_get(io, &gerr, BT_IO_OPT_DEST_BDADDR, dst, + BT_IO_OPT_DEST_TYPE, dst_type, + BT_IO_OPT_INVALID); + + if (gerr) { + error("gatt: bt_io_get: %s", gerr->message); + g_error_free(gerr); + g_io_channel_unref(io); + return false; + } + + g_io_channel_unref(io); + return true; +} +#endif + +static int imdevice_cmp(gconstpointer a, gconstpointer b) +{ + const struct connected_device *condev = a; + const struct btd_device *device = b; + + if (condev->device == device) + return 0; + + return -1; +} + +static struct connected_device * +find_connected_device(struct imm_alert_adapter *ia, struct btd_device *device) +{ + GSList *l = g_slist_find_custom(ia->connected_devices, device, + imdevice_cmp); + if (!l) + return NULL; + + return l->data; +} + +static int imadapter_cmp(gconstpointer a, gconstpointer b) +{ + const struct imm_alert_adapter *imadapter = a; + const struct btd_adapter *adapter = b; + + if (imadapter->adapter == adapter) + return 0; + + return -1; +} + +static struct imm_alert_adapter * +find_imm_alert_adapter(struct btd_adapter *adapter) +{ + GSList *l = g_slist_find_custom(imm_alert_adapters, adapter, + imadapter_cmp); + if (!l) + return NULL; + + return l->data; +} + +const char *imm_alert_get_level(struct btd_device *device) +{ + struct imm_alert_adapter *imadapter; + struct connected_device *condev; + + if (!device) + return get_alert_level_string(NO_ALERT); + + imadapter = find_imm_alert_adapter(device_get_adapter(device)); + if (!imadapter) + return get_alert_level_string(NO_ALERT); + + condev = find_connected_device(imadapter, device); + if (!condev) + return get_alert_level_string(NO_ALERT); + + return get_alert_level_string(condev->alert_level); +} + +static void imm_alert_emit_alert_signal(struct connected_device *condev, + uint8_t alert_level) +{ + const char *path, *alert_level_str; + + if (!condev) + return; + + path = device_get_path(condev->device); + alert_level_str = get_alert_level_string(alert_level); + + DBG("alert %s remote %s", alert_level_str, path); + + g_dbus_emit_property_changed(btd_get_dbus_connection(), path, + PROXIMITY_REPORTER_INTERFACE, "ImmediateAlertLevel"); +} + +static void imm_alert_remove_condev(struct connected_device *condev) +{ + struct imm_alert_adapter *ia; + + if (!condev) + return; + + ia = condev->adapter; + + if (condev->callback_id && condev->device) + btd_device_remove_attio_callback(condev->device, + condev->callback_id); + + if (condev->device) + btd_device_unref(condev->device); + + ia->connected_devices = g_slist_remove(ia->connected_devices, condev); + g_free(condev); +} + +/* condev can be NULL */ +static void imm_alert_disc_cb(gpointer user_data) +{ + struct connected_device *condev = user_data; + + if (!condev) + return; + + DBG("immediate alert remove device %p", condev->device); + + imm_alert_emit_alert_signal(condev, NO_ALERT); + imm_alert_remove_condev(condev); +} + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static void imm_alert_alert_lvl_write(struct gatt_db_attribute *attrib, + unsigned int id, uint16_t offset, + const uint8_t *value, size_t len, + uint8_t opcode, struct bt_att *att, + void *user_data) +{ + struct imm_alert_adapter *ia = user_data; + struct connected_device *condev = NULL; + uint8_t ecode = 0; + bdaddr_t bdaddr; + uint8_t bdaddr_type; + struct btd_device *device = NULL; + + if (!value || len == 0) { + ecode = BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN; + goto done; + } + + if (offset != 0) { + ecode = BT_ATT_ERROR_INVALID_OFFSET; + goto done; + } + + if (!get_dest_info(att, &bdaddr, &bdaddr_type)) { + ecode = BT_ATT_ERROR_UNLIKELY; + goto done; + } + + device = btd_adapter_get_device(ia->adapter, &bdaddr, bdaddr_type); + if (!device) { + ecode = BT_ATT_ERROR_UNLIKELY; + goto done; + } + + /* Write value should be anyone of 0x00, 0x01, 0x02 */ + if (value[0] > 0x02) { + ecode = 0x80; + goto done; + } + + /* condev might remain NULL here if nothing is found */ + condev = find_connected_device(ia, device); + + /* Register a disconnect cb if the alert level is non-zero */ + if (value[0] != NO_ALERT && !condev) { + condev = g_new0(struct connected_device, 1); + condev->device = btd_device_ref(device); + condev->adapter = ia; + condev->callback_id = btd_device_add_attio_callback(device, + NULL, imm_alert_disc_cb, condev); + ia->connected_devices = g_slist_append(ia->connected_devices, + condev); + DBG("added connected dev %p", device); + } + + if (condev) { + if (value[0] != NO_ALERT) { + condev->alert_level = value[0]; + imm_alert_emit_alert_signal(condev, value[0]); + } else { + imm_alert_emit_alert_signal(condev, value[0]); + imm_alert_disc_cb(condev); + } + } + + DBG("alert level set to %d by device %p", value[0], device); + gatt_db_attribute_write_result(attrib, id, ecode); + return; + +done: + error("Set immediate alert level for dev %p", device); + /* remove alerts by erroneous devices */ + imm_alert_disc_cb(condev); + gatt_db_attribute_write_result(attrib, id, ecode); +} + +void imm_alert_register(struct btd_adapter *adapter) +{ + bt_uuid_t uuid; + struct imm_alert_adapter *imadapter; + struct gatt_db_attribute *service, *charc; + struct gatt_db *db; + + imadapter = g_new0(struct imm_alert_adapter, 1); + imadapter->adapter = adapter; + + imm_alert_adapters = g_slist_append(imm_alert_adapters, imadapter); + db = (struct gatt_db *) btd_gatt_database_get_db(btd_adapter_get_database(adapter)); + + /* Immediate Alert Service */ + bt_uuid16_create(&uuid, IMMEDIATE_ALERT_SVC_UUID); + service = gatt_db_add_service(db, &uuid, true, 3); + if (!service) + goto err; + + imadapter->imservice = service; + + /* + * Alert Level characteristic. + */ + bt_uuid16_create(&uuid, ALERT_LEVEL_CHR_UUID); + charc = gatt_db_service_add_characteristic(service, &uuid, BT_ATT_PERM_WRITE, + BT_GATT_CHRC_PROP_WRITE, + NULL, + imm_alert_alert_lvl_write, imadapter); + if (!charc) + goto err; + + gatt_db_service_set_active(service, true); + + DBG("Immediate Alert service added"); + return; +err: + DBG("Error adding Immediate Alert service"); + imm_alert_unregister(adapter); +} +#else +static uint8_t imm_alert_alert_lvl_write(struct attribute *a, + struct btd_device *device, gpointer user_data) +{ + uint8_t value; + struct imm_alert_adapter *ia = user_data; + struct connected_device *condev = NULL; + + if (!device) + goto set_error; + + condev = find_connected_device(ia, device); + + if (a->len == 0) { + DBG("Illegal alert level length"); + goto set_error; + } + + value = a->data[0]; + if (value != NO_ALERT && value != MILD_ALERT && value != HIGH_ALERT) { + DBG("Illegal alert value"); + goto set_error; + } + + /* Register a disconnect cb if the alert level is non-zero */ + if (value != NO_ALERT && !condev) { + condev = g_new0(struct connected_device, 1); + condev->device = btd_device_ref(device); + condev->adapter = ia; + condev->callback_id = btd_device_add_attio_callback(device, + NULL, imm_alert_disc_cb, condev); + ia->connected_devices = g_slist_append(ia->connected_devices, + condev); + DBG("added connected dev %p", device); + } + + if (value != NO_ALERT) { + condev->alert_level = value; + imm_alert_emit_alert_signal(condev, value); + } + + /* + * Emit NO_ALERT if the alert level was non-zero before. This is + * guaranteed when there's a condev. + */ + if (value == NO_ALERT && condev) + imm_alert_disc_cb(condev); + + DBG("alert level set to %d by device %p", value, device); + return 0; + +set_error: + error("Set immediate alert level for dev %p", device); + /* remove alerts by erroneous devices */ + imm_alert_disc_cb(condev); + return ATT_ECODE_IO; +} + +void imm_alert_register(struct btd_adapter *adapter) +{ + gboolean svc_added; + bt_uuid_t uuid; + struct imm_alert_adapter *imadapter; + + bt_uuid16_create(&uuid, IMMEDIATE_ALERT_SVC_UUID); + + imadapter = g_new0(struct imm_alert_adapter, 1); + imadapter->adapter = adapter; + + imm_alert_adapters = g_slist_append(imm_alert_adapters, imadapter); + + /* Immediate Alert Service */ + svc_added = gatt_service_add(adapter, + GATT_PRIM_SVC_UUID, &uuid, + /* Alert level characteristic */ + GATT_OPT_CHR_UUID16, ALERT_LEVEL_CHR_UUID, + GATT_OPT_CHR_PROPS, + GATT_CHR_PROP_WRITE_WITHOUT_RESP, + GATT_OPT_CHR_VALUE_CB, ATTRIB_WRITE, + imm_alert_alert_lvl_write, imadapter, + GATT_OPT_INVALID); + + if (!svc_added) { + imm_alert_unregister(adapter); + return; + } + + DBG("Immediate Alert service added"); +} +#endif + +static void remove_condev_list_item(gpointer data, gpointer user_data) +{ + struct connected_device *condev = data; + + imm_alert_remove_condev(condev); +} + +void imm_alert_unregister(struct btd_adapter *adapter) +{ + struct imm_alert_adapter *imadapter; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + struct gatt_db *db; +#endif + + imadapter = find_imm_alert_adapter(adapter); + if (!imadapter) + return; + + g_slist_foreach(imadapter->connected_devices, remove_condev_list_item, + NULL); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + /* Remove registered service */ + if (imadapter->imservice) { + db = (struct gatt_db *) btd_gatt_database_get_db(btd_adapter_get_database(adapter)); + gatt_db_remove_service(db, imadapter->imservice); + } +#endif + + imm_alert_adapters = g_slist_remove(imm_alert_adapters, imadapter); + g_free(imadapter); +} diff --git a/profiles/proximity/immalert.h b/profiles/proximity/immalert.h new file mode 100755 index 0000000..1a09fa9 --- /dev/null +++ b/profiles/proximity/immalert.h @@ -0,0 +1,26 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2012 Texas Instruments Corporation + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +void imm_alert_register(struct btd_adapter *adapter); +void imm_alert_unregister(struct btd_adapter *adapter); +const char *imm_alert_get_level(struct btd_device *device); diff --git a/profiles/proximity/linkloss.c b/profiles/proximity/linkloss.c new file mode 100755 index 0000000..60b1064 --- /dev/null +++ b/profiles/proximity/linkloss.c @@ -0,0 +1,547 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2012 Texas Instruments Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include + +#include + +#include + +#include "lib/bluetooth.h" +#include "lib/sdp.h" +#include "lib/uuid.h" + +#include "gdbus/gdbus.h" + +#include "src/log.h" +#include "src/adapter.h" +#include "src/device.h" +#include "attrib/att-database.h" +#include "attrib/gattrib.h" +#include "attrib/att.h" +#include "attrib/gatt.h" +#include "attrib/gatt-service.h" +#include "src/attrib-server.h" +#include "src/service.h" +#include "src/profile.h" +#include "src/attio.h" +#include "src/dbus-common.h" + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#include "src/shared/queue.h" +#include "src/shared/gatt-db.h" +#include "src/shared/att.h" +#include "btio/btio.h" +#include "src/gatt-database.h" +#endif + +#include "reporter.h" +#include "linkloss.h" + +struct link_loss_adapter { + struct btd_adapter *adapter; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + struct gatt_db_attribute *llservice; +#else + uint16_t alert_lvl_value_handle; +#endif + GSList *connected_devices; +}; + +struct connected_device { + struct btd_device *device; + struct link_loss_adapter *adapter; + uint8_t alert_level; + guint callback_id; + guint local_disc_id; +}; + +static GSList *link_loss_adapters; + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static bool get_dest_info(struct bt_att *att, bdaddr_t *dst, uint8_t *dst_type) +{ + GIOChannel *io = NULL; + GError *gerr = NULL; + + io = g_io_channel_unix_new(bt_att_get_fd(att)); + if (!io) + return false; + + bt_io_get(io, &gerr, BT_IO_OPT_DEST_BDADDR, dst, + BT_IO_OPT_DEST_TYPE, dst_type, + BT_IO_OPT_INVALID); + + if (gerr) { + error("gatt: bt_io_get: %s", gerr->message); + g_error_free(gerr); + g_io_channel_unref(io); + return false; + } + + g_io_channel_unref(io); + return true; +} +#endif + +static int lldevice_cmp(gconstpointer a, gconstpointer b) +{ + const struct connected_device *llcondev = a; + const struct btd_device *device = b; + + if (llcondev->device == device) + return 0; + + return -1; +} + +static struct connected_device * +find_connected_device(struct link_loss_adapter *la, struct btd_device *device) +{ + GSList *l = g_slist_find_custom(la->connected_devices, device, + lldevice_cmp); + if (!l) + return NULL; + + return l->data; +} + +static int lladapter_cmp(gconstpointer a, gconstpointer b) +{ + const struct link_loss_adapter *lladapter = a; + const struct btd_adapter *adapter = b; + + if (lladapter->adapter == adapter) + return 0; + + return -1; +} + +static struct link_loss_adapter * +find_link_loss_adapter(struct btd_adapter *adapter) +{ + GSList *l = g_slist_find_custom(link_loss_adapters, adapter, + lladapter_cmp); + if (!l) + return NULL; + + return l->data; +} + +const char *link_loss_get_alert_level(struct btd_device *device) +{ + struct link_loss_adapter *lladapter; + struct connected_device *condev; + + if (!device) + return get_alert_level_string(NO_ALERT); + + lladapter = find_link_loss_adapter(device_get_adapter(device)); + if (!lladapter) + return get_alert_level_string(NO_ALERT); + + condev = find_connected_device(lladapter, device); + if (!condev) + return get_alert_level_string(NO_ALERT); + + return get_alert_level_string(condev->alert_level); +} + +static void link_loss_emit_alert_signal(struct connected_device *condev) +{ + const char *alert_level_str, *path; + + if (!condev->device) + return; + + path = device_get_path(condev->device); + alert_level_str = get_alert_level_string(condev->alert_level); + + DBG("alert %s remote %s", alert_level_str, path); + + g_dbus_emit_property_changed(btd_get_dbus_connection(), path, + PROXIMITY_REPORTER_INTERFACE, "LinkLossAlertLevel"); +} +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static void link_loss_alert_lvl_read(struct gatt_db_attribute *attrib, + unsigned int id, uint16_t offset, + uint8_t opcode, struct bt_att *att, + void *user_data) +{ + struct link_loss_adapter *la = user_data; + struct connected_device *condev; + uint8_t value; + uint8_t ecode = 0; + bdaddr_t bdaddr; + uint8_t bdaddr_type; + struct btd_device *device; + + value = NO_ALERT; + + if (offset != 0) { + ecode = BT_ATT_ERROR_INVALID_OFFSET; + goto out; + } + + if (!get_dest_info(att, &bdaddr, &bdaddr_type)) { + ecode = BT_ATT_ERROR_UNLIKELY; + goto out; + } + + device = btd_adapter_get_device(la->adapter, &bdaddr, bdaddr_type); + if (!device) { + ecode = BT_ATT_ERROR_UNLIKELY; + goto out; + } + + condev = find_connected_device(la, device); + if (!condev) { + ecode = BT_ATT_ERROR_UNLIKELY; + goto out; + } + + if (condev->alert_level) + value = condev->alert_level; + else + DBG("Alert Level is NULL"); + + DBG("Alert Level %d", value); +out: + gatt_db_attribute_read_result(attrib, id, ecode, &value, sizeof(value)); +} +#else +static uint8_t link_loss_alert_lvl_read(struct attribute *a, + struct btd_device *device, gpointer user_data) +{ + struct link_loss_adapter *la = user_data; + struct connected_device *condev; + uint8_t alert_level = NO_ALERT; + + if (!device) + goto out; + + condev = find_connected_device(la, device); + if (!condev) + goto out; + + alert_level = condev->alert_level; + +out: + DBG("return alert level %d for dev %p", alert_level, device); + + /* update the alert level according to the requesting device */ + attrib_db_update(la->adapter, a->handle, NULL, &alert_level, + sizeof(alert_level), NULL); + + return 0; +} +#endif + +/* condev can be NULL */ +static void link_loss_remove_condev(struct connected_device *condev) +{ + struct link_loss_adapter *la; + + if (!condev) + return; + + la = condev->adapter; + + if (condev->callback_id && condev->device) + btd_device_remove_attio_callback(condev->device, + condev->callback_id); + + if (condev->local_disc_id && condev->device) + device_remove_disconnect_watch(condev->device, + condev->local_disc_id); + + if (condev->device) + btd_device_unref(condev->device); + + la->connected_devices = g_slist_remove(la->connected_devices, condev); + g_free(condev); +} + +static void link_loss_disc_cb(gpointer user_data) +{ + struct connected_device *condev = user_data; + + DBG("alert loss disconnect device %p", condev->device); + + /* if an alert-level is set, emit a signal */ + if (condev->alert_level != NO_ALERT) + link_loss_emit_alert_signal(condev); + + /* we are open for more changes now */ + link_loss_remove_condev(condev); +} + +static void link_loss_local_disc(struct btd_device *device, + gboolean removal, void *user_data) +{ + struct connected_device *condev = user_data; + + /* no need to alert on this device - we requested disconnection */ + link_loss_remove_condev(condev); + + DBG("alert level zeroed for locally disconnecting dev %p", device); +} + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static void link_loss_alert_lvl_write(struct gatt_db_attribute *attrib, + unsigned int id, uint16_t offset, + const uint8_t *value, size_t len, + uint8_t opcode, struct bt_att *att, + void *user_data) +{ + struct link_loss_adapter *la = user_data; + struct connected_device *condev = NULL; + uint8_t ecode = 0; + bdaddr_t bdaddr; + uint8_t bdaddr_type; + struct btd_device *device = NULL; + + if (!value || len == 0) { + ecode = BT_ATT_ERROR_INVALID_ATTRIBUTE_VALUE_LEN; + goto done; + } + + if (offset != 0) { + ecode = BT_ATT_ERROR_INVALID_OFFSET; + goto done; + } + + if (!get_dest_info(att, &bdaddr, &bdaddr_type)) { + ecode = BT_ATT_ERROR_UNLIKELY; + goto done; + } + + device = btd_adapter_get_device(la->adapter, &bdaddr, bdaddr_type); + if (!device) { + ecode = BT_ATT_ERROR_UNLIKELY; + goto done; + } + + /* Write value should be anyone of 0x00, 0x01, 0x02 */ + if (value[0] > 0x02) { + ecode = 0x80; + goto done; + } + + /* condev might remain NULL here if nothing is found */ + condev = find_connected_device(la, device); + + /* Register a disconnect cb if the alert level is non-zero */ + if (value[0] != NO_ALERT && !condev) { + condev = g_new0(struct connected_device, 1); + condev->device = btd_device_ref(device); + condev->adapter = la; + condev->callback_id = btd_device_add_attio_callback(device, + NULL, link_loss_disc_cb, condev); + condev->local_disc_id = device_add_disconnect_watch(device, + link_loss_local_disc, condev, NULL); + + la->connected_devices = g_slist_append(la->connected_devices, + condev); + } + + if (condev) { + if (value[0] != NO_ALERT) { + condev->alert_level = value[0]; + link_loss_emit_alert_signal(condev); + } else { + link_loss_emit_alert_signal(condev); + link_loss_remove_condev(condev); + condev = NULL; + } + } + + DBG("alert level set to %d by device %p", value[0], device); + gatt_db_attribute_write_result(attrib, id, ecode); + return; +done: + DBG("Set link loss alert level for dev %p", device); + /* reset alert level on erroneous devices */ + link_loss_remove_condev(condev); + gatt_db_attribute_write_result(attrib, id, ecode); +} +#else +static uint8_t link_loss_alert_lvl_write(struct attribute *a, + struct btd_device *device, gpointer user_data) +{ + uint8_t value; + struct link_loss_adapter *la = user_data; + struct connected_device *condev = NULL; + + if (!device) + goto set_error; + + /* condev might remain NULL here if nothing is found */ + condev = find_connected_device(la, device); + + if (a->len == 0) { + DBG("Illegal alert level length"); + goto set_error; + } + + value = a->data[0]; + if (value != NO_ALERT && value != MILD_ALERT && value != HIGH_ALERT) { + DBG("Illegal alert value"); + goto set_error; + } + + /* Register a disconnect cb if the alert level is non-zero */ + if (value != NO_ALERT && !condev) { + condev = g_new0(struct connected_device, 1); + condev->device = btd_device_ref(device); + condev->adapter = la; + condev->callback_id = btd_device_add_attio_callback(device, + NULL, link_loss_disc_cb, condev); + condev->local_disc_id = device_add_disconnect_watch(device, + link_loss_local_disc, condev, NULL); + + la->connected_devices = g_slist_append(la->connected_devices, + condev); + } else if (value == NO_ALERT && condev) { + link_loss_remove_condev(condev); + condev = NULL; + } + + DBG("alert level set to %d by device %p", value, device); + + if (condev) + condev->alert_level = value; + + return 0; + +set_error: + error("Set link loss alert level for dev %p", device); + /* reset alert level on erroneous devices */ + link_loss_remove_condev(condev); + return ATT_ECODE_IO; +} +#endif + +void link_loss_register(struct btd_adapter *adapter) +{ +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + struct gatt_db_attribute *service, *charc; + struct gatt_db *db; +#else + gboolean svc_added; +#endif + bt_uuid_t uuid; + struct link_loss_adapter *lladapter; + + bt_uuid16_create(&uuid, LINK_LOSS_SVC_UUID); + + lladapter = g_new0(struct link_loss_adapter, 1); + lladapter->adapter = adapter; + + link_loss_adapters = g_slist_append(link_loss_adapters, lladapter); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + db = (struct gatt_db *) btd_gatt_database_get_db(btd_adapter_get_database(adapter)); + + /* + * Link Loss Service + */ + service = gatt_db_add_service(db, &uuid, true, 3); + if (!service) + goto err; + + lladapter->llservice = service; + + /* + * Alert Level characteristic. + */ + bt_uuid16_create(&uuid, ALERT_LEVEL_CHR_UUID); + charc = gatt_db_service_add_characteristic(service, &uuid, BT_ATT_PERM_READ | BT_ATT_PERM_WRITE, + BT_GATT_CHRC_PROP_READ | BT_GATT_CHRC_PROP_WRITE, + link_loss_alert_lvl_read, + link_loss_alert_lvl_write, lladapter); + + if (!charc) + goto err; + + gatt_db_service_set_active(service, true); +#else + /* Link Loss Service */ + svc_added = gatt_service_add(adapter, + GATT_PRIM_SVC_UUID, &uuid, + /* Alert level characteristic */ + GATT_OPT_CHR_UUID16, ALERT_LEVEL_CHR_UUID, + GATT_OPT_CHR_PROPS, + GATT_CHR_PROP_READ | GATT_CHR_PROP_WRITE, + GATT_OPT_CHR_VALUE_CB, ATTRIB_READ, + link_loss_alert_lvl_read, lladapter, + GATT_OPT_CHR_VALUE_CB, ATTRIB_WRITE, + link_loss_alert_lvl_write, lladapter, + GATT_OPT_CHR_VALUE_GET_HANDLE, + &lladapter->alert_lvl_value_handle, + GATT_OPT_INVALID); + + if (!svc_added) + goto err; +#endif + DBG("Link Loss service added"); + return; + +err: + error("Error adding Link Loss service"); + link_loss_unregister(adapter); +} + +static void remove_condev_list_item(gpointer data, gpointer user_data) +{ + struct connected_device *condev = data; + + link_loss_remove_condev(condev); +} + +void link_loss_unregister(struct btd_adapter *adapter) +{ + struct link_loss_adapter *lladapter; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + struct gatt_db *db; +#endif + + lladapter = find_link_loss_adapter(adapter); + if (!lladapter) + return; + + g_slist_foreach(lladapter->connected_devices, remove_condev_list_item, + NULL); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + /* Remove registered service */ + if (lladapter->llservice) { + db = (struct gatt_db *) btd_gatt_database_get_db(btd_adapter_get_database(adapter)); + gatt_db_remove_service(db, lladapter->llservice); + } +#endif + + link_loss_adapters = g_slist_remove(link_loss_adapters, lladapter); + g_free(lladapter); +} diff --git a/profiles/proximity/linkloss.h b/profiles/proximity/linkloss.h new file mode 100755 index 0000000..0447def --- /dev/null +++ b/profiles/proximity/linkloss.h @@ -0,0 +1,26 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2012 Texas Instruments Corporation + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +void link_loss_register(struct btd_adapter *adapter); +void link_loss_unregister(struct btd_adapter *adapter); +const char *link_loss_get_alert_level(struct btd_device *device); diff --git a/profiles/proximity/main.c b/profiles/proximity/main.c new file mode 100755 index 0000000..38a51f1 --- /dev/null +++ b/profiles/proximity/main.c @@ -0,0 +1,81 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2011 Nokia Corporation + * Copyright (C) 2011 Marcel Holtmann + * + * + * 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 +#endif + +#include +#include +#include + +#include "gdbus/gdbus.h" + +#include "src/log.h" +#include "src/plugin.h" +#include "manager.h" + +static GKeyFile *config = NULL; + +static GKeyFile *open_config_file(const char *file) +{ + GError *gerr = NULL; + GKeyFile *keyfile; + + keyfile = g_key_file_new(); + + g_key_file_set_list_separator(keyfile, ','); + + if (!g_key_file_load_from_file(keyfile, file, 0, &gerr)) { + if (!g_error_matches(gerr, G_FILE_ERROR, G_FILE_ERROR_NOENT)) + error("Parsing %s failed: %s", file, gerr->message); + g_error_free(gerr); + g_key_file_free(keyfile); + return NULL; + } + + return keyfile; +} + +static int proximity_init(void) +{ + config = open_config_file(CONFIGDIR "/proximity.conf"); + + if (proximity_manager_init(config) < 0) + return -EIO; + + return 0; +} + +static void proximity_exit(void) +{ + if (config) + g_key_file_free(config); + + proximity_manager_exit(); +} + +BLUETOOTH_PLUGIN_DEFINE(proximity, VERSION, + BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, + proximity_init, proximity_exit) diff --git a/profiles/proximity/manager.c b/profiles/proximity/manager.c new file mode 100755 index 0000000..dbb3bda --- /dev/null +++ b/profiles/proximity/manager.c @@ -0,0 +1,196 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2011 Nokia Corporation + * Copyright (C) 2011 Marcel Holtmann + * + * + * 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 +#endif + +#include + +#include + +#include "lib/bluetooth.h" +#include "lib/sdp.h" +#include "lib/uuid.h" + +#include "gdbus/gdbus.h" + +#include "src/adapter.h" +#include "src/device.h" +#include "src/profile.h" +#include "src/service.h" +#include "attrib/att.h" +#include "attrib/gattrib.h" +#include "attrib/gatt.h" +#include "monitor.h" +#include "reporter.h" +#include "manager.h" + +static struct enabled enabled = { + .linkloss = TRUE, + .pathloss = TRUE, + .findme = TRUE, +}; + +static int monitor_linkloss_probe(struct btd_service *service) +{ + struct btd_device *device = btd_service_get_device(service); + struct gatt_primary *linkloss; + + linkloss = btd_device_get_primary(device, LINK_LOSS_UUID); + if (linkloss == NULL) + return -1; + + return monitor_register_linkloss(device, &enabled, linkloss); +} + +static int monitor_immediate_probe(struct btd_service *service) +{ + struct btd_device *device = btd_service_get_device(service); + struct gatt_primary *immediate; + + immediate = btd_device_get_primary(device, IMMEDIATE_ALERT_UUID); + if (immediate == NULL) + return -1; + + return monitor_register_immediate(device, &enabled, immediate); +} + +static int monitor_txpower_probe(struct btd_service *service) +{ + struct btd_device *device = btd_service_get_device(service); + struct gatt_primary *txpower; + + txpower = btd_device_get_primary(device, TX_POWER_UUID); + if (txpower == NULL) + return -1; + + return monitor_register_txpower(device, &enabled, txpower); +} + +static void monitor_linkloss_remove(struct btd_service *service) +{ + struct btd_device *device = btd_service_get_device(service); + + monitor_unregister_linkloss(device); +} + +static void monitor_immediate_remove(struct btd_service *service) +{ + struct btd_device *device = btd_service_get_device(service); + + monitor_unregister_immediate(device); +} + +static void monitor_txpower_remove(struct btd_service *service) +{ + struct btd_device *device = btd_service_get_device(service); + + monitor_unregister_txpower(device); +} + +static struct btd_profile pxp_monitor_linkloss_profile = { + .name = "proximity-linkloss", + .remote_uuid = LINK_LOSS_UUID, + .device_probe = monitor_linkloss_probe, + .device_remove = monitor_linkloss_remove, +}; + +static struct btd_profile pxp_monitor_immediate_profile = { + .name = "proximity-immediate", + .remote_uuid = IMMEDIATE_ALERT_UUID, + .device_probe = monitor_immediate_probe, + .device_remove = monitor_immediate_remove, +}; + +static struct btd_profile pxp_monitor_txpower_profile = { + .name = "proximity-txpower", + .remote_uuid = TX_POWER_UUID, + .device_probe = monitor_txpower_probe, + .device_remove = monitor_txpower_remove, +}; + +static struct btd_profile pxp_reporter_profile = { + .name = "Proximity Reporter GATT Driver", + .remote_uuid = GATT_UUID, + .device_probe = reporter_device_probe, + .device_remove = reporter_device_remove, + + .adapter_probe = reporter_adapter_probe, + .adapter_remove = reporter_adapter_remove, +}; + +static void load_config_file(GKeyFile *config) +{ + char **list; + int i; + + if (config == NULL) + return; + + list = g_key_file_get_string_list(config, "General", "Disable", + NULL, NULL); + for (i = 0; list && list[i] != NULL; i++) { + if (g_str_equal(list[i], "FindMe")) + enabled.findme = FALSE; + else if (g_str_equal(list[i], "LinkLoss")) + enabled.linkloss = FALSE; + else if (g_str_equal(list[i], "PathLoss")) + enabled.pathloss = FALSE; + } + + g_strfreev(list); +} + +int proximity_manager_init(GKeyFile *config) +{ + load_config_file(config); + + if (btd_profile_register(&pxp_monitor_linkloss_profile) < 0) + goto fail; + + if (btd_profile_register(&pxp_monitor_immediate_profile) < 0) + goto fail; + + if (btd_profile_register(&pxp_monitor_txpower_profile) < 0) + goto fail; + + if (btd_profile_register(&pxp_reporter_profile) < 0) + goto fail; + + return 0; + +fail: + proximity_manager_exit(); + + return -1; +} + +void proximity_manager_exit(void) +{ + btd_profile_unregister(&pxp_reporter_profile); + btd_profile_unregister(&pxp_monitor_txpower_profile); + btd_profile_unregister(&pxp_monitor_immediate_profile); + btd_profile_unregister(&pxp_monitor_linkloss_profile); +} diff --git a/profiles/proximity/manager.h b/profiles/proximity/manager.h new file mode 100755 index 0000000..e65c31d --- /dev/null +++ b/profiles/proximity/manager.h @@ -0,0 +1,26 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2011 Nokia Corporation + * Copyright (C) 2011 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +int proximity_manager_init(GKeyFile *conf); +void proximity_manager_exit(void); diff --git a/profiles/proximity/monitor.c b/profiles/proximity/monitor.c new file mode 100755 index 0000000..a583eb7 --- /dev/null +++ b/profiles/proximity/monitor.c @@ -0,0 +1,822 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2011 Nokia Corporation + * Copyright (C) 2011 Marcel Holtmann + * + * + * 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 +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "lib/bluetooth.h" +#include "lib/sdp.h" +#include "lib/uuid.h" + +#include "gdbus/gdbus.h" + +#include "src/dbus-common.h" +#include "src/adapter.h" +#include "src/device.h" +#include "src/error.h" +#include "src/log.h" +#include "attrib/att.h" +#include "attrib/gattrib.h" +#include "attrib/gatt.h" +#include "src/attio.h" +#include "src/textfile.h" + +#include "monitor.h" + +#define PROXIMITY_INTERFACE "org.bluez.ProximityMonitor1" + +#define ALERT_LEVEL_CHR_UUID 0x2A06 +#define POWER_LEVEL_CHR_UUID 0x2A07 + +#define IMMEDIATE_TIMEOUT 5 +#define TX_POWER_SIZE 1 + +enum { + ALERT_NONE = 0, + ALERT_MILD, + ALERT_HIGH, +}; + +struct monitor { + struct btd_device *device; + GAttrib *attrib; + struct att_range *linkloss; + struct att_range *txpower; + struct att_range *immediate; + struct enabled enabled; + char *linklosslevel; /* Link Loss Alert Level */ + char *fallbacklevel; /* Immediate fallback alert level */ + char *immediatelevel; /* Immediate Alert Level */ + char *signallevel; /* Path Loss RSSI level */ + uint16_t linklosshandle; /* Link Loss Characteristic + * Value Handle */ + uint16_t txpowerhandle; /* Tx Characteristic Value Handle */ + uint16_t immediatehandle; /* Immediate Alert Value Handle */ + guint immediateto; /* Reset Immediate Alert to "none" */ + guint attioid; +}; + +static GSList *monitors = NULL; + +static struct monitor *find_monitor(struct btd_device *device) +{ + GSList *l; + + for (l = monitors; l; l = l->next) { + struct monitor *monitor = l->data; + + if (monitor->device == device) + return monitor; + } + + return NULL; +} + +static void write_proximity_config(struct btd_device *device, const char *alert, + const char *level) +{ + char *filename; + GKeyFile *key_file; + char *data; + gsize length = 0; + + filename = btd_device_get_storage_path(device, "proximity"); + if (!filename) { + warn("Unable to get proximity storage path for device"); + return; + } + + key_file = g_key_file_new(); + g_key_file_load_from_file(key_file, filename, 0, NULL); + + if (level) + g_key_file_set_string(key_file, alert, "Level", level); + else + g_key_file_remove_group(key_file, alert, NULL); + + data = g_key_file_to_data(key_file, &length, NULL); + if (length > 0) { + create_file(filename, S_IRUSR | S_IWUSR); + g_file_set_contents(filename, data, length, NULL); + } + + g_free(data); + g_free(filename); + g_key_file_free(key_file); +} + +static char *read_proximity_config(struct btd_device *device, const char *alert) +{ + char *filename; + GKeyFile *key_file; + char *str; + + filename = btd_device_get_storage_path(device, "proximity"); + if (!filename) { + warn("Unable to get proximity storage path for device"); + return NULL; + } + + key_file = g_key_file_new(); + g_key_file_load_from_file(key_file, filename, 0, NULL); + + str = g_key_file_get_string(key_file, alert, "Level", NULL); + + g_free(filename); + g_key_file_free(key_file); + + return str; +} + +static uint8_t str2level(const char *level) +{ + if (g_strcmp0("high", level) == 0) + return ALERT_HIGH; + else if (g_strcmp0("mild", level) == 0) + return ALERT_MILD; + + return ALERT_NONE; +} + +static void linkloss_written(guint8 status, const guint8 *pdu, guint16 plen, + gpointer user_data) +{ + struct monitor *monitor = user_data; + struct btd_device *device = monitor->device; + const char *path = device_get_path(device); + + if (status != 0) { + error("Link Loss Write Request failed: %s", + att_ecode2str(status)); + return; + } + + if (!dec_write_resp(pdu, plen)) { + error("Link Loss Write Request: protocol error"); + return; + } + + DBG("Link Loss Alert Level written"); + + g_dbus_emit_property_changed(btd_get_dbus_connection(), path, + PROXIMITY_INTERFACE, "LinkLossAlertLevel"); +} + +static void char_discovered_cb(uint8_t status, GSList *characteristics, + void *user_data) +{ + struct monitor *monitor = user_data; + struct gatt_char *chr; + uint8_t value = str2level(monitor->linklosslevel); + + if (status) { + error("Discover Link Loss handle: %s", att_ecode2str(status)); + return; + } + + DBG("Setting alert level \"%s\" on Reporter", monitor->linklosslevel); + + /* Assume there is a single Alert Level characteristic */ + chr = characteristics->data; + monitor->linklosshandle = chr->value_handle; + + gatt_write_char(monitor->attrib, monitor->linklosshandle, &value, 1, + linkloss_written, monitor); +} + +static int write_alert_level(struct monitor *monitor) +{ + struct att_range *linkloss = monitor->linkloss; + bt_uuid_t uuid; + + if (monitor->linklosshandle) { + uint8_t value = str2level(monitor->linklosslevel); + + gatt_write_char(monitor->attrib, monitor->linklosshandle, + &value, 1, linkloss_written, monitor); + return 0; + } + + bt_uuid16_create(&uuid, ALERT_LEVEL_CHR_UUID); + + /* FIXME: use cache (requires service changed support) ? */ + gatt_discover_char(monitor->attrib, linkloss->start, linkloss->end, + &uuid, char_discovered_cb, monitor); + + return 0; +} + +static void tx_power_read_cb(guint8 status, const guint8 *pdu, guint16 plen, + gpointer user_data) +{ + uint8_t value[TX_POWER_SIZE]; + ssize_t vlen; + + if (status != 0) { + DBG("Tx Power Level read failed: %s", att_ecode2str(status)); + return; + } + + vlen = dec_read_resp(pdu, plen, value, sizeof(value)); + if (vlen < 0) { + DBG("Protocol error"); + return; + } + + if (vlen != 1) { + DBG("Invalid length for TX Power value: %zd", vlen); + return; + } + + DBG("Tx Power Level: %02x", (int8_t) value[0]); +} + +static void tx_power_handle_cb(uint8_t status, GSList *characteristics, + void *user_data) +{ + struct monitor *monitor = user_data; + struct gatt_char *chr; + + if (status) { + error("Discover Tx Power handle: %s", att_ecode2str(status)); + return; + } + + chr = characteristics->data; + monitor->txpowerhandle = chr->value_handle; + + DBG("Tx Power handle: 0x%04x", monitor->txpowerhandle); + + gatt_read_char(monitor->attrib, monitor->txpowerhandle, + tx_power_read_cb, monitor); +} + +static void read_tx_power(struct monitor *monitor) +{ + struct att_range *txpower = monitor->txpower; + bt_uuid_t uuid; + + if (monitor->txpowerhandle != 0) { + gatt_read_char(monitor->attrib, monitor->txpowerhandle, + tx_power_read_cb, monitor); + return; + } + + bt_uuid16_create(&uuid, POWER_LEVEL_CHR_UUID); + + gatt_discover_char(monitor->attrib, txpower->start, txpower->end, + &uuid, tx_power_handle_cb, monitor); +} + +static gboolean immediate_timeout(gpointer user_data) +{ + struct monitor *monitor = user_data; + const char *path = device_get_path(monitor->device); + + monitor->immediateto = 0; + + if (g_strcmp0(monitor->immediatelevel, "none") == 0) + return FALSE; + + if (monitor->attrib) { + uint8_t value = ALERT_NONE; + gatt_write_cmd(monitor->attrib, monitor->immediatehandle, + &value, 1, NULL, NULL); + } + + g_free(monitor->immediatelevel); + monitor->immediatelevel = g_strdup("none"); + + + g_dbus_emit_property_changed(btd_get_dbus_connection(), path, + PROXIMITY_INTERFACE, "ImmediateAlertLevel"); + + return FALSE; +} + +static void immediate_written(gpointer user_data) +{ + struct monitor *monitor = user_data; + const char *path = device_get_path(monitor->device); + + g_free(monitor->fallbacklevel); + monitor->fallbacklevel = NULL; + + + g_dbus_emit_property_changed(btd_get_dbus_connection(), path, + PROXIMITY_INTERFACE, "ImmediateAlertLevel"); + + monitor->immediateto = g_timeout_add_seconds(IMMEDIATE_TIMEOUT, + immediate_timeout, monitor); +} + +static void write_immediate_alert(struct monitor *monitor) +{ + uint8_t value = str2level(monitor->immediatelevel); + + gatt_write_cmd(monitor->attrib, monitor->immediatehandle, &value, 1, + immediate_written, monitor); +} + +static void immediate_handle_cb(uint8_t status, GSList *characteristics, + void *user_data) +{ + struct monitor *monitor = user_data; + struct gatt_char *chr; + + if (status) { + error("Discover Immediate Alert handle: %s", + att_ecode2str(status)); + return; + } + + chr = characteristics->data; + monitor->immediatehandle = chr->value_handle; + + DBG("Immediate Alert handle: 0x%04x", monitor->immediatehandle); + + if (monitor->fallbacklevel) + write_immediate_alert(monitor); +} + +static void discover_immediate_handle(struct monitor *monitor) +{ + struct att_range *immediate = monitor->immediate; + bt_uuid_t uuid; + + bt_uuid16_create(&uuid, ALERT_LEVEL_CHR_UUID); + + gatt_discover_char(monitor->attrib, immediate->start, immediate->end, + &uuid, immediate_handle_cb, monitor); +} + +static void attio_connected_cb(GAttrib *attrib, gpointer user_data) +{ + struct monitor *monitor = user_data; + + monitor->attrib = g_attrib_ref(attrib); + + if (monitor->enabled.linkloss) + write_alert_level(monitor); + + if (monitor->enabled.pathloss) + read_tx_power(monitor); + + if (monitor->immediatehandle == 0) { + if(monitor->enabled.pathloss || monitor->enabled.findme) + discover_immediate_handle(monitor); + } else if (monitor->fallbacklevel) + write_immediate_alert(monitor); +} + +static void attio_disconnected_cb(gpointer user_data) +{ + struct monitor *monitor = user_data; + const char *path = device_get_path(monitor->device); + + g_attrib_unref(monitor->attrib); + monitor->attrib = NULL; + + if (monitor->immediateto == 0) + return; + + g_source_remove(monitor->immediateto); + monitor->immediateto = 0; + + if (g_strcmp0(monitor->immediatelevel, "none") == 0) + return; + + g_free(monitor->immediatelevel); + monitor->immediatelevel = g_strdup("none"); + + g_dbus_emit_property_changed(btd_get_dbus_connection(), path, + PROXIMITY_INTERFACE, "ImmediateAlertLevel"); +} + +static gboolean level_is_valid(const char *level) +{ + return (g_str_equal("none", level) || + g_str_equal("mild", level) || + g_str_equal("high", level)); +} + +static gboolean property_get_link_loss_level(const GDBusPropertyTable *property, + DBusMessageIter *iter, void *data) +{ + struct monitor *monitor = data; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, + &monitor->linklosslevel); + + return TRUE; +} + +static void property_set_link_loss_level(const GDBusPropertyTable *property, + DBusMessageIter *iter, GDBusPendingPropertySet id, void *data) +{ + struct monitor *monitor = data; + const char *level; + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) { + g_dbus_pending_property_error(id, + ERROR_INTERFACE ".InvalidArguments", + "Invalid arguments in method call"); + return; + } + + dbus_message_iter_get_basic(iter, &level); + + if (!level_is_valid(level)) { + g_dbus_pending_property_error(id, + ERROR_INTERFACE ".InvalidArguments", + "Invalid arguments in method call"); + return; + } + + if (g_strcmp0(monitor->linklosslevel, level) == 0) + goto done; + + g_free(monitor->linklosslevel); + monitor->linklosslevel = g_strdup(level); + + write_proximity_config(monitor->device, "LinkLossAlertLevel", level); + + if (monitor->attrib) + write_alert_level(monitor); + +done: + g_dbus_pending_property_success(id); +} + +static gboolean property_exists_link_loss_level( + const GDBusPropertyTable *property, void *data) +{ + struct monitor *monitor = data; + + if (!monitor->enabled.linkloss) + return FALSE; + + return TRUE; +} + +static gboolean property_get_immediate_alert_level( + const GDBusPropertyTable *property, + DBusMessageIter *iter, void *data) +{ + struct monitor *monitor = data; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, + &monitor->immediatelevel); + + return TRUE; +} + +static void property_set_immediate_alert_level( + const GDBusPropertyTable *property, DBusMessageIter *iter, + GDBusPendingPropertySet id, void *data) +{ + struct monitor *monitor = data; + struct btd_device *device = monitor->device; + const char *level; + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING) { + g_dbus_pending_property_error(id, + ERROR_INTERFACE ".InvalidArguments", + "Invalid arguments in method call"); + return; + } + + dbus_message_iter_get_basic(iter, &level); + + if (!level_is_valid(level)) { + g_dbus_pending_property_error(id, + ERROR_INTERFACE ".InvalidArguments", + "Invalid arguments in method call"); + return; + } + + if (g_strcmp0(monitor->immediatelevel, level) == 0) + goto done; + + if (monitor->immediateto) { + g_source_remove(monitor->immediateto); + monitor->immediateto = 0; + } + + /* Previous Immediate Alert level if connection/write fails */ + g_free(monitor->fallbacklevel); + monitor->fallbacklevel = monitor->immediatelevel; + + monitor->immediatelevel = g_strdup(level); + + /* + * Means that Link/Path Loss are disabled or there is a pending + * writting for Find Me(Immediate Alert characteristic value). + * If enabled, Path Loss always registers a connection callback + * when the Proximity Monitor starts. + */ + if (monitor->attioid == 0) + monitor->attioid = btd_device_add_attio_callback(device, + attio_connected_cb, + attio_disconnected_cb, + monitor); + else if (monitor->attrib) + write_immediate_alert(monitor); + +done: + g_dbus_pending_property_success(id); +} + +static gboolean property_exists_immediate_alert_level( + const GDBusPropertyTable *property, void *data) +{ + struct monitor *monitor = data; + + if (!(monitor->enabled.findme || monitor->enabled.pathloss)) + return FALSE; + + return TRUE; +} + +static gboolean property_get_signal_level( + const GDBusPropertyTable *property, + DBusMessageIter *iter, void *data) +{ + struct monitor *monitor = data; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, + &monitor->signallevel); + + return TRUE; +} + +static gboolean property_exists_signal_level(const GDBusPropertyTable *property, + void *data) +{ + struct monitor *monitor = data; + + if (!monitor->enabled.pathloss) + return FALSE; + + return TRUE; +} + +static const GDBusPropertyTable monitor_device_properties[] = { + { "LinkLossAlertLevel", "s", property_get_link_loss_level, + property_set_link_loss_level, + property_exists_link_loss_level }, + { "ImmediateAlertLevel", "s", property_get_immediate_alert_level, + property_set_immediate_alert_level, + property_exists_immediate_alert_level }, + { "SignalLevel", "s", property_get_signal_level, NULL, + property_exists_signal_level }, + { } +}; + +static void monitor_destroy(gpointer user_data) +{ + struct monitor *monitor = user_data; + + monitors = g_slist_remove(monitors, monitor); + + btd_device_unref(monitor->device); + g_free(monitor->linklosslevel); + g_free(monitor->immediatelevel); + g_free(monitor->signallevel); + g_free(monitor); +} + +static struct monitor *register_monitor(struct btd_device *device) +{ + const char *path = device_get_path(device); + struct monitor *monitor; + char *level; + + monitor = find_monitor(device); + if (monitor != NULL) + return monitor; + + level = read_proximity_config(device, "LinkLossAlertLevel"); + + monitor = g_new0(struct monitor, 1); + monitor->device = btd_device_ref(device); + monitor->linklosslevel = (level ? : g_strdup("high")); + monitor->signallevel = g_strdup("unknown"); + monitor->immediatelevel = g_strdup("none"); + + monitors = g_slist_append(monitors, monitor); + + if (g_dbus_register_interface(btd_get_dbus_connection(), path, + PROXIMITY_INTERFACE, + NULL, NULL, monitor_device_properties, + monitor, monitor_destroy) == FALSE) { + error("D-Bus failed to register %s interface", + PROXIMITY_INTERFACE); + monitor_destroy(monitor); + return NULL; + } + + DBG("Registered interface %s on path %s", PROXIMITY_INTERFACE, path); + + return monitor; +} + +static void update_monitor(struct monitor *monitor) +{ + if (monitor->txpower != NULL && monitor->immediate != NULL) + monitor->enabled.pathloss = TRUE; + else + monitor->enabled.pathloss = FALSE; + + DBG("Link Loss: %s, Path Loss: %s, FindMe: %s", + monitor->enabled.linkloss ? "TRUE" : "FALSE", + monitor->enabled.pathloss ? "TRUE" : "FALSE", + monitor->enabled.findme ? "TRUE" : "FALSE"); + + if (!monitor->enabled.linkloss && !monitor->enabled.pathloss) + return; + + if (monitor->attioid != 0) + return; + + monitor->attioid = btd_device_add_attio_callback(monitor->device, + attio_connected_cb, + attio_disconnected_cb, + monitor); +} + +int monitor_register_linkloss(struct btd_device *device, + struct enabled *enabled, + struct gatt_primary *linkloss) +{ + struct monitor *monitor; + + if (!enabled->linkloss) + return 0; + + monitor = register_monitor(device); + if (monitor == NULL) + return -1; + + monitor->linkloss = g_new0(struct att_range, 1); + monitor->linkloss->start = linkloss->range.start; + monitor->linkloss->end = linkloss->range.end; + monitor->enabled.linkloss = TRUE; + + update_monitor(monitor); + + return 0; +} + +int monitor_register_txpower(struct btd_device *device, + struct enabled *enabled, + struct gatt_primary *txpower) +{ + struct monitor *monitor; + + if (!enabled->pathloss) + return 0; + + monitor = register_monitor(device); + if (monitor == NULL) + return -1; + + monitor->txpower = g_new0(struct att_range, 1); + monitor->txpower->start = txpower->range.start; + monitor->txpower->end = txpower->range.end; + + update_monitor(monitor); + + return 0; +} + +int monitor_register_immediate(struct btd_device *device, + struct enabled *enabled, + struct gatt_primary *immediate) +{ + struct monitor *monitor; + + if (!enabled->pathloss && !enabled->findme) + return 0; + + monitor = register_monitor(device); + if (monitor == NULL) + return -1; + + monitor->immediate = g_new0(struct att_range, 1); + monitor->immediate->start = immediate->range.start; + monitor->immediate->end = immediate->range.end; + monitor->enabled.findme = enabled->findme; + + update_monitor(monitor); + + return 0; +} + +static void cleanup_monitor(struct monitor *monitor) +{ + struct btd_device *device = monitor->device; + const char *path = device_get_path(device); + + if (monitor->immediate != NULL || monitor->txpower != NULL) + return; + + if (monitor->immediateto != 0) { + g_source_remove(monitor->immediateto); + monitor->immediateto = 0; + } + + if (monitor->linkloss != NULL) + return; + + if (monitor->attioid != 0) { + btd_device_remove_attio_callback(device, monitor->attioid); + monitor->attioid = 0; + } + + if (monitor->attrib != NULL) { + g_attrib_unref(monitor->attrib); + monitor->attrib = NULL; + } + + g_dbus_unregister_interface(btd_get_dbus_connection(), path, + PROXIMITY_INTERFACE); +} + +void monitor_unregister_linkloss(struct btd_device *device) +{ + struct monitor *monitor; + + monitor = find_monitor(device); + if (monitor == NULL) + return; + + g_free(monitor->linkloss); + monitor->linkloss = NULL; + monitor->enabled.linkloss = FALSE; + + cleanup_monitor(monitor); +} + +void monitor_unregister_txpower(struct btd_device *device) +{ + struct monitor *monitor; + + monitor = find_monitor(device); + if (monitor == NULL) + return; + + g_free(monitor->txpower); + monitor->txpower = NULL; + monitor->enabled.pathloss = FALSE; + + cleanup_monitor(monitor); +} + +void monitor_unregister_immediate(struct btd_device *device) +{ + struct monitor *monitor; + + monitor = find_monitor(device); + if (monitor == NULL) + return; + + g_free(monitor->immediate); + monitor->immediate = NULL; + monitor->enabled.findme = FALSE; + monitor->enabled.pathloss = FALSE; + + cleanup_monitor(monitor); +} diff --git a/profiles/proximity/monitor.h b/profiles/proximity/monitor.h new file mode 100755 index 0000000..d9a40c6 --- /dev/null +++ b/profiles/proximity/monitor.h @@ -0,0 +1,43 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2011 Nokia Corporation + * Copyright (C) 2011 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +struct enabled { + gboolean linkloss; + gboolean pathloss; + gboolean findme; +}; + +int monitor_register_linkloss(struct btd_device *device, + struct enabled *enabled, + struct gatt_primary *linkloss); +int monitor_register_txpower(struct btd_device *device, + struct enabled *enabled, + struct gatt_primary *txpower); +int monitor_register_immediate(struct btd_device *device, + struct enabled *enabled, + struct gatt_primary *immediate); + +void monitor_unregister_linkloss(struct btd_device *device); +void monitor_unregister_txpower(struct btd_device *device); +void monitor_unregister_immediate(struct btd_device *device); diff --git a/profiles/proximity/reporter.c b/profiles/proximity/reporter.c new file mode 100755 index 0000000..30fc7c2 --- /dev/null +++ b/profiles/proximity/reporter.c @@ -0,0 +1,330 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2011 Nokia Corporation + * Copyright (C) 2011 Marcel Holtmann + * + * + * 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 +#endif + +#include +#include + +#include + +#include + +#include "lib/bluetooth.h" +#include "lib/sdp.h" +#include "lib/uuid.h" + +#include "gdbus/gdbus.h" + +#include "src/log.h" +#include "src/dbus-common.h" +#include "src/adapter.h" +#include "src/device.h" +#include "src/profile.h" +#include "src/service.h" +#include "src/shared/util.h" +#include "attrib/gattrib.h" +#include "attrib/att.h" +#include "attrib/gatt.h" +#include "attrib/att-database.h" +#include "src/attrib-server.h" + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#include "src/error.h" +#endif + +#include "reporter.h" +#include "linkloss.h" +#include "immalert.h" + +struct reporter_adapter { + struct btd_adapter *adapter; + GSList *devices; +}; + +static GSList *reporter_adapters; + +static int radapter_cmp(gconstpointer a, gconstpointer b) +{ + const struct reporter_adapter *radapter = a; + const struct btd_adapter *adapter = b; + + if (radapter->adapter == adapter) + return 0; + + return -1; +} + +static struct reporter_adapter * +find_reporter_adapter(struct btd_adapter *adapter) +{ + GSList *l = g_slist_find_custom(reporter_adapters, adapter, + radapter_cmp); + if (!l) + return NULL; + + return l->data; +} + +const char *get_alert_level_string(uint8_t level) +{ + switch (level) { + case NO_ALERT: + return "none"; + case MILD_ALERT: + return "mild"; + case HIGH_ALERT: + return "high"; + } + + return "unknown"; +} + +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY +static void register_tx_power(struct btd_adapter *adapter) +{ + uint16_t start_handle, h; + const int svc_size = 4; + uint8_t atval[256]; + bt_uuid_t uuid; + + bt_uuid16_create(&uuid, TX_POWER_SVC_UUID); + start_handle = attrib_db_find_avail(adapter, &uuid, svc_size); + if (start_handle == 0) { + error("Not enough free handles to register service"); + return; + } + + DBG("start_handle=0x%04x", start_handle); + + h = start_handle; + + /* Primary service definition */ + bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID); + put_le16(TX_POWER_SVC_UUID, &atval[0]); + attrib_db_add(adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2); + + /* Power level characteristic */ + bt_uuid16_create(&uuid, GATT_CHARAC_UUID); + atval[0] = GATT_CHR_PROP_READ | GATT_CHR_PROP_NOTIFY; + put_le16(h + 1, &atval[1]); + put_le16(POWER_LEVEL_CHR_UUID, &atval[3]); + attrib_db_add(adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5); + + /* Power level value */ + bt_uuid16_create(&uuid, POWER_LEVEL_CHR_UUID); + atval[0] = 0x00; + attrib_db_add(adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 1); + + /* Client characteristic configuration */ + bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID); + atval[0] = 0x00; + atval[1] = 0x00; + attrib_db_add(adapter, h++, &uuid, ATT_NONE, ATT_NONE, atval, 2); + + g_assert(h - start_handle == svc_size); +} +#endif + +static gboolean property_get_link_loss_level(const GDBusPropertyTable *property, + DBusMessageIter *iter, void *data) +{ + struct btd_device *device = data; + const char *level; + + level = link_loss_get_alert_level(device); + + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &level); + + return TRUE; +} + +static gboolean property_get_immediate_alert_level( + const GDBusPropertyTable *property, + DBusMessageIter *iter, void *data) +{ + struct btd_device *device = data; + const char *level; + + level = imm_alert_get_level(device); + + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &level); + + return TRUE; +} + +static const GDBusPropertyTable reporter_device_properties[] = { + { "LinkLossAlertLevel", "s", property_get_link_loss_level }, + { "ImmediateAlertLevel", "s", property_get_immediate_alert_level }, + { } +}; + +static void unregister_reporter_device(gpointer data, gpointer user_data) +{ + struct btd_device *device = data; + struct reporter_adapter *radapter = user_data; + const char *path = device_get_path(device); + + DBG("unregister on device %s", path); + + g_dbus_unregister_interface(btd_get_dbus_connection(), path, + PROXIMITY_REPORTER_INTERFACE); + + radapter->devices = g_slist_remove(radapter->devices, device); + btd_device_unref(device); +} + +static void register_reporter_device(struct btd_device *device, + struct reporter_adapter *radapter) +{ + const char *path = device_get_path(device); + + DBG("register on device %s", path); + + g_dbus_register_interface(btd_get_dbus_connection(), path, + PROXIMITY_REPORTER_INTERFACE, + NULL, NULL, reporter_device_properties, + device, NULL); + + btd_device_ref(device); + radapter->devices = g_slist_prepend(radapter->devices, device); +} + +int reporter_device_probe(struct btd_service *service) +{ + struct btd_device *device = btd_service_get_device(service); + struct reporter_adapter *radapter; + struct btd_adapter *adapter = device_get_adapter(device); + + radapter = find_reporter_adapter(adapter); + if (!radapter) + return -1; + + register_reporter_device(device, radapter); + + return 0; +} + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static DBusMessage *register_proximity(DBusConnection *conn, DBusMessage *msg, + void *user_data) +{ + struct btd_adapter *adapter = user_data; + + if(adapter == NULL) { + DBG("Adapter is NULL"); + return btd_error_invalid_args(msg); + } + + link_loss_register(adapter); + imm_alert_register(adapter); + + /* TODO: TX Power service implementation + * is incomplete in BlueZ. + */ + //register_tx_power(adapter); + + return dbus_message_new_method_return(msg); +} + +static DBusMessage *unregister_proximity(DBusConnection *conn, DBusMessage *msg, + void *user_data) +{ + struct btd_adapter *adapter = user_data; + + if(adapter == NULL) { + DBG("Adapter is NULL"); + return btd_error_invalid_args(msg); + } + + link_loss_unregister(adapter); + imm_alert_unregister(adapter); + + return dbus_message_new_method_return(msg); +} + +static const GDBusMethodTable reporter_adapter_methods[] = { + { GDBUS_METHOD("RegisterProximity", NULL, NULL, + register_proximity) }, + { GDBUS_METHOD("UnregisterProximity", NULL, NULL, + unregister_proximity) }, + { } +}; +#endif + +void reporter_device_remove(struct btd_service *service) +{ + struct btd_device *device = btd_service_get_device(service); + struct reporter_adapter *radapter; + struct btd_adapter *adapter = device_get_adapter(device); + + radapter = find_reporter_adapter(adapter); + if (!radapter) + return; + + unregister_reporter_device(device, radapter); +} + +int reporter_adapter_probe(struct btd_profile *p, struct btd_adapter *adapter) +{ + struct reporter_adapter *radapter; + + radapter = g_new0(struct reporter_adapter, 1); + radapter->adapter = adapter; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + const char *path = adapter_get_path(adapter); + + g_dbus_register_interface(btd_get_dbus_connection(), path, + PROXIMITY_REPORTER_INTERFACE, + reporter_adapter_methods, + NULL, NULL, adapter, NULL); +#else + link_loss_register(adapter); + register_tx_power(adapter); + imm_alert_register(adapter); +#endif + reporter_adapters = g_slist_prepend(reporter_adapters, radapter); + DBG("Proximity Reporter for adapter %p", adapter); + + return 0; +} + +void reporter_adapter_remove(struct btd_profile *p, + struct btd_adapter *adapter) +{ + struct reporter_adapter *radapter = find_reporter_adapter(adapter); + if (!radapter) + return; + + g_slist_foreach(radapter->devices, unregister_reporter_device, + radapter); + + link_loss_unregister(adapter); + imm_alert_unregister(adapter); + + reporter_adapters = g_slist_remove(reporter_adapters, radapter); + g_free(radapter); +} diff --git a/profiles/proximity/reporter.h b/profiles/proximity/reporter.h new file mode 100755 index 0000000..ed2c4dc --- /dev/null +++ b/profiles/proximity/reporter.h @@ -0,0 +1,46 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2011 Nokia Corporation + * Copyright (C) 2011 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#define PROXIMITY_REPORTER_INTERFACE "org.bluez.ProximityReporter1" + +#define IMMEDIATE_ALERT_SVC_UUID 0x1802 +#define LINK_LOSS_SVC_UUID 0x1803 +#define TX_POWER_SVC_UUID 0x1804 +#define ALERT_LEVEL_CHR_UUID 0x2A06 +#define POWER_LEVEL_CHR_UUID 0x2A07 + +enum { + NO_ALERT = 0x00, + MILD_ALERT = 0x01, + HIGH_ALERT = 0x02, +}; + +void reporter_device_remove(struct btd_service *service); +int reporter_device_probe(struct btd_service *service); + +int reporter_adapter_probe(struct btd_profile *p, struct btd_adapter *adapter); +void reporter_adapter_remove(struct btd_profile *p, + struct btd_adapter *adapter); + +const char *get_alert_level_string(uint8_t level); \ No newline at end of file diff --git a/profiles/tds/manager.c b/profiles/tds/manager.c new file mode 100755 index 0000000..6d13ee3 --- /dev/null +++ b/profiles/tds/manager.c @@ -0,0 +1,68 @@ +/* + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2016 Samsung Electronics Co. Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include "lib/bluetooth.h" +#include "lib/uuid.h" +#include "src/plugin.h" + +#include "gdbus/gdbus.h" + +#include "src/adapter.h" +#include "src/profile.h" + +#include "tds.h" + +int tds_provider_adapter_probe(struct btd_profile *p, + struct btd_adapter *adapter) +{ + tds_register_provider_interface(adapter); + return 0; +} + +void tds_provider_adapter_remove(struct btd_profile *p, + struct btd_adapter *adapter) +{ + tds_unregister_provider_interface(adapter); +} + +static struct btd_profile tds_provider = { + .name = "TDS Provider GATT Driver", + .remote_uuid = GATT_UUID, + .adapter_probe = tds_provider_adapter_probe, + .adapter_remove = tds_provider_adapter_remove, +}; + +static int tds_provider_init(void) +{ + return btd_profile_register(&tds_provider); +} + +static void tds_provider_exit(void) +{ + btd_profile_unregister(&tds_provider); +} + +BLUETOOTH_PLUGIN_DEFINE(tds, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, + tds_provider_init, tds_provider_exit) diff --git a/profiles/tds/tds.c b/profiles/tds/tds.c new file mode 100755 index 0000000..786bf3d --- /dev/null +++ b/profiles/tds/tds.c @@ -0,0 +1,785 @@ +/* + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2016 Samsung Electronics Co. Ltd. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + + +#include + +#include + +#include +#include + +#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); +} diff --git a/profiles/tds/tds.h b/profiles/tds/tds.h new file mode 100755 index 0000000..85b9b10 --- /dev/null +++ b/profiles/tds/tds.h @@ -0,0 +1,34 @@ +/* + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2011 Nokia Corporation + * Copyright (C) 2011 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#define TRANSPORT_DISCOVERY_SERVICE_UUID 0x1824 +#define TDS_CONTROL_POINT_CHARACTERISTIC_UUID 0x2abc + +#define TDS_USER_CHARACTERITIC_UUID 0x2af6 +#define TDS_USER_CHARACTERITIC_DESCRIPTOR_UUID 0x2a56 + +#define TDS_SERVICE_PROVIDER_INTERFACE "org.bluez.TdsServiceProvider1" + +void tds_register_provider_interface(struct btd_adapter *adapter); + +void tds_unregister_provider_interface(struct btd_adapter *adapter); diff --git a/profiles/thermometer/thermometer.c b/profiles/thermometer/thermometer.c new file mode 100755 index 0000000..b0fc3e0 --- /dev/null +++ b/profiles/thermometer/thermometer.c @@ -0,0 +1,1321 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2011 GSyC/LibreSoft, Universidad Rey Juan Carlos. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#ifdef HAVE_CONFIG_H +#include +#endif + +#include +#include + +#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[] = { + "", + "armpit", + "body", + "ear", + "finger", + "intestines", + "mouth", + "rectum", + "toe", + "tympanum" +}; + +static const char *temptype2str(uint8_t value) +{ + if (value > 0 && value < G_N_ELEMENTS(temp_type)) + return temp_type[value]; + + error("Temperature type %d reserved for future use", value); + return NULL; +} + +static void destroy_watcher(gpointer user_data) +{ + struct watcher *watcher = user_data; + + g_free(watcher->path); + g_free(watcher->srv); + g_free(watcher); +} + +static void remove_watcher(gpointer user_data) +{ + struct watcher *watcher = user_data; + + g_dbus_remove_watch(btd_get_dbus_connection(), watcher->id); +} + +static void destroy_thermometer(gpointer user_data) +{ + struct thermometer *t = user_data; + + if (t->attioid > 0) + btd_device_remove_attio_callback(t->dev, t->attioid); + + if (t->attrib != NULL) { + g_attrib_unregister(t->attrib, t->attio_measurement_id); + g_attrib_unregister(t->attrib, t->attio_intermediate_id); + g_attrib_unregister(t->attrib, t->attio_interval_id); + g_attrib_unref(t->attrib); + } + + btd_device_unref(t->dev); + g_free(t->svc_range); + g_free(t); +} + +static void destroy_thermometer_adapter(gpointer user_data) +{ + struct thermometer_adapter *tadapter = user_data; + + if (tadapter->devices != NULL) + g_slist_free_full(tadapter->devices, destroy_thermometer); + + if (tadapter->fwatchers != NULL) + g_slist_free_full(tadapter->fwatchers, remove_watcher); + + g_free(tadapter); +} + +static int cmp_adapter(gconstpointer a, gconstpointer b) +{ + const struct thermometer_adapter *tadapter = a; + const struct btd_adapter *adapter = b; + + if (adapter == tadapter->adapter) + return 0; + + return -1; +} + +static int cmp_device(gconstpointer a, gconstpointer b) +{ + const struct thermometer *t = a; + const struct btd_device *dev = b; + + if (dev == t->dev) + return 0; + + return -1; +} + +static int cmp_watcher(gconstpointer a, gconstpointer b) +{ + const struct watcher *watcher = a; + const struct watcher *match = b; + int ret; + + ret = g_strcmp0(watcher->srv, match->srv); + if (ret != 0) + return ret; + + return g_strcmp0(watcher->path, match->path); +} + +static struct thermometer_adapter * +find_thermometer_adapter(struct btd_adapter *adapter) +{ + GSList *l = g_slist_find_custom(thermometer_adapters, adapter, + cmp_adapter); + if (!l) + return NULL; + + return l->data; +} + +static void change_property(struct thermometer *t, const char *name, + gpointer value) { + if (g_strcmp0(name, "Intermediate") == 0) { + gboolean *intermediate = value; + if (t->intermediate == *intermediate) + return; + + t->intermediate = *intermediate; + } else if (g_strcmp0(name, "Interval") == 0) { + uint16_t *interval = value; + if (t->has_interval && t->interval == *interval) + return; + + t->has_interval = TRUE; + t->interval = *interval; + } else if (g_strcmp0(name, "Maximum") == 0) { + uint16_t *max = value; + if (t->max == *max) + return; + + t->max = *max; + } else if (g_strcmp0(name, "Minimum") == 0) { + uint16_t *min = value; + if (t->min == *min) + return; + + t->min = *min; + } else { + DBG("%s is not a thermometer property", name); + return; + } + + g_dbus_emit_property_changed(btd_get_dbus_connection(), + device_get_path(t->dev), + THERMOMETER_INTERFACE, name); +} + +static void update_watcher(gpointer data, gpointer user_data) +{ + struct watcher *w = data; + struct measurement *m = user_data; + const char *path = device_get_path(m->t->dev); + DBusMessageIter iter; + DBusMessageIter dict; + DBusMessage *msg; + + msg = dbus_message_new_method_call(w->srv, w->path, + THERMOMETER_WATCHER_INTERFACE, + "MeasurementReceived"); + if (msg == NULL) + return; + + dbus_message_iter_init_append(msg, &iter); + + dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH , &path); + + dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, + DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING + DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING + DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict); + + dict_append_entry(&dict, "Exponent", DBUS_TYPE_INT16, &m->exp); + dict_append_entry(&dict, "Mantissa", DBUS_TYPE_INT32, &m->mant); + dict_append_entry(&dict, "Unit", DBUS_TYPE_STRING, &m->unit); + + if (m->suptime) + dict_append_entry(&dict, "Time", DBUS_TYPE_UINT64, &m->time); + + dict_append_entry(&dict, "Type", DBUS_TYPE_STRING, &m->type); + dict_append_entry(&dict, "Measurement", DBUS_TYPE_STRING, &m->value); + + dbus_message_iter_close_container(&iter, &dict); + + dbus_message_set_no_reply(msg, TRUE); + g_dbus_send_message(btd_get_dbus_connection(), msg); +} + +static void recv_measurement(struct thermometer *t, struct measurement *m) +{ + GSList *wlist; + + m->t = t; + + if (g_strcmp0(m->value, "intermediate") == 0) + wlist = t->tadapter->iwatchers; + else + wlist = t->tadapter->fwatchers; + + g_slist_foreach(wlist, update_watcher, m); +} + +static void proc_measurement(struct thermometer *t, const uint8_t *pdu, + uint16_t len, gboolean final) +{ + struct measurement m; + const char *type = NULL; + uint8_t flags; + uint32_t raw; + + /* skip opcode and handle */ + pdu += 3; + len -= 3; + + if (len < 1) { + DBG("Mandatory flags are not provided"); + return; + } + + memset(&m, 0, sizeof(m)); + + flags = *pdu; + + if (flags & TEMP_UNITS) + m.unit = "fahrenheit"; + else + m.unit = "celsius"; + + pdu++; + len--; + + if (len < 4) { + DBG("Mandatory temperature measurement value is not provided"); + return; + } + + raw = get_le32(pdu); + m.mant = raw & 0x00FFFFFF; + m.exp = ((int32_t) raw) >> 24; + + if (m.mant & 0x00800000) { + /* convert to C2 negative value */ + m.mant = m.mant - FLOAT_MAX_MANTISSA; + } + + pdu += 4; + len -= 4; + + if (flags & TEMP_TIME_STAMP) { + struct tm ts; + time_t time; + + if (len < 7) { + DBG("Time stamp is not provided"); + return; + } + + ts.tm_year = get_le16(pdu) - 1900; + ts.tm_mon = *(pdu + 2) - 1; + ts.tm_mday = *(pdu + 3); + ts.tm_hour = *(pdu + 4); + ts.tm_min = *(pdu + 5); + ts.tm_sec = *(pdu + 6); + ts.tm_isdst = -1; + + time = mktime(&ts); + m.time = (uint64_t) time; + m.suptime = TRUE; + + pdu += 7; + len -= 7; + } + + if (flags & TEMP_TYPE) { + if (len < 1) { + DBG("Temperature type is not provided"); + return; + } + + type = temptype2str(*pdu); + } else if (t->has_type) { + type = temptype2str(t->type); + } + + m.type = g_strdup(type); + m.value = final ? "final" : "intermediate"; + + recv_measurement(t, &m); + g_free(m.type); +} + + +static void measurement_ind_handler(const uint8_t *pdu, uint16_t len, + gpointer user_data) +{ + struct thermometer *t = user_data; + uint8_t *opdu; + uint16_t olen; + size_t plen; + + if (len < 3) { + DBG("Bad pdu received"); + return; + } + + proc_measurement(t, pdu, len, TRUE); + + opdu = g_attrib_get_buffer(t->attrib, &plen); + olen = enc_confirmation(opdu, plen); + + if (olen > 0) + g_attrib_send(t->attrib, 0, opdu, olen, NULL, NULL, NULL); +} + +static void intermediate_notify_handler(const uint8_t *pdu, uint16_t len, + gpointer user_data) +{ + struct thermometer *t = user_data; + + if (len < 3) { + DBG("Bad pdu received"); + return; + } + + proc_measurement(t, pdu, len, FALSE); +} + +static void interval_ind_handler(const uint8_t *pdu, uint16_t len, + gpointer user_data) +{ + struct thermometer *t = user_data; + uint16_t interval; + uint8_t *opdu; + uint16_t olen; + size_t plen; + + if (len < 5) { + DBG("Bad pdu received"); + return; + } + + interval = get_le16(pdu + 3); + change_property(t, "Interval", &interval); + + opdu = g_attrib_get_buffer(t->attrib, &plen); + olen = enc_confirmation(opdu, plen); + + if (olen > 0) + g_attrib_send(t->attrib, 0, opdu, olen, NULL, NULL, NULL); +} + +static void valid_range_desc_cb(guint8 status, const guint8 *pdu, guint16 len, + gpointer user_data) +{ + struct thermometer *t = user_data; + uint8_t value[VALID_RANGE_DESC_SIZE]; + uint16_t max, min; + ssize_t vlen; + + if (status != 0) { + DBG("Valid Range descriptor read failed: %s", + att_ecode2str(status)); + return; + } + + vlen = dec_read_resp(pdu, len, value, sizeof(value)); + if (vlen < 0) { + DBG("Protocol error\n"); + return; + } + + if (vlen < 4) { + DBG("Invalid range received"); + return; + } + + min = get_le16(&value[0]); + max = get_le16(&value[2]); + + if (min == 0 || min > max) { + DBG("Invalid range"); + return; + } + + change_property(t, "Maximum", &max); + change_property(t, "Minimum", &min); +} + +static void write_ccc_cb(guint8 status, const guint8 *pdu, + guint16 len, gpointer user_data) +{ + char *msg = user_data; + + if (status != 0) + error("%s failed", msg); + + g_free(msg); +} + +static void process_thermometer_desc(struct characteristic *ch, uint16_t uuid, + uint16_t handle) +{ + uint8_t atval[2]; + uint16_t val; + char *msg; + + if (uuid == GATT_CHARAC_VALID_RANGE_UUID) { + if (g_strcmp0(ch->uuid, MEASUREMENT_INTERVAL_UUID) == 0) + gatt_read_char(ch->t->attrib, handle, + valid_range_desc_cb, ch->t); + return; + } + + if (uuid != GATT_CLIENT_CHARAC_CFG_UUID) + return; + + if (g_strcmp0(ch->uuid, TEMPERATURE_MEASUREMENT_UUID) == 0) { + ch->t->measurement_ccc_handle = handle; + + if (g_slist_length(ch->t->tadapter->fwatchers) == 0) { + val = 0x0000; + msg = g_strdup("Disable Temperature Measurement ind"); + } else { + val = GATT_CLIENT_CHARAC_CFG_IND_BIT; + msg = g_strdup("Enable Temperature Measurement ind"); + } + } else if (g_strcmp0(ch->uuid, INTERMEDIATE_TEMPERATURE_UUID) == 0) { + ch->t->intermediate_ccc_handle = handle; + + if (g_slist_length(ch->t->tadapter->iwatchers) == 0) { + val = 0x0000; + msg = g_strdup("Disable Intermediate Temperature noti"); + } else { + val = GATT_CLIENT_CHARAC_CFG_NOTIF_BIT; + msg = g_strdup("Enable Intermediate Temperature noti"); + } + } else if (g_strcmp0(ch->uuid, MEASUREMENT_INTERVAL_UUID) == 0) { + val = GATT_CLIENT_CHARAC_CFG_IND_BIT; + msg = g_strdup("Enable Measurement Interval indication"); + } else { + return; + } + + put_le16(val, atval); + gatt_write_char(ch->t->attrib, handle, atval, sizeof(atval), + write_ccc_cb, msg); +} + +static void discover_desc_cb(guint8 status, GSList *descs, gpointer user_data) +{ + struct characteristic *ch = user_data; + + if (status != 0) { + error("Discover all characteristic descriptors failed [%s]: %s", + ch->uuid, att_ecode2str(status)); + goto done; + } + + for ( ; descs; descs = descs->next) { + struct gatt_desc *desc = descs->data; + + process_thermometer_desc(ch, desc->uuid16, desc->handle); + } + +done: + g_free(ch); +} + +static void discover_desc(struct thermometer *t, struct gatt_char *c, + struct gatt_char *c_next) +{ + struct characteristic *ch; + uint16_t start, end; + + start = c->value_handle + 1; + + if (c_next != NULL) { + if (start == c_next->handle) + return; + end = c_next->handle - 1; + } else if (c->value_handle != t->svc_range->end) { + end = t->svc_range->end; + } else { + return; + } + + ch = g_new0(struct characteristic, 1); + ch->t = t; + memcpy(ch->uuid, c->uuid, sizeof(c->uuid)); + + gatt_discover_desc(t->attrib, start, end, NULL, discover_desc_cb, ch); +} + +static void read_temp_type_cb(guint8 status, const guint8 *pdu, guint16 len, + gpointer user_data) +{ + struct thermometer *t = user_data; + uint8_t value[TEMPERATURE_TYPE_SIZE]; + ssize_t vlen; + + if (status != 0) { + DBG("Temperature Type value read failed: %s", + att_ecode2str(status)); + return; + } + + vlen = dec_read_resp(pdu, len, value, sizeof(value)); + if (vlen < 0) { + DBG("Protocol error."); + return; + } + + if (vlen != 1) { + DBG("Invalid length for Temperature type"); + return; + } + + t->has_type = TRUE; + t->type = value[0]; +} + +static void read_interval_cb(guint8 status, const guint8 *pdu, guint16 len, + gpointer user_data) +{ + struct thermometer *t = user_data; + uint8_t value[MEASUREMENT_INTERVAL_SIZE]; + uint16_t interval; + ssize_t vlen; + + if (status != 0) { + DBG("Measurement Interval value read failed: %s", + att_ecode2str(status)); + return; + } + + vlen = dec_read_resp(pdu, len, value, sizeof(value)); + if (vlen < 0) { + DBG("Protocol error\n"); + return; + } + + if (vlen < 2) { + DBG("Invalid Interval received"); + return; + } + + interval = get_le16(&value[0]); + change_property(t, "Interval", &interval); +} + +static void process_thermometer_char(struct thermometer *t, + struct gatt_char *c, struct gatt_char *c_next) +{ + if (g_strcmp0(c->uuid, INTERMEDIATE_TEMPERATURE_UUID) == 0) { + gboolean intermediate = TRUE; + change_property(t, "Intermediate", &intermediate); + + t->attio_intermediate_id = g_attrib_register(t->attrib, + ATT_OP_HANDLE_NOTIFY, c->value_handle, + intermediate_notify_handler, t, NULL); + + discover_desc(t, c, c_next); + } else if (g_strcmp0(c->uuid, TEMPERATURE_MEASUREMENT_UUID) == 0) { + + t->attio_measurement_id = g_attrib_register(t->attrib, + ATT_OP_HANDLE_IND, c->value_handle, + measurement_ind_handler, t, NULL); + + discover_desc(t, c, c_next); + } else if (g_strcmp0(c->uuid, TEMPERATURE_TYPE_UUID) == 0) { + gatt_read_char(t->attrib, c->value_handle, + read_temp_type_cb, t); + } else if (g_strcmp0(c->uuid, MEASUREMENT_INTERVAL_UUID) == 0) { + bool need_desc = false; + + gatt_read_char(t->attrib, c->value_handle, read_interval_cb, t); + + if (c->properties & GATT_CHR_PROP_WRITE) { + t->interval_val_handle = c->value_handle; + need_desc = true; + } + + if (c->properties & GATT_CHR_PROP_INDICATE) { + t->attio_interval_id = g_attrib_register(t->attrib, + ATT_OP_HANDLE_IND, c->value_handle, + interval_ind_handler, t, NULL); + need_desc = true; + } + + if (need_desc) + discover_desc(t, c, c_next); + } +} + +static void configure_thermometer_cb(uint8_t status, GSList *characteristics, + void *user_data) +{ + struct thermometer *t = user_data; + GSList *l; + + if (status != 0) { + error("Discover thermometer characteristics: %s", + att_ecode2str(status)); + return; + } + + for (l = characteristics; l; l = l->next) { + struct gatt_char *c = l->data; + struct gatt_char *c_next = (l->next ? l->next->data : NULL); + + process_thermometer_char(t, c, c_next); + } +} + +static void write_interval_cb(guint8 status, const guint8 *pdu, guint16 len, + gpointer user_data) +{ + struct tmp_interval_data *data = user_data; + + if (status != 0) { + error("Interval Write Request failed %s", + att_ecode2str(status)); + goto done; + } + + if (!dec_write_resp(pdu, len)) { + error("Interval Write Request: protocol error"); + goto done; + } + + change_property(data->thermometer, "Interval", &data->interval); + +done: + g_free(user_data); +} + +static void enable_final_measurement(gpointer data, gpointer user_data) +{ + struct thermometer *t = data; + uint16_t handle = t->measurement_ccc_handle; + uint8_t value[2]; + char *msg; + + if (t->attrib == NULL || !handle) + return; + + put_le16(GATT_CLIENT_CHARAC_CFG_IND_BIT, value); + msg = g_strdup("Enable Temperature Measurement indications"); + + gatt_write_char(t->attrib, handle, value, sizeof(value), + write_ccc_cb, msg); +} + +static void enable_intermediate_measurement(gpointer data, gpointer user_data) +{ + struct thermometer *t = data; + uint16_t handle = t->intermediate_ccc_handle; + uint8_t value[2]; + char *msg; + + if (t->attrib == NULL || !handle) + return; + + put_le16(GATT_CLIENT_CHARAC_CFG_NOTIF_BIT, value); + msg = g_strdup("Enable Intermediate Temperature notifications"); + + gatt_write_char(t->attrib, handle, value, sizeof(value), + write_ccc_cb, msg); +} + +static void disable_final_measurement(gpointer data, gpointer user_data) +{ + struct thermometer *t = data; + uint16_t handle = t->measurement_ccc_handle; + uint8_t value[2]; + char *msg; + + if (t->attrib == NULL || !handle) + return; + + put_le16(0x0000, value); + msg = g_strdup("Disable Temperature Measurement indications"); + + gatt_write_char(t->attrib, handle, value, sizeof(value), + write_ccc_cb, msg); +} + +static void disable_intermediate_measurement(gpointer data, gpointer user_data) +{ + struct thermometer *t = data; + uint16_t handle = t->intermediate_ccc_handle; + uint8_t value[2]; + char *msg; + + if (t->attrib == NULL || !handle) + return; + + put_le16(0x0000, value); + msg = g_strdup("Disable Intermediate Temperature notifications"); + + gatt_write_char(t->attrib, handle, value, sizeof(value), + write_ccc_cb, msg); +} + +static void remove_int_watcher(struct thermometer_adapter *tadapter, + struct watcher *w) +{ + if (!g_slist_find(tadapter->iwatchers, w)) + return; + + tadapter->iwatchers = g_slist_remove(tadapter->iwatchers, w); + + if (g_slist_length(tadapter->iwatchers) == 0) + g_slist_foreach(tadapter->devices, + disable_intermediate_measurement, 0); +} + +static void watcher_exit(DBusConnection *conn, void *user_data) +{ + struct watcher *watcher = user_data; + struct thermometer_adapter *tadapter = watcher->tadapter; + + DBG("Thermometer watcher %s disconnected", watcher->path); + + remove_int_watcher(tadapter, watcher); + + tadapter->fwatchers = g_slist_remove(tadapter->fwatchers, watcher); + g_dbus_remove_watch(btd_get_dbus_connection(), watcher->id); + + if (g_slist_length(tadapter->fwatchers) == 0) + g_slist_foreach(tadapter->devices, + disable_final_measurement, 0); +} + +static struct watcher *find_watcher(GSList *list, const char *sender, + const char *path) +{ + struct watcher *match; + GSList *l; + + match = g_new0(struct watcher, 1); + match->srv = g_strdup(sender); + match->path = g_strdup(path); + + l = g_slist_find_custom(list, match, cmp_watcher); + destroy_watcher(match); + + if (l != NULL) + return l->data; + + return NULL; +} + +static DBusMessage *register_watcher(DBusConnection *conn, DBusMessage *msg, + void *data) +{ + const char *sender = dbus_message_get_sender(msg); + struct thermometer_adapter *tadapter = data; + struct watcher *watcher; + char *path; + + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID)) + return btd_error_invalid_args(msg); + + watcher = find_watcher(tadapter->fwatchers, sender, path); + if (watcher != NULL) + return btd_error_already_exists(msg); + + DBG("Thermometer watcher %s registered", path); + + watcher = g_new0(struct watcher, 1); + watcher->srv = g_strdup(sender); + watcher->path = g_strdup(path); + watcher->tadapter = tadapter; + watcher->id = g_dbus_add_disconnect_watch(conn, sender, watcher_exit, + watcher, destroy_watcher); + + if (g_slist_length(tadapter->fwatchers) == 0) + g_slist_foreach(tadapter->devices, enable_final_measurement, 0); + + tadapter->fwatchers = g_slist_prepend(tadapter->fwatchers, watcher); + + return dbus_message_new_method_return(msg); +} + +static DBusMessage *unregister_watcher(DBusConnection *conn, DBusMessage *msg, + void *data) +{ + const char *sender = dbus_message_get_sender(msg); + struct thermometer_adapter *tadapter = data; + struct watcher *watcher; + char *path; + + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID)) + return btd_error_invalid_args(msg); + + watcher = find_watcher(tadapter->fwatchers, sender, path); + if (watcher == NULL) + return btd_error_does_not_exist(msg); + + DBG("Thermometer watcher %s unregistered", path); + + remove_int_watcher(tadapter, watcher); + + tadapter->fwatchers = g_slist_remove(tadapter->fwatchers, watcher); + g_dbus_remove_watch(btd_get_dbus_connection(), watcher->id); + + if (g_slist_length(tadapter->fwatchers) == 0) + g_slist_foreach(tadapter->devices, + disable_final_measurement, 0); + + return dbus_message_new_method_return(msg); +} + +static DBusMessage *enable_intermediate(DBusConnection *conn, DBusMessage *msg, + void *data) +{ + const char *sender = dbus_message_get_sender(msg); + struct thermometer_adapter *ta = data; + struct watcher *watcher; + char *path; + + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID)) + return btd_error_invalid_args(msg); + + watcher = find_watcher(ta->fwatchers, sender, path); + if (watcher == NULL) + return btd_error_does_not_exist(msg); + + if (find_watcher(ta->iwatchers, sender, path)) + return btd_error_already_exists(msg); + + DBG("Intermediate measurement watcher %s registered", path); + + if (g_slist_length(ta->iwatchers) == 0) + g_slist_foreach(ta->devices, + enable_intermediate_measurement, 0); + + ta->iwatchers = g_slist_prepend(ta->iwatchers, watcher); + + return dbus_message_new_method_return(msg); +} + +static DBusMessage *disable_intermediate(DBusConnection *conn, DBusMessage *msg, + void *data) +{ + const char *sender = dbus_message_get_sender(msg); + struct thermometer_adapter *ta = data; + struct watcher *watcher; + char *path; + + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID)) + return btd_error_invalid_args(msg); + + watcher = find_watcher(ta->iwatchers, sender, path); + if (watcher == NULL) + return btd_error_does_not_exist(msg); + + DBG("Intermediate measurement %s unregistered", path); + + remove_int_watcher(ta, watcher); + + return dbus_message_new_method_return(msg); +} + +static gboolean property_get_intermediate(const GDBusPropertyTable *property, + DBusMessageIter *iter, void *data) +{ + struct thermometer *t = data; + dbus_bool_t val; + + val = !!t->intermediate; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &val); + + return TRUE; +} + +static gboolean property_get_interval(const GDBusPropertyTable *property, + DBusMessageIter *iter, void *data) +{ + struct thermometer *t = data; + + if (!t->has_interval) + return FALSE; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &t->interval); + + return TRUE; +} + +static void property_set_interval(const GDBusPropertyTable *property, + DBusMessageIter *iter, + GDBusPendingPropertySet id, void *data) +{ + struct thermometer *t = data; + struct tmp_interval_data *interval_data; + uint16_t val; + uint8_t atval[2]; + + if (t->interval_val_handle == 0) { + g_dbus_pending_property_error(id, + ERROR_INTERFACE ".NotSupported", + "Operation is not supported"); + return; + } + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_UINT16) { + g_dbus_pending_property_error(id, + ERROR_INTERFACE ".InvalidArguments", + "Invalid arguments in method call"); + return; + } + + dbus_message_iter_get_basic(iter, &val); + + if (val < t->min || val > t->max) { + g_dbus_pending_property_error(id, + ERROR_INTERFACE ".InvalidArguments", + "Invalid arguments in method call"); + return; + } + + put_le16(val, &atval[0]); + + interval_data = g_new0(struct tmp_interval_data, 1); + interval_data->thermometer = t; + interval_data->interval = val; + gatt_write_char(t->attrib, t->interval_val_handle, atval, sizeof(atval), + write_interval_cb, interval_data); + + g_dbus_pending_property_success(id); +} + +static gboolean property_exists_interval(const GDBusPropertyTable *property, + void *data) +{ + struct thermometer *t = data; + + return t->has_interval; +} + +static gboolean property_get_maximum(const GDBusPropertyTable *property, + DBusMessageIter *iter, void *data) +{ + struct thermometer *t = data; + + if (!t->has_interval) + return FALSE; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &t->max); + + return TRUE; +} + +static gboolean property_get_minimum(const GDBusPropertyTable *property, + DBusMessageIter *iter, void *data) +{ + struct thermometer *t = data; + + if (!t->has_interval) + return FALSE; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &t->min); + + return TRUE; +} + +static const GDBusPropertyTable thermometer_properties[] = { + { "Intermediate", "b", property_get_intermediate }, + { "Interval", "q", property_get_interval, property_set_interval, + property_exists_interval }, + { "Maximum", "q", property_get_maximum, NULL, + property_exists_interval }, + { "Minimum", "q", property_get_minimum, NULL, + property_exists_interval }, + { } +}; + +static void attio_connected_cb(GAttrib *attrib, gpointer user_data) +{ + struct thermometer *t = user_data; + + t->attrib = g_attrib_ref(attrib); + + gatt_discover_char(t->attrib, t->svc_range->start, t->svc_range->end, + NULL, configure_thermometer_cb, t); +} + +static void attio_disconnected_cb(gpointer user_data) +{ + struct thermometer *t = user_data; + + DBG("GATT Disconnected"); + + if (t->attio_measurement_id > 0) { + g_attrib_unregister(t->attrib, t->attio_measurement_id); + t->attio_measurement_id = 0; + } + + if (t->attio_intermediate_id > 0) { + g_attrib_unregister(t->attrib, t->attio_intermediate_id); + t->attio_intermediate_id = 0; + } + + if (t->attio_interval_id > 0) { + g_attrib_unregister(t->attrib, t->attio_interval_id); + t->attio_interval_id = 0; + } + + g_attrib_unref(t->attrib); + t->attrib = NULL; +} + +static int thermometer_register(struct btd_device *device, + struct gatt_primary *tattr) +{ + const char *path = device_get_path(device); + struct thermometer *t; + struct btd_adapter *adapter; + struct thermometer_adapter *tadapter; + + adapter = device_get_adapter(device); + + tadapter = find_thermometer_adapter(adapter); + + if (tadapter == NULL) + return -1; + + t = g_new0(struct thermometer, 1); + t->dev = btd_device_ref(device); + t->tadapter = tadapter; + t->svc_range = g_new0(struct att_range, 1); + t->svc_range->start = tattr->range.start; + t->svc_range->end = tattr->range.end; + + tadapter->devices = g_slist_prepend(tadapter->devices, t); + + if (!g_dbus_register_interface(btd_get_dbus_connection(), + path, THERMOMETER_INTERFACE, + NULL, NULL, thermometer_properties, + t, destroy_thermometer)) { + error("D-Bus failed to register %s interface", + THERMOMETER_INTERFACE); + destroy_thermometer(t); + return -EIO; + } + + t->attioid = btd_device_add_attio_callback(device, attio_connected_cb, + attio_disconnected_cb, t); + return 0; +} + +static void thermometer_unregister(struct btd_device *device) +{ + struct thermometer *t; + struct btd_adapter *adapter; + struct thermometer_adapter *tadapter; + GSList *l; + + adapter = device_get_adapter(device); + + tadapter = find_thermometer_adapter(adapter); + + if (tadapter == NULL) + return; + + l = g_slist_find_custom(tadapter->devices, device, cmp_device); + if (l == NULL) + return; + + t = l->data; + + tadapter->devices = g_slist_remove(tadapter->devices, t); + + g_dbus_unregister_interface(btd_get_dbus_connection(), + device_get_path(t->dev), THERMOMETER_INTERFACE); +} + +static const GDBusMethodTable thermometer_manager_methods[] = { + { GDBUS_METHOD("RegisterWatcher", + GDBUS_ARGS({ "agent", "o" }), NULL, + register_watcher) }, + { GDBUS_METHOD("UnregisterWatcher", + GDBUS_ARGS({ "agent", "o" }), NULL, + unregister_watcher) }, + { GDBUS_METHOD("EnableIntermediateMeasurement", + GDBUS_ARGS({ "agent", "o" }), NULL, + enable_intermediate) }, + { GDBUS_METHOD("DisableIntermediateMeasurement", + GDBUS_ARGS({ "agent", "o" }), NULL, + disable_intermediate) }, + { } +}; + +static int thermometer_adapter_register(struct btd_adapter *adapter) +{ + struct thermometer_adapter *tadapter; + + tadapter = g_new0(struct thermometer_adapter, 1); + tadapter->adapter = adapter; + + if (!g_dbus_register_interface(btd_get_dbus_connection(), + adapter_get_path(adapter), + THERMOMETER_MANAGER_INTERFACE, + thermometer_manager_methods, + NULL, NULL, tadapter, + destroy_thermometer_adapter)) { + error("D-Bus failed to register %s interface", + THERMOMETER_MANAGER_INTERFACE); + destroy_thermometer_adapter(tadapter); + return -EIO; + } + + thermometer_adapters = g_slist_prepend(thermometer_adapters, tadapter); + + return 0; +} + +static void thermometer_adapter_unregister(struct btd_adapter *adapter) +{ + struct thermometer_adapter *tadapter; + + tadapter = find_thermometer_adapter(adapter); + if (tadapter == NULL) + return; + + thermometer_adapters = g_slist_remove(thermometer_adapters, tadapter); + + g_dbus_unregister_interface(btd_get_dbus_connection(), + adapter_get_path(tadapter->adapter), + THERMOMETER_MANAGER_INTERFACE); +} + +static int thermometer_device_probe(struct btd_service *service) +{ + struct btd_device *device = btd_service_get_device(service); + struct gatt_primary *tattr; + + tattr = btd_device_get_primary(device, HEALTH_THERMOMETER_UUID); + if (tattr == NULL) + return -EINVAL; + + return thermometer_register(device, tattr); +} + +static void thermometer_device_remove(struct btd_service *service) +{ + struct btd_device *device = btd_service_get_device(service); + + thermometer_unregister(device); +} + +static int thermometer_adapter_probe(struct btd_profile *p, + struct btd_adapter *adapter) +{ + return thermometer_adapter_register(adapter); +} + +static void thermometer_adapter_remove(struct btd_profile *p, + struct btd_adapter *adapter) +{ + thermometer_adapter_unregister(adapter); +} + +static struct btd_profile thermometer_profile = { + .name = "Health Thermometer GATT driver", + .remote_uuid = HEALTH_THERMOMETER_UUID, + .device_probe = thermometer_device_probe, + .device_remove = thermometer_device_remove, + .adapter_probe = thermometer_adapter_probe, + .adapter_remove = thermometer_adapter_remove +}; + +static int thermometer_init(void) +{ + return btd_profile_register(&thermometer_profile); +} + +static void thermometer_exit(void) +{ + btd_profile_unregister(&thermometer_profile); +} + +BLUETOOTH_PLUGIN_DEFINE(thermometer, VERSION, BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, + thermometer_init, thermometer_exit) diff --git a/profiles/time/server.c b/profiles/time/server.c new file mode 100755 index 0000000..2289c6a --- /dev/null +++ b/profiles/time/server.c @@ -0,0 +1,283 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2011 Nokia Corporation + * Copyright (C) 2011 Marcel Holtmann + * + * + * 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 +#endif + +#include +#include +#include +#include + +#include + +#include "lib/bluetooth.h" +#include "lib/sdp.h" +#include "lib/uuid.h" + +#include "src/adapter.h" +#include "src/device.h" +#include "src/profile.h" +#include "src/plugin.h" +#include "attrib/gattrib.h" +#include "attrib/att.h" +#include "attrib/gatt.h" +#include "attrib/att-database.h" +#include "src/shared/util.h" +#include "src/attrib-server.h" +#include "attrib/gatt-service.h" +#include "src/log.h" + +#define CURRENT_TIME_SVC_UUID 0x1805 +#define REF_TIME_UPDATE_SVC_UUID 0x1806 + +#define LOCAL_TIME_INFO_CHR_UUID 0x2A0F +#define TIME_UPDATE_CTRL_CHR_UUID 0x2A16 +#define TIME_UPDATE_STAT_CHR_UUID 0x2A17 +#define CT_TIME_CHR_UUID 0x2A2B + +enum { + UPDATE_RESULT_SUCCESSFUL = 0, + UPDATE_RESULT_CANCELED = 1, + UPDATE_RESULT_NO_CONN = 2, + UPDATE_RESULT_ERROR = 3, + UPDATE_RESULT_TIMEOUT = 4, + UPDATE_RESULT_NOT_ATTEMPTED = 5, +}; + +enum { + UPDATE_STATE_IDLE = 0, + UPDATE_STATE_PENDING = 1, +}; + +enum { + GET_REFERENCE_UPDATE = 1, + CANCEL_REFERENCE_UPDATE = 2, +}; + +static int encode_current_time(uint8_t value[10]) +{ + struct timespec tp; + struct tm tm; + + if (clock_gettime(CLOCK_REALTIME, &tp) == -1) { + int err = -errno; + + error("clock_gettime: %s", strerror(-err)); + return err; + } + + if (localtime_r(&tp.tv_sec, &tm) == NULL) { + error("localtime_r() failed"); + /* localtime_r() does not set errno */ + return -EINVAL; + } + + put_le16(1900 + tm.tm_year, &value[0]); /* Year */ + value[2] = tm.tm_mon + 1; /* Month */ + value[3] = tm.tm_mday; /* Day */ + value[4] = tm.tm_hour; /* Hours */ + value[5] = tm.tm_min; /* Minutes */ + value[6] = tm.tm_sec; /* Seconds */ + value[7] = tm.tm_wday == 0 ? 7 : tm.tm_wday; /* Day of Week */ + /* From Time Profile spec: "The number of 1/256 fractions of a second." + * In 1s there are 256 fractions, in 1ns there are 256/10^9 fractions. + * To avoid integer overflow, we use the equivalent 1/3906250 ratio. */ + value[8] = tp.tv_nsec / 3906250; /* Fractions256 */ + value[9] = 0x00; /* Adjust Reason */ + + return 0; +} + +static uint8_t current_time_read(struct attribute *a, + struct btd_device *device, gpointer user_data) +{ + struct btd_adapter *adapter = user_data; + uint8_t value[10]; + + if (encode_current_time(value) < 0) + return ATT_ECODE_IO; + + attrib_db_update(adapter, a->handle, NULL, value, sizeof(value), NULL); + + return 0; +} + +static uint8_t local_time_info_read(struct attribute *a, + struct btd_device *device, gpointer user_data) +{ + struct btd_adapter *adapter = user_data; + uint8_t value[2]; + + DBG("a=%p", a); + + tzset(); + + /* Convert POSIX "timezone" (seconds West of GMT) to Time Profile + * format (offset from UTC in number of 15 minutes increments). */ + value[0] = (uint8_t) (-1 * timezone / (60 * 15)); + + /* FIXME: POSIX "daylight" variable only indicates whether there + * is DST for the local time or not. The offset is unknown. */ + value[1] = daylight ? 0xff : 0x00; + + attrib_db_update(adapter, a->handle, NULL, value, sizeof(value), NULL); + + return 0; +} + +static gboolean register_current_time_service(struct btd_adapter *adapter) +{ + bt_uuid_t uuid; + + bt_uuid16_create(&uuid, CURRENT_TIME_SVC_UUID); + + /* Current Time service */ + return gatt_service_add(adapter, GATT_PRIM_SVC_UUID, &uuid, + /* CT Time characteristic */ + GATT_OPT_CHR_UUID16, CT_TIME_CHR_UUID, + GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ | + GATT_CHR_PROP_NOTIFY, + GATT_OPT_CHR_VALUE_CB, ATTRIB_READ, + current_time_read, adapter, + + /* Local Time Information characteristic */ + GATT_OPT_CHR_UUID16, LOCAL_TIME_INFO_CHR_UUID, + GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ, + GATT_OPT_CHR_VALUE_CB, ATTRIB_READ, + local_time_info_read, adapter, + + GATT_OPT_INVALID); +} + +static uint8_t time_update_control(struct attribute *a, + struct btd_device *device, + gpointer user_data) +{ + DBG("handle 0x%04x", a->handle); + + if (a->len != 1) + DBG("Invalid control point value size: %zu", a->len); + + switch (a->data[0]) { + case GET_REFERENCE_UPDATE: + DBG("Get Reference Update"); + break; + case CANCEL_REFERENCE_UPDATE: + DBG("Cancel Reference Update"); + break; + default: + DBG("Unknown command: 0x%02x", a->data[0]); + } + + return 0; +} + +static uint8_t time_update_status(struct attribute *a, + struct btd_device *device, + gpointer user_data) +{ + struct btd_adapter *adapter = user_data; + uint8_t value[2]; + + DBG("handle 0x%04x", a->handle); + + value[0] = UPDATE_STATE_IDLE; + value[1] = UPDATE_RESULT_SUCCESSFUL; + attrib_db_update(adapter, a->handle, NULL, value, sizeof(value), NULL); + + return 0; +} + +static gboolean register_ref_time_update_service(struct btd_adapter *adapter) +{ + bt_uuid_t uuid; + + bt_uuid16_create(&uuid, REF_TIME_UPDATE_SVC_UUID); + + /* Reference Time Update service */ + return gatt_service_add(adapter, GATT_PRIM_SVC_UUID, &uuid, + /* Time Update control point */ + GATT_OPT_CHR_UUID16, TIME_UPDATE_CTRL_CHR_UUID, + GATT_OPT_CHR_PROPS, + GATT_CHR_PROP_WRITE_WITHOUT_RESP, + GATT_OPT_CHR_VALUE_CB, ATTRIB_WRITE, + time_update_control, adapter, + + /* Time Update status */ + GATT_OPT_CHR_UUID16, TIME_UPDATE_STAT_CHR_UUID, + GATT_OPT_CHR_PROPS, GATT_CHR_PROP_READ, + GATT_OPT_CHR_VALUE_CB, ATTRIB_READ, + time_update_status, adapter, + + GATT_OPT_INVALID); +} + +static int time_server_init(struct btd_profile *p, struct btd_adapter *adapter) +{ + const char *path = adapter_get_path(adapter); + + DBG("path %s", path); + + if (!register_current_time_service(adapter)) { + error("Current Time Service could not be registered"); + return -EIO; + } + + if (!register_ref_time_update_service(adapter)) { + error("Reference Time Update Service could not be registered"); + return -EIO; + } + + return 0; +} + +static void time_server_exit(struct btd_profile *p, + struct btd_adapter *adapter) +{ + const char *path = adapter_get_path(adapter); + + DBG("path %s", path); +} + +struct btd_profile time_profile = { + .name = "gatt-time-server", + .adapter_probe = time_server_init, + .adapter_remove = time_server_exit, +}; + +static int time_init(void) +{ + return btd_profile_register(&time_profile); +} + +static void time_exit(void) +{ + btd_profile_unregister(&time_profile); +} + +BLUETOOTH_PLUGIN_DEFINE(time, VERSION, + BLUETOOTH_PLUGIN_PRIORITY_DEFAULT, + time_init, time_exit) diff --git a/src/adapter.c b/src/adapter.c index 5d7a9a9..2a3e40b 100755 --- a/src/adapter.c +++ b/src/adapter.c @@ -77,8 +77,20 @@ #include "advertising.h" #include "eir.h" +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#include "adapter_le_vsc_features.h" +#endif + #define ADAPTER_INTERFACE "org.bluez.Adapter1" +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#ifdef TIZEN_FEATURE_BLUEZ_BATTERY_WATCH +#define DEVICED_DEST "org.tizen.system.deviced" +#define DEVICED_BATT_INTERFACE "org.tizen.system.deviced.Battery" +#define DEVICED_BATT_OBJECT_PATH "/Org/Tizen/System/DeviceD/Battery" +#endif /* TIZEN_FEATURE_BLUEZ_BATTERY_WATCH */ +#endif + #define MODE_OFF 0x00 #define MODE_CONNECTABLE 0x01 #define MODE_DISCOVERABLE 0x02 @@ -97,6 +109,16 @@ #define DISTANCE_VAL_INVALID 0x7FFF #define PATHLOSS_MAX 137 +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#define ADV_DATA_MAX_LENGTH 31 +#define SCAN_RESPONSE_DATA_LENGTH_MAX 31 +#define EIR_MANUFACTURER_DATA_LENGTH_MAX 100 + +#define LE_BEARER_POSTFIX " LE" +#define LE_BEARER_POSTFIX_LEN 3 +#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */ + + static DBusConnection *dbus_conn = NULL; static bool kernel_conn_control = false; @@ -181,6 +203,20 @@ struct btd_adapter_pin_cb_iter { /* When the iterator reaches the end, it is NULL and attempt is 0 */ }; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +struct adv_info { + int slot_id; /* Reservied slot id is 0 (Single adv) */ + bool status; /* Advertising status */ +}; + +static GSList *read_requests = NULL; + +struct le_data_length_read_request { + struct btd_adapter *adapter; + DBusMessage *msg; +}; +#endif + struct btd_adapter { int ref_count; @@ -188,6 +224,9 @@ struct btd_adapter { struct mgmt *mgmt; bdaddr_t bdaddr; /* controller Bluetooth address */ +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + bdaddr_t le_static_addr; +#endif uint32_t dev_class; /* controller class of device */ char *name; /* controller device name */ char *short_name; /* controller short name */ @@ -205,6 +244,18 @@ struct btd_adapter { char *current_alias; /* current adapter name alias */ char *stored_alias; /* stored adapter name alias */ +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + uint8_t *local_irk; /* adapter local IRK */ + uint8_t disc_type; + bool ipsp_intialized; /* Ipsp Initialization state */ + struct le_data_length_read_handler *read_handler; + struct le_data_length_read_default_data_length_handler *def_read_handler; +#ifdef TIZEN_FEATURE_BLUEZ_BATTERY_WATCH + guint charging_watch; + guint charging_timeout; + charging_state_e charging; +#endif /* TIZEN_FEATURE_BLUEZ_BATTERY_WATCH */ +#endif bool discovering; /* discovering property state */ bool filtered_discovery; /* we are doing filtered discovery */ @@ -222,6 +273,9 @@ struct btd_adapter { GSList *discovery_found; /* list of found devices */ guint discovery_idle_timeout; /* timeout between discovery runs */ +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + guint le_discovery_idle_timeout; /* timeout between le discovery runs */ +#endif guint passive_scan_timeout; /* timeout between passive scans */ guint temp_devices_timeout; /* timeout for temporary devices */ @@ -239,6 +293,14 @@ struct btd_adapter { struct btd_advertising *adv_manager; gboolean initialized; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + GSList *adv_list; /* List of advertising instance */ + bool advertising; /* Advertising active */ + gchar *version; /* Bluetooth Version */ + uint8_t adv_tx_power; + bool le_discovering; /* LE Discovery active */ + GSList *le_discovery_list; /* list of LE discovery clients */ +#endif GSList *pin_callbacks; GSList *msd_callbacks; @@ -258,10 +320,32 @@ struct btd_adapter { guint pair_device_timeout; unsigned int db_id; /* Service event handler for GATT db */ - +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + uint8_t central_rpa_res_support; + bluetooth_a2dp_role_t a2dp_role; +#ifdef TIZEN_FEATURE_PLATFROM_SCAN_FILTER + bool scan_filter_support; /* platform's scan filtering support */ + uint8_t scan_type; /* scan type */ + GSList *scan_params; /* scan filter parameters */ + GSList *addr_filters; /* adress scan filters list */ + GSList *service_data_changed_filters; /* service data changed scan filters list */ + GSList *service_uuid_filters; /* service uuid scan filters list */ + GSList *solicit_data_filters; /* solicitation data scan filters list */ + GSList *local_name_filters; /* local name scan filters list */ + GSList *manufaturer_data_filters; /* manufacturer data scan filters list */ + GSList *service_data_filters; /* service data scan filters list */ +#endif +#endif bool is_default; /* true if adapter is default one */ }; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +enum { + DEINIT_6LOWPAN, + INIT_6LOWPAN +}; +#endif + static struct btd_adapter *btd_adapter_lookup(uint16_t index) { GList *list; @@ -454,7 +538,13 @@ static void store_adapter_info(struct btd_adapter *adapter) if (adapter->stored_alias) g_key_file_set_string(key_file, "General", "Alias", adapter->stored_alias); - +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + /* Store A2DP Role */ + if (adapter->a2dp_role == BLUETOOTH_A2DP_SINK_ROLE) + g_key_file_set_string(key_file, "General", "DefaultA2DPRole", "sink"); + else + g_key_file_set_string(key_file, "General", "DefaultA2DPRole", "source"); +#endif ba2str(&adapter->bdaddr, address); snprintf(filename, PATH_MAX, STORAGEDIR "/%s/settings", address); @@ -467,12 +557,252 @@ static void store_adapter_info(struct btd_adapter *adapter) g_key_file_free(key_file); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +bluetooth_a2dp_role_t btd_adapter_get_a2dp_role(struct btd_adapter *adapter) +{ + if (!adapter) + return BLUETOOTH_A2DP_SOURCE_ROLE; + + return adapter->a2dp_role; +} + +void btd_adapter_set_a2dp_role(struct btd_adapter *adapter, bluetooth_a2dp_role_t role) +{ + if (!adapter) { + DBG("Could not set a2dp role"); + return; + } + + if (role == BLUETOOTH_A2DP_SOURCE_ROLE) { + DBG("Set audio source role"); + adapter->a2dp_role = BLUETOOTH_A2DP_SOURCE_ROLE; + } else if (role == BLUETOOTH_A2DP_SINK_ROLE) { + DBG("Set audio sink role"); + adapter->a2dp_role = BLUETOOTH_A2DP_SINK_ROLE; + } + + store_adapter_info(adapter); +} +#endif + static void trigger_pairable_timeout(struct btd_adapter *adapter); static void adapter_start(struct btd_adapter *adapter); static void adapter_stop(struct btd_adapter *adapter); static void trigger_passive_scanning(struct btd_adapter *adapter); static bool set_mode(struct btd_adapter *adapter, uint16_t opcode, uint8_t mode); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static bool load_local_irk(struct btd_adapter *adapter); +static bool set_local_irk(struct btd_adapter *adapter); +static bool set_privacy(struct btd_adapter *adapter, bool privacy); +#endif + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#ifdef TIZEN_FEATURE_BLUEZ_BATTERY_WATCH +static gboolean charging_state_timeout_cb(gpointer user_data) +{ + struct btd_adapter *adapter = user_data; + int bredr_pkt_type = ACL_PTYPE_MASK; + + adapter->charging_timeout = 0; + + DBG("Set all connections to BR/EDR type"); + g_slist_foreach(adapter->devices, device_change_pkt_type, + (gpointer)bredr_pkt_type); + + return FALSE; +} + +static void set_charging_state(struct btd_adapter *adapter, + charging_state_e state) +{ + int br_pkt_type = ACL_PTYPE_MASK | + HCI_2DH1 | HCI_2DH3 | HCI_2DH5 | + HCI_3DH1 | HCI_3DH3 | HCI_3DH5; + + if (adapter->charging == state) + return; + + DBG("old charging state : %d, new charging_state : %d", + adapter->charging, state); + + /* + * Only none / wire charging <-> wireless charging state change should + * be handled. + */ + if ((adapter->charging == NONE_CHARGING && state == WIRE_CHARGING) || + (adapter->charging == WIRE_CHARGING && state == NONE_CHARGING)) { + DBG("Just update charging state"); + adapter->charging = state; + return; + } + + if (adapter->charging_timeout) { + g_source_remove(adapter->charging_timeout); + adapter->charging_timeout = 0; + } + + adapter->charging = state; + if (adapter->charging == NONE_CHARGING || + adapter->charging == WIRE_CHARGING) { + DBG("Trigger timeout to set connection to BR/EDR type"); + adapter->charging_timeout = g_timeout_add(2000, + charging_state_timeout_cb, adapter); + } else if (adapter->charging == WIRELESS_CHARGING) { + DBG("Set all connections to BR type"); + g_slist_foreach(adapter->devices, device_change_pkt_type, + (gpointer)br_pkt_type); + } + + return; +} + +static gboolean charging_state_changed(DBusConnection *connection, + DBusMessage *msg, void *user_data) +{ + struct btd_adapter *adapter = user_data; + int state = 0; + + DBG("charging_state_changed"); + + if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_INT32, &state, + DBUS_TYPE_INVALID)) + return TRUE; + + set_charging_state(adapter, state); + + return TRUE; +} + +charging_state_e get_charging_state(struct btd_adapter *adapter) +{ + DBG("charging_state: %d", adapter->charging); + return adapter->charging; +} +#endif /* TIZEN_FEATURE_BLUEZ_BATTERY_WATCH */ + +static int compare_slot(gconstpointer a, gconstpointer b) +{ + const struct adv_info *adv = a; + const int id = *(int*)b; + + return (adv->slot_id == id ? 0 : -1); +} + +static struct adv_info *find_advertiser(struct btd_adapter *adapter, + int slot_id) +{ + GSList *list; + + list = g_slist_find_custom(adapter->adv_list, &slot_id, + compare_slot); + if (list) + return list->data; + + return NULL; +} + +static void create_advertiser(struct btd_adapter *adapter, + int slot_id) +{ + struct adv_info *adv; + + if (!adapter) + return; + + if (find_advertiser(adapter, slot_id) != NULL) { + error("Aleady existed [%d]", slot_id); + return; + } + + DBG("Create adv slot id : %d", slot_id); + + adv = g_new0(struct adv_info, 1); + if (adv == NULL) + return; + + adv->slot_id = slot_id; + + adapter->adv_list= g_slist_append(adapter->adv_list, adv); + return; +} + + +static void advertising_state_changed(struct btd_adapter *adapter, + int slot_id, bool enabled) +{ + struct adv_info *adv; + int id = slot_id; + int state = enabled; + + if (!adapter) + return; + + adv = find_advertiser(adapter, slot_id); + if (!adv) { + DBG("Unable to find advertiser [%d]", slot_id); + return; + } + + adv->status = enabled; + DBG("slot_id %d, status %d", adv->slot_id, adv->status); + + g_dbus_emit_signal(dbus_conn, adapter->path, + ADAPTER_INTERFACE, "AdvertisingEnabled", + DBUS_TYPE_INT32, &id, + DBUS_TYPE_BOOLEAN, &state, + DBUS_TYPE_INVALID); +} + +static void clear_advertiser_cb(gpointer data, gpointer user_data) +{ + struct adv_info *adv = data; + struct btd_adapter *adapter = user_data; + + if (adv->status) + advertising_state_changed(adapter, adv->slot_id, 0); +} + +static void advertiser_cleanup(struct btd_adapter *adapter) +{ + if (!adapter->adv_list) + return; + + g_slist_foreach(adapter->adv_list, clear_advertiser_cb, adapter); + g_slist_free(adapter->adv_list); + adapter->adv_list = NULL; +} +#endif + +#if defined TIZEN_FEATURE_BLUEZ_MODIFY && defined TIZEN_FEATURE_BLUEZ_SPRD_PAGE_SCAN +#define OCF_PAGE_SCAN_TIMEOUT 0x0018 +#define OGF_PAGE_SCAN_TIMEOUT 0x03 + +typedef struct { + uint16_t timeout; /* Value */ +} __attribute__ ((packed)) hci_page_scan_timeout; +#define HCI_PAGE_SCAN_TIMEOUT_CP_SIZE 2 + +static gboolean send_sprd_page_scan_timeout(gint value) +{ + int dd; + hci_page_scan_timeout cp; + DBG("+"); + dd = hci_open_dev(0); + cp.timeout = value; + if (hci_send_cmd(dd, OGF_PAGE_SCAN_TIMEOUT, OCF_PAGE_SCAN_TIMEOUT, + HCI_PAGE_SCAN_TIMEOUT_CP_SIZE, &cp) < 0) { + DBG("Error: While setting Page Timeout value"); + hci_close_dev(dd); + return FALSE; + } + DBG("Page Scan Timeout Value Patch %d", value); + + hci_close_dev(dd); + + return TRUE; +} +#endif static void settings_changed(struct btd_adapter *adapter, uint32_t settings) { @@ -490,6 +820,15 @@ static void settings_changed(struct btd_adapter *adapter, uint32_t settings) if (adapter->current_settings & MGMT_SETTING_POWERED) { adapter_start(adapter); +#if defined TIZEN_FEATURE_BLUEZ_MODIFY && defined TIZEN_FEATURE_BLUEZ_SPRD_PAGE_SCAN + /* Approx 6.4 Seconds of timeout */ + /* This Added because Z3 device was not able to connect with + * some device as it was getting Page Timeout + * (LG HBS800, sony carkit) etc. So, Increasing Page timeout value + * from 5.12 Sec (which is default) to ~6.4sec*/ + DBG("Setting value"); + send_sprd_page_scan_timeout(10240); +#endif /* TIZEN_FEATURE_BLUEZ_SPRD_PAGE_SCAN */ } else { adapter_stop(adapter); @@ -524,6 +863,31 @@ static void settings_changed(struct btd_adapter *adapter, uint32_t settings) trigger_pairable_timeout(adapter); } + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (changed_mask & MGMT_SETTING_ADVERTISING) { + if ((adapter->current_settings & MGMT_SETTING_ADVERTISING) && + (adapter->advertising)) { + return; + } + + adapter->advertising = adapter->current_settings & MGMT_SETTING_ADVERTISING; + advertising_state_changed(adapter, 0, adapter->advertising); + } + + if ((changed_mask & MGMT_SETTING_PRIVACY) && + !(adapter->current_settings & MGMT_SETTING_PRIVACY)) { + DBG("LE Privacy feature is disabled"); + + /* + * Some Android devices don't consider the device as LE one, + * if the device doesn't distribute IRK when pairing. + * Because of this compatibility issue, set IRK + * even though privacy feature is disabled. + */ + set_local_irk(adapter); + } +#endif } static void new_settings_callback(uint16_t index, uint16_t length, @@ -783,6 +1147,10 @@ struct btd_device *btd_adapter_find_device(struct btd_adapter *adapter, struct device_addr_type addr; struct btd_device *device; GSList *list; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + bool exact_match = false; +#endif + char addr_str[18]; if (!adapter) return NULL; @@ -790,13 +1158,33 @@ struct btd_device *btd_adapter_find_device(struct btd_adapter *adapter, bacpy(&addr.bdaddr, dst); addr.bdaddr_type = bdaddr_type; + ba2str(dst, addr_str); + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY list = g_slist_find_custom(adapter->devices, &addr, + device_addr_type_strict_cmp); + if (list) { + device = list->data; + exact_match = true; + } else { +#endif + list = g_slist_find_custom(adapter->devices, &addr, device_addr_type_cmp); + if (list) { + device = list->data; + } + } + if (!list) return NULL; device = list->data; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (exact_match) + return device; +#endif + /* * If we're looking up based on public address and the address * was not previously used over this bearer we may need to @@ -1045,6 +1433,13 @@ static void adapter_service_insert(struct btd_adapter *adapter, sdp_record_t *re DBG("%s", adapter->path); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (rec == NULL) { + DBG("record is NULL return"); + return; + } +#endif + /* skip record without a browse group */ if (sdp_get_browse_groups(rec, &browse_list) < 0) { DBG("skipping record without browse group"); @@ -1106,6 +1501,18 @@ void adapter_service_remove(struct btd_adapter *adapter, uint32_t handle) remove_record_from_server(rec->handle); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static void adapter_print_devices(struct btd_adapter *adapter) +{ + GSList *dev; + + dev = adapter->devices; + for (; dev; dev = dev->next) { + device_print_addr(dev->data); + } +} +#endif + static struct btd_device *adapter_create_device(struct btd_adapter *adapter, const bdaddr_t *bdaddr, uint8_t bdaddr_type) @@ -1118,6 +1525,10 @@ static struct btd_device *adapter_create_device(struct btd_adapter *adapter, adapter->devices = g_slist_append(adapter->devices, device); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + adapter_print_devices(adapter); +#endif + return device; } @@ -1144,6 +1555,46 @@ static void service_auth_cancel(struct service_auth *auth) g_free(auth); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +void btd_adapter_unpair_device(struct btd_adapter *adapter, + struct btd_device *dev) +{ + DBG("+"); + GList *l; + + adapter->connect_list = g_slist_remove(adapter->connect_list, dev); + +// adapter->devices = g_slist_remove(adapter->devices, dev); +// +// adapter->discovery_found = g_slist_remove(adapter->discovery_found, +// dev); + + adapter->connections = g_slist_remove(adapter->connections, dev); + + if (adapter->connect_le == dev) + adapter->connect_le = NULL; + + l = adapter->auths->head; + while (l != NULL) { + struct service_auth *auth = l->data; + GList *next = g_list_next(l); + + if (auth->device != dev) { + l = next; + continue; + } + + g_queue_delete_link(adapter->auths, l); + l = next; + + service_auth_cancel(auth); + } + + device_unpair(dev, TRUE); + DBG("-"); +} +#endif + void btd_adapter_remove_device(struct btd_adapter *adapter, struct btd_device *dev) { @@ -1189,6 +1640,11 @@ struct btd_device *btd_adapter_get_device(struct btd_adapter *adapter, if (!adapter) return NULL; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (!bacmp(addr, BDADDR_ANY)) + return NULL; +#endif + device = btd_adapter_find_device(adapter, addr, addr_type); if (device) return device; @@ -1207,6 +1663,11 @@ static void passive_scanning_complete(uint8_t status, uint16_t length, struct btd_adapter *adapter = user_data; const struct mgmt_cp_start_discovery *rp = param; + if (!rp) { + error("Error ocurred in Scanning, rp is NULL"); + return; + } + DBG("status 0x%02x", status); if (length < sizeof(*rp)) { @@ -1229,11 +1690,15 @@ static gboolean passive_scanning_timeout(gpointer user_data) adapter->passive_scan_timeout = 0; cp.type = SCAN_TYPE_LE; - +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + mgmt_send(adapter->mgmt, MGMT_OP_START_LE_DISCOVERY, + adapter->dev_id, sizeof(cp), &cp, + passive_scanning_complete, adapter, NULL); +#else mgmt_send(adapter->mgmt, MGMT_OP_START_DISCOVERY, adapter->dev_id, sizeof(cp), &cp, passive_scanning_complete, adapter, NULL); - +#endif return FALSE; } @@ -1264,8 +1729,13 @@ static void trigger_passive_scanning(struct btd_adapter *adapter) * The discovery procedure is using interleaved scanning and * thus will discover Low Energy devices as well. */ +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (adapter->discovery_list || adapter->le_discovery_list) + return; +#else if (adapter->discovery_list) return; +#endif if (adapter->discovery_enable == 0x01) return; @@ -1339,22 +1809,45 @@ static void stop_passive_scanning_complete(uint8_t status, uint16_t length, static void stop_passive_scanning(struct btd_adapter *adapter) { struct mgmt_cp_stop_discovery cp; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + struct mgmt_cp_stop_discovery le_cp; +#endif DBG(""); /* If there are any normal discovery clients passive scanning * wont be running */ +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (adapter->discovery_list || adapter->le_discovery_list) + return; +#else if (adapter->discovery_list) return; +#endif if (adapter->discovery_enable == 0x00) return; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if ((adapter->discovery_type & 0x01) > 0) { + cp.type = 0x01; + mgmt_send(adapter->mgmt, MGMT_OP_STOP_DISCOVERY, + adapter->dev_id, sizeof(cp), &cp, + stop_passive_scanning_complete, adapter, NULL); + } + if ((adapter->discovery_type & 0x06) > 0) { + le_cp.type = 0x06; + mgmt_send(adapter->mgmt, MGMT_OP_STOP_LE_DISCOVERY, + adapter->dev_id, sizeof(le_cp), &le_cp, + stop_passive_scanning_complete, adapter, NULL); + } +#else cp.type = adapter->discovery_type; mgmt_send(adapter->mgmt, MGMT_OP_STOP_DISCOVERY, adapter->dev_id, sizeof(cp), &cp, stop_passive_scanning_complete, adapter, NULL); +#endif } static void cancel_passive_scanning(struct btd_adapter *adapter) @@ -1403,6 +1896,9 @@ static void start_discovery_complete(uint8_t status, uint16_t length, const struct mgmt_cp_start_discovery *rp = param; DBG("status 0x%02x", status); +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY + DBG("Discovery Type 0x%02x", rp->type); +#endif if (length < sizeof(*rp)) { btd_error(adapter->dev_id, @@ -1411,7 +1907,12 @@ static void start_discovery_complete(uint8_t status, uint16_t length, } if (status == MGMT_STATUS_SUCCESS) { +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + DBG("Return param discovery type 0x%02x", rp->type); + adapter->discovery_type |= rp->type; +#else adapter->discovery_type = rp->type; +#endif adapter->discovery_enable = 0x01; if (adapter->current_discovery_filter) @@ -1426,30 +1927,150 @@ static void start_discovery_complete(uint8_t status, uint16_t length, g_dbus_emit_property_changed(dbus_conn, adapter->path, ADAPTER_INTERFACE, "Discovering"); return; + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + } else { + adapter->discovering = false; + g_dbus_emit_property_changed(dbus_conn, adapter->path, + ADAPTER_INTERFACE, "Discovering"); +#endif + } /* * In case the restart of the discovery failed, then just trigger * it for the next idle timeout again. */ +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY trigger_start_discovery(adapter, IDLE_DISCOV_TIMEOUT * 2); +#endif } -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); @@ -1516,9 +2137,38 @@ static gboolean start_discovery_timeout(gpointer user_data) adapter->dev_id, sizeof(*sd_cp) + sd_cp->uuid_count * 16, sd_cp, start_discovery_complete, adapter, NULL); +#endif + return FALSE; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static void trigger_start_le_discovery(struct btd_adapter *adapter, guint delay) +{ + + DBG(""); + + cancel_passive_scanning(adapter); + + if (adapter->le_discovery_idle_timeout > 0) { + g_source_remove(adapter->le_discovery_idle_timeout); + adapter->le_discovery_idle_timeout = 0; + } + + /* + * If the controller got powered down in between, then ensure + * that we do not keep trying to restart discovery. + * + * This is safe-guard and should actually never trigger. + */ + if (!(adapter->current_settings & MGMT_SETTING_POWERED)) + return; + + adapter->le_discovery_idle_timeout = g_timeout_add_seconds(delay, + start_le_discovery_timeout, adapter); +} +#endif + static void trigger_start_discovery(struct btd_adapter *adapter, guint delay) { @@ -1544,6 +2194,7 @@ static void trigger_start_discovery(struct btd_adapter *adapter, guint delay) start_discovery_timeout, adapter); } +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY static void suspend_discovery_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { @@ -1614,6 +2265,7 @@ static void resume_discovery(struct btd_adapter *adapter) */ trigger_start_discovery(adapter, IDLE_DISCOV_TIMEOUT); } +#endif static void discovering_callback(uint16_t index, uint16_t length, const void *param, void *user_data) @@ -1629,11 +2281,40 @@ static void discovering_callback(uint16_t index, uint16_t length, DBG("hci%u type %u discovering %u method %d", adapter->dev_id, ev->type, ev->discovering, adapter->filtered_discovery); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + DBG("info discov_type %d", adapter->discovery_type); + if (ev->type == SCAN_TYPE_BREDR) { + if (ev->discovering == FALSE) { + hci_clear_bit(BDADDR_BREDR, &adapter->discovery_type); + adapter->discovering = false; + } else { + hci_set_bit(BDADDR_BREDR, &adapter->discovery_type); + adapter->discovering = true; + } + g_dbus_emit_property_changed(dbus_conn, adapter->path, + ADAPTER_INTERFACE, "Discovering"); + + } else if (ev->type == SCAN_TYPE_LE) { + if (ev->discovering == FALSE) { + hci_clear_bit(BDADDR_LE_PUBLIC, &adapter->discovery_type); + hci_clear_bit(BDADDR_LE_RANDOM, &adapter->discovery_type); + adapter->le_discovering = false; + } else { + hci_set_bit(BDADDR_LE_PUBLIC, &adapter->discovery_type); + hci_set_bit(BDADDR_LE_RANDOM, &adapter->discovery_type); + adapter->le_discovering = true; + } + + g_dbus_emit_property_changed(dbus_conn, adapter->path, + ADAPTER_INTERFACE, "LEDiscovering"); + } +#else if (adapter->discovery_enable == ev->discovering) return; adapter->discovery_type = ev->type; adapter->discovery_enable = ev->discovering; +#endif /* * Check for existing discoveries triggered by client applications @@ -1642,11 +2323,19 @@ static void discovering_callback(uint16_t index, uint16_t length, * If there are no clients, then it is good idea to trigger a * passive scanning attempt. */ +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (adapter->discovery_list == NULL && adapter->le_discovery_list == NULL) { + if (!adapter->connect_le) + trigger_passive_scanning(adapter); + return; + } +#else if (!adapter->discovery_list) { if (!adapter->connect_le) trigger_passive_scanning(adapter); return; } +#endif if (adapter->discovery_suspended) return; @@ -1669,6 +2358,57 @@ static void discovering_callback(uint16_t index, uint16_t length, } } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static void stop_discovery_complete(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + struct btd_adapter *adapter = user_data; + + DBG("status 0x%02x", status); + + if (status == MGMT_STATUS_SUCCESS) { + adapter->discovery_type &= (~0x01); + DBG("Discovery Type 0x%02x", adapter->discovery_type); + + adapter->filtered_discovery = false; + adapter->no_scan_restart_delay = false; + adapter->discovering = false; + g_dbus_emit_property_changed(dbus_conn, adapter->path, + ADAPTER_INTERFACE, "Discovering"); + + if (adapter->discovery_list == NULL && adapter->le_discovery_list == NULL) { + adapter->discovery_enable = 0x00; + trigger_passive_scanning(adapter); + } + } +} + +static void stop_le_discovery_complete(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + struct btd_adapter *adapter = user_data; + + DBG("status 0x%02x", status); + + if (status == MGMT_STATUS_SUCCESS) { + adapter->discovery_type &= (~0x06); + DBG("Discovery Type 0x%02x", adapter->discovery_type); + + adapter->filtered_discovery = false; + adapter->no_scan_restart_delay = false; + adapter->le_discovering = false; + g_dbus_emit_property_changed(dbus_conn, adapter->path, + ADAPTER_INTERFACE, "LEDiscovering"); + + if (adapter->discovery_list == NULL && adapter->le_discovery_list == NULL) { + adapter->discovery_enable = 0x00; + trigger_passive_scanning(adapter); + } + } +} + +#else + static void stop_discovery_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { @@ -1688,6 +2428,7 @@ static void stop_discovery_complete(uint8_t status, uint16_t length, trigger_passive_scanning(adapter); } } +#endif static int compare_sender(gconstpointer a, gconstpointer b) { @@ -1969,7 +2710,11 @@ static void discovery_destroy(void *user_data) if (adapter->discovery_list) return; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + hci_clear_bit(BDADDR_BREDR, &adapter->discovery_type); +#else adapter->discovery_type = 0x00; +#endif if (adapter->discovery_idle_timeout > 0) { g_source_remove(adapter->discovery_idle_timeout); @@ -2059,80 +2804,2876 @@ static bool get_discovery_client(struct btd_adapter *adapter, 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_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 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; @@ -2187,7 +5728,9 @@ static bool parse_transport(DBusMessageIter *value, uint8_t *transport) *transport = SCAN_TYPE_BREDR; else if (!strcmp(transport_str, "le")) *transport = SCAN_TYPE_LE; - else if (strcmp(transport_str, "auto")) + else if (!strcmp(transport_str, "auto")) + *transport = SCAN_TYPE_DUAL; + else return false; return true; @@ -2368,6 +5911,9 @@ static DBusMessage *stop_discovery(DBusConnection *conn, client = list->data; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + adapter->disc_type = BT_DISC_TYPE_BREDR_ONLY; +#endif cp.type = adapter->discovery_type; /* @@ -2394,7 +5940,9 @@ static DBusMessage *stop_discovery(DBusConnection *conn, return dbus_message_new_method_return(msg); } - +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + cp.type = 0x01; +#endif mgmt_send(adapter->mgmt, MGMT_OP_STOP_DISCOVERY, adapter->dev_id, sizeof(cp), &cp, stop_discovery_complete, adapter, NULL); @@ -2416,6 +5964,38 @@ static gboolean property_get_address(const GDBusPropertyTable *property, return TRUE; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static gboolean property_get_le_address(const GDBusPropertyTable *property, + DBusMessageIter *iter, void *user_data) +{ + struct btd_adapter *adapter = user_data; + DBusMessageIter entry; + char addr[18]; + const char *str = addr; + char *type = NULL; + + dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, + DBUS_TYPE_STRING_AS_STRING, &entry); + + if (adapter->le_static_addr.b[5] != 0) { + ba2str(&adapter->le_static_addr, addr); + type = g_strdup_printf("%d", BDADDR_LE_RANDOM); + } else { + ba2str(&adapter->bdaddr, addr); + type = g_strdup_printf("%d", BDADDR_LE_PUBLIC); + } + + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &type); + g_free((void *)type); + + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &str); + + dbus_message_iter_close_container(iter, &entry); + + return TRUE; +} +#endif + static gboolean property_get_name(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { @@ -2592,6 +6172,7 @@ static void property_set_mode(struct btd_adapter *adapter, uint32_t setting, len = sizeof(mode); break; case MGMT_SETTING_DISCOVERABLE: +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY if (kernel_conn_control) { if (mode) { set_mode(adapter, MGMT_OP_SET_CONNECTABLE, @@ -2603,6 +6184,7 @@ static void property_set_mode(struct btd_adapter *adapter, uint32_t setting, break; } } +#endif memset(&cp, 0, sizeof(cp)); cp.val = mode; @@ -2618,6 +6200,13 @@ static void property_set_mode(struct btd_adapter *adapter, uint32_t setting, param = &mode; len = sizeof(mode); break; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + case MGMT_SETTING_CONNECTABLE: + opcode = MGMT_OP_SET_CONNECTABLE; + param = &mode; + len = sizeof(mode); + break; +#endif default: goto failed; } @@ -2632,8 +6221,16 @@ static void property_set_mode(struct btd_adapter *adapter, uint32_t setting, data->adapter = adapter; data->id = id; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + /* + * Use mgmt_send_nowait to avoid dbus timeout in a state of bonding. + */ + if (mgmt_send_nowait(adapter->mgmt, opcode, adapter->dev_id, len, param, + property_set_mode_complete, data, g_free) > 0) +#else if (mgmt_send(adapter->mgmt, opcode, adapter->dev_id, len, param, property_set_mode_complete, data, g_free) > 0) +#endif return; g_free(data); @@ -2815,6 +6412,122 @@ static void iter_append_uuid(gpointer key, gpointer value, gpointer user_data) dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &uuid); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static gboolean property_get_le_discovering(const GDBusPropertyTable *property, + DBusMessageIter *iter, void *user_data) +{ + struct btd_adapter *adapter = user_data; + dbus_bool_t discovering = adapter->le_discovering; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &discovering); + + return TRUE; +} + +static gboolean property_get_connectable(const GDBusPropertyTable *property, + DBusMessageIter *iter, void *user_data) +{ + struct btd_adapter *adapter = user_data; + + return property_get_mode(adapter, MGMT_SETTING_CONNECTABLE, iter); +} + +static void property_set_connectable(const GDBusPropertyTable *property, + DBusMessageIter *iter, + GDBusPendingPropertySet id, void *user_data) +{ + struct btd_adapter *adapter = user_data; + + property_set_mode(adapter, MGMT_SETTING_CONNECTABLE, iter, id); +} + +static gboolean property_get_version(const GDBusPropertyTable *property, + DBusMessageIter *iter, void *user_data) +{ + struct btd_adapter *adapter = user_data; + const char *str = adapter->version ? : ""; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &str); + + return TRUE; +} + +static gboolean property_get_supported_le_features( + const GDBusPropertyTable *property, + DBusMessageIter *iter, void *user_data) +{ + const char *str, *val; + int value; + DBusMessageIter entry; + + dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, + DBUS_TYPE_STRING_AS_STRING, &entry); + + value = adapter_le_get_max_adv_instance(); + if (value > 0) { + str = g_strdup("adv_inst_max"); + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &str); + + val = g_strdup_printf("%d", value); + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &val); + + g_free((void *)str); + g_free((void *)val); + } + + value = adapter_le_is_supported_offloading(); + if (value > 0) { + str = g_strdup("rpa_offloading"); + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &str); + + val = g_strdup_printf("%d", value); + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &val); + + g_free((void *)str); + g_free((void *)val); + } + + value = adapter_le_get_scan_filter_size(); +#ifdef TIZEN_FEATURE_PLATFROM_SCAN_FILTER + if (value <= 0) + value = SCAN_FILTER_SLOTS_MAX; +#endif + if (value > 0) { + str = g_strdup("max_filter"); + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &str); + + val = g_strdup_printf("%d", value); + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &val); + + g_free((void *)str); + g_free((void *)val); + } + + dbus_message_iter_close_container(iter, &entry); + + return TRUE; +} + +static gboolean property_get_ipsp_init_state( + const GDBusPropertyTable *property, + DBusMessageIter *iter, void *data) +{ + struct btd_adapter *adapter = data; + dbus_bool_t ipsp_initialized; + + DBG("property_get_ipsp_init_state called"); + if (adapter->ipsp_intialized) + ipsp_initialized = TRUE; + else + ipsp_initialized = FALSE; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, + &ipsp_initialized); + + return TRUE; +} +#endif + static gboolean property_get_uuids(const GDBusPropertyTable *property, DBusMessageIter *iter, void *user_data) { @@ -2837,51 +6550,294 @@ static gboolean property_get_uuids(const GDBusPropertyTable *property, 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) @@ -2922,10 +6878,157 @@ static const GDBusMethodTable adapter_methods[] = { GDBUS_ARGS({ "properties", "a{sv}" }), NULL, set_discovery_filter) }, { GDBUS_METHOD("StopDiscovery", NULL, NULL, stop_discovery) }, +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + { GDBUS_METHOD("StartCustomDiscovery", + GDBUS_ARGS({ "type", "s" }), NULL, + adapter_start_custom_discovery) }, + { GDBUS_METHOD("StartLEDiscovery", NULL, NULL, + adapter_start_le_discovery) }, + { GDBUS_ASYNC_METHOD("StopLEDiscovery", NULL, NULL, + adapter_stop_le_discovery) }, + { GDBUS_METHOD("SetAdvertising", + GDBUS_ARGS({ "enable", "b" }, + { "slot_id", "i" }), NULL, + adapter_set_advertising) }, + { GDBUS_METHOD("SetAdvertisingParameters", + GDBUS_ARGS({ "interval_min", "u" }, + { "interval_max", "u" }, + { "filter_policy", "u" }, + { "type", "u" }, + { "slot_id", "i" }), NULL, + adapter_set_advertising_params) }, + { GDBUS_METHOD("SetAdvertisingData", + GDBUS_ARGS({ "value", "ay" }, + { "slot_id", "i" }), NULL, + adapter_set_advertising_data) }, + { GDBUS_METHOD("SetScanParameters", + GDBUS_ARGS({ "type", "u" }, + { "interval", "u" }, + { "window", "u" }), NULL, + adapter_le_set_scan_params) }, + { GDBUS_ASYNC_METHOD("scan_filter_param_setup", + GDBUS_ARGS({ "client_if", "i" }, { "action", "i" }, + { "filt_index", "i" }, { "feat_seln", "i"}, + { "list_logic_type", "i" }, { "filt_logic_type", "i"}, + { "rssi_high_thres", "i" }, { "rssi_low_thres", "i"}, + { "dely_mode", "i" }, { "found_timeout", "i"}, + { "lost_timeout", "i" }, { "found_timeout_cnt", "i"}), NULL, + adapter_le_scan_filter_param_setup) }, + { GDBUS_ASYNC_METHOD("scan_filter_add_remove", + GDBUS_ARGS({ "client_if", "i" }, { "action", "i" }, + { "filt_type", "i" }, { "filt_index", "i"}, + { "company_id", "i" }, { "company_id_mask", "i"}, + { "p_uuid", "ay" }, { "p_uuid_mask", "ay" }, + { "string", "s" }, { "address_type", "u" }, + /*{ "data_len", "i" },*/ { "p_data", "ay" }, + /*{ "mask_len", "i" },*/ { "p_mask", "ay" }), NULL, + adapter_le_scan_filter_add_remove) }, + { GDBUS_ASYNC_METHOD("scan_filter_clear", + GDBUS_ARGS({ "client_if", "i" }, { "filt_index", "i" }), NULL, + adapter_le_scan_filter_clear) }, + { GDBUS_ASYNC_METHOD("scan_filter_enable", + GDBUS_ARGS({ "client_if", "i" }, { "enable", "b" }), NULL, + adapter_le_scan_filter_enable) }, + { GDBUS_METHOD("InitializeIpsp", + NULL, NULL, + adapter_initialize_ipsp) }, + { GDBUS_METHOD("DeinitializeIpsp", + NULL, NULL, + adapter_deinitialize_ipsp) }, + { GDBUS_METHOD("SetScanRespData", + GDBUS_ARGS({ "value", "ay" }, + { "slot_id", "i" }), NULL, + adapter_set_scan_rsp_data) }, + { GDBUS_METHOD("AddDeviceWhiteList", + GDBUS_ARGS({ "address", "s" }, + { "address_type", "u" }), NULL, + adapter_add_device_white_list) }, + { GDBUS_METHOD("RemoveDeviceWhiteList", + GDBUS_ARGS({ "address", "s" }, + { "address_type", "u" }), NULL, + adapter_remove_device_white_list) }, + { GDBUS_METHOD("ClearDeviceWhiteList", + NULL, NULL, + adapter_clear_device_white_list) }, + { GDBUS_METHOD("SetLePrivacy", + GDBUS_ARGS({ "enable", "b" }), NULL, + adapter_set_le_privacy) }, + { GDBUS_METHOD("SetLeStaticRandomAddress", + GDBUS_ARGS({ "enable", "b" }), NULL, + adapter_set_le_static_address) }, + { GDBUS_ASYNC_METHOD("EnableRssi", + GDBUS_ARGS({ "bt_address", "s" }, + { "link_type", "i" }, + { "low_th", "i" }, + { "in_range_th", "i" }, + { "high_th", "i"}), + NULL, + adapter_enable_rssi) }, + { GDBUS_ASYNC_METHOD("GetRssiStrength", + GDBUS_ARGS({ "bt_address", "s" }, { "link_type", "i" }), + NULL, + adapter_get_rssi) }, + { GDBUS_ASYNC_METHOD("UnpairDevice", + GDBUS_ARGS({ "device", "o" }), NULL, adapter_unpair_device) }, + { GDBUS_METHOD("FindDevice", + GDBUS_ARGS({ "address", "s" }), + GDBUS_ARGS({ "device", "o" }), + find_device) }, + { GDBUS_METHOD("SetWbsParameters", + GDBUS_ARGS({ "role", "s" }, { "bt_address", "s" }), + NULL, + set_wbs_parameters) }, + { GDBUS_METHOD("SetNbParameters", + GDBUS_ARGS({ "role", "s" }, { "bt_address", "s" }), + NULL, + set_nb_parameters) }, + { GDBUS_METHOD("SetManufacturerData", + GDBUS_ARGS({ "value", "ay" }), NULL, + adapter_set_manufacturer_data) }, + { GDBUS_ASYNC_METHOD("CreateDevice", + GDBUS_ARGS({ "address", "s" }), NULL, + create_device) }, +#endif { GDBUS_ASYNC_METHOD("RemoveDevice", GDBUS_ARGS({ "device", "o" }), NULL, remove_device) }, +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + { GDBUS_ASYNC_METHOD("LEReadMaximumDataLength", NULL, + GDBUS_ARGS({"maxTxOctets", "q" }, { "maxTxTime", "q" }, + {"maxRxOctets", "q" }, { "maxRxTime", "q" }), + le_read_maximum_data_length)}, + { GDBUS_ASYNC_METHOD("LEWriteHostSuggestedDataLength", + GDBUS_ARGS({"def_tx_octets", "q" }, { "def_tx_time", "q" }), NULL, + le_write_host_suggested_default_data_length)}, + { GDBUS_ASYNC_METHOD("LEReadHostSuggestedDataLength", NULL, + GDBUS_ARGS({"def_tx_octets", "q" }, { "def_tx_time", "q" }), + le_read_host_suggested_default_data_length)}, +#endif + { } +}; + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static const GDBusSignalTable adapter_signals[] = { + { GDBUS_SIGNAL("AdvertisingEnabled", + GDBUS_ARGS({ "slot_id", "i" }, + { "enabled", "b"})) }, + { GDBUS_SIGNAL("RssiEnabled", + GDBUS_ARGS({"address","s"}, + { "link_type", "i" }, + { "enabled", "b"})) }, + { GDBUS_SIGNAL("RssiAlert", + GDBUS_ARGS({"address","s"}, + { "link_type", "i" }, + { "alert_type", "i" }, + { "rssi_dbm", "i"})) }, + { GDBUS_SIGNAL("RawRssi", + GDBUS_ARGS({"address","s"}, + { "link_type", "i" }, + { "rssi_dbm", "i"})) }, + { GDBUS_SIGNAL("HardwareError", NULL) }, + { GDBUS_SIGNAL("TxTimeoutError", NULL) }, { } }; +#endif static const GDBusPropertyTable adapter_properties[] = { { "Address", "s", property_get_address }, @@ -2941,9 +7044,21 @@ static const GDBusPropertyTable adapter_properties[] = { { "PairableTimeout", "u", property_get_pairable_timeout, property_set_pairable_timeout }, { "Discovering", "b", property_get_discovering }, +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + { "LEDiscovering", "b", property_get_le_discovering }, +#endif { "UUIDs", "as", property_get_uuids }, { "Modalias", "s", property_get_modalias, NULL, property_exists_modalias }, +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + { "Connectable", "b", property_get_connectable, + property_set_connectable }, + { "Version", "s", property_get_version }, + { "SupportedLEFeatures", "as", property_get_supported_le_features}, + { "IpspInitStateChanged", "b", property_get_ipsp_init_state}, + { "LEAddress", "as", property_get_le_address }, +#endif + { } }; @@ -3094,8 +7209,17 @@ static struct irk_info *get_irk_info(GKeyFile *key_file, const char *peer, char *str; str = g_key_file_get_string(key_file, "IdentityResolvingKey", "Key", NULL); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (!str) + return NULL; + if (strlen(str) < 32) { + g_free(str); + return NULL; + } +#else if (!str || strlen(str) < 32) goto failed; +#endif irk = g_new0(struct irk_info, 1); @@ -3106,8 +7230,9 @@ static struct irk_info *get_irk_info(GKeyFile *key_file, const char *peer, str2buf(&str[2], irk->val, sizeof(irk->val)); else str2buf(&str[0], irk->val, sizeof(irk->val)); - +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY failed: +#endif g_free(str); return irk; @@ -3216,6 +7341,7 @@ static int load_irk(struct btd_adapter *adapter, uint8_t *irk) return 0; } +#if 0 static void set_privacy_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { @@ -3259,6 +7385,7 @@ static int set_privacy(struct btd_adapter *adapter, uint8_t privacy) return -1; } +#endif static void load_link_keys_complete(uint8_t status, uint16_t length, const void *param, void *user_data) @@ -3611,6 +7738,47 @@ static uint8_t get_le_addr_type(GKeyFile *keyfile) return addr_type; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static uint8_t get_addr_type(GKeyFile *keyfile) +{ + char **techno, **t; + char *str; + uint8_t bdaddr_type = BDADDR_BREDR; + bool le = false; + + /* Load device technology */ + techno = g_key_file_get_string_list(keyfile, "General", + "SupportedTechnologies", NULL, NULL); + if (!techno) + return 0xff; + + for (t = techno; *t; t++) { + if (g_str_equal(*t, "LE")) + le = true; + } + + if (!le) { + bdaddr_type = BDADDR_BREDR; + } else { + str = g_key_file_get_string(keyfile, "General", + "AddressType", NULL); + + if (str && g_str_equal(str, "public")) + bdaddr_type = BDADDR_LE_PUBLIC; + else if (str && g_str_equal(str, "static")) + bdaddr_type = BDADDR_LE_RANDOM; + else + error("Unknown LE device technology"); + + g_free(str); + } + + g_strfreev(techno); + + return bdaddr_type; +} +#endif + static void probe_devices(void *user_data) { struct btd_device *device = user_data; @@ -3646,8 +7814,14 @@ static void load_devices(struct btd_adapter *adapter) struct btd_device *device; char filename[PATH_MAX]; GKeyFile *key_file; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + struct link_key_info *key_info = NULL; + GSList *list, *ltk_info = NULL; + struct device_addr_type addr; +#else struct link_key_info *key_info; GSList *list, *ltk_info; +#endif struct irk_info *irk_info; struct conn_param *param; uint8_t bdaddr_type; @@ -3658,6 +7832,18 @@ static void load_devices(struct btd_adapter *adapter) if (entry->d_type != DT_DIR || bachk(entry->d_name) < 0) continue; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +{ + bdaddr_t bdaddr; + + str2ba(entry->d_name, &bdaddr); + + if (!bacmp(&bdaddr, BDADDR_ANY)) { + error("No Bluetooth address"); + continue; + } +} +#endif snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info", srcaddr, entry->d_name); @@ -3681,9 +7867,25 @@ static void load_devices(struct btd_adapter *adapter) if (param) params = g_slist_append(params, param); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + str2ba(entry->d_name, &addr.bdaddr); + addr.bdaddr_type = get_addr_type(key_file); + if (addr.bdaddr_type == 0xff) { + error("No SupportedTechnologies. Skipping"); + goto free; + } + + list = g_slist_find_custom(adapter->devices, &addr, + device_addr_type_strict_cmp); +#else list = g_slist_find_custom(adapter->devices, entry->d_name, device_address_cmp); +#endif if (list) { +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + DBG("Skip already loaded device [%s] [%d]", + entry->d_name, addr.bdaddr_type); +#endif device = list->data; goto device_exist; } @@ -3693,6 +7895,42 @@ static void load_devices(struct btd_adapter *adapter) if (!device) goto free; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +{ + char idaddr[18]; + + /* + * After loading IRK information from file, + * store it into device->bdaddr. + * RPA is stored in device->rpa_addr + */ + ba2str(device_get_address(device), idaddr); + + DBG("irk address: %s, rpa_exist %d", + idaddr, device_get_rpa_exist(device)); + + if (device_get_rpa_exist(device) == true) { + if (key_info) + str2ba(idaddr, &key_info->bdaddr); + + if (ltk_info) { + ltks = g_slist_remove(ltks, ltk_info); + ltk_info = get_ltk_info(key_file, + idaddr, bdaddr_type); + ltks = g_slist_concat(ltks, ltk_info); + } + + if (irk_info) { + str2ba(idaddr, &irk_info->bdaddr); + device_set_irk_value(device, irk_info->val); + } + + if (param) + str2ba(idaddr, ¶m->bdaddr); + } +} +#endif + btd_device_set_temporary(device, false); adapter->devices = g_slist_append(adapter->devices, device); @@ -4276,8 +8514,30 @@ void adapter_auto_connect_remove(struct btd_adapter *adapter, static void adapter_start(struct btd_adapter *adapter) { +#if defined(TIZEN_FEATURE_BLUEZ_MODIFY) && !defined(__SPRD_PATCH__) + if (adapter_le_read_ble_feature_info()) + g_dbus_emit_property_changed(dbus_conn, adapter->path, + ADAPTER_INTERFACE, "SupportedLEFeatures"); +#ifdef TIZEN_FEATURE_PLATFROM_SCAN_FILTER + else + g_dbus_emit_property_changed(dbus_conn, adapter->path, + ADAPTER_INTERFACE, "SupportedLEFeatures"); +#endif + + adapter_get_adv_tx_power(adapter); + + /* By default enable offloading for testing, this should be modified */ + if (adapter_le_is_supported_offloading()) + adapter_le_enable_offloading(TRUE); +#endif + +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY g_dbus_emit_property_changed(dbus_conn, adapter->path, ADAPTER_INTERFACE, "Powered"); +#else + g_dbus_emit_property_changed_full(dbus_conn, adapter->path, + ADAPTER_INTERFACE, "Powered", 1); +#endif DBG("adapter %s has been enabled", adapter->path); @@ -5234,7 +9494,9 @@ static void load_config(struct btd_adapter *adapter) char address[18]; struct stat st; GError *gerr = NULL; - +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + char *str; +#endif ba2str(&adapter->bdaddr, address); key_file = g_key_file_new(); @@ -5284,12 +9546,35 @@ static void load_config(struct btd_adapter *adapter) gerr = NULL; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + /* Get A2DP Role */ + str = g_key_file_get_string(key_file, "General", "DefaultA2DPRole", &gerr); + if (gerr || !str) { + adapter->a2dp_role = BLUETOOTH_A2DP_SOURCE_ROLE; + g_error_free(gerr); + gerr = NULL; + } else { + if (g_strcmp0(str, "sink") == 0) + adapter->a2dp_role = BLUETOOTH_A2DP_SINK_ROLE; + else if (g_strcmp0(str, "source") == 0) + adapter->a2dp_role = BLUETOOTH_A2DP_SOURCE_ROLE; + } +#endif + g_key_file_free(key_file); } static struct btd_adapter *btd_adapter_new(uint16_t index) { struct btd_adapter *adapter; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#ifdef TIZEN_FEATURE_BLUEZ_BATTERY_WATCH + DBusConnection *conn = btd_get_dbus_connection(); + DBusMessage *msg = NULL; + DBusMessage *reply = NULL; + int charging_state = 0; +#endif /* TIZEN_FEATURE_BLUEZ_BATTERY_WATCH */ +#endif adapter = g_try_new0(struct btd_adapter, 1); if (!adapter) @@ -5316,6 +9601,9 @@ static struct btd_adapter *btd_adapter_new(uint16_t index) main_opts.did_version); adapter->discoverable_timeout = main_opts.discovto; adapter->pairable_timeout = main_opts.pairto; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + adapter->advertising = FALSE; +#endif DBG("System name: %s", adapter->system_name); DBG("Major class: %u", adapter->major_class); @@ -5323,19 +9611,67 @@ static struct btd_adapter *btd_adapter_new(uint16_t index) DBG("Modalias: %s", adapter->modalias); DBG("Discoverable timeout: %u seconds", adapter->discoverable_timeout); DBG("Pairable timeout: %u seconds", adapter->pairable_timeout); - adapter->auths = g_queue_new(); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#ifdef TIZEN_FEATURE_BLUEZ_BATTERY_WATCH + adapter->charging_watch = g_dbus_add_signal_watch(conn, DEVICED_DEST, + DEVICED_BATT_OBJECT_PATH, + DEVICED_BATT_INTERFACE, "ChargerType", + charging_state_changed, adapter, NULL); + if (adapter->charging_watch == 0) + error("Cannot add signal watch for ChargerType"); + + msg = dbus_message_new_method_call(DEVICED_DEST, + DEVICED_BATT_OBJECT_PATH, + DEVICED_BATT_INTERFACE, "ChargerType"); + if (msg) { + reply = dbus_connection_send_with_reply_and_block(conn, + msg, 1000, NULL); + if (reply) { + if (dbus_message_get_args(reply, NULL, + DBUS_TYPE_INT32, &charging_state, + DBUS_TYPE_INVALID) == TRUE) { + set_charging_state(adapter, charging_state); + } + dbus_message_unref(reply); + } else { + error("Reply is NULL"); + } + dbus_message_unref(msg); + } else { + error("Unable to create dbus message for charging state"); + } +#endif /* TIZEN_FEATURE_BLUEZ_BATTERY_WATCH */ +#endif + return btd_adapter_ref(adapter); } static void adapter_remove(struct btd_adapter *adapter) { GSList *l; +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY struct gatt_db *db; +#endif DBG("Removing adapter %s", adapter->path); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#ifdef TIZEN_FEATURE_BLUEZ_BATTERY_WATCH + if (adapter->charging_watch > 0) { + g_dbus_remove_watch(btd_get_dbus_connection(), + adapter->charging_watch); + adapter->charging_watch = 0; + } + + if (adapter->charging_timeout) { + g_source_remove(adapter->charging_timeout); + adapter->charging_timeout = 0; + } +#endif /* TIZEN_FEATURE_BLUEZ_BATTERY_WATCH */ +#endif + if (adapter->discovery_idle_timeout > 0) { g_source_remove(adapter->discovery_idle_timeout); adapter->discovery_idle_timeout = 0; @@ -5359,12 +9695,16 @@ static void adapter_remove(struct btd_adapter *adapter) unload_drivers(adapter); +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY db = btd_gatt_database_get_db(adapter->database); gatt_db_unregister(db, adapter->db_id); adapter->db_id = 0; btd_gatt_database_destroy(adapter->database); adapter->database = NULL; +#else + btd_adapter_gatt_server_stop(adapter); +#endif btd_advertising_manager_destroy(adapter->adv_manager); adapter->adv_manager = NULL; @@ -5389,6 +9729,24 @@ const bdaddr_t *btd_adapter_get_address(struct btd_adapter *adapter) return &adapter->bdaddr; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +const bdaddr_t *btd_adapter_get_le_address(struct btd_adapter *adapter) +{ + if (adapter->le_static_addr.b[5] != 0) + return &adapter->le_static_addr; + else + return &adapter->bdaddr; +} + +uint8_t btd_adapter_get_le_address_type(struct btd_adapter * adapter) +{ + if (adapter->le_static_addr.b[5] != 0) + return BDADDR_LE_RANDOM; + else + return BDADDR_LE_PUBLIC; +} +#endif + static gboolean confirm_name_timeout(gpointer user_data) { struct btd_adapter *adapter = user_data; @@ -5554,28 +9912,53 @@ static bool is_filter_match(GSList *discovery_filter, struct eir_data *eir_data, return got_match; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static void update_found_devices(struct btd_adapter *adapter, + const bdaddr_t *bdaddr, + uint8_t bdaddr_type, int8_t rssi, + bool confirm, bool legacy, uint8_t adv_type, + const uint8_t *data, uint8_t data_len) +#else static void update_found_devices(struct btd_adapter *adapter, const bdaddr_t *bdaddr, uint8_t bdaddr_type, int8_t rssi, bool confirm, bool legacy, bool not_connectable, const uint8_t *data, uint8_t data_len) +#endif { struct btd_device *dev; struct eir_data eir_data; +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY bool name_known, discoverable; +#else + bool name_known; +#endif +#ifdef TIZEN_FEATURE_PLATFROM_SCAN_FILTER + uint8_t allow_report; +#endif char addr[18]; memset(&eir_data, 0, sizeof(eir_data)); eir_parse(&eir_data, data, data_len); - +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY if (bdaddr_type == BDADDR_BREDR || adapter->filtered_discovery) discoverable = true; else discoverable = eir_data.flags & (EIR_LIM_DISC | EIR_GEN_DISC); - +#endif ba2str(bdaddr, addr); +#ifdef TIZEN_FEATURE_PLATFROM_SCAN_FILTER + /* Check if the any filter policy */ + allow_report = validate_for_filter_policy(adapter, &eir_data, addr); + if (allow_report == NONE_REPORT && + ((adapter->scan_type == LE_ACTIVE_SCAN && adv_type == ADV_TYPE_SCAN_RESPONSE) || + adapter->scan_type == LE_PASSIVE_SCAN)) { + eir_data_free(&eir_data); + return; + } +#endif dev = btd_adapter_find_device(adapter, bdaddr, bdaddr_type); if (!dev) { /* @@ -5583,10 +9966,21 @@ static void update_found_devices(struct btd_adapter *adapter, * not marked as discoverable, then do not create new * device objects. */ +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + /*DBG("List BREDR:%p LE:%p Discoverable:%d", adapter->discovery_list, + adapter->le_discovery_list, discoverable);*/ + if ((adapter->discovery_list == NULL && + adapter->le_discovery_list == NULL)) { + DBG("discovery list is NULL"); + eir_data_free(&eir_data); + return; + } +#else if (!adapter->discovery_list || !discoverable) { eir_data_free(&eir_data); return; } +#endif dev = adapter_create_device(adapter, bdaddr, bdaddr_type); } @@ -5598,6 +9992,11 @@ static void update_found_devices(struct btd_adapter *adapter, return; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if(device_get_rpa_exist(dev) == true) + bdaddr_type = BDADDR_LE_RANDOM; +#endif + device_update_last_seen(dev, bdaddr_type); /* @@ -5620,11 +10019,26 @@ static void update_found_devices(struct btd_adapter *adapter, * If no client has requested discovery, then only update * already paired devices (skip temporary ones). */ +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (device_is_temporary(dev) && adapter->discovery_list == NULL && + adapter->le_discovery_list == NULL) { + DBG("discovery list is NULL"); + eir_data_free(&eir_data); + return; + } + + device_set_last_addr_type(dev, bdaddr_type); + device_set_ipsp_connected(dev, FALSE, NULL); +#else if (device_is_temporary(dev) && !adapter->discovery_list) { eir_data_free(&eir_data); return; } +#endif +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (bdaddr_type == BDADDR_BREDR) { +#endif if (adapter->filtered_discovery && !is_filter_match(adapter->discovery_list, &eir_data, rssi)) { eir_data_free(&eir_data); @@ -5640,13 +10054,23 @@ static void update_found_devices(struct btd_adapter *adapter, if (eir_data.tx_power != 127) device_set_tx_power(dev, eir_data.tx_power); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + } +#endif if (eir_data.appearance != 0) device_set_appearance(dev, eir_data.appearance); /* Report an unknown name to the kernel even if there is a short name * known, but still update the name with the known short name. */ +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (eir_data.name_complete) + name_known = device_name_known(dev); + else + name_known = false; +#else name_known = device_name_known(dev); +#endif if (eir_data.name && (eir_data.name_complete || !name_known)) btd_device_device_set_name(dev, eir_data.name); @@ -5663,6 +10087,19 @@ static void update_found_devices(struct btd_adapter *adapter, device_add_eir_uuids(dev, eir_data.services); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (eir_data.flags != 0) + device_set_remote_feature_flag(dev, eir_data.flags); + + if (bdaddr_type == BDADDR_BREDR) + device_set_manufacturer_info(dev, &eir_data); + else { + /* if the application has registered for iBeacon report, + * then send ibeacon report along with advertisement report */ + device_set_adv_report_info(dev, (void*)data, data_len, adv_type, rssi); + } +#endif + if (eir_data.msd_list) { device_set_manufacturer_data(dev, eir_data.msd_list); adapter_msd_notify(adapter, dev, eir_data.msd_list); @@ -5682,7 +10119,11 @@ static void update_found_devices(struct btd_adapter *adapter, * Otherwise, this is an event from passive discovery and we * should check if the device needs connecting to. */ +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (!adapter->discovery_list && !adapter->le_discovery_list) +#else if (!adapter->discovery_list) +#endif goto connect_le; if (g_slist_find(adapter->discovery_found, dev)) @@ -5697,9 +10138,11 @@ static void update_found_devices(struct btd_adapter *adapter, return; connect_le: +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY /* Ignore non-connectable events */ if (not_connectable) return; +#endif /* * If we're in the process of stopping passive scanning and @@ -5768,11 +10211,65 @@ static void device_found_callback(uint16_t index, uint16_t length, confirm_name = (flags & MGMT_DEV_FOUND_CONFIRM_NAME); legacy = (flags & MGMT_DEV_FOUND_LEGACY_PAIRING); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + update_found_devices(adapter, &ev->addr.bdaddr, ev->addr.type, + ev->rssi, confirm_name, legacy, 0, + eir, eir_len); +#else update_found_devices(adapter, &ev->addr.bdaddr, ev->addr.type, ev->rssi, confirm_name, legacy, flags & MGMT_DEV_FOUND_NOT_CONNECTABLE, eir, eir_len); +#endif +} + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static void le_device_found_callback(uint16_t index, uint16_t length, + const void *param, void *user_data) +{ + const struct mgmt_ev_le_device_found *ev = param; + struct btd_adapter *adapter = user_data; + const uint8_t *eir; + uint16_t eir_len; + uint32_t flags; + bool confirm_name; + bool legacy; + char addr[18]; + + if (length < sizeof(*ev)) { + error("Too short device found event (%u bytes)", length); + return; + } + + eir_len = btohs(ev->eir_len); + if (length != sizeof(*ev) + eir_len) { + error("Device found event size mismatch (%u != %zu)", + length, sizeof(*ev) + eir_len); + return; + } + + if (eir_len == 0) + eir = NULL; + else + eir = ev->eir; + + flags = btohl(ev->flags); + + ba2str(&ev->addr.bdaddr, addr); + /*DBG("hci%u addr %s, rssi %d flags 0x%04x eir_len %u", + index, addr, ev->rssi, flags, eir_len);*/ + + confirm_name = (flags & MGMT_DEV_FOUND_CONFIRM_NAME); + legacy = (flags & MGMT_DEV_FOUND_LEGACY_PAIRING); + + /*DBG("hci%u addr %s, addr_type %d rssi %d flags 0x%04x eir_len %u confirm_name %d legacy %d, adv_type %02x", + index, addr, ev->addr.type, ev->rssi, flags, eir_len, confirm_name, legacy, ev->adv_type);*/ + + update_found_devices(adapter, &ev->addr.bdaddr, ev->addr.type, + ev->rssi, confirm_name, legacy, ev->adv_type, + eir, eir_len); } +#endif struct agent *adapter_get_agent(struct btd_adapter *adapter) { @@ -5792,7 +10289,11 @@ static void adapter_remove_connection(struct btd_adapter *adapter, device_remove_connection(device, bdaddr_type); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (device_is_authenticating(device, bdaddr_type)) +#else if (device_is_authenticating(device)) +#endif device_cancel_authentication(device, TRUE); /* If another bearer is still connected */ @@ -5800,12 +10301,31 @@ static void adapter_remove_connection(struct btd_adapter *adapter, return; adapter->connections = g_slist_remove(adapter->connections, device); - +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + DBG("isPaired %d, isBonded %d", device_is_paired(device, bdaddr_type), + device_is_bonded(device, bdaddr_type)); + if ((device_is_temporary(device) && !device_is_retrying(device)) || + (!device_is_bonded(device, bdaddr_type))) { +#else if (device_is_temporary(device) && !device_is_retrying(device)) { +#endif const char *path = device_get_path(device); DBG("Removing temporary device %s", path); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + /* device_is_paired is added incase of tempoary bonded + * oop file transfer in that device is not bonded it's paired. + */ + if (!(device_is_bonded(device, bdaddr_type) || + device_is_paired(device, bdaddr_type))) { + DBG("addr type %d, bonded", bdaddr_type); + return; + } + + btd_adapter_unpair_device(adapter, device); +#else btd_adapter_remove_device(adapter, device); +#endif } } @@ -5858,6 +10378,10 @@ static void adapter_stop(struct btd_adapter *adapter) g_dbus_emit_property_changed(dbus_conn, adapter->path, ADAPTER_INTERFACE, "Discovering"); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + g_dbus_emit_property_changed(dbus_conn, adapter->path, + ADAPTER_INTERFACE, "LEDiscovering"); +#endif if (adapter->dev_class) { /* the kernel should reset the class of device when powering @@ -5867,6 +10391,9 @@ static void adapter_stop(struct btd_adapter *adapter) ADAPTER_INTERFACE, "Class"); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + advertiser_cleanup(adapter); +#endif g_dbus_emit_property_changed(dbus_conn, adapter->path, ADAPTER_INTERFACE, "Powered"); @@ -5949,14 +10476,31 @@ static gboolean process_auth_queue(gpointer user_data) const char *dev_path; /* Wait services to be resolved before asking authorization */ - if (auth->svc_id > 0) + if (auth->svc_id > 0) { +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + DBG("Wait services to be resolved before asking authorization"); +#endif return FALSE; + } +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY if (device_is_trusted(device) == TRUE) { +#else + if (device_is_trusted(device) == TRUE || + device_is_profile_trusted(device, auth->uuid)) { +#endif auth->cb(NULL, auth->user_data); goto next; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + /* If Profile is Blocked, Simply reject Authorization*/ + if (device_is_profile_blocked(device, auth->uuid) == TRUE) { + auth->cb(&err, auth->user_data); + goto next; + } +#endif + /* If agent is set authorization is already ongoing */ if (auth->agent) return FALSE; @@ -6218,210 +10762,558 @@ int btd_adapter_pincode_reply(struct btd_adapter *adapter, 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) { @@ -6499,6 +11391,9 @@ static void pin_code_request_callback(uint16_t index, uint16_t length, /* Flag the request of a pincode to allow a bonding retry. */ adapter->pincode_requested = true; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + btd_device_set_legacy_pairing(device, true); +#endif memset(pin, 0, sizeof(pin)); @@ -6586,8 +11481,9 @@ static void bonding_complete(struct btd_adapter *adapter, if (device != NULL) device_bonding_complete(device, addr_type, status); +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY resume_discovery(adapter); - +#endif check_oob_bonding_complete(adapter, bdaddr, status); } @@ -6704,8 +11600,9 @@ int adapter_create_bonding(struct btd_adapter *adapter, const bdaddr_t *bdaddr, return -EBUSY; } +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY suspend_discovery(adapter); - +#endif return adapter_bonding_attempt(adapter, bdaddr, addr_type, io_cap); } @@ -6775,16 +11672,38 @@ static void dev_disconnected(struct btd_adapter *adapter, { struct btd_device *device; char dst[18]; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + struct device_addr_type t_addr; +#endif ba2str(&addr->bdaddr, dst); DBG("Device %s disconnected, reason %u", dst, reason); device = btd_adapter_find_device(adapter, &addr->bdaddr, addr->type); + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (device) { + device_get_tizen_addr(device, addr->type, &t_addr); + + device_set_disconnect_reason(device, reason); + adapter_remove_connection(adapter, device, t_addr.bdaddr_type); + disconnect_notify(device, reason); + if (device_is_bonded(device, t_addr.bdaddr_type)) { + DBG("addr type %d, bonded", t_addr.bdaddr_type); + return; + } + + bonding_attempt_complete(adapter, &t_addr.bdaddr, + t_addr.bdaddr_type, MGMT_STATUS_DISCONNECTED); + return; + } +#else if (device) { adapter_remove_connection(adapter, device, addr->type); disconnect_notify(device, reason); } +#endif bonding_attempt_complete(adapter, &addr->bdaddr, addr->type, MGMT_STATUS_DISCONNECTED); @@ -6822,7 +11741,12 @@ static void disconnect_complete(uint8_t status, uint16_t length, return; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + /* Use HCI error code instead of MGMT disconnection reason */ + dev_disconnected(adapter, &rp->addr, 0x16); +#else dev_disconnected(adapter, &rp->addr, MGMT_DEV_DISCONN_LOCAL_HOST); +#endif } int btd_adapter_disconnect_device(struct btd_adapter *adapter, @@ -6938,7 +11862,16 @@ static void new_link_key_callback(uint16_t index, uint16_t length, key->pin_len); device_set_bonded(device, BDADDR_BREDR); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + } else { + if (btd_adapter_get_a2dp_role(adapter) == BLUETOOTH_A2DP_SINK_ROLE) { + DBG("store_hint %d", ev->store_hint); + btd_device_set_temporary(device, false); + } + } +#else } +#endif bonding_complete(adapter, &addr->bdaddr, addr->type, 0); } @@ -7005,6 +11938,9 @@ static void new_long_term_key_callback(uint16_t index, uint16_t length, struct btd_device *device; bool persistent; char dst[18]; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + struct device_addr_type t_addr; +#endif if (length < sizeof(*ev)) { btd_error(adapter->dev_id, "Too small long term key event"); @@ -7049,9 +11985,16 @@ static void new_long_term_key_callback(uint16_t index, uint16_t length, ediv = le16_to_cpu(key->ediv); rand = le64_to_cpu(key->rand); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + device_get_tizen_addr(device, addr->type, &t_addr); + store_longtermkey(bdaddr, &t_addr.bdaddr, t_addr.bdaddr_type, + key->val, key->master, key->type, + key->enc_size, ediv, rand); +#else store_longtermkey(bdaddr, &key->addr.bdaddr, key->addr.type, key->val, key->master, key->type, key->enc_size, ediv, rand); +#endif device_set_bonded(device, addr->type); } @@ -7131,6 +12074,9 @@ static void new_csrk_callback(uint16_t index, uint16_t length, const bdaddr_t *bdaddr = btd_adapter_get_address(adapter); struct btd_device *device; char dst[18]; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + struct device_addr_type t_addr; +#endif if (length < sizeof(*ev)) { btd_error(adapter->dev_id, "Too small CSRK event"); @@ -7152,8 +12098,14 @@ static void new_csrk_callback(uint16_t index, uint16_t length, if (!ev->store_hint) return; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + device_get_tizen_addr(device, addr->type, &t_addr); + store_csrk(bdaddr, &t_addr.bdaddr, t_addr.bdaddr_type, key->val, 0, + key->type); +#else store_csrk(bdaddr, &key->addr.bdaddr, key->addr.type, key->val, 0, key->type); +#endif btd_device_set_temporary(device, false); } @@ -7202,6 +12154,9 @@ static void new_irk_callback(uint16_t index, uint16_t length, struct btd_device *device, *duplicate; bool persistent; char dst[18], rpa[18]; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + struct device_addr_type t_addr; +#endif if (length < sizeof(*ev)) { btd_error(adapter->dev_id, "Too small New IRK event"); @@ -7215,11 +12170,16 @@ static void new_irk_callback(uint16_t index, uint16_t length, if (bacmp(&ev->rpa, BDADDR_ANY)) { device = btd_adapter_get_device(adapter, &ev->rpa, - BDADDR_LE_RANDOM); + BDADDR_LE_RANDOM); +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY duplicate = btd_adapter_find_device(adapter, &addr->bdaddr, - addr->type); + addr->type); if (duplicate == device) duplicate = NULL; +#else + device_set_rpa(device, &ev->rpa); + duplicate = NULL; +#endif } else { device = btd_adapter_get_device(adapter, &addr->bdaddr, addr->type); @@ -7237,11 +12197,20 @@ static void new_irk_callback(uint16_t index, uint16_t length, if (duplicate) device_merge_duplicate(device, duplicate); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + device_set_irk_value(device, irk->val); +#endif + persistent = !!ev->store_hint; if (!persistent) return; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + device_get_tizen_addr(device, addr->type, &t_addr); + store_irk(adapter, &t_addr.bdaddr, t_addr.bdaddr_type, irk->val); +#else store_irk(adapter, &addr->bdaddr, addr->type, irk->val); +#endif btd_device_set_temporary(device, false); } @@ -7294,7 +12263,9 @@ static void new_conn_param(uint16_t index, uint16_t length, uint16_t min, max, latency, timeout; struct btd_device *dev; char dst[18]; - +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + struct device_addr_type t_addr; +#endif if (length < sizeof(*ev)) { btd_error(adapter->dev_id, @@ -7322,19 +12293,71 @@ static void new_conn_param(uint16_t index, uint16_t length, 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; @@ -7342,22 +12365,31 @@ int adapter_set_io_capability(struct btd_adapter *adapter, uint8_t io_cap) 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, @@ -7367,17 +12399,18 @@ int btd_adapter_add_remote_oob_data(struct btd_adapter *adapter, 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, @@ -7386,6 +12419,7 @@ int btd_adapter_remove_remote_oob_data(struct btd_adapter *adapter, return -EIO; } +#endif bool btd_adapter_ssp_enabled(struct btd_adapter *adapter) { @@ -7412,6 +12446,10 @@ static void read_local_oob_data_complete(uint8_t status, uint16_t length, const struct mgmt_rp_read_local_oob_data *rp = param; struct btd_adapter *adapter = user_data; const uint8_t *hash, *randomizer; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + const uint8_t *hash256 = NULL; + const uint8_t *randomizer256 = NULL; +#endif if (status != MGMT_STATUS_SUCCESS) { btd_error(adapter->dev_id, @@ -7419,20 +12457,36 @@ static void read_local_oob_data_complete(uint8_t status, uint16_t length, mgmt_errstr(status), status); hash = NULL; randomizer = NULL; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + } else if (length < 32) { +#else } else if (length < sizeof(*rp)) { +#endif btd_error(adapter->dev_id, "Too small read local OOB data response"); return; } else { hash = rp->hash192; randomizer = rp->rand192; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (length > 32) { + hash256 = rp->hash256; + randomizer256 = rp->rand256; + } +#endif } if (!adapter->oob_handler || !adapter->oob_handler->read_local_cb) return; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + adapter->oob_handler->read_local_cb(adapter, hash, randomizer, + hash256, randomizer256, + adapter->oob_handler->user_data); +#else adapter->oob_handler->read_local_cb(adapter, hash, randomizer, adapter->oob_handler->user_data); +#endif g_free(adapter->oob_handler); adapter->oob_handler = NULL; @@ -7544,7 +12598,11 @@ static int adapter_register(struct btd_adapter *adapter) if (!g_dbus_register_interface(dbus_conn, adapter->path, ADAPTER_INTERFACE, +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + adapter_methods, adapter_signals, +#else adapter_methods, NULL, +#endif adapter_properties, adapter, adapter_free)) { btd_error(adapter->dev_id, @@ -7598,6 +12656,30 @@ static int adapter_register(struct btd_adapter *adapter) clear_blocked(adapter); load_devices(adapter); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + adapter_print_devices(adapter); + + if (load_local_irk(adapter)) { + if (!(adapter->supported_settings & MGMT_SETTING_PRIVACY)) + main_opts.le_privacy = false; + + /* + * Some Android devices don't consider the device as LE one, + * if the device doesn't distribute IRK when pairing. + * Because of this compatibility issue, set IRK + * even though privacy feature is disabled. + */ + set_local_irk(adapter); + + if (main_opts.le_privacy) { + DBG("Enable LE Privacy feature"); + set_privacy(adapter, true); + } else { + DBG("Disable LE Privacy feature"); + } + } +#endif + /* retrieve the active connections: address the scenario where * the are active connections before the daemon've started */ if (adapter->current_settings & MGMT_SETTING_POWERED) @@ -7825,11 +12907,13 @@ static void connect_failed_callback(uint16_t index, uint16_t length, device_bonding_failed(device, ev->status); } +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY /* In the case the bonding was canceled or did exists, remove the device * when it is temporary. */ if (device && !device_is_bonding(device, NULL) && device_is_temporary(device)) btd_adapter_remove_device(adapter, device); +#endif } static void remove_keys(struct btd_adapter *adapter, @@ -7845,6 +12929,11 @@ static void remove_keys(struct btd_adapter *adapter, ba2str(btd_adapter_get_address(adapter), adapter_addr); ba2str(device_get_address(device), device_addr); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (device_get_rpa_exist(device) == true) + ba2str(device_get_rpa(device), device_addr); +#endif + snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info", adapter_addr, device_addr); key_file = g_key_file_new(); @@ -7895,6 +12984,264 @@ static void unpaired_callback(uint16_t index, uint16_t length, device_set_unpaired(device, ev->addr.type); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static uint8_t *generate_irk(void) +{ + int fd; + uint8_t *irk; + + DBG("Generate IRK"); + + fd = open("/dev/urandom", O_RDONLY); + if (fd < 0) + return NULL; + + irk = g_malloc0(MGMT_IRK_SIZE); + if (read(fd, irk, MGMT_IRK_SIZE) != MGMT_IRK_SIZE) { + error("Cannot read random bytes"); + g_free(irk); + close(fd); + return NULL; + } + close(fd); + + return irk; +} + +#define LOCAL_IRK_DIRNAME "/csa/bluetooth" +#define LOCAL_IRK_FILENAME ".local_irk" + +static bool store_local_irk(struct btd_adapter *adapter) +{ + int fd; + int ret; + + if (adapter->local_irk == NULL) { + error("Local IRK is not proper"); + return false; + } + + if (access(LOCAL_IRK_DIRNAME, F_OK) < 0) { + if (mkdir(LOCAL_IRK_DIRNAME, 0755) < 0) { + error("Cannot create a directory for local IRK : %s", + strerror(errno)); + return false; + } + } + + fd = open(LOCAL_IRK_DIRNAME"/"LOCAL_IRK_FILENAME, + O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (fd < 0) { + error("Cannot open a file for local IRK : %s", strerror(errno)); + return false; + } + + ret = write(fd, adapter->local_irk, MGMT_IRK_SIZE); + if (ret != MGMT_IRK_SIZE) { + error("Cannot write local IRK [%d] : %s", ret, strerror(errno)); + + close(fd); + unlink(LOCAL_IRK_DIRNAME"/"LOCAL_IRK_FILENAME); + return false; + } + + ret = fdatasync(fd); + if (ret < 0) + error("sync failed : %s", strerror(errno)); + + close(fd); + return true; +} + +static bool load_local_irk(struct btd_adapter *adapter) +{ + int fd; + int ret; + + if (access(LOCAL_IRK_DIRNAME"/"LOCAL_IRK_FILENAME, F_OK) < 0) { + adapter->local_irk = generate_irk(); + if (store_local_irk(adapter) == false) { + error("Cannot store Local IRK"); + g_free(adapter->local_irk); + return false; + } + + return true; + } + + if (adapter->local_irk) { + DBG("Local IRK is already loaded"); + return true; + } + + fd = open(LOCAL_IRK_DIRNAME"/"LOCAL_IRK_FILENAME, O_RDONLY); + if (fd < 0) { + error("Cannot open local IRK file : %s", strerror(errno)); + return false; + } + + adapter->local_irk = g_malloc0(MGMT_IRK_SIZE); + + ret = read(fd, adapter->local_irk, MGMT_IRK_SIZE); + if (ret != MGMT_IRK_SIZE) { + error("Cannot read local IRK [%d] : %s", ret, strerror(errno)); + g_free(adapter->local_irk); + close(fd); + return false; + } + + close(fd); + return true; +} + +static void set_privacy_complete(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + struct btd_adapter *adapter = user_data; + + if (status != MGMT_STATUS_SUCCESS) + error("Setting privacy failed for hci%u: %s (0x%02x)", + adapter->dev_id, mgmt_errstr(status), status); + else + DBG("Privacy feature is set/unset successfully for hci%u", + adapter->dev_id); +} + +static bool set_privacy(struct btd_adapter *adapter, bool privacy) +{ + struct mgmt_cp_set_privacy cp; + + if (!adapter->local_irk) { + error("Local IRK is not available"); + return false; + } + + memset(&cp, 0, sizeof(cp)); + memcpy(cp.irk, adapter->local_irk, MGMT_IRK_SIZE); + + if (privacy) + cp.privacy = 0x01; + + if (mgmt_send(adapter->mgmt, MGMT_OP_SET_PRIVACY, + adapter->dev_id, sizeof(cp), &cp, + set_privacy_complete, adapter, NULL) > 0) + return true; + + error("Failed to set privacy and load local irk for index %u", + adapter->dev_id); + return false; +} + +static void set_irk_complete(uint8_t status, uint16_t length, + const void *param, void *user_data) +{ + struct btd_adapter *adapter = user_data; + + if (status != MGMT_STATUS_SUCCESS) + error("Setting IRK is failed for hci%u: %s (0x%02x)", + adapter->dev_id, mgmt_errstr(status), status); + else + DBG("Setting IRK is succeed for hci%u", adapter->dev_id); +} + +static bool set_local_irk(struct btd_adapter *adapter) +{ + struct mgmt_cp_set_irk cp; + + if (!adapter->local_irk) { + error("Local IRK is not available"); + return false; + } + + memcpy(cp.irk, adapter->local_irk, MGMT_IRK_SIZE); + + if (mgmt_send(adapter->mgmt, MGMT_OP_SET_IRK, + adapter->dev_id, sizeof(cp), &cp, + set_irk_complete, adapter, NULL) > 0) + return true; + + error("Failed to set irk %u", adapter->dev_id); + return false; +} + +int btd_adapter_connect_ipsp(struct btd_adapter *adapter, + const bdaddr_t *bdaddr, + uint8_t bdaddr_type) + +{ + struct mgmt_cp_connect_6lowpan cp; + + memset(&cp, 0, sizeof(cp)); + bacpy(&cp.addr.bdaddr, bdaddr); + cp.addr.type = bdaddr_type; + + if (mgmt_send(adapter->mgmt, MGMT_OP_CONNECT_6LOWPAN, + adapter->dev_id, sizeof(cp), &cp, + NULL, NULL, NULL) > 0) + return 0; + + return -EIO; +} + +int btd_adapter_disconnect_ipsp(struct btd_adapter *adapter, + const bdaddr_t *bdaddr, + uint8_t bdaddr_type) + +{ + struct mgmt_cp_disconnect_6lowpan cp; + + memset(&cp, 0, sizeof(cp)); + bacpy(&cp.addr.bdaddr, bdaddr); + cp.addr.type = bdaddr_type; + + if (mgmt_send(adapter->mgmt, MGMT_OP_DISCONNECT_6LOWPAN, + adapter->dev_id, sizeof(cp), &cp, + NULL, NULL, NULL) > 0) + return 0; + + return -EIO; +} + +uint8_t btd_adapter_get_rpa_res_support_value( + struct btd_adapter *adapter) +{ + return adapter->central_rpa_res_support; +} + +static void set_dev_rpa_res_support_complete(uint8_t status, + uint16_t length, const void *param, + void *user_data) +{ + if (status != MGMT_STATUS_SUCCESS) + error("Failed to set RPA resolution support of device : %s (0x%02x)", + mgmt_errstr(status), status); + else + DBG("Set RPA resolution support successful"); +} + +int btd_adapter_set_dev_rpa_res_support(struct btd_adapter *adapter, + struct btd_device *device) + +{ + struct mgmt_cp_set_dev_rpa_res_support cp; + + DBG("btd_adapter_set_dev_rpa_res_support called"); + + memset(&cp, 0, sizeof(cp)); + + bacpy(&cp.addr.bdaddr, device_get_address(device)); + cp.addr.type = btd_device_get_bdaddr_type(device); + cp.res_support = device_get_rpa_res_char_value(device); + + if (mgmt_send(adapter->mgmt, MGMT_OP_SET_DEV_RPA_RES_SUPPORT, + adapter->dev_id, sizeof(cp), &cp, + set_dev_rpa_res_support_complete, NULL, NULL) > 0) + return 0; + + return -EIO; +} +#endif + static void clear_devices_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { @@ -7927,6 +13274,17 @@ static int clear_devices(struct btd_adapter *adapter) return -EIO; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static gboolean adapter_start_idle_cb(gpointer user_data) +{ + struct btd_adapter *adapter = (struct btd_adapter*)user_data; + + adapter_start(adapter); + + return FALSE; +} +#endif + static void read_info_complete(uint8_t status, uint16_t length, const void *param, void *user_data) { @@ -7973,6 +13331,10 @@ static void read_info_complete(uint8_t status, uint16_t length, adapter->supported_settings = btohl(rp->supported_settings); adapter->current_settings = btohl(rp->current_settings); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + adapter_check_version(adapter, rp->version); +#endif + clear_uuids(adapter); clear_devices(adapter); @@ -8026,6 +13388,13 @@ static void read_info_complete(uint8_t status, uint16_t length, (missing_settings & MGMT_SETTING_FAST_CONNECTABLE)) set_mode(adapter, MGMT_OP_SET_FAST_CONNECTABLE, 0x01); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + /* Set the RPA resolution value to '1' if privacy is supported */ + if (main_opts.le_privacy && + adapter->supported_settings & MGMT_SETTING_PRIVACY) + adapter->central_rpa_res_support = 0x01; +#endif + err = adapter_register(adapter); if (err < 0) { btd_error(adapter->dev_id, "Unable to register new adapter"); @@ -8061,6 +13430,13 @@ static void read_info_complete(uint8_t status, uint16_t length, device_found_callback, adapter, NULL); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + mgmt_register(adapter->mgmt, MGMT_EV_LE_DEVICE_FOUND, + adapter->dev_id, + le_device_found_callback, + adapter, NULL); +#endif + mgmt_register(adapter->mgmt, MGMT_EV_DEVICE_DISCONNECTED, adapter->dev_id, disconnected_callback, @@ -8140,6 +13516,63 @@ static void read_info_complete(uint8_t status, uint16_t length, user_passkey_notify_callback, adapter, NULL); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + mgmt_register(adapter->mgmt, MGMT_EV_RSSI_ALERT, + adapter->dev_id, + rssi_alert_callback, + adapter, NULL); + + mgmt_register(adapter->mgmt, MGMT_EV_RAW_RSSI, + adapter->dev_id, + get_raw_rssi_callback, + adapter, NULL); + + mgmt_register(adapter->mgmt, MGMT_EV_RSSI_ENABLED, + adapter->dev_id, + rssi_enabled_callback, + adapter, NULL); + + mgmt_register(adapter->mgmt, MGMT_EV_RSSI_DISABLED, + adapter->dev_id, + rssi_disabled_callback, + adapter, NULL); + + mgmt_register(adapter->mgmt, MGMT_EV_HARDWARE_ERROR, + adapter->dev_id, + hardware_error_callback, + adapter, NULL); + + mgmt_register(adapter->mgmt, MGMT_EV_TX_TIMEOUT_ERROR, + adapter->dev_id, + tx_timeout_error_callback, + adapter, NULL); + + mgmt_register(adapter->mgmt, MGMT_EV_DEVICE_NAME_UPDATE, + adapter->dev_id, + device_name_update_callback, + adapter, NULL); + + mgmt_register(adapter->mgmt, MGMT_EV_MULTI_ADV_STATE_CHANGED, + adapter->dev_id, + multi_adv_state_change_callback, + adapter, NULL); + + mgmt_register(adapter->mgmt, MGMT_EV_6LOWPAN_CONN_STATE_CHANGED, + adapter->dev_id, + bt_6lowpan_conn_state_change_callback, + adapter, NULL); + + mgmt_register(adapter->mgmt, MGMT_EV_LE_DATA_LENGTH_CHANGED, + adapter->dev_id, + bt_le_data_length_changed_callback, + adapter, NULL); + + mgmt_register(adapter->mgmt, MGMT_EV_CONN_UPDATED, + adapter->dev_id, + le_conn_update_completed_callback, + adapter, NULL); +#endif + set_dev_class(adapter); set_name(adapter, btd_adapter_get_name(adapter)); @@ -8156,7 +13589,15 @@ static void read_info_complete(uint8_t status, uint16_t length, set_discoverable(adapter, 0x01, 0); if (adapter->current_settings & MGMT_SETTING_POWERED) +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + g_idle_add(adapter_start_idle_cb, adapter); +#else adapter_start(adapter); +#endif +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + else + set_mode(adapter, MGMT_OP_SET_POWERED, 0x01); +#endif return; diff --git a/src/adapter.h b/src/adapter.h index f2947fe..985e28e 100755 --- a/src/adapter.h +++ b/src/adapter.h @@ -25,21 +25,75 @@ #include #include #include +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#include +#include +#include +#include +#endif #define MAX_NAME_LENGTH 248 /* Invalid SSP passkey value used to indicate negative replies */ #define INVALID_PASSKEY 0xffffffff +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#define BT_DISC_TYPE_DEFAULT 0 +#define BT_DISC_TYPE_BREDR_ONLY 1 +#define BT_DISC_TYPE_LE_ONLY 2 +#define BT_DISC_TYPE_LE_BREDR 3 + +#define TIZEN_FEATURE_PLATFROM_SCAN_FILTER +#ifdef TIZEN_FEATURE_PLATFROM_SCAN_FILTER + +#define COMPANY_ID_APPLE 0x004C +/* Deafult value set to 16(same as vendor specific value) + * and value shall be changed when required */ +#define SCAN_FILTER_SLOTS_MAX 16 +#define ADV_TYPE_SCAN_RESPONSE 04 + +typedef enum { + NONE_REPORT, + SCAN_REPORT, + IBEACON_REPORT, +} adapter_le_scan_report_type; + +typedef enum { + LE_PASSIVE_SCAN, + LE_ACTIVE_SCAN, +} adapter_le_scan_type; +#endif /* TIZEN_FEATURE_PLATFROM_SCAN_FILTER */ + +#endif + struct btd_adapter; struct btd_device; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +typedef enum { + BLUETOOTH_A2DP_SOURCE_ROLE = 0, + BLUETOOTH_A2DP_SINK_ROLE, +} bluetooth_a2dp_role_t; +#endif + struct btd_adapter *btd_adapter_get_default(void); bool btd_adapter_is_default(struct btd_adapter *adapter); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +bluetooth_a2dp_role_t btd_adapter_get_a2dp_role(struct btd_adapter *adapter); +void btd_adapter_set_a2dp_role(struct btd_adapter *adapter, bluetooth_a2dp_role_t role); +#endif uint16_t btd_adapter_get_index(struct btd_adapter *adapter); typedef void (*adapter_cb) (struct btd_adapter *adapter, gpointer user_data); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +typedef void (*oob_ext_read_local_cb_t) (struct btd_adapter *adapter, + const uint8_t *hash192, + const uint8_t *randomizer192, + const uint8_t *hash256, + const uint8_t *randomizer256, + void *user_data); +#endif typedef void (*oob_read_local_cb_t) (struct btd_adapter *adapter, const uint8_t *hash, const uint8_t *randomizer, @@ -49,12 +103,26 @@ typedef void (*oob_bonding_cb_t) (struct btd_adapter *adapter, void *user_data); struct oob_handler { +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + oob_ext_read_local_cb_t read_local_cb; +#else oob_read_local_cb_t read_local_cb; +#endif oob_bonding_cb_t bonding_cb; bdaddr_t remote_addr; void *user_data; }; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#ifdef TIZEN_FEATURE_BLUEZ_BATTERY_WATCH +typedef enum { + NONE_CHARGING, + WIRE_CHARGING, + WIRELESS_CHARGING, +} charging_state_e; +#endif /* TIZEN_FEATURE_BLUEZ_BATTERY_WATCH */ +#endif + int adapter_init(void); void adapter_cleanup(void); void adapter_shutdown(void); @@ -92,6 +160,10 @@ struct btd_device *btd_adapter_find_device(struct btd_adapter *adapter, const char *adapter_get_path(struct btd_adapter *adapter); const bdaddr_t *btd_adapter_get_address(struct btd_adapter *adapter); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +const bdaddr_t *btd_adapter_get_le_address(struct btd_adapter *adapter); +uint8_t btd_adapter_get_le_address_type(struct btd_adapter * adapter); +#endif int adapter_set_name(struct btd_adapter *adapter, const char *name); int adapter_service_add(struct btd_adapter *adapter, sdp_record_t *rec); @@ -198,6 +270,16 @@ int btd_adapter_add_remote_oob_data(struct btd_adapter *adapter, int btd_adapter_remove_remote_oob_data(struct btd_adapter *adapter, const bdaddr_t *bdaddr); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +int btd_adapter_add_remote_oob_ext_data(struct btd_adapter *adapter, + const bdaddr_t *bdaddr, uint8_t bdaddr_type, + uint8_t *hash192, uint8_t *randomizer192, + uint8_t *hash256, uint8_t *randomizer256); + +int btd_adapter_remove_remote_oob_ext_data(struct btd_adapter *adapter, + const bdaddr_t *bdaddr, uint8_t bdaddr_type); +#endif + int btd_adapter_gatt_server_start(struct btd_adapter *adapter); void btd_adapter_gatt_server_stop(struct btd_adapter *adapter); @@ -226,3 +308,48 @@ void btd_adapter_for_each_device(struct btd_adapter *adapter, bool btd_le_connect_before_pairing(void); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +int btd_adapter_connect_ipsp(struct btd_adapter *adapter, + const bdaddr_t *bdaddr, + uint8_t bdaddr_type); +int btd_adapter_disconnect_ipsp(struct btd_adapter *adapter, + const bdaddr_t *bdaddr, + uint8_t bdaddr_type); + +uint8_t btd_adapter_get_rpa_res_support_value( + struct btd_adapter *adapter); + +int btd_adapter_set_dev_rpa_res_support(struct btd_adapter *adapter, + struct btd_device *device); +#endif + +typedef void (*read_max_data_length_cb_t) (struct btd_adapter *adapter, + const uint16_t max_txOctects, + const uint16_t max_txTime, + const uint16_t max_rxOctects, + const uint16_t max_rxTime, + void *user_data); + +struct le_data_length_read_handler { + read_max_data_length_cb_t read_callback; + void *user_data; +}; + +typedef void (*read_host_suggested_default_data_length_cb_t) (struct btd_adapter *adapter, + const uint16_t def_txOctects, + const uint16_t def_txTime, + void *user_data); + +struct le_data_length_read_default_data_length_handler { + read_host_suggested_default_data_length_cb_t read_callback; + void *user_data; +}; + +int btd_adapter_le_set_data_length(struct btd_adapter *adapter, bdaddr_t *bdaddr, + uint16_t max_tx_octets, uint16_t max_tx_time); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +void adapter_check_version(struct btd_adapter *adapter, uint8_t hci_ver); +#ifdef TIZEN_FEATURE_BLUEZ_BATTERY_WATCH +charging_state_e get_charging_state(struct btd_adapter *adapter); +#endif /* TIZEN_FEATURE_BLUEZ_BATTERY_WATCH */ +#endif diff --git a/src/adapter_le_vsc_features.c b/src/adapter_le_vsc_features.c new file mode 100755 index 0000000..6c586eb --- /dev/null +++ b/src/adapter_le_vsc_features.c @@ -0,0 +1,769 @@ +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + +#include + +#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 */ diff --git a/src/adapter_le_vsc_features.h b/src/adapter_le_vsc_features.h new file mode 100755 index 0000000..44a4bcc --- /dev/null +++ b/src/adapter_le_vsc_features.h @@ -0,0 +1,503 @@ +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + +typedef enum { + BLE_ADV_TX_POWER_MIN = 0x00, + BLE_ADV_TX_POWER_LOW = 0x01, + BLE_ADV_TX_POWER_MID = 0x02, + BLE_ADV_TX_POWER_UPPER = 0x03, + BLE_ADV_TX_POWER_MAX = 0x04, +} adapter_le_tx_power_t; + +typedef struct { + uint8_t inst_id; + uint8_t bdaddr_type; + bdaddr_t bdaddr; +} adapter_le_adv_inst_info_t; + +typedef struct { + uint16_t adv_int_min; /* minimum adv interval */ + uint16_t adv_int_max; /* maximum adv interval */ + uint8_t adv_type; /* adv event type (0x00 ~ 0x04) */ + uint8_t channel_map; /* adv channel map (all channel = 0x07) */ + uint8_t adv_filter_policy; /* advertising filter policy (0x00 ~ 0x04) */ + adapter_le_tx_power_t tx_power; /* adv tx power */ +} adapter_le_adv_param_t; + +typedef enum { + ADD, + DELETE, + CLEAR, +} adapter_le_scan_filter_action_type; + +typedef enum { + ADDR_LE_PUBLIC, + ADDR_LE_RANDOM, +} adapter_vsc_le_addr_type; + +typedef enum { + TYPE_DEVICE_ADDRESS = 0x01, + TYPE_SERVICE_DATA_CHANGED = 0x02, + TYPE_SERVICE_UUID = 0x04, + TYPE_SOLICIT_UUID = 0x08, + TYPE_LOCAL_NAME = 0x10, + TYPE_MANUFACTURER_DATA = 0x20, + TYPE_SERVICE_DATA = 0x40, +} adapter_le_scan_filter_type; + +#define BDADDR_BREDR 0x00 +#define BDADDR_LE_PUBLIC 0x01 + +#define BROADCAST_ADDR_FILTER 0x01 +#define SERVICE_DATA_CHANGE_FILTER 0x02 +#define SERVICE_UUID_CHECK 0x04 +#define SERVICE_SOLICITATION_UUID_CHECK 0x08 +#define LOCAL_NAME_CHECK 0x10 +#define MANUFACTURE_DATA_CHECK 0x20 +#define SERVICE_DATA_CHECK 0x40 + +typedef uint16_t adapter_le_scan_filter_feature_t; + +typedef enum { + OR, + AND, +} adapter_le_scan_filter_logic_type; + +typedef enum { + IMMEDIATE, + ON_FOUND, + BATCHED, +} adapter_le_scan_filter_delivery_mode; + +typedef enum { + UUID_16_LEN=2, + UUID_32_LEN=4, + UUID_128_LEN =16, +} adapter_le_uuid_len; + +typedef struct { + adapter_le_scan_filter_action_type action; + uint8_t index; + adapter_le_scan_filter_feature_t feature; + adapter_le_scan_filter_logic_type list_logic_type; + adapter_le_scan_filter_logic_type filter_logic_type; + uint8_t rssi_high_threshold; + adapter_le_scan_filter_delivery_mode delivery_mode; + uint16_t onfound_timeout; + uint8_t onfound_timeout_cnt; + uint8_t rssi_low_threshold; + uint16_t onlost_timeout; +}adapter_le_scan_filter_param_t; + +typedef struct { + adapter_le_scan_filter_action_type action; + uint8_t filter_index; + bdaddr_t broadcaster_addr; + adapter_vsc_le_addr_type bdaddr_type; +} adapter_le_address_filter_params_t; + +typedef struct { + adapter_le_scan_filter_action_type action; + uint8_t filter_index; + uint8_t *uuid; + uint8_t *uuid_mask; + adapter_le_uuid_len uuid_len; +}adapter_le_uuid_params_t; + +typedef struct { + adapter_le_scan_filter_action_type action; + uint8_t filter_index; + const char *local_name; + uint8_t name_len; +}adapter_le_local_name_params_t; + +typedef struct { + adapter_le_scan_filter_action_type action; + uint8_t filter_index; + uint16_t company_id; + uint16_t company_id_mask; + uint8_t *man_data; + uint8_t *man_data_mask; + uint8_t man_data_len; +}adapter_le_manf_data_params_t; + +typedef struct { + adapter_le_scan_filter_action_type action; + uint8_t filter_index; + uint8_t *service_data; + uint8_t *service_data_mask; + uint8_t service_data_len; +}adapter_le_service_data_params_t; + +/***************************************************************************** +** Defentions for HCI Error Codes that are past in the events +*/ +#define HCI_SUCCESS 0x00 + + + +/***************************************************************************** +** Vendor Specific Commands +** +*/ + +#define OCF_BCM_LE_GET_VENDOR_CAP 0x0153 /* LE Get Vendor Capabilities */ + +#define OCF_BCM_LE_MULTI_ADV 0x0154 /* Multi adv OCF */ + +/* subcode for multi adv feature */ +#define SUB_CMD_LE_MULTI_ADV_SET_PARAM 0x01 +#define SUB_CMD_LE_MULTI_ADV_WRITE_ADV_DATA 0x02 +#define SUB_CMD_LE_MULTI_ADV_WRITE_SCAN_RSP_DATA 0x03 +#define SUB_CMD_LE_MULTI_ADV_SET_RANDOM_ADDR 0x04 +#define SUB_CMD_LE_MULTI_ADV_ENB 0x05 + +/* APCF : Advertising Packet Content Filter feature */ +#define OCF_BCM_LE_SCAN_FILTER 0x0157 /* Advertising filter OCF */ + +/* Sub codes for APCF */ +#define SUB_CMD_LE_META_PF_ENABLE 0x00 +#define SUB_CMD_LE_META_PF_FEAT_SEL 0x01 +#define SUB_CMD_LE_META_PF_ADDR 0x02 +#define SUB_CMD_LE_META_PF_UUID 0x03 +#define SUB_CMD_LE_META_PF_SOL_UUID 0x04 +#define SUB_CMD_LE_META_PF_LOCAL_NAME 0x05 +#define SUB_CMD_LE_META_PF_MANU_DATA 0x06 +#define SUB_CMD_LE_META_PF_SRVC_DATA 0x07 +#define SUB_CMD_LE_META_PF_ALL 0x08 + +/* Offloaded resolution of private address */ +#define OCF_BCM_LE_RPA_OFFLOAD 0x0155 /* RPA Offload OCF */ + +/* subcode for rpa offloading feature */ +#define SUB_CMD_LE_ENABLE_OFFLOADING 0x01 +#define SUB_CMD_LE_ADD_IRK_TO_LIST 0x02 +#define SUB_CMD_LE_REMOVE_IRK_TO_LIST 0x03 +#define SUB_CMD_LE_CLEAR_IRK_TO_LIST 0x04 +#define SUB_CMD_LE_READ_IRK_TO_LIST 0x05 + +/***************************************************************************** +** CP & RP for OCF_BCM_LE_GET_VENDOR_CAP +** +*/ + +/** +* +* RP +* +* (1 octet) status : Command complete status +* (1 octet) adv_inst_max : Num of advertisement instances supported +* (1 octet) rpa_offloading: BT chip capability of RPA +* (value 0 not capable, value 1 capable) +* If supported by chip, it needs enablement by host +* (2 octet) tot_scan_results_strg : Storage for scan results in bytes +* (1 octet) max_irk_list_sz : Num of IRK entries supported in f/w +* (1 octet) filter_support : Support Filtering in controller. +* 0 = Not supported / 1 = supported +* (1 octet) max_filter : Number of filters supported +* (1 octet) energy_support : Supports reporting of activity and energy info +* 0 = not capable, 1 = capable +* (1 octet) onlost_follow : Number of advertisers that can be analysed +* for onlost per filter +*/ +typedef struct { + uint8_t status; + uint8_t adv_inst_max; + uint8_t rpa_offloading; + uint16_t tot_scan_results_strg; + uint8_t max_irk_list_sz; + uint8_t filter_support; + uint8_t max_filter; + uint8_t energy_support; + uint8_t onlost_follow; +} __attribute__ ((packed)) apater_le_vsc_rp_get_vendor_cap; + + + +/***************************************************************************** +** CP & RP for OCF_BCM_LE_MULTI_ADV +** +*/ + +/** +* +* CP for OCF_BCM_LE_MULTI_ADV & SUB_CMD_LE_MULTI_ADV_SET_PARAM +* +* (1 octet) subcode : SUB_CMD_LE_MULTI_ADV_SET_PARAM +* (2 octet) adv_int_min : per spec +* (2 octet) adv_int_max : per spec +* (1 octet) adv_type : per spec +* (1 octet) bdaddr_type : per spec +* (6 octet) bdaddr : per spec +* (1 octet) direct_bdaddr_type : per spec +* (6 octet) direct_bdaddr : per spec +* (1 octet) channel_map : per spec +* (1 octet) adv_filter_policy : per spec +* (1 octet) inst_id : Specifies the applicability of the above parameters to an instance +* (1 octet) tx_power : Transmit_Power Unit - in dBm (signed integer) Range (70 to +20) +*/ +typedef struct { + uint8_t subcode; + uint16_t adv_int_min; + uint16_t adv_int_max; + uint8_t adv_type; + uint8_t bdaddr_type; + bdaddr_t bdaddr; + uint8_t direct_bdaddr_type; + bdaddr_t direct_bdaddr; + uint8_t channel_map; + uint8_t adv_filter_policy; + uint8_t inst_id; + uint8_t tx_power; +} __attribute__ ((packed)) adapter_le_vsc_cp_set_multi_adv_params; + +/** +* +* CP for SUB_CMD_LE_MULTI_ADV_WRITE_ADV_DATA +* CP for SUB_CMD_LE_MULTI_ADV_WRITE_SCAN_RSP_DATA +* +* ( 1 octet) subcode : SUB_CMD_LE_MULTI_ADV_WRITE_ADV_DATA +* or SUB_CMD_LE_MULTI_ADV_WRITE_SCAN_RSP_DATA +* ( 1 octet) data_len : per spec +* (31 octet) data : per spec +* ( 1 octet) inst_id : Specifies the applicability of the above parameters to an instance. +*/ +typedef struct { + uint8_t subcode; + uint8_t data_len; + uint8_t data[31]; + uint8_t inst_id; +} __attribute__ ((packed)) adapter_le_vsc_cp_set_multi_adv_data; + +/** +* +* CP for SUB_CMD_LE_MULTI_ADV_ENB +* +* (1 octet) subcode : SUB_CMD_LE_MULTI_ADV_ENB +* (1 octet) enable : When set to 1, it means enable, otherwise disable. +* (1 octet) inst_id : Specifies the applicability of the above parameters +* to an instance. Instance 0 has special meaning this +* refers to std HCI instance. +*/ +typedef struct { + uint8_t subcode; + uint8_t enable; + uint8_t inst_id; +} __attribute__ ((packed)) adapter_le_vsc_cp_enable_multi_adv; + +/** +* +* RP +* +* (1 octet) status : Command complete status +* (1 octet) subcode : subcode of OCF_BCM_LE_MULTI_ADV +*/ +typedef struct { + uint8_t status; + uint8_t subcode; +} __attribute__ ((packed)) apater_le_vsc_rp_multi_adv; + + + +/***************************************************************************** +** CP & RP for OCF_BCM_LE_SCAN_FILTER +** +*/ + + +/* CP for SUB_CMD_LE_META_PF_ENABLE */ +typedef struct { + uint8_t subcode; + uint8_t enable; +} __attribute__ ((packed)) adapter_le_vsc_cp_enable_scan_filter; + +/* RP for SUB_CMD_LE_META_PF_ENABLE */ +typedef struct { + uint8_t status; + uint8_t subcode; + uint8_t enable; +} __attribute__ ((packed)) apater_le_vsc_rp_enable_scan_filter; + +/* CP for SUB_CMD_LE_META_PF_FEAT_SEL */ +typedef struct { + uint8_t subcode; + uint8_t action; + uint8_t filter_index; + uint16_t feature; + uint16_t feature_list_logic; + uint8_t filter_logic; + int8_t rssi_high_threshold; + uint8_t delivery_mode; + uint16_t onfound_timeout; + uint8_t onfound_timeout_cnt; + uint8_t rssi_low_thresh; + uint16_t onlost_timeout; +} __attribute__ ((packed)) adapter_le_vsc_cp_apcf_set_filter_params; + +/* CP for SUB_CMD_LE_META_PF_ADDR */ +typedef struct { + uint8_t subcode; + uint8_t action; + uint8_t filter_index; + bdaddr_t bdaddr; + uint8_t bdaddr_type; +} __attribute__ ((packed))adapter_le_vsc_cp_address_scan_filter; + +/* CP for SUB_CMD_LE_META_PF_UUID & SUB_CMD_LE_META_PF_SOL_UUID */ +typedef struct { + uint8_t subcode; + uint8_t action; + uint8_t filter_index; + uint8_t data[40]; /* UUID + UUID_MASK */ +} __attribute__ ((packed))adapter_le_vsc_cp_service_uuid_scan_filter; +#define UUID_SCAN_FILTER_HEADER_SIZE 3 + +#define SCAN_FILTER_DATA_MAX_LEN 29 + +/* CP for SUB_CMD_LE_META_PF_LOCAL_NAME*/ +typedef struct { + uint8_t subcode; + uint8_t action; + uint8_t filter_index; + uint8_t name[SCAN_FILTER_DATA_MAX_LEN]; +} __attribute__ ((packed)) adapter_le_vsc_cp_local_name_scan_filter; +#define NAME_SCAN_FILTER_HEADER_SIZE 3 + +/* CP for SUB_CMD_LE_META_PF_MANU_DATA*/ +typedef struct { + uint8_t subcode; + uint8_t action; + uint8_t filter_index; + uint8_t data[SCAN_FILTER_DATA_MAX_LEN * 2]; /* data + mask filed */ +} __attribute__ ((packed)) adapter_le_vsc_cp_manf_data_scan_filter; +#define MANF_DATA_SCAN_FILTER_HEADER_SIZE 3 + +/* CP for SUB_CMD_LE_META_PF_SRVC_DATA*/ +typedef struct { + uint8_t subcode; + uint8_t action; + uint8_t filter_index; + uint8_t data[SCAN_FILTER_DATA_MAX_LEN * 2]; /* data + mask filed */ +} __attribute__ ((packed)) adapter_le_vsc_cp_service_data_scan_filter; +#define SERVICE_DATA_SCAN_FILTER_HEADER_SIZE 3 + +/* RP for SUB_CMD_LE_META_PF_ADDR & SUB_CMD_LE_META_PF_FEAT_SEL & + SUB_CMD_LE_META_PF_UUID & SUB_CMD_LE_META_PF_SOL_UUID & + SUB_CMD_LE_META_PF_LOCAL_NAME & SUB_CMD_LE_META_PF_MANU_DATA & + SUB_CMD_LE_META_PF_SRVC_DATA */ +typedef struct { + uint8_t status; + uint8_t subcode; + uint8_t action; + uint8_t available_space; +} __attribute__ ((packed)) adapter_le_vsc_rp_apcf_set_scan_filter; + + +/***************************************************************************** +** CP & RP for OCF_BCM_LE_RPA_OFFLOAD +** +*/ + +/** +* +* CP for SUB_CMD_ENABLE_RPA_OFFLOAD +* +* (1 octet) subcode : SUB_CMD_ENABLE_RPA_OFFLOAD (0x01) +* (2 octet) enable : When set to 1, it means enable, otherwise disable. +*/ +typedef struct { + uint8_t subcode; + uint8_t enable; +} __attribute__ ((packed)) adapter_le_vsc_cp_enable_rpa_offload; + +/* RP for SUB_CMD_ENABLE_RPA_OFFLOAD */ +typedef struct { + uint8_t status; + uint8_t subcode; +} __attribute__ ((packed)) adapter_le_vsc_rp_enable_rpa_offload; + +/** +* +* CP for SUB_CMD_ADD_IRK_TO_LIST +* +* (1 octet) subcode : SUB_CMD_ADD_IRK_TO_LIST (0x02) +* (16 octet) le_irk : LE IRK (1st byte LSB) +* (1 octet) bdaddr_type : per spec +* (6 octet) bdaddr : per spec +*/ +typedef struct { + uint8_t subcode; + uint8_t le_irk[16]; + uint8_t bdaddr_type; + bdaddr_t bdaddr; +} __attribute__ ((packed)) adapter_le_vsc_cp_add_irk_to_list; + +/** +* +* CP for SUB_CMD_REMOVE_IRK_TO_LIST +* +* (1 octet) subcode : SUB_CMD_REMOVE_IRK_TO_LIST (0x03) +* (16 octet) le_irk : LE IRK (1st byte LSB) +* (1 octet) bdaddr_type : per spec +* (6 octet) bdaddr : per spec +*/ +typedef struct { + uint8_t subcode; + uint8_t bdaddr_type; + bdaddr_t bdaddr; +} __attribute__ ((packed)) adapter_le_vsc_cp_remove_irk_to_list; + +/* RP for SUB_CMD_ADD_IRK_TO_LIST & SUB_CMD_REMOVE_IRK_TO_LIST */ +typedef struct { + uint8_t status; + uint8_t subcode; + uint8_t available_space; +} __attribute__ ((packed)) adapter_le_vsc_rp_irk_to_list; + + +/***************************************************************************** +** Functions +** +*/ + +/* Read supported BLE feature info from chipset */ +gboolean adapter_le_read_ble_feature_info(void); + +gboolean adapter_le_is_supported_multi_advertising(void); + +gboolean adapter_le_is_supported_offloading(void); + +int adapter_le_get_max_adv_instance(void); + +int adapter_le_get_scan_filter_size(void); + +gboolean adapter_le_set_multi_adv_params (adapter_le_adv_inst_info_t *p_inst, + adapter_le_adv_param_t *p_params); + +gboolean adapter_le_set_multi_adv_data(uint8_t inst_id, gboolean is_scan_rsp, + uint8_t data_len, uint8_t *p_data); + +gboolean adapter_le_enable_multi_adv (struct btd_adapter *adapter, + gboolean enable, uint8_t inst_id); + +gboolean adapter_le_enable_scan_filtering (gboolean enable); + +gboolean adapter_le_set_scan_filter_params(adapter_le_scan_filter_param_t *params); + +gboolean adapter_le_set_scan_filter_data(int client_if, int action, + int filt_type, int filter_index, + int company_id, + int company_id_mask, + int uuid_len, uint8_t *p_uuid, + int uuid_mask_len, uint8_t *p_uuid_mask, + gchar *string, int addr_type, + int data_len, uint8_t *p_data, + int mask_len, uint8_t *p_mask); +gboolean adapter_le_clear_scan_filter_data(int client_if, int filter_index); + +gboolean adapter_le_enable_offloading(gboolean enable); + +gboolean adapter_le_add_irk_to_list(const uint8_t *le_irk, const bdaddr_t *bdaddr, uint8_t bdaddr_type); + +gboolean adapter_le_remove_irk_to_list(const bdaddr_t *bdaddr, uint8_t bdaddr_type); + +#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */ diff --git a/src/attio.h b/src/attio.h new file mode 100755 index 0000000..16e2873 --- /dev/null +++ b/src/attio.h @@ -0,0 +1,33 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2011 Nokia Corporation + * Copyright (C) 2011 Marcel Holtmann + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +typedef void (*attio_connect_cb) (GAttrib *attrib, gpointer user_data); +typedef void (*attio_disconnect_cb) (gpointer user_data); + +guint btd_device_add_attio_callback(struct btd_device *device, + attio_connect_cb cfunc, + attio_disconnect_cb dcfunc, + gpointer user_data); + +gboolean btd_device_remove_attio_callback(struct btd_device *device, guint id); diff --git a/src/attrib-server.c b/src/attrib-server.c index 4439c27..de6919d 100755 --- a/src/attrib-server.c +++ b/src/attrib-server.c @@ -255,6 +255,37 @@ static int attribute_cmp(gconstpointer a1, gconstpointer a2) return attrib1->handle - attrib2->handle; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static int attribute_uuid_cmp(gconstpointer a, gconstpointer b) +{ + const struct attribute *attrib1 = a; + const bt_uuid_t *uuid = b; + + return bt_uuid_cmp(&attrib1->uuid, uuid); +} + +struct attribute *attribute_find(struct btd_adapter *adapter, const bt_uuid_t *uuid) +{ + GSList *l; + GList *ldata; + struct gatt_server *server; + + /* Find the attrib server database for the given adapter */ + l = g_slist_find_custom(servers, adapter, adapter_cmp); + if (!l) + return NULL; + + server = l->data; + + ldata = g_list_find_custom(server->database, GUINT_TO_POINTER(uuid), + attribute_uuid_cmp); + if (!ldata) + return NULL; + + return ldata->data; +} +#endif + static struct attribute *find_svc_range(struct gatt_server *server, uint16_t start, uint16_t *end) { @@ -306,6 +337,10 @@ static uint32_t attrib_create_sdp_new(struct gatt_server *server, if (a->len == 2) sdp_uuid16_create(&svc, get_le16(a->data)); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + else if (a->len == 4) + sdp_uuid32_create(&svc, get_le32(a->data)); +#endif else if (a->len == 16) { uint8_t be128[16]; @@ -320,14 +355,20 @@ static uint32_t attrib_create_sdp_new(struct gatt_server *server, return 0; if (name != NULL) +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + sdp_set_info_attr(record, name, "Samsung", NULL); +#else sdp_set_info_attr(record, name, "BlueZ", NULL); +#endif sdp_uuid16_create(&gap_uuid, GENERIC_ACCESS_PROFILE_ID); +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY if (sdp_uuid_cmp(&svc, &gap_uuid) == 0) { sdp_set_url_attr(record, "http://www.bluez.org/", "http://www.bluez.org/", "http://www.bluez.org/"); } +#endif if (adapter_service_add(server->adapter, record) == 0) return record->handle; @@ -498,6 +539,10 @@ static uint16_t read_by_group(struct gatt_channel *channel, uint16_t start, if (dl == NULL) cur->end = a->handle; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + else if (a->handle == end && end == 0xffff) + cur->end = a->handle; +#endif else cur->end = last_handle; @@ -936,6 +981,35 @@ static uint16_t write_value(struct gatt_channel *channel, uint16_t handle, g_file_set_contents(filename, data, length, NULL); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + g_free(filename); + filename = btd_device_get_storage_path(channel->device, "ccc_sc"); + if (!filename) { + warn("Unable to get ccc storage path for device"); + g_free(data); + return enc_error_resp(ATT_OP_WRITE_REQ, handle, + ATT_ECODE_WRITE_NOT_PERM, + pdu, len); + } + + g_key_file_free(key_file); + key_file = g_key_file_new(); + g_key_file_load_from_file(key_file, filename, 0, NULL); + + memset(&group, 0x00, 6); + memset(&value, 0x00, 5); + sprintf(group, "%hu", handle); + sprintf(value, "%hX", cccval); + g_key_file_set_string(key_file, group, "Value", value); + + g_free(data); + data = g_key_file_to_data(key_file, &length, NULL); + if (length > 0) { + create_file(filename, S_IRUSR | S_IWUSR); + g_file_set_contents(filename, data, length, NULL); + } +#endif + g_free(data); g_free(filename); g_key_file_free(key_file); @@ -1066,6 +1140,12 @@ static void channel_handler(const uint8_t *ipdu, uint16_t len, } length = find_info(channel, start, end, opdu, channel->mtu); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (length == 0 && start == end) { + status = ATT_ECODE_ATTR_NOT_FOUND; + goto done; + } +#endif break; case ATT_OP_WRITE_REQ: length = dec_write_req(ipdu, len, &start, value, &vlen); @@ -1289,6 +1369,9 @@ static gboolean register_core_services(struct gatt_server *server) uint8_t atval[256]; bt_uuid_t uuid; uint16_t appearance = 0x0000; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + uint16_t service_changed_handle; +#endif /* GAP service: primary service definition */ bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID); @@ -1337,6 +1420,29 @@ static gboolean register_core_services(struct gatt_server *server) attrib_db_add_new(server, 0x0010, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + /* GATT service: service changed characteristic */ + service_changed_handle = 0x0012; + bt_uuid16_create(&uuid, GATT_CHARAC_UUID); + + atval[0] = GATT_CHR_PROP_INDICATE; + put_le16(service_changed_handle, &atval[1]); + put_le16(GATT_CHARAC_SERVICE_CHANGED, &atval[3]); + + attrib_db_add_new(server, 0x0011, &uuid, ATT_NONE, ATT_NOT_PERMITTED, + atval, 5); + + /* GATT service: service changed attribute */ + bt_uuid16_create(&uuid, GATT_CHARAC_SERVICE_CHANGED); + attrib_db_add_new(server, service_changed_handle, &uuid, ATT_NOT_PERMITTED, + ATT_NOT_PERMITTED, NULL, 0); + + bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID); + atval[0] = GATT_CHR_PROP_READ | GATT_CHR_PROP_WRITE; + atval[1] = 0; + attrib_db_add_new(server, 0x0013, &uuid, ATT_NONE, ATT_NONE, atval, 2); +#endif + server->gatt_sdp_handle = attrib_create_sdp_new(server, 0x0010, "Generic Attribute Profile"); if (server->gatt_sdp_handle == 0) { @@ -1623,6 +1729,97 @@ int attrib_db_del(struct btd_adapter *adapter, uint16_t handle) return 0; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +uint16_t send_sc_indication(uint16_t start_handle, uint16_t end_handle, size_t vlen, + uint8_t *pdu, size_t len) +{ + const uint16_t min_len = sizeof(pdu[0]) + sizeof(uint16_t); + + if (pdu == NULL) + return 0; + + if (len < (vlen + min_len)) + return 0; + + pdu[0] = ATT_OP_HANDLE_IND; +/* API replaced by put_le16 in bluez 5.25 + att_put_u16(start_handle, &pdu[1]); + att_put_u16(end_handle, &pdu[3]);*/ + put_le16(start_handle, &pdu[1]); + put_le16(end_handle, &pdu[3]); + + return vlen + min_len; +} + +static uint8_t attrib_get_ccc_info(struct btd_device *device, uint16_t handle) +{ + uint16_t cccval = 0; + char *filename; + GKeyFile *key_file; + char group[6]; + char *value; + + filename = btd_device_get_storage_path(device, "ccc"); + if (!filename) { + warn("Unable to get ccc storage path for device"); + return 0; + } + + key_file = g_key_file_new(); + g_key_file_load_from_file(key_file, filename, 0, NULL); + sprintf(group, "%hu", handle); + + /* Get the CCC value */ + value = g_key_file_get_string(key_file, group, "Value", NULL); + if (!value) + return 0; + + sscanf(value, "%hX", &cccval); + + g_free(value); + g_free(filename); + g_key_file_free(key_file); + + return cccval; +} + +void attrib_send_sc_ind(struct btd_device *device, GAttrib *attrib, + uint16_t start_handle, uint16_t end_handle, + size_t vlen) +{ + size_t length = 0; + uint8_t *pdu; + size_t mtu; + + pdu = g_attrib_get_buffer(attrib, &mtu); + length = send_sc_indication(start_handle, end_handle, vlen, pdu, mtu); + g_attrib_send(attrib, 0, pdu, length, NULL, NULL, NULL); +} + +void attrib_send_noty_ind(struct btd_device *device, GAttrib *attrib, + uint16_t handle, uint16_t desc_handle, + uint8_t *value, size_t vlen) +{ + size_t length = 0; + uint16_t cccval; + uint8_t *pdu; + size_t mtu; + + cccval = attrib_get_ccc_info(device, desc_handle); + if (!cccval) + return; + + pdu = g_attrib_get_buffer(attrib, &mtu); + if (cccval == GATT_CLIENT_CHARAC_CFG_NOTIF_BIT) { + length = enc_notification(handle, value, vlen, pdu, mtu); + g_attrib_send(attrib, 0, pdu, length, NULL, NULL, NULL); + } else if (cccval == GATT_CLIENT_CHARAC_CFG_IND_BIT) { + length = enc_indication(handle, value, vlen, pdu, mtu); + g_attrib_send(attrib, 0, pdu, length, NULL, NULL, NULL); + } +} +#endif + int attrib_gap_set(struct btd_adapter *adapter, uint16_t uuid, const uint8_t *value, size_t len) { diff --git a/src/attrib-server.h b/src/attrib-server.h index 063cb66..2a951e4 100755 --- a/src/attrib-server.h +++ b/src/attrib-server.h @@ -40,3 +40,15 @@ void attrib_free_sdp(struct btd_adapter *adapter, uint32_t sdp_handle); GAttrib *attrib_from_device(struct btd_device *device); guint attrib_channel_attach(GAttrib *attrib); gboolean attrib_channel_detach(GAttrib *attrib, guint id); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +struct attribute *attribute_find(struct btd_adapter *adapter, const bt_uuid_t *uuid); +void attrib_send_noty_ind(struct btd_device *device, GAttrib *attrib, + uint16_t handle, uint16_t desc_handle, + uint8_t *value, size_t vlen); +uint16_t send_sc_indication(uint16_t handle, uint16_t end_handle, size_t vlen, + uint8_t *pdu, size_t len); + +void attrib_send_sc_ind(struct btd_device *device, GAttrib *attrib, + uint16_t start_handle, uint16_t end_handle, + size_t vlen); +#endif diff --git a/src/bluetooth.conf b/src/bluetooth.conf index 10d2d36..2add5d7 100755 --- a/src/bluetooth.conf +++ b/src/bluetooth.conf @@ -10,12 +10,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -28,14 +56,91 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + + + + + + + + + + + + + + + + + + + + diff --git a/src/bluetooth.service.in b/src/bluetooth.service.in index f799f65..e41c2da 100755 --- a/src/bluetooth.service.in +++ b/src/bluetooth.service.in @@ -1,20 +1,17 @@ [Unit] Description=Bluetooth service Documentation=man:bluetoothd(8) -ConditionPathIsDirectory=/sys/class/bluetooth [Service] Type=dbus BusName=org.bluez -ExecStart=@libexecdir@/bluetoothd +ExecStart=/usr/libexec/bluetooth/bluetoothd -d -C NotifyAccess=main -#WatchdogSec=10 -#Restart=on-failure CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE LimitNPROC=1 ProtectHome=true ProtectSystem=full [Install] -WantedBy=bluetooth.target +WantedBy=multi-user.target Alias=dbus-org.bluez.service diff --git a/src/device.c b/src/device.c index 99454a7..217e402 100755 --- a/src/device.c +++ b/src/device.c @@ -75,6 +75,13 @@ #include "attrib-server.h" #include "eir.h" +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#include "sdp-xml.h" +#ifdef TIZEN_FEATURE_BLUEZ_BATTERY_WATCH +#include +#endif /* TIZEN_FEATURE_BLUEZ_BATTERY_WATCH */ +#endif + #define IO_CAPABILITY_NOINPUTNOOUTPUT 0x03 #define DISCONNECT_TIMER 2 @@ -92,6 +99,10 @@ #define GATT_INCLUDE_UUID_STR "2802" #define GATT_CHARAC_UUID_STR "2803" +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#define DEV_MAX_MANUFACTURER_DATA_LEN 248 +#endif + static DBusConnection *dbus_conn = NULL; static unsigned service_state_cb_id; @@ -159,6 +170,14 @@ struct svc_callback { void *user_data; }; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +struct le_adv_report_info { + uint8_t flags; + char manufacturer_data[DEV_MAX_MANUFACTURER_DATA_LEN]; + uint8_t manufacturer_data_len; +}; +#endif + /* Per-bearer (LE or BR/EDR) device state */ struct bearer_state { bool paired; @@ -172,6 +191,33 @@ struct csrk_info { uint32_t counter; }; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +typedef enum { + DEV_PAIRED_NONE = 0, + DEV_PAIRED_BREDR = 1, + DEV_PAIRED_LE, + DEV_PAIRED_BREDR_LE, +} dev_paired_state; + +typedef enum { + DEV_CONNECTED_NONE = 0, + DEV_CONNECTED_BREDR = 1, + DEV_CONNECTED_LE, + DEV_CONNECTED_BREDR_LE, +} dev_connected_state; + +struct trusted_profile_t { + uint32_t pbap:2; + uint32_t map:2; + uint32_t sap:2; +} __packed; + +struct restricted_profile_t { + uint32_t hfp_hs; + uint32_t a2dp; +}; +#endif + struct btd_device { int ref_count; @@ -239,6 +285,10 @@ struct btd_device { time_t le_seen; gboolean trusted; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + struct trusted_profile_t trusted_profiles; + struct restricted_profile_t restricted_profiles; +#endif gboolean blocked; gboolean auto_connect; gboolean disable_auto_connect; @@ -250,6 +300,30 @@ struct btd_device { GIOChannel *att_io; guint store_id; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + bool legacy_pairing; + char *manufacturer_data; + int manufacturer_data_len; + struct le_adv_report_info le_adv_data; + int remote_feature_flags; + guint attio_id; + gboolean gatt_connected; + uint16_t auth_payload_timeout; + uint8_t disc_reason; + uint8_t last_bdaddr_type; + uint8_t auth_bdaddr_type; + gboolean ipsp_connected; /* IPSP Connection state */ + char if_name[16 + 1]; /* BT interface UP after IPSP connection */ + uint8_t rpa_res_support; /* RPA Resolution capability of device */ + uint16_t max_tx_octets; + uint16_t max_tx_time; + uint16_t max_rx_octets; + uint16_t max_rx_time; + bdaddr_t *rpa; + DBusMessage *req_att_mtu; /* Attribute MTU request message */ + uint8_t irk_val[16]; + bool pending_conn_update; +#endif }; static const uint16_t uuid_list[] = { @@ -259,6 +333,29 @@ static const uint16_t uuid_list[] = { 0 }; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +typedef enum { + SHOW_AUTHORIZATION = 0x0, /* 0b00 */ + SUPPORTED_BLOCKED = 0x1, /* 0b01 */ + SUPPORTED_TRUSTED= 0x2, /* 0b10 */ +} bt_profile_trusted_states; + +#define PBAP_SHIFT_OFFSET 0 +#define MAP_SHIFT_OFFSET 2 +#define SAP_SHIFT_OFFSET 4 + +#define PROFILE_SUPPORTED 0x3 /* This corresponds to binary 0b11*/ + +typedef enum { + CONNECTION_PERMITTED = 0x0, /* 0b00 */ + CONNECTION_RESTRICTED = 0x1, /* 0b01 */ +} bt_profile_restricted_states; + +#define HFP_HS_SHIFT_OFFSET 0 +#define A2DP_SHIFT_OFFSET 2 + +#endif + static int device_browse_gatt(struct btd_device *device, DBusMessage *msg); static int device_browse_sdp(struct btd_device *device, DBusMessage *msg); @@ -353,6 +450,42 @@ static void store_csrk(struct csrk_info *csrk, GKeyFile *key_file, g_key_file_set_integer(key_file, group, "Counter", csrk->counter); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static char *manufacturer_data2str(char *data, int size) +{ + char str[DEV_MAX_MANUFACTURER_DATA_LEN * 3 + 1]; + char tmp[5]; + int i; + + str[0] = '\0'; + for(i = 0; i < size; i++) { + snprintf(tmp, sizeof(tmp), "%d ", data[i]); + g_strlcat(str, tmp, sizeof(str)); + } + + return g_strdup(str); +} + +static void load_manufacturer_data_2digit(char *data, int len, char *buf) +{ + int i; + char **split; + + split = g_strsplit(data, " ", 0); + + for (i = 0; i < len; i++) { + if (split[i] == NULL) + break; + + buf[i] = (char)g_ascii_strtoull(split[i], NULL, 10); + } + + g_strfreev(split); + + return; +} +#endif + static gboolean store_device_info_cb(gpointer user_data) { struct btd_device *device = user_data; @@ -364,11 +497,19 @@ static gboolean store_device_info_cb(gpointer user_data) char class[9]; char **uuids = NULL; gsize length = 0; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + gboolean svc_change_regd = false; +#endif device->store_id = 0; ba2str(btd_adapter_get_address(device->adapter), adapter_addr); ba2str(&device->bdaddr, device_addr); + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (device->rpa) + ba2str(device->rpa, device_addr); +#endif snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info", adapter_addr, device_addr); @@ -397,11 +538,36 @@ static gboolean store_device_info_cb(gpointer user_data) g_key_file_remove_key(key_file, "General", "Appearance", NULL); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (device->rpa_res_support) { + g_key_file_set_integer(key_file, "General", "RPAResSupport", + device->rpa_res_support); + } else { + g_key_file_remove_key(key_file, "General", "RPAResSupport", NULL); + } +#endif + update_technologies(key_file, device); g_key_file_set_boolean(key_file, "General", "Trusted", device->trusted); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + struct trusted_profile_t trust_profile = device->trusted_profiles; + int trusted_profiles = (trust_profile.pbap << PBAP_SHIFT_OFFSET) | + (trust_profile.map << MAP_SHIFT_OFFSET) | + (trust_profile.sap << SAP_SHIFT_OFFSET); + DBG("Storing TrustedProfiles %d", trusted_profiles); + g_key_file_set_integer(key_file, "General", "TrustedProfiles", + trusted_profiles); + + struct restricted_profile_t restrict_profile = device->restricted_profiles; + int restricted_profiles = (restrict_profile.hfp_hs << HFP_HS_SHIFT_OFFSET) | + (restrict_profile.a2dp << A2DP_SHIFT_OFFSET); + DBG("Storing RestrictedProfiles %d", restricted_profiles); + g_key_file_set_integer(key_file, "General", "RestrictedProfiles", + restricted_profiles); +#endif g_key_file_set_boolean(key_file, "General", "Blocked", device->blocked); @@ -418,6 +584,43 @@ static gboolean store_device_info_cb(gpointer user_data) g_key_file_remove_key(key_file, "General", "Services", NULL); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (device->le_adv_data.flags) { + g_key_file_set_integer(key_file, "General", "Flags", + device->le_adv_data.flags); + } else { + g_key_file_remove_key(key_file, "General", "Flags", NULL); + } + + if (device->manufacturer_data) { + str = manufacturer_data2str(device->manufacturer_data, + device->manufacturer_data_len); + g_key_file_set_string(key_file, "General", + "ManufacturerData", + str); + g_free(str); + g_key_file_set_integer(key_file, "General", + "ManufacturerDataLen", + device->manufacturer_data_len); + } else { + g_key_file_remove_key(key_file, "General", + "ManufacturerData", NULL); + g_key_file_remove_key(key_file, "General", + "ManufacturerDataLen", NULL); + } + + if (device->rpa) { + char irk_addr[18]; + + ba2str(&device->bdaddr, irk_addr); + g_key_file_set_string(key_file, "General", "IdentityAddress", + irk_addr); + } else { + g_key_file_remove_key(key_file, "General", "IdentityAddress", + NULL); + } +#endif + if (device->vendor_src) { g_key_file_set_integer(key_file, "DeviceID", "Source", device->vendor_src); @@ -431,6 +634,12 @@ static gboolean store_device_info_cb(gpointer user_data) g_key_file_remove_group(key_file, "DeviceID", NULL); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + svc_change_regd = bt_att_get_svc_changed_indication_registered(device->att); + g_key_file_set_boolean(key_file, "Att", "SvcChangeRegd", + svc_change_regd); +#endif + if (device->local_csrk) store_csrk(device->local_csrk, key_file, "LocalSignatureKey"); @@ -493,6 +702,12 @@ void device_store_cached_name(struct btd_device *dev, const char *name) ba2str(btd_adapter_get_address(dev->adapter), s_addr); ba2str(&dev->bdaddr, d_addr); + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (dev->rpa) + ba2str(dev->rpa, d_addr); +#endif + snprintf(filename, PATH_MAX, STORAGEDIR "/%s/cache/%s", s_addr, d_addr); create_file(filename, S_IRUSR | S_IWUSR); @@ -509,6 +724,9 @@ void device_store_cached_name(struct btd_device *dev, const char *name) static void browse_request_free(struct browse_req *req) { +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + DBG(""); +#endif if (req->listener_id) g_dbus_remove_watch(dbus_conn, req->listener_id); if (req->msg) @@ -644,6 +862,9 @@ static void device_free(gpointer user_data) if (device->eir_uuids) g_slist_free_full(device->eir_uuids, g_free); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + g_free(device->rpa); +#endif g_free(device->local_csrk); g_free(device->remote_csrk); g_free(device->path); @@ -652,6 +873,18 @@ static void device_free(gpointer user_data) g_free(device); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +void device_set_remote_feature_flag(struct btd_device *device, int flags) +{ + device->remote_feature_flags = flags; +} + +gboolean device_is_bredrle(struct btd_device *device) +{ + return (device->remote_feature_flags & (EIR_CONTROLLER | EIR_SIM_HOST)); +} +#endif + bool device_is_paired(struct btd_device *device, uint8_t bdaddr_type) { struct bearer_state *state = get_state(device, bdaddr_type); @@ -671,6 +904,53 @@ gboolean device_is_trusted(struct btd_device *device) return device->trusted; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +gboolean device_is_profile_trusted(struct btd_device *device, + const char *uuid) +{ + if (g_strcmp0(uuid, OBEX_PSE_UUID) == 0) { + if (device->trusted_profiles.pbap == SUPPORTED_TRUSTED) + return TRUE; + } else if (g_strcmp0(uuid, OBEX_MAS_UUID) == 0) { + if (device->trusted_profiles.map == SUPPORTED_TRUSTED) + return TRUE; + } else if (g_strcmp0(uuid, SAP_UUID) == 0) { + if (device->trusted_profiles.sap == SUPPORTED_TRUSTED) + return TRUE; + } + return FALSE; +} + +gboolean device_is_profile_restricted(struct btd_device *device, + const char *uuid) +{ + if (g_strcmp0(uuid, HFP_HS_UUID) == 0) { + if (device->restricted_profiles.hfp_hs == CONNECTION_RESTRICTED) + return TRUE; + } else if (g_strcmp0(uuid, A2DP_SINK_UUID) == 0) { + if (device->restricted_profiles.a2dp == CONNECTION_RESTRICTED) + return TRUE; + } + return FALSE; +} + +gboolean device_is_profile_blocked(struct btd_device *device, + const char *uuid) +{ + if (g_strcmp0(uuid, OBEX_PSE_UUID) == 0) { + if (device->trusted_profiles.pbap == SUPPORTED_BLOCKED) + return TRUE; + } else if (g_strcmp0(uuid, OBEX_MAS_UUID) == 0) { + if (device->trusted_profiles.map == SUPPORTED_BLOCKED) + return TRUE; + } else if (g_strcmp0(uuid, SAP_UUID) == 0) { + if (device->trusted_profiles.sap == SUPPORTED_BLOCKED) + return TRUE; + } + return FALSE; +} +#endif + static gboolean dev_property_get_address(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { @@ -678,7 +958,12 @@ static gboolean dev_property_get_address(const GDBusPropertyTable *property, char dstaddr[18]; const char *ptr = dstaddr; - ba2str(&device->bdaddr, dstaddr); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (device->rpa) + ba2str(device->rpa, dstaddr); + else +#endif + ba2str(&device->bdaddr, dstaddr); dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &ptr); return TRUE; @@ -717,7 +1002,9 @@ static gboolean dev_property_get_alias(const GDBusPropertyTable *property, ptr = device->name; } else { ba2str(&device->bdaddr, dstaddr); +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY g_strdelimit(dstaddr, ":", '-'); +#endif ptr = dstaddr; } @@ -726,6 +1013,24 @@ static gboolean dev_property_get_alias(const GDBusPropertyTable *property, return TRUE; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static gboolean dev_property_get_alias_set(const GDBusPropertyTable *property, + DBusMessageIter *iter, void *data) +{ + struct btd_device *device = data; + dbus_bool_t val; + + if (device->alias != NULL) + val = TRUE; + else + val = FALSE; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &val); + + return TRUE; +} +#endif + static void set_alias(GDBusPendingPropertySet id, const char *alias, void *data) { @@ -1005,6 +1310,40 @@ static void dev_property_set_trusted(const GDBusPropertyTable *property, set_trust(id, b, data); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static gboolean dev_property_get_trusted_profiles(const GDBusPropertyTable *property, + DBusMessageIter *iter, void *data) +{ + struct btd_device *device = data; + uint32_t pbap = device->trusted_profiles.pbap; + uint32_t map = device->trusted_profiles.map; + uint32_t sap = device->trusted_profiles.sap; + + unsigned int val = (pbap << PBAP_SHIFT_OFFSET) | + (map << MAP_SHIFT_OFFSET) | + (sap << SAP_SHIFT_OFFSET); + + dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &val); + + return TRUE; +} + +static gboolean dev_property_get_restricted_profiles(const GDBusPropertyTable *property, + DBusMessageIter *iter, void *data) +{ + struct btd_device *device = data; + uint32_t hfp_hs = device->restricted_profiles.hfp_hs; + uint32_t a2dp = device->restricted_profiles.a2dp; + + unsigned int val = (hfp_hs << HFP_HS_SHIFT_OFFSET) | + (a2dp << A2DP_SHIFT_OFFSET); + + dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &val); + + return TRUE; +} +#endif + static gboolean dev_property_get_blocked(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { @@ -1060,10 +1399,112 @@ static void dev_property_set_blocked(const GDBusPropertyTable *property, set_blocked(id, b, data); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static uint8_t device_get_connected_state(struct btd_device *device) +{ + if (device->bredr_state.connected && device->le_state.connected) + return DEV_CONNECTED_BREDR_LE; + else if (device->bredr_state.connected) + return DEV_CONNECTED_BREDR; + else if (device->le_state.connected) + return DEV_CONNECTED_LE; + else + return DEV_CONNECTED_NONE; +} + +static gboolean dev_property_get_payload(const GDBusPropertyTable *property, + DBusMessageIter *iter, void *data) +{ + struct btd_device *dev = data; + dbus_uint16_t payload_timeout = dev->auth_payload_timeout; + + dbus_message_iter_append_basic(iter, + DBUS_TYPE_UINT16, &payload_timeout); + + return TRUE; +} + +static gboolean dev_property_get_last_addr_type(const GDBusPropertyTable *property, + DBusMessageIter *iter, void *data) +{ + struct btd_device *dev = data; + uint8_t last_addr_type = dev->last_bdaddr_type; + + dbus_message_iter_append_basic(iter, + DBUS_TYPE_BYTE, &last_addr_type); + + return TRUE; +} + +static gboolean dev_property_get_att_mtu(const GDBusPropertyTable *property, + DBusMessageIter *iter, void *data) +{ + struct btd_device *device = data; + dbus_uint16_t mtu = bt_gatt_client_get_mtu(device->client); + + dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &mtu); + + return TRUE; +} + +static gboolean dev_property_get_gatt_connected(const GDBusPropertyTable *property, + DBusMessageIter *iter, void *data) +{ + struct btd_device *device = data; + dbus_bool_t gatt_connected; + + if (device->gatt_connected) + gatt_connected = TRUE; + else + gatt_connected = FALSE; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, + &gatt_connected); + + return TRUE; +} + +static gboolean dev_property_get_ipsp_conn_state(const GDBusPropertyTable *property, + DBusMessageIter *iter, void *data) +{ + struct btd_device *dev = data; + dbus_bool_t ipsp_connected; + + if (dev->ipsp_connected) + ipsp_connected = TRUE; + else + ipsp_connected = FALSE; + + dbus_message_iter_append_basic(iter, + DBUS_TYPE_BOOLEAN, &ipsp_connected); + + return TRUE; +} + +static gboolean dev_property_get_ipsp_conn_bt_iface_name(const GDBusPropertyTable *property, + DBusMessageIter *iter, void *data) +{ + struct btd_device *dev = data; + const char *ptr = g_strdup(dev->if_name); + + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &ptr); + + g_free(ptr); + + return TRUE; +} +#endif + static gboolean dev_property_get_connected(const GDBusPropertyTable *property, DBusMessageIter *iter, void *data) { struct btd_device *dev = data; + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + uint8_t connected = device_get_connected_state(dev); + + dbus_message_iter_append_basic(iter, DBUS_TYPE_BYTE, &connected); +#else dbus_bool_t connected; if (dev->bredr_state.connected || dev->le_state.connected) @@ -1072,6 +1513,7 @@ static gboolean dev_property_get_connected(const GDBusPropertyTable *property, connected = FALSE; dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &connected); +#endif return TRUE; } @@ -1135,6 +1577,48 @@ static gboolean dev_property_get_adapter(const GDBusPropertyTable *property, return TRUE; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static gboolean property_get_manufacturer_data_len(const GDBusPropertyTable *property, + DBusMessageIter *iter, void *user_data) +{ + struct btd_device *device = user_data; + dbus_uint16_t val = device->manufacturer_data_len; + + dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &val); + + return TRUE; +} + +static gboolean property_get_manufacturer_data(const GDBusPropertyTable *property, + DBusMessageIter *iter, void *user_data) +{ + struct btd_device *device = user_data; + char str[DEV_MAX_MANUFACTURER_DATA_LEN] = {0}; + DBusMessageIter array; + + memset(str, 0, DEV_MAX_MANUFACTURER_DATA_LEN); + if (device->manufacturer_data_len) + memcpy(str, device->manufacturer_data, + device->manufacturer_data_len); + + dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, + DBUS_TYPE_BYTE_AS_STRING, &array); + + dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE, + &device->manufacturer_data, + device->manufacturer_data_len); + + dbus_message_iter_close_container(iter, &array); + + return TRUE; +} + +gboolean device_get_gatt_connected(const struct btd_device *device) +{ + return device->gatt_connected; +} +#endif + static void append_manufacturer_data(void *data, void *user_data) { struct bt_ad_manufacturer_data *md = data; @@ -1483,8 +1967,10 @@ static void device_profile_connected(struct btd_device *dev, if (!err) btd_device_set_temporary(dev, false); +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY if (dev->pending == NULL) goto done; +#endif if (!btd_device_is_connected(dev)) { switch (-err) { @@ -1495,6 +1981,10 @@ static void device_profile_connected(struct btd_device *dev, } } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (dev->pending == NULL) + return; +#endif pending = dev->pending->data; l = find_service_with_profile(dev->pending, profile); @@ -1530,10 +2020,28 @@ done: g_dbus_send_message(dbus_conn, btd_error_failed(dev->connect, strerror(-err))); else { +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + /* SDP is not required for Samsung TV Power on */ + if (g_strcmp0(profile->name, "hid-device") == 0) { + DBG("Skip SDP discovery."); + } else { +#endif /* Start passive SDP discovery to update known services */ if (dev->bredr && !dev->svc_refreshed) device_browse_sdp(dev, NULL); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + } +#endif + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (err) + g_dbus_send_message(dbus_conn, + btd_error_failed(dev->connect, strerror(-err))); + else + g_dbus_send_reply(dbus_conn, dev->connect, DBUS_TYPE_INVALID); +#else g_dbus_send_reply(dbus_conn, dev->connect, DBUS_TYPE_INVALID); +#endif } dbus_message_unref(dev->connect); @@ -1604,7 +2112,9 @@ static struct btd_service *find_connectable_service(struct btd_device *dev, const char *uuid) { GSList *l; - +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + struct btd_service *s = NULL; +#endif for (l = dev->services; l != NULL; l = g_slist_next(l)) { struct btd_service *service = l->data; struct btd_profile *p = btd_service_get_profile(service); @@ -1612,9 +2122,24 @@ static struct btd_service *find_connectable_service(struct btd_device *dev, if (!p->connect || !p->remote_uuid) continue; +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY if (strcasecmp(uuid, p->remote_uuid) == 0) return service; +#else + if (strcasecmp(uuid, p->remote_uuid) == 0) { + s = service; + if (ext_profile_is_registered_as_client_role(p) == TRUE) { + return service; + } else { + continue; + } + } +#endif } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (s) + return s; +#endif return NULL; } @@ -1632,12 +2157,29 @@ static GSList *create_pending_list(struct btd_device *dev, const char *uuid) struct btd_service *service; struct btd_profile *p; GSList *l; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + bool hs_hf_verify = FALSE; +#endif if (uuid) { service = find_connectable_service(dev, uuid); if (service) return g_slist_prepend(dev->pending, service); - +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + else if ((service == NULL) && + (g_strcmp0(uuid, HFP_HS_UUID) == 0)) { + DBG("HFP service not found check for HSP service"); + service = find_connectable_service(dev, HSP_HS_UUID); + if (service) + return g_slist_prepend(dev->pending, service); + } else if (g_strcmp0(uuid, HID_UUID) == 0) { + DBG("HID service not found, add HID service"); + btd_device_add_uuid(dev, uuid); + service = find_connectable_service(dev, HID_UUID); + if (service) + return g_slist_prepend(dev->pending, service); + } +#endif return dev->pending; } @@ -1645,6 +2187,28 @@ static GSList *create_pending_list(struct btd_device *dev, const char *uuid) service = l->data; p = btd_service_get_profile(service); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + DBG("profile uuid %s", p->remote_uuid); + if (g_strcmp0(p->remote_uuid, HSP_HS_UUID) == 0) { + DBG("HSP service is found check for HFP service"); + struct btd_service *service; + struct btd_profile *p; + GSList *h; + + for (h = dev->services; h != NULL; h = g_slist_next(h)) { + service = h->data; + p = btd_service_get_profile(service); + + if (g_strcmp0(p->remote_uuid, HFP_HS_UUID) == 0) { + DBG("HFP found,ignore HSP "); + hs_hf_verify = TRUE; + break; + } + } + if (hs_hf_verify) + continue; + } +#endif if (!p->auto_connect) continue; @@ -1693,8 +2257,13 @@ static DBusMessage *connect_profiles(struct btd_device *dev, uint8_t bdaddr_type DBG("%s %s, client %s", dev->path, uuid ? uuid : "(all)", dbus_message_get_sender(msg)); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (dev->pending || dev->connect) + return btd_error_in_progress(msg); +#else if (dev->pending || dev->connect || dev->browse) return btd_error_in_progress(msg); +#endif if (!btd_adapter_get_powered(dev->adapter)) return btd_error_not_ready(msg); @@ -1707,8 +2276,13 @@ static DBusMessage *connect_profiles(struct btd_device *dev, uint8_t bdaddr_type dev->pending = create_pending_list(dev, uuid); if (!dev->pending) { if (dev->svc_refreshed) { +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (!uuid && find_service_with_state(dev->services, + BTD_SERVICE_STATE_CONNECTED)) +#else if (find_service_with_state(dev->services, BTD_SERVICE_STATE_CONNECTED)) +#endif return dbus_message_new_method_return(msg); else return btd_error_not_available(msg); @@ -1892,6 +2466,12 @@ static DBusMessage *disconnect_profile(DBusConnection *conn, DBusMessage *msg, return btd_error_invalid_args(msg); service = find_connectable_service(dev, uuid); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if ((service == NULL) && (g_strcmp0(uuid, HFP_HS_UUID) == 0)) { + DBG("HFP service is not found check for HSP service"); + service = find_connectable_service(dev, HSP_HS_UUID); + } +#endif free(uuid); if (!service) @@ -1906,6 +2486,9 @@ static DBusMessage *disconnect_profile(DBusConnection *conn, DBusMessage *msg, if (err == 0) return NULL; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (dev->disconnect) +#endif dbus_message_unref(dev->disconnect); dev->disconnect = NULL; @@ -1941,6 +2524,11 @@ static void store_services(struct btd_device *device) ba2str(btd_adapter_get_address(adapter), src_addr); ba2str(&device->bdaddr, dst_addr); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (device->rpa) + ba2str(device->rpa, dst_addr); +#endif + snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/attributes", src_addr, dst_addr); key_file = g_key_file_new(); @@ -2211,9 +2799,18 @@ static void device_svc_resolved(struct btd_device *dev, uint8_t bdaddr_type, struct bearer_state *state = get_state(dev, bdaddr_type); struct browse_req *req = dev->browse; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + DBG("%s bdaddr_type %d err %d", dev->path, bdaddr_type, err); +#else DBG("%s err %d", dev->path, err); +#endif +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY state->svc_resolved = true; +#else + if (err == 0) + state->svc_resolved = true; +#endif /* Disconnection notification can happen before this function * gets called, so don't set svc_refreshed for a disconnected @@ -2253,6 +2850,14 @@ static void device_svc_resolved(struct btd_device *dev, uint8_t bdaddr_type, if (!req) return; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + /* If bdaddr_type is LE but req is for SDP, don't complete browse req. */ + if (bdaddr_type != BDADDR_BREDR && req->search_uuid) { + DBG("Discover comp. is for LE but browse req. is for SDP."); + return; + } +#endif + dev->browse = NULL; browse_request_complete(req, bdaddr_type, err); } @@ -2376,22 +2981,52 @@ static DBusMessage *pair_device(DBusConnection *conn, DBusMessage *msg, { struct btd_device *device = data; struct btd_adapter *adapter = device->adapter; +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY struct bearer_state *state; +#endif uint8_t bdaddr_type; const char *sender; struct agent *agent; struct bonding_req *bonding; uint8_t io_cap; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + uint8_t conn_type; + bool connect_le = FALSE; +#endif int err; btd_device_set_temporary(device, false); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (dbus_message_get_args(msg, NULL, DBUS_TYPE_BYTE, &conn_type, + DBUS_TYPE_INVALID) == FALSE) +#else if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_INVALID)) +#endif return btd_error_invalid_args(msg); if (device->bonding) return btd_error_in_progress(msg); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (conn_type == DEV_CONN_DEFAULT) { + if (device_is_bonded(device, DEV_CONN_BREDR)) + return btd_error_already_exists(msg); + else if (device_is_bonded(device, DEV_CONN_LE)) + return btd_error_already_exists(msg); + + if (device->bredr) + conn_type = DEV_CONN_BREDR; + else if (device->le) + conn_type = DEV_CONN_LE; + else + conn_type = DEV_CONN_BREDR; + } else { + if (device_is_bonded(device, conn_type)) + return btd_error_already_exists(msg); + } + bdaddr_type = device->bdaddr_type; +#else if (device->bredr_state.bonded) bdaddr_type = device->bdaddr_type; else if (device->le_state.bonded) @@ -2403,6 +3038,15 @@ static DBusMessage *pair_device(DBusConnection *conn, DBusMessage *msg, if (state->bonded) return btd_error_already_exists(msg); +#endif + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (conn_type == DEV_CONN_LE && + (device_is_bredrle(device) || bdaddr_type != BDADDR_BREDR)) { + DBG("LE Connect request"); + connect_le = TRUE; + } +#endif sender = dbus_message_get_sender(msg); @@ -2412,7 +3056,15 @@ static DBusMessage *pair_device(DBusConnection *conn, DBusMessage *msg, else io_cap = IO_CAPABILITY_NOINPUTNOOUTPUT; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if ((conn_type == DEV_CONN_LE && bdaddr_type != BDADDR_BREDR) || + connect_le) + bonding = bonding_request_new(msg, device, bdaddr_type, agent); + else + bonding = bonding_request_new(msg, device, BDADDR_BREDR, agent); +#else bonding = bonding_request_new(msg, device, bdaddr_type, agent); +#endif if (agent) agent_unref(agent); @@ -2429,6 +3081,17 @@ static DBusMessage *pair_device(DBusConnection *conn, DBusMessage *msg, * channel first and only then start pairing (there's code for * this in the ATT connect callback) */ +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (((conn_type == DEV_CONN_LE && bdaddr_type != BDADDR_BREDR) || + (connect_le)) && !device->le_state.connected) + err = device_connect_le(device); + else if (connect_le) /* Send bonding request if LE is already connected*/ + err = adapter_create_bonding(adapter, &device->bdaddr, + bdaddr_type, io_cap); + else + err = adapter_create_bonding(adapter, &device->bdaddr, + BDADDR_BREDR, io_cap); +#else if (bdaddr_type != BDADDR_BREDR) { if (!state->connected && btd_le_connect_before_pairing()) err = device_connect_le(device); @@ -2440,6 +3103,7 @@ static DBusMessage *pair_device(DBusConnection *conn, DBusMessage *msg, err = adapter_create_bonding(adapter, &device->bdaddr, BDADDR_BREDR, io_cap); } +#endif if (err < 0) { bonding_request_free(device->bonding); @@ -2470,7 +3134,9 @@ static DBusMessage *new_authentication_return(DBusMessage *msg, uint8_t status) "Authentication Rejected"); case MGMT_STATUS_CANCELLED: case MGMT_STATUS_NO_RESOURCES: +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY case MGMT_STATUS_DISCONNECTED: +#endif return dbus_message_new_error(msg, ERROR_INTERFACE ".AuthenticationCanceled", "Authentication Canceled"); @@ -2500,28 +3166,665 @@ static void device_cancel_bonding(struct btd_device *device, uint8_t status) 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) }, @@ -2530,8 +3833,45 @@ static const GDBusMethodTable device_methods[] = { NULL, connect_profile) }, { GDBUS_ASYNC_METHOD("DisconnectProfile", GDBUS_ARGS({ "UUID", "s" }), NULL, disconnect_profile) }, +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + { GDBUS_ASYNC_METHOD("Pair", GDBUS_ARGS({ "conn_type", "y" }), NULL, + pair_device) }, +#else { GDBUS_ASYNC_METHOD("Pair", NULL, NULL, pair_device) }, +#endif { GDBUS_METHOD("CancelPairing", NULL, NULL, cancel_pairing) }, +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + { GDBUS_ASYNC_METHOD("ConnectLE", GDBUS_ARGS({ "auto_connect", "b"}), + NULL, connect_le) }, + { GDBUS_ASYNC_METHOD("DisconnectLE", NULL, NULL, disconnect_le) }, + { GDBUS_METHOD("IsConnectedProfile", GDBUS_ARGS({ "UUID", "s" }), + GDBUS_ARGS({ "IsConnected", "b" }), + is_connected_profile)}, + { GDBUS_METHOD("LeConnUpdate", GDBUS_ARGS({ "interval_min", "u" }, + { "interval_max", "u" }, { "latency", "u" }, + { "time_out", "u" }), NULL, + update_le_conn_parm) }, + { GDBUS_ASYNC_METHOD("DiscoverServices", GDBUS_ARGS({ "pattern", "s" }), + NULL, discover_services) }, + { GDBUS_METHOD("CancelDiscovery", NULL, NULL, cancel_discover) }, + { GDBUS_ASYNC_METHOD("ConnectIpsp", NULL, NULL, connect_ipsp) }, + { GDBUS_ASYNC_METHOD("DisconnectIpsp", NULL, NULL, disconnect_ipsp) }, + { GDBUS_ASYNC_METHOD("LESetDataLength", + GDBUS_ARGS({"max_tx_octets", "q" }, + { "max_tx_time", "q" }), NULL, + le_set_data_length)}, + { GDBUS_ASYNC_METHOD("RequestAttMtu", GDBUS_ARGS({ "mtu", "q" }), + GDBUS_ARGS({ "mtu", "q" }, { "status", "y"}), + request_att_mtu) }, + { GDBUS_METHOD("GetIDAddress", NULL, GDBUS_ARGS({ "IDAdress", "s" }), + device_get_ida) }, + { GDBUS_METHOD("SetTrustedProfile", + GDBUS_ARGS({ "uuid", "s"}, { "trusted", "b"}), NULL, + set_trusted_profile) }, + { GDBUS_METHOD("SetRestrictedProfile", + GDBUS_ARGS({ "uuid", "s"}, { "restricted", "b"}), NULL, + set_restricted_profile) }, +#endif { } }; @@ -2550,11 +3890,30 @@ static const GDBusPropertyTable device_properties[] = { { "Blocked", "b", dev_property_get_blocked, dev_property_set_blocked }, { "LegacyPairing", "b", dev_property_get_legacy }, { "RSSI", "n", dev_property_get_rssi, NULL, dev_property_exists_rssi }, +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + {"IsAliasSet", "b", dev_property_get_alias_set }, + { "Connected", "y", dev_property_get_connected }, +#else { "Connected", "b", dev_property_get_connected }, +#endif { "UUIDs", "as", dev_property_get_uuids }, { "Modalias", "s", dev_property_get_modalias, NULL, dev_property_exists_modalias }, { "Adapter", "o", dev_property_get_adapter }, +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + /* To handle Failed Legacy Pairing when initiated from Remote device*/ + { "LegacyPaired", "b", dev_property_get_paired }, + { "ManufacturerDataLen", "q", property_get_manufacturer_data_len }, + { "ManufacturerData", "ay", property_get_manufacturer_data }, + { "GattConnected", "b", dev_property_get_gatt_connected }, + { "PayloadTimeout", "q", dev_property_get_payload}, + { "LastAddrType", "y", dev_property_get_last_addr_type}, + { "IpspConnected", "b", dev_property_get_ipsp_conn_state }, + { "IpspBtInterfaceInfo", "s", dev_property_get_ipsp_conn_bt_iface_name }, + { "AttMtu", "q", dev_property_get_att_mtu }, + { "TrustedProfiles", "u", dev_property_get_trusted_profiles}, + { "RestrictedProfiles", "u", dev_property_get_restricted_profiles}, +#endif { "ManufacturerData", "a{qv}", dev_property_get_manufacturer_data, NULL, dev_property_manufacturer_data_exist }, { "ServiceData", "a{sv}", dev_property_get_service_data, @@ -2569,6 +3928,28 @@ static const GDBusPropertyTable device_properties[] = { { } }; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static const GDBusSignalTable device_signals[] = { + { GDBUS_SIGNAL("Disconnected", + GDBUS_ARGS({ "bdaddr_type", "y" }, { "reason", "y" }, + { "name", "s" })) }, + { GDBUS_SIGNAL("DeviceConnected", GDBUS_ARGS({ "bdaddr_type", "y"})) }, + { GDBUS_SIGNAL("ProfileStateChanged", + GDBUS_ARGS({ "profile", "s"}, {"state", "i"})) }, + { GDBUS_SIGNAL("AdvReport", + GDBUS_ARGS({"Address","s"}, { "Address Type", "y" }, + { "Adv Type", "y"}, { "RSSI", "i"}, + { "AdvDataLen", "i"}, { "AdvData", "ay"})) }, + { GDBUS_SIGNAL("LEDataLengthChanged", + GDBUS_ARGS({"max_tx_octets","q"}, + { "max_tx_time", "q" }, + { "max_rx_octets", "q"}, + { "max_rx_time", "q"})) }, + { GDBUS_SIGNAL("IpspStateChanged", + GDBUS_ARGS({"connected","b"},{"if_name","s"}))}, +}; +#endif + uint8_t btd_device_get_bdaddr_type(struct btd_device *dev) { return dev->bdaddr_type; @@ -2600,16 +3981,38 @@ void device_add_connection(struct btd_device *dev, uint8_t bdaddr_type) state->connected = true; +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY if (dev->le_state.connected && dev->bredr_state.connected) return; g_dbus_emit_property_changed(dbus_conn, dev->path, DEVICE_INTERFACE, "Connected"); +#else +#ifdef TIZEN_FEATURE_BLUEZ_BATTERY_WATCH + if (bdaddr_type == BDADDR_BREDR && + get_charging_state(dev->adapter) == WIRELESS_CHARGING) { + int br_pkt_type = ACL_PTYPE_MASK | + HCI_2DH1 | HCI_2DH3 | HCI_2DH5 | + HCI_3DH1 | HCI_3DH3 | HCI_3DH5; + + DBG("During wireless charging... Change packet type"); + device_change_pkt_type(dev, (gpointer)br_pkt_type); + } +#endif /* TIZEN_FEATURE_BLUEZ_BATTERY_WATCH */ + + g_dbus_emit_signal(dbus_conn, dev->path, + DEVICE_INTERFACE, "DeviceConnected", + DBUS_TYPE_BYTE, &bdaddr_type, + DBUS_TYPE_INVALID); +#endif } void device_remove_connection(struct btd_device *device, uint8_t bdaddr_type) { struct bearer_state *state = get_state(device, bdaddr_type); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + char *dev_name = device->name; +#endif if (!state->connected) return; @@ -2624,6 +4027,18 @@ void device_remove_connection(struct btd_device *device, uint8_t bdaddr_type) device->disconn_timer = 0; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (device->browse) { + struct browse_req *req = device->browse; + + if ((bdaddr_type == BDADDR_BREDR && req->search_uuid != 0) || + (bdaddr_type != BDADDR_BREDR && req->search_uuid == 0)) + device->browse = NULL; + else + DBG("device->browse is for other link"); + } +#endif + while (device->disconnects) { DBusMessage *msg = device->disconnects->data; @@ -2636,11 +4051,20 @@ void device_remove_connection(struct btd_device *device, uint8_t bdaddr_type) btd_adapter_remove_bonding(device->adapter, &device->bdaddr, bdaddr_type); +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY if (device->bredr_state.connected || device->le_state.connected) return; g_dbus_emit_property_changed(dbus_conn, device->path, DEVICE_INTERFACE, "Connected"); +#else + g_dbus_emit_signal(dbus_conn, device->path, + DEVICE_INTERFACE, "Disconnected", + DBUS_TYPE_BYTE, &bdaddr_type, + DBUS_TYPE_BYTE, &device->disc_reason, + DBUS_TYPE_STRING, &dev_name, + DBUS_TYPE_INVALID); +#endif } guint device_add_disconnect_watch(struct btd_device *device, @@ -2808,7 +4232,10 @@ static void load_info(struct btd_device *device, const char *local, char **uuids; int source, vendor, product, version; char **techno, **t; - +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + gboolean svc_change_regd; + char buf[DEV_MAX_MANUFACTURER_DATA_LEN] = { 0, }; +#endif /* Load device name from storage info file, if that fails fall back to * the cache. */ @@ -2845,6 +4272,37 @@ static void load_info(struct btd_device *device, const char *local, g_free(str); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + /* Load RPA Resolution Support value */ + device->rpa_res_support = g_key_file_get_integer(key_file, + "General", "RPAResSupport", NULL); + + str = g_key_file_get_string(key_file, "General", "ManufacturerDataLen", NULL); + if (str) { + device->manufacturer_data_len = strtol(str, NULL, 10); + g_free(str); + + str = g_key_file_get_string(key_file, "General", "ManufacturerData", NULL); + if (str) { + load_manufacturer_data_2digit(str, + device->manufacturer_data_len, buf); + device->manufacturer_data = g_memdup(buf, + device->manufacturer_data_len); + g_free(str); + } + } + + str = g_key_file_get_string(key_file, "General", "IdentityAddress", + NULL); + + if (str) { + device->rpa = g_malloc0(sizeof(bdaddr_t)); + bacpy(device->rpa, &device->bdaddr); + str2ba(str, &device->bdaddr); + g_free(str); + } +#endif + /* Load device technology */ techno = g_key_file_get_string_list(key_file, "General", "SupportedTechnologies", NULL, NULL); @@ -2886,6 +4344,27 @@ next: device->trusted = g_key_file_get_boolean(key_file, "General", "Trusted", NULL); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + /* Load Trusted Profiles*/ + int trusted_profiles = g_key_file_get_integer(key_file, "General", + "TrustedProfiles", NULL); + DBG("Loading TrustedProfiles %d", trusted_profiles); + device->trusted_profiles.pbap = ((trusted_profiles & + (PROFILE_SUPPORTED << PBAP_SHIFT_OFFSET)) >> PBAP_SHIFT_OFFSET); + device->trusted_profiles.map = ((trusted_profiles & + (PROFILE_SUPPORTED << MAP_SHIFT_OFFSET)) >> MAP_SHIFT_OFFSET); + device->trusted_profiles.sap = ((trusted_profiles & + (PROFILE_SUPPORTED << SAP_SHIFT_OFFSET)) >> SAP_SHIFT_OFFSET); + + /* Load Restricted Profiles*/ + int restricted_profiles = g_key_file_get_integer(key_file, "General", + "RestrictedProfiles", NULL); + DBG("Loading RestrictedProfiles %d", restricted_profiles); + device->restricted_profiles.hfp_hs = (restricted_profiles >> HFP_HS_SHIFT_OFFSET) & 0x01; + device->restricted_profiles.a2dp = (restricted_profiles >> A2DP_SHIFT_OFFSET) & 0x01; + +#endif + /* Load device blocked */ blocked = g_key_file_get_boolean(key_file, "General", "Blocked", NULL); if (blocked) @@ -2916,6 +4395,15 @@ next: btd_device_set_pnpid(device, source, vendor, product, version); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + /* Load Service changed Registered flag */ + svc_change_regd = g_key_file_get_boolean(key_file, "Att", + "SvcChangeRegd", NULL); + + bt_att_set_svc_changed_indication_registered(device->att, + svc_change_regd); +#endif + if (store_needed) store_device_info(device); } @@ -3342,8 +4830,13 @@ static bool device_match_profile(struct btd_device *device, return false; if (g_slist_find_custom(uuids, profile->remote_uuid, - bt_uuid_strcmp) == NULL) + bt_uuid_strcmp) == NULL) { +#ifdef TIZEN_BT_HID_DEVICE_ENABLE + if (strcmp(profile->name, "hid-device") == 0) + return true; +#endif return false; + } return true; } @@ -3404,12 +4897,35 @@ static void device_add_gatt_services(struct btd_device *device) gatt_db_foreach_service(device->db, NULL, add_gatt_service, device); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static void accept_gatt_service(struct gatt_db_attribute *attr, void *user_data) +{ + struct btd_device *device = user_data; + GSList *l; + bt_uuid_t uuid; + char uuid_str[MAX_LEN_UUID_STR]; + + gatt_db_attribute_get_service_uuid(attr, &uuid); + bt_uuid_to_string(&uuid, uuid_str, sizeof(uuid_str)); + + l = find_service_with_uuid(device->services, uuid_str); + if (!l) + return; + + service_accept(l->data); +} +#endif + static void device_accept_gatt_profiles(struct btd_device *device) { +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY GSList *l; for (l = device->services; l != NULL; l = g_slist_next(l)) service_accept(l->data); +#else + gatt_db_foreach_service(device->db, NULL, accept_gatt_service, device); +#endif } static void device_remove_gatt_service(struct btd_device *device, @@ -3608,7 +5124,11 @@ static struct btd_device *device_new(struct btd_adapter *adapter, if (g_dbus_register_interface(dbus_conn, device->path, DEVICE_INTERFACE, +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + device_methods, device_signals, +#else device_methods, NULL, +#endif device_properties, device, device_free) == FALSE) { error("Unable to register device interface for %s", address); @@ -3625,6 +5145,28 @@ static struct btd_device *device_new(struct btd_adapter *adapter, return btd_device_ref(device); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +void device_print_addr(struct btd_device *dev) +{ + char ida[18]; + char rpa[18]; + + ba2str(&dev->bdaddr, ida); + + if (dev->rpa) { + ba2str(dev->rpa, rpa); + + DBG("IDA %s [%d] : RPA [%s], BREDR [%d], LE [%d]", + ida, dev->bdaddr_type, rpa, + dev->bredr ? 1 : 0, dev->le ? 1 : 0); + } else { + DBG("ADDR %s [%d] : BREDR [%d], LE [%d]", + ida, dev->bdaddr_type, + dev->bredr ? 1 : 0, dev->le ? 1 : 0); + } +} +#endif + struct btd_device *device_create_from_storage(struct btd_adapter *adapter, const char *address, GKeyFile *key_file) { @@ -3697,6 +5239,11 @@ char *btd_device_get_storage_path(struct btd_device *device, ba2str(btd_adapter_get_address(device->adapter), srcaddr); ba2str(&device->bdaddr, dstaddr); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (device->rpa) + ba2str(device->rpa, dstaddr); +#endif + if (!filename) return g_strdup_printf(STORAGEDIR "/%s/%s", srcaddr, dstaddr); @@ -3778,6 +5325,23 @@ void device_update_addr(struct btd_device *device, const bdaddr_t *bdaddr, void device_set_bredr_support(struct btd_device *device) { +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + char addr_str[18]; + + if (device->rpa) { + ba2str(device->rpa, addr_str); + error("Cannot set bredr support to RPA device [%s]", addr_str); + return; + } + + if (device->bdaddr_type == BDADDR_LE_RANDOM) { + ba2str(&device->bdaddr, addr_str); + error("Cannot set bredr support to LE random device [%s]", + addr_str); + return; + } +#endif + if (device->bredr) return; @@ -3785,6 +5349,22 @@ void device_set_bredr_support(struct btd_device *device) store_device_info(device); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +void device_set_rpa(struct btd_device *device, const bdaddr_t *rpa) +{ + if (device->rpa == NULL) { + device->rpa = g_malloc0(sizeof(bdaddr_t)); + bacpy(device->rpa, rpa); + } else + error("RPA is already set"); +} + +void device_set_irk_value(struct btd_device *device, const uint8_t *val) +{ + memcpy(&device->irk_val, val, sizeof(device->irk_val)); +} +#endif + void device_set_le_support(struct btd_device *device, uint8_t bdaddr_type) { if (device->le) @@ -3925,6 +5505,11 @@ static void device_remove_stored(struct btd_device *device) ba2str(src, adapter_addr); ba2str(&device->bdaddr, device_addr); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (device->rpa) + ba2str(device->rpa, device_addr); +#endif + snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s", adapter_addr, device_addr); delete_folder_tree(filename); @@ -3946,6 +5531,102 @@ static void device_remove_stored(struct btd_device *device) g_key_file_free(key_file); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +void device_unpair(struct btd_device *device, gboolean remove_stored) +{ + DBG("+"); + DBG("Unpairing device %s", device->path); + + if (device->bonding) { + uint8_t status; + + if (device->bredr_state.connected) + status = MGMT_STATUS_DISCONNECTED; + else + status = MGMT_STATUS_CONNECT_FAILED; + + device_cancel_bonding(device, status); + } + + if (device->browse) + browse_request_cancel(device->browse); + + +// while (device->services != NULL) { +// struct btd_service *service = device->services->data; +// +// device->services = g_slist_remove(device->services, service); +// service_remove(service); +// } + + g_slist_free(device->pending); + device->pending = NULL; + + if (btd_device_is_connected(device)) + disconnect_all(device); + + if (device->store_id > 0) { + g_source_remove(device->store_id); + device->store_id = 0; + + if (!remove_stored) + store_device_info_cb(device); + } + + if (remove_stored) + device_remove_stored(device); + + gatt_db_clear(device->db); + + if (device->rpa) { + bacpy(&device->bdaddr, device->rpa); + device->bdaddr_type = BDADDR_LE_RANDOM; + + g_free(device->rpa); + device->rpa = NULL; + } + + device->bredr_state.paired = 0; + device->le_state.paired = 0; + device->bredr_state.svc_resolved = false; + device->trusted = false; + device->trusted_profiles.pbap = SHOW_AUTHORIZATION; + device->trusted_profiles.map = SHOW_AUTHORIZATION; + device->trusted_profiles.sap = SHOW_AUTHORIZATION; + if (device->alias != NULL) { + /* Remove alias name because + * In UG if we rename and then unpair device and + * initiates connection without scanning then paired + * list will have alias name as first preference is + * given to alias name. + */ + DBG("Freeing device alias name"); + g_free(device->alias); + device->alias = NULL; + } + g_dbus_emit_property_changed(dbus_conn, device->path, + DEVICE_INTERFACE, "Paired"); +// btd_device_unref(device); + DBG("-"); + } + +void device_remove_stored_folder(struct btd_device *device) +{ + const bdaddr_t *src = btd_adapter_get_address(device->adapter); + char adapter_addr[18]; + char device_addr[18]; + char filename[PATH_MAX]; + + ba2str(src, adapter_addr); + ba2str(&device->bdaddr, device_addr); + + snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s", adapter_addr, + device_addr); + + delete_folder_tree(filename); +} +#endif + void device_remove(struct btd_device *device, gboolean remove_stored) { DBG("Removing device %s", device->path); @@ -3993,6 +5674,30 @@ void device_remove(struct btd_device *device, gboolean remove_stored) btd_device_unref(device); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +int device_rpa_cmp(gconstpointer a, gconstpointer b) +{ + const struct btd_device *device = a; + const char *address = b; + char addr[18]; + + if (!device->rpa) + return -1; + + ba2str(device->rpa, addr); + + return strcasecmp(addr, address); +} + +int device_addr_cmp(gconstpointer a, gconstpointer b) +{ + const struct btd_device *device = a; + const bdaddr_t *bdaddr = b; + + return bacmp(&device->bdaddr, bdaddr); +} +#endif + int device_address_cmp(gconstpointer a, gconstpointer b) { const struct btd_device *device = a; @@ -4019,6 +5724,36 @@ static bool addr_is_public(uint8_t addr_type) return false; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +int device_addr_type_strict_cmp(gconstpointer a, gconstpointer b) +{ + const struct btd_device *dev = a; + const struct device_addr_type *addr = b; + int cmp; + + cmp = bacmp(&dev->bdaddr, &addr->bdaddr); + + if (addr->bdaddr_type == BDADDR_BREDR) { + if (!dev->bredr) + return -1; + + return cmp; + } + + if (!dev->le) + return -1; + + if (cmp && dev->rpa && addr->bdaddr_type == BDADDR_LE_RANDOM && + (addr->bdaddr.b[5] >> 6) == 0x01) + return bacmp(dev->rpa, &addr->bdaddr); + + if (addr->bdaddr_type != dev->bdaddr_type) + return -1; + + return cmp; +} +#endif + int device_addr_type_cmp(gconstpointer a, gconstpointer b) { const struct btd_device *dev = a; @@ -4034,7 +5769,20 @@ int device_addr_type_cmp(gconstpointer a, gconstpointer b) */ if (!cmp && addr_is_public(addr->bdaddr_type) && addr_is_public(dev->bdaddr_type)) +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + { + if (dev->rpa && addr->bdaddr_type == BDADDR_BREDR) { + char addr_str[18]; + + ba2str(&dev->bdaddr, addr_str); + DBG("Don't match. LE Only device [%s]", addr_str); + return -1; + } + return 0; + } +#else return 0; +#endif if (addr->bdaddr_type == BDADDR_BREDR) { if (!dev->bredr) @@ -4052,6 +5800,68 @@ int device_addr_type_cmp(gconstpointer a, gconstpointer b) return cmp; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#ifdef TIZEN_FEATURE_BLUEZ_BATTERY_WATCH +void device_change_pkt_type(gpointer data, gpointer user_data) +{ + int pkt_type = (int)user_data; + struct btd_device *device = data; + struct hci_conn_info_req *cr; + set_conn_ptype_cp cp; + char addr[18]; + int hdev = 0; + int err = 0; + + /* Change a packet type only for Phone device */ + if ((device->class & 0x00001F00) >> 8 != 0x02) + return; + + if (!device->bredr_state.connected) + return; + + hdev = hci_open_dev(0); + if (hdev < 0) { + error("Cannot open hdev"); + return; + } + + cr = g_malloc0(sizeof(*cr) + sizeof(struct hci_conn_info)); + if (cr == NULL) { + error("Out of memory"); + return; + } + cr->type = ACL_LINK; + bacpy(&cr->bdaddr, &device->bdaddr); + + err = ioctl(hdev, HCIGETCONNINFO, cr); + if (err < 0) { + error("Fail to get HCIGETCOINFO"); + g_free(cr); + hci_close_dev(hdev); + return; + } + + cp.handle = cr->conn_info->handle; + g_free(cr); + cp.pkt_type = cpu_to_le16((uint16_t)pkt_type); + + ba2str(&device->bdaddr, addr); + DBG("Handle %d, Addr %s", cp.handle, addr); + DBG("Send Change pkt type request : 0x%X", pkt_type); + + if (hci_send_cmd(hdev, OGF_LINK_CTL, OCF_SET_CONN_PTYPE, + SET_CONN_PTYPE_CP_SIZE, &cp) < 0) { + error("hci_send_cmd is failed"); + hci_close_dev(hdev); + return; + } + + hci_close_dev(hdev); + return; +} +#endif /* TIZEN_FEATURE_BLUEZ_BATTERY_WATCH */ +#endif + static gboolean record_has_uuid(const sdp_record_t *rec, const char *profile_uuid) { @@ -4108,8 +5918,10 @@ static struct btd_service *probe_service(struct btd_device *device, /* Only set auto connect if profile has set the flag and can really * accept connections. */ +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY if (profile->auto_connect && profile->accept) device_set_auto_connect(device, TRUE); +#endif return service; } @@ -4119,6 +5931,13 @@ static void dev_probe(struct btd_profile *p, void *user_data) struct probe_data *d = user_data; struct btd_service *service; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (find_service_with_profile(d->dev->services, p)) { + DBG("%s is already probed.(UUID:%s)", p->name, p->remote_uuid); + return; + } +#endif + service = probe_service(d->dev, p, d->uuids); if (!service) return; @@ -4155,8 +5974,30 @@ void device_remove_profile(gpointer a, gpointer b) GSList *l; l = find_service_with_profile(device->services, profile); +#ifdef TIZEN_BT_HID_DEVICE_ENABLE + if (l == NULL) { + if (g_strcmp0(profile->local_uuid , HID_DEVICE_UUID) == 0) { + l = find_service_with_uuid(device->services, + HID_DEVICE_UUID); + if (l == NULL) + return; + + service = l->data; + + if (btd_service_get_state(service) == + BTD_SERVICE_STATE_CONNECTED) { + int err; + err = btd_service_disconnect(service); + if (err) + error("error: %s", strerror(-err)); + } + } + return; + } +#else if (l == NULL) return; +#endif service = l->data; device->services = g_slist_delete_link(device->services, l); @@ -4512,7 +6353,20 @@ static void search_cb(sdp_list_t *recs, int err, gpointer user_data) 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); } @@ -4598,9 +6452,26 @@ static void att_disconnected_cb(int err, void *user_data) adapter_connect_list_add(device->adapter, device); done: +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + device_set_gatt_connected(device, FALSE); +#endif attio_cleanup(device); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static void att_mtu_changed(uint16_t mtu, void *user_data) +{ + struct btd_device *device = user_data; + + DBG("att mtu changed %d", mtu); + + g_dbus_emit_signal(dbus_conn, device->path, + DEVICE_INTERFACE, "AttMtuChanged", + DBUS_TYPE_UINT16, &mtu, + DBUS_TYPE_INVALID); +} +#endif + static void register_gatt_services(struct btd_device *device) { struct browse_req *req = device->browse; @@ -4617,8 +6488,17 @@ static void register_gatt_services(struct btd_device *device) btd_device_set_temporary(device, false); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (req) { + if (req->search_uuid) + DBG("browse req. is for SDP. Ignore it."); + else + update_gatt_uuids(req, device->primaries, services); + } +#else if (req) update_gatt_uuids(req, device->primaries, services); +#endif g_slist_free_full(device->primaries, g_free); device->primaries = NULL; @@ -4649,6 +6529,15 @@ static void gatt_client_ready_cb(bool success, uint8_t att_ecode, device_svc_resolved(device, device->bdaddr_type, 0); store_gatt_db(device); + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (device->name[0] == '\0') { + char *name = NULL; + name = bt_gatt_client_get_gap_device_name(device->client); + if (name) + strncpy(device->name, name, MAX_NAME_LENGTH); + } +#endif } static void gatt_client_service_changed(uint16_t start_handle, @@ -4715,6 +6604,15 @@ static void gatt_server_init(struct btd_device *device, struct gatt_db *db) error("Failed to initialize bt_gatt_server"); bt_gatt_server_set_debug(device->server, gatt_debug, NULL, NULL); + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (!bt_gatt_server_set_mtu_changed(device->server, + att_mtu_changed, + device, NULL)) { + DBG("Failed to set mtu changed handler"); + return; + } +#endif } static bool local_counter(uint32_t *sign_cnt, void *user_data) @@ -4745,6 +6643,33 @@ static bool remote_counter(uint32_t *sign_cnt, void *user_data) return true; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static bool load_svc_change_indication_status(struct btd_device *device, const char *local, + const char *peer) +{ + char filename[PATH_MAX]; + GKeyFile *key_file; + gboolean svc_change_regd = false; + snprintf(filename, PATH_MAX, STORAGEDIR "/%s/%s/info", local, peer); + + key_file = g_key_file_new(); + if (!g_key_file_load_from_file(key_file, filename, 0, NULL)) + goto failed; + + /* Load Service changed Registered flag */ + svc_change_regd = g_key_file_get_boolean(key_file, "Att", + "SvcChangeRegd", NULL); + bt_att_set_svc_changed_indication_registered(device->att, + svc_change_regd); + + +failed: + g_key_file_free(key_file); + + return svc_change_regd; +} +#endif + bool device_attach_att(struct btd_device *dev, GIOChannel *io) { GError *gerr = NULL; @@ -4755,10 +6680,16 @@ bool device_attach_att(struct btd_device *dev, GIOChannel *io) struct btd_gatt_database *database; const bdaddr_t *src, *dst; char srcaddr[18], dstaddr[18]; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + uint8_t dst_type = BDADDR_BREDR; +#endif bt_io_get(io, &gerr, BT_IO_OPT_SEC_LEVEL, &sec_level, BT_IO_OPT_IMTU, &mtu, BT_IO_OPT_CID, &cid, +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + BT_IO_OPT_DEST_TYPE, &dst_type, +#endif BT_IO_OPT_INVALID); if (gerr) { @@ -4767,6 +6698,7 @@ bool device_attach_att(struct btd_device *dev, GIOChannel *io) return false; } +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY if (sec_level == BT_IO_SEC_LOW && dev->le_state.paired) { DBG("Elevating security level since LTK is available"); @@ -4779,6 +6711,7 @@ bool device_attach_att(struct btd_device *dev, GIOChannel *io) return false; } } +#endif dev->att_mtu = MIN(mtu, BT_ATT_MAX_LE_MTU); attrib = g_attrib_new(io, BT_ATT_DEFAULT_LE_MTU, false); @@ -4804,6 +6737,16 @@ bool device_attach_att(struct btd_device *dev, GIOChannel *io) bt_att_set_remote_key(dev->att, dev->remote_csrk->key, remote_counter, dev); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (dst_type != BDADDR_BREDR && device_get_rpa_exist(dev) == true) { + bt_att_set_remote_addr(dev->att, + device_get_rpa(dev), BDADDR_LE_RANDOM); + } else { + bt_att_set_remote_addr(dev->att, + &dev->bdaddr, dev->bdaddr_type); + } +#endif + database = btd_adapter_get_database(dev->adapter); src = btd_adapter_get_address(dev->adapter); @@ -4825,6 +6768,11 @@ bool device_attach_att(struct btd_device *dev, GIOChannel *io) */ adapter_connect_list_remove(dev->adapter, dev); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + /* load the service changed indication status on connection */ + load_svc_change_indication_status(dev, srcaddr, dstaddr); +#endif + return true; } @@ -4988,6 +6936,9 @@ static int device_browse_gatt(struct btd_device *device, DBusMessage *msg) struct btd_adapter *adapter = device->adapter; struct browse_req *req; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + DBG(""); +#endif req = browse_request_new(device, msg); if (!req) return -EBUSY; @@ -5060,6 +7011,9 @@ static int device_browse_sdp(struct btd_device *device, DBusMessage *msg) uuid_t uuid; int err; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + DBG(""); +#endif req = browse_request_new(device, msg); if (!req) return -EBUSY; @@ -5072,6 +7026,9 @@ static int device_browse_sdp(struct btd_device *device, DBusMessage *msg) &device->bdaddr, &uuid, browse_cb, req, NULL, req->sdp_flags); if (err < 0) { +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + device->browse = NULL; +#endif browse_request_free(req); return err; } @@ -5079,6 +7036,123 @@ static int device_browse_sdp(struct btd_device *device, DBusMessage *msg) return err; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +void device_set_last_addr_type(struct btd_device *device, uint8_t type) +{ + if (!device) + return; + + //DBG("Last addr type %d", type); + + device->last_bdaddr_type = type; +} + +gboolean device_is_ipsp_connected(struct btd_device * device) +{ + return device->ipsp_connected; +} + +void device_set_ipsp_connected(struct btd_device *device, gboolean connected, + const unsigned char *ifname) +{ + char *iface_name = NULL; + + if (device == NULL) { + error("device is NULL"); + return; + } + + if (device->ipsp_connected == connected) + return; + + device->ipsp_connected = connected; + + memset(device->if_name, 0, sizeof(device->if_name)); + memcpy(device->if_name, ifname, 16); + iface_name = device->if_name; + + DBG("ipsp_connected %d", connected); + DBG("ipsp_iface: %s is Up !", iface_name); + + g_dbus_emit_signal(dbus_conn, device->path, + DEVICE_INTERFACE, "IpspStateChanged", + DBUS_TYPE_BOOLEAN, &connected, + DBUS_TYPE_STRING, &iface_name, + DBUS_TYPE_INVALID); +} +void device_le_data_length_changed(struct btd_device *device, uint16_t max_tx_octets, + uint16_t max_tx_time, uint16_t max_rx_octets, uint16_t max_rx_time) +{ + if (device == NULL) { + error("device is NULL"); + return; + } + + device->max_tx_octets = max_tx_octets; + device->max_tx_time = max_tx_time; + device->max_rx_octets = max_rx_octets; + device->max_rx_time = max_rx_time; + + DBG("data length changed values :max_tx_octets: %d max_tx_time: %d max_rx_octets: %d max_rx_time: %d", + max_tx_octets, max_tx_time, max_rx_octets, max_rx_time); + + g_dbus_emit_signal(dbus_conn, device->path, + DEVICE_INTERFACE, "LEDataLengthChanged", + DBUS_TYPE_UINT16, &max_tx_octets, + DBUS_TYPE_UINT16, &max_tx_time, + DBUS_TYPE_UINT16, &max_rx_octets, + DBUS_TYPE_UINT16, &max_rx_time, + DBUS_TYPE_INVALID); +} + +const bdaddr_t *device_get_rpa(struct btd_device *device) +{ + return device->rpa; +} + +const uint8_t *device_get_irk_value(struct btd_device *device) +{ + return device->irk_val; +} + +bool device_get_rpa_exist(struct btd_device *device) +{ + return device->rpa ? true : false; +} + +void device_set_auth_addr_type(struct btd_device *device, uint8_t type) +{ + if (!device) + return; + + DBG("Auth addr type %d", type); + + device->auth_bdaddr_type = type; +} + +void device_get_tizen_addr(struct btd_device *device, uint8_t type, + struct device_addr_type *addr) +{ + if (!device || !addr) + return; + + if (type == BDADDR_BREDR) { + bacpy(&addr->bdaddr, &device->bdaddr); + addr->bdaddr_type = BDADDR_BREDR; + return; + } + + if (device->rpa) { + bacpy(&addr->bdaddr, device->rpa); + addr->bdaddr_type = BDADDR_LE_RANDOM; + return; + } + + bacpy(&addr->bdaddr, &device->bdaddr); + addr->bdaddr_type = device->bdaddr_type; +} +#endif + int device_discover_services(struct btd_device *device) { int err; @@ -5168,6 +7242,50 @@ void btd_device_set_trusted(struct btd_device *device, gboolean trusted) DEVICE_INTERFACE, "Trusted"); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +void btd_device_set_trusted_profiles(struct btd_device *device, + uint32_t pbap, uint32_t map, uint32_t sap) +{ + if (!device) + return; + DBG("TrustedProfiles Parameters: [PBAP %d] [MAP %d] [SAP %d]", pbap, map, sap); + + if (device->trusted_profiles.pbap == pbap && + device->trusted_profiles.map == map && + device->trusted_profiles.sap == sap) + return; + + device->trusted_profiles.pbap = pbap; + device->trusted_profiles.map = map; + device->trusted_profiles.sap = sap; + + store_device_info(device); + + g_dbus_emit_property_changed(dbus_conn, device->path, + DEVICE_INTERFACE, "TrustedProfiles"); +} + +void btd_device_set_restricted_profiles(struct btd_device *device, + uint32_t hfp_hs, uint32_t a2dp) +{ + if (!device) + return; + DBG("RestrictedProfiles Parameters: [HFP %d] [A2DP %d]", hfp_hs, a2dp); + + if (device->restricted_profiles.hfp_hs == hfp_hs && + device->restricted_profiles.a2dp == a2dp) + return; + + device->restricted_profiles.hfp_hs = hfp_hs; + device->restricted_profiles.a2dp = a2dp; + + store_device_info(device); + + g_dbus_emit_property_changed(dbus_conn, device->path, + DEVICE_INTERFACE, "RestrictedProfiles"); +} +#endif + void device_set_bonded(struct btd_device *device, uint8_t bdaddr_type) { if (!device) @@ -5205,6 +7323,15 @@ void device_set_rssi_with_delta(struct btd_device *device, int8_t rssi, if (!device) return; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (rssi == 0 || device->rssi == 0) { + if (device->rssi == rssi) + return; + } + + device->rssi = rssi; + DBG("rssi %d", rssi); +#else if (rssi == 0 || device->rssi == 0) { if (device->rssi == rssi) return; @@ -5228,6 +7355,7 @@ void device_set_rssi_with_delta(struct btd_device *device, int8_t rssi, device->rssi = rssi; } +#endif g_dbus_emit_property_changed(dbus_conn, device->path, DEVICE_INTERFACE, "RSSI"); @@ -5378,14 +7506,36 @@ void device_bonding_complete(struct btd_device *device, uint8_t bdaddr_type, if (status) { device_cancel_authentication(device, TRUE); device_bonding_failed(device, status); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + device->legacy_pairing = false; +#endif return; } - +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + device->legacy_pairing = false; +#endif device_auth_req_free(device); /* If we're already paired nothing more is needed */ - if (state->paired) + if (state->paired) { +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (bdaddr_type == BDADDR_BREDR && state->svc_resolved) { + DBG("Link key has been changed. Report it"); + if (!device->rpa) + g_dbus_emit_property_changed(dbus_conn, + device->path, DEVICE_INTERFACE, + "Paired"); + else + DBG("Just overwrite Link key"); + } else if (bdaddr_type == BDADDR_LE_RANDOM || + bdaddr_type == BDADDR_LE_PUBLIC) { + DBG("Long Term Key has been changed. Report it"); + g_dbus_emit_property_changed(dbus_conn, device->path, + DEVICE_INTERFACE, "Paired"); + } +#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */ return; + } device_set_paired(device, bdaddr_type); @@ -5473,6 +7623,13 @@ unsigned int device_wait_for_svc_complete(struct btd_device *dev, g_source_remove(dev->discov_timer); dev->discov_timer = g_idle_add(start_discovery, dev); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + else if (!dev->browse) { + DBG("Service is not going on. Start discovery"); + dev->discov_timer = g_idle_add(start_discovery, dev); + } else + DBG("Wait for service discovery"); +#endif return cb->id; } @@ -5578,8 +7735,19 @@ void device_bonding_failed(struct btd_device *device, uint8_t status) DBG("status %u", status); - if (!bonding) + if (!bonding) { +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (device->legacy_pairing) { + DBG("Emit LegacyPaired"); + g_dbus_emit_property_changed(dbus_conn, device->path, + DEVICE_INTERFACE, "LegacyPaired"); + } +#endif return; + } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + btd_device_set_temporary(device, TRUE); +#endif if (device->authr) device_cancel_authentication(device, FALSE); @@ -5624,9 +7792,16 @@ static void confirm_cb(struct agent *agent, DBusError *err, void *data) if (auth->agent == NULL) return; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + btd_adapter_confirm_reply(device->adapter, &device->bdaddr, + auth->addr_type, + err ? FALSE : TRUE); + device_set_auth_addr_type(device, BDADDR_BREDR); +#else btd_adapter_confirm_reply(device->adapter, &device->bdaddr, auth->addr_type, err ? FALSE : TRUE); +#endif agent_unref(device->authr->agent); device->authr->agent = NULL; @@ -5645,8 +7820,14 @@ static void passkey_cb(struct agent *agent, DBusError *err, if (err) passkey = INVALID_PASSKEY; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + btd_adapter_passkey_reply(device->adapter, &device->bdaddr, + auth->addr_type, passkey); + device_set_auth_addr_type(device, BDADDR_BREDR); +#else btd_adapter_passkey_reply(device->adapter, &device->bdaddr, auth->addr_type, passkey); +#endif agent_unref(device->authr->agent); device->authr->agent = NULL; @@ -5751,10 +7932,12 @@ int device_confirm_passkey(struct btd_device *device, uint8_t type, auth->passkey = passkey; +#ifndef TIZEN_FEATURE_BLUEZ_CONFIRM_ONLY if (confirm_hint) err = agent_request_authorization(auth->agent, device, confirm_cb, auth, NULL); else +#endif err = agent_request_confirmation(auth->agent, device, passkey, confirm_cb, auth, NULL); @@ -5866,12 +8049,23 @@ void device_cancel_authentication(struct btd_device *device, gboolean aborted) cancel_authentication(auth); device_auth_req_free(device); + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + device_set_auth_addr_type(device, BDADDR_BREDR); +#endif } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +gboolean device_is_authenticating(struct btd_device *dev, uint8_t bdaddr_type) +{ + return (dev->auth_bdaddr_type == bdaddr_type && dev->authr != NULL); +} +#else gboolean device_is_authenticating(struct btd_device *device) { return (device->authr != NULL); } +#endif struct gatt_primary *btd_device_get_primary(struct btd_device *device, const char *uuid) @@ -5966,6 +8160,11 @@ static sdp_list_t *read_device_records(struct btd_device *device) ba2str(btd_adapter_get_address(device->adapter), local); ba2str(&device->bdaddr, peer); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (device->rpa) + ba2str(device->rpa, peer); +#endif + snprintf(filename, PATH_MAX, STORAGEDIR "/%s/cache/%s", local, peer); key_file = g_key_file_new(); @@ -6053,6 +8252,126 @@ void device_set_appearance(struct btd_device *device, uint16_t value) store_device_info(device); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +int device_get_rpa_res_char_value(struct btd_device *device) +{ + return device->rpa_res_support; +} + +/* Store the RPA Resolution Characteristic Value of remote device. + * This value would be checked before start directed advertising using RPA. + */ +void device_set_rpa_res_char_value(struct btd_device *device, uint8_t value) +{ + if (device->rpa_res_support == value) + return; + + device->rpa_res_support = value; + store_device_info(device); +} + +void device_set_manufacturer_info(struct btd_device *device, struct eir_data *eir) +{ + if (!device) + return; + + if (eir->manufacturer_data_len == 0) + return; + + device->manufacturer_data = g_memdup(eir->manufacturer_data, + eir->manufacturer_data_len); + device->manufacturer_data_len = eir->manufacturer_data_len; + + store_device_info(device); + + g_dbus_emit_property_changed(dbus_conn, device->path, + DEVICE_INTERFACE, "ManufacturerDataLen"); + + g_dbus_emit_property_changed(dbus_conn, device->path, + DEVICE_INTERFACE, "ManufacturerData"); +} + + +void device_set_adv_report_info(struct btd_device *device, void *data, uint8_t data_len, + uint8_t adv_type, int8_t rssi) +{ + if (!device) + return; + + char peer_addr[18]; + const char *paddr = peer_addr; + dbus_int32_t rssi_val = rssi; + int adv_len = data_len; + uint8_t addr_type; + + ba2str(&device->bdaddr, peer_addr); + + /* Replace address type for paired RPA device since IDA passed from controller */ + if (device->rpa) + addr_type = BDADDR_LE_RANDOM; + else + addr_type = device->bdaddr_type; + + g_dbus_emit_signal(dbus_conn, device->path, + DEVICE_INTERFACE, "AdvReport", + DBUS_TYPE_STRING, &paddr, + DBUS_TYPE_BYTE, &addr_type, + DBUS_TYPE_BYTE, &adv_type, + DBUS_TYPE_INT32, &rssi_val, + DBUS_TYPE_INT32, &adv_len, + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &data, data_len, + DBUS_TYPE_INVALID); +} + +void device_set_payload_timeout(struct btd_device *device, + uint16_t payload_timeout) +{ + if (!device) + return; + if (device->auth_payload_timeout == payload_timeout) + return; + + DBG("Payload timeout %d", payload_timeout); + + device->auth_payload_timeout = payload_timeout; + g_dbus_emit_property_changed(dbus_conn, device->path, + DEVICE_INTERFACE, "PayloadTimeout"); +} + +void device_set_disconnect_reason(struct btd_device *device, uint8_t reason) +{ + device->disc_reason = reason; +} + +void btd_device_disconnect(struct btd_device *device) +{ + char dst[18]; + struct btd_service *service; + btd_service_state_t state; + + ba2str(&device->bdaddr, dst); + + DBG(""); + if (device->bredr_state.connected == false) + return; + + service = btd_device_get_service(device, HFP_HS_UUID); + if (!service) + return; + + state = btd_service_get_state(service); + DBG("Connected State : %d", state); + + if (state == BTD_SERVICE_STATE_DISCONNECTED) { + btd_adapter_disconnect_device(device->adapter, &device->bdaddr, + BDADDR_BREDR); + } + + return; +} + +#endif + void btd_device_set_pnpid(struct btd_device *device, uint16_t source, uint16_t vendor, uint16_t product, uint16_t version) { @@ -6083,8 +8402,26 @@ static void service_state_changed(struct btd_service *service, struct btd_device *device = btd_service_get_device(service); int err = btd_service_get_error(service); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (!err) { + if (old_state == BTD_SERVICE_STATE_UNAVAILABLE || + new_state == BTD_SERVICE_STATE_UNAVAILABLE) + DBG("Skip status updating ([%d] -> [%d])", old_state, new_state); + else + g_dbus_emit_signal(dbus_conn, device->path, + DEVICE_INTERFACE, "ProfileStateChanged", + DBUS_TYPE_STRING, &profile->remote_uuid, + DBUS_TYPE_INT32, &new_state, + DBUS_TYPE_INVALID); + } + + if (new_state == BTD_SERVICE_STATE_CONNECTING || + new_state == BTD_SERVICE_STATE_DISCONNECTING || + new_state == BTD_SERVICE_STATE_UNAVAILABLE) +#else if (new_state == BTD_SERVICE_STATE_CONNECTING || new_state == BTD_SERVICE_STATE_DISCONNECTING) +#endif return; if (old_state == BTD_SERVICE_STATE_CONNECTING) @@ -6104,6 +8441,13 @@ struct btd_service *btd_device_get_service(struct btd_device *dev, if (g_str_equal(p->remote_uuid, remote_uuid)) return service; + +#ifdef TIZEN_BT_HID_DEVICE_ENABLE + if (g_str_equal(HID_UUID, remote_uuid)) { + if (strcmp(p->name, "hid-device") == 0) + return service; + } +#endif } return NULL; @@ -6120,3 +8464,21 @@ void btd_device_cleanup(void) { btd_service_remove_state_cb(service_state_cb_id); } + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +void btd_device_set_legacy_pairing(struct btd_device *dev, bool legacy_pairing) +{ + dev->legacy_pairing = legacy_pairing; +} + +void btd_device_set_svc_changed_indication(struct btd_device *dev, bool value) +{ + bt_att_set_svc_changed_indication_registered(dev->att, value); + store_device_info(dev); +} + +bool btd_device_get_svc_changed_indication(struct btd_device *dev) +{ + return bt_att_get_svc_changed_indication_registered(dev->att); +} +#endif diff --git a/src/device.h b/src/device.h index 3cab366..ff39856 100755 --- a/src/device.h +++ b/src/device.h @@ -26,6 +26,13 @@ struct btd_device; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +/* Device Physical channel connection Type */ +#define DEV_CONN_DEFAULT 0xFF /* Represents support for BREDR and LE */ +#define DEV_CONN_BREDR 0x00 /* Only BREDR */ +#define DEV_CONN_LE 0x01 /* Only LE*/ +#endif + struct btd_device *device_create(struct btd_adapter *adapter, const bdaddr_t *address, uint8_t bdaddr_type); struct btd_device *device_create_from_storage(struct btd_adapter *adapter, @@ -59,6 +66,9 @@ struct device_addr_type { uint8_t bdaddr_type; }; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +int device_addr_type_strict_cmp(gconstpointer a, gconstpointer b); +#endif int device_addr_type_cmp(gconstpointer a, gconstpointer b); GSList *btd_device_get_uuids(struct btd_device *device); void device_probe_profiles(struct btd_device *device, GSList *profiles); @@ -83,6 +93,26 @@ void device_remove_profile(gpointer a, gpointer b); struct btd_adapter *device_get_adapter(struct btd_device *device); const bdaddr_t *device_get_address(struct btd_device *device); const char *device_get_path(const struct btd_device *device); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +void device_set_remote_feature_flag(struct btd_device *device, int flags); +gboolean device_is_bredrle(struct btd_device *device); +void device_set_disconnect_reason(struct btd_device *device, uint8_t reason); +void device_set_gatt_connected(struct btd_device *device, gboolean connected); +void device_unpair(struct btd_device *device, gboolean remove_stored); +gboolean device_get_gatt_connected(const struct btd_device *device); +void device_set_rpa(struct btd_device *device, const bdaddr_t *rpa_addr); +const bdaddr_t *device_get_rpa(struct btd_device *device); +bool device_get_rpa_exist(struct btd_device *device); +int device_rpa_cmp(gconstpointer a, gconstpointer b); +int device_addr_cmp(gconstpointer a, gconstpointer b); +void device_remove_stored_folder(struct btd_device *device); +const uint8_t *device_get_irk_value(struct btd_device *device); +void device_set_irk_value(struct btd_device *device, const uint8_t *val); +void device_set_conn_update_state(struct btd_device *device, bool state); +bool device_get_conn_update_state(struct btd_device *device); +void btd_device_set_trusted_profiles(struct btd_device *device, + uint32_t pbap, uint32_t map, uint32_t sap); +#endif gboolean device_is_temporary(struct btd_device *device); bool device_is_paired(struct btd_device *device, uint8_t bdaddr_type); bool device_is_bonded(struct btd_device *device, uint8_t bdaddr_type); @@ -119,7 +149,11 @@ int device_notify_passkey(struct btd_device *device, uint8_t type, int device_notify_pincode(struct btd_device *device, gboolean secure, const char *pincode); void device_cancel_authentication(struct btd_device *device, gboolean aborted); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +gboolean device_is_authenticating(struct btd_device *dev, uint8_t bdaddr_type); +#else gboolean device_is_authenticating(struct btd_device *device); +#endif void device_add_connection(struct btd_device *dev, uint8_t bdaddr_type); void device_remove_connection(struct btd_device *device, uint8_t bdaddr_type); void device_request_disconnect(struct btd_device *device, DBusMessage *msg); @@ -135,6 +169,27 @@ void device_remove_disconnect_watch(struct btd_device *device, guint id); int device_get_appearance(struct btd_device *device, uint16_t *value); void device_set_appearance(struct btd_device *device, uint16_t value); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +struct eir_data; +void device_set_manufacturer_info(struct btd_device *dev, struct eir_data *eir); +void device_set_adv_report_info(struct btd_device *device, void *data, + uint8_t data_len, uint8_t adv_info, int8_t rssi); +void device_set_payload_timeout(struct btd_device *device, + uint16_t payload_timeout); +void device_set_auth_addr_type(struct btd_device *device, uint8_t type); +void device_set_last_addr_type(struct btd_device *device, uint8_t type); +gboolean device_is_ipsp_connected(struct btd_device * device); +void device_set_ipsp_connected(struct btd_device *device, gboolean connected, + const unsigned char *ifname); +int device_get_rpa_res_char_value(struct btd_device *device); +void device_set_rpa_res_char_value(struct btd_device *device, uint8_t value); +void device_le_data_length_changed(struct btd_device *device, uint16_t max_tx_octets, + uint16_t max_tx_time, uint16_t max_rx_octets, + uint16_t max_rx_time); +void device_get_tizen_addr(struct btd_device *device, uint8_t type, + struct device_addr_type *addr); +#endif + struct btd_device *btd_device_ref(struct btd_device *device); void btd_device_unref(struct btd_device *device); @@ -158,6 +213,23 @@ bool device_remove_svc_complete_callback(struct btd_device *dev, struct btd_service *btd_device_get_service(struct btd_device *dev, const char *remote_uuid); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +void device_print_addr(struct btd_device *dev); +gboolean device_is_profile_trusted(struct btd_device *device, + const char *uuid); +gboolean device_is_profile_blocked(struct btd_device *device, + const char *uuid); +gboolean device_is_profile_restricted(struct btd_device *device, + const char *uuid); +void btd_device_disconnect(struct btd_device *dev); +void btd_device_set_legacy_pairing(struct btd_device *dev, bool legacy_pairing); +void btd_device_set_svc_changed_indication(struct btd_device *dev, bool value); +bool btd_device_get_svc_changed_indication(struct btd_device *dev); +#ifdef TIZEN_FEATURE_BLUEZ_BATTERY_WATCH +void device_change_pkt_type(gpointer data, gpointer user_data); +#endif /* TIZEN_FEATURE_BLUEZ_BATTERY_WATCH */ +#endif + int device_discover_services(struct btd_device *device); int btd_device_connect_services(struct btd_device *dev, GSList *services); diff --git a/src/eir.c b/src/eir.c index c984fa5..ee5975a 100755 --- a/src/eir.c +++ b/src/eir.c @@ -65,6 +65,10 @@ void eir_data_free(struct eir_data *eir) eir->msd_list = NULL; g_slist_free_full(eir->sd_list, sd_free); eir->sd_list = NULL; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + g_free(eir->manufacturer_data); + eir->manufacturer_data = NULL; +#endif } static void eir_parse_uuid16(struct eir_data *eir, const void *data, @@ -128,6 +132,26 @@ static void eir_parse_uuid128(struct eir_data *eir, const uint8_t *data, static char *name2utf8(const uint8_t *name, uint8_t len) { +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + char *utf8_name; + char *in_name; + char *ptr; + + in_name = g_malloc0(sizeof(char) * (len + 1)); + /* Fix : NULL_RETURNS */ + if (in_name == NULL) + return NULL; + memcpy(in_name, name, sizeof(char) * len); + in_name[len] = '\0'; + + if (!g_utf8_validate(in_name, -1, (const char **)&ptr)) + *ptr = '\0'; + + utf8_name = g_strdup(in_name); + g_free(in_name); + + return utf8_name; +#else char utf8_name[HCI_MAX_NAME_LENGTH + 2]; int i; @@ -147,6 +171,7 @@ static char *name2utf8(const uint8_t *name, uint8_t len) g_strstrip(utf8_name); return g_strdup(utf8_name); +#endif } static void eir_parse_msd(struct eir_data *eir, const uint8_t *data, @@ -343,6 +368,14 @@ void eir_parse(struct eir_data *eir, const uint8_t *eir_data, uint8_t eir_len) break; case EIR_MANUFACTURER_DATA: +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (data_len < 1) + break; + + eir->manufacturer_data = g_memdup(data, + data_len); + eir->manufacturer_data_len = data_len; +#endif eir_parse_msd(eir, data, data_len); break; diff --git a/src/eir.h b/src/eir.h index 219ee79..5e8bbd5 100755 --- a/src/eir.h +++ b/src/eir.h @@ -92,6 +92,10 @@ struct eir_data { uint16_t did_source; GSList *msd_list; GSList *sd_list; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + char *manufacturer_data; + uint8_t manufacturer_data_len; +#endif }; void eir_data_free(struct eir_data *eir); diff --git a/src/gatt-client.c b/src/gatt-client.c index 114981c..a7d0829 100755 --- a/src/gatt-client.c +++ b/src/gatt-client.c @@ -62,6 +62,9 @@ struct btd_gatt_client { struct queue *services; struct queue *all_notify_clients; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + guint wait_charcs_id; +#endif }; struct service { @@ -72,6 +75,12 @@ struct service { bt_uuid_t uuid; char *path; struct queue *chrcs; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + bool chrcs_ready; + struct queue *pending_ext_props; + guint idle_id; + guint idle_id2; +#endif }; typedef bool (*async_dbus_op_complete_t)(void *data); @@ -216,6 +225,26 @@ static int parse_value_arg(DBusMessageIter *iter, uint8_t **value, int *len) return 0; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static int parse_type_value_arg(DBusMessageIter *iter, uint8_t *type, uint8_t **value, + int *len) +{ + DBusMessageIter array; + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_BYTE) + return -EINVAL; + dbus_message_iter_get_basic(iter, type); + dbus_message_iter_next(iter); + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) + return -EINVAL; + + dbus_message_iter_recurse(iter, &array); + dbus_message_iter_get_fixed_array(&array, value, len); + + return 0; +} +#endif + static void async_dbus_op_free(void *data) { struct async_dbus_op *op = data; @@ -310,6 +339,12 @@ static void async_dbus_op_reply(struct async_dbus_op *op, int err, if (err) { reply = err > 0 ? create_gatt_dbus_error(msg, err) : btd_error_failed(msg, strerror(-err)); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (err > 0) + dbus_message_append_args(reply, + DBUS_TYPE_BYTE, &err, + DBUS_TYPE_INVALID); +#endif goto send_reply; } @@ -545,6 +580,29 @@ static struct async_dbus_op *start_write_request(DBusMessage *msg, return op; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static struct async_dbus_op *start_write_cmd(DBusMessage *msg, uint16_t handle, + struct bt_gatt_client *gatt, bool signed_write, + const uint8_t *value, size_t value_len, + void *data, async_dbus_op_complete_t complete) +{ + struct async_dbus_op *op; + + op = async_dbus_op_new(msg, data); + op->complete = complete; + + op->id = bt_gatt_client_write_without_response_async(gatt, handle, + signed_write, value, value_len, + write_cb, op, async_dbus_op_free); + + if (!op->id) { + async_dbus_op_free(op); + return NULL; + } + + return op; +} +#endif static bool desc_write_complete(void *data) { @@ -824,6 +882,17 @@ static void write_characteristic_cb(struct gatt_db_attribute *attr, int err, } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static void notify_characteristic_cb(struct gatt_db_attribute *attr, int err, + void *user_data) +{ + if (err) { + error("Failed to notify_characteristic_cb : %d", err); + return; + } +} +#endif + static void chrc_read_cb(bool success, uint8_t att_ecode, const uint8_t *value, uint16_t length, void *user_data) { @@ -939,6 +1008,7 @@ static DBusMessage *characteristic_write_value(DBusConnection *conn, * - If value is larger than MTU - 3: long-write * * "write-without-response" property set -> write command. */ +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY if ((chrc->ext_props & BT_GATT_CHRC_EXT_PROP_RELIABLE_WRITE)) { supported = true; chrc->write_op = start_long_write(msg, chrc->value_handle, gatt, @@ -947,6 +1017,7 @@ static DBusMessage *characteristic_write_value(DBusConnection *conn, if (chrc->write_op) return NULL; } +#endif if (chrc->props & BT_GATT_CHRC_PROP_WRITE) { uint16_t mtu; @@ -989,6 +1060,78 @@ fail: return btd_error_not_supported(msg); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static DBusMessage *characteristic_write_value_by_type(DBusConnection *conn, + DBusMessage *msg, void *user_data) +{ + struct characteristic *chrc = user_data; + struct bt_gatt_client *gatt = chrc->service->client->gatt; + DBusMessageIter iter; + uint8_t *value = NULL; + int value_len = 0; + bool supported = false; + uint8_t write_type = 0; + uint16_t offset = 0; + + if (!gatt) + return btd_error_failed(msg, "Not connected"); + + if (chrc->write_op) + return btd_error_in_progress(msg); + + dbus_message_iter_init(msg, &iter); + + if (parse_type_value_arg(&iter, &write_type, &value, &value_len)) + return btd_error_invalid_args(msg); + + if (parse_options(&iter, &offset)) + return btd_error_invalid_args(msg); + + if ((write_type & chrc->props) == BT_GATT_CHRC_PROP_WRITE) { + uint16_t mtu; + + supported = true; + mtu = bt_gatt_client_get_mtu(gatt); + if (!mtu) + return btd_error_failed(msg, "No ATT transport"); + + if (value_len <= mtu - 3 && !offset) + chrc->write_op = start_write_request(msg, + chrc->value_handle, + gatt, value, value_len, + chrc, chrc_write_complete); + else + chrc->write_op = start_long_write(msg, + chrc->value_handle, gatt, + false, value, value_len, offset, + chrc, chrc_write_complete); + + if (chrc->write_op) + return NULL; + } else if ((write_type & chrc->props) == + BT_GATT_CHRC_PROP_WRITE_WITHOUT_RESP) { + supported = true; + chrc->write_op = start_write_cmd(msg, chrc->value_handle, gatt, + false, value, value_len, + chrc, chrc_write_complete); + if (chrc->write_op) + return NULL; + } else if ((write_type & chrc->props) == BT_GATT_CHRC_PROP_AUTH) { + supported = true; + chrc->write_op = start_write_cmd(msg, chrc->value_handle, gatt, + true, value, value_len, + chrc, chrc_write_complete); + if (chrc->write_op) + return NULL; + } + + if (supported) + return btd_error_failed(msg, "Failed to initiate write"); + + return btd_error_not_supported(msg); +} +#endif + struct notify_client { struct characteristic *chrc; int ref_count; @@ -1099,6 +1242,27 @@ static bool match_notify_sender(const void *a, const void *b) return strcmp(client->owner, sender) == 0; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +void gatt_characteristic_value_changed(struct notify_client *client, const uint8_t *data, uint16_t data_len, void *user_data) +{ + struct characteristic *chrc = user_data; + char *chrc_path = strdup(chrc->path); + dbus_int32_t result = 0; + + g_dbus_emit_signal_to_dest(btd_get_dbus_connection(), + client->owner, chrc_path, + GATT_CHARACTERISTIC_IFACE, "GattValueChanged", + DBUS_TYPE_INT32, &result, + DBUS_TYPE_STRING, &chrc_path, + DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &data, data_len, + DBUS_TYPE_INVALID); + + if (chrc_path) + free(chrc_path); + +} +#endif + static void notify_cb(uint16_t value_handle, const uint8_t *value, uint16_t length, void *user_data) { @@ -1112,8 +1276,15 @@ static void notify_cb(uint16_t value_handle, const uint8_t *value, * applications. */ gatt_db_attribute_reset(chrc->attr); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + gatt_db_attribute_write(chrc->attr, 0, value, length, 0, NULL, + notify_characteristic_cb, chrc); + + gatt_characteristic_value_changed(client, value, length, chrc); +#else gatt_db_attribute_write(chrc->attr, 0, value, length, 0, NULL, write_characteristic_cb, chrc); +#endif } static void create_notify_reply(struct async_dbus_op *op, bool success, @@ -1245,14 +1416,48 @@ static DBusMessage *characteristic_stop_notify(DBusConnection *conn, return dbus_message_new_method_return(msg); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static void append_desc_path(void *data, void *user_data) +{ + struct descriptor *desc = data; + DBusMessageIter *array = user_data; + + dbus_message_iter_append_basic(array, DBUS_TYPE_OBJECT_PATH, + &desc->path); +} + +static gboolean characteristic_get_descriptors( + const GDBusPropertyTable *property, + DBusMessageIter *iter, void *data) +{ + struct characteristic *chrc = data; + DBusMessageIter array; + + dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "o", &array); + + queue_foreach(chrc->descs, append_desc_path, &array); + + dbus_message_iter_close_container(iter, &array); + + return TRUE; +} +#endif + static const GDBusPropertyTable characteristic_properties[] = { { "UUID", "s", characteristic_get_uuid, NULL, NULL }, { "Service", "o", characteristic_get_service, NULL, NULL }, { "Value", "ay", characteristic_get_value, NULL, characteristic_value_exists }, +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + { "ChangedValue", "ay", characteristic_get_value, NULL, + characteristic_value_exists }, +#endif { "Notifying", "b", characteristic_get_notifying, NULL, characteristic_notifying_exists }, { "Flags", "as", characteristic_get_flags, NULL, NULL }, +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + { "Descriptors", "ao", characteristic_get_descriptors }, +#endif { } }; @@ -1264,6 +1469,11 @@ static const GDBusMethodTable characteristic_methods[] = { { "options", "a{sv}" }), NULL, characteristic_write_value) }, + { GDBUS_ASYNC_METHOD("WriteValuebyType", + GDBUS_ARGS({ "type", "y" }, { "value", "ay" }, + { "options", "a{sv}" }), + GDBUS_ARGS({ "result", "y" }), + characteristic_write_value_by_type) }, { GDBUS_ASYNC_METHOD("StartNotify", NULL, NULL, characteristic_start_notify) }, { GDBUS_METHOD("StopNotify", NULL, NULL, @@ -1271,6 +1481,19 @@ static const GDBusMethodTable characteristic_methods[] = { { } }; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static const GDBusSignalTable service_signals[] = { + { GDBUS_SIGNAL("GattServiceAdded", + GDBUS_ARGS({ "Service Path","s"})) }, +}; +static const GDBusSignalTable characteristic_signals[] = { + { GDBUS_SIGNAL("GattValueChanged", + GDBUS_ARGS({ "Result", "i"}, + { "Characteristic Path","s"}, + { "GattData", "ay"})) }, +}; +#endif + static void characteristic_free(void *data) { struct characteristic *chrc = data; @@ -1295,11 +1518,24 @@ static struct characteristic *characteristic_create( chrc->notify_clients = queue_new(); chrc->service = service; +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY gatt_db_attribute_get_char_data(attr, &chrc->handle, &chrc->value_handle, &chrc->props, &chrc->ext_props, &uuid); +#else + if (!gatt_db_attribute_get_char_data(attr, &chrc->handle, + &chrc->value_handle, + &chrc->props, + &chrc->ext_props, + &uuid)) { + queue_destroy(chrc->descs, NULL); + queue_destroy(chrc->notify_clients, NULL); + free(chrc); + return NULL; + } +#endif chrc->attr = gatt_db_get_attribute(service->client->db, chrc->value_handle); @@ -1316,7 +1552,11 @@ static struct characteristic *characteristic_create( if (!g_dbus_register_interface(btd_get_dbus_connection(), chrc->path, GATT_CHARACTERISTIC_IFACE, +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + characteristic_methods, characteristic_signals, +#else characteristic_methods, NULL, +#endif characteristic_properties, chrc, characteristic_free)) { error("Unable to register GATT characteristic with handle " @@ -1398,10 +1638,40 @@ static gboolean service_get_primary(const GDBusPropertyTable *property, return TRUE; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static void append_chrc_path(void *data, void *user_data) +{ + struct characteristic *chrc = data; + DBusMessageIter *array = user_data; + + dbus_message_iter_append_basic(array, DBUS_TYPE_OBJECT_PATH, + &chrc->path); +} + +static gboolean service_get_characteristics(const GDBusPropertyTable *property, + DBusMessageIter *iter, void *data) +{ + struct service *service = data; + DBusMessageIter array; + + dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "o", &array); + + if (service->chrcs_ready) + queue_foreach(service->chrcs, append_chrc_path, &array); + + dbus_message_iter_close_container(iter, &array); + + return TRUE; +} +#endif + static const GDBusPropertyTable service_properties[] = { { "UUID", "s", service_get_uuid }, { "Device", "o", service_get_device }, { "Primary", "b", service_get_primary }, +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + { "Characteristics", "ao", service_get_characteristics }, +#endif { } }; @@ -1436,7 +1706,11 @@ static struct service *service_create(struct gatt_db_attribute *attr, if (!g_dbus_register_interface(btd_get_dbus_connection(), service->path, GATT_SERVICE_IFACE, +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + NULL, service_signals, +#else NULL, NULL, +#endif service_properties, service, service_free)) { error("Unable to register GATT service with handle 0x%04x for " @@ -1465,12 +1739,36 @@ static void unregister_service(void *data) DBG("Removing GATT service: %s", service->path); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (service->idle_id) + g_source_remove(service->idle_id); + + if (service->idle_id2) + g_source_remove(service->idle_id2); +#endif + queue_remove_all(service->chrcs, NULL, NULL, unregister_characteristic); g_dbus_unregister_interface(btd_get_dbus_connection(), service->path, GATT_SERVICE_IFACE); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static void notify_chrcs(struct service *service) +{ + + if (service->chrcs_ready || + !queue_isempty(service->pending_ext_props)) + return; + + service->chrcs_ready = true; + + g_dbus_emit_property_changed(btd_get_dbus_connection(), service->path, + GATT_SERVICE_IFACE, + "Characteristics"); +} +#endif + struct export_data { void *root; bool failed; @@ -1527,6 +1825,11 @@ static void export_char(struct gatt_db_attribute *attr, void *user_data) queue_push_tail(service->chrcs, charac); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (charac->ext_props_handle) + queue_push_tail(service->pending_ext_props, charac); +#endif + return; fail: @@ -1546,6 +1849,38 @@ static bool create_characteristics(struct gatt_db_attribute *attr, return !data.failed; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static gboolean set_chrcs_ready(gpointer user_data) +{ + struct service *service = user_data; + + service->idle_id = 0; + notify_chrcs(service); + + return FALSE; +} + +static gboolean notify_service_added_complete(gpointer user_data) +{ + struct service *service = user_data; + char *svc_path; + + if (service == NULL || service->path == NULL) + return FALSE; + + svc_path = g_strdup(service->path); + + g_dbus_emit_signal(btd_get_dbus_connection(), service->path, + GATT_SERVICE_IFACE, "GattServiceAdded", + DBUS_TYPE_STRING, &svc_path, + DBUS_TYPE_INVALID); + + g_free(svc_path); + + return FALSE; +} +#endif + static void export_service(struct gatt_db_attribute *attr, void *user_data) { struct btd_gatt_client *client = user_data; @@ -1565,6 +1900,19 @@ static void export_service(struct gatt_db_attribute *attr, void *user_data) } queue_push_tail(client->services, service); + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + /* + * Asynchronously update the "Characteristics" property of the service. + * If there are any pending reads to obtain the value of the "Extended + * Properties" descriptor then wait until they are complete. + */ + if (!service->chrcs_ready && queue_isempty(service->pending_ext_props)) + service->idle_id = g_idle_add(set_chrcs_ready, service); + + if (service->primary == true) + service->idle_id2 = g_idle_add(notify_service_added_complete, service); +#endif } static void create_services(struct btd_gatt_client *client) @@ -1602,6 +1950,10 @@ void btd_gatt_client_destroy(struct btd_gatt_client *client) if (!client) return; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (client->wait_charcs_id) + g_source_remove(client->wait_charcs_id); +#endif queue_destroy(client->services, unregister_service); queue_destroy(client->all_notify_clients, NULL); bt_gatt_client_unref(client->gatt); @@ -1637,6 +1989,58 @@ static void register_notify(void *data, void *user_data) notify_client_free(notify_client); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static void check_chrcs_ready(void *data, void *user_data) +{ + gboolean *chrcs_ready = user_data; + struct service *service = data; + + /* + * Return FALSE if charcteristics are not ready or if there is + * any pending request to read char. extended properties exist. + */ + if (!service->chrcs_ready || + !queue_isempty(service->pending_ext_props)) + *chrcs_ready = FALSE; +} + +static gboolean check_all_chrcs_ready(gpointer user_data) +{ + struct btd_gatt_client *client = user_data; + gboolean all_chrcs_ready = TRUE; + static int count = 0; + + queue_foreach(client->services, check_chrcs_ready, &all_chrcs_ready); + + /* + * By adding below condition, forcing to call check_chrcs_ready() + * function to check whether all char./extended properties are ready. + * Above function would be called max. for 50 times (Assuming more + * no of services). Emit signal only when all characteristics are ready. + */ + if (all_chrcs_ready == FALSE && count < 50) { + count++; + return TRUE; + } + + /* + * There are chances when all of the primary services are not found, + * instead service changed is received while reading REQs in progress, + * so emit signal after service changed operation is completed (if any). + */ + if (bt_gatt_client_svc_changed_received(client->gatt)) + return TRUE; + + device_set_gatt_connected(client->device, TRUE); + + client->wait_charcs_id = 0; + + count = 0; + + return FALSE; +} +#endif + void btd_gatt_client_ready(struct btd_gatt_client *client) { if (!client) @@ -1658,6 +2062,20 @@ void btd_gatt_client_ready(struct btd_gatt_client *client) DBG("GATT client ready"); create_services(client); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + /* + * In case of more number of services and services having + * characteristics extended properties; GattConnected signal + * should be emitted only after all the characteristics are ready. + * This piece of code checks all the characteristics periodically and + * emit the signal if characteristics are ready. + */ + if (client->wait_charcs_id > 0) + g_source_remove(client->wait_charcs_id); + + client->wait_charcs_id = g_timeout_add(100, + check_all_chrcs_ready, client); +#endif } void btd_gatt_client_connected(struct btd_gatt_client *client) @@ -1731,6 +2149,13 @@ void btd_gatt_client_disconnected(struct btd_gatt_client *client) DBG("Device disconnected. Cleaning up."); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (client->wait_charcs_id) { + g_source_remove(client->wait_charcs_id); + client->wait_charcs_id = 0; + } +#endif + /* * TODO: Once GATT over BR/EDR is properly supported, we should pass the * correct bdaddr_type based on the transport over which GATT is being diff --git a/src/gatt-database.c b/src/gatt-database.c index 0877b25..9fa059b 100755 --- a/src/gatt-database.c +++ b/src/gatt-database.c @@ -170,6 +170,10 @@ struct device_info { uint8_t bdaddr_type; }; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static void conf_cb(void *user_data); +#endif + static void ccc_cb_free(void *data) { struct ccc_cb_data *ccc_cb = data; @@ -223,6 +227,56 @@ find_device_state(struct btd_gatt_database *database, bdaddr_t *bdaddr, return queue_find(database->device_states, dev_state_match, &dev_info); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static void send_service_changed_indication_on_reconnect( + struct btd_device *device, + struct bt_gatt_server *server) +{ + struct btd_gatt_database *database; + uint16_t start_handle = 0X0001, end_handle = 0xFFFF; + uint8_t value[4]; + uint16_t svc_chngd_handle; + DBG(""); + + database = btd_adapter_get_database(device_get_adapter(device)); + + if (!database) + return; + + svc_chngd_handle = gatt_db_attribute_get_handle(database->svc_chngd_ccc); + + put_le16(start_handle, value); + put_le16(end_handle, value + 2); + + bt_gatt_server_send_indication(server, svc_chngd_handle, + value, sizeof(value), conf_cb, + NULL, NULL); +} + +static bool dev_addr_match(const void *a, const void *b) +{ + const struct device_state *dev_state = a; + const struct device_info *dev_info = b; + + if (bacmp(&dev_state->bdaddr, &dev_info->bdaddr) == 0) + return TRUE; + + return FALSE; +} + +static struct device_state * +find_device_state_from_address(struct btd_gatt_database *database, const bdaddr_t *bdaddr) +{ + struct device_info dev_info; + + memset(&dev_info, 0, sizeof(dev_info)); + + bacpy(&dev_info.bdaddr, bdaddr); + + return queue_find(database->device_states, dev_addr_match, &dev_info); +} +#endif + static bool ccc_state_match(const void *a, const void *b) { const struct ccc_state *ccc = a; @@ -242,6 +296,12 @@ static struct device_state *device_state_create(bdaddr_t *bdaddr, uint8_t bdaddr_type) { struct device_state *dev_state; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + char addr[18] = { 0 }; + + ba2str(bdaddr, addr); + DBG("create device_state for %s [%d]", addr, bdaddr_type); +#endif dev_state = new0(struct device_state, 1); dev_state->ccc_states = queue_new(); @@ -279,6 +339,9 @@ static struct ccc_state *get_ccc_state(struct btd_gatt_database *database, { struct device_state *dev_state; struct ccc_state *ccc; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + char addr[18] = { 0 }; +#endif dev_state = get_device_state(database, bdaddr, bdaddr_type); @@ -286,6 +349,12 @@ static struct ccc_state *get_ccc_state(struct btd_gatt_database *database, if (ccc) return ccc; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + ba2str(bdaddr, addr); + DBG("create ccc_state of handle: 0x%04x for %s [%d]", + handle, addr, bdaddr_type); +#endif + ccc = new0(struct ccc_state, 1); ccc->handle = handle; queue_push_tail(dev_state->ccc_states, ccc); @@ -499,8 +568,22 @@ static void connect_cb(GIOChannel *io, GError *gerr, gpointer user_data) return; device_attach_att(device, io); + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (btd_device_get_svc_changed_indication(device)) { + send_service_changed_indication_on_reconnect(device, + btd_device_get_gatt_server(device)); + } +#endif } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#define LE_BEARER_POSTFIX " LE" +#define LE_BEARER_POSTFIX_LEN 3 + +static bool get_dst_info(struct bt_att *att, bdaddr_t *dst, uint8_t *dst_type); +#endif + static void gap_device_name_read_cb(struct gatt_db_attribute *attrib, unsigned int id, uint16_t offset, uint8_t opcode, struct bt_att *att, @@ -511,10 +594,32 @@ static void gap_device_name_read_cb(struct gatt_db_attribute *attrib, size_t len = 0; const uint8_t *value = NULL; const char *device_name; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + bdaddr_t dst = { { 0 } }; + uint8_t dst_type = 0; + char le_name[MAX_NAME_LENGTH + 1]; +#endif DBG("GAP Device Name read request\n"); device_name = btd_adapter_get_name(database->adapter); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (get_dst_info(att, &dst, &dst_type) && dst_type != BDADDR_BREDR && + bacmp(btd_adapter_get_address(database->adapter), + btd_adapter_get_le_address(database->adapter))) { + char *ptr = NULL; + + g_strlcpy(le_name, device_name, + sizeof(le_name) - LE_BEARER_POSTFIX_LEN); + if (!g_utf8_validate(le_name, -1, (const char **)&ptr)) + *ptr = '\0'; + + g_strlcat(le_name, LE_BEARER_POSTFIX, sizeof(le_name)); + DBG("Append LE bearer postfix : %s", le_name); + + device_name = le_name; + } +#endif len = strlen(device_name); if (offset > len) { @@ -560,6 +665,33 @@ done: gatt_db_attribute_read_result(attrib, id, error, value, len); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static void gap_rpa_res_support_read_cb(struct gatt_db_attribute *attrib, + unsigned int id, uint16_t offset, + uint8_t opcode, struct bt_att *att, + void *user_data) +{ + struct btd_gatt_database *database = user_data; + uint8_t error = 0; + size_t len = 1; + const uint8_t *value = NULL; + uint8_t rpa_res_support = 0x00; + + rpa_res_support = btd_adapter_get_rpa_res_support_value(database->adapter); + + if (offset > 1) { + error = BT_ATT_ERROR_INVALID_OFFSET; + goto done; + } + + len -= offset; + value = len ? &rpa_res_support : NULL; + +done: + gatt_db_attribute_read_result(attrib, id, error, value, len); +} +#endif + static sdp_record_t *record_new(uuid_t *uuid, uint16_t start, uint16_t end) { sdp_list_t *svclass_id, *apseq, *proto[2], *root, *aproto; @@ -632,14 +764,20 @@ static uint32_t database_add_record(struct btd_gatt_database *database, return 0; if (name != NULL) +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + sdp_set_info_attr(record, name, "Samsung", NULL); +#else sdp_set_info_attr(record, name, "BlueZ", NULL); +#endif sdp_uuid16_create(&gap_uuid, UUID_GAP); +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY if (sdp_uuid_cmp(&svc, &gap_uuid) == 0) { sdp_set_url_attr(record, "http://www.bluez.org/", "http://www.bluez.org/", "http://www.bluez.org/"); } +#endif if (adapter_service_add(database->adapter, record) == 0) return record->handle; @@ -677,11 +815,23 @@ static void populate_gap_service(struct btd_gatt_database *database) gap_appearance_read_cb, NULL, database); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + /* Central address resolution characteristic */ + bt_uuid16_create(&uuid, GATT_CHARAC_CENTRAL_RPA_RESOLUTION); + gatt_db_service_add_characteristic(service, &uuid, BT_ATT_PERM_READ, + BT_GATT_CHRC_PROP_READ, + gap_rpa_res_support_read_cb, + NULL, database); +#endif + gatt_db_service_set_active(service, true); } static bool get_dst_info(struct bt_att *att, bdaddr_t *dst, uint8_t *dst_type) { +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + return bt_att_get_remote_addr(att, dst, dst_type); +#else GIOChannel *io = NULL; GError *gerr = NULL; @@ -700,7 +850,9 @@ static bool get_dst_info(struct bt_att *att, bdaddr_t *dst, uint8_t *dst_type) } g_io_channel_unref(io); + return true; +#endif } static void gatt_ccc_read_cb(struct gatt_db_attribute *attrib, @@ -796,6 +948,23 @@ static void gatt_ccc_write_cb(struct gatt_db_attribute *attrib, done: gatt_db_attribute_write_result(attrib, id, ecode); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (!ecode) { + uint16_t svc_chngd_handle = gatt_db_attribute_get_handle(database->svc_chngd_ccc); + if (handle == svc_chngd_handle) { + if (ccc->value[0] != 0) { + struct btd_device *device = NULL; + device = btd_adapter_get_device( + database->adapter, + &bdaddr, bdaddr_type); + + if (!device) + return; + btd_device_set_svc_changed_indication(device, true); + } + } + } +#endif } static struct gatt_db_attribute * @@ -868,10 +1037,182 @@ struct notify { bool indicate; }; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +struct notify_indicate { + struct btd_gatt_database *database; + GDBusProxy *proxy; + uint16_t handle, ccc_handle; + const uint8_t *value; + uint16_t len; + bool indicate; +}; + +struct notify_indicate_cb { + GDBusProxy *proxy; + struct btd_device *device; +}; + +static void indicate_confirm_free(void *data) +{ + struct notify_indicate_cb *indicate = data; + + if (indicate) + free(indicate); +} + +static void indicate_confirm_setup_cb(DBusMessageIter *iter, void *user_data) +{ + struct btd_device *device = user_data; + char dst_addr[18] = { 0 }; + char *addr_value = dst_addr; + gboolean complete = FALSE; + + if (device_get_rpa_exist(device) == true) { + ba2str(device_get_rpa(device), dst_addr); + } else { + ba2str(device_get_address(device), dst_addr); + } + + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &addr_value); + + complete = TRUE; + dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &complete); +} + +static void indicate_confirm_reply_cb(DBusMessage *message, void *user_data) +{ + DBusError error; + + dbus_error_init(&error); + + if (dbus_set_error_from_message(&error, message) == TRUE) { + DBG("Failed to send indication/notification"); + dbus_error_free(&error); + return; + } +} +#endif + static void conf_cb(void *user_data) { DBG("GATT server received confirmation"); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + struct notify_indicate_cb *confirm = user_data; + + if (confirm) { + /* Send confirmation to application */ + if (g_dbus_proxy_method_call(confirm->proxy, "IndicateConfirm", + indicate_confirm_setup_cb, + indicate_confirm_reply_cb, confirm->device, + NULL) == TRUE) + return; + } +#endif +} + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static void send_notification_indication_to_device(void *data, void *user_data) +{ + struct device_state *device_state = data; + struct notify_indicate *notify_indicate = user_data; + struct ccc_state *ccc; + struct btd_device *device; + struct notify_indicate_cb *confirm; + + ccc = find_ccc_state(device_state, notify_indicate->ccc_handle); + if (!ccc) + return; + + if (!ccc->value[0] || (notify_indicate->indicate && !(ccc->value[0] & 0x02))) + return; + + device = btd_adapter_get_device(notify_indicate->database->adapter, + &device_state->bdaddr, + device_state->bdaddr_type); + if (!device) + return; + + confirm = new0(struct notify_indicate_cb, 1); + confirm->proxy = notify_indicate->proxy; + confirm->device = device; + /* + * TODO: If the device is not connected but bonded, send the + * notification/indication when it becomes connected. + */ + if (!notify_indicate->indicate) { + DBG("GATT server sending notification"); + bt_gatt_server_send_notification( + btd_device_get_gatt_server(device), + notify_indicate->handle, notify_indicate->value, + notify_indicate->len); + /* In case of Notification, send response to application + * as remote device do not respond for notification */ + conf_cb(confirm); + indicate_confirm_free((void *)confirm); + return; + } + + DBG("GATT server sending indication"); + + bt_gatt_server_send_indication(btd_device_get_gatt_server(device), + notify_indicate->handle, + notify_indicate->value, + notify_indicate->len, conf_cb, + confirm, indicate_confirm_free); +} + +static void send_notification_indication_to_devices(GDBusProxy *proxy, + struct btd_gatt_database *database, + uint16_t handle, const uint8_t *value, + uint16_t len, uint16_t ccc_handle, + bool indicate) +{ + struct notify_indicate notify_indicate; + + DBG("notify for handle: 0x%04x", handle); + + memset(¬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) { @@ -1620,6 +1961,7 @@ static struct pending_op *pending_read_new(struct btd_device *device, return op; } +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY static void append_options(DBusMessageIter *iter, void *user_data) { struct pending_op *op = user_data; @@ -1627,10 +1969,22 @@ static void append_options(DBusMessageIter *iter, void *user_data) dict_append_entry(iter, "device", DBUS_TYPE_OBJECT_PATH, &path); } +#endif static void read_setup_cb(DBusMessageIter *iter, void *user_data) { struct pending_op *op = user_data; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + char dst_addr[18] = { 0 }; + char *addr_value = dst_addr; + uint16_t offset = 0; + const bdaddr_t *bdaddr = device_get_address(op->device); + + ba2str(bdaddr, dst_addr); + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &addr_value); + dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &op->id); + dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &offset); +#else DBusMessageIter dict; dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, @@ -1643,6 +1997,7 @@ static void read_setup_cb(DBusMessageIter *iter, void *user_data) append_options(&dict, op); dbus_message_iter_close_container(iter, &dict); +#endif } static struct pending_op *send_read(struct btd_device *device, @@ -1668,12 +2023,25 @@ static void write_setup_cb(DBusMessageIter *iter, void *user_data) { struct pending_op *op = user_data; DBusMessageIter array, dict; - +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + char dst_addr[18] = { 0 }; + char *addr_value = dst_addr; + uint16_t offset = 0; + gboolean response_needed = TRUE; + const bdaddr_t *bdaddr = device_get_address(op->device); + + ba2str(bdaddr, dst_addr); + dbus_message_iter_append_basic(iter, DBUS_TYPE_STRING, &addr_value); + dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT32, &op->id); + dbus_message_iter_append_basic(iter, DBUS_TYPE_UINT16, &offset); + dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN, &response_needed); +#endif dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "y", &array); dbus_message_iter_append_fixed_array(&array, DBUS_TYPE_BYTE, &op->data.iov_base, op->data.iov_len); dbus_message_iter_close_container(iter, &array); +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING DBUS_TYPE_STRING_AS_STRING @@ -1684,6 +2052,7 @@ static void write_setup_cb(DBusMessageIter *iter, void *user_data) append_options(&dict, op); dbus_message_iter_close_container(iter, &dict); +#endif if (!op->owner_queue) { gatt_db_attribute_write_result(op->attrib, op->id, 0); @@ -1826,7 +2195,87 @@ static void property_changed_cb(GDBusProxy *proxy, const char *name, DBusMessageIter array; uint8_t *value = NULL; int len = 0; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + bool enable = FALSE; + const bdaddr_t *unicast_addr = NULL; +#endif + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (strcmp(name, "Value") == 0) { + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_ARRAY) { + DBG("Malformed \"Value\" property received"); + return; + } + + dbus_message_iter_recurse(iter, &array); + dbus_message_iter_get_fixed_array(&array, &value, &len); + + if (len < 0) { + DBG("Malformed \"Value\" property received"); + return; + } + + /* Truncate the value if it's too large */ + len = MIN(BT_ATT_MAX_VALUE_LEN, len); + value = len ? value : NULL; + } else if (strcmp(name, "Notifying") == 0) { + gboolean notify_indicate = FALSE; + + if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_BOOLEAN) { + DBG("Malformed \"Notifying\" property received"); + return; + } + + dbus_message_iter_get_basic(iter, ¬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; @@ -1852,6 +2301,7 @@ static void property_changed_cb(GDBusProxy *proxy, const char *name, value, len, gatt_db_attribute_get_handle(chrc->ccc), chrc->props & BT_GATT_CHRC_PROP_INDICATE); +#endif } static bool database_add_ccc(struct external_service *service, @@ -2096,6 +2546,24 @@ fail: NULL, 0); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static bool database_check_ccc_desc(struct external_desc *desc) +{ + bt_uuid_t uuid, uuid_ccc; + + if (!parse_uuid(desc->proxy, &uuid)) { + error("Failed to read \"UUID\" property of descriptor"); + return false; + } + + bt_uuid16_create(&uuid_ccc, GATT_CLIENT_CHARAC_CFG_UUID); + if (bt_uuid_cmp(&uuid, &uuid_ccc) == 0) + return true; + else + return false; +} +#endif + static bool database_add_chrc(struct external_service *service, struct external_chrc *chrc) { @@ -2121,8 +2589,13 @@ static bool database_add_chrc(struct external_service *service, return false; } +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY + /* Existing implementation adds CCC descriptor by default + * if notification and indication properties are set. But as per the requirment + * CCCD shall be added by the application */ if (!database_add_ccc(service, chrc)) return false; +#endif if (!database_add_cep(service, chrc)) return false; @@ -2135,11 +2608,28 @@ static bool database_add_chrc(struct external_service *service, if (desc->handled || g_strcmp0(desc->chrc_path, chrc->path)) continue; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + /* Check if Application wants to add CCC and use existing + * implemenation to add CCC descriptors */ + if (database_check_ccc_desc(desc)) { + if (!database_add_ccc(service, chrc)) { + chrc->attrib = NULL; + return false; + } + desc->attrib = chrc->ccc; + desc->handled = true; + } else if (!database_add_desc(service, desc)) { + chrc->attrib = NULL; + error("Failed to create descriptor entry"); + return false; + } +#else if (!database_add_desc(service, desc)) { chrc->attrib = NULL; error("Failed to create descriptor entry"); return false; } +#endif } return true; diff --git a/src/hcid.h b/src/hcid.h index 0b785ee..6449879 100755 --- a/src/hcid.h +++ b/src/hcid.h @@ -41,6 +41,9 @@ struct main_opts { gboolean name_resolv; gboolean debug_keys; gboolean fast_conn; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + gboolean le_privacy; +#endif uint16_t did_source; uint16_t did_vendor; diff --git a/src/log.c b/src/log.c index d2a20de..8a5057e 100755 --- a/src/log.c +++ b/src/log.c @@ -302,6 +302,22 @@ void __btd_toggle_debug(void) desc->flags |= BTD_DEBUG_FLAG_PRINT; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +void __hci_attach_log_init(void) +{ + int option = LOG_NDELAY | LOG_PID; + + enabled = g_strsplit_set(g_strdup("*"), ":, ", 0); + + __btd_enable_debug(__start___debug, __stop___debug); + + openlog("hciattach", option, LOG_DAEMON); + + syslog(LOG_INFO, "hciattach daemon for debugging"); +} +#endif + + void __btd_log_init(const char *debug, int detach) { int option = LOG_NDELAY | LOG_PID; diff --git a/src/log.h b/src/log.h index 0d243ce..1299cdd 100755 --- a/src/log.h +++ b/src/log.h @@ -39,6 +39,9 @@ void btd_info(uint16_t index, const char *format, ...) void btd_debug(uint16_t index, const char *format, ...) __attribute__((format(printf, 2, 3))); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +void __hci_attach_log_init(void ); +#endif void __btd_log_init(const char *debug, int detach); void __btd_log_cleanup(void); void __btd_toggle_debug(void); diff --git a/src/main.c b/src/main.c index bcc1e6f..44110c5 100755 --- a/src/main.c +++ b/src/main.c @@ -90,6 +90,9 @@ static const char * const supported_options[] = { "ControllerMode", "MultiProfile", "Privacy", +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + "EnableLEPrivacy", +#endif }; GKeyFile *btd_get_main_conf(void) @@ -357,6 +360,14 @@ static void parse_config(GKeyFile *config) g_clear_error(&err); else main_opts.fast_conn = boolean; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + boolean = g_key_file_get_boolean(config, "General", + "EnableLEPrivacy", &err); + if (err) + g_clear_error(&err); + else + main_opts.le_privacy = boolean; +#endif } static void init_defaults(void) @@ -372,14 +383,18 @@ static void init_defaults(void) main_opts.reverse_sdp = TRUE; main_opts.name_resolv = TRUE; main_opts.debug_keys = FALSE; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + main_opts.le_privacy = FALSE; +#endif if (sscanf(VERSION, "%hhu.%hhu", &major, &minor) != 2) return; - +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY main_opts.did_source = 0x0002; /* USB */ main_opts.did_vendor = 0x1d6b; /* Linux Foundation */ main_opts.did_product = 0x0246; /* BlueZ */ main_opts.did_version = (major << 8 | minor); +#endif } static void log_handler(const gchar *log_domain, GLogLevelFlags log_level, diff --git a/src/main_hive.conf b/src/main_hive.conf new file mode 100755 index 0000000..f521092 --- /dev/null +++ b/src/main_hive.conf @@ -0,0 +1,87 @@ +[General] + +# Default adaper name +# %h - substituted for hostname +# %d - substituted for adapter id +# Defaults to 'BlueZ' +#Name = %h-%d + +# Default device class. Only the major and minor device class bits are +# considered. Defaults to '0x000000'. +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +Class = 0x40414 # HIVE +#else +#Class = 0x000100 +#endif + +# How long to stay in discoverable mode before going back to non-discoverable +# The value is in seconds. Default is 180, i.e. 3 minutes. +# 0 = disable timer, i.e. stay discoverable forever +#DiscoverableTimeout = 0 + +# How long to stay in pairable mode before going back to non-discoverable +# The value is in seconds. Default is 0. +# 0 = disable timer, i.e. stay pairable forever +#PairableTimeout = 0 + +# Automatic connection for bonded devices driven by platform/user events. +# If a platform plugin uses this mechanism, automatic connections will be +# enabled during the interval defined below. Initially, this feature +# intends to be used to establish connections to ATT channels. Default is 60. +#AutoConnectTimeout = 60 + +# Use vendor id source (assigner), vendor, product and version information for +# DID profile support. The values are separated by ":" and assigner, VID, PID +# and version. +# Possible vendor id source values: bluetooth, usb (defaults to usb) +#DeviceID = bluetooth:1234:5678:abcd + +# Do reverse service discovery for previously unknown devices that connect to +# us. This option is really only needed for qualification since the BITE tester +# doesn't like us doing reverse SDP for some test cases (though there could in +# theory be other useful purposes for this too). Defaults to 'true'. +#ReverseServiceDiscovery = true + +# Enable name resolving after inquiry. Set it to 'false' if you don't need +# remote devices name and want shorter discovery cycle. Defaults to 'true'. +#NameResolving = true + +# Enable runtime persistency of debug link keys. Default is false which +# makes debug link keys valid only for the duration of the connection +# that they were created for. +#DebugKeys = false + +# Restricts all controllers to the specified transport. Default value +# is "dual", i.e. both BR/EDR and LE enabled (when supported by the HW). +# Possible values: "dual", "bredr", "le" +#ControllerMode = dual + +# Enables Multi Profile Specification support. This allows to specify if +# system supports only Multiple Profiles Single Device (MPSD) configuration +# or both Multiple Profiles Single Device (MPSD) and Multiple Profiles Multiple +# Devices (MPMD) configurations. +# Possible values: "off", "single", "multiple" +#MultiProfile = off + +# Permanently enables the Fast Connectable setting for adapters that +# support it. When enabled other devices can connect faster to us, +# however the tradeoff is increased power consumptions. This feature +# will fully work only on kernel version 4.1 and newer. Defaults to +# 'false'. +#FastConnectable = false + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +# Enable the LE Privacy feature. If value is true, i.e. LE Privacy is enabled +# otherwise the feature is disabled by default for the local device. +EnableLEPrivacy = true +#endif + +#[Policy] +# +# The ReconnectUUIDs defines the set of remote services that should try +# to be reconnected to in case of a link loss (link supervision +# timeout). The policy plugin should contain a sane set of values by +# default, but this list can be overridden here. By setting the list to +# empty the reconnection feature gets disabled. +#ReconnectUUIDs= + diff --git a/src/main_ivi.conf b/src/main_ivi.conf new file mode 100755 index 0000000..4e7b1f2 --- /dev/null +++ b/src/main_ivi.conf @@ -0,0 +1,87 @@ +[General] + +# Default adaper name +# %h - substituted for hostname +# %d - substituted for adapter id +# Defaults to 'BlueZ' +#Name = %h-%d + +# Default device class. Only the major and minor device class bits are +# considered. Defaults to '0x000000'. +#ifdef __TIZEN_PATCH__ +Class = 0x200428 # IVI device +#else +#Class = 0x000100 +#endif + +# How long to stay in discoverable mode before going back to non-discoverable +# The value is in seconds. Default is 180, i.e. 3 minutes. +# 0 = disable timer, i.e. stay discoverable forever +#DiscoverableTimeout = 0 + +# How long to stay in pairable mode before going back to non-discoverable +# The value is in seconds. Default is 0. +# 0 = disable timer, i.e. stay pairable forever +#PairableTimeout = 0 + +# Automatic connection for bonded devices driven by platform/user events. +# If a platform plugin uses this mechanism, automatic connections will be +# enabled during the interval defined below. Initially, this feature +# intends to be used to establish connections to ATT channels. Default is 60. +#AutoConnectTimeout = 60 + +# Use vendor id source (assigner), vendor, product and version information for +# DID profile support. The values are separated by ":" and assigner, VID, PID +# and version. +# Possible vendor id source values: bluetooth, usb (defaults to usb) +#DeviceID = bluetooth:1234:5678:abcd + +# Do reverse service discovery for previously unknown devices that connect to +# us. This option is really only needed for qualification since the BITE tester +# doesn't like us doing reverse SDP for some test cases (though there could in +# theory be other useful purposes for this too). Defaults to 'true'. +#ReverseServiceDiscovery = true + +# Enable name resolving after inquiry. Set it to 'false' if you don't need +# remote devices name and want shorter discovery cycle. Defaults to 'true'. +#NameResolving = true + +# Enable runtime persistency of debug link keys. Default is false which +# makes debug link keys valid only for the duration of the connection +# that they were created for. +#DebugKeys = false + +# Restricts all controllers to the specified transport. Default value +# is "dual", i.e. both BR/EDR and LE enabled (when supported by the HW). +# Possible values: "dual", "bredr", "le" +#ControllerMode = dual + +# Enables Multi Profile Specification support. This allows to specify if +# system supports only Multiple Profiles Single Device (MPSD) configuration +# or both Multiple Profiles Single Device (MPSD) and Multiple Profiles Multiple +# Devices (MPMD) configurations. +# Possible values: "off", "single", "multiple" +#MultiProfile = off + +# Permanently enables the Fast Connectable setting for adapters that +# support it. When enabled other devices can connect faster to us, +# however the tradeoff is increased power consumptions. This feature +# will fully work only on kernel version 4.1 and newer. Defaults to +# 'false'. +#FastConnectable = false + +#ifdef __TIZEN_PATCH__ +# Enable the LE Privacy feature. If value is true, i.e. LE Privacy is enabled +# otherwise the feature is disabled by default for the local device. +EnableLEPrivacy = true +#endif + +#[Policy] +# +# The ReconnectUUIDs defines the set of remote services that should try +# to be reconnected to in case of a link loss (link supervision +# timeout). The policy plugin should contain a sane set of values by +# default, but this list can be overridden here. By setting the list to +# empty the reconnection feature gets disabled. +#ReconnectUUIDs= + diff --git a/src/main_m.conf b/src/main_m.conf new file mode 100755 index 0000000..bb431e4 --- /dev/null +++ b/src/main_m.conf @@ -0,0 +1,87 @@ +[General] + +# Default adaper name +# %h - substituted for hostname +# %d - substituted for adapter id +# Defaults to 'BlueZ' +#Name = %h-%d + +# Default device class. Only the major and minor device class bits are +# considered. Defaults to '0x000000'. +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +Class = 0x00020C # Smart phone +#else +#Class = 0x000100 +#endif + +# How long to stay in discoverable mode before going back to non-discoverable +# The value is in seconds. Default is 180, i.e. 3 minutes. +# 0 = disable timer, i.e. stay discoverable forever +#DiscoverableTimeout = 0 + +# How long to stay in pairable mode before going back to non-discoverable +# The value is in seconds. Default is 0. +# 0 = disable timer, i.e. stay pairable forever +#PairableTimeout = 0 + +# Automatic connection for bonded devices driven by platform/user events. +# If a platform plugin uses this mechanism, automatic connections will be +# enabled during the interval defined below. Initially, this feature +# intends to be used to establish connections to ATT channels. Default is 60. +#AutoConnectTimeout = 60 + +# Use vendor id source (assigner), vendor, product and version information for +# DID profile support. The values are separated by ":" and assigner, VID, PID +# and version. +# Possible vendor id source values: bluetooth, usb (defaults to usb) +#DeviceID = bluetooth:1234:5678:abcd + +# Do reverse service discovery for previously unknown devices that connect to +# us. This option is really only needed for qualification since the BITE tester +# doesn't like us doing reverse SDP for some test cases (though there could in +# theory be other useful purposes for this too). Defaults to 'true'. +#ReverseServiceDiscovery = true + +# Enable name resolving after inquiry. Set it to 'false' if you don't need +# remote devices name and want shorter discovery cycle. Defaults to 'true'. +#NameResolving = true + +# Enable runtime persistency of debug link keys. Default is false which +# makes debug link keys valid only for the duration of the connection +# that they were created for. +#DebugKeys = false + +# Restricts all controllers to the specified transport. Default value +# is "dual", i.e. both BR/EDR and LE enabled (when supported by the HW). +# Possible values: "dual", "bredr", "le" +#ControllerMode = dual + +# Enables Multi Profile Specification support. This allows to specify if +# system supports only Multiple Profiles Single Device (MPSD) configuration +# or both Multiple Profiles Single Device (MPSD) and Multiple Profiles Multiple +# Devices (MPMD) configurations. +# Possible values: "off", "single", "multiple" +#MultiProfile = off + +# Permanently enables the Fast Connectable setting for adapters that +# support it. When enabled other devices can connect faster to us, +# however the tradeoff is increased power consumptions. This feature +# will fully work only on kernel version 4.1 and newer. Defaults to +# 'false'. +#FastConnectable = false + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +# Enable the LE Privacy feature. If value is true, i.e. LE Privacy is enabled +# otherwise the feature is disabled by default for the local device. +EnableLEPrivacy = false +#endif + +#[Policy] +# +# The ReconnectUUIDs defines the set of remote services that should try +# to be reconnected to in case of a link loss (link supervision +# timeout). The policy plugin should contain a sane set of values by +# default, but this list can be overridden here. By setting the list to +# empty the reconnection feature gets disabled. +#ReconnectUUIDs= + diff --git a/src/main_w.conf b/src/main_w.conf new file mode 100755 index 0000000..822be5e --- /dev/null +++ b/src/main_w.conf @@ -0,0 +1,84 @@ +[General] + +# Default adaper name +# %h - substituted for hostname +# %d - substituted for adapter id +# Defaults to 'BlueZ' +#Name = %h-%d + +# Default device class. Only the major and minor device class bits are +# considered. Defaults to '0x000000'. +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +Class = 0x000704 # Wearable, Wrist Watch +#else +#Class = 0x000100 +#endif + +# How long to stay in discoverable mode before going back to non-discoverable +# The value is in seconds. Default is 180, i.e. 3 minutes. +# 0 = disable timer, i.e. stay discoverable forever +#DiscoverableTimeout = 0 + +# How long to stay in pairable mode before going back to non-discoverable +# The value is in seconds. Default is 0. +# 0 = disable timer, i.e. stay pairable forever +#PairableTimeout = 0 + +# Automatic connection for bonded devices driven by platform/user events. +# If a platform plugin uses this mechanism, automatic connections will be +# enabled during the interval defined below. Initially, this feature +# intends to be used to establish connections to ATT channels. Default is 60. +#AutoConnectTimeout = 60 + +# Use vendor id source (assigner), vendor, product and version information for +# DID profile support. The values are separated by ":" and assigner, VID, PID +# and version. +# Possible vendor id source values: bluetooth, usb (defaults to usb) +#DeviceID = bluetooth:1234:5678:abcd + +# Do reverse service discovery for previously unknown devices that connect to +# us. This option is really only needed for qualification since the BITE tester +# doesn't like us doing reverse SDP for some test cases (though there could in +# theory be other useful purposes for this too). Defaults to 'true'. +#ReverseServiceDiscovery = true + +# Enable name resolving after inquiry. Set it to 'false' if you don't need +# remote devices name and want shorter discovery cycle. Defaults to 'true'. +#NameResolving = true + +# Enable runtime persistency of debug link keys. Default is false which +# makes debug link keys valid only for the duration of the connection +# that they were created for. +#DebugKeys = false + +# Restricts all controllers to the specified transport. Default value +# is "dual", i.e. both BR/EDR and LE enabled (when supported by the HW). +# Possible values: "dual", "bredr", "le" +#ControllerMode = dual + +# Permanently enables the Fast Connectable setting for adapters that +# support it. When enabled other devices can connect faster to us, +# however the tradeoff is increased power consumptions. This feature +# will fully work only on kernel version 4.1 and newer. Defaults to +# 'false'. +#FastConnectable = false + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +# Enable the LE Privacy feature. If value is true, i.e. LE Privacy is enabled +# otherwise the feature is disabled by default for the local device. +EnableLEPrivacy = false +#endif + +[Policy] +# +# The ReconnectUUIDs defines the set of remote services that should try +# to be reconnected to in case of a link loss (link supervision +# timeout). The policy plugin should contain a sane set of values by +# default, but this list can be overridden here. By setting the list to +# empty the reconnection feature gets disabled. +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +ReconnectUUIDs= +#else +#ReconnectUUIDs= +#endif + diff --git a/src/oob.c b/src/oob.c new file mode 100755 index 0000000..708467b --- /dev/null +++ b/src/oob.c @@ -0,0 +1,46 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2011 ST-Ericsson SA + * + * Author: Szymon Janc for ST-Ericsson + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include "adapter.h" +#include "oob.h" + +static oob_read_cb_t local_oob_read_cb = NULL; + +void oob_register_cb(oob_read_cb_t cb) +{ + local_oob_read_cb = cb; +} + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +void oob_read_local_data_complete(struct btd_adapter *adapter, uint8_t *hash, + uint8_t *randomizer, void *user_data) +#else +void oob_read_local_data_complete(struct btd_adapter *adapter, uint8_t *hash, + uint8_t *randomizer) +#endif +{ + if (local_oob_read_cb) + local_oob_read_cb(adapter, hash, randomizer); +} diff --git a/src/oob.h b/src/oob.h new file mode 100755 index 0000000..d720315 --- /dev/null +++ b/src/oob.h @@ -0,0 +1,36 @@ +/* + * + * BlueZ - Bluetooth protocol stack for Linux + * + * Copyright (C) 2011 ST-Ericsson SA + * + * Author: Szymon Janc for ST-Ericsson + * + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +typedef void (*oob_read_cb_t) (struct btd_adapter *adapter, uint8_t *hash, + uint8_t *randomizer); + +void oob_register_cb(oob_read_cb_t cb); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +void oob_read_local_data_complete(struct btd_adapter *adapter, uint8_t *hash, + uint8_t *randomizer, void *user_data); +#else +void oob_read_local_data_complete(struct btd_adapter *adapter, uint8_t *hash, + uint8_t *randomizer); +#endif diff --git a/src/profile.c b/src/profile.c index 7c5318c..bb833bd 100755 --- a/src/profile.c +++ b/src/profile.c @@ -69,6 +69,11 @@ #define BTD_PROFILE_PSM_AUTO -1 #define BTD_PROFILE_CHAN_AUTO -1 +#ifdef TIZEN_BT_HID_DEVICE_ENABLE +#define HID_DEVICE_INTR_PSM 17 +#define HID_DEVICE_CTRL_PSM 19 +#endif + #define HFP_HF_RECORD \ " \ \ @@ -192,6 +197,54 @@ \ " +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#define SPP_RECORD \ + " \ + \ + \ + \ + %s \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + %s \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + " + +#define LANG_SEQ \ + " \ + \ + \ + \ + \ + \ + " +#else #define SPP_RECORD \ " \ \ @@ -229,6 +282,7 @@ \ \ " +#endif #define DUN_RECORD \ " \ @@ -268,6 +322,58 @@ \ " +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#define OPP_RECORD \ + " \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + " +#else #define OPP_RECORD \ " \ \ @@ -321,6 +427,7 @@ \ \ " +#endif #define FTP_RECORD \ " \ @@ -391,6 +498,57 @@ \ " +#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 \ + " \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + " +#else #define PSE_RECORD \ " \ \ @@ -439,6 +597,13 @@ \ \ " +#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 \ " \ @@ -611,6 +776,109 @@ \ " +#ifdef TIZEN_BT_HID_DEVICE_ENABLE +#define HID_DEVICE_RECORD \ + " \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + \ + " +#endif + + struct ext_io; struct ext_profile { @@ -652,6 +920,10 @@ struct ext_profile { GSList *conns; GSList *connects; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + char *destination; + char *app_path; +#endif }; struct ext_io { @@ -695,6 +967,11 @@ static GSList *custom_props = NULL; static GSList *profiles = NULL; static GSList *ext_profiles = NULL; +#ifdef TIZEN_BT_HID_DEVICE_ENABLE +static int connect_io(struct ext_io *conn, const bdaddr_t *src, + const bdaddr_t *dst); +#endif + void btd_profile_foreach(void (*func)(struct btd_profile *p, void *data), void *data) { @@ -1174,6 +1451,23 @@ static void ext_confirm(GIOChannel *io, gpointer user_data) DBG("incoming connect from %s", addr); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +{ + struct btd_device *device; + gboolean restricted = FALSE; + + device = btd_adapter_find_device(adapter_find(&src), &dst, + BDADDR_BREDR); + if (device) { + restricted = device_is_profile_restricted(device, HFP_HS_UUID); + if (restricted) { + DBG("HFP_HS is restricted"); + return; + } + } +} +#endif + conn = create_conn(server, io, &src, &dst); if (conn == NULL) return; @@ -1368,6 +1662,21 @@ static struct ext_profile *find_ext(struct btd_profile *p) return l->data; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +gboolean ext_profile_is_registered_as_client_role(struct btd_profile *p) +{ + struct ext_profile *ext = find_ext(p); + if (ext && ext->role) { + if(strcasecmp(ext->role, "client") == 0) { + return TRUE; + } else { + return FALSE; + } + } + return FALSE; +} +#endif + static int ext_adapter_probe(struct btd_profile *p, struct btd_adapter *adapter) { @@ -1569,6 +1878,38 @@ static void record_cb(sdp_list_t *recs, int err, gpointer user_data) sdp_record_t *rec = r->data; sdp_list_t *protos; int port; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + char *profile_uuid; + sdp_list_t *svcclass = NULL; + + if (sdp_get_service_classes(rec, &svcclass) < 0) + continue; + + /* Check for empty service classes list */ + if (svcclass == NULL) { + DBG("Skipping record with no service classes"); + continue; + } + + /* Extract the first element and skip the remainning */ + profile_uuid = bt_uuid2string(svcclass->data); + if (!profile_uuid) { + sdp_list_free(svcclass, free); + continue; + } + + sdp_list_free(svcclass, free); + + DBG("profile uuid %s port uuid %s", profile_uuid, ext->remote_uuid); + + if (g_ascii_strncasecmp(profile_uuid, ext->remote_uuid, + strlen(profile_uuid)) != 0) { + free(profile_uuid); + continue; + } + + free(profile_uuid); +#endif if (sdp_get_access_protos(rec, &protos) < 0) { error("Unable to get proto list from %s record", @@ -1771,14 +2112,28 @@ static char *get_spp_record(struct ext_profile *ext, struct ext_io *l2cap, struct ext_io *rfcomm) { char *svc, *rec; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + char *lan_seq; + uint16_t code_ISO639 = (0x65 << 8) | 0x6e; + uint16_t encoding = 106; + uint16_t base_offset = SDP_PRIMARY_LANG_BASE; +#endif if (ext->service) svc = g_strdup_printf("", ext->service); else svc = g_strdup(""); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + lan_seq = g_strdup_printf(LANG_SEQ, code_ISO639, encoding, + base_offset); + rec = g_strdup_printf(SPP_RECORD, svc, rfcomm->chan, lan_seq, ext->version, + ext->name); + g_free(lan_seq); +#else rec = g_strdup_printf(SPP_RECORD, svc, rfcomm->chan, ext->version, ext->name); +#endif g_free(svc); return rec; } @@ -1799,6 +2154,14 @@ static char *get_pce_record(struct ext_profile *ext, struct ext_io *l2cap, static char *get_pse_record(struct ext_profile *ext, struct ext_io *l2cap, struct ext_io *rfcomm) { +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + uint8_t chan = 0; + + if (rfcomm) + chan = rfcomm->chan; + + return g_strdup_printf(PSE_RECORD, chan, ext->version, ext->name); +#else uint16_t psm = 0; uint8_t chan = 0; @@ -1808,6 +2171,7 @@ static char *get_pse_record(struct ext_profile *ext, struct ext_io *l2cap, chan = rfcomm->chan; return g_strdup_printf(PSE_RECORD, chan, ext->version, ext->name, psm); +#endif } static char *get_mas_record(struct ext_profile *ext, struct ext_io *l2cap, @@ -1845,18 +2209,34 @@ static char *get_sync_record(struct ext_profile *ext, struct ext_io *l2cap, ext->name); } +#ifdef TIZEN_BT_HID_DEVICE_ENABLE +static char *get_hid_device_record(struct ext_profile *ext, struct ext_io *l2cap, + struct ext_io *rfcomm) +{ + return g_strdup(HID_DEVICE_RECORD); +} +#endif + static char *get_opp_record(struct ext_profile *ext, struct ext_io *l2cap, struct ext_io *rfcomm) { +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY uint16_t psm = 0; +#endif uint8_t chan = 0; +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY if (l2cap) psm = l2cap->psm; +#endif if (rfcomm) chan = rfcomm->chan; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + return g_strdup_printf(OPP_RECORD, chan, ext->version, ext->name); +#else return g_strdup_printf(OPP_RECORD, chan, ext->version, psm, ext->name); +#endif } static char *get_ftp_record(struct ext_profile *ext, struct ext_io *l2cap, @@ -2001,7 +2381,11 @@ static struct default_settings { .sec_level = BT_IO_SEC_LOW, .authorize = false, .get_record = get_opp_record, - .version = 0x0102, +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + .version = 0x0100, +#else + .version = 0x0102, +#endif }, { .uuid = OBEX_FTP_UUID, .name = "File Transfer", @@ -2052,7 +2436,18 @@ static struct default_settings { .authorize = true, .get_record = get_mns_record, .version = 0x0102 +#ifdef TIZEN_BT_HID_DEVICE_ENABLE + }, { + .uuid = HID_DEVICE_UUID, + .name = "HID Device", + .psm = HID_DEVICE_INTR_PSM, + .authorize = TRUE, + .get_record = get_hid_device_record, + .version = 0x0100, }, +#else + }, +#endif }; static void ext_set_defaults(struct ext_profile *ext) @@ -2151,10 +2546,32 @@ static int parse_ext_opt(struct ext_profile *ext, const char *key, return -EINVAL; dbus_message_iter_get_basic(value, &b); +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY if (b) ext->sec_level = BT_IO_SEC_MEDIUM; else ext->sec_level = BT_IO_SEC_LOW; +#else +#ifdef TIZEN_BT_IO_CAPA_NO_INPUT_OUTPUT + /* + * NoInputOut device should have another authentication method. + * So turn off force authentication setting for that device. + */ + if (b) + ext->sec_level = BT_IO_SEC_MEDIUM; + else + ext->sec_level = BT_IO_SEC_LOW; +#else + if (!strcasecmp(ext->uuid, WEARABLE_OLD_SAP_UUID) || + !strcasecmp(ext->uuid, WEARABLE_NEW_SAP_UUID)) { + DBG("Set SAP UUID's sec_level to HIGH"); + ext->sec_level = BT_IO_SEC_HIGH; + } else if (b) + ext->sec_level = BT_IO_SEC_MEDIUM; + else + ext->sec_level = BT_IO_SEC_LOW; +#endif +#endif } else if (strcasecmp(key, "RequireAuthorization") == 0) { if (type != DBUS_TYPE_BOOLEAN) return -EINVAL; @@ -2235,6 +2652,89 @@ static void set_service(struct ext_profile *ext) } } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static struct ext_profile *create_ext2(const char *owner, const char *path, + const char *uuid, const char *destination, const char *app_path, + DBusMessageIter *opts) +{ + struct btd_profile *p; + struct ext_profile *ext; + + ext = g_new0(struct ext_profile, 1); + + ext->uuid = bt_name2string(uuid); + if (ext->uuid == NULL) { + g_free(ext); + return NULL; + } + + ext->owner = g_strdup(destination); + ext->path = g_strdup(app_path); + ext->destination = g_strdup(destination); + ext->app_path = g_strdup(app_path); + DBG("VALUES Dest %s, path2 %s", destination, app_path); + ext_set_defaults(ext); + + while (dbus_message_iter_get_arg_type(opts) == DBUS_TYPE_DICT_ENTRY) { + DBusMessageIter value, entry; + const char *key; + + dbus_message_iter_recurse(opts, &entry); + dbus_message_iter_get_basic(&entry, &key); + + dbus_message_iter_next(&entry); + dbus_message_iter_recurse(&entry, &value); + + if (parse_ext_opt(ext, key, &value) < 0) + error("Invalid value for profile option %s", key); + + dbus_message_iter_next(opts); + } + + if (!ext->service) + set_service(ext); + + if (ext->enable_server && !(ext->record || ext->get_record)) + ext->get_record = get_generic_record; + + if (!ext->name) + ext->name = g_strdup_printf("%s%s/%s", owner, path, uuid); + + if (!ext->remote_uuid) { + if (ext->service) + ext->remote_uuid = g_strdup(ext->service); + else + ext->remote_uuid = g_strdup(ext->uuid); + } + + p = &ext->p; + + p->name = ext->name; + p->local_uuid = ext->service ? ext->service : ext->uuid; + p->remote_uuid = ext->remote_uuid; + + if (ext->enable_server) { + p->adapter_probe = ext_adapter_probe; + p->adapter_remove = ext_adapter_remove; + } + + if (ext->enable_client) { + p->device_probe = ext_device_probe; + p->device_remove = ext_device_remove; + p->connect = ext_connect_dev; + p->disconnect = ext_disconnect_dev; + } + + DBG("Created \"%s\"", ext->name); + + ext_profiles = g_slist_append(ext_profiles, ext); + + adapter_foreach(adapter_add_profile, &ext->p); + + return ext; +} +#endif + static struct ext_profile *create_ext(const char *owner, const char *path, const char *uuid, DBusMessageIter *opts) @@ -2307,7 +2807,10 @@ static struct ext_profile *create_ext(const char *owner, const char *path, } DBG("Created \"%s\"", ext->name); - +#ifdef TIZEN_BT_HID_DEVICE_ENABLE + if (g_strcmp0(ext->uuid , HID_DEVICE_UUID) == 0) + ext->local_psm = 0; +#endif ext_profiles = g_slist_append(ext_profiles, ext); adapter_foreach(adapter_add_profile, &ext->p); @@ -2411,11 +2914,72 @@ static DBusMessage *unregister_profile(DBusConnection *conn, return dbus_message_new_method_return(msg); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static DBusMessage *register_profile2(DBusConnection *conn, + DBusMessage *msg, void *user_data) +{ + const char *path, *sender, *uuid; + DBusMessageIter args, opts; + struct ext_profile *ext; + const char *destination, *app_path; + sender = dbus_message_get_sender(msg); + + DBG("sender %s", sender); + + dbus_message_iter_init(msg, &args); + + dbus_message_iter_get_basic(&args, &path); + dbus_message_iter_next(&args); + DBG("path %s", path); + + DBG("path %s", path); + dbus_message_iter_get_basic(&args, &uuid); + dbus_message_iter_next(&args); + DBG("uuid %s", uuid); + dbus_message_iter_get_basic(&args, &destination); + dbus_message_iter_next(&args); + DBG("destination %s", destination); + dbus_message_iter_get_basic(&args, &app_path); + dbus_message_iter_next(&args); + DBG("path2 %s", app_path); + ext = find_ext_profile(destination, path); + if (ext) + return btd_error_already_exists(msg); + if (dbus_message_iter_get_arg_type(&args) != DBUS_TYPE_ARRAY) + return btd_error_invalid_args(msg); + DBG("interator"); + dbus_message_iter_recurse(&args, &opts); + + ext = create_ext2(sender, path, uuid, destination, app_path, &opts); + if (!ext) + return btd_error_invalid_args(msg); +#if 0 + ext->id = g_dbus_add_disconnect_watch(conn, sender, ext_exited, ext, + NULL); +#endif + + return dbus_message_new_method_return(msg); +} +#endif + static const GDBusMethodTable methods[] = { { GDBUS_METHOD("RegisterProfile", GDBUS_ARGS({ "profile", "o"}, { "UUID", "s" }, { "options", "a{sv}" }), NULL, register_profile) }, +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + /* For Dbus Smack devides dbus API, the functionality is same */ + { GDBUS_METHOD("RegisterProfile1", + GDBUS_ARGS({ "profile", "o"}, { "UUID", "s" }, + { "options", "a{sv}" }), + NULL, register_profile) }, + { GDBUS_METHOD("RegisterProfile2", + GDBUS_ARGS({"profile", "o"}, { "UUID", "s" }, + {"destination", "s"}, {"path", "s"}, + { "options", "a{sv}"}), + NULL, register_profile2) }, +#endif + { GDBUS_METHOD("UnregisterProfile", GDBUS_ARGS({ "profile", "o" }), NULL, unregister_profile) }, { } @@ -2509,11 +3073,22 @@ void btd_profile_cleanup(void) g_slist_free_full(ext->conns, ext_io_destroy); ext->conns = NULL; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (ext->destination == NULL) { + msg = dbus_message_new_method_call(ext->owner, + ext->path, + "org.bluez.Profile1", + "Release"); + if (msg) + g_dbus_send_message(conn, msg); + } +#else msg = dbus_message_new_method_call(ext->owner, ext->path, "org.bluez.Profile1", "Release"); if (msg) g_dbus_send_message(conn, msg); +#endif g_dbus_remove_watch(conn, ext->id); remove_ext(ext); diff --git a/src/profile.h b/src/profile.h index 4448a2a..d6e20f3 100755 --- a/src/profile.h +++ b/src/profile.h @@ -73,5 +73,9 @@ bool btd_profile_add_custom_prop(const char *uuid, const char *type, void *user_data); bool btd_profile_remove_custom_prop(const char *uuid, const char *name); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +gboolean ext_profile_is_registered_as_client_role(struct btd_profile *p); +#endif + void btd_profile_init(void); void btd_profile_cleanup(void); diff --git a/src/sdp-xml.c b/src/sdp-xml.c index 0a3eb60..ec863b6 100755 --- a/src/sdp-xml.c +++ b/src/sdp-xml.c @@ -531,6 +531,11 @@ static void element_end(GMarkupParseContext *context, struct context_data *ctx_data = user_data; struct sdp_xml_data *elem; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (element_name == NULL) + return; +#endif + if (!strcmp(element_name, "record")) return; diff --git a/src/service.c b/src/service.c index 207ffae..28a4db7 100755 --- a/src/service.c +++ b/src/service.c @@ -171,6 +171,10 @@ int service_probe(struct btd_service *service) void service_remove(struct btd_service *service) { +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (service->profile == NULL) + return; +#endif change_state(service, BTD_SERVICE_STATE_DISCONNECTED, -ECONNABORTED); change_state(service, BTD_SERVICE_STATE_UNAVAILABLE, 0); service->profile->device_remove(service); @@ -238,8 +242,11 @@ int btd_service_connect(struct btd_service *service) struct btd_profile *profile = service->profile; char addr[18]; int err; - +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (profile != NULL && !profile->connect) +#else if (!profile->connect) +#endif return -ENOTSUP; switch (service->state) { @@ -373,9 +380,14 @@ bool btd_service_remove_state_cb(unsigned int id) void btd_service_connecting_complete(struct btd_service *service, int err) { +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY if (service->state != BTD_SERVICE_STATE_DISCONNECTED && service->state != BTD_SERVICE_STATE_CONNECTING) return; +#else + if (service->state != BTD_SERVICE_STATE_CONNECTING) + return; +#endif if (err == 0) change_state(service, BTD_SERVICE_STATE_CONNECTED, 0); diff --git a/src/shared/att.c b/src/shared/att.c index 3071b51..bb6a437 100755 --- a/src/shared/att.c +++ b/src/shared/att.c @@ -68,6 +68,12 @@ struct bt_att { bool in_req; /* There's a pending incoming request */ +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + bdaddr_t bdaddr; + uint8_t bdaddr_type; + bool service_change_indication; /* Service changed indication status */ +#endif + uint8_t *buf; uint16_t mtu; @@ -209,6 +215,10 @@ static void destroy_att_send_op(void *data) static void cancel_att_send_op(struct att_send_op *op) { +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (op->callback) + op->callback(BT_ATT_OP_ERROR_RSP, NULL, 0, op->user_data); +#endif if (op->destroy) op->destroy(op->user_data); @@ -334,8 +344,14 @@ static struct att_send_op *create_att_send_op(struct bt_att *att, * response from the remote end, then no callback should have been * provided, since it will never be called. */ +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (callback && type != ATT_OP_TYPE_REQ && type != ATT_OP_TYPE_IND + && type != ATT_OP_TYPE_CMD) + return NULL; +#else if (callback && type != ATT_OP_TYPE_REQ && type != ATT_OP_TYPE_IND) return NULL; +#endif /* Similarly, if the operation does elicit a response then a callback * must be provided. @@ -479,12 +495,21 @@ static bool can_write_data(struct io *io, void *user_data) case ATT_OP_TYPE_IND: att->pending_ind = op; break; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + case ATT_OP_TYPE_CMD: + if (op->callback) + op->callback(0, NULL, 0, op->user_data); + destroy_att_send_op(op); + return true; +#endif case ATT_OP_TYPE_RSP: /* Set in_req to false to indicate that no request is pending */ att->in_req = false; /* Fall through to the next case */ +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY case ATT_OP_TYPE_CMD: +#endif case ATT_OP_TYPE_NOT: case ATT_OP_TYPE_CONF: case ATT_OP_TYPE_UNKNOWN: @@ -645,6 +670,13 @@ static bool handle_error_rsp(struct bt_att *att, uint8_t *pdu, att->pending_req = NULL; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (op->timeout_id) { + timeout_remove(op->timeout_id); + op->timeout_id = 0; + } +#endif + /* Push operation back to request queue */ return queue_push_head(att->req_queue, op); } @@ -1515,3 +1547,50 @@ bool bt_att_has_crypto(struct bt_att *att) return att->crypto ? true : false; } + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +bool bt_att_set_remote_addr(struct bt_att *att, + const bdaddr_t *bdaddr, uint8_t bdaddr_type) +{ + if (!att) + return false; + + bacpy(&att->bdaddr, bdaddr); + att->bdaddr_type = bdaddr_type; + + return true; +} + +bool bt_att_get_remote_addr(struct bt_att *att, + bdaddr_t *bdaddr, uint8_t *bdaddr_type) +{ + if (!att) + return false; + + if (!bacmp(&att->bdaddr, BDADDR_ANY)) + return false; + + bacpy(bdaddr, &att->bdaddr); + *bdaddr_type = att->bdaddr_type; + + return true; +} + +bool bt_att_set_svc_changed_indication_registered(struct bt_att *att, bool value) +{ + if (!att) + return false; + + att->service_change_indication = value; + + return true; +} + +bool bt_att_get_svc_changed_indication_registered(struct bt_att *att) +{ + if (!att) + return false; + + return att->service_change_indication; +} +#endif diff --git a/src/shared/att.h b/src/shared/att.h index 7bffee7..d05e720 100755 --- a/src/shared/att.h +++ b/src/shared/att.h @@ -25,6 +25,9 @@ #include #include "src/shared/att-types.h" +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#include "lib/bluetooth.h" +#endif struct bt_att; @@ -92,3 +95,11 @@ bool bt_att_set_local_key(struct bt_att *att, uint8_t sign_key[16], bool bt_att_set_remote_key(struct bt_att *att, uint8_t sign_key[16], bt_att_counter_func_t func, void *user_data); bool bt_att_has_crypto(struct bt_att *att); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +bool bt_att_set_remote_addr(struct bt_att *att, + const bdaddr_t *bdaddr, uint8_t bdaddr_type); +bool bt_att_get_remote_addr(struct bt_att *att, + bdaddr_t *bdaddr, uint8_t *bdaddr_type); +bool bt_att_set_svc_changed_indication_registered(struct bt_att *att, bool value); +bool bt_att_get_svc_changed_indication_registered(struct bt_att *att); +#endif \ No newline at end of file diff --git a/src/shared/btsnoop.c b/src/shared/btsnoop.c index e20d1b3..d111e15 100755 --- a/src/shared/btsnoop.c +++ b/src/shared/btsnoop.c @@ -25,6 +25,9 @@ #include #endif +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#include +#endif #include #include #include @@ -73,6 +76,11 @@ struct btsnoop { bool aborted; bool pklg_format; bool pklg_v2; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + char *path; + int16_t rotate_count; + ssize_t file_size; +#endif }; struct btsnoop *btsnoop_open(const char *path, unsigned long flags) @@ -131,7 +139,12 @@ failed: return NULL; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +struct btsnoop *btsnoop_create(const char *path, uint32_t format, + int16_t rotate_count, ssize_t file_size) +#else struct btsnoop *btsnoop_create(const char *path, uint32_t format) +#endif { struct btsnoop *btsnoop; struct btsnoop_hdr hdr; @@ -162,9 +175,99 @@ struct btsnoop *btsnoop_create(const char *path, uint32_t format) return NULL; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (rotate_count > 0 && file_size > 0) { + btsnoop->path = strdup(path); + btsnoop->rotate_count = rotate_count; + btsnoop->file_size = file_size; + } +#endif + return btsnoop_ref(btsnoop); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static int btsnoop_create_2(struct btsnoop *btsnoop) +{ + struct btsnoop_hdr hdr; + ssize_t written; + + if (btsnoop->fd >= 0) + close(btsnoop->fd); + + btsnoop->fd = open(btsnoop->path, + O_WRONLY | O_CREAT | O_TRUNC | O_CLOEXEC, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (btsnoop->fd < 0) { + btsnoop_unref(btsnoop); + return -1; + } + + memcpy(hdr.id, btsnoop_id, sizeof(btsnoop_id)); + hdr.version = htobe32(btsnoop_version); + hdr.type = htobe32(btsnoop->format); + + written = write(btsnoop->fd, &hdr, BTSNOOP_HDR_SIZE); + if (written < 0) { + btsnoop_unref(btsnoop); + return -1; + } + + return btsnoop->fd; +} + +static void btsnoop_rotate_files(struct btsnoop *btsnoop) +{ + char *filename = NULL; + char *new_filename = NULL; + int i; + int postfix_width = 0; + int err; + + if (btsnoop->rotate_count <= 1) + return; + + for (i = btsnoop->rotate_count / 10; i; i /= 10) + postfix_width++; + + for (i = btsnoop->rotate_count - 2; i >= 0; i--) { + if (i == 0) { + filename = strdup(btsnoop->path); + err = (filename == NULL) ? -1 : 0; + } else { + err = asprintf(&filename, "%s.%0*d", + btsnoop->path, postfix_width, i); + } + + if (err < 0 || access(filename, F_OK) < 0) + goto done; + + err = asprintf(&new_filename, "%s.%0*d", + btsnoop->path, postfix_width, i + 1); + if (err < 0) + goto done; + + err = rename(filename, new_filename); + +done: + if (new_filename) { + free(new_filename); + new_filename = NULL; + } + + if (filename) { + free(filename); + filename = NULL; + } + + if (err < 0) + break; + } + + return; +} +#endif + struct btsnoop *btsnoop_ref(struct btsnoop *btsnoop) { if (!btsnoop) @@ -183,6 +286,11 @@ void btsnoop_unref(struct btsnoop *btsnoop) if (__sync_sub_and_fetch(&btsnoop->ref_count, 1)) return; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (btsnoop->path) + free(btsnoop->path); +#endif + if (btsnoop->fd >= 0) close(btsnoop->fd); @@ -216,6 +324,16 @@ bool btsnoop_write(struct btsnoop *btsnoop, struct timeval *tv, pkt.drops = htobe32(drops); pkt.ts = htobe64(ts + 0x00E03AB44A676000ll); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if ((btsnoop->rotate_count > 0 && btsnoop->file_size > 0) && + lseek(btsnoop->fd, 0x00, SEEK_CUR) + + BTSNOOP_PKT_SIZE + size > btsnoop->file_size) { + btsnoop_rotate_files(btsnoop); + if (btsnoop_create_2(btsnoop) < 0) + return false; + } +#endif + written = write(btsnoop->fd, &pkt, BTSNOOP_PKT_SIZE); if (written < 0) return false; diff --git a/src/shared/btsnoop.h b/src/shared/btsnoop.h index 3df8998..25d1af1 100755 --- a/src/shared/btsnoop.h +++ b/src/shared/btsnoop.h @@ -99,7 +99,12 @@ struct btsnoop_opcode_user_logging { struct btsnoop; struct btsnoop *btsnoop_open(const char *path, unsigned long flags); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +struct btsnoop *btsnoop_create(const char *path, uint32_t format, + int16_t rotate_count, ssize_t file_size); +#else struct btsnoop *btsnoop_create(const char *path, uint32_t format); +#endif struct btsnoop *btsnoop_ref(struct btsnoop *btsnoop); void btsnoop_unref(struct btsnoop *btsnoop); diff --git a/src/shared/crypto.c b/src/shared/crypto.c index 6de5514..d8188f2 100755 --- a/src/shared/crypto.c +++ b/src/shared/crypto.c @@ -213,7 +213,11 @@ static int alg_new(int fd, const void *keyval, socklen_t keylen) return -1; /* FIXME: This should use accept4() with SOCK_CLOEXEC */ +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + return accept(fd, NULL, NULL); +#else return accept(fd, NULL, 0); +#endif } static bool alg_encrypt(int fd, const void *inbuf, size_t inlen, diff --git a/src/shared/gatt-client.c b/src/shared/gatt-client.c index 4386692..025a533 100755 --- a/src/shared/gatt-client.c +++ b/src/shared/gatt-client.c @@ -33,6 +33,9 @@ #include "src/shared/queue.h" #include "src/shared/gatt-db.h" #include "src/shared/gatt-client.h" +#if defined TIZEN_FEATURE_BLUEZ_MODIFY +#include "../log.h" +#endif #include #include @@ -112,6 +115,12 @@ struct bt_gatt_client { struct bt_gatt_request *discovery_req; unsigned int mtu_req_id; + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + char *device_name; + struct queue *pending_noti; + bool svc_changed_failed; +#endif }; struct request { @@ -126,6 +135,26 @@ struct request { void (*destroy)(void *); }; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +struct noti { + uint8_t opcode; + void *pdu; + uint16_t length; +}; + +static void notify_cb(uint8_t opcode, const void *pdu, uint16_t length, + void *user_data); + +static void notification_free(void *data) +{ + struct noti *noti = data; + + if (noti->pdu) + free(noti->pdu); + free(noti); +} +#endif + static struct request *request_ref(struct request *req) { __sync_fetch_and_add(&req->ref_count, 1); @@ -341,11 +370,31 @@ static void discovery_op_complete(struct discovery_op *op, bool success, { /* Reset remaining range */ if (success) { +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + util_debug(op->client->debug_callback, op->client->debug_data, + "op->start : %u, op->end : %u, op->last : %u", + op->start, op->end, op->last); + + if (op->last != UINT16_MAX) { + if (op->start != op->last) + op->last++; + + if (op->last <= op->end) + gatt_db_clear_range(op->client->db, + op->last, op->end); + } +#else if (op->last != UINT16_MAX) gatt_db_clear_range(op->client->db, op->last + 1, UINT16_MAX); - } else +#endif + } else { +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + util_debug(op->client->debug_callback, op->client->debug_data, + "Fail to discover service. Clear DB [%d]", err); +#endif gatt_db_clear(op->client->db); + } op->success = success; op->complete_func(op, success, err); @@ -368,6 +417,9 @@ static struct discovery_op *discovery_op_create(struct bt_gatt_client *client, op->failure_func = failure_func; op->start = start; op->end = end; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + op->last = start; +#endif return op; } @@ -817,6 +869,56 @@ done: discovery_op_complete(op, success, att_ecode); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static void read_name_cb(bool success, uint8_t att_ecode, const uint8_t *value, + uint16_t length, void *user_data) +{ + struct bt_gatt_client *client = user_data; + char *name; + + if (!success) { + util_debug(client->debug_callback, client->debug_data, + "read_name_cb failed"); + return; + } + + if (length == 0) + return; + + name = malloc(length + 1); + if (!name) + return; + + memcpy(name, value, length); + name[length] = '\0'; + + util_debug(client->debug_callback, client->debug_data, + "read_name_cb : %s", name); + + if (client->device_name) + free(client->device_name); + + client->device_name = name; +} + +bool bt_gatt_request_att_mtu(struct bt_gatt_client *client, uint16_t mtu, + void *callback, void *user_data) +{ + if (!client || !client->ready) + return false; + + /* Configure the MTU */ + if(!bt_gatt_exchange_mtu(client->att, + MAX(BT_ATT_DEFAULT_LE_MTU, mtu), + callback, + user_data, + NULL)) { + return false; + } + return true; +} +#endif + static void discover_chrcs_cb(bool success, uint8_t att_ecode, struct bt_gatt_result *result, void *user_data) @@ -874,6 +976,16 @@ static void discover_chrcs_cb(bool success, uint8_t att_ecode, chrc_data->properties = properties; chrc_data->uuid = uuid; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (!strcmp(uuid_str, "00002a00-0000-1000-8000-00805f9b34fb")) { + if (!bt_gatt_client_read_value(client, chrc_data->value_handle, + read_name_cb, client, NULL)) { + util_debug(client->debug_callback, client->debug_data, + "Failed to read value"); + } + } +#endif + queue_push_tail(op->pending_chrcs, chrc_data); } @@ -970,6 +1082,13 @@ static void discover_secondary_cb(bool success, uint8_t att_ecode, start, end, uuid_str); /* Store the service */ +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (client->in_svc_chngd) { + util_debug(client->debug_callback, client->debug_data, + "In service changed, delete service first."); + gatt_db_clear_range(client->db, start, end); + } +#endif attr = gatt_db_insert_service(client->db, start, &uuid, false, end - start + 1); if (!attr) { @@ -1052,7 +1171,11 @@ static void discover_primary_cb(bool success, uint8_t att_ecode, "Primary service discovery failed." " ATT ECODE: 0x%02x", att_ecode); /* Reset error in case of not found */ +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (att_ecode == BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND) { +#else if (BT_ATT_ERROR_ATTRIBUTE_NOT_FOUND) { +#endif success = true; att_ecode = 0; } @@ -1077,6 +1200,13 @@ static void discover_primary_cb(bool success, uint8_t att_ecode, "start: 0x%04x, end: 0x%04x, uuid: %s", start, end, uuid_str); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (client->in_svc_chngd) { + util_debug(client->debug_callback, client->debug_data, + "In service changed, delete service first."); + gatt_db_clear_range(client->db, start, end); + } +#endif attr = gatt_db_insert_service(client->db, start, &uuid, true, end - start + 1); if (!attr) { @@ -1185,6 +1315,9 @@ static void exchange_mtu_cb(bool success, uint8_t att_ecode, void *user_data) bt_att_get_mtu(client->att)); discover: +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + op->success = false; +#endif client->discovery_req = bt_gatt_discover_all_primary_services( client->att, NULL, discover_primary_cb, @@ -1208,6 +1341,33 @@ struct service_changed_op { uint16_t end_handle; }; +#if defined TIZEN_FEATURE_BLUEZ_MODIFY +bool bt_gatt_discover_services(struct bt_gatt_client *client) +{ + struct discovery_op *op; + + if (!client->ready) + return false; + + op = new0(struct discovery_op, 1); + if (!op) + return false; + + op->client = client; + gatt_db_unref(op->client->db); + + if (bt_gatt_discover_all_primary_services(client->att, NULL, + discover_primary_cb, + discovery_op_ref(op), + discovery_op_unref)) + return true; + + discovery_op_unref(op); + + return false; +} +#endif + static void process_service_changed(struct bt_gatt_client *client, uint16_t start_handle, uint16_t end_handle); @@ -1424,6 +1584,23 @@ static void service_changed_register_cb(uint16_t att_ecode, void *user_data) done: notify_client_ready(client, success, att_ecode); + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (success) { + struct noti *noti; + + while ((noti = queue_pop_head(client->pending_noti))) { + notify_cb(noti->opcode, noti->pdu, + noti->length, client); + notification_free(noti); + } + } else { + util_debug(client->debug_callback, client->debug_data, + "Remove all pending notifications"); + queue_remove_all(client->pending_noti, NULL, NULL, + notification_free); + } +#endif } static bool register_service_changed(struct bt_gatt_client *client) @@ -1471,6 +1648,9 @@ static void service_changed_complete(struct discovery_op *op, bool success, "Failed to discover services within changed range - " "error: 0x%02x", att_ecode); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + client->svc_changed_failed = true; +#endif gatt_db_clear_range(client->db, start_handle, end_handle); } @@ -1509,6 +1689,11 @@ static void service_changed_failure(struct discovery_op *op) { struct bt_gatt_client *client = op->client; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + util_debug(client->debug_callback, client->debug_data, + "Failed to discover services"); +#endif + gatt_db_clear_range(client->db, op->start, op->end); } @@ -1549,8 +1734,14 @@ static void service_changed_cb(uint16_t value_handle, const uint8_t *value, struct service_changed_op *op; uint16_t start, end; - if (length != 4) + if (length != 4) { +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + util_debug(client->debug_callback, client->debug_data, + "Service changed is received with invalid length : %d", + length); +#endif return; + } start = get_le16(value); end = get_le16(value + 2); @@ -1603,12 +1794,34 @@ fail: done: notify_client_ready(client, success, att_ecode); + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (success) { + struct noti *noti; + + while ((noti = queue_pop_head(client->pending_noti))) { + notify_cb(noti->opcode, noti->pdu, + noti->length, client); + notification_free(noti); + } + } else { + util_debug(client->debug_callback, client->debug_data, + "Remove all pending notifications"); + queue_remove_all(client->pending_noti, NULL, NULL, + notification_free); + } +#endif } static void init_fail(struct discovery_op *op) { struct bt_gatt_client *client = op->client; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + util_debug(client->debug_callback, client->debug_data, + "GATT client init is failed"); +#endif + gatt_db_clear(client->db); } @@ -1707,6 +1920,10 @@ static void complete_unregister_notify(void *data) if (notify_data->att_id) { bt_att_cancel(notify_data->client->att, notify_data->att_id); notify_data->att_id = 0; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + notify_data->chrc->ccc_write_id = 0; + __sync_sub_and_fetch(¬ify_data->chrc->notify_count, 1); +#endif goto done; } @@ -1750,6 +1967,35 @@ static void notify_cb(uint8_t opcode, const void *pdu, uint16_t length, struct bt_gatt_client *client = user_data; struct pdu_data pdu_data; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (client->ready == false) { + struct noti *noti; + + util_debug(client->debug_callback, client->debug_data, + "Client is not ready. pend notification."); + + noti = new0(struct noti, 1); + if (!noti) + return; + + noti->pdu = malloc(length); + if (!noti->pdu) { + free(noti); + return; + } + + noti->opcode = opcode; + noti->length = length; + memcpy(noti->pdu, pdu, length); + + util_debug(client->debug_callback, client->debug_data, + "Notification handle : %d", get_le16(pdu)); + + queue_push_tail(client->pending_noti, noti); + return; + } +#endif + bt_gatt_client_ref(client); memset(&pdu_data, 0, sizeof(pdu_data)); @@ -1784,6 +2030,16 @@ static void bt_gatt_client_free(struct bt_gatt_client *client) bt_att_unref(client->att); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (client->in_svc_chngd || client->svc_changed_failed) { + util_debug(client->debug_callback, client->debug_data, + "Service changed is going. Clear DB"); + gatt_db_clear(client->db); + } + + queue_destroy(client->pending_noti, notification_free); +#endif + gatt_db_unref(client->db); queue_destroy(client->clones, NULL); @@ -1792,6 +2048,13 @@ static void bt_gatt_client_free(struct bt_gatt_client *client) queue_destroy(client->notify_chrcs, notify_chrc_free); queue_destroy(client->pending_requests, request_unref); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (client->device_name) { + free(client->device_name); + client->device_name = NULL; + } +#endif + if (client->parent) { queue_remove(client->parent->clones, client); bt_gatt_client_unref(client->parent); @@ -1834,6 +2097,9 @@ static struct bt_gatt_client *gatt_client_new(struct gatt_db *db, client->notify_list = queue_new(); client->notify_chrcs = queue_new(); client->pending_requests = queue_new(); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + client->pending_noti = queue_new(); +#endif client->notify_id = bt_att_register(att, BT_ATT_OP_HANDLE_VAL_NOT, notify_cb, client, NULL); @@ -2443,6 +2709,93 @@ unsigned int bt_gatt_client_read_long_value(struct bt_gatt_client *client, return req->id; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +struct write_cmd_op { + struct bt_gatt_client *client; + bt_gatt_client_callback_t callback; + void *user_data; + bt_gatt_destroy_func_t destroy; +}; + +static void destroy_write_cmd_op(void *data) +{ + struct write_cmd_op *op = data; + + if (op->destroy) + op->destroy(op->user_data); + + free(op); +} + +static void write_cmd_cb(uint8_t opcode, const void *pdu, uint16_t length, + void *user_data) +{ + struct request *req = user_data; + struct write_cmd_op *op = req->data; + bool success = true; + uint8_t att_ecode = 0; + + if (op->callback) + op->callback(success, att_ecode, op->user_data); +} + +unsigned int bt_gatt_client_write_without_response_async( + struct bt_gatt_client *client, + uint16_t value_handle, + bool signed_write, + const uint8_t *value, uint16_t length, + bt_gatt_client_callback_t callback, + void *user_data, + bt_gatt_client_destroy_func_t destroy) +{ + + uint8_t pdu[2 + length]; + struct request *req; + struct write_cmd_op *op; + int security; + uint8_t opcode; + + if (!client) + return 0; + + op = new0(struct write_cmd_op, 1); + if (!op) + return 0; + + req = request_create(client); + if (!req) + return 0; + + op->callback = callback; + op->user_data = user_data; + op->destroy = destroy; + + req->data = op; + req->destroy = destroy_write_cmd_op; + + /* Only use signed write if unencrypted */ + if (signed_write) { + security = bt_att_get_security(client->att); + opcode = security > BT_SECURITY_LOW ? BT_ATT_OP_WRITE_CMD : + BT_ATT_OP_SIGNED_WRITE_CMD; + } else + opcode = BT_ATT_OP_WRITE_CMD; + + put_le16(value_handle, pdu); + memcpy(pdu + 2, value, length); + + req->att_id = bt_att_send(client->att, opcode, + pdu, sizeof(pdu), write_cmd_cb, req, request_unref); + if (!req->att_id) { + op->destroy = NULL; + request_unref(req); + return 0; + } + + return req->id; +} +#endif + unsigned int bt_gatt_client_write_without_response( struct bt_gatt_client *client, uint16_t value_handle, @@ -3134,8 +3487,14 @@ unsigned int bt_gatt_client_register_notify(struct bt_gatt_client *client, if (!client || !client->db || !chrc_value_handle || !callback) return 0; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (client->in_svc_chngd) + util_debug(client->debug_callback, client->debug_data, + "register_notify in service changed handling"); +#else if (client->in_svc_chngd) return 0; +#endif return register_notify(client, chrc_value_handle, callback, notify, user_data, destroy); @@ -3176,3 +3535,21 @@ int bt_gatt_client_get_security(struct bt_gatt_client *client) return bt_att_get_security(client->att); } + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +char *bt_gatt_client_get_gap_device_name(struct bt_gatt_client *client) +{ + if (!client) + return NULL; + + return client->device_name; +} + +bool bt_gatt_client_svc_changed_received(struct bt_gatt_client *client) +{ + if (!client) + return false; + + return client->in_svc_chngd; +} +#endif diff --git a/src/shared/gatt-client.h b/src/shared/gatt-client.h index aceb570..58931c4 100755 --- a/src/shared/gatt-client.h +++ b/src/shared/gatt-client.h @@ -92,6 +92,17 @@ unsigned int bt_gatt_client_read_multiple(struct bt_gatt_client *client, void *user_data, bt_gatt_client_destroy_func_t destroy); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +unsigned int bt_gatt_client_write_without_response_async( + struct bt_gatt_client *client, + uint16_t value_handle, + bool signed_write, + const uint8_t *value, uint16_t length, + bt_gatt_client_callback_t callback, + void *user_data, + bt_gatt_client_destroy_func_t destroy); +#endif + unsigned int bt_gatt_client_write_without_response( struct bt_gatt_client *client, uint16_t value_handle, @@ -134,3 +145,14 @@ bool bt_gatt_client_unregister_notify(struct bt_gatt_client *client, bool bt_gatt_client_set_security(struct bt_gatt_client *client, int level); int bt_gatt_client_get_security(struct bt_gatt_client *client); + +#if defined TIZEN_FEATURE_BLUEZ_MODIFY +bool bt_gatt_discover_services(struct bt_gatt_client *client); +#endif + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +char *bt_gatt_client_get_gap_device_name(struct bt_gatt_client *client); +bool bt_gatt_request_att_mtu(struct bt_gatt_client *client, uint16_t mtu, + void *callback, void *user_data); +bool bt_gatt_client_svc_changed_received(struct bt_gatt_client *client); +#endif diff --git a/src/shared/gatt-db.c b/src/shared/gatt-db.c index 513451f..69554a9 100755 --- a/src/shared/gatt-db.c +++ b/src/shared/gatt-db.c @@ -95,6 +95,10 @@ struct gatt_db_attribute { uint32_t permissions; uint16_t value_len; uint8_t *value; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + bool notify_indicate; + bdaddr_t unicast_addr; +#endif gatt_db_read_t read_func; gatt_db_write_t write_func; @@ -719,6 +723,9 @@ service_insert_characteristic(struct gatt_db_service *service, service->attributes[i] = new_attribute(service, handle, uuid, NULL, 0); if (!service->attributes[i]) { free(service->attributes[i - 1]); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + service->attributes[i - 1] = NULL; +#endif return NULL; } @@ -1784,3 +1791,34 @@ bool gatt_db_attribute_reset(struct gatt_db_attribute *attrib) return true; } + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +void set_ccc_notify_indicate(struct gatt_db_attribute *ccc, + bool enable) +{ + if (ccc) + ccc->notify_indicate = enable; +} + +bool get_ccc_notify_indicate(const struct gatt_db_attribute *ccc) +{ + if (ccc) + return ccc->notify_indicate; + + return false; +} + +void set_ccc_unicast_address(struct gatt_db_attribute *ccc, + const char *address) +{ + if (ccc) + str2ba(address, &ccc->unicast_addr); +} + +const bdaddr_t *get_ccc_unicast_address(const struct gatt_db_attribute *ccc) +{ + if (ccc) + return &ccc->unicast_addr; + return NULL; +} +#endif diff --git a/src/shared/gatt-db.h b/src/shared/gatt-db.h index 134ec63..8ac0c32 100755 --- a/src/shared/gatt-db.h +++ b/src/shared/gatt-db.h @@ -235,3 +235,15 @@ bool gatt_db_attribute_write_result(struct gatt_db_attribute *attrib, unsigned int id, int err); bool gatt_db_attribute_reset(struct gatt_db_attribute *attrib); + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +void set_ccc_notify_indicate(struct gatt_db_attribute *ccc, + bool enable); + +bool get_ccc_notify_indicate(const struct gatt_db_attribute *ccc); + +void set_ccc_unicast_address(struct gatt_db_attribute *ccc, + const char *address); + +const bdaddr_t *get_ccc_unicast_address(const struct gatt_db_attribute *ccc); +#endif diff --git a/src/shared/gatt-server.c b/src/shared/gatt-server.c index 79e01c8..f89ae01 100755 --- a/src/shared/gatt-server.c +++ b/src/shared/gatt-server.c @@ -112,6 +112,12 @@ struct bt_gatt_server { bt_gatt_server_debug_func_t debug_callback; bt_gatt_server_destroy_func_t debug_destroy; void *debug_data; + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + bt_gatt_server_mtu_changed_callback_t mtu_chngd_callback; + bt_gatt_server_destroy_func_t mtu_chngd_destroy; + void *mtu_chngd_data; +#endif }; static void bt_gatt_server_free(struct bt_gatt_server *server) @@ -182,7 +188,11 @@ static bool encode_read_by_grp_type_rsp(struct gatt_db *db, struct queue *q, int iter = 0; uint16_t start_handle, end_handle; struct iovec value; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + uint8_t data_val_len = 0; +#else uint8_t data_val_len; +#endif *len = 0; @@ -524,7 +534,11 @@ static bool encode_find_info_rsp(struct gatt_db *db, struct queue *q, uint16_t handle; struct gatt_db_attribute *attr; const bt_uuid_t *type; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + int uuid_len = 0, cur_uuid_len; +#else int uuid_len, cur_uuid_len; +#endif int iter = 0; *len = 0; @@ -789,14 +803,26 @@ static void write_cb(uint8_t opcode, const void *pdu, goto error; if (server->pending_write_op) { +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (opcode != BT_ATT_OP_WRITE_CMD) { +#endif ecode = BT_ATT_ERROR_UNLIKELY; goto error; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + } +#endif } op = new0(struct async_write_op, 1); op->server = server; op->opcode = opcode; + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (opcode != BT_ATT_OP_WRITE_CMD) + server->pending_write_op = op; +#else server->pending_write_op = op; +#endif if (gatt_db_attribute_write(attr, 0, pdu + 2, length - 2, opcode, server->att, @@ -809,6 +835,13 @@ static void write_cb(uint8_t opcode, const void *pdu, ecode = BT_ATT_ERROR_UNLIKELY; error: +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + util_debug(server->debug_callback, server->debug_data, + "Handling \"Write %s\" is failed : %d", + (opcode == BT_ATT_OP_WRITE_REQ) ? "Req" : "Cmd", + ecode); +#endif + if (opcode == BT_ATT_OP_WRITE_CMD) return; @@ -912,6 +945,12 @@ static void handle_read_req(struct bt_gatt_server *server, uint8_t opcode, ecode = BT_ATT_ERROR_UNLIKELY; error: +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + util_debug(server->debug_callback, server->debug_data, + "Handling \"Read %sReq\" is failed : %d", + (opcode == BT_ATT_OP_READ_BLOB_REQ) ? "Blob" : "", + ecode); +#endif if (op) async_read_op_destroy(op); @@ -1369,6 +1408,7 @@ static void exchange_mtu_cb(uint8_t opcode, const void *pdu, } client_rx_mtu = get_le16(pdu); +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY final_mtu = MAX(MIN(client_rx_mtu, server->mtu), BT_ATT_DEFAULT_LE_MTU); /* Respond with the server MTU */ @@ -1379,6 +1419,21 @@ static void exchange_mtu_cb(uint8_t opcode, const void *pdu, /* Set MTU to be the minimum */ server->mtu = final_mtu; bt_att_set_mtu(server->att, final_mtu); +#else + final_mtu = MAX(MIN(client_rx_mtu, BT_ATT_MAX_LE_MTU), BT_ATT_DEFAULT_LE_MTU); + + /* Set MTU to be the minimum */ + server->mtu = final_mtu; + bt_att_set_mtu(server->att, final_mtu); + + /* Respond with the server MTU */ + put_le16(server->mtu, rsp_pdu); + bt_att_send(server->att, BT_ATT_OP_MTU_RSP, rsp_pdu, 2, NULL, NULL, + NULL); + + if (server->mtu_chngd_callback) + server->mtu_chngd_callback(final_mtu, server->mtu_chngd_data); +#endif util_debug(server->debug_callback, server->debug_data, "MTU exchange complete, with MTU: %u", final_mtu); @@ -1633,3 +1688,23 @@ bool bt_gatt_server_send_indication(struct bt_gatt_server *server, return result; } + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +bool bt_gatt_server_set_mtu_changed(struct bt_gatt_server *server, + bt_gatt_server_mtu_changed_callback_t callback, + void *user_data, + bt_gatt_server_destroy_func_t destroy) +{ + if (!server) + return false; + + if (server->mtu_chngd_destroy) + server->mtu_chngd_destroy(server->mtu_chngd_data); + + server->mtu_chngd_callback = callback; + server->mtu_chngd_destroy = destroy; + server->mtu_chngd_data = user_data; + + return true; +} +#endif diff --git a/src/shared/gatt-server.h b/src/shared/gatt-server.h index 0e480e1..c61eabe 100755 --- a/src/shared/gatt-server.h +++ b/src/shared/gatt-server.h @@ -50,3 +50,14 @@ bool bt_gatt_server_send_indication(struct bt_gatt_server *server, bt_gatt_server_conf_func_t callback, void *user_data, bt_gatt_server_destroy_func_t destroy); + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +typedef void (*bt_gatt_server_mtu_changed_callback_t)(uint16_t mtu, + void *user_data); + +bool bt_gatt_server_set_mtu_changed(struct bt_gatt_server *server, + bt_gatt_server_mtu_changed_callback_t callback, + void *user_data, + bt_gatt_server_destroy_func_t destroy); + +#endif diff --git a/src/storage.c b/src/storage.c index 734a9e0..c478e03 100755 --- a/src/storage.c +++ b/src/storage.c @@ -144,6 +144,53 @@ int read_local_name(const bdaddr_t *bdaddr, char *name) return 0; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +int write_device_characteristics(const bdaddr_t *sba, const bdaddr_t *dba, + uint8_t bdaddr_type, uint16_t handle, + const char *chars) +{ + char filename[PATH_MAX + 1], addr[18], key[25]; + + create_filename(filename, PATH_MAX, sba, "characteristics"); + + create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + + ba2str(dba, addr); + snprintf(key, sizeof(key), "%17s#%hhu#%04X", addr, bdaddr_type, handle); + + return textfile_put(filename, key, chars); +} + +char *read_device_characteristics(const bdaddr_t *sba, const bdaddr_t *dba, + uint8_t bdaddr_type, uint16_t handle) +{ + char filename[PATH_MAX + 1], addr[18], key[25]; + + create_filename(filename, PATH_MAX, sba, "characteristics"); + + ba2str(dba, addr); + snprintf(key, sizeof(key), "%17s#%hhu#%04X", addr, bdaddr_type, handle); + + return textfile_get(filename, key); +} + +int write_device_attribute(const bdaddr_t *sba, const bdaddr_t *dba, + uint8_t bdaddr_type, uint16_t handle, + const char *chars) +{ + char filename[PATH_MAX + 1], addr[18], key[25]; + + create_filename(filename, PATH_MAX, sba, "attributes"); + + create_file(filename, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + + ba2str(dba, addr); + snprintf(key, sizeof(key), "%17s#%hhu#%04X", addr, bdaddr_type, handle); + + return textfile_put(filename, key, chars); +} +#endif + sdp_record_t *record_from_string(const char *str) { sdp_record_t *rec; diff --git a/src/storage.h b/src/storage.h index 1c0ad57..979075b 100755 --- a/src/storage.h +++ b/src/storage.h @@ -25,5 +25,15 @@ int read_discoverable_timeout(const char *src, int *timeout); int read_pairable_timeout(const char *src, int *timeout); int read_on_mode(const char *src, char *mode, int length); int read_local_name(const bdaddr_t *bdaddr, char *name); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +int write_device_characteristics(const bdaddr_t *sba, const bdaddr_t *dba, + uint8_t bdaddr_type, uint16_t handle, + const char *chars); +char *read_device_characteristics(const bdaddr_t *sba, const bdaddr_t *dba, + uint8_t bdaddr_type, uint16_t handle); +int write_device_attribute(const bdaddr_t *sba, const bdaddr_t *dba, + uint8_t bdaddr_type, uint16_t handle, + const char *chars); +#endif sdp_record_t *record_from_string(const char *str); sdp_record_t *find_record_in_list(sdp_list_t *recs, const char *uuid); diff --git a/test/advertisement-example b/test/advertisement-example new file mode 100755 index 0000000..98aeafa --- /dev/null +++ b/test/advertisement-example @@ -0,0 +1,170 @@ +#!/usr/bin/python + +import dbus +import dbus.exceptions +import dbus.mainloop.glib +import dbus.service + +import array +import gobject + +from random import randint + +mainloop = None + +BLUEZ_SERVICE_NAME = 'org.bluez' +LE_ADVERTISING_MANAGER_IFACE = 'org.bluez.LEAdvertisingManager1' +DBUS_OM_IFACE = 'org.freedesktop.DBus.ObjectManager' +DBUS_PROP_IFACE = 'org.freedesktop.DBus.Properties' + +LE_ADVERTISEMENT_IFACE = 'org.bluez.LEAdvertisement1' + + +class InvalidArgsException(dbus.exceptions.DBusException): + _dbus_error_name = 'org.freedesktop.DBus.Error.InvalidArgs' + + +class NotSupportedException(dbus.exceptions.DBusException): + _dbus_error_name = 'org.bluez.Error.NotSupported' + + +class NotPermittedException(dbus.exceptions.DBusException): + _dbus_error_name = 'org.bluez.Error.NotPermitted' + + +class InvalidValueLengthException(dbus.exceptions.DBusException): + _dbus_error_name = 'org.bluez.Error.InvalidValueLength' + + +class FailedException(dbus.exceptions.DBusException): + _dbus_error_name = 'org.bluez.Error.Failed' + + +class Advertisement(dbus.service.Object): + PATH_BASE = '/org/bluez/example/advertisement' + + def __init__(self, bus, index, advertising_type): + self.path = self.PATH_BASE + str(index) + self.bus = bus + self.ad_type = advertising_type + self.service_uuids = None + self.manufacturer_data = None + self.solicit_uuids = None + self.service_data = None + dbus.service.Object.__init__(self, bus, self.path) + + def get_properties(self): + properties = dict() + properties['Type'] = self.ad_type + if self.service_uuids is not None: + properties['ServiceUUIDs'] = dbus.Array(self.service_uuids, + signature='s') + if self.solicit_uuids is not None: + properties['SolicitUUIDs'] = dbus.Array(self.solicit_uuids, + signature='s') + if self.manufacturer_data is not None: + properties['ManufacturerData'] = dbus.Dictionary( + self.manufacturer_data, signature='qay') + if self.service_data is not None: + properties['ServiceData'] = dbus.Dictionary(self.service_data, + signature='say') + return {LE_ADVERTISEMENT_IFACE: properties} + + def get_path(self): + return dbus.ObjectPath(self.path) + + def add_service_uuid(self, uuid): + if not self.service_uuids: + self.service_uuids = [] + self.service_uuids.append(uuid) + + def add_solicit_uuid(self, uuid): + if not self.solicit_uuids: + self.solicit_uuids = [] + self.solicit_uuids.append(uuid) + + def add_manufacturer_data(self, manuf_code, data): + if not self.manufacturer_data: + self.manufacturer_data = dict() + self.manufacturer_data[manuf_code] = data + + def add_service_data(self, uuid, data): + if not self.service_data: + self.service_data = dict() + self.service_data[uuid] = data + + @dbus.service.method(DBUS_PROP_IFACE, + in_signature='s', + out_signature='a{sv}') + def GetAll(self, interface): + print 'GetAll' + if interface != LE_ADVERTISEMENT_IFACE: + raise InvalidArgsException() + print 'returning props' + return self.get_properties()[LE_ADVERTISEMENT_IFACE] + + @dbus.service.method(LE_ADVERTISEMENT_IFACE, + in_signature='', + out_signature='') + def Release(self): + print '%s: Released!' % self.path + +class TestAdvertisement(Advertisement): + + def __init__(self, bus, index): + Advertisement.__init__(self, bus, index, 'broadcast') + self.add_service_uuid('0000180D-0000-1000-8000-00805F9B34FB') + self.add_service_uuid('0000180F-0000-1000-8000-00805F9B34FB') + self.add_manufacturer_data(0xffff, [0x00, 0x01, 0x02, 0x03, 0x04]) + self.add_service_data('00009999-0000-1000-8000-00805F9B34FB', + [0x00, 0x01, 0x02, 0x03, 0x04]) + + +def register_ad_cb(): + print 'Advertisement registered' + + +def register_ad_error_cb(error): + print 'Failed to register advertisement: ' + str(error) + mainloop.quit() + + +def find_adapter(bus): + remote_om = dbus.Interface(bus.get_object(BLUEZ_SERVICE_NAME, '/'), + DBUS_OM_IFACE) + objects = remote_om.GetManagedObjects() + + for o, props in objects.iteritems(): + if LE_ADVERTISING_MANAGER_IFACE in props: + return o + + return None + + +def main(): + global mainloop + + dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) + + bus = dbus.SystemBus() + + adapter = find_adapter(bus) + if not adapter: + print 'LEAdvertisingManager1 interface not found' + return + + ad_manager = dbus.Interface(bus.get_object(BLUEZ_SERVICE_NAME, adapter), + LE_ADVERTISING_MANAGER_IFACE) + + test_advertisement = TestAdvertisement(bus, 0) + + mainloop = gobject.MainLoop() + + ad_manager.RegisterAdvertisement(test_advertisement.get_path(), {}, + reply_handler=register_ad_cb, + error_handler=register_ad_error_cb) + + mainloop.run() + +if __name__ == '__main__': + main() diff --git a/test/exchange-business-cards b/test/exchange-business-cards new file mode 100755 index 0000000..6805cf7 --- /dev/null +++ b/test/exchange-business-cards @@ -0,0 +1,19 @@ +#!/usr/bin/python + +import sys +import dbus + +bus = dbus.SessionBus() +client = dbus.Interface(bus.get_object("org.bluez.obex", "/org/bluez/obex"), + "org.bluez.obex.Client") + +if (len(sys.argv) < 4): + print "Usage: %s " % (sys.argv[0]) + sys.exit(1) + +print "Creating Session" +path = client.CreateSession(sys.argv[1], { "Target": "OPP" }) +opp = dbus.Interface(bus.get_object("org.bluez.obex", path), + "org.bluez.obex.ObjectPush") + +opp.ExchangeBusinessCards(sys.argv[2], sys.argv[3]) diff --git a/test/get-managed-objects b/test/get-managed-objects new file mode 100755 index 0000000..3156f65 --- /dev/null +++ b/test/get-managed-objects @@ -0,0 +1,29 @@ +#!/usr/bin/python + +from __future__ import absolute_import, print_function, unicode_literals + +import dbus + +bus = dbus.SystemBus() + +manager = dbus.Interface(bus.get_object("org.bluez", "/"), + "org.freedesktop.DBus.ObjectManager") + +objects = manager.GetManagedObjects() + +for path in objects.keys(): + print("[ %s ]" % (path)) + + interfaces = objects[path] + + for interface in interfaces.keys(): + if interface in ["org.freedesktop.DBus.Introspectable", + "org.freedesktop.DBus.Properties"]: + continue + + print(" %s" % (interface)) + + properties = interfaces[interface] + + for key in properties.keys(): + print(" %s = %s" % (key, properties[key])) diff --git a/test/get-obex-capabilities b/test/get-obex-capabilities new file mode 100755 index 0000000..e8afbad --- /dev/null +++ b/test/get-obex-capabilities @@ -0,0 +1,19 @@ +#!/usr/bin/python + +import sys +import dbus + +bus = dbus.SessionBus() +client = dbus.Interface(bus.get_object("org.bluez.obex", "/org/bluez/obex"), + "org.bluez.obex.Client") + +if (len(sys.argv) < 3): + print "Usage: %s " % (sys.argv[0]) + sys.exit(1) + +print "Creating Session" +session_path = client.CreateSession(sys.argv[1], { "Target": sys.argv[2] }) +session = dbus.Interface(bus.get_object("org.bluez.obex", session_path), + "org.bluez.obex.Session") + +print session.GetCapabilities() diff --git a/test/list-folders b/test/list-folders new file mode 100755 index 0000000..7321a15 --- /dev/null +++ b/test/list-folders @@ -0,0 +1,39 @@ +#!/usr/bin/python + +import sys +import dbus + + +def list_folder(folder): + bus = dbus.SessionBus() + client = dbus.Interface(bus.get_object("org.bluez.obex", + "/org/bluez/obex"), + "org.bluez.obex.Client") + + path = client.CreateSession(sys.argv[1], { "Target": "ftp" }) + + ftp = dbus.Interface(bus.get_object("org.bluez.obex", path), + "org.bluez.obex.FileTransfer") + + if folder: + for node in folder.split("/"): + ftp.ChangeFolder(node) + + for i in ftp.ListFolder(): + if i["Type"] == "folder": + print "%s/" % (i["Name"]) + else: + print "%s" % (i["Name"]) + + +if __name__ == '__main__': + + if len(sys.argv) < 2: + print "Usage: %s [folder]" % (sys.argv[0]) + sys.exit(1) + + folder = None + if len(sys.argv) == 3: + folder = sys.argv[2] + + list_folder(folder) diff --git a/test/simple-obex-agent b/test/simple-obex-agent new file mode 100755 index 0000000..05ec4ed --- /dev/null +++ b/test/simple-obex-agent @@ -0,0 +1,87 @@ +#!/usr/bin/python + +from __future__ import absolute_import, print_function, unicode_literals + +import sys +import dbus +import dbus.service +import dbus.mainloop.glib +try: + from gi.repository import GObject +except ImportError: + import gobject as GObject + +BUS_NAME = 'org.bluez.obex' +PATH = '/org/bluez/obex' +AGENT_MANAGER_INTERFACE = 'org.bluez.obex.AgentManager1' +AGENT_INTERFACE = 'org.bluez.obex.Agent1' +TRANSFER_INTERFACE = 'org.bluez.obex.Transfer1' + +def ask(prompt): + try: + return raw_input(prompt) + except: + return input(prompt) + +class Agent(dbus.service.Object): + def __init__(self, conn=None, obj_path=None): + dbus.service.Object.__init__(self, conn, obj_path) + self.pending_auth = False + + @dbus.service.method(AGENT_INTERFACE, in_signature="o", + out_signature="s") + def AuthorizePush(self, path): + transfer = dbus.Interface(bus.get_object(BUS_NAME, path), + 'org.freedesktop.DBus.Properties') + properties = transfer.GetAll(TRANSFER_INTERFACE); + + self.pending_auth = True + auth = ask("Authorize (%s, %s) (Y/n):" % (path, + properties['Name'])) + + if auth == "n" or auth == "N": + self.pending_auth = False + raise dbus.DBusException( + "org.bluez.obex.Error.Rejected: " + "Not Authorized") + + self.pending_auth = False + + return properties['Name'] + + @dbus.service.method(AGENT_INTERFACE, in_signature="", + out_signature="") + def Cancel(self): + print("Authorization Canceled") + self.pending_auth = False + +if __name__ == '__main__': + dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) + + bus = dbus.SessionBus() + manager = dbus.Interface(bus.get_object(BUS_NAME, PATH), + AGENT_MANAGER_INTERFACE) + + path = "/test/agent" + agent = Agent(bus, path) + + mainloop = GObject.MainLoop() + + manager.RegisterAgent(path) + print("Agent registered") + + cont = True + while cont: + try: + mainloop.run() + except KeyboardInterrupt: + if agent.pending_auth: + agent.Cancel() + elif len(transfers) > 0: + for a in transfers: + a.cancel() + else: + cont = False + + # manager.UnregisterAgent(path) + # print "Agent unregistered" diff --git a/tools/bdaddr.1 b/tools/bdaddr.1 new file mode 100755 index 0000000..efb77d2 --- /dev/null +++ b/tools/bdaddr.1 @@ -0,0 +1,68 @@ +.TH BDADDR 1 "Sep 27 2005" BlueZ "Linux System Administration" +.SH NAME +bdaddr \- Utility for changing the Bluetooth device address +.SH SYNOPSIS +.B bdaddr +.br +.B bdaddr -h +.br +.B bdaddr [-i ] [-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\ +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 , +man page by Adam Laurie +.PP diff --git a/tools/bneptest.c b/tools/bneptest.c index 1404252..c761e16 100755 --- a/tools/bneptest.c +++ b/tools/bneptest.c @@ -327,7 +327,11 @@ static gboolean setup_bnep_cb(GIOChannel *chan, GIOCondition cond, sk = g_io_channel_unix_get_fd(chan); /* Reading BNEP_SETUP_CONNECTION_REQUEST_MSG */ +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY n = recv(sk, packet, sizeof(packet), MSG_PEEK); +#else + n = read(sk, packet, sizeof(packet)); +#endif if (n < 0) { error("read(): %s(%d)", strerror(errno), errno); return FALSE; diff --git a/tools/btgatt-client.c b/tools/btgatt-client.c index 4c8c9dd..31848f9 100755 --- a/tools/btgatt-client.c +++ b/tools/btgatt-client.c @@ -365,8 +365,13 @@ static void ready_cb(bool success, uint8_t att_ecode, void *user_data) struct client *cli = user_data; if (!success) { +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY PRLOG("GATT discovery procedures failed - error code: 0x%02x\n", att_ecode); +#else + PRLOG("GATT discovery procedures failed: %s (0x%02x)\n", + ecode_to_string(att_ecode), att_ecode); +#endif return; } @@ -480,7 +485,12 @@ static void read_multiple_cb(bool success, uint8_t att_ecode, int i; if (!success) { +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY PRLOG("\nRead multiple request failed: 0x%02x\n", att_ecode); +#else + PRLOG("\nRead multiple request failed: %s (0x%02x)\n", + ecode_to_string(att_ecode), att_ecode); +#endif return; } @@ -1109,8 +1119,13 @@ static void notify_cb(uint16_t value_handle, const uint8_t *value, static void register_notify_cb(uint16_t att_ecode, void *user_data) { if (att_ecode) { +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY PRLOG("Failed to register notify handler " "- error code: 0x%02x\n", att_ecode); +#else + PRLOG("Failed to register notify handler: %s (0x%02x)\n", + ecode_to_string(att_ecode), att_ecode); +#endif return; } diff --git a/tools/btsnoop.c b/tools/btsnoop.c index 3eb8082..c2a194b 100755 --- a/tools/btsnoop.c +++ b/tools/btsnoop.c @@ -34,6 +34,10 @@ #include #include #include +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#include +#include +#endif #include #include #include @@ -271,6 +275,118 @@ close_input: for (i = 0; i < num_input; i++) close(input_fd[i]); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#define BT_SNOOP_TYPE_HCI_PREFIX "btsnoop_type_hci" +#define MAX_SUPPORTED_ADAPTER 16 + +static void command_split(const char *input) +{ + unsigned char buf[BTSNOOP_MAX_PACKET_SIZE]; + uint16_t pktlen,opcode; + uint32_t type; + struct timeval tv; + uint16_t index, max_index = 0; + char write_file_name[255]; + struct btsnoop *btsnoop_read_file = NULL; + struct btsnoop *btsnoop_write_file[MAX_SUPPORTED_ADAPTER] = { NULL }; + time_t t; + struct tm tm; + unsigned long num_packets = 0; + + btsnoop_read_file = btsnoop_open(input, BTSNOOP_FLAG_PKLG_SUPPORT); + if (!btsnoop_read_file) + return; + + type = btsnoop_get_format(btsnoop_read_file); + if (type != BTSNOOP_FORMAT_MONITOR) { + fprintf(stderr, "unsupported link data type %u\n", type); + btsnoop_unref(btsnoop_read_file); + return; + } + +next_packet: + if (!btsnoop_read_hci(btsnoop_read_file, &tv, &index, &opcode, buf, + &pktlen)) + goto close_files; + + if (opcode == 0xffff || index >= MAX_SUPPORTED_ADAPTER) + goto next_packet; + + switch (opcode) { + case BTSNOOP_OPCODE_NEW_INDEX: + t = tv.tv_sec; + localtime_r(&t, &tm); + + if (max_index < index) + max_index = index; + + sprintf(write_file_name, "%s%d_%02d:%02d:%02d.%06lu.log", + BT_SNOOP_TYPE_HCI_PREFIX, index, tm.tm_hour, tm.tm_min, + tm.tm_sec, tv.tv_usec); + + printf("New Index %d would be saved in %s\n", index, + write_file_name); + + btsnoop_write_file[index] = btsnoop_create(write_file_name, + BTSNOOP_FORMAT_HCI, -1, -1); + if (!btsnoop_write_file[index]) + goto close_files; + + break; + case BTSNOOP_OPCODE_DEL_INDEX: + printf("Del Index %d\n", index); + + btsnoop_unref(btsnoop_write_file[index]); + btsnoop_write_file[index] = NULL; + break; + + case BTSNOOP_OPCODE_COMMAND_PKT: + case BTSNOOP_OPCODE_EVENT_PKT: + case BTSNOOP_OPCODE_ACL_TX_PKT: + case BTSNOOP_OPCODE_ACL_RX_PKT: + case BTSNOOP_OPCODE_SCO_TX_PKT: + case BTSNOOP_OPCODE_SCO_RX_PKT: + if (!btsnoop_write_file[index]) { + t = tv.tv_sec; + localtime_r(&t, &tm); + + if (max_index < index) + max_index = index; + + sprintf(write_file_name, "%s%d_%02d:%02d:%02d.%06lu.log", + BT_SNOOP_TYPE_HCI_PREFIX, index, + tm.tm_hour, tm.tm_min, + tm.tm_sec, tv.tv_usec); + + printf("New Index %d would be saved in %s\n", index, + write_file_name); + + btsnoop_write_file[index] = btsnoop_create(write_file_name, + BTSNOOP_FORMAT_HCI, -1, -1); + } + + if (!btsnoop_write_file[index]) + goto close_files; + btsnoop_write_hci(btsnoop_write_file[index], &tv, index, 0, + opcode, buf, pktlen); + break; + default: + printf("skip btmon opcode(%d)\n",opcode); + } + num_packets++; + + goto next_packet; + +close_files: + for (index = 0; index < max_index; index++) + btsnoop_unref(btsnoop_write_file[index]); + + btsnoop_unref(btsnoop_read_file); + + printf("BT Snoop data link transfer is completed for %lu packets\n", + num_packets); +} +#endif static void command_extract_eir(const char *input) { @@ -518,6 +634,9 @@ static void usage(void) printf("commands:\n" "\t-m, --merge Merge multiple btsnoop files\n" "\t-e, --extract Extract data from btsnoop file\n" +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + "\t-s, --split Split btmon file into legacy btsnoop file(s)\n" +#endif "\t-h, --help Show help options\n"); } @@ -525,12 +644,19 @@ static const struct option main_options[] = { { "merge", required_argument, NULL, 'm' }, { "extract", required_argument, NULL, 'e' }, { "type", required_argument, NULL, 't' }, +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + { "split", required_argument, NULL, 's' }, +#endif { "version", no_argument, NULL, 'v' }, { "help", no_argument, NULL, 'h' }, { } }; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +enum { INVALID, MERGE, EXTRACT, SPLIT }; +#else enum { INVALID, MERGE, EXTRACT }; +#endif int main(int argc, char *argv[]) { @@ -541,8 +667,11 @@ int main(int argc, char *argv[]) for (;;) { int opt; - +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + opt = getopt_long(argc, argv, "m:e:s:t:vh", main_options, NULL); +#else opt = getopt_long(argc, argv, "m:e:t:vh", main_options, NULL); +#endif if (opt < 0) break; @@ -555,6 +684,11 @@ int main(int argc, char *argv[]) command = EXTRACT; input_path = optarg; break; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + case 's': + command = SPLIT; + input_path = optarg; +#endif case 't': type = optarg; break; @@ -600,6 +734,16 @@ int main(int argc, char *argv[]) fprintf(stderr, "extract type not supported\n"); break; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + case SPLIT: + if (argc - optind > 0) { + fprintf(stderr, "extra arguments not allowed\n"); + return EXIT_FAILURE; + } + + command_split(input_path); + break; +#endif default: usage(); return EXIT_FAILURE; diff --git a/tools/example.psr b/tools/example.psr new file mode 100755 index 0000000..bbbec73 --- /dev/null +++ b/tools/example.psr @@ -0,0 +1,12 @@ +// PSKEY_BDADDR +&0001 = 0001 2821 005b 6789 +// PSKEY_ANA_FTRIM +&01f6 = 0025 +// PSKEY_HOST_INTERFACE +&01f9 = 0001 +// PSKEY_UART_BAUD_RATE +&0204 = 01d8 +// PSKEY_ANA_FREQ +&01fe = 0004 +// PSKEY_UART_CONFIG +&0205 = 0006 diff --git a/tools/gatt-example b/tools/gatt-example new file mode 100755 index 0000000..a6f5cbe --- /dev/null +++ b/tools/gatt-example @@ -0,0 +1,533 @@ +#!/usr/bin/python + +import dbus +import dbus.exceptions +import dbus.mainloop.glib +import dbus.service + +import array +import gobject + +from random import randint + +mainloop = None + +BLUEZ_SERVICE_NAME = 'org.bluez' +GATT_MANAGER_IFACE = 'org.bluez.GattManager1' +DBUS_OM_IFACE = 'org.freedesktop.DBus.ObjectManager' +DBUS_PROP_IFACE = 'org.freedesktop.DBus.Properties' + +GATT_SERVICE_IFACE = 'org.bluez.GattService1' +GATT_CHRC_IFACE = 'org.bluez.GattCharacteristic1' +GATT_DESC_IFACE = 'org.bluez.GattDescriptor1' + +class InvalidArgsException(dbus.exceptions.DBusException): + _dbus_error_name = 'org.freedesktop.DBus.Error.InvalidArgs' + +class NotSupportedException(dbus.exceptions.DBusException): + _dbus_error_name = 'org.bluez.Error.NotSupported' + +class NotPermittedException(dbus.exceptions.DBusException): + _dbus_error_name = 'org.bluez.Error.NotPermitted' + +class InvalidValueLengthException(dbus.exceptions.DBusException): + _dbus_error_name = 'org.bluez.Error.InvalidValueLength' + +class FailedException(dbus.exceptions.DBusException): + _dbus_error_name = 'org.bluez.Error.Failed' + + +class Service(dbus.service.Object): + PATH_BASE = '/org/bluez/example/service' + + def __init__(self, bus, index, uuid, primary): + self.path = self.PATH_BASE + str(index) + self.bus = bus + self.uuid = uuid + self.primary = primary + self.characteristics = [] + dbus.service.Object.__init__(self, bus, self.path) + + def get_properties(self): + return { + GATT_SERVICE_IFACE: { + 'UUID': self.uuid, + 'Primary': self.primary, + 'Characteristics': dbus.Array( + self.get_characteristic_paths(), + signature='o') + } + } + + def get_path(self): + return dbus.ObjectPath(self.path) + + def add_characteristic(self, characteristic): + self.characteristics.append(characteristic) + + def get_characteristic_paths(self): + result = [] + for chrc in self.characteristics: + result.append(chrc.get_path()) + return result + + def get_characteristics(self): + return self.characteristics + + @dbus.service.method(DBUS_PROP_IFACE, + in_signature='s', + out_signature='a{sv}') + def GetAll(self, interface): + if interface != GATT_SERVICE_IFACE: + raise InvalidArgsException() + + return self.get_properties[GATT_SERVICE_IFACE] + + @dbus.service.method(DBUS_OM_IFACE, out_signature='a{oa{sa{sv}}}') + def GetManagedObjects(self): + response = {} + print 'GetManagedObjects' + + response[self.get_path()] = self.get_properties() + chrcs = self.get_characteristics() + for chrc in chrcs: + response[chrc.get_path()] = chrc.get_properties() + descs = chrc.get_descriptors() + for desc in descs: + response[desc.get_path()] = desc.get_properties() + + return response + + +class Characteristic(dbus.service.Object): + def __init__(self, bus, index, uuid, flags, service): + self.path = service.path + '/char' + str(index) + self.bus = bus + self.uuid = uuid + self.service = service + self.flags = flags + self.descriptors = [] + dbus.service.Object.__init__(self, bus, self.path) + + def get_properties(self): + return { + GATT_CHRC_IFACE: { + 'Service': self.service.get_path(), + 'UUID': self.uuid, + 'Flags': self.flags, + 'Descriptors': dbus.Array( + self.get_descriptor_paths(), + signature='o') + } + } + + def get_path(self): + return dbus.ObjectPath(self.path) + + def add_descriptor(self, descriptor): + self.descriptors.append(descriptor) + + def get_descriptor_paths(self): + result = [] + for desc in self.descriptors: + result.append(desc.get_path()) + return result + + def get_descriptors(self): + return self.descriptors + + @dbus.service.method(DBUS_PROP_IFACE, + in_signature='s', + out_signature='a{sv}') + def GetAll(self, interface): + if interface != GATT_CHRC_IFACE: + raise InvalidArgsException() + + return self.get_properties[GATT_CHRC_IFACE] + + @dbus.service.method(GATT_CHRC_IFACE, out_signature='ay') + def ReadValue(self): + print 'Default ReadValue called, returning error' + raise NotSupportedException() + + @dbus.service.method(GATT_CHRC_IFACE, in_signature='ay') + def WriteValue(self, value): + print 'Default WriteValue called, returning error' + raise NotSupportedException() + + @dbus.service.method(GATT_CHRC_IFACE) + def StartNotify(self): + print 'Default StartNotify called, returning error' + raise NotSupportedException() + + @dbus.service.method(GATT_CHRC_IFACE) + def StopNotify(self): + print 'Default StopNotify called, returning error' + raise NotSupportedException() + + @dbus.service.signal(DBUS_PROP_IFACE, + signature='sa{sv}as') + def PropertiesChanged(self, interface, changed, invalidated): + pass + + +class Descriptor(dbus.service.Object): + def __init__(self, bus, index, uuid, characteristic): + self.path = characteristic.path + '/desc' + str(index) + self.bus = bus + self.uuid = uuid + self.chrc = characteristic + dbus.service.Object.__init__(self, bus, self.path) + + def get_properties(self): + return { + GATT_DESC_IFACE: { + 'Characteristic': self.chrc.get_path(), + 'UUID': self.uuid, + } + } + + def get_path(self): + return dbus.ObjectPath(self.path) + + @dbus.service.method(DBUS_PROP_IFACE, + in_signature='s', + out_signature='a{sv}') + def GetAll(self, interface): + if interface != GATT_DESC_IFACE: + raise InvalidArgsException() + + return self.get_properties[GATT_CHRC_IFACE] + + @dbus.service.method(GATT_DESC_IFACE, out_signature='ay') + def ReadValue(self): + print 'Default ReadValue called, returning error' + raise NotSupportedException() + + @dbus.service.method(GATT_DESC_IFACE, in_signature='ay') + def WriteValue(self, value): + print 'Default WriteValue called, returning error' + raise NotSupportedException() + + +class HeartRateService(Service): + """ + Fake Heart Rate Service that simulates a fake heart beat and control point + behavior. + + """ + HR_UUID = '0000180d-0000-1000-8000-00805f9b34fb' + + def __init__(self, bus, index): + Service.__init__(self, bus, index, self.HR_UUID, True) + self.add_characteristic(HeartRateMeasurementChrc(bus, 0, self)) + self.add_characteristic(BodySensorLocationChrc(bus, 1, self)) + self.add_characteristic(HeartRateControlPointChrc(bus, 2, self)) + self.energy_expended = 0 + + +class HeartRateMeasurementChrc(Characteristic): + HR_MSRMT_UUID = '00002a37-0000-1000-8000-00805f9b34fb' + + def __init__(self, bus, index, service): + Characteristic.__init__( + self, bus, index, + self.HR_MSRMT_UUID, + ['notify'], + service) + self.notifying = False + self.hr_ee_count = 0 + + def hr_msrmt_cb(self): + value = [] + value.append(dbus.Byte(0x06)) + + value.append(dbus.Byte(randint(90, 130))) + + if self.hr_ee_count % 10 == 0: + value[0] = dbus.Byte(value[0] | 0x08) + value.append(dbus.Byte(self.service.energy_expended & 0xff)) + value.append(dbus.Byte((self.service.energy_expended >> 8) & 0xff)) + + self.service.energy_expended = \ + min(0xffff, self.service.energy_expended + 1) + self.hr_ee_count += 1 + + print 'Updating value: ' + repr(value) + + self.PropertiesChanged(GATT_CHRC_IFACE, { 'Value': value }, []) + + return self.notifying + + def _update_hr_msrmt_simulation(self): + print 'Update HR Measurement Simulation' + + if not self.notifying: + return + + gobject.timeout_add(1000, self.hr_msrmt_cb) + + def StartNotify(self): + if self.notifying: + print 'Already notifying, nothing to do' + return + + self.notifying = True + self._update_hr_msrmt_simulation() + + def StopNotify(self): + if not self.notifying: + print 'Not notifying, nothing to do' + return + + self.notifying = False + self._update_hr_msrmt_simulation() + + +class BodySensorLocationChrc(Characteristic): + BODY_SNSR_LOC_UUID = '00002a38-0000-1000-8000-00805f9b34fb' + + def __init__(self, bus, index, service): + Characteristic.__init__( + self, bus, index, + self.BODY_SNSR_LOC_UUID, + ['read'], + service) + + def ReadValue(self): + # Return 'Chest' as the sensor location. + return [ 0x01 ] + +class HeartRateControlPointChrc(Characteristic): + HR_CTRL_PT_UUID = '00002a39-0000-1000-8000-00805f9b34fb' + + def __init__(self, bus, index, service): + Characteristic.__init__( + self, bus, index, + self.HR_CTRL_PT_UUID, + ['write'], + service) + + def WriteValue(self, value): + print 'Heart Rate Control Point WriteValue called' + + if len(value) != 1: + raise InvalidValueLengthException() + + byte = value[0] + print 'Control Point value: ' + repr(byte) + + if byte != 1: + raise FailedException("0x80") + + print 'Energy Expended field reset!' + self.service.energy_expended = 0 + + +class BatteryService(Service): + """ + Fake Battery service that emulates a draining battery. + + """ + BATTERY_UUID = '180f' + + def __init__(self, bus, index): + Service.__init__(self, bus, index, self.BATTERY_UUID, True) + self.add_characteristic(BatteryLevelCharacteristic(bus, 0, self)) + + +class BatteryLevelCharacteristic(Characteristic): + """ + Fake Battery Level characteristic. The battery level is drained by 2 points + every 5 seconds. + + """ + BATTERY_LVL_UUID = '2a19' + + def __init__(self, bus, index, service): + Characteristic.__init__( + self, bus, index, + self.BATTERY_LVL_UUID, + ['read', 'notify'], + service) + self.notifying = False + self.battery_lvl = 100 + gobject.timeout_add(5000, self.drain_battery) + + def notify_battery_level(self): + if not self.notifying: + return + self.PropertiesChanged( + GATT_CHRC_IFACE, + { 'Value': [dbus.Byte(self.battery_lvl)] }, []) + + def drain_battery(self): + if self.battery_lvl > 0: + self.battery_lvl -= 2 + if self.battery_lvl < 0: + self.battery_lvl = 0 + print 'Battery Level drained: ' + repr(self.battery_lvl) + self.notify_battery_level() + return True + + def ReadValue(self): + print 'Battery Level read: ' + repr(self.battery_lvl) + return [dbus.Byte(self.battery_lvl)] + + def StartNotify(self): + if self.notifying: + print 'Already notifying, nothing to do' + return + + self.notifying = True + self.notify_battery_level() + + def StopNotify(self): + if not self.notifying: + print 'Not notifying, nothing to do' + return + + self.notifying = False + + +class TestService(Service): + """ + Dummy test service that provides characteristics and descriptors that + exercise various API functionality. + + """ + TEST_SVC_UUID = '12345678-1234-5678-1234-56789abcdef0' + + def __init__(self, bus, index): + Service.__init__(self, bus, index, self.TEST_SVC_UUID, False) + self.add_characteristic(TestCharacteristic(bus, 0, self)) + + +class TestCharacteristic(Characteristic): + """ + Dummy test characteristic. Allows writing arbitrary bytes to its value, and + contains "extended properties", as well as a test descriptor. + + """ + TEST_CHRC_UUID = '12345678-1234-5678-1234-56789abcdef1' + + def __init__(self, bus, index, service): + Characteristic.__init__( + self, bus, index, + self.TEST_CHRC_UUID, + ['read', 'write', 'writable-auxiliaries'], + service) + self.value = [] + self.add_descriptor(TestDescriptor(bus, 0, self)) + self.add_descriptor( + CharacteristicUserDescriptionDescriptor(bus, 1, self)) + + def ReadValue(self): + print 'TestCharacteristic Read: ' + repr(self.value) + return self.value + + def WriteValue(self, value): + print 'TestCharacteristic Write: ' + repr(value) + self.value = value + + +class TestDescriptor(Descriptor): + """ + Dummy test descriptor. Returns a static value. + + """ + TEST_DESC_UUID = '12345678-1234-5678-1234-56789abcdef2' + + def __init__(self, bus, index, characteristic): + Descriptor.__init__( + self, bus, index, + self.TEST_DESC_UUID, + characteristic) + + def ReadValue(self): + return [ + dbus.Byte('T'), dbus.Byte('e'), dbus.Byte('s'), dbus.Byte('t') + ] + + +class CharacteristicUserDescriptionDescriptor(Descriptor): + """ + Writable CUD descriptor. + + """ + CUD_UUID = '2901' + + def __init__(self, bus, index, characteristic): + self.writable = 'writable-auxiliaries' in characteristic.flags + self.value = array.array('B', 'This is a characteristic for testing') + self.value = self.value.tolist() + Descriptor.__init__( + self, bus, index, + self.CUD_UUID, + characteristic) + + def ReadValue(self): + return self.value + + def WriteValue(self, value): + if not self.writable: + raise NotPermittedException() + self.value = value + + +def register_service_cb(): + print 'GATT service registered' + + +def register_service_error_cb(error): + print 'Failed to register service: ' + str(error) + mainloop.quit() + + +def find_adapter(bus): + remote_om = dbus.Interface(bus.get_object(BLUEZ_SERVICE_NAME, '/'), + DBUS_OM_IFACE) + objects = remote_om.GetManagedObjects() + + for o, props in objects.iteritems(): + if props.has_key(GATT_MANAGER_IFACE): + return o + + return None + +def main(): + global mainloop + + dbus.mainloop.glib.DBusGMainLoop(set_as_default=True) + + bus = dbus.SystemBus() + + adapter = find_adapter(bus) + if not adapter: + print 'GattManager1 interface not found' + return + + service_manager = dbus.Interface( + bus.get_object(BLUEZ_SERVICE_NAME, adapter), + GATT_MANAGER_IFACE) + + hr_service = HeartRateService(bus, 0) + bat_service = BatteryService(bus, 1) + test_service = TestService(bus, 2) + + mainloop = gobject.MainLoop() + + service_manager.RegisterService(hr_service.get_path(), {}, + reply_handler=register_service_cb, + error_handler=register_service_error_cb) + service_manager.RegisterService(bat_service.get_path(), {}, + reply_handler=register_service_cb, + error_handler=register_service_error_cb) + service_manager.RegisterService(test_service.get_path(), {}, + reply_handler=register_service_cb, + error_handler=register_service_error_cb) + + mainloop.run() + +if __name__ == '__main__': + main() diff --git a/tools/hciattach.c b/tools/hciattach.c index fad176c..0ba56e7 100755 --- a/tools/hciattach.c +++ b/tools/hciattach.c @@ -27,6 +27,9 @@ #include #endif +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY +#define _GNU_SOURCE +#endif #include #include #include @@ -49,6 +52,7 @@ #include "src/shared/tty.h" #include "hciattach.h" +#include "../profile.h" struct uart_t { char *type; @@ -62,8 +66,26 @@ struct uart_t { char *bdaddr; int (*init) (int fd, struct uart_t *u, struct termios *ti); int (*post) (int fd, struct uart_t *u, struct termios *ti); + +/* TIZEN_FEATURE_BLUEZ_MODIFY */ +#if defined TIZEN_FEATURE_BLUEZ_MODIFY && defined __TI_PATCH__ + uint16_t device_param; +#endif }; +#if defined TIZEN_FEATURE_BLUEZ_MODIFY && defined __TI_PATCH__ + int firmware_path = 0; +#endif + +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#if defined(__TI_PATCH__) || 1 +#define TIOSETBRFPOWER 0x6000 +#define BRF_DEEP_SLEEP_OPCODE_BYTE_1 0x0c +#define BRF_DEEP_SLEEP_OPCODE_BYTE_2 0xfd +#define BRF_DEEP_SLEEP_OPCODE \ + (BRF_DEEP_SLEEP_OPCODE_BYTE_1 | (BRF_DEEP_SLEEP_OPCODE_BYTE_2 << 8)) +#endif +#endif #define FLOW_CTL 0x0001 #define AMP_DEV 0x0002 #define ENABLE_PM 1 @@ -946,40 +968,78 @@ static int bcm2035(int fd, struct uart_t *u, struct termios *ti) /* Set the baud rate */ memset(cmd, 0, sizeof(cmd)); memset(resp, 0, sizeof(resp)); - cmd[0] = HCI_COMMAND_PKT; - cmd[1] = 0x18; - cmd[2] = 0xfc; - cmd[3] = 0x02; - switch (u->speed) { - case 57600: - cmd[4] = 0x00; - cmd[5] = 0xe6; - break; - case 230400: - cmd[4] = 0x22; - cmd[5] = 0xfa; - break; - case 460800: - cmd[4] = 0x22; - cmd[5] = 0xfd; - break; - case 921600: - cmd[4] = 0x55; - cmd[5] = 0xff; - break; - default: - /* Default is 115200 */ + +/* TIZEN_FEATURE_BLUEZ_MODIFY */ +#if defined(TIZEN_FEATURE_BLUEZ_MODIFY) + if (TIZEN_FEATURE_BLUEZ_BRCM_CHIP) { + cmd[0] = HCI_COMMAND_PKT; + cmd[1] = 0x18; + cmd[2] = 0xfc; + cmd[3] = 0x02; + switch (u->speed) { + case 57600: + cmd[4] = 0x00; + cmd[5] = 0xe6; + break; + case 230400: + cmd[4] = 0x22; + cmd[5] = 0xfa; + break; + case 460800: + cmd[4] = 0x22; + cmd[5] = 0xfd; + break; + case 921600: + cmd[4] = 0x55; + cmd[5] = 0xff; + break; + default: + /* Default is 115200 */ + cmd[4] = 0x00; + cmd[5] = 0xf3; + break; + } + fprintf(stderr, "Baud rate parameters: DHBR=0x%2x,DLBR=0x%2x\n", + cmd[4], cmd[5]); + + /* Send command */ + if (write(fd, cmd, 6) != 6) { + fprintf(stderr, "Failed to write \"set baud rate\" command\n"); + return -1; + } + } else +#endif + { + cmd[0] = HCI_COMMAND_PKT; + cmd[1] = 0x18; + cmd[2] = 0xfc; + + switch (u->speed) { + case 57600: + case 230400: + case 460800: + case 921600: + case 3000000: + break; + default: + break; + } + + cmd[3] = 0x06; cmd[4] = 0x00; - cmd[5] = 0xf3; - break; - } - fprintf(stderr, "Baud rate parameters: DHBR=0x%2x,DLBR=0x%2x\n", - cmd[4], cmd[5]); + cmd[5] = 0x00; + cmd[6] = u->speed & 0xFF; + cmd[7] = (u->speed >> 8) & 0xFF; + cmd[8] = (u->speed >> 16) & 0xFF; + cmd[9] = (u->speed >> 24) & 0xFF; - /* Send command */ - if (write(fd, cmd, 6) != 6) { - fprintf(stderr, "Failed to write \"set baud rate\" command\n"); - return -1; + fprintf(stderr, "Set the baud rate %d : 0x%02x,0x%02x,0x%02x,0x%02x\n",u->speed,cmd[6],cmd[7],cmd[8],cmd[9] ); + + /* Send command */ + if (write(fd, cmd, 10) != 10) { + fprintf(stderr, "Failed to write \"set baud rate\" command\n"); + return -1; + } } if ((n = read_hci_event(fd, resp, 6)) < 0) { @@ -990,10 +1050,21 @@ static int bcm2035(int fd, struct uart_t *u, struct termios *ti) return 0; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY /*SPRD add Start*/ +static int init_sprd_config(int fd, struct uart_t *u, struct termios *ti) +{ + + return sprd_config_init(fd, u->bdaddr, ti); +} +#endif + struct uart_t uart[] = { { "any", 0x0000, 0x0000, HCI_UART_H4, 115200, 115200, FLOW_CTL, DISABLE_PM, NULL, NULL }, - +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY /*SPRD*/ + { "sprd", 0x0000, 0x0000, HCI_UART_H4, 3000000, 3000000, + FLOW_CTL, DISABLE_PM, NULL, init_sprd_config }, +#endif { "ericsson", 0x0000, 0x0000, HCI_UART_H4, 57600, 115200, FLOW_CTL, DISABLE_PM, NULL, ericsson }, @@ -1019,12 +1090,18 @@ struct uart_t uart[] = { { "swave", 0x0000, 0x0000, HCI_UART_H4, 115200, 115200, FLOW_CTL, DISABLE_PM, NULL, swave }, +/* TIZEN_FEATURE_BLUEZ_MODIFY */ +#if defined TIZEN_FEATURE_BLUEZ_MODIFY && defined __TI_PATCH__ + /* Texas Instruments BRF63xx modules */ + { "texas", 0x0000, 0x0000, HCI_UART_LL, 115200,3000000, FLOW_CTL, NULL, texas, NULL/*texas_continue_script*/, BRF_DEEP_SLEEP_OPCODE}, +#else /* Texas Instruments Bluelink (BRF) modules */ { "texas", 0x0000, 0x0000, HCI_UART_LL, 115200, 115200, FLOW_CTL, DISABLE_PM, NULL, texas, texas2 }, { "texasalt", 0x0000, 0x0000, HCI_UART_LL, 115200, 115200, FLOW_CTL, DISABLE_PM, NULL, texasalt, NULL }, +#endif /* ST Microelectronics minikits based on STLC2410/STLC2415 */ { "st", 0x0000, 0x0000, HCI_UART_H4, 57600, 115200, @@ -1132,6 +1209,46 @@ static struct uart_t * get_by_type(char *type) return NULL; } +#if defined TIZEN_FEATURE_BLUEZ_MODIFY +static int enable_hci(char *dev, struct uart_t *u) +{ + int fd, i; + unsigned long flags = 0; + + fd = open(dev, O_RDWR | O_NOCTTY); + if (fd < 0) { + fprintf(stderr, "Can't open serial port"); + return -1; + } + + tcflush(fd, TCIOFLUSH); + + /* Set TTY to N_HCI line discipline */ + i = N_HCI; + if (ioctl(fd, TIOCSETD, &i) < 0) { + fprintf(stderr, "Can't set line discipline"); + close(fd); + return -1; + } + + if (flags && ioctl(fd, HCIUARTSETFLAGS, flags) < 0) { + fprintf(stderr, "Can't set UART flags"); + close(fd); + return -1; + } + + tcflush(fd, TCIOFLUSH); + + if (ioctl(fd, HCIUARTSETPROTO, u->proto) < 0) { + fprintf(stderr, "Can't set device"); + close(fd); + return -1; + } + + return fd; +} +#endif /* TIZEN_FEATURE_BLUEZ_MODIFY */ + /* Initialize UART driver */ static int init_uart(char *dev, struct uart_t *u, int send_break, int raw) { @@ -1139,6 +1256,13 @@ static int init_uart(char *dev, struct uart_t *u, int send_break, int raw) int fd, i; unsigned long flags = 0; +/* TIZEN_FEATURE_BLUEZ_MODIFY */ +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#if defined(__TI_PATCH__) || 1 + int power; +#endif +#endif + if (raw) flags |= 1 << HCI_UART_RAW_DEVICE; @@ -1160,11 +1284,16 @@ static int init_uart(char *dev, struct uart_t *u, int send_break, int raw) cfmakeraw(&ti); - ti.c_cflag |= CLOCAL; - if (u->flags & FLOW_CTL) - ti.c_cflag |= CRTSCTS; - else - ti.c_cflag &= ~CRTSCTS; +/* TIZEN_FEATURE_BLUEZ_MODIFY */ +#if defined(TIZEN_FEATURE_BLUEZ_MODIFY) + if (!TIZEN_FEATURE_BLUEZ_BRCM_CHIP) { + ti.c_cflag |= CLOCAL; + if (u->flags & FLOW_CTL) + ti.c_cflag |= CRTSCTS; + else + ti.c_cflag &= ~CRTSCTS; + } +#endif if (tcsetattr(fd, TCSANOW, &ti) < 0) { perror("Can't set port settings"); @@ -1184,6 +1313,24 @@ static int init_uart(char *dev, struct uart_t *u, int send_break, int raw) usleep(500000); } +/* TIZEN_FEATURE_BLUEZ_MODIFY */ +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#if defined(__TI_PATCH__) + /* Power up the BRF chip */ + power = 1; + ioctl(fd, TIOSETBRFPOWER, &power); +#else + if (TIZEN_FEATURE_BLUEZ_BRCM_CHIP) { + /* Power up the BRF chip */ + power = 1; + ioctl(fd, TIOSETBRFPOWER, &power); + } +#endif +#ifdef __TI_PATCH__ + usleep(500000); +#endif +#endif + if (u->init && u->init(fd, u, &ti) < 0) goto fail; @@ -1212,8 +1359,10 @@ static int init_uart(char *dev, struct uart_t *u, int send_break, int raw) goto fail; } +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY if (u->post && u->post(fd, u, &ti) < 0) goto fail; +#endif return fd; @@ -1226,33 +1375,69 @@ static void usage(void) { printf("hciattach - HCI UART driver initialization utility\n"); printf("Usage:\n"); + +/* TIZEN_FEATURE_BLUEZ_MODIFY */ +#if defined(TIZEN_FEATURE_BLUEZ_MODIFY) && !defined(__TI_PATCH__) +/* This commented code was present before bluez 5.25 upgrade + * printf("\thciattach [-n] [-p] [-b] [-g device_param] [-r] [-f] [-t timeout] [-s initial_speed] [speed] [flow|noflow] [bdaddr]\n");*/ + printf("\thciattach [-n] [-p] [-b] [-g device_param] [-r] [-f]" + " [-t timeout] [-s initial_speed]" + " [speed] [flow|noflow]" + " [sleep|nosleep] [bdaddr]\n"); +#else printf("\thciattach [-n] [-p] [-b] [-r] [-t timeout] [-s initial_speed]" " [speed] [flow|noflow]" " [sleep|nosleep] [bdaddr]\n"); +#endif printf("\thciattach -l\n"); } int main(int argc, char *argv[]) { struct uart_t *u = NULL; +#if defined(TIZEN_FEATURE_BLUEZ_MODIFY) int detach, printpid, raw, opt, i, n, ld, err; +#else + int detach, printpid, opt, i, n, ld, err; +#endif int to = 10; int init_speed = 0; +#if defined(TIZEN_FEATURE_BLUEZ_MODIFY) int send_break = 0; +#endif pid_t pid; struct sigaction sa; struct pollfd p; sigset_t sigs; char dev[PATH_MAX]; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#if defined(__TI_PATCH__) || 1 +/* TIZEN_FEATURE_BLUEZ_MODIFY */ + int power; +#endif +#endif +#ifdef __TI_PATCH__ + uint16_t device_param = 0; + int reset_device = 0; + int bt_fd; +#endif detach = 1; printpid = 0; +#if defined(TIZEN_FEATURE_BLUEZ_MODIFY) raw = 0; - +#endif +#if defined(TIZEN_FEATURE_BLUEZ_MODIFY) && !defined(__TI_PATCH__) + while ((opt=getopt(argc, argv, "bnprft:g:s:l")) != EOF) { +#else while ((opt=getopt(argc, argv, "bnpt:s:lr")) != EOF) { +#endif switch(opt) { case 'b': - send_break = 1; +#if defined(TIZEN_FEATURE_BLUEZ_MODIFY) + if (!TIZEN_FEATURE_BLUEZ_BRCM_CHIP) + send_break = 1; +#endif break; case 'n': @@ -1267,6 +1452,20 @@ int main(int argc, char *argv[]) to = atoi(optarg); break; +/* TIZEN_FEATURE_BLUEZ_MODIFY */ +#if defined(TIZEN_FEATURE_BLUEZ_MODIFY) && defined(__TI_PATCH__) + case 'g': + device_param = (uint16_t)strtol(optarg, NULL, 16); + break; + + case 'r': + reset_device = 1; + break; + + case 'f': + firmware_path = 1; + break; +#endif case 's': init_speed = atoi(optarg); break; @@ -1279,7 +1478,10 @@ int main(int argc, char *argv[]) exit(0); case 'r': - raw = 1; +#if defined(TIZEN_FEATURE_BLUEZ_MODIFY) + if (!TIZEN_FEATURE_BLUEZ_BRCM_CHIP) + raw = 1; +#endif break; default: @@ -1289,6 +1491,10 @@ int main(int argc, char *argv[]) } n = argc - optind; +/* TIZEN_FEATURE_BLUEZ_MODIFY */ +#if defined(TIZEN_FEATURE_BLUEZ_MODIFY) && defined(__TI_PATCH__) + if (!reset_device || (reset_device && n < 1)) +#endif if (n < 2) { usage(); exit(1); @@ -1352,16 +1558,42 @@ int main(int argc, char *argv[]) break; } } +/* TIZEN_FEATURE_BLUEZ_MODIFY */ +#if defined(TIZEN_FEATURE_BLUEZ_MODIFY) && defined(__TI_PATCH__) + if (reset_device) + { + // Reset row device + bt_fd = open(dev, O_RDWR | O_NOCTTY); + if (bt_fd< 0) { + perror("Can't open serial port"); + return -1; + } + /* Power up the BRF chip */ + power = 0; + ioctl(bt_fd, TIOSETBRFPOWER, &power); + return 0; + } +#endif if (!u) { fprintf(stderr, "Unknown device type or id\n"); exit(1); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +// __hci_attach_log_init(); +#endif + /* If user specified a initial speed, use that instead of the hardware's default */ if (init_speed) u->init_speed = init_speed; +#if defined(TIZEN_FEATURE_BLUEZ_MODIFY) && defined(__TI_PATCH__) + /* If user specified a device parameter, use that instead of + the hardware's default */ + if (device_param) + u->device_param = device_param; +#endif memset(&sa, 0, sizeof(sa)); sa.sa_flags = SA_NOCLDSTOP; @@ -1371,8 +1603,14 @@ int main(int argc, char *argv[]) /* 10 seconds should be enough for initialization */ alarm(to); bcsp_max_retries = to; - +#if defined TIZEN_FEATURE_BLUEZ_MODIFY + if (TIZEN_FEATURE_BLUEZ_BRCM_CHIP) + n = enable_hci(dev, u); + else + n = init_uart(dev, u, send_break, raw); +#else n = init_uart(dev, u, send_break, raw); +#endif if (n < 0) { perror("Can't initialize device"); exit(1); @@ -1433,5 +1671,19 @@ int main(int argc, char *argv[]) exit(1); } +/* TIZEN_FEATURE_BLUEZ_MODIFY */ +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#if defined(__TI_PATCH__) + /* Power down the BRF or BCMchip */ + power = 0; + ioctl(n, TIOSETBRFPOWER, &power); +#else + if (TIZEN_FEATURE_BLUEZ_BRCM_CHIP) { + /* Power down the BRF or BCMchip */ + power = 0; + ioctl(n, TIOSETBRFPOWER, &power); + } +#endif +#endif return 0; } diff --git a/tools/hciattach.h b/tools/hciattach.h index 249aab4..9c94965 100755 --- a/tools/hciattach.h +++ b/tools/hciattach.h @@ -64,6 +64,9 @@ int stlc2500_init(int fd, bdaddr_t *bdaddr); int bgb2xx_init(int dd, bdaddr_t *bdaddr); int ath3k_init(int fd, int speed, int init_speed, char *bdaddr, struct termios *ti); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +int sprd_config_init(int fd, char *bdaddr, struct termios *ti); +#endif int ath3k_post(int fd, int pm); int qualcomm_init(int fd, int speed, struct termios *ti, const char *bdaddr); int intel_init(int fd, int init_speed, int *speed, struct termios *ti); diff --git a/tools/hciattach_bcm43xx.c b/tools/hciattach_bcm43xx.c index 81f38cb..af03538 100755 --- a/tools/hciattach_bcm43xx.c +++ b/tools/hciattach_bcm43xx.c @@ -43,8 +43,12 @@ #include "hciattach.h" #ifndef FIRMWARE_DIR +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#define FIRMWARE_DIR "/usr/etc/bluetooth" +#else #define FIRMWARE_DIR "/etc/firmware" #endif +#endif #define FW_EXT ".hcd" diff --git a/tools/hciattach_sprd.c b/tools/hciattach_sprd.c new file mode 100755 index 0000000..6fd2954 --- /dev/null +++ b/tools/hciattach_sprd.c @@ -0,0 +1,592 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include "hciattach.h" +#include + +#include "hciattach_sprd.h" + +//#include +//#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= '0' && ch <= '9') + *dest_ptr =(uint8)((ch - '0') << 4); + // a - f + else if (ch >= 'a' && ch <= 'f') + *dest_ptr = (uint8)((ch - 'a' + 10) << 4); + // A - F + else if (ch >= 'A' && ch <= 'F') + *dest_ptr = (uint8)((ch -'A' + 10) << 4); + else{ + return 0; + } + + // the bit 1,2,3,4 + ch = hex_ptr[i+1]; + // digtial 0 - 9 + if (ch >= '0' && ch <= '9') + *dest_ptr |= (uint8)(ch - '0'); + // a - f + else if (ch >= 'a' && ch <= 'f') + *dest_ptr |= (uint8)(ch - 'a' + 10); + // A - F + else if (ch >= 'A' && ch <= 'F') + *dest_ptr |= (uint8)(ch -'A' + 10); + else{ + return 0; + } + + dest_ptr++; + } + + return 1; +} + +static int read_mac_address(char *file_name, uint8 *addr) { + char buf[MAC_ADDR_FILE_LEN] = {0}; + uint32 addr_t[MAC_ADDR_LEN] = {0}; + int i = 0; + + +#if 1 + int fd = open(file_name, O_RDONLY, 0666); + LOGD("%s read file: %s", __func__, file_name); + if (fd < 0) { + LOGD("%s open %s error reason: %s", __func__, file_name, strerror(errno)); + return -1; + } + if (read(fd, buf, BD_ADDR_LEN) < 0) { + LOGD("%s read %s error reason: %s", __func__, file_name, strerror(errno)); + goto done; + } + if (sscanf(buf, "%02X%02X\n%02X\n%02X%02X%02X", &addr_t[0], &addr_t[1], &addr_t[2], &addr_t[3], &addr_t[4], &addr_t[5]) < 0) { + LOGD("%s sscanf %s error reason: %s", __func__, file_name, strerror(errno)); + goto done; + } + + for (i = 0; i < MAC_ADDR_LEN; i++) { + addr[i] = addr_t[i] & 0xFF; + } + LOGD("%s %s addr: [%02X:%02X:%02X:%02X:%02X:%02X]", __func__, file_name, addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]); + +done: + close(fd); +#endif + return 0; +} + +static void mac_address_stream_compose(uint8 *addr) { + uint8 tmp, i, j; + for (i = 0, j = MAC_ADDR_LEN - 1; (i < MAC_ADDR_LEN / 2) && (i != j); i++, j--) { + tmp = addr[i]; + addr[i] = addr[j]; + addr[j] = tmp; + } +} + +#if 0 +/* + * random bluetooth mac address + */ +static void random_mac_addr(uint8 *addr) { + int fd, randseed, ret, mac_rd; + uint8 addr_t[MAC_ADDR_LEN] = {0}; + + LOGD("%s", __func__); + /* urandom seed build */ + fd = open("/dev/urandom", O_RDONLY); + if (fd < 0){ + LOGD("%s: open urandom fail", __func__); + } else { + ret = read(fd, &randseed, sizeof(randseed)); + LOGD("%s urandom:0x%08X", __func__, randseed); + close(fd); + } + + /* time seed build */ + if (fd < 0 || ret < 0) { + struct timeval tt; + if (gettimeofday(&tt, (struct timezone *)0) > 0) { + randseed = (unsigned int) tt.tv_usec; + } else { + randseed = (unsigned int) time(NULL); + } + LOGD("urandom fail, using system time for randseed"); + } + + LOGD("%s: randseed = %u",__func__, randseed); + srand(randseed); + mac_rd = rand(); + + addr_t[0] = 0x40; /* FOR */ + addr_t[1] = 0x45; /* SPRD */ + addr_t[2] = 0xDA; /* ADDR */ + addr_t[3] = (uint8)(mac_rd & 0xFF); + addr_t[4] = (uint8)((mac_rd >> 8) & 0xFF); + addr_t[5] = (uint8)((mac_rd >> 16) & 0xFF); + + memcpy(addr, addr_t, MAC_ADDR_LEN); + LOGD("%s: MAC ADDR: [%02X:%02X:%02X:%02X:%02X:%02X]",__func__, addr_t[0], addr_t[1], addr_t[2], addr_t[3], addr_t[4], addr_t[5]); +} +#endif +static void get_mac_address(uint8 *addr){ + int ret = -1; + uint8 addr_t[6] = {0}; + char bt_mac[BD_ADDR_LEN] = {0, }; + + LOGD("%s", __func__); + /* check misc mac file exist */ + ret = access(DATMISC_MAC_ADDR_PATH, F_OK); + if (ret != 0) { + LOGD("%s %s miss", __func__, DATMISC_MAC_ADDR_PATH); + + /* Try to make bt address file */ + create_mac_folder(); + + mac_rand(bt_mac); + LOGD("bt random mac=%s",bt_mac); + write_btmac2file(bt_mac); + + } + + /* read mac file */ + read_mac_address(DATMISC_MAC_ADDR_PATH, addr_t); + + /* compose mac stream */ + mac_address_stream_compose(addr_t); + + memcpy(addr, addr_t, MAC_ADDR_LEN); + +} + + +/* + * hci command preload stream, special order + */ +static void pskey_stream_compose(uint8 * buf, BT_PSKEY_CONFIG_T *bt_par) { + int i = 0; + uint8 *p = buf; + + LOGD("%s", __func__); + + UINT24_TO_STREAM(p, bt_par->pskey_cmd); + UINT8_TO_STREAM(p, (uint8)(PSKEY_PREAMBLE_SIZE & 0xFF)); + + UINT8_TO_STREAM(p, bt_par->g_dbg_source_sink_syn_test_data); + UINT8_TO_STREAM(p, bt_par->g_sys_sleep_in_standby_supported); + UINT8_TO_STREAM(p, bt_par->g_sys_sleep_master_supported); + UINT8_TO_STREAM(p, bt_par->g_sys_sleep_slave_supported); + + UINT32_TO_STREAM(p, bt_par->default_ahb_clk); + UINT32_TO_STREAM(p, bt_par->device_class); + UINT32_TO_STREAM(p, bt_par->win_ext); + + for (i = 0; i < 6; i++) { + UINT32_TO_STREAM(p, bt_par->g_aGainValue[i]); + } + for (i = 0; i < 5; i++) { + UINT32_TO_STREAM(p, bt_par->g_aPowerValue[i]); + } + + for (i = 0; i < 16; i++) { + UINT8_TO_STREAM(p, bt_par->feature_set[i]); + } + for (i = 0; i < 6; i++) { + UINT8_TO_STREAM(p, bt_par->device_addr[i]); + } + + UINT8_TO_STREAM(p, bt_par->g_sys_sco_transmit_mode); + UINT8_TO_STREAM(p, bt_par->g_sys_uart0_communication_supported); + UINT8_TO_STREAM(p, bt_par->edr_tx_edr_delay); + UINT8_TO_STREAM(p, bt_par->edr_rx_edr_delay); + + UINT16_TO_STREAM(p, bt_par->g_wbs_nv_117); + + UINT32_TO_STREAM(p, bt_par->is_wdg_supported); + + UINT32_TO_STREAM(p, bt_par->share_memo_rx_base_addr); + //UINT32_TO_STREAM(p, bt_par->share_memo_tx_base_addr); + UINT16_TO_STREAM(p, bt_par->g_wbs_nv_118); + UINT16_TO_STREAM(p, bt_par->g_nbv_nv_117); + + UINT32_TO_STREAM(p, bt_par->share_memo_tx_packet_num_addr); + UINT32_TO_STREAM(p, bt_par->share_memo_tx_data_base_addr); + + UINT32_TO_STREAM(p, bt_par->g_PrintLevel); + + UINT16_TO_STREAM(p, bt_par->share_memo_tx_block_length); + UINT16_TO_STREAM(p, bt_par->share_memo_rx_block_length); + UINT16_TO_STREAM(p, bt_par->share_memo_tx_water_mark); + //UINT16_TO_STREAM(p, bt_par->share_memo_tx_timeout_value); + UINT16_TO_STREAM(p, bt_par->g_nbv_nv_118); + + UINT16_TO_STREAM(p, bt_par->uart_rx_watermark); + UINT16_TO_STREAM(p, bt_par->uart_flow_control_thld); + UINT32_TO_STREAM(p, bt_par->comp_id); + UINT16_TO_STREAM(p, bt_par->pcm_clk_divd); + + + for (i = 0; i < 8; i++) { + UINT32_TO_STREAM(p, bt_par->reserved[i]); + } +} + +void sprd_get_pskey(BT_PSKEY_CONFIG_T * pskey_t) { + BT_PSKEY_CONFIG_T pskey; + uint8 buf[180] = {0}; + + LOGD("%s", __func__); + memset(&pskey, 0 , sizeof(BT_PSKEY_CONFIG_T)); + if (bt_getPskeyFromFile(&pskey) < 0 ) { + LOGD("%s bt_getPskeyFromFile failed", __func__); + memcpy(pskey_t, &bt_para_setting, sizeof(BT_PSKEY_CONFIG_T)); + return; + } + + memset(buf, 0, PSKEY_PRELOAD_SIZE + PSKEY_PREAMBLE_SIZE); + + /* get bluetooth mac address */ + get_mac_address(pskey.device_addr); + + /* compose pskey hci command pkt */ + pskey_stream_compose(buf, &pskey); + + memcpy(pskey_t, &pskey, sizeof(BT_PSKEY_CONFIG_T)); +} + +#define HCI_HDR_LEN 3 + +int sprd_config_init(int fd, char *bdaddr, struct termios *ti) +{ + int ret = 0,r=0; + unsigned char resp[30] = {0}; + BT_PSKEY_CONFIG_T bt_para_tmp; + uint8 data_tmp[30] = {'a'}; + static int index = 0; + uint8 *buf = NULL; + uint8 hci_len = 0; + uint8 is_expected_hci_evt = 0; +#if 0 + char buffer; + int btsleep_fd_sprd = -1; +#endif + LOGD("sprd_config_init"); + +#if 0 + uart_fd = open(UART_INFO_PATH, O_WRONLY); + if(uart_fd > 0) + { + buffer = '2'; + if (write(uart_fd, &buffer, 1) < 0) + { + LOGD("%s write(%s) failed: %s (%d) 2", __func__, + UART_INFO_PATH, strerror(errno),errno); + } + + close(uart_fd); + } +#endif + +#if 0 + btsleep_fd_sprd = open(VENDOR_BTWRITE_PROC_NODE, O_WRONLY); + if (btsleep_fd_sprd < 0) + { + LOGD("%s open(%s) for write failed: %s (%d)", __func__, + VENDOR_BTWRITE_PROC_NODE, strerror(errno), errno); + } + else + { + buffer = '1'; + if (write(btsleep_fd_sprd, &buffer, 1) < 0) + { + LOGD("%s write(%s) failed: %s (%d)", __func__, + VENDOR_BTWRITE_PROC_NODE, strerror(errno),errno); + } + } +#endif + + ret = bt_getPskeyFromFile(&bt_para_tmp); + if (ret < 0) { + LOGD("init_sprd_config bt_getPskeyFromFile failed\n"); + memcpy(&bt_para_tmp, &bt_para_setting, sizeof(BT_PSKEY_CONFIG_T)); + } + + buf = (uint8 *)malloc(PSKEY_PRELOAD_SIZE + PSKEY_PREAMBLE_SIZE); + if (buf == NULL) { + LOGD("%s alloc stream memory failed", __func__); + return -1; + } + memset(buf, 0, PSKEY_PRELOAD_SIZE + PSKEY_PREAMBLE_SIZE); + + /* get bluetooth mac address */ + get_mac_address(bt_para_tmp.device_addr); + + /* compose pskey hci command pkt */ + pskey_stream_compose(buf, &bt_para_tmp); + + ret = write(fd, buf, PSKEY_PRELOAD_SIZE + PSKEY_PREAMBLE_SIZE); + LOGD("write pskey ret = %d", ret); + + free(buf); + buf = NULL; + + if (ret < 0) { + LOGD("%s write pskey stream failed", __func__); + return -1; + } + + memset(data_tmp, 0xff, sizeof(data_tmp)); + while (1) { + r = read(fd, resp, 1); + + if (r <= 0) + return -1; + else{ + data_tmp[index] = resp[0]; + LOGD("recive from controller 0x%x", data_tmp[index]); + ++index; + } + + if (index >= 6) { + hci_len = data_tmp[2]+HCI_HDR_LEN; + + if ((data_tmp[0] == 0x04) && (data_tmp[1] == 0xe) && + (data_tmp[2] == 0xa) &&(data_tmp[3] == 0x1) && + (data_tmp[4] == 0xa0) &&(data_tmp[5] == 0xfc)) { + LOGD("read response ok \n"); + is_expected_hci_evt = 1; + } else { + LOGD("this is not what we expect HCI evt\n"); + is_expected_hci_evt = 0; + } + + if (index == hci_len) { + index = 0; + memset(data_tmp, 0x0, sizeof(data_tmp)); + + if(is_expected_hci_evt) + break; + } + } + } + return 0; +} diff --git a/tools/hciattach_sprd.h b/tools/hciattach_sprd.h new file mode 100755 index 0000000..6e59408 --- /dev/null +++ b/tools/hciattach_sprd.h @@ -0,0 +1,82 @@ +#ifndef HCIATTACH_SPRD_H__ +#define HCIATTACH_SPRD_H__ + +#define MAC_ERROR "FF:FF:FF:FF:FF:FF" + +#define BT_MAC_FILE "/productinfo/btmac.txt" +//#define GET_BTMAC_ATCMD "AT+SNVM=0,401" +//#define GET_BTPSKEY_ATCMD "AT+SNVM=0,415" +//#define SET_BTMAC_ATCMD "AT+SNVM=1,401" +#define BT_RAND_MAC_LENGTH 17 + +// used to store BT pskey structure and default values +#define BT_PSKEY_STRUCT_FILE "/system/lib/modules/pskey_bt.txt" +//#define BT_PSKEY_FILE "/system/lib/modules/pskey_bt.txt" + + +typedef unsigned char uint8; +typedef unsigned int uint32; +typedef unsigned short uint16; + +#define BT_ADDRESS_SIZE 6 + + +typedef struct SPRD_BT_PSKEY_INFO_T{ + uint32 pskey_cmd; + + uint8 g_dbg_source_sink_syn_test_data; + uint8 g_sys_sleep_in_standby_supported; + uint8 g_sys_sleep_master_supported; + uint8 g_sys_sleep_slave_supported; + + uint32 default_ahb_clk; + uint32 device_class; + uint32 win_ext; + + uint32 g_aGainValue[6]; + uint32 g_aPowerValue[5]; + + uint8 feature_set[16]; + uint8 device_addr[6]; + + uint8 g_sys_sco_transmit_mode; //true tramsmit by uart, otherwise by share memory + uint8 g_sys_uart0_communication_supported; //true use uart0, otherwise use uart1 for debug + uint8 edr_tx_edr_delay; + uint8 edr_rx_edr_delay; + + uint16 g_wbs_nv_117; + + uint32 is_wdg_supported; + + uint32 share_memo_rx_base_addr; + + // uint32 share_memo_tx_base_addr; + uint16 g_wbs_nv_118; + uint16 g_nbv_nv_117; + + uint32 share_memo_tx_packet_num_addr; + uint32 share_memo_tx_data_base_addr; + + uint32 g_PrintLevel; + + uint16 share_memo_tx_block_length; + uint16 share_memo_rx_block_length; + uint16 share_memo_tx_water_mark; + + //uint16 share_memo_tx_timeout_value; + uint16 g_nbv_nv_118; + + uint16 uart_rx_watermark; + uint16 uart_flow_control_thld; + uint32 comp_id; + uint16 pcm_clk_divd; + + uint32 reserved[8]; +}BT_PSKEY_CONFIG_T; + + +#endif /* HCIATTACH_SPRD_H__ */ + + + + diff --git a/tools/hciattach_ti.c b/tools/hciattach_ti.c index 828dd61..b962677 100755 --- a/tools/hciattach_ti.c +++ b/tools/hciattach_ti.c @@ -55,7 +55,13 @@ #define TI_MANUFACTURER_ID 13 +/* TIZEN_FEATURE_BLUEZ_MODIFY */ +#ifdef __TI_PATCH__ +#define FIRMWARE_DIRECTORY1 "/mnt/mmc/" +#define FIRMWARE_DIRECTORY2 "/usr/etc/bluetooth/" +#else #define FIRMWARE_DIRECTORY "/lib/firmware/ti-connectivity/" +#endif #define ACTION_SEND_COMMAND 1 #define ACTION_WAIT_EVENT 2 @@ -197,7 +203,24 @@ static const char *get_firmware_name(const uint8_t *respond) if (version & 0x8000) maj_ver |= 0x0008; +/* TIZEN_FEATURE_BLUEZ_MODIFY */ +#ifdef __TI_PATCH__ + FILE *fp; + sprintf(firmware_file_name, FIRMWARE_DIRECTORY1 "TIInit_%d.%d.%d.bts", chip, maj_ver, min_ver); + + if ((fp = fopen(firmware_file_name, "r")) == NULL ) { + extern int firmware_path; + if (firmware_path) + sprintf(firmware_file_name, FIRMWARE_DIRECTORY2 "TIInit_edutm_%d.%d.%d.bts", chip, maj_ver, min_ver); + else + sprintf(firmware_file_name, FIRMWARE_DIRECTORY2 "TIInit_%d.%d.%d.bts", chip, maj_ver, min_ver); + } + else { + fclose(fp); + } +#else sprintf(firmware_file_name, FIRMWARE_DIRECTORY "TIInit_%d.%d.%d.bts", chip, maj_ver, min_ver); +#endif return firmware_file_name; } diff --git a/tools/hciconfig.c b/tools/hciconfig.c index 8a97cc4..b4a3856 100755 --- a/tools/hciconfig.c +++ b/tools/hciconfig.c @@ -93,7 +93,14 @@ static void print_pkt_type(struct hci_dev_info *di) static void print_link_policy(struct hci_dev_info *di) { +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + char *str; + str = hci_lptostr(di->link_policy); + printf("\tLink policy: %s\n", str); + bt_free(str); +#else printf("\tLink policy: %s\n", hci_lptostr(di->link_policy)); +#endif } static void print_link_mode(struct hci_dev_info *di) diff --git a/tools/hcidump.c b/tools/hcidump.c index af8f592..b460daa 100755 --- a/tools/hcidump.c +++ b/tools/hcidump.c @@ -59,10 +59,27 @@ enum { /* Default options */ static int snap_len = SNAP_LEN; static int mode = PARSE; +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY static char *dump_file = NULL; +#endif static char *pppdump_file = NULL; static char *audio_file = NULL; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#define DUMP_MAX_SIZE 10000000 /* 10MB */ +#define DUMP_MAX_COUNT 2 +#define NAME_MAX 255 + +struct dump_file { + char *filename; + int postfix_width; + unsigned int max_size; + int max_count; +}; + +struct dump_file df = {NULL, 1, DUMP_MAX_SIZE, DUMP_MAX_COUNT}; +#endif + struct hcidump_hdr { uint16_t len; uint8_t in; @@ -101,6 +118,10 @@ struct pktlog_hdr { } __attribute__ ((packed)); #define PKTLOG_HDR_SIZE (sizeof(struct pktlog_hdr)) +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static int open_new_dumpfile(unsigned long flags); +#endif + static inline int read_n(int fd, char *buf, int len) { int t = 0, w; @@ -148,6 +169,9 @@ static int process_frames(int dev, int sock, int fd, unsigned long flags) char *buf; char ctrl[100]; int len, hdr_size = HCIDUMP_HDR_SIZE; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + int written = 0; +#endif if (sock < 0) return -1; @@ -261,10 +285,28 @@ static int process_frames(int dev, int sock, int fd, unsigned long flags) dh->ts_usec = htobl(frm.ts.tv_usec); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + if (mode == WRITE && df.max_size != 0 && + written + frm.data_len + hdr_size > df.max_size) { + close(fd); + fd = open_new_dumpfile(flags); + if (fd < 0) + return -1; + written = 0; + } + + len = write_n(fd, buf, frm.data_len + hdr_size); + if (len < 0) { + perror("Write error"); + return -1; + } + written += len; +#else if (write_n(fd, buf, frm.data_len + hdr_size) < 0) { perror("Write error"); return -1; } +#endif break; default: @@ -399,8 +441,10 @@ static void read_dump(int fd) if (err < 0) goto failed; +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY if (!err) goto done; +#endif frm.ptr = frm.data; frm.len = frm.data_len; @@ -516,6 +560,38 @@ static int open_file(char *file, int mode, unsigned long flags) return fd; } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static int open_new_dumpfile(unsigned long flags) +{ + char filename[NAME_MAX + 1]; + char new_filename[NAME_MAX + 1]; + int i; + + if (df.max_count <= 1) + return open_file(df.filename, WRITE, flags); + + for (i = df.max_count - 2; i >= 0; i--) { + if (i == 0) { + snprintf(filename, sizeof(filename), "%s", df.filename); + } else { + snprintf(filename, sizeof(filename), "%s.%0*d", + df.filename, df.postfix_width, i); + } + + if (access(filename, F_OK) < 0) + continue; + + snprintf(new_filename, sizeof(new_filename), "%s.%0*d", + df.filename, df.postfix_width, i + 1); + + if (rename(filename, new_filename) < 0) + perror("rename failed"); + } + + return open_file(df.filename, WRITE, flags); +} +#endif + static int open_socket(int dev, unsigned long flags) { struct sockaddr_hci addr; @@ -635,6 +711,10 @@ static void usage(void) " -D, --pppdump=file Extract PPP traffic\n" " -A, --audio=file Extract SCO audio data\n" " -Y, --novendor No vendor commands or events\n" +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + " -s --dump-size=size Maximum save-dump's file size. The unit is million bytes. Use this with -w. (default: 1,000,000 bytes)\n" + " -c --dump-count=count Specified count's dump files will be generated at most. Use this with -w. (default: 4)\n" +#endif " -h, --help Give this help list\n" " -v, --version Give version information\n" " --usage Give a short usage message\n" @@ -661,6 +741,10 @@ static struct option main_options[] = { { "pppdump", 1, 0, 'D' }, { "audio", 1, 0, 'A' }, { "novendor", 0, 0, 'Y' }, +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + { "dump-size", 1, 0, 's' }, + { "dump-count", 1, 0, 'c' }, +#endif { "help", 0, 0, 'h' }, { "version", 0, 0, 'v' }, { 0 } @@ -676,9 +760,15 @@ int main(int argc, char *argv[]) int opt, pppdump_fd = -1, audio_fd = -1; uint16_t obex_port; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + while ((opt = getopt_long(argc, argv, + "i:l:p:m:w:r:taxXRC:H:O:P:S:D:A:Ys:c:hv", + main_options, NULL)) != -1) { +#else while ((opt = getopt_long(argc, argv, "i:l:p:m:w:r:taxXRC:H:O:P:S:D:A:Yhv", main_options, NULL)) != -1) { +#endif switch(opt) { case 'i': if (strcasecmp(optarg, "none") && strcasecmp(optarg, "system")) @@ -701,12 +791,20 @@ int main(int argc, char *argv[]) case 'w': mode = WRITE; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + df.filename = strdup(optarg); +#else dump_file = strdup(optarg); +#endif break; case 'r': mode = READ; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + df.filename = strdup(optarg); +#else dump_file = strdup(optarg); +#endif break; case 't': @@ -765,6 +863,21 @@ int main(int argc, char *argv[]) flags |= DUMP_NOVENDOR; break; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + case 's': + df.max_size = atoi(optarg) * 1000000; + break; + + case 'c': + { + int i; + df.max_count = atoi(optarg); + for (i = df.max_count / 10; i; i /= 10) + df.postfix_width++; + break; + } +#endif + case 'v': printf("%s\n", VERSION); exit(0); @@ -807,13 +920,24 @@ int main(int argc, char *argv[]) flags |= DUMP_VERBOSE; init_parser(flags, filter, defpsm, defcompid, pppdump_fd, audio_fd); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + read_dump(open_file(df.filename, mode, flags)); +#else read_dump(open_file(dump_file, mode, flags)); +#endif break; case WRITE: flags |= DUMP_BTSNOOP; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + printf("Maximum size of one file : %u, Rotated file count : %d", + df.max_size, df.max_count); + process_frames(device, open_socket(device, flags), + open_file(df.filename, mode, flags), flags); +#else process_frames(device, open_socket(device, flags), open_file(dump_file, mode, flags), flags); +#endif break; } diff --git a/tools/hcitool.c b/tools/hcitool.c index 02c4ebe..0b6f499 100755 --- a/tools/hcitool.c +++ b/tools/hcitool.c @@ -1098,6 +1098,66 @@ static void cmd_epinq(int dev_id, int argc, char **argv) hci_close_dev(dd); } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +/* Send arbitrary ACL data */ +static struct option data_options[] = { + { "help", 0, 0, 'h' }, + { 0, 0, 0, 0 } +}; + +static const char *data_help = + "Usage:\n" + "\tcmd \n" + "Example:\n" + "\tcmd 0x0064 0x41 0x42 0x43 0x44\n"; + +static void cmd_data(int dev_id, int argc, char **argv) +{ + unsigned char buf[HCI_MAX_ACL_SIZE], *ptr = buf; + struct hci_filter flt; + int i, opt, len, dd; + uint16_t handle; + + for_each_opt(opt, data_options, NULL) { + switch (opt) { + default: + printf("%s", data_help); + return; + } + } + helper_arg(2, -1, &argc, &argv, data_help); + + if (dev_id < 0) + dev_id = hci_get_route(NULL); + + handle = atoi(argv[0]); + + for (i = 1, len = 0; i < argc && len < (int) sizeof(buf); i++, len++) + *ptr++ = (uint8_t) strtol(argv[i], NULL, 16); + + dd = hci_open_dev(dev_id); + if (dd < 0) { + perror("Device open failed"); + exit(EXIT_FAILURE); + } + + /* Setup filter */ + hci_filter_clear(&flt); + hci_filter_all_events(&flt); + if (setsockopt(dd, SOL_HCI, HCI_FILTER, &flt, sizeof(flt)) < 0) { + perror("HCI filter setup failed"); + exit(EXIT_FAILURE); + } + + if (hci_send_data(dd, handle, len, buf) < 0) { + perror("Send failed"); + exit(EXIT_FAILURE); + } + + hci_close_dev(dd); +} +#endif + /* Send arbitrary HCI commands */ static struct option cmd_options[] = { @@ -3388,6 +3448,9 @@ static struct { { "spinq", cmd_spinq, "Start periodic inquiry" }, { "epinq", cmd_epinq, "Exit periodic inquiry" }, { "cmd", cmd_cmd, "Submit arbitrary HCI commands" }, +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + { "acl", cmd_data, "Submit arbitrary ACL data" }, +#endif { "con", cmd_con, "Display active connections" }, { "cc", cmd_cc, "Create connection to remote device" }, { "dc", cmd_dc, "Disconnect from remote device" }, diff --git a/tools/ibeacon.c b/tools/ibeacon.c index 6208e8a..9d48e66 100755 --- a/tools/ibeacon.c +++ b/tools/ibeacon.c @@ -149,6 +149,7 @@ static void adv_tx_power_callback(const void *data, uint8_t size, cmd.data[0] = 0x02; /* Field length */ cmd.data[1] = 0x01; /* Flags */ + cmd.data[2] = 0x02; /* LE General Discoverable Mode */ cmd.data[2] |= 0x04; /* BR/EDR Not Supported */ cmd.data[3] = 0x1a; /* Field length */ diff --git a/tools/l2test.c b/tools/l2test.c index 1819423..98f7353 100755 --- a/tools/l2test.c +++ b/tools/l2test.c @@ -69,9 +69,17 @@ enum { LSENDRECV, CSENDRECV, INFOREQ, +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + CONFIGREQ, +#endif PAIRING, }; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +#define L2CAP_DEFAULT_RETRANS_TO 2000 /* 2 seconds */ +#define L2CAP_DEFAULT_MONITOR_TO 12000 /* 12 seconds */ +#endif + static unsigned char *buf; /* Default mtu */ @@ -95,6 +103,9 @@ static long buffer_size = 2048; static bdaddr_t bdaddr; static unsigned short psm = 0; static unsigned short cid = 0; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static uint16_t dcid = 0x0000; +#endif /* Default number of frames to send (-1 = infinite) */ static int num_frames = -1; @@ -1103,6 +1114,146 @@ static void multi_connect_mode(int argc, char *argv[]) } } +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +static void l2cap_add_conf_opt(void **ptr, uint8_t type, uint8_t len, unsigned long val) +{ + l2cap_conf_opt *opt = *ptr; + + printf("type 0x%2.2x len %u val 0x%lx \n", type, len, val); + opt->type = htobs(type); + opt->len = htobs(len); + + switch (opt->len) { + case 1: + *((uint8_t *) opt->val) = val; + break; + case 2: + bt_put_le16(val, opt->val); + break; + case 4: + bt_put_le32(val, opt->val); + break; + default: + memcpy(opt->val, (void *) val, len); + break; + } + + *ptr += L2CAP_CONF_OPT_SIZE + len; +} + +static int l2cap_build_conf_req(void *data) +{ + l2cap_conf_req *req = data; + l2cap_conf_rfc rfc; + void *ptr = req->data; + + req->dcid = htobs(dcid); + req->flags = htobs(0x0000); + + switch (rfcmode) { + case L2CAP_MODE_BASIC: + rfc.mode = htobs(L2CAP_MODE_BASIC); + rfc.txwin_size = htobs(0); + rfc.max_transmit = htobs(0); + rfc.retrans_timeout = htobs(0); + rfc.monitor_timeout = htobs(0); + rfc.max_pdu_size = htobs(0); + + l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), + (unsigned long) &rfc); + + break; + + case L2CAP_MODE_ERTM: + rfc.mode = htobs(L2CAP_MODE_ERTM); + rfc.txwin_size = htobs(txwin_size); + rfc.max_transmit = htobs(max_transmit); + rfc.retrans_timeout = htobs(L2CAP_DEFAULT_RETRANS_TO); + rfc.monitor_timeout = htobs(L2CAP_DEFAULT_MONITOR_TO); + rfc.max_pdu_size = htobs(imtu); + + /* TODO: Enable FCS, FOC options if required */ + l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), + (unsigned long) &rfc); + break; + + case L2CAP_MODE_STREAMING: + rfc.mode = htobs(L2CAP_MODE_STREAMING); + rfc.txwin_size = htobs(txwin_size); + rfc.max_transmit = htobs(max_transmit); + rfc.retrans_timeout = htobs(L2CAP_DEFAULT_RETRANS_TO); + rfc.monitor_timeout = htobs(L2CAP_DEFAULT_MONITOR_TO); + rfc.max_pdu_size = htobs(imtu); + + /* TODO: Enable FCS, FOC options if required */ + l2cap_add_conf_opt(&ptr, L2CAP_CONF_RFC, sizeof(rfc), + (unsigned long) &rfc); + + break; + default: + return L2CAP_CONF_REQ_SIZE; + } + return ptr - data; +} + +static void config_request(char *svr) +{ + unsigned char buf[48]; + l2cap_cmd_hdr *cmd = (l2cap_cmd_hdr *) buf; + uint8_t *req_buf = (uint8_t *) (buf + L2CAP_CMD_HDR_SIZE); +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY + uint16_t mtu; + uint32_t channels, mask = 0x0000; +#endif + struct sockaddr_l2 addr; +#ifndef TIZEN_FEATURE_BLUEZ_MODIFY + int sk, err; +#else + int sk; +#endif + int data_len = 0; + + sk = socket(PF_BLUETOOTH, SOCK_RAW, BTPROTO_L2CAP); + if (sk < 0) { + perror("Can't create socket"); + return; + } + + memset(&addr, 0, sizeof(addr)); + addr.l2_family = AF_BLUETOOTH; + bacpy(&addr.l2_bdaddr, &bdaddr); + addr.l2_bdaddr_type = bdaddr_type; + + if (bind(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0) { + perror("Can't bind socket"); + goto failed; + } + + memset(&addr, 0, sizeof(addr)); + addr.l2_family = AF_BLUETOOTH; + str2ba(svr, &addr.l2_bdaddr); + addr.l2_bdaddr_type = bdaddr_type; + + if (connect(sk, (struct sockaddr *) &addr, sizeof(addr)) < 0 ) { + perror("Can't connect socket"); + goto failed; + } + + memset(buf, 0, sizeof(buf)); + cmd->code = L2CAP_CONF_REQ; + cmd->ident = 141; + data_len = l2cap_build_conf_req(req_buf); + cmd->len = htobs(data_len); + + if (send(sk, buf, L2CAP_CMD_HDR_SIZE + data_len, 0) < 0) { + perror("Can't send info request"); + goto failed; + } +failed: + close(sk); +} +#endif + static void info_request(char *svr) { unsigned char buf[48]; @@ -1314,6 +1465,9 @@ static void usage(void) "\t-c connect, disconnect, connect, ...\n" "\t-m multiple connects\n" "\t-p trigger dedicated bonding\n" +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + "\t-o configuration request\n" +#endif "\t-z information request\n"); printf("Options:\n" @@ -1343,6 +1497,9 @@ static void usage(void) "\t[-M] become master\n" "\t[-T] enable timestamps\n" "\t[-V type] address type (help for list, default = bredr)\n" +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + "\t[-f DCID] destination CID\n" +#endif "\t[-e seq] initial sequence value (default = 0)\n"); } @@ -1353,8 +1510,13 @@ int main(int argc, char *argv[]) bacpy(&bdaddr, BDADDR_ANY); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + while ((opt = getopt(argc, argv, "a:b:cde:f:g:i:mnopqrstuwxyz" + "AB:C:D:EF:GH:I:J:K:L:MN:O:P:Q:RSTUV:W:X:Y:Z:")) != EOF) { +#else while ((opt = getopt(argc, argv, "a:b:cde:g:i:mnpqrstuwxyz" "AB:C:D:EF:GH:I:J:K:L:MN:O:P:Q:RSTUV:W:X:Y:Z:")) != EOF) { +#endif switch (opt) { case 'r': mode = RECV; @@ -1383,6 +1545,13 @@ int main(int argc, char *argv[]) need_addr = 1; break; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + case 'o': + mode = CONFIGREQ; + need_addr = 1; + break; +#endif + case 'n': mode = CONNECT; need_addr = 1; @@ -1560,6 +1729,13 @@ int main(int argc, char *argv[]) break; +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + case 'f': + dcid = atoi(optarg); + printf("dcid %d", dcid); + break; +#endif + case 'e': seq_start = atoi(optarg); break; @@ -1669,6 +1845,12 @@ int main(int argc, char *argv[]) info_request(argv[optind]); exit(0); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + case CONFIGREQ: + config_request(argv[optind]); + exit(0); +#endif + case PAIRING: do_pairing(argv[optind]); exit(0); diff --git a/tools/parse_companies.pl b/tools/parse_companies.pl new file mode 100755 index 0000000..6dc358e --- /dev/null +++ b/tools/parse_companies.pl @@ -0,0 +1,59 @@ +#!/usr/bin/perl + +# parse companies from +# https://www.bluetooth.com/specifications/assigned-numbers/company-identifiers + +use strict; +# use URI::Encode qw(uri_decode); + +my %known_entities = ( + 'nbsp' => ' ', + 'eacute' => 'é', + 'auml' => 'ä', +); + +# better to use URI::Encode if you have it +sub uri_decode { + my $name = $_[0]; + foreach my $entity (keys %known_entities) { + my $to = $known_entities{$entity}; + $name =~ s/&$entity;/$to/g; + } + foreach my $entity (map { lc $_ } $name =~ /&([^;]+);/g) { + if ($entity ne 'amp') { + print "Unable to convert &$entity;, giving up\n"; + exit 1; + } + } + $name =~ s/&/&/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 (/\ should be company name + } elsif ($next_is_name && m|\(.*)\|) { + my $name = uri_decode($1); + $name =~ s/^\s+//g; # kill leading + $name =~ s/\s+$//g; # and trailing space + my $id = hex($identifier); + if ($id != 65535) { + print "\tcase $id:\n"; + print "\t\treturn \"$name\";\n"; + } + $next_is_name = 0; + } +} diff --git a/tools/parser/l2cap.h b/tools/parser/l2cap.h index 788aef0..6bd5b2f 100755 --- a/tools/parser/l2cap.h +++ b/tools/parser/l2cap.h @@ -179,6 +179,18 @@ typedef struct { } __attribute__ ((packed)) l2cap_conf_opt; #define L2CAP_CONF_OPT_SIZE 2 +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY +typedef struct { + uint8_t mode; + uint8_t txwin_size; + uint8_t max_transmit; + uint16_t retrans_timeout; + uint16_t monitor_timeout; + uint16_t max_pdu_size; +} __attribute__ ((packed)) l2cap_conf_rfc ; +#define L2CAP_CONF_RFC_SIZE 9 +#endif + #define L2CAP_CONF_MTU 0x01 #define L2CAP_CONF_FLUSH_TO 0x02 #define L2CAP_CONF_QOS 0x03 diff --git a/tools/pskey_get.c b/tools/pskey_get.c new file mode 100755 index 0000000..db4d228 --- /dev/null +++ b/tools/pskey_get.c @@ -0,0 +1,384 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#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; inum; i++) + { + memset(string, 0, 16); + sprintf(string, "0x%x, ", cmd->par[i] ); + strcat(tmp, string); + } + ALOGI("%s\n", tmp); +#endif + for(i = 0; i < cmd->num; i++) + { + if(1 == pTable->type) + *((unsigned char *)p + i) = (unsigned char)(cmd->par[i]); + else if(2 == pTable->type) + *((unsigned short *)p + i) = (unsigned short)(cmd->par[i]); + else if(4 == pTable->type) + *( (unsigned int *)p + i) = (unsigned int)(cmd->par[i]); + else + ALOGE("%s, type err\n", __func__); + } + return 0; +} + +static cmd_par_table *bt_cmd_table_match(cmd_par *cmd) +{ + int i; + cmd_par_table *pTable = NULL; + int len = sizeof(g_pskey_table) / sizeof(cmd_par_table); + if(NULL == cmd->item) + return NULL; + for(i = 0; i < len; i++) + { + if(NULL == g_pskey_table[i].item) + continue; + if( 0 != strcmp( g_pskey_table[i].item, cmd->item ) ) + continue; + pTable = &g_pskey_table[i]; + break; + } + return pTable; +} + + +static int bt_getDataFromBuf(void *pData, unsigned char *pBuf, int file_len) +{ + int i, p; + cmd_par cmd; + cmd_par_table *pTable = NULL; + if((NULL == pBuf) || (0 == file_len) || (NULL == pData) ) + return -1; + for(i = 0, p = 0; i < file_len; i++) + { + if( ('\n' == *(pBuf + i)) || ( '\r' == *(pBuf + i)) || ( '\0' == *(pBuf + i) ) ) + { + if(5 <= (i - p) ) + { + bt_getCmdOneline((pBuf + p), &cmd); + pTable = bt_cmd_table_match(&cmd); + if(NULL != pTable) + { + bt_getDataFromCmd(pTable, &cmd, pData); + } + } + p = i + 1; + } + + } + return 0; +} + +static int bt_dumpPskey(BT_PSKEY_CONFIG_T *p) +{ + ALOGI("pskey_cmd: 0x%08X", p->pskey_cmd); + + ALOGI("g_dbg_source_sink_syn_test_data: 0x%02X", p->g_dbg_source_sink_syn_test_data); + ALOGI("g_sys_sleep_in_standby_supported: 0x%02X", p->g_sys_sleep_in_standby_supported); + ALOGI("g_sys_sleep_master_supported: 0x%02X", p->g_sys_sleep_master_supported); + ALOGI("g_sys_sleep_slave_supported: 0x%02X", p->g_sys_sleep_slave_supported); + + ALOGI("default_ahb_clk: %d", p->default_ahb_clk); + ALOGI("device_class: 0x%08X", p->device_class); + ALOGI("win_ext: 0x%08X", p->win_ext); + + ALOGI("g_aGainValue: 0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X", p->g_aGainValue[0], p->g_aGainValue[1], p->g_aGainValue[2], p->g_aGainValue[3], p->g_aGainValue[4], p->g_aGainValue[5]); + ALOGI("g_aPowerValue: 0x%08X, 0x%08X, 0x%08X, 0x%08X, 0x%08X", p->g_aPowerValue[0], p->g_aPowerValue[1], p->g_aPowerValue[2], p->g_aPowerValue[3], p->g_aPowerValue[4]); + + + ALOGI("feature_set(0~7): 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X", p->feature_set[0], p->feature_set[1], p->feature_set[2], + p->feature_set[3], p->feature_set[4], p->feature_set[5], p->feature_set[6], p->feature_set[7]); + ALOGI("feature_set(8~15): 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X", p->feature_set[8], p->feature_set[9], p->feature_set[10], + p->feature_set[11], p->feature_set[12], p->feature_set[13], p->feature_set[14], p->feature_set[15]); + ALOGI("device_addr: 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X", p->device_addr[0], p->device_addr[1], p->device_addr[2], p->device_addr[3], p->device_addr[4], p->device_addr[5]); + + ALOGI("g_sys_sco_transmit_mode: 0x%02X", p->g_sys_sco_transmit_mode); + ALOGI("g_sys_uart0_communication_supported: 0x%02X", p->g_sys_uart0_communication_supported); + ALOGI("edr_tx_edr_delay: %d", p->edr_tx_edr_delay); + ALOGI("edr_rx_edr_delay: %d", p->edr_rx_edr_delay); + + ALOGI("g_wbs_nv_117 : 0x%04X", p->g_wbs_nv_117 ); + + ALOGI("is_wdg_supported: 0x%08X", p->is_wdg_supported); + + ALOGI("share_memo_rx_base_addr: 0x%08X", p->share_memo_rx_base_addr); + //ALOGI("share_memo_tx_base_addr: 0x%08X", p->share_memo_tx_base_addr); + ALOGI("g_wbs_nv_118 : 0x%04X", p->g_wbs_nv_118 ); + ALOGI("g_nbv_nv_117 : 0x%04X", p->g_nbv_nv_117 ); + + + ALOGI("share_memo_tx_packet_num_addr: 0x%08X", p->share_memo_tx_packet_num_addr); + ALOGI("share_memo_tx_data_base_addr: 0x%08X", p->share_memo_tx_data_base_addr); + + ALOGI("g_PrintLevel: 0x%08X", p->g_PrintLevel); + + ALOGI("share_memo_tx_block_length: 0x%04X", p->share_memo_tx_block_length); + ALOGI("share_memo_rx_block_length: 0x%04X", p->share_memo_rx_block_length); + ALOGI("share_memo_tx_water_mark: 0x%04X", p->share_memo_tx_water_mark); + //ALOGI("share_memo_tx_timeout_value: 0x%04X", p->share_memo_tx_timeout_value); + ALOGI("g_nbv_nv_118 : 0x%04X", p->g_nbv_nv_118 ); + + ALOGI("uart_rx_watermark: %d", p->uart_rx_watermark); + ALOGI("uart_flow_control_thld: %d", p->uart_flow_control_thld); + ALOGI("comp_id: 0x%08X", p->comp_id); + ALOGI("pcm_clk_divd : 0x%04X", p->pcm_clk_divd ); + + + ALOGI("reserved(0~7): 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X, 0x%02X", p->reserved[0], p->reserved[1], p->reserved[2], + p->reserved[3], p->reserved[4], p->reserved[5], p->reserved[6], p->reserved[7]); + return 0; +} +#if 0 +static int bt_get_config_ver(unsigned char *pBuf, int len) +{ + int i, p; + cmd_par cmd; + int ret = -1; + for(i = 0, p = 0; i < len; i++) + { + if( ('\n' == *(pBuf + i)) || ( '\r' == *(pBuf + i)) || ( '\0' == *(pBuf + i) ) ) + { + if(5 <= (i - p) ) + { + bt_getCmdOneline((pBuf + p), &cmd); + if( 0 == strcmp(cmd.item, "version") ) + { + ret = cmd.par[0]; + break; + } + memset(&cmd, 0, sizeof(cmd_par) ); + } + p = i + 1; + } + + } + return ret; +} +#endif +int bt_getPskeyFromFile(void *pData) +{ + int ret = -1; + int fd; + unsigned char *pBuf = NULL; + int len; + + ALOGI("begin to bt_getPskeyFromFile"); + fd = open(PSKEY_PATH, O_RDONLY, 0644); + if(-1 != fd) + { + len = bt_getFileSize(PSKEY_PATH); + pBuf = (unsigned char *)malloc(len); + ret = read(fd, pBuf, len); + if(-1 == ret) + { + ALOGE("%s read %s ret:%d\n", __FUNCTION__, PSKEY_PATH, ret); + free(pBuf); + close(fd); + return -1; + } + close(fd); + } + else + { + ALOGE("%s open %s ret:%d\n", __FUNCTION__, PSKEY_PATH, fd); + return -1; + } + + ret = bt_getDataFromBuf(pData, pBuf, len); + if(-1 == ret) + { + free(pBuf); + return -1; + } + ALOGI("begin to dumpPskey"); + bt_dumpPskey((BT_PSKEY_CONFIG_T *)pData); + free(pBuf); + return 0; +} + + diff --git a/tools/sdptool.c b/tools/sdptool.c index b1cbcfd..0881c6f 100755 --- a/tools/sdptool.c +++ b/tools/sdptool.c @@ -1219,7 +1219,11 @@ static int add_sp(sdp_session_t *session, svc_info_t *si) sdp_set_service_classes(&record, svclass_id); sdp_uuid16_create(&profile.uuid, SERIAL_PORT_PROFILE_ID); +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + profile.version = 0x0102; +#else profile.version = 0x0100; +#endif profiles = sdp_list_append(0, &profile); sdp_set_profile_descs(&record, profiles); diff --git a/tools/test-runner.c b/tools/test-runner.c index 9b54426..57df7fb 100755 --- a/tools/test-runner.c +++ b/tools/test-runner.c @@ -544,7 +544,11 @@ static const char *test_table[] = { static void run_command(char *cmdname, char *home) { +#ifdef TIZEN_FEATURE_BLUEZ_MODIFY + char *argv[10], *envp[3]; +#else char *argv[9], *envp[3]; +#endif int pos = 0, idx = 0; int serial_fd; pid_t pid, dbus_pid, daemon_pid; diff --git a/tools/update_compids.sh b/tools/update_compids.sh new file mode 100755 index 0000000..95c961d --- /dev/null +++ b/tools/update_compids.sh @@ -0,0 +1,57 @@ +#!/bin/bash +# Download the list of company IDs from bluetooth.org and generate a diff which +# can be applied to source tree to update bt_compidtostr(). Usage: +# +# 1) ./tools/update_compids.sh | git apply -p0 +# 2) Inspect changes to make sure they are sane +# 3) git commit -m "lib: Update list of company identifiers" lib/bluetooth.c +# +# Requires html2text: http://www.mbayer.de/html2text/ +# +set -e -u + +tmpdir=$(mktemp -d) +trap "rm -rf $tmpdir" EXIT + +mkdir $tmpdir/lib +cp lib/bluetooth.c $tmpdir/lib/bluetooth.c.orig +cp lib/bluetooth.c $tmpdir/lib/bluetooth.c + +cd $tmpdir + +path=en-us/specification/assigned-numbers/company-identifiers +# Use "iconv -c" to strip unwanted unicode characters +# Fixups: +# - strip 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 '//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 diff --git a/tools/valgrind.supp b/tools/valgrind.supp new file mode 100755 index 0000000..9efb6f1 --- /dev/null +++ b/tools/valgrind.supp @@ -0,0 +1,27 @@ +{ + ecb_bind + Memcheck:Param + socketcall.bind(my_addr.sa_data) + fun:bind + fun:ecb_aes_setup +} +{ + cmac_bind + Memcheck:Param + socketcall.bind(my_addr.sa_data) + fun:bind + fun:cmac_aes_setup +} +{ + logging_open + Memcheck:Param + socketcall.bind(my_addr.rc_bdaddr) + fun:bind + fun:logging_open +} +{ + bind + Memcheck:Param + socketcall.bind(my_addr.rc_channel) + fun:bind +}