2015-03-16 19:51:30

by Jakub Pawlowski

[permalink] [raw]
Subject: [PATCH 1/7] 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 | 14 +++++++++++---
src/device.h | 2 ++
2 files changed, 13 insertions(+), 3 deletions(-)

diff --git a/src/device.c b/src/device.c
index 97c0523..295143a 100644
--- a/src/device.c
+++ b/src/device.c
@@ -83,6 +83,8 @@
#define MIN(a, b) ((a) < (b) ? (a) : (b))
#endif

+#define RSSI_THRESHOLD 8
+
static DBusConnection *dbus_conn = NULL;
static unsigned service_state_cb_id;

@@ -4534,7 +4536,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_with_delta(struct btd_device *device, int8_t rssi,
+ int8_t delta_threshold)
{
if (!device)
return;
@@ -4554,8 +4557,8 @@ void device_set_rssi(struct btd_device *device, int8_t rssi)
else
delta = rssi - device->rssi;

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

DBG("rssi %d delta %d", rssi, delta);
@@ -4567,6 +4570,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_with_delta(device, rssi, RSSI_THRESHOLD);
+}
+
static gboolean start_discovery(gpointer user_data)
{
struct btd_device *device = user_data;
diff --git a/src/device.h b/src/device.h
index 8edd0df..4945c1a 100644
--- a/src/device.h
+++ b/src/device.h
@@ -90,6 +90,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_with_delta(struct btd_device *device, int8_t rssi,
+ int8_t delta_threshold);
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-03-16 19:51:34

by Jakub Pawlowski

[permalink] [raw]
Subject: [PATCH 5/7] core: adapter: properly set the filter for discovery

This patch adds logic for properly setting, updating and deleting
scan filter.

Note that the filter is not used yet, making proper use from filter
will be added in following patches.
---
src/adapter.c | 112 +++++++++++++++++++++++++++++++++++++++++++++++++++++-----
1 file changed, 104 insertions(+), 8 deletions(-)

diff --git a/src/adapter.c b/src/adapter.c
index 5c6fd65..59e7c97 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -158,6 +158,7 @@ struct watch_client {
struct btd_adapter *adapter;
char *owner;
guint watch;
+ struct discovery_filter *discovery_filter;
};

struct service_auth {
@@ -207,6 +208,9 @@ struct btd_adapter {
uint8_t discovery_enable; /* discovery enabled/disabled */
bool discovery_suspended; /* discovery has been suspended */
GSList *discovery_list; /* list of discovery clients */
+ GSList *set_filter_list; /* list of clients that specified
+ * filter, but don't scan yet
+ */
GSList *discovery_found; /* list of found devices */
guint discovery_idle_timeout; /* timeout between discovery runs */
guint passive_scan_timeout; /* timeout between passive scans */
@@ -1355,6 +1359,17 @@ static uint8_t get_current_type(struct btd_adapter *adapter)
return type;
}

+static void free_discovery_filter(struct discovery_filter *discovery_filter)
+{
+ if (!discovery_filter)
+ return;
+
+ if (discovery_filter->uuids)
+ g_slist_free_full(discovery_filter->uuids, g_free);
+
+ g_free(discovery_filter);
+}
+
static void trigger_start_discovery(struct btd_adapter *adapter, guint delay);

static void start_discovery_complete(uint8_t status, uint16_t length,
@@ -1654,9 +1669,17 @@ static void discovery_destroy(void *user_data)

DBG("owner %s", client->owner);

+ adapter->set_filter_list = g_slist_remove(adapter->set_filter_list,
+ client);
+
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);

@@ -1693,6 +1716,9 @@ static void discovery_disconnect(DBusConnection *conn, void *user_data)

DBG("owner %s", client->owner);

+ adapter->set_filter_list = g_slist_remove(adapter->set_filter_list,
+ client);
+
adapter->discovery_list = g_slist_remove(adapter->discovery_list,
client);

@@ -1748,16 +1774,28 @@ static DBusMessage *start_discovery(DBusConnection *conn,
if (list)
return btd_error_busy(msg);

- client = g_new0(struct watch_client, 1);
+ /* If client called SetDiscoveryFilter before, use pre-set filter. */
+ list = g_slist_find_custom(adapter->set_filter_list, sender,
+ compare_sender);

- client->adapter = adapter;
- client->owner = g_strdup(sender);
- client->watch = g_dbus_add_disconnect_watch(dbus_conn, sender,
- discovery_disconnect, client,
- discovery_destroy);
+ if (list) {
+ client = list->data;
+ adapter->set_filter_list = g_slist_remove_link(
+ adapter->set_filter_list, list);
+ } else {
+ client = g_new0(struct watch_client, 1);
+
+ client->adapter = adapter;
+ client->owner = g_strdup(sender);
+ client->discovery_filter = NULL;
+ client->watch = g_dbus_add_disconnect_watch(dbus_conn, sender,
+ discovery_disconnect,
+ client,
+ discovery_destroy);
+ }

adapter->discovery_list = g_slist_prepend(adapter->discovery_list,
- client);
+ client);

/*
* Just trigger the discovery here. In case an already running
@@ -1922,8 +1960,9 @@ static DBusMessage *set_discovery_filter(DBusConnection *conn,
DBusMessage *msg, void *user_data)
{
struct btd_adapter *adapter = user_data;
+ struct watch_client *client;
struct discovery_filter *discovery_filter;
-
+ GSList *list;
const char *sender = dbus_message_get_sender(msg);

DBG("sender %s", sender);
@@ -1935,6 +1974,63 @@ static DBusMessage *set_discovery_filter(DBusConnection *conn,
if (!parse_discovery_filter_dict(&discovery_filter, msg))
return btd_error_invalid_args(msg);

+ list = g_slist_find_custom(adapter->discovery_list, sender,
+ compare_sender);
+
+ if (!list) {
+ /* Client is not running any form of discovery. */
+ GSList *filter_list;
+
+ /* Check wether client already pre-set his filter. */
+ filter_list = g_slist_find_custom(adapter->set_filter_list,
+ sender, compare_sender);
+
+ if (filter_list) {
+ client = filter_list->data;
+ free_discovery_filter(client->discovery_filter);
+
+ if (discovery_filter != NULL) {
+ /* just update existing pre-set filter */
+ client->discovery_filter = discovery_filter;
+ DBG("successfully modified pre-set filter");
+ return dbus_message_new_method_return(msg);
+ }
+
+ /* Removing pre-set filter */
+ adapter->set_filter_list = g_slist_remove(
+ adapter->set_filter_list,
+ client);
+ g_free(client->owner);
+ g_free(client);
+ DBG("successfully cleared pre-set filter");
+ return dbus_message_new_method_return(msg);
+ }
+ /* Client haven't pre-set his filter yet. */
+
+ /* If there's no filter, no need to set. */
+ if (discovery_filter == NULL)
+ return dbus_message_new_method_return(msg);
+
+ /* Client pre-setting his filter for first time */
+ 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->set_filter_list = g_slist_prepend(
+ adapter->set_filter_list, client);
+
+ DBG("successfully pre-set filter");
+ return dbus_message_new_method_return(msg);
+ }
+
+ /* Client have already started discovery. */
+ client = list->data;
+ free_discovery_filter(client->discovery_filter);
+ client->discovery_filter = discovery_filter;
+
return btd_error_failed(msg, "Not implemented yet");
}

--
2.2.0.rc0.207.ga3a616c


2015-03-16 19:51:35

by Jakub Pawlowski

[permalink] [raw]
Subject: [PATCH 6/7] core: adapter: start proper discovery depending on filter type

This patch implement starting proper discovery, depending on kind
of filters used. It also removes iddle timeout for filtered scans,
to make sure that this kind of scan will run continuously.

Code starting (or restarting) scan is added at end of
SetDiscoveryFilter, to ensure updated results right away.
---
src/adapter.c | 227 +++++++++++++++++++++++++++++++++++++++++++++++++++++++---
1 file changed, 219 insertions(+), 8 deletions(-)

diff --git a/src/adapter.c b/src/adapter.c
index 59e7c97..15c99ea 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -92,6 +92,7 @@
#define SCAN_TYPE_LE ((1 << BDADDR_LE_PUBLIC) | (1 << BDADDR_LE_RANDOM))
#define SCAN_TYPE_DUAL (SCAN_TYPE_BREDR | SCAN_TYPE_LE)

+#define HCI_RSSI_INVALID 127
#define DISTNACE_VAL_INVALID 0x7FFF

static DBusConnection *dbus_conn = NULL;
@@ -204,6 +205,9 @@ struct btd_adapter {
char *stored_alias; /* stored adapter name alias */

bool discovering; /* discovering property state */
+ bool filtered_discovery; /* we are doing filtered discovery */
+ bool no_scan_restart_delay; /* when this flag is set, restart scan
+ * without delay */
uint8_t discovery_type; /* current active discovery type */
uint8_t discovery_enable; /* discovery enabled/disabled */
bool discovery_suspended; /* discovery has been suspended */
@@ -1359,6 +1363,120 @@ static uint8_t get_current_type(struct btd_adapter *adapter)
return type;
}

+/* This method merges all adapter filters into one that will be send to kernel.
+ * cp_ptr is set to null when regular non-filtered discovery is needed,
+ * otherwise it's pointing to filter. Returns 0 on succes, -1 on error
+ */
+static int discovery_filter_to_mgmt_cp(struct btd_adapter *adapter,
+ struct mgmt_cp_start_service_discovery **cp_ptr)
+{
+ 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];
+ /* 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.
+ */
+ bool empty_uuid = false;
+ bool has_regular_discovery = false;
+ bool has_filtered_discovery = false;
+
+ DBG("");
+ 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) {
+ has_regular_discovery = true;
+ continue;
+ }
+
+ has_filtered_discovery = true;
+
+ discovery_type |= item->type;
+
+ if (item->rssi == DISTNACE_VAL_INVALID)
+ rssi = HCI_RSSI_INVALID;
+ else if (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 any of filters had empty uuid, do not filter by uuid */
+ if (empty_uuid == true)
+ uuid_count = 0;
+
+ if (has_regular_discovery) {
+ /* It there is both regular and filtered scan running, then
+ * clear whole fitler to report all devices. If tere are only
+ * regular scans, run just regular scan.
+ */
+ if (has_filtered_discovery) {
+ discovery_type = get_current_type(adapter);
+ uuid_count = 0;
+ rssi = HCI_RSSI_INVALID;
+ } else {
+ *cp_ptr = NULL;
+ return 0;
+ }
+ }
+
+ cp = g_try_malloc(sizeof(cp) + 16*uuid_count);
+ *cp_ptr = cp;
+ if (cp == NULL)
+ return -1;
+
+ cp->type = discovery_type;
+ cp->rssi = rssi;
+ cp->uuid_count = uuid_count;
+
+ /* if filter requires no uuid filter, stop processing here. */
+ if (uuid_count == 0)
+ return 0;
+
+ 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 0;
+}
+
static void free_discovery_filter(struct discovery_filter *discovery_filter)
{
if (!discovery_filter)
@@ -1388,6 +1506,42 @@ static void start_discovery_complete(uint8_t status, uint16_t length,
if (status == MGMT_STATUS_SUCCESS) {
adapter->discovery_type = rp->type;
adapter->discovery_enable = 0x01;
+ adapter->filtered_discovery = false;
+
+ if (adapter->discovering)
+ return;
+
+ adapter->discovering = true;
+ g_dbus_emit_property_changed(dbus_conn, adapter->path,
+ ADAPTER_INTERFACE, "Discovering");
+ return;
+ }
+
+ /*
+ * In case the restart of the discovery failed, then just trigger
+ * it for the next idle timeout again.
+ */
+ trigger_start_discovery(adapter, IDLE_DISCOV_TIMEOUT * 2);
+}
+
+
+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 uint8_t *type = param;
+
+ DBG("status 0x%02x", status);
+
+ if (length < 1) {
+ error("Wrong size of return parameters");
+ return;
+ }
+
+ if (status == MGMT_STATUS_SUCCESS) {
+ adapter->discovery_type = *type;
+ adapter->discovery_enable = 0x01;
+ adapter->filtered_discovery = true;

if (adapter->discovering)
return;
@@ -1409,20 +1563,72 @@ static gboolean start_discovery_timeout(gpointer user_data)
{
struct btd_adapter *adapter = user_data;
struct mgmt_cp_start_discovery cp;
+ struct mgmt_cp_start_service_discovery *sd_cp;
uint8_t new_type;

DBG("");

adapter->discovery_idle_timeout = 0;

+ if (discovery_filter_to_mgmt_cp(adapter, &sd_cp)) {
+ error("discovery_filter_to_mgmt_cp returned error");
+ return TRUE;
+ }
+
+ /* Service Discovery filter was returned, that means that filtered
+ * discovery is required.
+ */
+ if (sd_cp != NULL) {
+ DBG("sd_cp != NULL");
+ /* if there's any scan running, stop it. */
+ if (adapter->discovery_enable == 0x01) {
+ struct mgmt_cp_stop_discovery cp;
+
+ DBG("discovery must be restarted");
+
+ /* Make sure that filtered discovery will be quickly
+ * restarted.
+ */
+ adapter->no_scan_restart_delay = true;
+
+ cp.type = adapter->discovery_type;
+ mgmt_send(adapter->mgmt, MGMT_OP_STOP_DISCOVERY,
+ adapter->dev_id, sizeof(cp), &cp,
+ NULL, NULL, NULL);
+ /* Don't even bother to try to quickly start discovery
+ * just after stopping it, it would fail with status
+ * MGMT_BUSY. Instead discovering_callback will take
+ * care of that.
+ */
+ g_free(sd_cp);
+ return FALSE;
+ }
+
+ DBG("sending MGMT_OP_START_SERVICE_DISCOVERY %d, %d, %d",
+ sd_cp->rssi, sd_cp->type, sd_cp->uuid_count);
+
+ mgmt_send(adapter->mgmt, MGMT_OP_START_SERVICE_DISCOVERY,
+ adapter->dev_id,
+ sizeof(*sd_cp) + sd_cp->uuid_count * 16, sd_cp,
+ start_filtered_discovery_complete, adapter, NULL);
+
+ g_free(sd_cp);
+ return FALSE;
+ }
+
+ DBG("sd_cp == NULL");
+ /* We're doing regular discovery. */
new_type = get_current_type(adapter);

+ adapter->no_scan_restart_delay = false;
+
if (adapter->discovery_enable == 0x01) {
/*
* If there is an already running discovery and it has the
* same type, then just keep it.
*/
- if (adapter->discovery_type == new_type) {
+ if (adapter->discovery_type == new_type &&
+ adapter->filtered_discovery == false) {
if (adapter->discovering)
return FALSE;

@@ -1440,14 +1646,12 @@ static gboolean start_discovery_timeout(gpointer user_data)
* devices is ongoing.
*/
cp.type = adapter->discovery_type;
-
mgmt_send(adapter->mgmt, MGMT_OP_STOP_DISCOVERY,
adapter->dev_id, sizeof(cp), &cp,
NULL, NULL, NULL);
}

cp.type = new_type;
-
mgmt_send(adapter->mgmt, MGMT_OP_START_DISCOVERY,
adapter->dev_id, sizeof(cp), &cp,
start_discovery_complete, adapter, NULL);
@@ -1562,8 +1766,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;
@@ -1589,7 +1793,10 @@ 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->no_scan_restart_delay)
+ trigger_start_discovery(adapter, 0);
+ else
+ trigger_start_discovery(adapter, IDLE_DISCOV_TIMEOUT);
break;

case 0x01:
@@ -1597,6 +1804,7 @@ static void discovering_callback(uint16_t index, uint16_t length,
g_source_remove(adapter->discovery_idle_timeout);
adapter->discovery_idle_timeout = 0;
}
+
break;
}
}
@@ -1611,7 +1819,8 @@ static void stop_discovery_complete(uint8_t status, uint16_t length,
if (status == MGMT_STATUS_SUCCESS) {
adapter->discovery_type = 0x00;
adapter->discovery_enable = 0x00;
-
+ adapter->filtered_discovery = false;
+ adapter->no_scan_restart_delay = false;
adapter->discovering = false;
g_dbus_emit_property_changed(dbus_conn, adapter->path,
ADAPTER_INTERFACE, "Discovering");
@@ -2031,7 +2240,9 @@ static DBusMessage *set_discovery_filter(DBusConnection *conn,
free_discovery_filter(client->discovery_filter);
client->discovery_filter = discovery_filter;

- return btd_error_failed(msg, "Not implemented yet");
+ trigger_start_discovery(adapter, 0);
+
+ return dbus_message_new_method_return(msg);
}

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


2015-03-16 19:51:36

by Jakub Pawlowski

[permalink] [raw]
Subject: [PATCH 7/7] core: adapter: add device filtering when filered discovery is used

This patch adds filtering to device found event, and enforces lack
of RSSI delta for filtered scan.

When filtering, we compare against each client filter. That
additionally removes some of reports.
---
src/adapter.c | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 72 insertions(+), 1 deletion(-)

diff --git a/src/adapter.c b/src/adapter.c
index 15c99ea..f0d11b9 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -5234,6 +5234,67 @@ 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 one of currently running scans is regular scan, then
+ * return all devices as matches
+ */
+ if (item == NULL) {
+ got_match = true;
+ 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 contains string representation of
+ * uuid.
+ */
+ if (g_slist_find_custom(eir_data->services,
+ m->data,
+ g_strcmp) != NULL)
+ got_match = true;
+
+ if (got_match) {
+ /* we have service match, check proximity */
+ if (item->rssi != DISTNACE_VAL_INVALID &&
+ item->rssi > rssi)
+ got_match = false;
+ if (item->pathloss != DISTNACE_VAL_INVALID &&
+ (eir_data->tx_power == 127 ||
+ eir_data->tx_power - rssi > item->pathloss))
+ got_match = false;
+
+ if (got_match)
+ return true;
+ }
+ }
+
+ 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,
@@ -5300,8 +5361,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_with_delta(dev, rssi, 0);
+ 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-03-16 19:51:33

by Jakub Pawlowski

[permalink] [raw]
Subject: [PATCH 4/7] core: adapter: Add parameter parsing to SetDiscoveryFilter

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

diff --git a/src/adapter.c b/src/adapter.c
index 8fe922c..5c6fd65 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -92,6 +92,8 @@
#define SCAN_TYPE_LE ((1 << BDADDR_LE_PUBLIC) | (1 << BDADDR_LE_RANDOM))
#define SCAN_TYPE_DUAL (SCAN_TYPE_BREDR | SCAN_TYPE_LE)

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

static bool kernel_conn_control = false;
@@ -145,6 +147,13 @@ 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;
@@ -1760,9 +1769,172 @@ static DBusMessage *start_discovery(DBusConnection *conn,
return dbus_message_new_method_return(msg);
}

+static bool parse_discovery_filter_entry(char *key, DBusMessageIter *value,
+ GSList **uuids, int16_t *rssi,
+ uint16_t *pathloss, uint8_t *transport)
+{
+ 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("Transport", key) == 0) {
+ char *transport_str;
+
+ if (dbus_message_iter_get_arg_type(value) != DBUS_TYPE_STRING)
+ return false;
+ dbus_message_iter_get_basic(value, &transport_str);
+
+ if (strcmp(transport_str, "bredr") == 0)
+ *transport = SCAN_TYPE_BREDR;
+ else if (strcmp(transport_str, "le") == 0)
+ *transport = SCAN_TYPE_LE;
+ else if (strcmp(transport_str, "auto") == 0)
+ *transport = SCAN_TYPE_DUAL;
+ else
+ return false;
+ } else {
+ DBG("Unknown key parameter: %s!\n", key);
+ return false;
+ }
+
+ return true;
+}
+
+/* This method is responsible for parsing parameters to SetDiscoveryFilter. If
+ * filter in msg was empty, sets *filter to NULL. If whole parsing was
+ * successful, sets *filter to proper value.
+ * Returns false on any error, and true on success.
+ */
+static bool parse_discovery_filter_dict(struct discovery_filter **filter,
+ DBusMessage *msg)
+{
+ DBusMessageIter iter, subiter, dictiter, variantiter;
+ GSList *uuids = NULL;
+ uint16_t pathloss = DISTNACE_VAL_INVALID;
+ int16_t rssi = DISTNACE_VAL_INVALID;
+ uint8_t transport = SCAN_TYPE_DUAL;
+ uint8_t is_empty = true;
+
+ 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 false;
+
+ 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;
+
+ is_empty = false;
+ dbus_message_iter_recurse(&subiter, &dictiter);
+
+ dbus_message_iter_get_basic(&dictiter, &key);
+ if (!dbus_message_iter_next(&dictiter))
+ goto invalid_args;
+
+ if (dbus_message_iter_get_arg_type(&dictiter) !=
+ DBUS_TYPE_VARIANT)
+ goto invalid_args;
+
+ dbus_message_iter_recurse(&dictiter, &variantiter);
+
+ if (!parse_discovery_filter_entry(key, &variantiter,
+ &uuids, &rssi, &pathloss,
+ &transport))
+ goto invalid_args;
+ }
+
+ dbus_message_iter_next(&subiter);
+ } while (true);
+
+ if (is_empty) {
+ *filter = NULL;
+ return 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: transport: %d rssi: %d pathloss: %d",
+ transport, rssi, pathloss);
+
+ *filter = g_try_malloc(sizeof(*filter));
+ if (*filter == NULL) {
+ g_slist_free_full(uuids, g_free);
+ return false;
+ }
+
+ (*filter)->type = transport;
+ (*filter)->pathloss = pathloss;
+ (*filter)->rssi = rssi;
+ (*filter)->uuids = uuids;
+
+ return true;
+
+invalid_args:
+ g_slist_free_full(uuids, g_free);
+ return false;
+}
+
static DBusMessage *set_discovery_filter(DBusConnection *conn,
DBusMessage *msg, void *user_data)
{
+ struct btd_adapter *adapter = user_data;
+ struct discovery_filter *discovery_filter;
+
+ const char *sender = dbus_message_get_sender(msg);
+
+ DBG("sender %s", sender);
+
+ if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+ return btd_error_not_ready(msg);
+
+ /* parse parameters */
+ if (!parse_discovery_filter_dict(&discovery_filter, msg))
+ return btd_error_invalid_args(msg);
+
return btd_error_failed(msg, "Not implemented yet");
}

--
2.2.0.rc0.207.ga3a616c


2015-03-16 19:51:31

by Jakub Pawlowski

[permalink] [raw]
Subject: [PATCH 2/7] core/adapter: Refactor of scan type

This patch replaces scan type with defined constants, and creates
new method that might be used to get currently avaliable scan type.
---
src/adapter.c | 29 +++++++++++++++++++++--------
1 file changed, 21 insertions(+), 8 deletions(-)

diff --git a/src/adapter.c b/src/adapter.c
index 3744c2b..66a6234 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -88,6 +88,10 @@
#define TEMP_DEV_TIMEOUT (3 * 60)
#define BONDING_TIMEOUT (2 * 60)

+#define SCAN_TYPE_BREDR (1 << BDADDR_BREDR)
+#define SCAN_TYPE_LE ((1 << BDADDR_LE_PUBLIC) | (1 << BDADDR_LE_RANDOM))
+#define SCAN_TYPE_DUAL (SCAN_TYPE_BREDR | SCAN_TYPE_LE)
+
static DBusConnection *dbus_conn = NULL;

static bool kernel_conn_control = false;
@@ -1185,7 +1189,7 @@ static gboolean passive_scanning_timeout(gpointer user_data)

adapter->passive_scan_timeout = 0;

- cp.type = (1 << BDADDR_LE_PUBLIC) | (1 << BDADDR_LE_RANDOM);
+ cp.type = SCAN_TYPE_LE;

mgmt_send(adapter->mgmt, MGMT_OP_START_DISCOVERY,
adapter->dev_id, sizeof(cp), &cp,
@@ -1327,6 +1331,21 @@ static void cancel_passive_scanning(struct btd_adapter *adapter)
}
}

+static uint8_t get_current_type(struct btd_adapter *adapter)
+{
+ uint8_t type;
+
+ if (adapter->current_settings & MGMT_SETTING_BREDR)
+ type = SCAN_TYPE_BREDR;
+ else
+ type = 0;
+
+ if (adapter->current_settings & MGMT_SETTING_LE)
+ type |= SCAN_TYPE_LE;
+
+ return type;
+}
+
static void trigger_start_discovery(struct btd_adapter *adapter, guint delay);

static void start_discovery_complete(uint8_t status, uint16_t length,
@@ -1372,13 +1391,7 @@ static gboolean start_discovery_timeout(gpointer user_data)

adapter->discovery_idle_timeout = 0;

- if (adapter->current_settings & MGMT_SETTING_BREDR)
- new_type = (1 << BDADDR_BREDR);
- else
- new_type = 0;
-
- if (adapter->current_settings & MGMT_SETTING_LE)
- new_type |= (1 << BDADDR_LE_PUBLIC) | (1 << BDADDR_LE_RANDOM);
+ new_type = get_current_type(adapter);

if (adapter->discovery_enable == 0x01) {
/*
--
2.2.0.rc0.207.ga3a616c


2015-03-16 19:51:32

by Jakub Pawlowski

[permalink] [raw]
Subject: [PATCH 3/7] core: adapter: Add SetDiscoveryFilter method

This patch adds SetDiscoveryFilter 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 66a6234..8fe922c 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -1760,6 +1760,12 @@ static DBusMessage *start_discovery(DBusConnection *conn,
return dbus_message_new_method_return(msg);
}

+static DBusMessage *set_discovery_filter(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)
{
@@ -2332,6 +2338,9 @@ static DBusMessage *remove_device(DBusConnection *conn,

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