2015-02-13 03:50:18

by Jakub Pawlowski

[permalink] [raw]
Subject: [PATCH BlueZ v2 1/6] core: adapter: Add StartFilteredDiscovery method

This patch adds StartFilteredDiscovery method, as described in
doc/adapter-api.txt to DBus interface. Method content wil follow in
next patches.
---
src/adapter.c | 9 +++++++++
1 file changed, 9 insertions(+)

diff --git a/src/adapter.c b/src/adapter.c
index 1839286..df0c0b8 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -1744,6 +1744,12 @@ static DBusMessage *start_discovery(DBusConnection *conn,
return dbus_message_new_method_return(msg);
}

+static DBusMessage *start_filtered_discovery(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ return btd_error_failed(msg, "Not implemented yet");
+}
+
static DBusMessage *stop_discovery(DBusConnection *conn,
DBusMessage *msg, void *user_data)
{
@@ -2278,6 +2284,9 @@ static DBusMessage *remove_device(DBusConnection *conn,

static const GDBusMethodTable adapter_methods[] = {
{ GDBUS_METHOD("StartDiscovery", NULL, NULL, start_discovery) },
+ { GDBUS_METHOD("StartFilteredDiscovery",
+ GDBUS_ARGS({ "properties", "a{sv}" }), NULL,
+ start_filtered_discovery) },
{ GDBUS_METHOD("StopDiscovery", NULL, NULL, stop_discovery) },
{ GDBUS_ASYNC_METHOD("RemoveDevice",
GDBUS_ARGS({ "device", "o" }), NULL, remove_device) },
--
2.2.0.rc0.207.ga3a616c



2015-02-13 03:50:23

by Jakub Pawlowski

[permalink] [raw]
Subject: [PATCH BlueZ v2 6/6] core: adapter: Implement internals of StartFilteredDiscovery

This patch implement guts of StartFilteredDiscovery method. It adds new
structure, discovery_filter to client_watch that might contain
information about filter for given client.
---
src/adapter.c | 417 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 401 insertions(+), 16 deletions(-)

diff --git a/src/adapter.c b/src/adapter.c
index cf4cf0a..01d5dbd 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -82,6 +82,7 @@
#define TEMP_DEV_TIMEOUT (3 * 60)
#define BONDING_TIMEOUT (2 * 60)

+#define HCI_RSSI_INVALID 127
#define DISTNACE_VAL_INVALID 0x7FFF

static DBusConnection *dbus_conn = NULL;
@@ -137,10 +138,18 @@ struct conn_param {
uint16_t timeout;
};

+struct discovery_filter {
+ uint8_t type;
+ uint16_t pathloss;
+ int16_t rssi;
+ GSList *uuids;
+};
+
struct watch_client {
struct btd_adapter *adapter;
char *owner;
guint watch;
+ struct discovery_filter *discovery_filter;
};

struct service_auth {
@@ -193,6 +202,8 @@ struct btd_adapter {
GSList *discovery_list; /* list of discovery clients */
GSList *discovery_found; /* list of found devices */
guint discovery_idle_timeout; /* timeout between discovery runs */
+ guint filtered_discovery_idle_timeout;
+ /* timeout between filtered discovery runs */
guint passive_scan_timeout; /* timeout between passive scans */
guint temp_devices_timeout; /* timeout for temporary devices */

@@ -1385,7 +1396,8 @@ static gboolean start_discovery_timeout(gpointer user_data)
* If there is an already running discovery and it has the
* same type, then just keep it.
*/
- if (adapter->discovery_type == new_type) {
+ if (adapter->discovery_type == new_type &&
+ adapter->filtered_discovery == false) {
if (adapter->discovering)
return FALSE;

@@ -1395,6 +1407,13 @@ static gboolean start_discovery_timeout(gpointer user_data)
return FALSE;
}

+
+ if (adapter->filtered_discovery) {
+ adapter->filtered_discovery = false;
+ g_dbus_emit_property_changed(dbus_conn, adapter->path,
+ ADAPTER_INTERFACE, "FilteredDiscovery");
+ }
+
/*
* Otherwise the current discovery must be stopped. So
* queue up a stop discovery command.
@@ -1430,6 +1449,11 @@ static void trigger_start_discovery(struct btd_adapter *adapter, guint delay)
adapter->discovery_idle_timeout = 0;
}

+ if (adapter->filtered_discovery_idle_timeout > 0) {
+ g_source_remove(adapter->filtered_discovery_idle_timeout);
+ adapter->filtered_discovery_idle_timeout = 0;
+ }
+
/*
* If the controller got powered down in between, then ensure
* that we do not keep trying to restart discovery.
@@ -1443,6 +1467,203 @@ static void trigger_start_discovery(struct btd_adapter *adapter, guint delay)
start_discovery_timeout, adapter);
}

+static void trigger_start_filtered_discovery(struct btd_adapter *adapter,
+ guint delay);
+
+static void start_filtered_discovery_complete(uint8_t status, uint16_t length,
+ const void *param, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ const int *type = param;
+
+ DBG("status 0x%02x", status);
+ if (length < 1) {
+ error("Wrong size of return parameters");
+ return;
+ }
+
+ if (status == MGMT_STATUS_SUCCESS) {
+ adapter->discovery_enable = 0x01;
+ adapter->discovery_type = *type;
+
+ if (adapter->discovering) {
+ adapter->discovering = false;
+ g_dbus_emit_property_changed(dbus_conn, adapter->path,
+ ADAPTER_INTERFACE, "Discovering");
+ }
+
+ if (adapter->filtered_discovery)
+ return;
+
+ adapter->filtered_discovery = true;
+ g_dbus_emit_property_changed(dbus_conn, adapter->path,
+ ADAPTER_INTERFACE, "FilteredDiscovery");
+ return;
+ }
+
+ /*
+ * In case the restart of the discovery failed, then just trigger
+ * it for the next idle timeout again.
+ */
+ trigger_start_filtered_discovery(adapter, IDLE_DISCOV_TIMEOUT * 2);
+}
+
+/* This method merges all filters into one that will be send to kernel.
+ * empty_uuid variable determines wether there was any filter with no uuids. In
+ * this case someone might be looking for all devices in certain proximity, and
+ * we need to have empty uuids in kernel filter.
+ */
+static struct mgmt_cp_start_service_discovery *discovery_filter_to_mgmt_cp(
+ struct btd_adapter *adapter)
+{
+ GSList *l, *m;
+ struct mgmt_cp_start_service_discovery *cp;
+ int rssi = DISTNACE_VAL_INVALID;
+ int uuid_count = 0;
+ uint8_t discovery_type = 0;
+ uint8_t (*current_uuid)[16];
+ bool empty_uuid = false;
+
+ for (l = adapter->discovery_list; l != NULL; l = g_slist_next(l)) {
+ struct watch_client *client = l->data;
+ struct discovery_filter *item = client->discovery_filter;
+ int curr_uuid_count;
+
+ if (item == NULL) {
+ /* We should be running regular, not filtered discov */
+ error("item == NULL this should never happen!!");
+ continue;
+ }
+
+ discovery_type |= item->type;
+
+ if (item->rssi != DISTNACE_VAL_INVALID &&
+ rssi != HCI_RSSI_INVALID && rssi >= item->rssi)
+ rssi = item->rssi;
+ else if (item->pathloss != DISTNACE_VAL_INVALID)
+ /* if we're doing pathloss filtering, or no range
+ * filtering, we disable RSSI filter.
+ */
+ rssi = HCI_RSSI_INVALID;
+
+ curr_uuid_count = g_slist_length(item->uuids);
+
+ if (curr_uuid_count == 0)
+ empty_uuid = true;
+
+ uuid_count += curr_uuid_count;
+ }
+
+ /* if no proximity filtering is set, disable it */
+ if (rssi == DISTNACE_VAL_INVALID)
+ rssi = HCI_RSSI_INVALID;
+
+ if (empty_uuid == true)
+ uuid_count = 0;
+
+ cp = g_try_malloc(sizeof(cp) + 16*uuid_count);
+ if (cp == NULL)
+ return NULL;
+
+ cp->type = discovery_type;
+ cp->rssi = rssi;
+ cp->uuid_count = uuid_count;
+
+ if (empty_uuid)
+ return cp;
+
+ current_uuid = cp->uuids;
+ for (l = adapter->discovery_list; l != NULL; l = g_slist_next(l)) {
+ struct watch_client *client = l->data;
+ struct discovery_filter *item = client->discovery_filter;
+
+ for (m = item->uuids; m != NULL; m = g_slist_next(m)) {
+ char *uuid_str = m->data;
+ uuid_t uuid_tmp;
+ uint128_t uint128;
+ uuid_t uuid128;
+
+ bt_string2uuid(&uuid_tmp, uuid_str);
+
+ uuid_to_uuid128(&uuid128, &uuid_tmp);
+ ntoh128((uint128_t *) uuid128.value.uuid128.data,
+ &uint128);
+ htob128(&uint128, (uint128_t *) current_uuid);
+
+ current_uuid++;
+ }
+ }
+ return cp;
+}
+
+static void free_discovery_filter(struct discovery_filter *discovery_filter)
+{
+ g_slist_free_full(discovery_filter->uuids, g_free);
+ g_free(discovery_filter);
+}
+
+static gboolean start_filtered_discovery_timeout(gpointer user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ struct mgmt_cp_start_service_discovery *cp;
+
+ DBG("");
+
+ adapter->filtered_discovery_idle_timeout = 0;
+
+ /* if there's any scan running, stop it. */
+ if (adapter->discovery_enable == 0x01) {
+ struct mgmt_cp_stop_discovery cp;
+
+ cp.type = adapter->discovery_type;
+ mgmt_send(adapter->mgmt, MGMT_OP_STOP_DISCOVERY,
+ adapter->dev_id, sizeof(cp), &cp,
+ NULL, NULL, NULL);
+ }
+
+ cp = discovery_filter_to_mgmt_cp(adapter);
+ if (cp == NULL) {
+ error("discovery_filter_to_mgmt_cp returned NULL");
+ return FALSE;
+ }
+
+ mgmt_send(adapter->mgmt, MGMT_OP_START_SERVICE_DISCOVERY,
+ adapter->dev_id, sizeof(*cp) + cp->uuid_count * 16, cp,
+ start_filtered_discovery_complete, adapter, NULL);
+
+ g_free(cp);
+ return FALSE;
+}
+
+static void trigger_start_filtered_discovery(struct btd_adapter *adapter,
+ guint delay)
+{
+ DBG("");
+ cancel_passive_scanning(adapter);
+
+ if (adapter->discovery_idle_timeout > 0) {
+ g_source_remove(adapter->discovery_idle_timeout);
+ adapter->discovery_idle_timeout = 0;
+ }
+
+ if (adapter->filtered_discovery_idle_timeout > 0) {
+ g_source_remove(adapter->filtered_discovery_idle_timeout);
+ adapter->filtered_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->filtered_discovery_idle_timeout = g_timeout_add_seconds(delay,
+ start_filtered_discovery_timeout, adapter);
+}
+
static void suspend_discovery_complete(uint8_t status, uint16_t length,
const void *param, void *user_data)
{
@@ -1483,6 +1704,12 @@ static void suspend_discovery(struct btd_adapter *adapter)
adapter->discovery_idle_timeout = 0;
}

+ if (adapter->filtered_discovery_idle_timeout > 0) {
+ g_source_remove(adapter->filtered_discovery_idle_timeout);
+ adapter->filtered_discovery_idle_timeout = 0;
+ }
+
+
if (adapter->discovery_enable == 0x00)
return;

@@ -1525,8 +1752,8 @@ static void discovering_callback(uint16_t index, uint16_t length,
return;
}

- DBG("hci%u type %u discovering %u", adapter->dev_id,
- ev->type, ev->discovering);
+ DBG("hci%u type %u discovering %u method %d", adapter->dev_id, ev->type,
+ ev->discovering, adapter->filtered_discovery);

if (adapter->discovery_enable == ev->discovering)
return;
@@ -1552,7 +1779,13 @@ static void discovering_callback(uint16_t index, uint16_t length,

switch (adapter->discovery_enable) {
case 0x00:
- trigger_start_discovery(adapter, IDLE_DISCOV_TIMEOUT);
+ if (adapter->discovering)
+ trigger_start_discovery(adapter, IDLE_DISCOV_TIMEOUT);
+ else if (adapter->filtered_discovery)
+ trigger_start_filtered_discovery(adapter, 0);
+ else
+ error("Wrong discovery state!");
+
break;

case 0x01:
@@ -1560,10 +1793,23 @@ static void discovering_callback(uint16_t index, uint16_t length,
g_source_remove(adapter->discovery_idle_timeout);
adapter->discovery_idle_timeout = 0;
}
+ if (adapter->filtered_discovery_idle_timeout > 0) {
+ g_source_remove(
+ adapter->filtered_discovery_idle_timeout);
+ adapter->filtered_discovery_idle_timeout = 0;
+ }
+
break;
}
}

+static int find_regular_scan(gconstpointer a, gconstpointer b)
+{
+ const struct watch_client *client = a;
+
+ return client->discovery_filter != NULL;
+}
+
static void stop_discovery_complete(uint8_t status, uint16_t length,
const void *param, void *user_data)
{
@@ -1574,12 +1820,26 @@ static void stop_discovery_complete(uint8_t status, uint16_t length,
if (status == MGMT_STATUS_SUCCESS) {
adapter->discovery_type = 0x00;
adapter->discovery_enable = 0x00;
+ if (adapter->discovering) {
+ adapter->discovering = false;
+ g_dbus_emit_property_changed(dbus_conn, adapter->path,
+ ADAPTER_INTERFACE, "Discovering");
+ }
+ if (adapter->filtered_discovery) {
+ adapter->filtered_discovery = false;
+ g_dbus_emit_property_changed(dbus_conn, adapter->path,
+ ADAPTER_INTERFACE, "FilteredDiscovery");
+ }

- adapter->discovering = false;
- g_dbus_emit_property_changed(dbus_conn, adapter->path,
- ADAPTER_INTERFACE, "Discovering");
-
- trigger_passive_scanning(adapter);
+ /* if we just finished last reglar scan, and there's some
+ * filtered scan left, bring it back
+ */
+ if (adapter->discovery_list != NULL &&
+ g_slist_find_custom(adapter->discovery_list, NULL,
+ find_regular_scan) == NULL)
+ trigger_start_filtered_discovery(adapter, 0);
+ else
+ trigger_passive_scanning(adapter);
}
}

@@ -1635,6 +1895,11 @@ static void discovery_destroy(void *user_data)
adapter->discovery_list = g_slist_remove(adapter->discovery_list,
client);

+ if (client->discovery_filter != NULL) {
+ free_discovery_filter(client->discovery_filter);
+ client->discovery_filter = NULL;
+ }
+
g_free(client->owner);
g_free(client);

@@ -1652,6 +1917,12 @@ static void discovery_destroy(void *user_data)
adapter->discovery_idle_timeout = 0;
}

+ if (adapter->filtered_discovery_idle_timeout > 0) {
+ g_source_remove(adapter->filtered_discovery_idle_timeout);
+ adapter->filtered_discovery_idle_timeout = 0;
+ }
+
+
if (adapter->temp_devices_timeout > 0) {
g_source_remove(adapter->temp_devices_timeout);
adapter->temp_devices_timeout = 0;
@@ -1730,6 +2001,7 @@ static DBusMessage *start_discovery(DBusConnection *conn,

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);
@@ -1818,7 +2090,9 @@ static DBusMessage *start_filtered_discovery(DBusConnection *conn,
DBusMessage *msg, void *user_data)
{
struct btd_adapter *adapter = user_data;
+ struct watch_client *client;
const char *sender = dbus_message_get_sender(msg);
+ struct discovery_filter *discovery_filter;
DBusMessageIter iter, subiter, dictiter, variantiter;
uint16_t pathloss = DISTNACE_VAL_INVALID;
int16_t rssi = DISTNACE_VAL_INVALID;
@@ -1887,8 +2161,37 @@ static DBusMessage *start_filtered_discovery(DBusConnection *conn,

DBG("filtered discovery params: type: %d rssi: %d pathloss: %d",
discov_type, rssi, pathloss);
- g_slist_free_full(uuids, g_free);
- return btd_error_failed(msg, "Not implemented yet");
+
+ discovery_filter = g_try_malloc(sizeof(discovery_filter));
+ if (discovery_filter == NULL) {
+ g_slist_free_full(uuids, g_free);
+ return btd_error_failed(msg, "Not enough memory.");
+ }
+
+ discovery_filter->type = discov_type;
+ discovery_filter->pathloss = pathloss;
+ discovery_filter->rssi = rssi;
+ discovery_filter->uuids = uuids;
+
+ client = g_new0(struct watch_client, 1);
+
+ client->adapter = adapter;
+ client->owner = g_strdup(sender);
+ client->discovery_filter = discovery_filter;
+ 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 filtered discovery here. In case an already running
+ * discovery in idle phase exists, it will be restarted right
+ * away.
+ */
+ trigger_start_filtered_discovery(adapter, 0);
+
+ return dbus_message_new_method_return(msg);

invalid_args:
g_slist_free_full(uuids, g_free);
@@ -1928,17 +2231,32 @@ static DBusMessage *stop_discovery(DBusConnection *conn,
* As long as other discovery clients are still active, just
* return success.
*/
- if (adapter->discovery_list)
+ if (adapter->discovery_list) {
+ /* if only filtered scans are left, first kill regular scan */
+ if (g_slist_find_custom(adapter->discovery_list, NULL,
+ find_regular_scan) == NULL) {
+ mgmt_send(adapter->mgmt, MGMT_OP_STOP_DISCOVERY,
+ adapter->dev_id, sizeof(cp), &cp,
+ stop_discovery_complete, adapter, NULL);
+ }
+
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->discovering = false;
- g_dbus_emit_property_changed(dbus_conn, adapter->path,
- ADAPTER_INTERFACE, "Discovering");
+ if (adapter->discovering) {
+ adapter->discovering = false;
+ g_dbus_emit_property_changed(dbus_conn, adapter->path,
+ ADAPTER_INTERFACE, "Discovering");
+ } else if (adapter->filtered_discovery) {
+ adapter->filtered_discovery = false;
+ g_dbus_emit_property_changed(dbus_conn, adapter->path,
+ ADAPTER_INTERFACE, "FilteredDiscovery");
+ }

trigger_passive_scanning(adapter);

@@ -4699,6 +5017,11 @@ static void adapter_remove(struct btd_adapter *adapter)
adapter->discovery_idle_timeout = 0;
}

+ if (adapter->filtered_discovery_idle_timeout > 0) {
+ g_source_remove(adapter->filtered_discovery_idle_timeout);
+ adapter->filtered_discovery_idle_timeout = 0;
+ }
+
if (adapter->temp_devices_timeout > 0) {
g_source_remove(adapter->temp_devices_timeout);
adapter->temp_devices_timeout = 0;
@@ -4844,6 +5167,58 @@ static void adapter_msd_notify(struct btd_adapter *adapter,
}
}

+static gint g_strcmp(gconstpointer a, gconstpointer b)
+{
+ return strcmp(a, b);
+}
+
+static bool is_filter_match(GSList *discovery_filter, struct eir_data *eir_data,
+ int8_t rssi)
+{
+ GSList *l, *m;
+ bool got_match = false;
+
+ for (l = discovery_filter; l != NULL && got_match != true;
+ l = g_slist_next(l)) {
+ struct watch_client *client = l->data;
+ struct discovery_filter *item = client->discovery_filter;
+
+ if (item == NULL) {
+ /** we should be running regular scan */
+ error("empty filter - this should never happen!");
+ continue;
+ }
+
+ /* if someone started discovery with empty uuids, he wants all
+ * devices in given proximity.
+ */
+ if (item->uuids == NULL)
+ got_match = true;
+ else
+ for (m = item->uuids; m != NULL && got_match != true;
+ m = g_slist_next(m))
+ /* m->data is string representation of uuid. */
+ if (g_slist_find_custom(eir_data->services,
+ m->data, g_strcmp) != NULL)
+ got_match = true;
+
+ if (got_match) {
+ if (item->rssi != DISTNACE_VAL_INVALID &&
+ item->rssi > rssi)
+ return false;
+ if (item->pathloss != DISTNACE_VAL_INVALID &&
+ (eir_data->tx_power == 127 ||
+ eir_data->tx_power - rssi > item->pathloss))
+ return false;
+ }
+ }
+
+ if (!got_match)
+ return false;
+
+ return true;
+}
+
static void update_found_devices(struct btd_adapter *adapter,
const bdaddr_t *bdaddr,
uint8_t bdaddr_type, int8_t rssi,
@@ -4910,8 +5285,18 @@ static void update_found_devices(struct btd_adapter *adapter,
return;
}

+ if (adapter->filtered_discovery &&
+ !is_filter_match(adapter->discovery_list, &eir_data, rssi)) {
+ eir_data_free(&eir_data);
+ return;
+ }
+
device_set_legacy(dev, legacy);
- device_set_rssi(dev, rssi);
+
+ if (adapter->filtered_discovery)
+ device_set_rssi_no_delta(dev, rssi, false);
+ else
+ device_set_rssi(dev, rssi);

if (eir_data.appearance != 0)
device_set_appearance(dev, eir_data.appearance);
--
2.2.0.rc0.207.ga3a616c


2015-02-13 03:50:22

by Jakub Pawlowski

[permalink] [raw]
Subject: [PATCH BlueZ v2 5/6] core: adapter: Add FilteredDiscovery property

This patch adds FilteredDiscovery property, that will be used to
indicate that StartFilteredDiscovery method is running. If
StartDiscovery method is called (it takes precedence), this property
will be set to false.
---
src/adapter.c | 15 +++++++++++++++
1 file changed, 15 insertions(+)

diff --git a/src/adapter.c b/src/adapter.c
index fa4b7ce..cf4cf0a 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -186,6 +186,7 @@ struct btd_adapter {
char *stored_alias; /* stored adapter name alias */

bool discovering; /* discovering property state */
+ bool filtered_discovery; /* filtered discovery property state */
uint8_t discovery_type; /* current active discovery type */
uint8_t discovery_enable; /* discovery enabled/disabled */
bool discovery_suspended; /* discovery has been suspended */
@@ -2337,6 +2338,19 @@ static gboolean property_get_discovering(const GDBusPropertyTable *property,
return TRUE;
}

+static gboolean property_get_filtered_discovery(
+ const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ dbus_bool_t filtered_discovery = adapter->filtered_discovery;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_BOOLEAN,
+ &filtered_discovery);
+
+ return TRUE;
+}
+
static gboolean property_get_uuids(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *user_data)
{
@@ -2451,6 +2465,7 @@ static const GDBusPropertyTable adapter_properties[] = {
{ "PairableTimeout", "u", property_get_pairable_timeout,
property_set_pairable_timeout },
{ "Discovering", "b", property_get_discovering },
+ { "FilteredDiscovery", "b", property_get_filtered_discovery },
{ "UUIDs", "as", property_get_uuids },
{ "Modalias", "s", property_get_modalias, NULL,
property_exists_modalias },
--
2.2.0.rc0.207.ga3a616c


2015-02-13 03:50:21

by Jakub Pawlowski

[permalink] [raw]
Subject: [PATCH BlueZ v2 4/6] client: main: add support for StartFilteredDiscovery

This patch adds filtered-scan command to sample DBus client that might
be used to call StartFilteredDiscovery.
---
client/main.c | 223 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 223 insertions(+)

diff --git a/client/main.c b/client/main.c
index ea80ee7..067b8b1 100644
--- a/client/main.c
+++ b/client/main.c
@@ -527,6 +527,7 @@ static void cmd_show(const char *arg)
print_uuids(proxy);
print_property(proxy, "Modalias");
print_property(proxy, "Discovering");
+ print_property(proxy, "FilteredDiscovery");
}

static void cmd_select(const char *arg)
@@ -772,6 +773,226 @@ static void cmd_scan(const char *arg)
}
}

+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;
+
+ if (type == DBUS_TYPE_STRING) {
+ const char *str = *((const char **) val);
+
+ if (str == NULL)
+ return;
+ }
+
+ 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);
+}
+
+#define DISTNACE_VAL_INVALID 0x7FFF
+
+struct start_filtered_discovery_args {
+ char *discovery_type;
+ dbus_uint16_t RSSI;
+ dbus_int16_t pathloss;
+ GList *UUIDs;
+};
+
+static void start_filtered_discovery_setup(DBusMessageIter *iter,
+ void *user_data)
+{
+ struct start_filtered_discovery_args *args = user_data;
+ DBusMessageIter dict;
+ char *uuids = g_strdup("UUIDs");
+
+ dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
+
+ if (args->UUIDs != NULL) {
+ DBusMessageIter entry, value, arrayIter;
+ GList *list;
+
+ dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY,
+ NULL, &entry);
+ /* dict key */
+ dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING,
+ &uuids);
+
+ dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
+ "as", &value);
+
+ dbus_message_iter_open_container(&value, DBUS_TYPE_ARRAY, "s",
+ &arrayIter);
+
+ for (list = g_list_first(args->UUIDs); list;
+ list = g_list_next(list)) {
+ char *uuid = g_strdup(list->data);
+
+ dbus_message_iter_append_basic(&arrayIter,
+ DBUS_TYPE_STRING, &uuid);
+ }
+
+ dbus_message_iter_close_container(&value, &arrayIter);
+
+ /* close vararg*/
+ dbus_message_iter_close_container(&entry, &value);
+
+ /* close entry */
+ dbus_message_iter_close_container(&dict, &entry);
+ }
+
+ if (args->pathloss != DISTNACE_VAL_INVALID)
+ dict_append_entry(&dict, "pathloss", DBUS_TYPE_UINT16,
+ &args->pathloss);
+
+ if (args->RSSI != DISTNACE_VAL_INVALID)
+ dict_append_entry(&dict, "RSSI", DBUS_TYPE_INT16, &args->RSSI);
+
+ dbus_message_iter_close_container(iter, &dict);
+}
+
+
+static void start_filtered_discovery_reply(DBusMessage *message,
+ void *user_data)
+{
+ DBusError error;
+
+ dbus_error_init(&error);
+
+ if (dbus_set_error_from_message(&error, message) == TRUE) {
+ rl_printf("Failed to start filtered discovery: %s\n",
+ error.name);
+ dbus_error_free(&error);
+ return;
+ }
+}
+
+static gint filtered_scan_rssi, filtered_scan_pathloss;
+static char **filtered_scan_uuids;
+static gboolean filtered_scan_help;
+
+static GOptionEntry filtered_discovery_options[] = {
+ { "rssi", 'r', 0, G_OPTION_ARG_INT, &filtered_scan_rssi,
+ "RSSI filter" },
+ { "pathloss", 'p', 0, G_OPTION_ARG_INT, &filtered_scan_pathloss,
+ "pathloss filter" },
+ { "uuids", 'u', 0, G_OPTION_ARG_STRING_ARRAY, &filtered_scan_uuids,
+ "uuid to filter by" },
+ { "help", 'h', 0, G_OPTION_ARG_NONE, &filtered_scan_help,
+ "show help" },
+ { NULL },
+};
+
+static void cmd_filtered_scan(const char *arg)
+{
+ struct start_filtered_discovery_args args;
+ GOptionContext *context;
+ GError *error = NULL;
+ int argc, loop;
+ gchar **argv;
+ gchar **arguments = NULL;
+ gchar *cmdline_arg;
+
+ /* add fake prog name at beginning, g_shell_parse_argv expects that */
+ cmdline_arg = g_strconcat("filtered-scan ", arg, NULL);
+ if (g_shell_parse_argv(cmdline_arg, &argc, &arguments, &error)
+ == FALSE) {
+ if (error != NULL) {
+ g_printerr("error in g_shell_parse_argv: %s\n",
+ error->message);
+ g_error_free(error);
+ } else
+ g_printerr("An unknown error occurred\n");
+
+ g_strfreev(arguments);
+ g_free(cmdline_arg);
+ return;
+ }
+ g_free(cmdline_arg);
+
+ argc = g_strv_length(arguments);
+ argv = g_new(gchar *, argc);
+ for (loop = 0; loop < argc; loop++)
+ argv[loop] = arguments[loop];
+
+ context = g_option_context_new(NULL);
+ g_option_context_add_main_entries(context, filtered_discovery_options,
+ NULL);
+ /* set default values for all options */
+ filtered_scan_rssi = DISTNACE_VAL_INVALID;
+ filtered_scan_pathloss = DISTNACE_VAL_INVALID;
+ filtered_scan_uuids = NULL;
+ filtered_scan_help = FALSE;
+
+ g_option_context_set_help_enabled(context, FALSE);
+ if (g_option_context_parse(context, &argc, &argv, &error) == FALSE) {
+ if (error != NULL) {
+ g_printerr("error in g_option_context_parse: %s\n",
+ error->message);
+ g_error_free(error);
+ } else
+ g_printerr("An unknown error occurred\n");
+
+ g_strfreev(arguments);
+ g_free(argv);
+ return;
+ }
+
+ if (filtered_scan_help) {
+ rl_printf("Filtered scan. Usage:\n");
+ rl_printf(" filtered-scan [-r rssi | -p pathlos] -u <uuid1> [-u <uuid2> ...]\n");
+ rl_printf("\n");
+ rl_printf("Example: filtered-scan -p 65 -u baba -u 1900 -u cafa\n");
+ rl_printf("Use \"scan off\" to disable filtered scan\n");
+ return;
+ }
+
+ args.UUIDs = NULL;
+ args.pathloss = filtered_scan_pathloss;
+ args.RSSI = filtered_scan_rssi;
+
+ if (filtered_scan_uuids != NULL)
+ for (loop = 0; filtered_scan_uuids[loop] != NULL; loop++)
+ args.UUIDs = g_list_append(args.UUIDs,
+ strdup(filtered_scan_uuids[loop]));
+
+ g_strfreev(arguments);
+ g_free(argv);
+ g_strfreev(filtered_scan_uuids);
+
+ g_option_context_free(context);
+
+ if (check_default_ctrl() == FALSE)
+ return;
+
+ if (g_dbus_proxy_method_call(default_ctrl, "StartFilteredDiscovery",
+ start_filtered_discovery_setup, start_filtered_discovery_reply,
+ &args, NULL /* TODO: proper freeing method here*/) == FALSE) {
+ rl_printf("Failed to start filtered discovery\n");
+ return;
+ }
+}
+
static void cmd_info(const char *arg)
{
GDBusProxy *proxy;
@@ -1194,6 +1415,8 @@ static const struct {
capability_generator},
{ "default-agent",NULL, cmd_default_agent,
"Set agent as the default one" },
+ { "filtered-scan","", cmd_filtered_scan,
+ "Start filtered scan for devices. filtered-scan -h for help" },
{ "scan", "<on/off>", cmd_scan, "Scan for devices" },
{ "info", "<dev>", cmd_info, "Device information",
dev_generator },
--
2.2.0.rc0.207.ga3a616c


2015-02-13 03:50:20

by Jakub Pawlowski

[permalink] [raw]
Subject: [PATCH BlueZ v2 3/6] core: device: add device_set_rssi_no_delta

This patch adds new method that allow to update RSSI value without
taking delta into account. It will be used by StartFilteredDiscovery
method, in order to achieve more accurate proximity detection.
---
src/device.c | 10 ++++++++--
src/device.h | 2 ++
2 files changed, 10 insertions(+), 2 deletions(-)

diff --git a/src/device.c b/src/device.c
index a28d6fb..6c06c70 100644
--- a/src/device.c
+++ b/src/device.c
@@ -4417,7 +4417,8 @@ void device_set_legacy(struct btd_device *device, bool legacy)
DEVICE_INTERFACE, "LegacyPairing");
}

-void device_set_rssi(struct btd_device *device, int8_t rssi)
+void device_set_rssi_no_delta(struct btd_device *device, int8_t rssi,
+ bool use_delta)
{
if (!device)
return;
@@ -4438,7 +4439,7 @@ void device_set_rssi(struct btd_device *device, int8_t rssi)
delta = rssi - device->rssi;

/* only report changes of 8 dBm or more */
- if (delta < 8)
+ if (use_delta && delta < 8)
return;

DBG("rssi %d delta %d", rssi, delta);
@@ -4450,6 +4451,11 @@ void device_set_rssi(struct btd_device *device, int8_t rssi)
DEVICE_INTERFACE, "RSSI");
}

+void device_set_rssi(struct btd_device *device, int8_t rssi)
+{
+ device_set_rssi_no_delta(device, rssi, true);
+}
+
static gboolean start_discovery(gpointer user_data)
{
struct btd_device *device = user_data;
diff --git a/src/device.h b/src/device.h
index a7fefee..97e6f9e 100644
--- a/src/device.h
+++ b/src/device.h
@@ -89,6 +89,8 @@ void btd_device_set_temporary(struct btd_device *device, gboolean temporary);
void btd_device_set_trusted(struct btd_device *device, gboolean trusted);
void device_set_bonded(struct btd_device *device, uint8_t bdaddr_type);
void device_set_legacy(struct btd_device *device, bool legacy);
+void device_set_rssi_no_delta(struct btd_device *device, int8_t rssi,
+ bool use_delta);
void device_set_rssi(struct btd_device *device, int8_t rssi);
bool btd_device_is_connected(struct btd_device *dev);
uint8_t btd_device_get_bdaddr_type(struct btd_device *dev);
--
2.2.0.rc0.207.ga3a616c


2015-02-13 03:50:19

by Jakub Pawlowski

[permalink] [raw]
Subject: [PATCH BlueZ v2 2/6] core: adapter: Add parameter parsing to StartFilteredDiscovery

This patch adds parameter parsing, and basic internal logic checks to
StartFilteredDiscovery method.
---
src/adapter.c | 144 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 144 insertions(+)

diff --git a/src/adapter.c b/src/adapter.c
index df0c0b8..fa4b7ce 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -82,6 +82,8 @@
#define TEMP_DEV_TIMEOUT (3 * 60)
#define BONDING_TIMEOUT (2 * 60)

+#define DISTNACE_VAL_INVALID 0x7FFF
+
static DBusConnection *dbus_conn = NULL;

static bool kernel_conn_control = false;
@@ -1744,10 +1746,152 @@ static DBusMessage *start_discovery(DBusConnection *conn,
return dbus_message_new_method_return(msg);
}

+static bool parse_filtered_discovery_dict(char *key, DBusMessageIter *value,
+ GSList **uuids, int16_t *rssi,
+ uint16_t *pathloss, uint8_t *discov_type)
+{
+ uint8_t type;
+
+ if (strcmp("UUIDs", key) == 0) {
+ DBusMessageIter arriter;
+
+ if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_ARRAY)
+ return false;
+ dbus_message_iter_recurse(value, &arriter);
+ while ((type = dbus_message_iter_get_arg_type(&arriter)) !=
+ DBUS_TYPE_INVALID) {
+ char *uuid_str;
+ char *result_uuid;
+ uuid_t uuid_tmp;
+
+ if (dbus_message_iter_get_arg_type(&arriter) !=
+ DBUS_TYPE_STRING)
+ return false;
+
+ dbus_message_iter_get_basic(&arriter, &uuid_str);
+ if (bt_string2uuid(&uuid_tmp, uuid_str) < 0)
+ return false;
+
+ result_uuid = bt_uuid2string(&uuid_tmp);
+
+ *uuids = g_slist_prepend(*uuids, result_uuid);
+
+ dbus_message_iter_next(&arriter);
+ }
+ } else if (strcmp("RSSI", key) == 0) {
+ if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_INT16)
+ return false;
+ dbus_message_iter_get_basic(value, rssi);
+ if (*rssi > 0 || *rssi < -127)
+ return false;
+ } else if (strcmp("pathloss", key) == 0) {
+ if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_UINT16)
+ return false;
+ dbus_message_iter_get_basic(value, pathloss);
+ if (*pathloss > 127)
+ return false;
+ } else if (strcmp("type", key) == 0) {
+ char *type_str;
+
+ if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_STRING)
+ return false;
+ dbus_message_iter_get_basic(value, &type_str);
+
+ if (strcmp(type_str, "bredr") == 0)
+ *discov_type = 1;
+ else if (strcmp(type_str, "le") == 0)
+ *discov_type = 6;
+ else if (strcmp(type_str, "auto") == 0)
+ *discov_type = 7;
+ else
+ return false;
+ } else {
+ DBG("Unknown key parameter: %s!\n", key);
+ return false;
+ }
+
+ return true;
+}
+
static DBusMessage *start_filtered_discovery(DBusConnection *conn,
DBusMessage *msg, void *user_data)
{
+ struct btd_adapter *adapter = user_data;
+ const char *sender = dbus_message_get_sender(msg);
+ DBusMessageIter iter, subiter, dictiter, variantiter;
+ uint16_t pathloss = DISTNACE_VAL_INVALID;
+ int16_t rssi = DISTNACE_VAL_INVALID;
+ GSList *uuids = NULL, *list;
+ uint8_t discov_type = 6;
+
+ 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.
+ */
+ list = g_slist_find_custom(adapter->discovery_list, sender,
+ compare_sender);
+ if (list)
+ return btd_error_busy(msg);
+
+ /* Regular discovery takes precedence over filtered one */
+ if (adapter->discovering)
+ return btd_error_busy(msg);
+
+ /* parse parameters */
+ dbus_message_iter_init(msg, &iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_ARRAY ||
+ dbus_message_iter_get_element_type(&iter) != DBUS_TYPE_DICT_ENTRY)
+ return btd_error_invalid_args(msg);
+
+ dbus_message_iter_recurse(&iter, &subiter);
+ do {
+ int type = dbus_message_iter_get_arg_type(&subiter);
+
+ if (type == DBUS_TYPE_INVALID)
+ break;
+
+ if (type == DBUS_TYPE_DICT_ENTRY) {
+ char *key;
+
+ dbus_message_iter_recurse(&subiter, &dictiter);
+
+ dbus_message_iter_get_basic(&dictiter, &key);
+ if (!dbus_message_iter_next(&dictiter))
+ return btd_error_invalid_args(msg);
+
+ if (dbus_message_iter_get_arg_type(&dictiter) !=
+ DBUS_TYPE_VARIANT)
+ return btd_error_invalid_args(msg);
+
+ dbus_message_iter_recurse(&dictiter, &variantiter);
+
+ if (!parse_filtered_discovery_dict(key, &variantiter,
+ &uuids, &rssi, &pathloss,
+ &discov_type))
+ goto invalid_args;
+ }
+
+ dbus_message_iter_next(&subiter);
+ } while (true);
+
+ /* only pathlos or rssi can be set, never both*/
+ if (pathloss != DISTNACE_VAL_INVALID && rssi != DISTNACE_VAL_INVALID)
+ goto invalid_args;
+
+ DBG("filtered discovery params: type: %d rssi: %d pathloss: %d",
+ discov_type, rssi, pathloss);
+ g_slist_free_full(uuids, g_free);
return btd_error_failed(msg, "Not implemented yet");
+
+invalid_args:
+ g_slist_free_full(uuids, g_free);
+ return btd_error_invalid_args(msg);
}

static DBusMessage *stop_discovery(DBusConnection *conn,
--
2.2.0.rc0.207.ga3a616c