2012-08-27 17:02:52

by Bruna Moreira

[permalink] [raw]
Subject: [RFC BlueZ 00/33] Broadcaster/Observer implementation

This series contains the implementation for Broadcaster/Observer roles. It has
changes for adapter API, userspace implementation for MGMT API and new commands
in btmgmt tool.

The kernel patches for new MGMT commands were sent few weeks ago by Jefferson
Delfes: http://thread.gmane.org/gmane.linux.bluez.kernel/28228.

For now, only Service Data and Manufacturer Specific Data can be
broadcasted/observed. All other data types defined by BT SIG are not
appropriate for general application use, and should be managed by BlueZ itself.

For Observer role, we have:
* Two methods to register watchers: RegisterServiceObserver() and
RegisterManufacturerObserver().
* Respective methods to unregister watchers: UnregisterServiceObserver() and
UnregisterManufacturerObserver()
* The data received are sent to application through ServiceReceived() and/or
ManufacturerReceived().

An application can register only a observer watcher for each type: one for
Service Data and one for Manufacturer Specific Data. Both types can be enabled
for the same watcher.

For Broadcaster role, we have:
* Two methods to set data in controller: SetServiceData() and
SetManufacturerData().
* One method for releasing any previously set Advertising data:
ClearBroadcastData().

The same application can set a new Service Data and a new Manufacturer Specific
Data. To update any data set previously, the application should use
SetServiceData() or SetManufacturerData() using same Service UUID or Company
Identifier Code, respectively. Using ClearBroadcastData() will clean all data
set by application.

For MGMT API, new functions were implemented for Set Observer, Set Broadcaster,
Set Controller Data and Unset Controller Data (see doc/mgmt-api.txt for
details).


Anderson Lizardo (1):
mgmt-api: Broadcaster/Observer management API

Bruna Moreira (18):
lib: Add set broadcaster operation
btmgmt: Add set observer command
btmgmt: Add set controller data support
btmgmt: Add unset controller data support
doc: Add Observer D-Bus API documentation
observer: Add UnregisterServiceObserver() D-Bus method
observer: Add RegisterManufacturerObserver() D-Bus method
observer: Add UnregisterManufacturerObserver() D-Bus method
observer: Add watchers and filters for observers
observer: Add ServiceReceived() and ManufacturerReceived() D-Bus
method
observer: Add python test script
doc: Add Broadcaster D-Bus API documentation
lib: Maximum value to advertising and scan response
broadcaster: Add SetManufacturerData() D-Bus method
broadcaster: Build and send ADV/EIR data blob to kernel
broadcaster: Add ClearBroadcastData() D-Bus method
broadcaster: Update some data of already broadcast value
broadcaster: Add python test script

Jefferson Delfes (14):
lib: Add set controller data operation
lib: Add unset controller data operation
lib: Add set observer operation
btmgmt: Add set broadcaster support
mgmt: Add set observer command
mgmt: Add set broadcaster command
mgmt: Add set controller data command
mgmt: Add unset controller data command
adapter: Add D-Bus API for Observer GAP Role
observer: Add RegisterServiceObserver() D-Bus method
eir: Add manufacturer and service data fields
adapter: Add D-Bus API for Broadcaster GAP Role
broadcaster: Add SetServiceData() D-Bus method
broadcaster: Add list of broadcasters session

doc/adapter-api.txt | 120 ++++++++
doc/mgmt-api.txt | 64 ++++
lib/hci.h | 6 +-
lib/mgmt.h | 26 ++
src/adapter.c | 775 +++++++++++++++++++++++++++++++++++++++++++++++++
src/bluetooth.conf | 1 +
src/eir.c | 43 +++
src/eir.h | 19 ++
src/mgmt.c | 64 ++++
src/mgmt.h | 6 +
test/test-broadcaster | 76 +++++
test/test-observer | 87 ++++++
tools/btmgmt.c | 136 +++++++++
13 files changed, 1421 insertions(+), 2 deletions(-)
create mode 100755 test/test-broadcaster
create mode 100755 test/test-observer

--
1.7.9.5



2012-08-27 17:03:07

by Bruna Moreira

[permalink] [raw]
Subject: [RFC BlueZ 15/33] adapter: Add D-Bus API for Observer GAP Role

From: Jefferson Delfes <[email protected]>

Implement dummy calls for register or unregister Observer Agents.
---
src/adapter.c | 38 ++++++++++++++++++++++++++++++++++++++
src/bluetooth.conf | 1 +
2 files changed, 39 insertions(+)

diff --git a/src/adapter.c b/src/adapter.c
index 50779fd..36c285a 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -1617,6 +1617,30 @@ static DBusMessage *unregister_agent(DBusConnection *conn, DBusMessage *msg,
return dbus_message_new_method_return(msg);
}

+static DBusMessage *register_service_observer(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *unregister_service_observer(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *register_manufobserver(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *unregister_manufobserver(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ return dbus_message_new_method_return(msg);
+}
+
static const GDBusMethodTable adapter_methods[] = {
{ GDBUS_METHOD("GetProperties",
NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
@@ -1658,6 +1682,20 @@ static const GDBusMethodTable adapter_methods[] = {
{ GDBUS_METHOD("UnregisterAgent",
GDBUS_ARGS({ "agent", "o" }), NULL,
unregister_agent) },
+ { GDBUS_METHOD("RegisterServiceObserver",
+ GDBUS_ARGS({ "observer", "o" }, { "match_value", "q" }),
+ NULL,
+ register_service_observer) },
+ { GDBUS_METHOD("UnregisterServiceObserver",
+ GDBUS_ARGS({ "observer", "o" }), NULL,
+ unregister_service_observer) },
+ { GDBUS_METHOD("RegisterManufacturerObserver",
+ GDBUS_ARGS({ "observer", "o" }, { "match_value", "q" }),
+ NULL,
+ register_manufobserver) },
+ { GDBUS_METHOD("UnregisterManufacturerObserver",
+ GDBUS_ARGS({ "observer", "o" }), NULL,
+ unregister_manufobserver) },
{ }
};

diff --git a/src/bluetooth.conf b/src/bluetooth.conf
index 664dbd9..a2fef4f 100644
--- a/src/bluetooth.conf
+++ b/src/bluetooth.conf
@@ -14,6 +14,7 @@
<allow send_interface="org.bluez.HandsfreeAgent"/>
<allow send_interface="org.bluez.MediaEndpoint"/>
<allow send_interface="org.bluez.MediaPlayer"/>
+ <allow send_interface="org.bluez.Observer"/>
<allow send_interface="org.bluez.Watcher"/>
<allow send_interface="org.bluez.ThermometerWatcher"/>
</policy>
--
1.7.9.5


2012-08-27 17:03:25

by Bruna Moreira

[permalink] [raw]
Subject: [RFC BlueZ 33/33] broadcaster: Add python test script

---
test/test-broadcaster | 76 +++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 76 insertions(+)
create mode 100755 test/test-broadcaster

diff --git a/test/test-broadcaster b/test/test-broadcaster
new file mode 100755
index 0000000..35283b1
--- /dev/null
+++ b/test/test-broadcaster
@@ -0,0 +1,76 @@
+#!/usr/bin/python
+
+'''Broadcaster test script
+'''
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+from gi.repository import GObject
+
+import sys
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+from optparse import OptionParser, make_option
+import time
+
+def property_changed(name, value):
+ print("PropertyChanged('%s', '%s')" % (name, value))
+
+if __name__ == "__main__":
+ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+ bus = dbus.SystemBus()
+
+ option_list = [
+ make_option("-i", "--adapter", action="store",
+ type="string", dest="adapter"),
+ make_option("-s", "--service", action="store",
+ type="int", dest="service",
+ help="Format: Service UUID (0xNNNN) followed by" +
+ " Service Data (e.g. aa bb cc)"),
+ make_option("-m", "--manufacturer", action="store",
+ type="int", dest="manufacturer",
+ help="Format: Company Identifier Code (0xNNNN)" +
+ " followed by Manufacturer Specific Data (e.g. 11 22 aa)"),
+ ]
+
+ parser = OptionParser(option_list=option_list,
+ usage="Usage: %prog <-s uuid | -m cid> <data bytes in hex>")
+
+ (options, args) = parser.parse_args()
+ if not args:
+ parser.error("Hex encoded data is required.")
+
+ if options.service and options.manufacturer:
+ parser.error("options -m and -s are mutually exclusive")
+
+ try:
+ data = dbus.Array(map(lambda i: dbus.Byte(int(i, 16)), args))
+ except ValueError:
+ parser.error("Invalid data. Should be hex encoded bytes" +
+ " separated by whitespace.")
+
+ manager = dbus.Interface(bus.get_object("org.bluez", "/"),
+ "org.bluez.Manager")
+
+ if options.adapter:
+ adapter_path = manager.FindAdapter(options.adapter)
+ else:
+ adapter_path = manager.DefaultAdapter()
+
+ adapter = dbus.Interface(bus.get_object("org.bluez", adapter_path),
+ "org.bluez.Adapter")
+
+ bus.add_signal_receiver(property_changed, bus_name="org.bluez",
+ dbus_interface="org.bluez.Adapter",
+ signal_name="PropertyChanged")
+
+ if options.service:
+ adapter.SetServiceData(dbus.UInt16(options.service), data)
+
+ if options.manufacturer:
+ adapter.SetManufacturerData(dbus.UInt16(options.manufacturer), data)
+
+ mainloop = GObject.MainLoop()
+ mainloop.run()
--
1.7.9.5


2012-08-27 17:03:24

by Bruna Moreira

[permalink] [raw]
Subject: [RFC BlueZ 32/33] broadcaster: Update some data of already broadcast value

To update a data set previously, it is need to use same identifier
Service UUID or Company Identifier Code in SetServiceData() and
SetManufacturerData(), respectively.
---
src/adapter.c | 4 ++++
1 file changed, 4 insertions(+)

diff --git a/src/adapter.c b/src/adapter.c
index bbf6b06..84535cd 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -2009,6 +2009,10 @@ static void bcast_session_exit(DBusConnection *conn, void *user_data)
static void update_adv_data(struct btd_adapter *adapter,
struct bcast_session *session, uint8_t *data, int size)
{
+ session->data_len = size;
+ memcpy(session->data, data, size);
+
+ restore_bcast_type(adapter, session->data_type);
}

static DBusMessage *set_service_data(DBusConnection *conn,
--
1.7.9.5


2012-08-27 17:03:23

by Bruna Moreira

[permalink] [raw]
Subject: [RFC BlueZ 31/33] broadcaster: Add ClearBroadcastData() D-Bus method

The ClearBroadcastData() function will clean all data (Service Data or
Manufacturer Specific Data) set by the current application. All data set
by any other application will be kept.
---
src/adapter.c | 80 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 80 insertions(+)

diff --git a/src/adapter.c b/src/adapter.c
index 02306a1..bbf6b06 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -2157,9 +2157,89 @@ failed:
return btd_error_failed(msg, strerror(-err));
}

+static void restore_bcast_data(gpointer data, gpointer user_data)
+{
+ struct bcast_session *session = data, *cleared = user_data;
+
+ if (session == cleared)
+ return;
+
+ if (session->data_type != cleared->data_type)
+ return;
+
+ send_advdata_blob(session->adapter, session);
+}
+
+static gint cmp_bcast_session_owner(gconstpointer a, gconstpointer b)
+{
+ const struct bcast_session *session = a;
+ const char *owner = b;
+
+ return g_strcmp0(session->owner, owner);
+}
+
+static gboolean find_bcast_session_by_owner(struct btd_adapter *adapter,
+ const char *sender,
+ struct bcast_session **session)
+{
+ GSList *l;
+
+ l = g_slist_find_custom(adapter->bcast_sessions, sender,
+ cmp_bcast_session_owner);
+ if (!l)
+ return FALSE;
+
+ if (session)
+ *session = l->data;
+
+ return TRUE;
+}
+
static DBusMessage *clear_broadcast_data(DBusConnection *conn,
DBusMessage *msg, void *data)
{
+ struct btd_adapter *adapter = data;
+ struct bcast_session *session;
+ const char *sender;
+ int err;
+
+ sender = dbus_message_get_sender(msg);
+
+ DBG("Clear all data int broadcaster registered for hci%d at %s",
+ adapter->dev_id, sender);
+
+ err = mgmt_set_broadcaster(adapter->dev_id, FALSE);
+ if (err < 0)
+ error("Failed to set Broadcaster: %s (%d)", strerror(-err),
+ -err);
+
+ while (find_bcast_session_by_owner(adapter, sender, &session)) {
+ uint8_t type = session->data_type;
+
+ err = mgmt_unset_controller_data(adapter->dev_id, type);
+ if (err < 0)
+ error("Failed to set Broadcaster: %s (%d)",
+ strerror(-err), -err);
+
+ /* mgmt_unset_controller_data() also clears broadcast data from
+ * other clients with the same data type, so they need to be
+ * restored. */
+ g_slist_foreach(adapter->bcast_sessions, restore_bcast_data,
+ session);
+
+ adapter->bcast_sessions =
+ g_slist_remove(adapter->bcast_sessions, session);
+
+ free_bcast_session(session);
+ }
+
+ if (adapter->bcast_sessions) {
+ err = mgmt_set_broadcaster(adapter->dev_id, TRUE);
+ if (err < 0)
+ error("Failed to set Broadcaster: %s (%d)",
+ strerror(-err), -err);
+ }
+
return dbus_message_new_method_return(msg);
}

--
1.7.9.5


2012-08-27 17:03:22

by Bruna Moreira

[permalink] [raw]
Subject: [RFC BlueZ 30/33] broadcaster: Build and send ADV/EIR data blob to kernel

SetServiceData() and SetManufacturerData() operations will save all data
and send the ADV/EIR data to kernel. The ADV/EIR data will added in the
current one.

Note: For now, the maximum ADV/EIR data length is limited to 236
(HCI_MAX_EIR_LENGTH - 4) octets. This is need to reserve 2 octets for
length and data type and more 2 octets for UUID/CompanyID. If the
ADV/EIR data from new broadcaster session try to insert more octets than
236 bytes the operation will fail.
---
src/adapter.c | 127 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 122 insertions(+), 5 deletions(-)

diff --git a/src/adapter.c b/src/adapter.c
index 3041d6c..02306a1 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -86,6 +86,9 @@
#define FILTER_SERVICE_UUID 0x01
#define FILTER_COMPANY_IC 0x02

+/* Flags for Broadcaster */
+#define BCAST_DATA_NORMAL_PRIORITY 0x00
+
static DBusConnection *connection = NULL;
static GSList *adapter_drivers = NULL;

@@ -1928,6 +1931,60 @@ static struct bcast_session *find_bcast_session(GSList *list,
return (l ? l->data : NULL);
}

+static void send_advdata_blob(struct btd_adapter *adapter,
+ struct bcast_session *session)
+{
+ uint8_t data[EIR_DATA_MAX_LEN];
+ uint16_t *u16 = (void *) data;
+ int err;
+
+ bt_put_unaligned(htobs(session->data_id), u16);
+ memcpy(data + sizeof(session->data_id), session->data,
+ session->data_len);
+
+ err = mgmt_set_controller_data(adapter->dev_id,
+ BCAST_DATA_NORMAL_PRIORITY,
+ session->data_type, data,
+ session->data_len + sizeof(session->data_id));
+ if (err < 0)
+ error("Failed to set controller data in Broadcaster: %s (%d)",
+ strerror(-err), -err);
+}
+
+static void restore_bcast_type(struct btd_adapter *adapter, uint8_t type)
+{
+ GSList *l;
+ int err;
+
+ err = mgmt_set_broadcaster(adapter->dev_id, FALSE);
+ if (err < 0) {
+ error("Failed to set Broadcaster: %s (%d)", strerror(-err),
+ -err);
+ return;
+ }
+
+ err = mgmt_unset_controller_data(adapter->dev_id, type);
+ if (err < 0) {
+ error("Failed to unset controller data: %s (%d)",
+ strerror(-err), -err);
+ return;
+ }
+
+ for (l = adapter->bcast_sessions; l; l = g_slist_next(l)) {
+ struct bcast_session *session = l->data;
+
+ if (session->data_type == type)
+ send_advdata_blob(adapter, session);
+ }
+
+ if (adapter->bcast_sessions) {
+ err = mgmt_set_broadcaster(adapter->dev_id, TRUE);
+ if (err < 0)
+ error("Failed to set Broadcaster: %s (%d)",
+ strerror(-err), -err);
+ }
+}
+
static void bcast_session_exit(DBusConnection *conn, void *user_data)
{
struct bcast_session *session = user_data;
@@ -1943,10 +2000,10 @@ static void bcast_session_exit(DBusConnection *conn, void *user_data)

adapter->bcast_sessions = g_slist_remove(adapter->bcast_sessions,
session);
- free_bcast_session(session);

- /* FIXME: Stop advertising, send data blob from others apps to kernel
- * with same type and restart advertising*/
+ restore_bcast_type(adapter, type);
+
+ free_bcast_session(session);
}

static void update_adv_data(struct btd_adapter *adapter,
@@ -1962,7 +2019,7 @@ static DBusMessage *set_service_data(DBusConnection *conn,
const char *sender;
uint16_t uuid;
uint8_t *sdata;
- int ssize;
+ int ssize, err;

if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT16, &uuid,
DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
@@ -1970,6 +2027,12 @@ static DBusMessage *set_service_data(DBusConnection *conn,
DBUS_TYPE_INVALID))
return btd_error_invalid_args(msg);

+ /* It will accept new ADV/EIR data only if there is space for complete
+ * data. Reserve 2 octets for Service UUID */
+ if (ssize + sizeof(uint16_t) > EIR_DATA_MAX_LEN)
+ return btd_error_failed(msg, "New ADV/EIR data is bigger than"
+ " permitted");
+
sender = dbus_message_get_sender(msg);

session = find_bcast_session(adapter->bcast_sessions, sender,
@@ -1981,8 +2044,13 @@ static DBusMessage *set_service_data(DBusConnection *conn,

session = g_new(struct bcast_session, 1);
session->data_id = uuid;
+ session->data_len = ssize;
session->owner = g_strdup(sender);
session->adapter = btd_adapter_ref(adapter);
+ session->data_type = EIR_SVC_DATA;
+
+ memcpy(session->data, sdata, ssize);
+
session->id = g_dbus_add_disconnect_watch(conn, sender,
bcast_session_exit, session,
NULL);
@@ -1990,11 +2058,30 @@ static DBusMessage *set_service_data(DBusConnection *conn,
adapter->bcast_sessions = g_slist_prepend(adapter->bcast_sessions,
session);

+ err = mgmt_set_broadcaster(adapter->dev_id, FALSE);
+ if (err < 0)
+ goto failed;
+
+ send_advdata_blob(adapter, session);
+
+ err = mgmt_set_broadcaster(adapter->dev_id, TRUE);
+ if (err < 0)
+ goto failed;
+
DBG("Service Data Broadcaster registered for hci%d at %s",
adapter->dev_id, sender);

done:
return dbus_message_new_method_return(msg);
+
+failed:
+ error("Failed to set Broadcaster: %s (%d)", strerror(-err), -err);
+
+ adapter->bcast_sessions = g_slist_remove(adapter->bcast_sessions,
+ session);
+ free_bcast_session(session);
+
+ return btd_error_failed(msg, strerror(-err));
}

static DBusMessage *set_manufacturer_data(DBusConnection *conn,
@@ -2005,7 +2092,7 @@ static DBusMessage *set_manufacturer_data(DBusConnection *conn,
const char *sender;
uint16_t company_id;
uint8_t *mdata;
- int msize;
+ int msize, err;

if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT16, &company_id,
DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
@@ -2013,6 +2100,12 @@ static DBusMessage *set_manufacturer_data(DBusConnection *conn,
DBUS_TYPE_INVALID))
return btd_error_invalid_args(msg);

+ /* It will accept new ADV/EIR data only if there is space for complete
+ * data. Reserve 2 octets for Company Identifier Code */
+ if (msize + sizeof(uint16_t) > EIR_DATA_MAX_LEN)
+ return btd_error_failed(msg, "New ADV/EIR data is bigger than"
+ " permitted");
+
sender = dbus_message_get_sender(msg);

session = find_bcast_session(adapter->bcast_sessions, sender,
@@ -2024,8 +2117,13 @@ static DBusMessage *set_manufacturer_data(DBusConnection *conn,

session = g_new(struct bcast_session, 1);
session->data_id = company_id;
+ session->data_len = msize;
session->owner = g_strdup(sender);
session->adapter = btd_adapter_ref(adapter);
+ session->data_type = EIR_MANUF_DATA;
+
+ memcpy(session->data, mdata, msize);
+
session->id = g_dbus_add_disconnect_watch(conn, sender,
bcast_session_exit, session,
NULL);
@@ -2033,11 +2131,30 @@ static DBusMessage *set_manufacturer_data(DBusConnection *conn,
adapter->bcast_sessions = g_slist_prepend(adapter->bcast_sessions,
session);

+ err = mgmt_set_broadcaster(adapter->dev_id, FALSE);
+ if (err < 0)
+ goto failed;
+
+ send_advdata_blob(adapter, session);
+
+ err = mgmt_set_broadcaster(adapter->dev_id, TRUE);
+ if (err < 0)
+ goto failed;
+
DBG("Manufacturer Specific Data Broadcaster registered for hci%d at %s",
adapter->dev_id, sender);

done:
return dbus_message_new_method_return(msg);
+
+failed:
+ error("Failed to set Broadcaster: %s (%d)", strerror(-err), -err);
+
+ adapter->bcast_sessions = g_slist_remove(adapter->bcast_sessions,
+ session);
+ free_bcast_session(session);
+
+ return btd_error_failed(msg, strerror(-err));
}

static DBusMessage *clear_broadcast_data(DBusConnection *conn,
--
1.7.9.5


2012-08-27 17:03:19

by Bruna Moreira

[permalink] [raw]
Subject: [RFC BlueZ 27/33] broadcaster: Add SetServiceData() D-Bus method

From: Jefferson Delfes <[email protected]>

Receive the service UUID with advertising data will be set in device.
---
src/adapter.c | 17 +++++++++++++++++
1 file changed, 17 insertions(+)

diff --git a/src/adapter.c b/src/adapter.c
index 61abb34..118e633 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -1872,6 +1872,23 @@ static DBusMessage *unregister_manufobserver(DBusConnection *conn,
static DBusMessage *set_service_data(DBusConnection *conn,
DBusMessage *msg, void *data)
{
+ struct btd_adapter *adapter = data;
+ const char *sender;
+ uint16_t uuid;
+ uint8_t *sdata;
+ int ssize;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT16, &uuid,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
+ &sdata, &ssize,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ sender = dbus_message_get_sender(msg);
+
+ DBG("Service Data Broadcaster registered for hci%d at %s",
+ adapter->dev_id, sender);
+
return dbus_message_new_method_return(msg);
}

--
1.7.9.5


2012-08-27 17:03:21

by Bruna Moreira

[permalink] [raw]
Subject: [RFC BlueZ 29/33] broadcaster: Add list of broadcasters session

From: Jefferson Delfes <[email protected]>

The adapter will keep a list of broadcasters which set new values of
advertising. The new broadcaster is added on the list when the function
SetServiceData() or SetManufacturerData() is called.
---
src/adapter.c | 135 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
src/eir.h | 3 ++
2 files changed, 138 insertions(+)

diff --git a/src/adapter.c b/src/adapter.c
index 94c813c..3041d6c 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -117,6 +117,17 @@ struct observer_watcher {
char *path; /* DBus path */
};

+struct bcast_session {
+ struct btd_adapter *adapter;
+ guint id;
+ char *owner;
+ uint8_t data_type;
+ uint16_t data_id;
+ uint8_t data_len;
+ /* Reserve 2 octets for ServiceUUID/CompanyId */
+ uint8_t data[EIR_DATA_MAX_LEN - sizeof(uint16_t)];
+};
+
struct btd_adapter {
uint16_t dev_id;
gboolean up;
@@ -147,6 +158,7 @@ struct btd_adapter {
GSList *mode_sessions; /* Request Mode sessions */
GSList *disc_sessions; /* Discovery sessions */
GSList *observers; /* Observer watchers */
+ GSList *bcast_sessions; /* Broadcast sessions */
guint discov_id; /* Discovery timer */
gboolean discovering; /* Discovery active */
gboolean discov_suspended; /* Discovery suspended */
@@ -1869,10 +1881,84 @@ static DBusMessage *unregister_manufobserver(DBusConnection *conn,
return dbus_message_new_method_return(msg);
}

+static void free_bcast_session(gpointer user_data)
+{
+ struct bcast_session *session = user_data;
+
+ btd_adapter_unref(session->adapter);
+
+ g_dbus_remove_watch(connection, session->id);
+
+ g_free(session->owner);
+ g_free(session);
+}
+
+static gint cmp_bcast_session(gconstpointer a, gconstpointer b)
+{
+ const struct bcast_session *session = a;
+ const struct bcast_session *match = b;
+ int ret;
+
+ ret = g_strcmp0(session->owner, match->owner);
+ if (ret)
+ return ret;
+
+ ret = session->data_type - match->data_type;
+ if (ret)
+ return ret;
+
+ return session->data_id - match->data_id;
+}
+
+static struct bcast_session *find_bcast_session(GSList *list,
+ const char *sender, uint8_t type, uint16_t data_id)
+{
+ struct bcast_session *match;
+ GSList *l;
+
+ match = g_new0(struct bcast_session, 1);
+ match->owner = g_strdup(sender);
+ match->data_type = type;
+ match->data_id = data_id;
+
+ l = g_slist_find_custom(list, match, cmp_bcast_session);
+ g_free(match->owner);
+ g_free(match);
+
+ return (l ? l->data : NULL);
+}
+
+static void bcast_session_exit(DBusConnection *conn, void *user_data)
+{
+ struct bcast_session *session = user_data;
+ struct btd_adapter *adapter = session->adapter;
+ uint8_t type = session->data_type;
+
+ if (type == EIR_SVC_DATA)
+ DBG("Service Data Broadcaster watcher %s disconnected",
+ session->owner);
+ else
+ DBG("Manufacturer Data Broadcasterer watcher %s disconnected",
+ session->owner);
+
+ adapter->bcast_sessions = g_slist_remove(adapter->bcast_sessions,
+ session);
+ free_bcast_session(session);
+
+ /* FIXME: Stop advertising, send data blob from others apps to kernel
+ * with same type and restart advertising*/
+}
+
+static void update_adv_data(struct btd_adapter *adapter,
+ struct bcast_session *session, uint8_t *data, int size)
+{
+}
+
static DBusMessage *set_service_data(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct btd_adapter *adapter = data;
+ struct bcast_session *session;
const char *sender;
uint16_t uuid;
uint8_t *sdata;
@@ -1886,9 +1972,28 @@ static DBusMessage *set_service_data(DBusConnection *conn,

sender = dbus_message_get_sender(msg);

+ session = find_bcast_session(adapter->bcast_sessions, sender,
+ EIR_SVC_DATA, uuid);
+ if (session) {
+ update_adv_data(adapter, session, sdata, ssize);
+ goto done;
+ }
+
+ session = g_new(struct bcast_session, 1);
+ session->data_id = uuid;
+ session->owner = g_strdup(sender);
+ session->adapter = btd_adapter_ref(adapter);
+ session->id = g_dbus_add_disconnect_watch(conn, sender,
+ bcast_session_exit, session,
+ NULL);
+
+ adapter->bcast_sessions = g_slist_prepend(adapter->bcast_sessions,
+ session);
+
DBG("Service Data Broadcaster registered for hci%d at %s",
adapter->dev_id, sender);

+done:
return dbus_message_new_method_return(msg);
}

@@ -1896,6 +2001,7 @@ static DBusMessage *set_manufacturer_data(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct btd_adapter *adapter = data;
+ struct bcast_session *session;
const char *sender;
uint16_t company_id;
uint8_t *mdata;
@@ -1909,9 +2015,28 @@ static DBusMessage *set_manufacturer_data(DBusConnection *conn,

sender = dbus_message_get_sender(msg);

+ session = find_bcast_session(adapter->bcast_sessions, sender,
+ EIR_MANUF_DATA, company_id);
+ if (session != NULL) {
+ update_adv_data(adapter, session, mdata, msize);
+ goto done;
+ }
+
+ session = g_new(struct bcast_session, 1);
+ session->data_id = company_id;
+ session->owner = g_strdup(sender);
+ session->adapter = btd_adapter_ref(adapter);
+ session->id = g_dbus_add_disconnect_watch(conn, sender,
+ bcast_session_exit, session,
+ NULL);
+
+ adapter->bcast_sessions = g_slist_prepend(adapter->bcast_sessions,
+ session);
+
DBG("Manufacturer Specific Data Broadcaster registered for hci%d at %s",
adapter->dev_id, sender);

+done:
return dbus_message_new_method_return(msg);
}

@@ -2698,6 +2823,16 @@ static void adapter_free(gpointer user_data)
g_slist_free_full(adapter->observers, destroy_observer);
}

+ if (adapter->bcast_sessions) {
+ int err;
+
+ err = mgmt_set_broadcaster(adapter->dev_id, FALSE);
+ if (err < 0)
+ error("Failed to set Broadcaster: %s (%d)",
+ strerror(-err), -err);
+ g_slist_free_full(adapter->bcast_sessions, free_bcast_session);
+ }
+
g_free(adapter->path);
g_free(adapter->name);
g_free(adapter);
diff --git a/src/eir.h b/src/eir.h
index f8e6c7c..428fc36 100644
--- a/src/eir.h
+++ b/src/eir.h
@@ -38,6 +38,9 @@
#define EIR_GAP_APPEARANCE 0x19 /* GAP appearance */
#define EIR_MANUF_DATA 0xFF /* manufacturer specific data */

+/* Reserve 2 octets for length and data type */
+#define EIR_DATA_MAX_LEN (HCI_MAX_EIR_LENGTH - 2)
+
struct uuid_info {
uuid_t uuid;
uint8_t svc_hint;
--
1.7.9.5


2012-08-27 17:03:20

by Bruna Moreira

[permalink] [raw]
Subject: [RFC BlueZ 28/33] broadcaster: Add SetManufacturerData() D-Bus method

Receive the Company Identifier Code with advertising data will be set in
device.
---
src/adapter.c | 17 +++++++++++++++++
1 file changed, 17 insertions(+)

diff --git a/src/adapter.c b/src/adapter.c
index 118e633..94c813c 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -1895,6 +1895,23 @@ static DBusMessage *set_service_data(DBusConnection *conn,
static DBusMessage *set_manufacturer_data(DBusConnection *conn,
DBusMessage *msg, void *data)
{
+ struct btd_adapter *adapter = data;
+ const char *sender;
+ uint16_t company_id;
+ uint8_t *mdata;
+ int msize;
+
+ if (!dbus_message_get_args(msg, NULL, DBUS_TYPE_UINT16, &company_id,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
+ &mdata, &msize,
+ DBUS_TYPE_INVALID))
+ return btd_error_invalid_args(msg);
+
+ sender = dbus_message_get_sender(msg);
+
+ DBG("Manufacturer Specific Data Broadcaster registered for hci%d at %s",
+ adapter->dev_id, sender);
+
return dbus_message_new_method_return(msg);
}

--
1.7.9.5


2012-08-27 17:03:18

by Bruna Moreira

[permalink] [raw]
Subject: [RFC BlueZ 26/33] lib: Maximum value to advertising and scan response

Create a constant to define the maximum lenght to advertising and scan
response data format.
---
lib/hci.h | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/lib/hci.h b/lib/hci.h
index f90ac26..c93b1c6 100644
--- a/lib/hci.h
+++ b/lib/hci.h
@@ -1496,17 +1496,19 @@ typedef struct {
} __attribute__ ((packed)) le_read_advertising_channel_tx_power_rp;
#define LE_READ_ADVERTISING_CHANNEL_TX_POWER_RP_SIZE 2

+#define HCI_LE_MAX_ADV_DATA_LENGTH 31
+
#define OCF_LE_SET_ADVERTISING_DATA 0x0008
typedef struct {
uint8_t length;
- uint8_t data[31];
+ uint8_t data[HCI_LE_MAX_ADV_DATA_LENGTH];
} __attribute__ ((packed)) le_set_advertising_data_cp;
#define LE_SET_ADVERTISING_DATA_CP_SIZE 32

#define OCF_LE_SET_SCAN_RESPONSE_DATA 0x0009
typedef struct {
uint8_t length;
- uint8_t data[31];
+ uint8_t data[HCI_LE_MAX_ADV_DATA_LENGTH];
} __attribute__ ((packed)) le_set_scan_response_data_cp;
#define LE_SET_SCAN_RESPONSE_DATA_CP_SIZE 32

--
1.7.9.5


2012-08-27 17:03:17

by Bruna Moreira

[permalink] [raw]
Subject: [RFC BlueZ 25/33] adapter: Add D-Bus API for Broadcaster GAP Role

From: Jefferson Delfes <[email protected]>

Implement dummy calls for register or unregister Broadcaster.
---
src/adapter.c | 26 ++++++++++++++++++++++++++
1 file changed, 26 insertions(+)

diff --git a/src/adapter.c b/src/adapter.c
index cd0a1b4..61abb34 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -1869,6 +1869,24 @@ static DBusMessage *unregister_manufobserver(DBusConnection *conn,
return dbus_message_new_method_return(msg);
}

+static DBusMessage *set_service_data(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *set_manufacturer_data(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *clear_broadcast_data(DBusConnection *conn,
+ DBusMessage *msg, void *data)
+{
+ return dbus_message_new_method_return(msg);
+}
+
static const GDBusMethodTable adapter_methods[] = {
{ GDBUS_METHOD("GetProperties",
NULL, GDBUS_ARGS({ "properties", "a{sv}" }),
@@ -1924,6 +1942,14 @@ static const GDBusMethodTable adapter_methods[] = {
{ GDBUS_METHOD("UnregisterManufacturerObserver",
GDBUS_ARGS({ "observer", "o" }), NULL,
unregister_manufobserver) },
+ { GDBUS_METHOD("SetServiceData", GDBUS_ARGS({ "uuid", "q" },
+ { "data", "ay" }), NULL,
+ set_service_data) },
+ { GDBUS_METHOD("SetManufacturerData", GDBUS_ARGS({ "company_id", "q" },
+ { "data", "ay" }), NULL,
+ set_manufacturer_data) },
+ { GDBUS_METHOD("ClearBroadcastData", NULL, NULL,
+ clear_broadcast_data) },
{ }
};

--
1.7.9.5


2012-08-27 17:03:16

by Bruna Moreira

[permalink] [raw]
Subject: [RFC BlueZ 24/33] doc: Add Broadcaster D-Bus API documentation

This is a "high level" API for Broadcaster GAP role.
---
doc/adapter-api.txt | 45 +++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 45 insertions(+)

diff --git a/doc/adapter-api.txt b/doc/adapter-api.txt
index 9e2e652..139a1a2 100644
--- a/doc/adapter-api.txt
+++ b/doc/adapter-api.txt
@@ -224,6 +224,51 @@ Methods dict GetProperties()
Possible errors: org.bluez.Error.InvalidArguments
org.bluez.Error.DoesNotExist

+ void SetServiceData(uint16 uuid, variant data)
+
+ Set Service Data for broadcast. Different applications
+ can register different Adv. data types, and they are
+ all concatenated to form the Adv. data. Broadcasting is
+ enabled as soon as the first SetServiceData() call is
+ made.
+
+ This method can be used to update service data already
+ being broadcasted.
+
+ Use ClearBroadcastData() to release any Adv. data for
+ the application. Advertising data is also released when
+ application exits, and once the last Broadcaster exits,
+ advertising is disabled.
+
+ Possible Errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.Failed
+
+ void SetManufacturerData(uint16 cid, variant data)
+
+ Set Manufacturer Specific Data for broadcast. Different
+ applications can register different Adv. data types,
+ and they are all concatenated to form the Adv. data.
+ Broadcasting is enabled as soon as the first
+ SetManufacturerData() call is made.
+
+ This method can be used to update manufacturer data
+ already being broadcasted.
+
+ Use ClearBroadcastData() to release any Adv. data for
+ the application. Advertising data is also released when
+ application exits, and once the last Broadcaster exits,
+ advertising is disabled.
+
+ Possible Errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.Failed
+
+ void ClearBroadcastData()
+
+ This method will release any previously set Advertising
+ data.
+
+ Possible errors: org.bluez.Error.Failed
+
Signals PropertyChanged(string name, variant value)

This signal indicates a changed value of the given
--
1.7.9.5


2012-08-27 17:03:15

by Bruna Moreira

[permalink] [raw]
Subject: [RFC BlueZ 23/33] observer: Add python test script

---
test/test-observer | 87 ++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 87 insertions(+)
create mode 100755 test/test-observer

diff --git a/test/test-observer b/test/test-observer
new file mode 100755
index 0000000..110f487
--- /dev/null
+++ b/test/test-observer
@@ -0,0 +1,87 @@
+#!/usr/bin/python
+
+'''Observer test script
+'''
+
+from __future__ import absolute_import, print_function, unicode_literals
+
+from gi.repository import GObject
+
+import sys
+import dbus
+import dbus.service
+import dbus.mainloop.glib
+from optparse import OptionParser, make_option
+
+def dbus_data(data):
+ return "".join(["%02x" % c for c in data])
+
+class Observer(dbus.service.Object):
+ @dbus.service.method("org.bluez.Observer",
+ in_signature="sqv", out_signature="")
+ def ServiceReceived(self, address, uuid, value):
+ print("[ " + address + " ]")
+ print(" Service UUID = 0x%04x" % int(uuid))
+ print(" Data = %s" % dbus_data(value))
+ print()
+
+ @dbus.service.method("org.bluez.Observer",
+ in_signature="sqv", out_signature="")
+ def ManufacturerReceived(self, address, cid, value):
+ print("[ " + address + " ]")
+ print(" Company ID = 0x%04x" % int(cid))
+ print(" Data = %s" % dbus_data(value))
+ print()
+
+def property_changed(name, value):
+ print("PropertyChanged('%s', '%s')" % (name, value))
+
+if __name__ == "__main__":
+ dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
+
+ bus = dbus.SystemBus()
+
+ manager = dbus.Interface(bus.get_object("org.bluez", "/"),
+ "org.bluez.Manager")
+
+ option_list = [
+ make_option("-i", "--adapter", action="store",
+ type="string", dest="adapter"),
+ make_option("-s", "--service", action="store",
+ type="int", dest="service_uuid",
+ help="Service UUID (e.g. \"0x0001\")"),
+ make_option("-c", "--company", action="store",
+ type="int", dest="company_id",
+ help="Company Identifier Code (e.g. \"0x0002\")"),
+ ]
+
+ parser = OptionParser(option_list=option_list)
+
+ (options, args) = parser.parse_args()
+
+ if not options.service_uuid and not options.company_id:
+ parser.error("At least one option is required: -c or -s")
+
+ if options.adapter:
+ adapter_path = manager.FindAdapter(options.adapter)
+ else:
+ adapter_path = manager.DefaultAdapter()
+
+ adapter = dbus.Interface(bus.get_object("org.bluez", adapter_path),
+ "org.bluez.Adapter")
+
+ bus.add_signal_receiver(property_changed, bus_name="org.bluez",
+ dbus_interface="org.bluez.Adapter",
+ signal_name="PropertyChanged")
+
+ path = "/test/observer"
+ observer = Observer(bus, path)
+
+ if options.service_uuid:
+ adapter.RegisterServiceObserver(path, dbus.UInt16(options.service_uuid))
+
+ if options.company_id:
+ adapter.RegisterManufacturerObserver(path, dbus.UInt16(options.company_id))
+
+ mainloop = GObject.MainLoop()
+ mainloop.run()
--
1.7.9.5


2012-08-27 17:03:13

by Bruna Moreira

[permalink] [raw]
Subject: [RFC BlueZ 21/33] eir: Add manufacturer and service data fields

From: Jefferson Delfes <[email protected]>

For advertising, there are another possible fields, Service Data and
Manufacturer Specific Data.
---
src/eir.c | 43 +++++++++++++++++++++++++++++++++++++++++++
src/eir.h | 16 ++++++++++++++++
2 files changed, 59 insertions(+)

diff --git a/src/eir.c b/src/eir.c
index 9d42917..676e303 100644
--- a/src/eir.c
+++ b/src/eir.c
@@ -38,12 +38,32 @@
#include "glib-helper.h"
#include "eir.h"

+static void svc_data_free(void *p)
+{
+ struct svc_data *sdata = p;
+
+ g_free(sdata->data);
+ g_free(sdata);
+}
+
+static void manuf_data_free(void *p)
+{
+ struct manuf_data *mdata = p;
+
+ g_free(mdata->data);
+ g_free(mdata);
+}
+
void eir_data_free(struct eir_data *eir)
{
g_slist_free_full(eir->services, g_free);
eir->services = NULL;
g_free(eir->name);
eir->name = NULL;
+ g_slist_free_full(eir->svcs_data, svc_data_free);
+ eir->svcs_data = NULL;
+ g_slist_free_full(eir->manufs_data, manuf_data_free);
+ eir->manufs_data = NULL;
}

static void eir_parse_uuid16(struct eir_data *eir, void *data, uint8_t len)
@@ -107,6 +127,8 @@ int eir_parse(struct eir_data *eir, uint8_t *eir_data, uint8_t eir_len)
while (len < eir_len - 1) {
uint8_t field_len = eir_data[0];
uint8_t data_len, *data = &eir_data[2];
+ struct svc_data *sdata;
+ struct manuf_data *mdata;

/* Check for the end of EIR */
if (field_len == 0)
@@ -163,11 +185,32 @@ int eir_parse(struct eir_data *eir, uint8_t *eir_data, uint8_t eir_len)
memcpy(eir->dev_class, data, 3);
break;

+ case EIR_SVC_DATA:
+ if (data_len < 2)
+ break;
+ sdata = g_new(struct svc_data, 1);
+ sdata->uuid = bt_get_le16(data);
+ sdata->data_len = data_len - 2;
+ sdata->data = g_memdup(data + 2, data_len - 2);
+ eir->svcs_data = g_slist_append(eir->svcs_data, sdata);
+ break;
+
case EIR_GAP_APPEARANCE:
if (data_len < 2)
break;
eir->appearance = bt_get_le16(data);
break;
+
+ case EIR_MANUF_DATA:
+ if (data_len < 2)
+ break;
+ mdata = g_new(struct manuf_data, 1);
+ mdata->company_id = bt_get_le16(data);
+ mdata->data_len = data_len - 2;
+ mdata->data = g_memdup(data + 2, data_len - 2);
+ eir->manufs_data = g_slist_append(eir->manufs_data,
+ mdata);
+ break;
}

eir_data += field_len + 1;
diff --git a/src/eir.h b/src/eir.h
index 3c81024..f8e6c7c 100644
--- a/src/eir.h
+++ b/src/eir.h
@@ -34,13 +34,27 @@
#define EIR_TX_POWER 0x0A /* transmit power level */
#define EIR_CLASS_OF_DEV 0x0D /* Class of Device */
#define EIR_DEVICE_ID 0x10 /* device ID */
+#define EIR_SVC_DATA 0x16 /* service data */
#define EIR_GAP_APPEARANCE 0x19 /* GAP appearance */
+#define EIR_MANUF_DATA 0xFF /* manufacturer specific data */

struct uuid_info {
uuid_t uuid;
uint8_t svc_hint;
};

+struct svc_data {
+ uint16_t uuid;
+ uint8_t data_len;
+ uint8_t *data;
+};
+
+struct manuf_data {
+ uint16_t company_id;
+ uint8_t data_len;
+ uint8_t *data;
+};
+
struct eir_data {
GSList *services;
int flags;
@@ -48,6 +62,8 @@ struct eir_data {
uint8_t dev_class[3];
uint16_t appearance;
gboolean name_complete;
+ GSList *svcs_data;
+ GSList *manufs_data;
};

void eir_data_free(struct eir_data *eir);
--
1.7.9.5


2012-08-27 17:03:12

by Bruna Moreira

[permalink] [raw]
Subject: [RFC BlueZ 20/33] observer: Add watchers and filters for observers

The adapter will keep a list of observers to listening for advertising.
The new observer is added on the list when it is registered.

When an observer is registered, the filter must be stored to be used
after receiving a new advertising. The observer only will received the
data from advertising if the filter matches.
---
src/adapter.c | 179 ++++++++++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 171 insertions(+), 8 deletions(-)

diff --git a/src/adapter.c b/src/adapter.c
index b2c052f..3b8cbcd 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -82,6 +82,10 @@

#define OFF_TIMER 3

+/* Filter Types (for Observer) */
+#define FILTER_SERVICE_UUID 0x01
+#define FILTER_COMPANY_IC 0x02
+
static DBusConnection *connection = NULL;
static GSList *adapter_drivers = NULL;

@@ -103,6 +107,16 @@ struct service_auth {
struct btd_adapter *adapter;
};

+struct observer_watcher {
+ struct btd_adapter *adapter;
+ guint id; /* Listener id */
+ uint8_t filter_type; /* Filer type: Service or Manufacturer
+ * data */
+ void *filter_data; /* Value of filter */
+ char *sender; /* DBus sender */
+ char *path; /* DBus path */
+};
+
struct btd_adapter {
uint16_t dev_id;
gboolean up;
@@ -132,6 +146,7 @@ struct btd_adapter {
GSList *devices; /* Devices structure pointers */
GSList *mode_sessions; /* Request Mode sessions */
GSList *disc_sessions; /* Discovery sessions */
+ GSList *observers; /* Observer watchers */
guint discov_id; /* Discovery timer */
gboolean discovering; /* Discovery active */
gboolean discov_suspended; /* Discovery suspended */
@@ -1617,10 +1632,85 @@ static DBusMessage *unregister_agent(DBusConnection *conn, DBusMessage *msg,
return dbus_message_new_method_return(msg);
}

+static void destroy_observer(gpointer user_data)
+{
+ struct observer_watcher *obs = user_data;
+
+ btd_adapter_unref(obs->adapter);
+
+ g_dbus_remove_watch(connection, obs->id);
+
+ g_free(obs->path);
+ g_free(obs->sender);
+ g_free(obs);
+}
+
+static gint cmp_observer(gconstpointer a, gconstpointer b)
+{
+ const struct observer_watcher *obs = a;
+ const struct observer_watcher *match = b;
+ int ret;
+
+ ret = g_strcmp0(obs->sender, match->sender);
+ if (ret != 0)
+ return ret;
+
+ ret = g_strcmp0(obs->path, match->path);
+ if (ret != 0)
+ return ret;
+
+ return obs->filter_type - match->filter_type;
+}
+
+static struct observer_watcher *find_observer(GSList *list, const char *sender,
+ const char *path, uint8_t filter)
+{
+ struct observer_watcher *match;
+ GSList *l;
+
+ match = g_new0(struct observer_watcher, 1);
+ match->sender = g_strdup(sender);
+ match->path = g_strdup(path);
+ match->filter_type = filter;
+
+ l = g_slist_find_custom(list, match, cmp_observer);
+ g_free(match->sender);
+ g_free(match->path);
+ g_free(match);
+
+ return (l ? l->data : NULL);
+}
+
+static void observer_exit(DBusConnection *conn, void *user_data)
+{
+ struct observer_watcher *obs = user_data;
+ struct btd_adapter *adapter = obs->adapter;
+
+ if (obs->filter_type == FILTER_SERVICE_UUID)
+ DBG("Service Data Observer watcher %s disconnected", obs->path);
+ else
+ DBG("Manufacturer Data Observer watcher %s disconnected",
+ obs->path);
+
+ adapter->observers = g_slist_remove(adapter->observers, obs);
+
+ if (adapter->observers == NULL) {
+ int err;
+
+ err = mgmt_set_observer(adapter->dev_id, FALSE);
+ if (err < 0)
+ error("Failed to set Observer: %s (%d)",
+ strerror(-err), -err);
+ }
+
+ destroy_observer(obs);
+}
+
static DBusMessage *register_service_observer(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct btd_adapter *adapter = data;
+ struct observer_watcher *obs;
const char *path, *sender = dbus_message_get_sender(msg);
unsigned int uuid;
int err;
@@ -1630,8 +1720,26 @@ static DBusMessage *register_service_observer(DBusConnection *conn,
DBUS_TYPE_INVALID) == FALSE)
return btd_error_invalid_args(msg);

+ obs = find_observer(adapter->observers, sender, path,
+ FILTER_SERVICE_UUID);
+ if (obs)
+ return btd_error_already_exists(msg);
+
+ obs = g_new0(struct observer_watcher, 1);
+ obs->filter_type = FILTER_SERVICE_UUID;
+ obs->filter_data = GUINT_TO_POINTER(uuid);
+ obs->sender = g_strdup(sender);
+ obs->path = g_strdup(path);
+ obs->adapter = btd_adapter_ref(adapter);
+ obs->id = g_dbus_add_disconnect_watch(conn, sender, observer_exit,
+ obs, NULL);
+
+ adapter->observers = g_slist_prepend(adapter->observers, obs);
+
err = mgmt_set_observer(adapter->dev_id, TRUE);
if (err < 0) {
+ adapter->observers = g_slist_remove(adapter->observers, obs);
+ destroy_observer(obs);
error("Failed to set Observer: %s (%d)", strerror(-err), -err);

return btd_error_failed(msg, strerror(-err));
@@ -1648,15 +1756,28 @@ static DBusMessage *unregister_service_observer(DBusConnection *conn,
{
const char *path, *sender = dbus_message_get_sender(msg);
struct btd_adapter *adapter = data;
- int err;
+ struct observer_watcher *obs;

if (dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
DBUS_TYPE_INVALID) == FALSE)
return btd_error_invalid_args(msg);

- err = mgmt_set_observer(adapter->dev_id, FALSE);
- if (err < 0)
- return btd_error_failed(msg, strerror(-err));
+ obs = find_observer(adapter->observers, sender, path,
+ FILTER_SERVICE_UUID);
+ if (obs == NULL)
+ return btd_error_does_not_exist(msg);
+
+ adapter->observers = g_slist_remove(adapter->observers, obs);
+ g_dbus_remove_watch(connection, obs->id);
+ destroy_observer(obs);
+
+ if (adapter->observers == NULL) {
+ int err;
+
+ err = mgmt_set_observer(adapter->dev_id, FALSE);
+ if (err < 0)
+ return btd_error_failed(msg, strerror(-err));
+ }

DBG("Service Data Observer unregistered for hci%d at %s:%s",
adapter->dev_id, sender, path);
@@ -1668,6 +1789,7 @@ static DBusMessage *register_manufobserver(DBusConnection *conn,
DBusMessage *msg, void *data)
{
struct btd_adapter *adapter = data;
+ struct observer_watcher *obs;
const char *path, *sender = dbus_message_get_sender(msg);
unsigned int company_id;
int err;
@@ -1677,8 +1799,26 @@ static DBusMessage *register_manufobserver(DBusConnection *conn,
DBUS_TYPE_INVALID) == FALSE)
return btd_error_invalid_args(msg);

+ obs = find_observer(adapter->observers, sender, path,
+ FILTER_COMPANY_IC);
+ if (obs)
+ return btd_error_already_exists(msg);
+
+ obs = g_new0(struct observer_watcher, 1);
+ obs->filter_type = FILTER_COMPANY_IC;
+ obs->filter_data = GUINT_TO_POINTER(company_id);
+ obs->sender = g_strdup(sender);
+ obs->path = g_strdup(path);
+ obs->adapter = btd_adapter_ref(adapter);
+ obs->id = g_dbus_add_disconnect_watch(conn, sender, observer_exit,
+ obs, NULL);
+
+ adapter->observers = g_slist_prepend(adapter->observers, obs);
+
err = mgmt_set_observer(adapter->dev_id, TRUE);
if (err < 0) {
+ adapter->observers = g_slist_remove(adapter->observers, obs);
+ destroy_observer(obs);
error("Failed to set Observer: %s (%d)", strerror(-err), -err);

return btd_error_failed(msg, strerror(-err));
@@ -1695,15 +1835,28 @@ static DBusMessage *unregister_manufobserver(DBusConnection *conn,
{
const char *path, *sender = dbus_message_get_sender(msg);
struct btd_adapter *adapter = data;
- int err;
+ struct observer_watcher *obs;

if (dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
DBUS_TYPE_INVALID) == FALSE)
return btd_error_invalid_args(msg);

- err = mgmt_set_observer(adapter->dev_id, FALSE);
- if (err < 0)
- return btd_error_failed(msg, strerror(-err));
+ obs = find_observer(adapter->observers, sender, path,
+ FILTER_COMPANY_IC);
+ if (obs == NULL)
+ return btd_error_does_not_exist(msg);
+
+ adapter->observers = g_slist_remove(adapter->observers, obs);
+ g_dbus_remove_watch(connection, obs->id);
+ destroy_observer(obs);
+
+ if (adapter->observers == NULL) {
+ int err;
+
+ err = mgmt_set_observer(adapter->dev_id, FALSE);
+ if (err < 0)
+ return btd_error_failed(msg, strerror(-err));
+ }

DBG("Manufacturer Specific Data Observer unregistered for hci%d at "
"%s:%s", adapter->dev_id, sender, path);
@@ -2470,6 +2623,16 @@ static void adapter_free(gpointer user_data)

g_slist_free(adapter->oor_devices);

+ if (adapter->observers) {
+ int err;
+
+ err = mgmt_set_observer(adapter->dev_id, FALSE);
+ if (err < 0)
+ error("Failed to set Observer: %s (%d)",
+ strerror(-err), -err);
+ g_slist_free_full(adapter->observers, destroy_observer);
+ }
+
g_free(adapter->path);
g_free(adapter->name);
g_free(adapter);
--
1.7.9.5


2012-08-27 17:03:14

by Bruna Moreira

[permalink] [raw]
Subject: [RFC BlueZ 22/33] observer: Add ServiceReceived() and ManufacturerReceived() D-Bus method

When an advertising matches with the filter registered in
RegisterServiceObserver() or RegisterManufacturerObserver(), the
respective data is sent to all observers registered with this filter
using ServiceReceived() or ManufacturerReceived(), respectively.
---
src/adapter.c | 108 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 108 insertions(+)

diff --git a/src/adapter.c b/src/adapter.c
index 3b8cbcd..cd0a1b4 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -168,6 +168,11 @@ struct btd_adapter {
GSList *loaded_drivers;
};

+struct broadcast_data {
+ struct eir_data *eir_data;
+ char peer_addr[18];
+};
+
static void dev_info_free(void *data)
{
struct remote_dev_info *dev = data;
@@ -3107,6 +3112,106 @@ static char *read_stored_data(bdaddr_t *local, bdaddr_t *peer,
return textfile_get(filename, key);
}

+static void service_received(struct observer_watcher *obs, char *peer_addr,
+ struct svc_data *sdata)
+{
+ const char *paddr = peer_addr;
+ DBusMessage *msg;
+
+ msg = dbus_message_new_method_call(obs->sender, obs->path,
+ "org.bluez.Observer",
+ "ServiceReceived");
+ if (msg == NULL) {
+ error("Unable to allocate new ServiceReceived method");
+ return;
+ }
+
+ dbus_message_append_args(msg, DBUS_TYPE_STRING, &paddr,
+ DBUS_TYPE_UINT16, &sdata->uuid,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
+ &sdata->data, sdata->data_len,
+ DBUS_TYPE_INVALID);
+
+ dbus_message_set_no_reply(msg, TRUE);
+ g_dbus_send_message(connection, msg);
+}
+
+static void manufacturer_received(struct observer_watcher *obs,
+ char *peer_addr, struct manuf_data *mdata)
+{
+ const char *paddr = peer_addr;
+ DBusMessage *msg;
+
+ msg = dbus_message_new_method_call(obs->sender, obs->path,
+ "org.bluez.Observer",
+ "ManufacturerReceived");
+ if (msg == NULL) {
+ error("Unable to allocate new ManufacturerReceived method");
+ return;
+ }
+
+ dbus_message_append_args(msg, DBUS_TYPE_STRING, &paddr,
+ DBUS_TYPE_UINT16, &mdata->company_id,
+ DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE,
+ &mdata->data, mdata->data_len,
+ DBUS_TYPE_INVALID);
+
+ dbus_message_set_no_reply(msg, TRUE);
+ g_dbus_send_message(connection, msg);
+}
+
+static void update_observer(gpointer data, gpointer user_data)
+{
+ struct observer_watcher *obs = data;
+ struct broadcast_data *bdata = user_data;
+ struct eir_data *eir = bdata->eir_data;
+ GSList *l;
+
+ if (obs->filter_type == FILTER_SERVICE_UUID) {
+ for (l = eir->svcs_data; l != NULL; l = g_slist_next(l)) {
+ struct svc_data *sdata = l->data;
+ uint16_t uuid = GPOINTER_TO_UINT(obs->filter_data);
+
+ if (uuid == sdata->uuid)
+ service_received(obs, bdata->peer_addr, sdata);
+ }
+ } else if (obs->filter_type == FILTER_COMPANY_IC) {
+ for (l = eir->manufs_data; l != NULL; l = g_slist_next(l)) {
+ struct manuf_data *mdata = l->data;
+ uint16_t company_id =
+ GPOINTER_TO_UINT(obs->filter_data);
+
+ if (company_id == mdata->company_id)
+ manufacturer_received(obs, bdata->peer_addr,
+ mdata);
+ }
+ }
+}
+
+static void adapter_broadcast_received(struct btd_adapter *adapter,
+ bdaddr_t *bdaddr, struct eir_data *eir_data)
+{
+ struct broadcast_data *bdata;
+
+ /* No observers listening for advertising */
+ if (adapter->observers == NULL)
+ return;
+
+ /* No service/manufacturer data was sent */
+ if (eir_data->svcs_data == NULL && eir_data->manufs_data == NULL)
+ return;
+
+ bdata = g_new(struct broadcast_data, 1);
+ bdata->eir_data = eir_data;
+ ba2str(bdaddr, bdata->peer_addr);
+
+ DBG("Broadcast received from %s", bdata->peer_addr);
+
+ g_slist_foreach(adapter->observers, update_observer, bdata);
+
+ g_free(bdata);
+}
+
void adapter_update_found_devices(struct btd_adapter *adapter,
bdaddr_t *bdaddr, uint8_t bdaddr_type,
int8_t rssi, uint8_t confirm_name,
@@ -3126,6 +3231,9 @@ void adapter_update_found_devices(struct btd_adapter *adapter,
return;
}

+ /* Send advertsing to observers */
+ adapter_broadcast_received(adapter, bdaddr, &eir_data);
+
dev_class = eir_data.dev_class[0] | (eir_data.dev_class[1] << 8) |
(eir_data.dev_class[2] << 16);
if (dev_class != 0)
--
1.7.9.5


2012-08-27 17:03:10

by Bruna Moreira

[permalink] [raw]
Subject: [RFC BlueZ 18/33] observer: Add RegisterManufacturerObserver() D-Bus method

An observer for Manufacturer Specific Data must be registered using
RegisterManufacturerObserver() D-Bus method so it can receive any
advertising which matches with the filter used.
---
src/adapter.c | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)

diff --git a/src/adapter.c b/src/adapter.c
index b437cf6..e5237b5 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -1667,6 +1667,26 @@ static DBusMessage *unregister_service_observer(DBusConnection *conn,
static DBusMessage *register_manufobserver(DBusConnection *conn,
DBusMessage *msg, void *data)
{
+ struct btd_adapter *adapter = data;
+ const char *path, *sender = dbus_message_get_sender(msg);
+ unsigned int company_id;
+ int err;
+
+ if (dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_UINT16, &company_id,
+ DBUS_TYPE_INVALID) == FALSE)
+ return btd_error_invalid_args(msg);
+
+ err = mgmt_set_observer(adapter->dev_id, TRUE);
+ if (err < 0) {
+ error("Failed to set Observer: %s (%d)", strerror(-err), -err);
+
+ return btd_error_failed(msg, strerror(-err));
+ }
+
+ DBG("Manufacturer Specific Data Observer registered for hci%d at %s:%s",
+ adapter->dev_id, sender, path);
+
return dbus_message_new_method_return(msg);
}

--
1.7.9.5


2012-08-27 17:03:11

by Bruna Moreira

[permalink] [raw]
Subject: [RFC BlueZ 19/33] observer: Add UnregisterManufacturerObserver() D-Bus method

Unregister an observer for Manufacturer Specific Data and avoid to send
broadcaster to this agent.
---
src/adapter.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)

diff --git a/src/adapter.c b/src/adapter.c
index e5237b5..b2c052f 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -1693,6 +1693,21 @@ static DBusMessage *register_manufobserver(DBusConnection *conn,
static DBusMessage *unregister_manufobserver(DBusConnection *conn,
DBusMessage *msg, void *data)
{
+ const char *path, *sender = dbus_message_get_sender(msg);
+ struct btd_adapter *adapter = data;
+ int err;
+
+ if (dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID) == FALSE)
+ return btd_error_invalid_args(msg);
+
+ err = mgmt_set_observer(adapter->dev_id, FALSE);
+ if (err < 0)
+ return btd_error_failed(msg, strerror(-err));
+
+ DBG("Manufacturer Specific Data Observer unregistered for hci%d at "
+ "%s:%s", adapter->dev_id, sender, path);
+
return dbus_message_new_method_return(msg);
}

--
1.7.9.5


2012-08-27 17:03:09

by Bruna Moreira

[permalink] [raw]
Subject: [RFC BlueZ 17/33] observer: Add UnregisterServiceObserver() D-Bus method

Unregister an observer for Service Data and avoid to send broadcaster to
this agent.
---
src/adapter.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)

diff --git a/src/adapter.c b/src/adapter.c
index 27692f9..b437cf6 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -1646,6 +1646,21 @@ static DBusMessage *register_service_observer(DBusConnection *conn,
static DBusMessage *unregister_service_observer(DBusConnection *conn,
DBusMessage *msg, void *data)
{
+ const char *path, *sender = dbus_message_get_sender(msg);
+ struct btd_adapter *adapter = data;
+ int err;
+
+ if (dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_INVALID) == FALSE)
+ return btd_error_invalid_args(msg);
+
+ err = mgmt_set_observer(adapter->dev_id, FALSE);
+ if (err < 0)
+ return btd_error_failed(msg, strerror(-err));
+
+ DBG("Service Data Observer unregistered for hci%d at %s:%s",
+ adapter->dev_id, sender, path);
+
return dbus_message_new_method_return(msg);
}

--
1.7.9.5


2012-08-27 17:03:08

by Bruna Moreira

[permalink] [raw]
Subject: [RFC BlueZ 16/33] observer: Add RegisterServiceObserver() D-Bus method

From: Jefferson Delfes <[email protected]>

An observer for Service Data must be registered using
RegisterServiceObserver() D-Bus method so it can receive any advertising
which matches with the filter used.
---
src/adapter.c | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)

diff --git a/src/adapter.c b/src/adapter.c
index 36c285a..27692f9 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -1620,6 +1620,26 @@ static DBusMessage *unregister_agent(DBusConnection *conn, DBusMessage *msg,
static DBusMessage *register_service_observer(DBusConnection *conn,
DBusMessage *msg, void *data)
{
+ struct btd_adapter *adapter = data;
+ const char *path, *sender = dbus_message_get_sender(msg);
+ unsigned int uuid;
+ int err;
+
+ if (dbus_message_get_args(msg, NULL, DBUS_TYPE_OBJECT_PATH, &path,
+ DBUS_TYPE_UINT16, &uuid,
+ DBUS_TYPE_INVALID) == FALSE)
+ return btd_error_invalid_args(msg);
+
+ err = mgmt_set_observer(adapter->dev_id, TRUE);
+ if (err < 0) {
+ error("Failed to set Observer: %s (%d)", strerror(-err), -err);
+
+ return btd_error_failed(msg, strerror(-err));
+ }
+
+ DBG("Service Data Observer registered for hci%d at %s:%s",
+ adapter->dev_id, sender, path);
+
return dbus_message_new_method_return(msg);
}

--
1.7.9.5


2012-08-27 17:03:06

by Bruna Moreira

[permalink] [raw]
Subject: [RFC BlueZ 14/33] doc: Add Observer D-Bus API documentation

This is a "high level" API for Observer GAP role.
---
doc/adapter-api.txt | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 75 insertions(+)

diff --git a/doc/adapter-api.txt b/doc/adapter-api.txt
index 23f0a2f..9e2e652 100644
--- a/doc/adapter-api.txt
+++ b/doc/adapter-api.txt
@@ -173,6 +173,57 @@ Methods dict GetProperties()

Possible errors: org.bluez.Error.DoesNotExist

+ void RegisterServiceObserver(object observer,
+ uint16 match_value)
+
+ Registers an observer agent to monitor Service Data
+ broadcasts. This agent will be notified whenever a
+ broadcast is received that matches that filter. It is
+ possible use same object path for two observers:
+ Service Data and Manufacturer Specific Data.
+
+ The match_value parameter must be the 16-bit UUID for
+ the service whose data is to be monitored.
+
+ Possible errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.AlreadyExists
+
+ void RegisterManufacturerObserver(object observer,
+ uint16 match_value)
+
+ Registers an observer agent to monitor Manufacturer
+ Specific Data broadcasts. This agent will be notified
+ whenever a broadcast is received that matches that
+ filter. It is possible use same object path for two
+ observers: Service Data and Manufacturer Specific Data.
+
+ The match_value parameter must be the 16-bit Company
+ Identifier Code for the manufacturer whose data is to
+ be monitored.
+
+ Possible errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.AlreadyExists
+
+ void UnregisterServiceObserver(object observer)
+
+ Unregisters a Service Data observer. Broadcasts will
+ not be notified to this agent anymore. The observer
+ will be destroyed only if no Manufacturer Specific Data
+ was registered with the same object path.
+
+ Possible errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.DoesNotExist
+
+ void UnregisterManufacturerObserver(object observer)
+
+ Unregisters a Manufacturer Specific Data observer.
+ Broadcasts will not be notified to this agent anymore.
+ The observer will be destroyed only if no Service Data
+ was registered with the same object path.
+
+ Possible errors: org.bluez.Error.InvalidArguments
+ org.bluez.Error.DoesNotExist
+
Signals PropertyChanged(string name, variant value)

This signal indicates a changed value of the given
@@ -275,3 +326,27 @@ Properties string Address [readonly]

List of 128-bit UUIDs that represents the available
local services.
+
+Observer hierarchy
+==================
+
+Service unique name
+Interface org.bluez.Observer
+Object path freely definable
+
+Methods void ServiceReceived(string address, uint16 uuid, variant data)
+
+ This callback gets called when a broadcast data has
+ arrived that matches filter used in
+ RegisterServiceObserver.
+
+ Note: data is a bytearray.
+
+ void ManufacturerReceived(string address, uint16 cid,
+ variant data)
+
+ This callback gets called when a broadcast data has
+ arrived that matches filter used in
+ RegisterManufacturerObserver.
+
+ Note: data is a bytearray.
--
1.7.9.5


2012-08-27 17:03:05

by Bruna Moreira

[permalink] [raw]
Subject: [RFC BlueZ 13/33] mgmt: Add unset controller data command

From: Jefferson Delfes <[email protected]>

Implement Unset Controller Data command in MGMT API. This operation
will remove all data from specific type that was set previously with Set
Controller Data operation.
---
src/mgmt.c | 22 ++++++++++++++++++++++
src/mgmt.h | 1 +
2 files changed, 23 insertions(+)

diff --git a/src/mgmt.c b/src/mgmt.c
index a011b3d..0f7b8ed 100644
--- a/src/mgmt.c
+++ b/src/mgmt.c
@@ -2529,3 +2529,25 @@ int mgmt_set_controller_data(int index, uint8_t flags, uint8_t data_type,

return err;
}
+
+int mgmt_unset_controller_data(int index, uint8_t data_type)
+{
+ char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_unset_controller_data)];
+ struct mgmt_hdr *hdr = (void *) buf;
+ struct mgmt_cp_unset_controller_data *cp = (void *) &buf[sizeof(*hdr)];
+
+ DBG("hci%d data_type 0x%hhx", index, data_type);
+
+ memset(buf, 0, sizeof(buf));
+
+ hdr->opcode = htobs(MGMT_OP_UNSET_CONTROLLER_DATA);
+ hdr->index = htobs(index);
+ hdr->len = htobs(sizeof(*cp));
+
+ cp->data_type = data_type;
+
+ if (write(mgmt_sock, buf, sizeof(buf)) < 0)
+ return -errno;
+
+ return 0;
+}
diff --git a/src/mgmt.h b/src/mgmt.h
index 18f3544..c442231 100644
--- a/src/mgmt.h
+++ b/src/mgmt.h
@@ -85,3 +85,4 @@ int mgmt_ssp_enabled(int index);

int mgmt_set_controller_data(int index, uint8_t flags, uint8_t data_type,
uint8_t *data, uint8_t data_length);
+int mgmt_unset_controller_data(int index, uint8_t data_type);
--
1.7.9.5


2012-08-27 17:03:04

by Bruna Moreira

[permalink] [raw]
Subject: [RFC BlueZ 12/33] mgmt: Add set controller data command

From: Jefferson Delfes <[email protected]>

Implement new Set Controller Data command in MGMT API. The maximum size
accept for new data is HCI_MAX_EIR_LENGTH (240 bytes). The data sent
using this command will be stored in internal list and they will be set
in adapter after receiving a Set Broadcaster with TRUE.
---
src/mgmt.c | 32 ++++++++++++++++++++++++++++++++
src/mgmt.h | 3 +++
2 files changed, 35 insertions(+)

diff --git a/src/mgmt.c b/src/mgmt.c
index 3d3a8f7..a011b3d 100644
--- a/src/mgmt.c
+++ b/src/mgmt.c
@@ -2497,3 +2497,35 @@ int mgmt_ssp_enabled(int index)

return mgmt_ssp(info->current_settings);
}
+
+int mgmt_set_controller_data(int index, uint8_t flags, uint8_t data_type,
+ uint8_t *data, uint8_t data_length)
+{
+ char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_set_controller_data) +
+ HCI_MAX_EIR_LENGTH];
+ struct mgmt_hdr *hdr = (void *) buf;
+ struct mgmt_cp_set_controller_data *cp = (void *) &buf[sizeof(*hdr)];
+ uint16_t cp_size;
+ int err = 0;
+
+ DBG("hci%d flags %d data_type 0x%hhx data_length %d", index, flags,
+ data_type, data_length);
+
+ memset(buf, 0, sizeof(buf));
+
+ cp_size = sizeof(*cp) + data_length;
+
+ hdr->opcode = htobs(MGMT_OP_SET_CONTROLLER_DATA);
+ hdr->index = htobs(index);
+ hdr->len = htobs(cp_size);
+
+ cp->flags = flags;
+ cp->data_type = data_type;
+ cp->data_length = data_length;
+ memcpy(cp->data, data, data_length);
+
+ if (write(mgmt_sock, buf, sizeof(*hdr) + cp_size) < 0)
+ err = -errno;
+
+ return err;
+}
diff --git a/src/mgmt.h b/src/mgmt.h
index e77a804..18f3544 100644
--- a/src/mgmt.h
+++ b/src/mgmt.h
@@ -82,3 +82,6 @@ int mgmt_confirm_name(int index, bdaddr_t *bdaddr, uint8_t bdaddr_type,
gboolean name_known);

int mgmt_ssp_enabled(int index);
+
+int mgmt_set_controller_data(int index, uint8_t flags, uint8_t data_type,
+ uint8_t *data, uint8_t data_length);
--
1.7.9.5


2012-08-27 17:03:03

by Bruna Moreira

[permalink] [raw]
Subject: [RFC BlueZ 11/33] mgmt: Add set broadcaster command

From: Jefferson Delfes <[email protected]>

Implement Set Broadcaster support in MGMT API. Setting TRUE will make
the new data sent by Set Controller Data command to be set in adapter
and the advertising will start. Stop the advertising setting FALSE.
---
src/mgmt.c | 5 +++++
src/mgmt.h | 1 +
2 files changed, 6 insertions(+)

diff --git a/src/mgmt.c b/src/mgmt.c
index 0ff1d71..3d3a8f7 100644
--- a/src/mgmt.c
+++ b/src/mgmt.c
@@ -2045,6 +2045,11 @@ int mgmt_set_fast_connectable(int index, gboolean enable)
return 0;
}

+int mgmt_set_broadcaster(int index, gboolean enable)
+{
+ return mgmt_set_mode(index, MGMT_OP_SET_BROADCASTER, enable);
+}
+
int mgmt_set_observer(int index, gboolean enable)
{
return mgmt_set_mode(index, MGMT_OP_SET_OBSERVER, enable);
diff --git a/src/mgmt.h b/src/mgmt.h
index 7a21f37..e77a804 100644
--- a/src/mgmt.h
+++ b/src/mgmt.h
@@ -31,6 +31,7 @@ int mgmt_set_pairable(int index, gboolean pairable);
int mgmt_set_name(int index, const char *name);
int mgmt_set_dev_class(int index, uint8_t major, uint8_t minor);
int mgmt_set_fast_connectable(int index, gboolean enable);
+int mgmt_set_broadcaster(int index, gboolean enable);
int mgmt_set_observer(int index, gboolean enable);

int mgmt_start_discovery(int index);
--
1.7.9.5


2012-08-27 17:03:02

by Bruna Moreira

[permalink] [raw]
Subject: [RFC BlueZ 10/33] mgmt: Add set observer command

From: Jefferson Delfes <[email protected]>

Implement Set Observer support in MGMT API. Setting TRUE will make the
adapter start LE discovery, otherwise, setting FALSE will make the
adater stop LE discovery.
---
src/mgmt.c | 5 +++++
src/mgmt.h | 1 +
2 files changed, 6 insertions(+)

diff --git a/src/mgmt.c b/src/mgmt.c
index 58aab2d..0ff1d71 100644
--- a/src/mgmt.c
+++ b/src/mgmt.c
@@ -2045,6 +2045,11 @@ int mgmt_set_fast_connectable(int index, gboolean enable)
return 0;
}

+int mgmt_set_observer(int index, gboolean enable)
+{
+ return mgmt_set_mode(index, MGMT_OP_SET_OBSERVER, enable);
+}
+
int mgmt_read_clock(int index, bdaddr_t *bdaddr, int which, int timeout,
uint32_t *clock, uint16_t *accuracy)
{
diff --git a/src/mgmt.h b/src/mgmt.h
index 95245d2..7a21f37 100644
--- a/src/mgmt.h
+++ b/src/mgmt.h
@@ -31,6 +31,7 @@ int mgmt_set_pairable(int index, gboolean pairable);
int mgmt_set_name(int index, const char *name);
int mgmt_set_dev_class(int index, uint8_t major, uint8_t minor);
int mgmt_set_fast_connectable(int index, gboolean enable);
+int mgmt_set_observer(int index, gboolean enable);

int mgmt_start_discovery(int index);
int mgmt_stop_discovery(int index);
--
1.7.9.5


2012-08-27 17:03:01

by Bruna Moreira

[permalink] [raw]
Subject: [RFC BlueZ 09/33] mgmt-api: Broadcaster/Observer management API

From: Anderson Lizardo <[email protected]>

Add new management commands for enabling or disabling broadcasting and
observation modes, as defined by the Observer and Broadcaster GAP roles
on the Core specification 4.0.

The commands can also be used for adding information to EIR on BR/EDR
controllers. For a list of all available AD/EIR types, see the Core
Specification Supplement (CSS) document.

Note that some AD/EIR types are managed internally by the kernel and are
not available through these new commands.
---
doc/mgmt-api.txt | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 64 insertions(+)

diff --git a/doc/mgmt-api.txt b/doc/mgmt-api.txt
index c25f377..48d6355 100644
--- a/doc/mgmt-api.txt
+++ b/doc/mgmt-api.txt
@@ -139,6 +139,8 @@ Read Controller Information Command
8 Basic Rate/Enhanced Data Rate
9 High Speed
10 Low Energy
+ 11 Broadcaster
+ 12 Observer

This command generates a Command Complete event on success or
a Command Status event on failure.
@@ -845,6 +847,68 @@ Set Device ID Command
a Command Status event on failure.


+Set Controller Data Command
+===========================
+
+ Command Code: 0x0029
+ Controller Index: <controller id>
+ Command Parameters: Flags (1 Octet)
+ Data_Type (1 Octet)
+ Data_Length (1 Octet)
+ Data (0-255 Octets)
+ Return Parameters:
+
+ This command can be used to set AD for LE capable controllers or EIR
+ for BR/EDR controllers. Multiple AD/EIR types can be configured by
+ calling this command multiple times. If the controller is BR/EDR/LE
+ capable (also known as "dual mode"), only the LE advertising is set.
+
+ The Flags parameter is currently unused, but it will contain bitwise
+ flags to fine tune how and when the data will set on the controller.
+
+ This command generates a Command Complete event on success or
+ a Command Status event on failure.
+
+
+Unset Controller Data Command
+=============================
+
+ Command Code: 0x002A
+ Controller Index: <controller id>
+ Command Parameters: Data_Type (1 Octet)
+ Return Parameters:
+
+ Remove the AD/EIR information identified by Data_Type, so it will not
+ be broadcasted anymore.
+
+ This command generates a Command Complete event on success or
+ a Command Status event on failure.
+
+
+Set Broadcaster Command
+=======================
+
+ Command Code: 0x002B
+ Controller Index: <controller id>
+ Command Parameters: Broadcaster (1 Octet)
+ Return Parameters: Current_Settings (4 Octets)
+
+ This command generates a Command Complete event on success or
+ a Command Status event on failure.
+
+
+Set Observer Command
+====================
+
+ Command Code: 0x002C
+ Controller Index: <controller id>
+ Command Parameters: Observer (1 Octet)
+ Return Parameters: Current_Settings (4 Octets)
+
+ This command generates a Command Complete event on success or
+ a Command Status event on failure.
+
+
Command Complete Event
======================

--
1.7.9.5


2012-08-27 17:03:00

by Bruna Moreira

[permalink] [raw]
Subject: [RFC BlueZ 08/33] btmgmt: Add unset controller data support

Add new command unset-data for running the Unset Controller Data
operation from MGMT API.
---
tools/btmgmt.c | 40 ++++++++++++++++++++++++++++++++++++++++
1 file changed, 40 insertions(+)

diff --git a/tools/btmgmt.c b/tools/btmgmt.c
index 628d21b..44c4db8 100644
--- a/tools/btmgmt.c
+++ b/tools/btmgmt.c
@@ -1896,6 +1896,45 @@ static void cmd_set_data(int mgmt_sk, uint16_t index, int argc, char **argv)
free(cp);
}

+static void unset_data_rsp(int mgmt_sk, uint16_t op, uint16_t id,
+ uint8_t status, void *rsp, uint16_t len, void *user_data)
+{
+ if (status != 0) {
+ fprintf(stderr, "Unable to unset controller data. status 0x%02x"
+ " (%s)\n", status, mgmt_errstr(status));
+ exit(EXIT_FAILURE);
+ }
+
+ exit(EXIT_SUCCESS);
+}
+
+static void cmd_unset_data(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+ struct mgmt_cp_unset_controller_data cp;
+
+ if (argc < 2) {
+ printf("Usage: btmgmt %s <data type>\n", argv[0]);
+ printf("\nexample: btmgmt %s 0xff\n", argv[0]);
+ printf("\nparameters:\n");
+ printf("\t<data type>: 0x16 (service data) or 0xff\n"
+ "\t(manufacturer specific data).\n");
+
+ exit(EXIT_FAILURE);
+ }
+
+ memset(&cp, 0, sizeof(cp));
+ sscanf(argv[1], "%hhx", &cp.data_type);
+
+ if (index == MGMT_INDEX_NONE)
+ index = 0;
+
+ if (mgmt_send_cmd(mgmt_sk, MGMT_OP_UNSET_CONTROLLER_DATA, index,
+ &cp, sizeof(cp), unset_data_rsp, NULL) < 0) {
+ fprintf(stderr, "Unable to send unset controller data cmd\n");
+ exit(EXIT_FAILURE);
+ }
+}
+
static struct {
char *cmd;
void (*func)(int mgmt_sk, uint16_t index, int argc, char **argv);
@@ -1930,6 +1969,7 @@ static struct {
{ "broadcaster",cmd_broadcaster,"Toggle Broadcaster Mode", },
{ "observer", cmd_observer, "Toggle Observer Mode", },
{ "set-data", cmd_set_data, "Set Controller Data", },
+ { "unset-data", cmd_unset_data, "Unset Controller Data", },
{ NULL, NULL, 0 }
};

--
1.7.9.5


2012-08-27 17:02:59

by Bruna Moreira

[permalink] [raw]
Subject: [RFC BlueZ 07/33] btmgmt: Add set controller data support

Add new command set-data for running the Set Controller Data operation
from MGMT API.
---
tools/btmgmt.c | 84 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 84 insertions(+)

diff --git a/tools/btmgmt.c b/tools/btmgmt.c
index 5cc3198..628d21b 100644
--- a/tools/btmgmt.c
+++ b/tools/btmgmt.c
@@ -1813,6 +1813,89 @@ done:
}
}

+static void set_data_rsp(int mgmt_sk, uint16_t op, uint16_t id, uint8_t status,
+ void *rsp, uint16_t len, void *user_data)
+{
+ if (status != 0) {
+ fprintf(stderr, "Unable to set controller data. status 0x%02x"
+ " (%s)\n", status, mgmt_errstr(status));
+ exit(EXIT_FAILURE);
+ }
+
+ exit(EXIT_SUCCESS);
+}
+
+static void set_data_usage(void)
+{
+ printf("Usage: btmgmt set_data [-p] <data type> <byte array>\n");
+ printf("\nexample: btmgmt set_data -p 0xff 11 11 aa bb cc\n");
+ printf("\nparameters:\n");
+ printf("\t<data type>: 0x16 (service data) or 0xff (manufacturer\n"
+ "\t specific data). Values different from that will return\n"
+ "\t error from kernel.\n");
+ printf("\t<byte array>: two bytes (service uuid or company identifier\n"
+ "\t code) following by hex encoded bytes (data).\n");
+}
+
+static struct option set_data_options[] = {
+ { "help", 0, 0, 'h' },
+ { "priority", 1, 0, 'p' },
+ { 0, 0, 0, 0 }
+};
+
+static void cmd_set_data(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+ struct mgmt_cp_set_controller_data *cp;
+ uint8_t data_len, flags = 0;
+ unsigned int i;
+ int opt;
+
+ while ((opt = getopt_long(argc, argv, "+ph", set_data_options,
+ NULL)) != -1) {
+ switch (opt) {
+ case 'p':
+ flags = MGMT_DATA_HIGH_PRIORITY;
+ break;
+ case 'h':
+ default:
+ set_data_usage();
+ exit(EXIT_SUCCESS);
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+ optind = 0;
+
+ if (argc < 1) {
+ set_data_usage();
+ exit(EXIT_FAILURE);
+ }
+
+ data_len = argc - 1;
+ cp = malloc(sizeof(*cp) + data_len);
+ cp->flags = flags;
+ sscanf(argv[0], "%hhx", &cp->data_type);
+
+ for (i = 0; i < data_len; i++)
+ sscanf(argv[i + 1], "%hhx", &cp->data[i]);
+
+ cp->data_length = data_len;
+
+ if (index == MGMT_INDEX_NONE)
+ index = 0;
+
+ if (mgmt_send_cmd(mgmt_sk, MGMT_OP_SET_CONTROLLER_DATA, index,
+ cp, sizeof(*cp) + data_len,
+ set_data_rsp, NULL) < 0) {
+ free(cp);
+ fprintf(stderr, "Unable to send set controller data cmd\n");
+ exit(EXIT_FAILURE);
+ }
+
+ free(cp);
+}
+
static struct {
char *cmd;
void (*func)(int mgmt_sk, uint16_t index, int argc, char **argv);
@@ -1846,6 +1929,7 @@ static struct {
{ "did", cmd_did, "Set Device ID", },
{ "broadcaster",cmd_broadcaster,"Toggle Broadcaster Mode", },
{ "observer", cmd_observer, "Toggle Observer Mode", },
+ { "set-data", cmd_set_data, "Set Controller Data", },
{ NULL, NULL, 0 }
};

--
1.7.9.5


2012-08-27 17:02:58

by Bruna Moreira

[permalink] [raw]
Subject: [RFC BlueZ 06/33] btmgmt: Add set observer command

Add new observer command for running the Set Observer operation from
MGMT API.
---
tools/btmgmt.c | 6 ++++++
1 file changed, 6 insertions(+)

diff --git a/tools/btmgmt.c b/tools/btmgmt.c
index adfab83..5cc3198 100644
--- a/tools/btmgmt.c
+++ b/tools/btmgmt.c
@@ -1104,6 +1104,11 @@ static void cmd_broadcaster(int mgmt_sk, uint16_t index, int argc, char **argv)
cmd_setting(mgmt_sk, index, MGMT_OP_SET_BROADCASTER, argc, argv);
}

+static void cmd_observer(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+ cmd_setting(mgmt_sk, index, MGMT_OP_SET_OBSERVER, argc, argv);
+}
+
static void class_rsp(int mgmt_sk, uint16_t op, uint16_t id, uint8_t status,
void *rsp, uint16_t len, void *user_data)
{
@@ -1840,6 +1845,7 @@ static struct {
{ "clr-uuids", cmd_clr_uuids, "Clear UUIDs", },
{ "did", cmd_did, "Set Device ID", },
{ "broadcaster",cmd_broadcaster,"Toggle Broadcaster Mode", },
+ { "observer", cmd_observer, "Toggle Observer Mode", },
{ NULL, NULL, 0 }
};

--
1.7.9.5


2012-08-27 17:02:57

by Bruna Moreira

[permalink] [raw]
Subject: [RFC BlueZ 05/33] btmgmt: Add set broadcaster support

From: Jefferson Delfes <[email protected]>

Add new broadcaster command for running the Set Broadcaster operation
from MGMT API.
---
tools/btmgmt.c | 6 ++++++
1 file changed, 6 insertions(+)

diff --git a/tools/btmgmt.c b/tools/btmgmt.c
index f06f0bf..adfab83 100644
--- a/tools/btmgmt.c
+++ b/tools/btmgmt.c
@@ -1099,6 +1099,11 @@ static void cmd_le(int mgmt_sk, uint16_t index, int argc, char **argv)
cmd_setting(mgmt_sk, index, MGMT_OP_SET_LE, argc, argv);
}

+static void cmd_broadcaster(int mgmt_sk, uint16_t index, int argc, char **argv)
+{
+ cmd_setting(mgmt_sk, index, MGMT_OP_SET_BROADCASTER, argc, argv);
+}
+
static void class_rsp(int mgmt_sk, uint16_t op, uint16_t id, uint8_t status,
void *rsp, uint16_t len, void *user_data)
{
@@ -1834,6 +1839,7 @@ static struct {
{ "rm-uuid", cmd_add_uuid, "Remove UUID" },
{ "clr-uuids", cmd_clr_uuids, "Clear UUIDs", },
{ "did", cmd_did, "Set Device ID", },
+ { "broadcaster",cmd_broadcaster,"Toggle Broadcaster Mode", },
{ NULL, NULL, 0 }
};

--
1.7.9.5


2012-08-27 17:02:56

by Bruna Moreira

[permalink] [raw]
Subject: [RFC BlueZ 04/33] lib: Add set observer operation

From: Jefferson Delfes <[email protected]>

Add opcodes for Set Observer command.
---
lib/mgmt.h | 4 ++++
1 file changed, 4 insertions(+)

diff --git a/lib/mgmt.h b/lib/mgmt.h
index 6ecad43..1e6bb32 100644
--- a/lib/mgmt.h
+++ b/lib/mgmt.h
@@ -93,6 +93,7 @@ struct mgmt_rp_read_index_list {
#define MGMT_SETTING_HS 0x00000100
#define MGMT_SETTING_LE 0x00000200
#define MGMT_SETTING_BROADCASTER 0x00000400
+#define MGMT_SETTING_OBSERVER 0x00000800

#define MGMT_OP_READ_INFO 0x0004
struct mgmt_rp_read_info {
@@ -337,6 +338,8 @@ struct mgmt_cp_unset_controller_data {

#define MGMT_OP_SET_BROADCASTER 0x002B

+#define MGMT_OP_SET_OBSERVER 0x002C
+
#define MGMT_EV_CMD_COMPLETE 0x0001
struct mgmt_ev_cmd_complete {
uint16_t opcode;
@@ -511,6 +514,7 @@ static const char *mgmt_op[] = {
"Set Controller Data",
"Unset Controller Data",
"Set Broadcaster",
+ "Set Observer",
};

static const char *mgmt_ev[] = {
--
1.7.9.5


2012-08-27 17:02:55

by Bruna Moreira

[permalink] [raw]
Subject: [RFC BlueZ 03/33] lib: Add set broadcaster operation

Add opcodes for new Set Broadcaster command.
---
lib/mgmt.h | 4 ++++
1 file changed, 4 insertions(+)

diff --git a/lib/mgmt.h b/lib/mgmt.h
index e65b7fb..6ecad43 100644
--- a/lib/mgmt.h
+++ b/lib/mgmt.h
@@ -92,6 +92,7 @@ struct mgmt_rp_read_index_list {
#define MGMT_SETTING_BREDR 0x00000080
#define MGMT_SETTING_HS 0x00000100
#define MGMT_SETTING_LE 0x00000200
+#define MGMT_SETTING_BROADCASTER 0x00000400

#define MGMT_OP_READ_INFO 0x0004
struct mgmt_rp_read_info {
@@ -334,6 +335,8 @@ struct mgmt_cp_unset_controller_data {
uint8_t data_type;
} __packed;

+#define MGMT_OP_SET_BROADCASTER 0x002B
+
#define MGMT_EV_CMD_COMPLETE 0x0001
struct mgmt_ev_cmd_complete {
uint16_t opcode;
@@ -507,6 +510,7 @@ static const char *mgmt_op[] = {
"Set Device ID",
"Set Controller Data",
"Unset Controller Data",
+ "Set Broadcaster",
};

static const char *mgmt_ev[] = {
--
1.7.9.5


2012-08-27 17:02:54

by Bruna Moreira

[permalink] [raw]
Subject: [RFC BlueZ 02/33] lib: Add unset controller data operation

From: Jefferson Delfes <[email protected]>

Add structure for new command: Unset Controller Data.
---
lib/mgmt.h | 6 ++++++
1 file changed, 6 insertions(+)

diff --git a/lib/mgmt.h b/lib/mgmt.h
index 99243b1..e65b7fb 100644
--- a/lib/mgmt.h
+++ b/lib/mgmt.h
@@ -329,6 +329,11 @@ struct mgmt_cp_set_controller_data {
uint8_t data[0];
} __packed;

+#define MGMT_OP_UNSET_CONTROLLER_DATA 0x002A
+struct mgmt_cp_unset_controller_data {
+ uint8_t data_type;
+} __packed;
+
#define MGMT_EV_CMD_COMPLETE 0x0001
struct mgmt_ev_cmd_complete {
uint16_t opcode;
@@ -501,6 +506,7 @@ static const char *mgmt_op[] = {
"Unblock Device",
"Set Device ID",
"Set Controller Data",
+ "Unset Controller Data",
};

static const char *mgmt_ev[] = {
--
1.7.9.5


2012-08-27 17:02:53

by Bruna Moreira

[permalink] [raw]
Subject: [RFC BlueZ 01/33] lib: Add set controller data operation

From: Jefferson Delfes <[email protected]>

Add structure for new command: Set Controller Data. Add new defines for
flags that will be used in Set Controller Data operation.
---
lib/mgmt.h | 12 ++++++++++++
1 file changed, 12 insertions(+)

diff --git a/lib/mgmt.h b/lib/mgmt.h
index a2648bc..99243b1 100644
--- a/lib/mgmt.h
+++ b/lib/mgmt.h
@@ -318,6 +318,17 @@ struct mgmt_cp_set_device_id {
uint16_t version;
} __packed;

+#define MGMT_DATA_NORMAL_PRIORITY 0x00
+#define MGMT_DATA_HIGH_PRIORITY 0x01
+
+#define MGMT_OP_SET_CONTROLLER_DATA 0x0029
+struct mgmt_cp_set_controller_data {
+ uint8_t flags;
+ uint8_t data_type;
+ uint8_t data_length;
+ uint8_t data[0];
+} __packed;
+
#define MGMT_EV_CMD_COMPLETE 0x0001
struct mgmt_ev_cmd_complete {
uint16_t opcode;
@@ -489,6 +500,7 @@ static const char *mgmt_op[] = {
"Block Device",
"Unblock Device",
"Set Device ID",
+ "Set Controller Data",
};

static const char *mgmt_ev[] = {
--
1.7.9.5