2014-05-19 23:44:32

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [RFC 00/11] Connection parameters D-Bus API

Hi,

Following patches add possibility for applications to have RSSI and TX
power data for connected device. This uses recently introduced mgmt
command Get Connection Information.

There are 3 new properties for RSSI, current TX power and max TX power.
Polling for RSSI and TX power can be enabled via Start-/Stop- methods
in a manner similar to discovery on adapter. Max TX power is read once
when device is connected and obviously does not change.

As for now polling interval is set to arbitrary chosen value of 2secs,
but perhapts this could be configurable via main.conf entry.


Andrzej Kaczmarek (9):
core: Add ConnectionRSSI property
core: Add ConnectionTXPower property
core: Add ConnectionTXPowerMax property
core: Make connection properties exist only when connected
core: Read max TX power when device connected
shared: Fix queue_find to accept const ptr as match data
core: Add API to start/stop connection monitoring
core: Store list of monitored devices in adapter
core: Poll for connection info

Lukasz Rymanowski (2):
doc: Update RSSI property description.
doc: Introduce connection monitoring API

doc/device-api.txt | 56 +++++++++++
src/adapter.c | 205 +++++++++++++++++++++++++++++++++++++++++
src/adapter.h | 7 ++
src/device.c | 265 +++++++++++++++++++++++++++++++++++++++++++++++++++++
src/device.h | 3 +
src/shared/queue.c | 4 +-
src/shared/queue.h | 2 +-
7 files changed, 539 insertions(+), 3 deletions(-)

--
1.9.3



2014-05-23 11:46:23

by Johan Hedberg

[permalink] [raw]
Subject: Re: [RFC 01/11] doc: Update RSSI property description.

Hi Andrzej,

On Tue, May 20, 2014, Andrzej Kaczmarek wrote:
> RSSI property applies to either inquiry or advertising packets and this
> should be stated here especially that we plan to add connection RSSI
> property.
> ---
> doc/device-api.txt | 2 +-
> 1 file changed, 1 insertion(+), 1 deletion(-)
>
> diff --git a/doc/device-api.txt b/doc/device-api.txt
> index 2d9fa3c..577ee60 100644
> --- a/doc/device-api.txt
> +++ b/doc/device-api.txt
> @@ -192,4 +192,4 @@ Properties string Address [readonly]
> int16 RSSI [readonly, optional]
>
> Received Signal Strength Indicator of the remote
> - device.
> + device (inquiry or advertising).

I've applied this first patch from the set since it's trivial, but e.g.
for the second one I'd at least want an ack from Marcel before pushing.

Johan

2014-05-23 11:40:07

by Johan Hedberg

[permalink] [raw]
Subject: Re: [RFC 06/11] core: Make connection properties exist only when connected

Hi Andrzej,

On Tue, May 20, 2014, Andrzej Kaczmarek wrote:
> +static gboolean dev_property_exists_conn(const GDBusPropertyTable *property,
> + void *data)
> +{
> + struct btd_device *device = data;
> +
> + /* TODO: only exists when monitoring is enabled? */
> + return btd_device_is_connected(device) ? TRUE : FALSE;

To answer this open, yes, I think the properties should only be
available when there is someone that has requested monitoring of them.
If you didn't fix this later in the set please do it for the first
non-RFC set that you send. I haven't seen anything else too
objectionable in this set.

Johan

2014-05-23 07:47:33

by Andrzej Kaczmarek

[permalink] [raw]
Subject: Re: [RFC 00/11] Connection parameters D-Bus API

ping...


On 20 May 2014 01:44, Andrzej Kaczmarek <[email protected]> wrote:
> Hi,
>
> Following patches add possibility for applications to have RSSI and TX
> power data for connected device. This uses recently introduced mgmt
> command Get Connection Information.
>
> There are 3 new properties for RSSI, current TX power and max TX power.
> Polling for RSSI and TX power can be enabled via Start-/Stop- methods
> in a manner similar to discovery on adapter. Max TX power is read once
> when device is connected and obviously does not change.
>
> As for now polling interval is set to arbitrary chosen value of 2secs,
> but perhapts this could be configurable via main.conf entry.
>
>
> Andrzej Kaczmarek (9):
> core: Add ConnectionRSSI property
> core: Add ConnectionTXPower property
> core: Add ConnectionTXPowerMax property
> core: Make connection properties exist only when connected
> core: Read max TX power when device connected
> shared: Fix queue_find to accept const ptr as match data
> core: Add API to start/stop connection monitoring
> core: Store list of monitored devices in adapter
> core: Poll for connection info
>
> Lukasz Rymanowski (2):
> doc: Update RSSI property description.
> doc: Introduce connection monitoring API
>
> doc/device-api.txt | 56 +++++++++++
> src/adapter.c | 205 +++++++++++++++++++++++++++++++++++++++++
> src/adapter.h | 7 ++
> src/device.c | 265 +++++++++++++++++++++++++++++++++++++++++++++++++++++
> src/device.h | 3 +
> src/shared/queue.c | 4 +-
> src/shared/queue.h | 2 +-
> 7 files changed, 539 insertions(+), 3 deletions(-)
>
> --
> 1.9.3
>

2014-05-19 23:44:36

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [RFC 04/11] core: Add ConnectionTXPower property

This patch adds ConnectionTXPower property on org.bluez.Device1
interface which provides local TX power on currently connected link.

Property value will be refreshed once connection monitoring code is in
place.
---
src/device.c | 32 ++++++++++++++++++++++++++++++++
src/device.h | 1 +
2 files changed, 33 insertions(+)

diff --git a/src/device.c b/src/device.c
index fd4e120..ac42a75 100644
--- a/src/device.c
+++ b/src/device.c
@@ -220,6 +220,7 @@ struct btd_device {
int8_t rssi;

int8_t conn_rssi;
+ int8_t conn_tx_power;

GIOChannel *att_io;
guint cleanup_id;
@@ -838,6 +839,18 @@ static gboolean dev_property_get_conn_rssi(const GDBusPropertyTable *property,
return TRUE;
}

+static gboolean dev_property_get_conn_txpower(
+ const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct btd_device *device = data;
+ dbus_int16_t val = device->conn_tx_power;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_INT16, &val);
+
+ return TRUE;
+}
+
static void set_trust(GDBusPendingPropertySet id, gboolean value, void *data)
{
struct btd_device *device = data;
@@ -1932,6 +1945,7 @@ static const GDBusPropertyTable device_properties[] = {
dev_property_exists_modalias },
{ "Adapter", "o", dev_property_get_adapter },
{ "ConnectionRSSI", "n", dev_property_get_conn_rssi },
+ { "ConnectionTXPower", "n", dev_property_get_conn_txpower },
{ }
};

@@ -2327,6 +2341,8 @@ static struct btd_device *device_new(struct btd_adapter *adapter,
str2ba(address, &device->bdaddr);
device->adapter = adapter;

+ device->conn_tx_power = 127; /* invalid */
+
return btd_device_ref(device);
}

@@ -4073,6 +4089,22 @@ void device_set_conn_rssi(struct btd_device *device, int8_t rssi)
"ConnectionRSSI");
}

+void device_set_conn_tx_power(struct btd_device *device, int8_t tx_power)
+{
+ if (!device)
+ return;
+
+ if (device->conn_tx_power == tx_power)
+ return;
+
+ DBG("conn_tx_power %d", tx_power);
+
+ device->conn_tx_power = tx_power;
+
+ g_dbus_emit_property_changed(dbus_conn, device->path, DEVICE_INTERFACE,
+ "ConnectionTXPower");
+}
+
static void device_set_auto_connect(struct btd_device *device, gboolean enable)
{
char addr[18];
diff --git a/src/device.h b/src/device.h
index 79d5132..e0e44cc 100644
--- a/src/device.h
+++ b/src/device.h
@@ -89,6 +89,7 @@ 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(struct btd_device *device, int8_t rssi);
void device_set_conn_rssi(struct btd_device *device, int8_t rssi);
+void device_set_conn_tx_power(struct btd_device *device, int8_t tx_power);
bool btd_device_is_connected(struct btd_device *dev);
uint8_t btd_device_get_bdaddr_type(struct btd_device *dev);
bool device_is_retrying(struct btd_device *device);
--
1.9.3


2014-05-19 23:44:42

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [RFC 10/11] core: Store list of monitored devices in adapter

This patch adds list of devices which should be polled for connection
information to adapter.
---
src/adapter.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 48 insertions(+)

diff --git a/src/adapter.c b/src/adapter.c
index a3204d0..045ce30 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -53,6 +53,7 @@
#include "lib/mgmt.h"
#include "src/shared/mgmt.h"
#include "src/shared/util.h"
+#include "src/shared/queue.h"

#include "hcid.h"
#include "sdpd.h"
@@ -178,6 +179,7 @@ struct btd_adapter {
bool discovery_suspended; /* discovery has been suspended */
GSList *discovery_list; /* list of discovery clients */
GSList *discovery_found; /* list of found devices */
+ struct queue *monitored_devices;
guint discovery_idle_timeout; /* timeout between discovery runs */
guint passive_scan_timeout; /* timeout between passive scans */
guint temp_devices_timeout; /* timeout for temporary devices */
@@ -4166,6 +4168,8 @@ static struct btd_adapter *btd_adapter_new(uint16_t index)

adapter->auths = g_queue_new();

+ adapter->monitored_devices = queue_new();
+
return btd_adapter_ref(adapter);
}

@@ -4190,6 +4194,9 @@ static void adapter_remove(struct btd_adapter *adapter)
g_slist_free(adapter->connect_list);
adapter->connect_list = NULL;

+ queue_destroy(adapter->monitored_devices, free);
+ adapter->monitored_devices = NULL;
+
for (l = adapter->devices; l; l = l->next)
device_remove(l->data, FALSE);

@@ -5460,15 +5467,41 @@ static void disconnect_complete(uint8_t status, uint16_t length,
dev_disconnected(adapter, &rp->addr, MGMT_DEV_DISCONN_LOCAL_HOST);
}

+struct monitored_device {
+ struct mgmt_addr_info addr;
+};
+
+static bool compare_monitored_device(const void *a, const void *b)
+{
+ const struct monitored_device *a1 = a;
+ const struct monitored_device *b1 = b;
+
+ return !memcmp(&a1->addr.bdaddr, &b1->addr.bdaddr, sizeof(bdaddr_t)) &&
+ a1->addr.type == b1->addr.type;
+}
+
int btd_adapter_add_monitored_device(struct btd_adapter *adapter,
const bdaddr_t *bdaddr,
uint8_t bdaddr_type)
{
+ struct monitored_device *mdev;
char addrstr[18];

ba2str(bdaddr, addrstr);
DBG("addr %s type %d", addrstr, bdaddr_type);

+ mdev = new0(struct monitored_device, 1);
+ bacpy(&mdev->addr.bdaddr, bdaddr);
+ mdev->addr.type = bdaddr_type;
+
+ if (queue_find(adapter->monitored_devices, compare_monitored_device,
+ mdev)) {
+ free(mdev);
+ return 0;
+ }
+
+ queue_push_tail(adapter->monitored_devices, mdev);
+
return 0;
}

@@ -5476,11 +5509,26 @@ int btd_adapter_remove_monitored_device(struct btd_adapter *adapter,
const bdaddr_t *bdaddr,
uint8_t bdaddr_type)
{
+ struct monitored_device match;
+ struct monitored_device *mdev;
char addrstr[18];

ba2str(bdaddr, addrstr);
DBG("addr %s type %d", addrstr, bdaddr_type);

+ memset(&match, 0, sizeof(match));
+ bacpy(&match.addr.bdaddr, bdaddr);
+ match.addr.type = bdaddr_type;
+
+ mdev = queue_find(adapter->monitored_devices, compare_monitored_device,
+ &match);
+ if (!mdev)
+ return 0;
+
+ queue_remove(adapter->monitored_devices, mdev);
+
+ free(mdev);
+
return 0;
}

--
1.9.3


2014-05-19 23:44:39

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [RFC 07/11] core: Read max TX power when device connected

This patch adds proper support for max TX power, i.e. this property
is refreshed once device is connected.
---
src/adapter.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 50 insertions(+)

diff --git a/src/adapter.c b/src/adapter.c
index f5f8c8c..585f2e2 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -6174,6 +6174,54 @@ static void disconnected_callback(uint16_t index, uint16_t length,
dev_disconnected(adapter, &ev->addr, reason);
}

+static void read_max_tx_power_destroy(void *data)
+{
+ struct btd_device *device = data;
+
+ btd_device_unref(device);
+}
+
+static void read_max_tx_power_rsp(uint8_t status, uint16_t len,
+ const void *param, void *user_data)
+{
+ const struct mgmt_rp_get_conn_info *rp = param;
+ struct btd_device *device = user_data;
+
+ if (status != MGMT_STATUS_SUCCESS) {
+ error("Get Connection Information failed: %s (0x%02x)",
+ mgmt_errstr(status), status);
+ return;
+ }
+
+ if (len < sizeof(*rp)) {
+ error("Too small Get Connection Information response");
+ return;
+ }
+
+ device_set_conn_max_tx_power(device, rp->max_tx_power);
+}
+
+static void read_max_tx_power(struct btd_adapter *adapter,
+ struct btd_device *device,
+ const bdaddr_t *addr, uint8_t addr_type)
+{
+ struct mgmt_cp_get_conn_info cp;
+ char addrstr[18];
+
+ ba2str(addr, addrstr);
+ DBG("bdaddr %s type %d", addrstr, addr_type);
+
+ btd_device_ref(device);
+
+ memset(&cp, 0, sizeof(cp));
+ bacpy(&cp.addr.bdaddr, addr);
+ cp.addr.type = addr_type;
+
+ mgmt_send(adapter->mgmt, MGMT_OP_GET_CONN_INFO, adapter->dev_id,
+ sizeof(cp), &cp, read_max_tx_power_rsp,
+ device, read_max_tx_power_destroy);
+}
+
static void connected_callback(uint16_t index, uint16_t length,
const void *param, void *user_data)
{
@@ -6220,6 +6268,8 @@ static void connected_callback(uint16_t index, uint16_t length,
btd_device_device_set_name(device, eir_data.name);
}

+ read_max_tx_power(adapter, device, &ev->addr.bdaddr, ev->addr.type);
+
eir_data_free(&eir_data);
}

--
1.9.3


2014-05-19 23:44:38

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [RFC 06/11] core: Make connection properties exist only when connected

This patch adds common 'exists' function for Connection* properties
on org.bluez.Device1 interface. It will make properties to exist only
when device is connected since they do not make sense otherwise.
---
src/device.c | 18 +++++++++++++++---
1 file changed, 15 insertions(+), 3 deletions(-)

diff --git a/src/device.c b/src/device.c
index 8661386..1891735 100644
--- a/src/device.c
+++ b/src/device.c
@@ -864,6 +864,15 @@ static gboolean dev_property_get_conn_max_txpower(
return TRUE;
}

+static gboolean dev_property_exists_conn(const GDBusPropertyTable *property,
+ void *data)
+{
+ struct btd_device *device = data;
+
+ /* TODO: only exists when monitoring is enabled? */
+ return btd_device_is_connected(device) ? TRUE : FALSE;
+}
+
static void set_trust(GDBusPendingPropertySet id, gboolean value, void *data)
{
struct btd_device *device = data;
@@ -1957,9 +1966,12 @@ static const GDBusPropertyTable device_properties[] = {
{ "Modalias", "s", dev_property_get_modalias, NULL,
dev_property_exists_modalias },
{ "Adapter", "o", dev_property_get_adapter },
- { "ConnectionRSSI", "n", dev_property_get_conn_rssi },
- { "ConnectionTXPower", "n", dev_property_get_conn_txpower },
- { "ConnectionTXPowerMax", "n", dev_property_get_conn_max_txpower },
+ { "ConnectionRSSI", "n", dev_property_get_conn_rssi, NULL,
+ dev_property_exists_conn },
+ { "ConnectionTXPower", "n", dev_property_get_conn_txpower, NULL,
+ dev_property_exists_conn },
+ { "ConnectionTXPowerMax", "n", dev_property_get_conn_max_txpower, NULL,
+ dev_property_exists_conn },
{ }
};

--
1.9.3


2014-05-19 23:44:40

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [RFC 08/11] shared: Fix queue_find to accept const ptr as match data

This patch fixes queue_find definition to have const pointer as data to
be matched against. Match function already does have const pointers as
arguments. Also parameter name is updated to be 'match_data' since this
actually is not user_data, just data to be matched against.
---
src/shared/queue.c | 4 ++--
src/shared/queue.h | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/shared/queue.c b/src/shared/queue.c
index 8a69729..ea4ff96 100644
--- a/src/shared/queue.c
+++ b/src/shared/queue.c
@@ -188,7 +188,7 @@ void queue_foreach(struct queue *queue, queue_foreach_func_t function,
}

void *queue_find(struct queue *queue, queue_match_func_t function,
- void *user_data)
+ const void *match_data)
{
struct queue_entry *entry;

@@ -196,7 +196,7 @@ void *queue_find(struct queue *queue, queue_match_func_t function,
return NULL;

for (entry = queue->head; entry; entry = entry->next)
- if (function(entry->data, user_data))
+ if (function(entry->data, match_data))
return entry->data;

return NULL;
diff --git a/src/shared/queue.h b/src/shared/queue.h
index 8201ff8..709590b 100644
--- a/src/shared/queue.h
+++ b/src/shared/queue.h
@@ -44,7 +44,7 @@ void queue_foreach(struct queue *queue, queue_foreach_func_t function,
typedef bool (*queue_match_func_t)(const void *a, const void *b);

void *queue_find(struct queue *queue, queue_match_func_t function,
- void *user_data);
+ const void *match_data);

bool queue_remove(struct queue *queue, void *data);
void *queue_remove_if(struct queue *queue, queue_match_func_t function,
--
1.9.3


2014-05-19 23:44:37

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [RFC 05/11] core: Add ConnectionTXPowerMax property

This patch adds ConnectionTXPowerMax property on org.bluez.Device1
interface which provides maximum local TX power on currently connected
link.

Property value will be read after connection and won't change during
connection lifetime.
---
src/device.c | 31 +++++++++++++++++++++++++++++++
src/device.h | 1 +
2 files changed, 32 insertions(+)

diff --git a/src/device.c b/src/device.c
index ac42a75..8661386 100644
--- a/src/device.c
+++ b/src/device.c
@@ -221,6 +221,7 @@ struct btd_device {

int8_t conn_rssi;
int8_t conn_tx_power;
+ int8_t conn_max_tx_power;

GIOChannel *att_io;
guint cleanup_id;
@@ -851,6 +852,18 @@ static gboolean dev_property_get_conn_txpower(
return TRUE;
}

+static gboolean dev_property_get_conn_max_txpower(
+ const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct btd_device *device = data;
+ dbus_int16_t val = device->conn_max_tx_power;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_INT16, &val);
+
+ return TRUE;
+}
+
static void set_trust(GDBusPendingPropertySet id, gboolean value, void *data)
{
struct btd_device *device = data;
@@ -1946,6 +1959,7 @@ static const GDBusPropertyTable device_properties[] = {
{ "Adapter", "o", dev_property_get_adapter },
{ "ConnectionRSSI", "n", dev_property_get_conn_rssi },
{ "ConnectionTXPower", "n", dev_property_get_conn_txpower },
+ { "ConnectionTXPowerMax", "n", dev_property_get_conn_max_txpower },
{ }
};

@@ -2342,6 +2356,7 @@ static struct btd_device *device_new(struct btd_adapter *adapter,
device->adapter = adapter;

device->conn_tx_power = 127; /* invalid */
+ device->conn_max_tx_power = 127; /* invalid */

return btd_device_ref(device);
}
@@ -4105,6 +4120,22 @@ void device_set_conn_tx_power(struct btd_device *device, int8_t tx_power)
"ConnectionTXPower");
}

+void device_set_conn_max_tx_power(struct btd_device *device, int8_t tx_power)
+{
+ if (!device)
+ return;
+
+ if (device->conn_max_tx_power == tx_power)
+ return;
+
+ DBG("conn_max_tx_power %d", tx_power);
+
+ device->conn_max_tx_power = tx_power;
+
+ g_dbus_emit_property_changed(dbus_conn, device->path, DEVICE_INTERFACE,
+ "ConnectionTXPowerMax");
+}
+
static void device_set_auto_connect(struct btd_device *device, gboolean enable)
{
char addr[18];
diff --git a/src/device.h b/src/device.h
index e0e44cc..b59fa19 100644
--- a/src/device.h
+++ b/src/device.h
@@ -90,6 +90,7 @@ void device_set_legacy(struct btd_device *device, bool legacy);
void device_set_rssi(struct btd_device *device, int8_t rssi);
void device_set_conn_rssi(struct btd_device *device, int8_t rssi);
void device_set_conn_tx_power(struct btd_device *device, int8_t tx_power);
+void device_set_conn_max_tx_power(struct btd_device *device, int8_t tx_power);
bool btd_device_is_connected(struct btd_device *dev);
uint8_t btd_device_get_bdaddr_type(struct btd_device *dev);
bool device_is_retrying(struct btd_device *device);
--
1.9.3


2014-05-19 23:44:41

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [RFC 09/11] core: Add API to start/stop connection monitoring

This patch adds Start-/StopConnectionMonitor methods to
org.bluez.Device1 which can be used to request polling of connection
properties, i.e. ConnectionRSSI and ConnectionTXPower.
---
src/adapter.c | 24 +++++++++
src/adapter.h | 7 +++
src/device.c | 160 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 191 insertions(+)

diff --git a/src/adapter.c b/src/adapter.c
index 585f2e2..a3204d0 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -5460,6 +5460,30 @@ static void disconnect_complete(uint8_t status, uint16_t length,
dev_disconnected(adapter, &rp->addr, MGMT_DEV_DISCONN_LOCAL_HOST);
}

+int btd_adapter_add_monitored_device(struct btd_adapter *adapter,
+ const bdaddr_t *bdaddr,
+ uint8_t bdaddr_type)
+{
+ char addrstr[18];
+
+ ba2str(bdaddr, addrstr);
+ DBG("addr %s type %d", addrstr, bdaddr_type);
+
+ return 0;
+}
+
+int btd_adapter_remove_monitored_device(struct btd_adapter *adapter,
+ const bdaddr_t *bdaddr,
+ uint8_t bdaddr_type)
+{
+ char addrstr[18];
+
+ ba2str(bdaddr, addrstr);
+ DBG("addr %s type %d", addrstr, bdaddr_type);
+
+ return 0;
+}
+
int btd_adapter_disconnect_device(struct btd_adapter *adapter,
const bdaddr_t *bdaddr,
uint8_t bdaddr_type)
diff --git a/src/adapter.h b/src/adapter.h
index f88c339..713c3ec 100644
--- a/src/adapter.h
+++ b/src/adapter.h
@@ -207,3 +207,10 @@ gboolean btd_adapter_check_oob_handler(struct btd_adapter *adapter);
void btd_adapter_for_each_device(struct btd_adapter *adapter,
void (*cb)(struct btd_device *device, void *data),
void *data);
+
+int btd_adapter_add_monitored_device(struct btd_adapter *adapter,
+ const bdaddr_t *bdaddr,
+ uint8_t bdaddr_type);
+int btd_adapter_remove_monitored_device(struct btd_adapter *adapter,
+ const bdaddr_t *bdaddr,
+ uint8_t bdaddr_type);
diff --git a/src/device.c b/src/device.c
index 1891735..4069861 100644
--- a/src/device.c
+++ b/src/device.c
@@ -48,6 +48,8 @@
#include "btio/btio.h"
#include "lib/uuid.h"
#include "lib/mgmt.h"
+#include "src/shared/queue.h"
+#include "src/shared/util.h"
#include "attrib/att.h"
#include "hcid.h"
#include "adapter.h"
@@ -159,6 +161,13 @@ struct bearer_state {
bool bonded;
bool connected;
bool svc_resolved;
+ bool monitored;
+};
+
+struct monitor_client {
+ struct btd_device *device;
+ char *owner;
+ guint watch;
};

struct btd_device {
@@ -219,6 +228,7 @@ struct btd_device {
bool legacy;
int8_t rssi;

+ struct queue *monitor_list;
int8_t conn_rssi;
int8_t conn_tx_power;
int8_t conn_max_tx_power;
@@ -515,6 +525,13 @@ static void svc_dev_remove(gpointer user_data)
g_free(cb);
}

+static void monitor_client_remove(void *data, void *user_data)
+{
+ struct monitor_client *client = data;
+
+ g_dbus_remove_watch(dbus_conn, client->watch);
+}
+
static void device_free(gpointer user_data)
{
struct btd_device *device = user_data;
@@ -555,6 +572,9 @@ static void device_free(gpointer user_data)
if (device->eir_uuids)
g_slist_free_full(device->eir_uuids, g_free);

+ queue_foreach(device->monitor_list, monitor_client_remove, NULL);
+ queue_destroy(device->monitor_list, NULL);
+
g_free(device->path);
g_free(device->alias);
free(device->modalias);
@@ -1934,6 +1954,134 @@ static DBusMessage *cancel_pairing(DBusConnection *conn, DBusMessage *msg,
return dbus_message_new_method_return(msg);
}

+static bool compare_sender(const void *a, const void *b)
+{
+ const struct monitor_client *client = a;
+ const char *sender = b;
+
+ return !strcmp(client->owner, sender);
+}
+
+static void monitor_start(struct btd_device *device)
+{
+ DBG("");
+
+ if (device->bredr_state.connected && !device->bredr_state.monitored) {
+ btd_adapter_add_monitored_device(device->adapter,
+ &device->bdaddr,
+ BDADDR_BREDR);
+ device->bredr_state.monitored = true;
+ }
+
+ if (device->le_state.connected && !device->le_state.monitored) {
+ btd_adapter_add_monitored_device(device->adapter,
+ &device->bdaddr,
+ device->bdaddr_type);
+ device->le_state.monitored = true;
+ }
+}
+
+static void monitor_stop(struct btd_device *device, bool stop_all)
+{
+ DBG("");
+
+ if (device->bredr_state.monitored &&
+ (stop_all || !device->bredr_state.connected)) {
+ btd_adapter_remove_monitored_device(device->adapter,
+ &device->bdaddr,
+ BDADDR_BREDR);
+ device->bredr_state.monitored = false;
+ }
+
+ if (device->le_state.monitored &&
+ (stop_all || !device->le_state.connected)) {
+ btd_adapter_remove_monitored_device(device->adapter,
+ &device->bdaddr,
+ device->bdaddr_type);
+ device->le_state.monitored = false;
+ }
+}
+
+static void monitor_destroy(void *user_data)
+{
+ struct monitor_client *client = user_data;
+ struct btd_device *device = client->device;
+
+ DBG("owner %s", client->owner);
+
+ queue_remove(device->monitor_list, client);
+
+ g_free(client->owner);
+ free(client);
+}
+
+static void monitor_disconnect(DBusConnection *conn, void *user_data)
+{
+ struct monitor_client *client = user_data;
+ struct btd_device *device = client->device;
+
+ DBG("owner %s", client->owner);
+
+ queue_remove(device->monitor_list, client);
+
+ if (queue_length(device->monitor_list))
+ return;
+
+ monitor_stop(device, true);
+}
+
+static DBusMessage *start_conn_monitoring(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ struct btd_device *device = user_data;
+ const char *sender = dbus_message_get_sender(msg);
+ struct monitor_client *client;
+
+ DBG("sender %s", sender);
+
+ if (!btd_device_is_connected(device))
+ return btd_error_not_connected(msg);
+
+ if (queue_find(device->monitor_list, compare_sender, sender))
+ return btd_error_busy(msg);
+
+ client = new0(struct monitor_client, 1);
+ client->device = device;
+ client->owner = g_strdup(sender);
+ client->watch = g_dbus_add_disconnect_watch(dbus_conn, sender,
+ monitor_disconnect,
+ client,
+ monitor_destroy);
+
+ if (!queue_length(device->monitor_list))
+ monitor_start(device);
+
+ queue_push_head(device->monitor_list, client);
+
+ return dbus_message_new_method_return(msg);
+}
+
+static DBusMessage *stop_conn_monitoring(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ struct btd_device *device = user_data;
+ const char *sender = dbus_message_get_sender(msg);
+ struct monitor_client *client;
+
+ DBG("sender %s", sender);
+
+ client = queue_find(device->monitor_list, compare_sender, sender);
+ if (!client)
+ return btd_error_failed(msg, "No monitor started");
+
+ g_dbus_remove_watch(dbus_conn, client->watch);
+
+ if (!queue_length(device->monitor_list))
+ monitor_stop(device, true);
+
+ return dbus_message_new_method_return(msg);
+}
+
static const GDBusMethodTable device_methods[] = {
{ GDBUS_ASYNC_METHOD("Disconnect", NULL, NULL, dev_disconnect) },
{ GDBUS_ASYNC_METHOD("Connect", NULL, NULL, dev_connect) },
@@ -1943,6 +2091,10 @@ static const GDBusMethodTable device_methods[] = {
NULL, disconnect_profile) },
{ GDBUS_ASYNC_METHOD("Pair", NULL, NULL, pair_device) },
{ GDBUS_METHOD("CancelPairing", NULL, NULL, cancel_pairing) },
+ { GDBUS_METHOD("StartConnectionMonitor", NULL, NULL,
+ start_conn_monitoring) },
+ { GDBUS_METHOD("StopConnectionMonitor", NULL, NULL,
+ stop_conn_monitoring) },
{ }
};

@@ -1998,6 +2150,8 @@ void device_add_connection(struct btd_device *dev, uint8_t bdaddr_type)
return;
}

+ monitor_start(dev);
+
/* If this is the first connection over this bearer */
if (bdaddr_type == BDADDR_BREDR)
device_set_bredr_support(dev);
@@ -2024,6 +2178,8 @@ void device_remove_connection(struct btd_device *device, uint8_t bdaddr_type)
device->svc_refreshed = false;
device->general_connect = FALSE;

+ monitor_stop(device, false);
+
if (device->disconn_timer > 0) {
g_source_remove(device->disconn_timer);
device->disconn_timer = 0;
@@ -2044,6 +2200,8 @@ void device_remove_connection(struct btd_device *device, uint8_t bdaddr_type)
if (device->bredr_state.connected || device->le_state.connected)
return;

+ queue_foreach(device->monitor_list, monitor_client_remove, NULL);
+
g_dbus_emit_property_changed(dbus_conn, device->path,
DEVICE_INTERFACE, "Connected");
}
@@ -2367,6 +2525,8 @@ static struct btd_device *device_new(struct btd_adapter *adapter,
str2ba(address, &device->bdaddr);
device->adapter = adapter;

+ device->monitor_list = queue_new();
+
device->conn_tx_power = 127; /* invalid */
device->conn_max_tx_power = 127; /* invalid */

--
1.9.3


2014-05-19 23:44:43

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [RFC 11/11] core: Poll for connection info

This patch adds actual polling mechanism for devices which are stored
on list for each adapter.
---
src/adapter.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 83 insertions(+)

diff --git a/src/adapter.c b/src/adapter.c
index 045ce30..c0ebfab 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -183,6 +183,7 @@ struct btd_adapter {
guint discovery_idle_timeout; /* timeout between discovery runs */
guint passive_scan_timeout; /* timeout between passive scans */
guint temp_devices_timeout; /* timeout for temporary devices */
+ guint mon_devices_timeout;

guint pairable_timeout_id; /* pairable timeout id */
guint auth_idle_id; /* Pending authorization dequeue */
@@ -4189,6 +4190,11 @@ static void adapter_remove(struct btd_adapter *adapter)
adapter->temp_devices_timeout = 0;
}

+ if (adapter->mon_devices_timeout > 0) {
+ g_source_remove(adapter->mon_devices_timeout);
+ adapter->mon_devices_timeout = 0;
+ }
+
discovery_cleanup(adapter);

g_slist_free(adapter->connect_list);
@@ -5467,6 +5473,73 @@ static void disconnect_complete(uint8_t status, uint16_t length,
dev_disconnected(adapter, &rp->addr, MGMT_DEV_DISCONN_LOCAL_HOST);
}

+static void monitor_conn_info_destroy(void *data)
+{
+ struct btd_device *device = data;
+
+ btd_device_unref(device);
+}
+
+static void monitor_conn_info_rsp(uint8_t status, uint16_t len,
+ const void *param, void *user_data)
+{
+ const struct mgmt_rp_get_conn_info *rp = param;
+ struct btd_device *device = user_data;
+
+ DBG("status %d", status);
+
+ if (status != MGMT_STATUS_SUCCESS) {
+ error("Get Connection Information failed: %s (0x%02x)",
+ mgmt_errstr(status), status);
+ return;
+ }
+
+ if (len < sizeof(*rp)) {
+ error("Too small Get Connection Information response");
+ return;
+ }
+
+ device_set_conn_rssi(device, rp->rssi);
+ device_set_conn_tx_power(device, rp->tx_power);
+}
+
+static void monitor_conn_info(void *data, void *user_data)
+{
+ struct mgmt_addr_info *addr = data;
+ struct btd_adapter *adapter = user_data;
+ struct mgmt_cp_get_conn_info cp;
+ char addrstr[18];
+ struct btd_device *device;
+
+ ba2str(&addr->bdaddr, addrstr);
+ DBG("bdaddr %s type %d", addrstr, addr->type);
+
+ device = btd_adapter_find_device(adapter, &addr->bdaddr, addr->type);
+ if (!device)
+ return;
+
+ btd_device_ref(device);
+
+ memset(&cp, 0, sizeof(cp));
+ bacpy(&cp.addr.bdaddr, &addr->bdaddr);
+ cp.addr.type = addr->type;
+
+ mgmt_send(adapter->mgmt, MGMT_OP_GET_CONN_INFO, adapter->dev_id,
+ sizeof(cp), &cp, monitor_conn_info_rsp,
+ device, monitor_conn_info_destroy);
+}
+
+static gboolean trigger_conn_monitor(gpointer user_data)
+{
+ struct btd_adapter *adapter = user_data;
+
+ DBG("");
+
+ queue_foreach(adapter->monitored_devices, monitor_conn_info, adapter);
+
+ return TRUE;
+}
+
struct monitored_device {
struct mgmt_addr_info addr;
};
@@ -5500,6 +5573,10 @@ int btd_adapter_add_monitored_device(struct btd_adapter *adapter,
return 0;
}

+ if (!queue_length(adapter->monitored_devices))
+ adapter->mon_devices_timeout =
+ g_timeout_add(2000, trigger_conn_monitor, adapter);
+
queue_push_tail(adapter->monitored_devices, mdev);

return 0;
@@ -5529,6 +5606,12 @@ int btd_adapter_remove_monitored_device(struct btd_adapter *adapter,

free(mdev);

+ if (!queue_length(adapter->monitored_devices))
+ if (adapter->mon_devices_timeout > 0) {
+ g_source_remove(adapter->mon_devices_timeout);
+ adapter->mon_devices_timeout = 0;
+ }
+
return 0;
}

--
1.9.3


2014-05-19 23:44:35

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [RFC 03/11] core: Add ConnectionRSSI property

This patch adds ConnectionRSSI property on org.bluez.Device1 interface
which provides RSSI on currently connected link (unlike RSSI which
provides RSSI for inquiry/advertising packets).

Property value will be refreshed once connection monitoring code is in
place.
---
src/device.c | 30 ++++++++++++++++++++++++++++++
src/device.h | 1 +
2 files changed, 31 insertions(+)

diff --git a/src/device.c b/src/device.c
index 8222610..fd4e120 100644
--- a/src/device.c
+++ b/src/device.c
@@ -219,6 +219,8 @@ struct btd_device {
bool legacy;
int8_t rssi;

+ int8_t conn_rssi;
+
GIOChannel *att_io;
guint cleanup_id;
guint store_id;
@@ -825,6 +827,17 @@ static gboolean dev_property_get_trusted(const GDBusPropertyTable *property,
return TRUE;
}

+static gboolean dev_property_get_conn_rssi(const GDBusPropertyTable *property,
+ DBusMessageIter *iter, void *data)
+{
+ struct btd_device *device = data;
+ dbus_int16_t val = device->conn_rssi;
+
+ dbus_message_iter_append_basic(iter, DBUS_TYPE_INT16, &val);
+
+ return TRUE;
+}
+
static void set_trust(GDBusPendingPropertySet id, gboolean value, void *data)
{
struct btd_device *device = data;
@@ -1918,6 +1931,7 @@ static const GDBusPropertyTable device_properties[] = {
{ "Modalias", "s", dev_property_get_modalias, NULL,
dev_property_exists_modalias },
{ "Adapter", "o", dev_property_get_adapter },
+ { "ConnectionRSSI", "n", dev_property_get_conn_rssi },
{ }
};

@@ -4043,6 +4057,22 @@ void device_set_rssi(struct btd_device *device, int8_t rssi)
DEVICE_INTERFACE, "RSSI");
}

+void device_set_conn_rssi(struct btd_device *device, int8_t rssi)
+{
+ if (!device)
+ return;
+
+ if (device->conn_rssi == rssi)
+ return;
+
+ DBG("conn_rssi %d", rssi);
+
+ device->conn_rssi = rssi;
+
+ g_dbus_emit_property_changed(dbus_conn, device->path, DEVICE_INTERFACE,
+ "ConnectionRSSI");
+}
+
static void device_set_auto_connect(struct btd_device *device, gboolean enable)
{
char addr[18];
diff --git a/src/device.h b/src/device.h
index 2e0473e..79d5132 100644
--- a/src/device.h
+++ b/src/device.h
@@ -88,6 +88,7 @@ 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(struct btd_device *device, int8_t rssi);
+void device_set_conn_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);
bool device_is_retrying(struct btd_device *device);
--
1.9.3


2014-05-19 23:44:33

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [RFC 01/11] doc: Update RSSI property description.

From: Lukasz Rymanowski <[email protected]>

RSSI property applies to either inquiry or advertising packets and this
should be stated here especially that we plan to add connection RSSI
property.
---
doc/device-api.txt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/doc/device-api.txt b/doc/device-api.txt
index 2d9fa3c..577ee60 100644
--- a/doc/device-api.txt
+++ b/doc/device-api.txt
@@ -192,4 +192,4 @@ Properties string Address [readonly]
int16 RSSI [readonly, optional]

Received Signal Strength Indicator of the remote
- device.
+ device (inquiry or advertising).
--
1.9.3


2014-05-19 23:44:34

by Andrzej Kaczmarek

[permalink] [raw]
Subject: [RFC 02/11] doc: Introduce connection monitoring API

From: Lukasz Rymanowski <[email protected]>

This patch introduces API to monitor connection parameters.

New device properties are introduced: ConnectionRSSI, ConnectionTXPower
and ConnectionTXPowerMax.

Client can request to poll for updates of RSSI and TX power via Start-
and StopConnectionMonitor methods.
---
doc/device-api.txt | 56 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 56 insertions(+)

diff --git a/doc/device-api.txt b/doc/device-api.txt
index 577ee60..67f573c 100644
--- a/doc/device-api.txt
+++ b/doc/device-api.txt
@@ -100,6 +100,35 @@ Methods void Connect()
Possible errors: org.bluez.Error.DoesNotExist
org.bluez.Error.Failed

+
+ void StartConnectionMonitor()
+
+ This method starts connection monitor session. This
+ includes link RSSI and TX power.
+ Use StopConnectionMonitor to release the sessions
+ acquired.
+
+ This process will start updating connection-related
+ properties, e.g. ConnectionRSSI and ConnectionTXPower.
+
+ Note that all sessions for device are released when
+ device is disconnected and it's required to start
+ sessiong again once reconnected.
+
+ Possible errors: org.bluez.Error.Busy
+ org.bluez.Error.NotConnected
+
+ void StopConnectionMonitor()
+
+ This method stops previous StartConnectionMonitor
+ session.
+
+ Note that connection monitor is shared between all
+ monitor sessions thus calling StopConnectionMonitor
+ releases a single session.
+
+ Possible errors: org.bluez.Error.Failed
+
Properties string Address [readonly]

The Bluetooth device address of the remote device.
@@ -193,3 +222,30 @@ Properties string Address [readonly]

Received Signal Strength Indicator of the remote
device (inquiry or advertising).
+
+ int16 ConnectionRSSI [readonly, optional]
+
+ Received Signal Strength Indicator of connected remote
+ device.
+
+ This property is present only if device is connected
+ and will be updated if at least one monitoring session
+ is active.
+
+ Note that RSSI has different units for BR/EDR (dB)
+ and LE (dBm) as specified in BT Core ver. 4.1, Vol. 2,
+ Part E, Chapter 7.5.4
+
+ int16 ConnectionTXPower [readonly, optional]
+
+ Transmit power level to connected remote device.
+
+ This property is present only if device is connected
+ and will be updated if at least one monitoring session
+ is active.
+
+ int16 ConnectionTXPowerMax [readonly, optional]
+
+ Maximum transmit power level to connected remote device.
+
+ This property is present only if device is connected.
--
1.9.3