2012-03-11 09:23:16

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 0/3] Implement TPS profile

We replace the TPS stub in reporter.c with a fully functional
implementation. The Tx power level for each LE device is read on
connection and cached (as it cannot change). When a remote device
requests it the cached value is returned.

This series depends on the kernel patch:
"Bluetooth: Implement Read Transmit Power Level command"

It also depends the bluez series "Implement ProximityReporter profiles",
mostly for the ATT patch adding the remote-device to each ATT read/write
callback.

Arik Nemtsov (3):
mgmt: implement read tx power
adapter: implement API to read Tx power
proximity: tx power: implement tx power server

Makefile.am | 3 +-
lib/mgmt.h | 11 ++
plugins/mgmtops.c | 64 +++++++++++
proximity/reporter.c | 48 +--------
proximity/txpower.c | 292 ++++++++++++++++++++++++++++++++++++++++++++++++++
proximity/txpower.h | 24 ++++
src/adapter.c | 75 +++++++++++++
src/adapter.h | 17 +++
8 files changed, 488 insertions(+), 46 deletions(-)
create mode 100644 proximity/txpower.c
create mode 100644 proximity/txpower.h

--
1.7.5.4



2012-03-14 13:33:27

by Arik Nemtsov

[permalink] [raw]
Subject: Re: [PATCH 0/3] Implement TPS profile

On Wed, Mar 14, 2012 at 15:23, Anderson Lizardo
<[email protected]> wrote:
>
> I believe we need to hold this TPS series until some kernel support
> for TX power reading (either automatically on every connection and
> sent along the Device Connected event, or through a new mgmt command,
> depending on what you agreed on IRC regarding what's the best
> approach) is upstream.
>

Of course. I'm still waiting for a comment from Marcel about this.

The dependency is mentioned in the cover letter as well.

Regards,
Arik

2012-03-14 13:23:44

by Anderson Lizardo

[permalink] [raw]
Subject: Re: [PATCH 0/3] Implement TPS profile

Hi Arik,

On Sun, Mar 11, 2012 at 5:23 AM, Arik Nemtsov <[email protected]> wrote:
> We replace the TPS stub in reporter.c with a fully functional
> implementation. The Tx power level for each LE device is read on
> connection and cached (as it cannot change). When a remote device
> requests it the cached value is returned.
>
> This series depends on the kernel patch:
> "Bluetooth: Implement Read Transmit Power Level command"
>
> It also depends the bluez series "Implement ProximityReporter profiles",
> mostly for the ATT patch adding the remote-device to each ATT read/write
> callback.
>
> Arik Nemtsov (3):
> ?mgmt: implement read tx power
> ?adapter: implement API to read Tx power
> ?proximity: tx power: implement tx power server
>
> ?Makefile.am ? ? ? ? ?| ? ?3 +-
> ?lib/mgmt.h ? ? ? ? ? | ? 11 ++
> ?plugins/mgmtops.c ? ?| ? 64 +++++++++++
> ?proximity/reporter.c | ? 48 +--------
> ?proximity/txpower.c ?| ?292 ++++++++++++++++++++++++++++++++++++++++++++++++++
> ?proximity/txpower.h ?| ? 24 ++++
> ?src/adapter.c ? ? ? ?| ? 75 +++++++++++++
> ?src/adapter.h ? ? ? ?| ? 17 +++
> ?8 files changed, 488 insertions(+), 46 deletions(-)
> ?create mode 100644 proximity/txpower.c
> ?create mode 100644 proximity/txpower.h

I believe we need to hold this TPS series until some kernel support
for TX power reading (either automatically on every connection and
sent along the Device Connected event, or through a new mgmt command,
depending on what you agreed on IRC regarding what's the best
approach) is upstream.

Regards,
--
Anderson Lizardo
Instituto Nokia de Tecnologia - INdT
Manaus - Brazil

2012-03-11 09:23:19

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 3/3] proximity: tx power: implement tx power server

The profile is implemented in txpower.[ch]. A GATT service is registered
with a read callback on the Tx power level attribute.

The Tx power of each LE device is requested from the controller on
connection. Upon receipt, it is cached in a connected_device structure.
When a remote device asks for its Tx power level, the cached value is
returned. The cache is deleted on disconnection.
---
Makefile.am | 3 +-
proximity/reporter.c | 48 +--------
proximity/txpower.c | 292 ++++++++++++++++++++++++++++++++++++++++++++++++++
proximity/txpower.h | 24 ++++
4 files changed, 321 insertions(+), 46 deletions(-)
create mode 100644 proximity/txpower.c
create mode 100644 proximity/txpower.h

diff --git a/Makefile.am b/Makefile.am
index 0295ce2..d6e1b87 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -206,7 +206,8 @@ builtin_sources += proximity/main.c \
proximity/monitor.h proximity/monitor.c \
proximity/reporter.h proximity/reporter.c \
proximity/linkloss.h proximity/linkloss.c \
- proximity/immalert.h proximity/immalert.c
+ proximity/immalert.h proximity/immalert.c \
+ proximity/txpower.h proximity/txpower.c
endif

if SERVICEPLUGIN
diff --git a/proximity/reporter.c b/proximity/reporter.c
index d540233..0da08cc 100644
--- a/proximity/reporter.c
+++ b/proximity/reporter.c
@@ -46,6 +46,7 @@
#include "reporter.h"
#include "linkloss.h"
#include "immalert.h"
+#include "txpower.h"

#define BLUEZ_SERVICE "org.bluez"

@@ -74,50 +75,6 @@ const char *get_alert_level_string(uint8_t level)
return "unknown";
}

-static void register_tx_power(struct btd_adapter *adapter)
-{
- uint16_t start_handle, h;
- const int svc_size = 4;
- uint8_t atval[256];
- bt_uuid_t uuid;
-
- bt_uuid16_create(&uuid, TX_POWER_SVC_UUID);
- start_handle = attrib_db_find_avail(adapter, &uuid, svc_size);
- if (start_handle == 0) {
- error("Not enough free handles to register service");
- return;
- }
-
- DBG("start_handle=0x%04x", start_handle);
-
- h = start_handle;
-
- /* Primary service definition */
- bt_uuid16_create(&uuid, GATT_PRIM_SVC_UUID);
- att_put_u16(TX_POWER_SVC_UUID, &atval[0]);
- attrib_db_add(adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 2);
-
- /* Power level characteristic */
- bt_uuid16_create(&uuid, GATT_CHARAC_UUID);
- atval[0] = ATT_CHAR_PROPER_READ | ATT_CHAR_PROPER_NOTIFY;
- att_put_u16(h + 1, &atval[1]);
- att_put_u16(POWER_LEVEL_CHR_UUID, &atval[3]);
- attrib_db_add(adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 5);
-
- /* Power level value */
- bt_uuid16_create(&uuid, POWER_LEVEL_CHR_UUID);
- att_put_u8(0x00, &atval[0]);
- attrib_db_add(adapter, h++, &uuid, ATT_NONE, ATT_NOT_PERMITTED, atval, 1);
-
- /* Client characteristic configuration */
- bt_uuid16_create(&uuid, GATT_CLIENT_CHARAC_CFG_UUID);
- atval[0] = 0x00;
- atval[1] = 0x00;
- attrib_db_add(adapter, h++, &uuid, ATT_NONE, ATT_NONE, atval, 2);
-
- g_assert(h - start_handle == svc_size);
-}
-
static DBusMessage *get_properties(DBusConnection *conn,
DBusMessage *msg, void *data)
{
@@ -251,7 +208,7 @@ int reporter_init(struct btd_adapter *adapter)
radapter->conn = conn;

register_link_loss(adapter, radapter->conn);
- register_tx_power(adapter);
+ register_tx_power(adapter, radapter->conn);
register_imm_alert(adapter, radapter->conn);

/*
@@ -309,6 +266,7 @@ void reporter_exit(struct btd_adapter *adapter)

unregister_link_loss(adapter);
unregister_imm_alert(adapter);
+ unregister_tx_power(adapter);
dbus_connection_unref(radapter->conn);

reporter_adapters = g_slist_remove(reporter_adapters, radapter);
diff --git a/proximity/txpower.c b/proximity/txpower.c
new file mode 100644
index 0000000..4b2e65d
--- /dev/null
+++ b/proximity/txpower.c
@@ -0,0 +1,292 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012 Texas Instruments Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <bluetooth/uuid.h>
+#include <adapter.h>
+
+#include <dbus/dbus.h>
+#include <gdbus.h>
+
+#include "log.h"
+#include "att.h"
+#include "gattrib.h"
+#include "gatt-service.h"
+#include "attrib-server.h"
+#include "device.h"
+#include "reporter.h"
+#include "txpower.h"
+
+#define MIN_TX_POWER_LEVEL (-100)
+
+#define BLUEZ_SERVICE "org.bluez"
+
+struct tx_power_adapter {
+ struct btd_adapter *adapter;
+ DBusConnection *conn;
+ GSList *connected_devices;
+ guint watch;
+};
+
+struct connected_device {
+ struct btd_device *device;
+ struct tx_power_adapter *adapter;
+ int8_t tx_power_level;
+};
+
+static GSList *tx_power_adapters;
+
+static int tpdevice_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct connected_device *condev = a;
+ const struct btd_device *device = b;
+
+ if (condev->device == device)
+ return 0;
+
+ return -1;
+}
+
+static struct connected_device *
+find_connected_device(struct tx_power_adapter *ta, struct btd_device *device)
+{
+ GSList *l = g_slist_find_custom(ta->connected_devices, device,
+ tpdevice_cmp);
+ if (!l)
+ return NULL;
+
+ return l->data;
+}
+
+static int tpadapter_cmp(gconstpointer a, gconstpointer b)
+{
+ const struct tx_power_adapter *ta = a;
+ const struct btd_adapter *adapter = b;
+
+ if (ta->adapter == adapter)
+ return 0;
+
+ return -1;
+}
+
+static struct tx_power_adapter *
+find_tx_power_adapter(struct btd_adapter *adapter)
+{
+ GSList *l = g_slist_find_custom(tx_power_adapters, adapter,
+ tpadapter_cmp);
+ if (!l)
+ return NULL;
+
+ return l->data;
+}
+
+/* condev can be NULL */
+static void tx_power_remove_condev(struct connected_device *condev)
+{
+ struct tx_power_adapter *ta;
+
+ if (!condev)
+ return;
+
+ ta = condev->adapter;
+
+ if (condev->device)
+ btd_device_unref(condev->device);
+
+ ta->connected_devices = g_slist_remove(ta->connected_devices, condev);
+ g_free(condev);
+}
+
+static uint8_t tx_power_attrib_read(struct attribute *a, gpointer user_data,
+ struct btd_device *device)
+{
+ struct tx_power_adapter *ta = user_data;
+ struct connected_device *condev;
+ int8_t value = MIN_TX_POWER_LEVEL;
+
+ if (!device)
+ goto out;
+
+ condev = find_connected_device(ta, device);
+ if (!condev)
+ goto out;
+
+ value = condev->tx_power_level;
+
+out:
+ attrib_db_update(ta->adapter, a->handle, NULL, (uint8_t *)&value,
+ sizeof(value), NULL);
+ DBG("Tx power level: %d", value);
+ return 0;
+}
+
+static void tx_power_read_cb(struct btd_adapter *adapter,
+ struct btd_device *dev, int8_t level,
+ gpointer user_data)
+{
+ struct tx_power_adapter *ta = user_data;
+ struct connected_device *condev;
+
+ ta = find_tx_power_adapter(adapter);
+ if (!ta)
+ return;
+
+ condev = find_connected_device(ta, dev);
+ if (!condev)
+ return;
+
+ condev->tx_power_level = level;
+ DBG("updated tx power level of %p to %d", dev, level);
+}
+
+static gboolean handle_property_change(DBusConnection *conn, DBusMessage *msg,
+ void *data)
+{
+ struct tx_power_adapter *ta = data;
+ DBusMessageIter iter, sub;
+ const char *property;
+ dbus_bool_t conn_state;
+ struct btd_device *device;
+ struct connected_device *condev;
+ const char *obj_path = dbus_message_get_path(msg);
+
+ DBG("path %s", obj_path);
+
+ dbus_message_iter_init(msg, &iter);
+
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_STRING) {
+ error("Unexpected signature in device PropertyChanged signal");
+ return TRUE;
+ }
+
+ dbus_message_iter_get_basic(&iter, &property);
+ DBG("property %s", property);
+
+ dbus_message_iter_next(&iter);
+ dbus_message_iter_recurse(&iter, &sub);
+ if (!g_str_equal(property, "Connected"))
+ return TRUE;
+
+ device = adapter_get_device_by_path(ta->adapter, obj_path);
+ if (!device)
+ return TRUE;
+
+ dbus_message_iter_get_basic(&sub, &conn_state);
+ DBG("Connected %d", conn_state);
+ if (conn_state) {
+ /* we only care about LE devices */
+ if (device_is_bredr(device))
+ return TRUE;
+
+ condev = g_new0(struct connected_device, 1);
+ condev->device = btd_device_ref(device);
+ condev->adapter = ta;
+ condev->tx_power_level = MIN_TX_POWER_LEVEL;
+ ta->connected_devices = g_slist_append(ta->connected_devices,
+ condev);
+
+ /*
+ * Get the Tx power to this device. It cannot change during
+ * an LE connection.
+ */
+ adapter_read_tx_power(ta->adapter, device,
+ TX_POWER_CURRENT_POWER,
+ tx_power_read_cb, ta);
+
+ DBG("added connecting device %p", device);
+ } else {
+ condev = find_connected_device(ta, device);
+ if (condev) {
+ tx_power_remove_condev(condev);
+ DBG("removed disconnecting device %p", device);
+ }
+ }
+
+ return TRUE;
+}
+
+void register_tx_power(struct btd_adapter *adapter, DBusConnection *conn)
+{
+ gboolean svc_added;
+ bt_uuid_t uuid;
+ struct tx_power_adapter *ta;
+
+ bt_uuid16_create(&uuid, TX_POWER_SVC_UUID);
+
+ ta = g_new0(struct tx_power_adapter, 1);
+ ta->adapter = adapter;
+ ta->conn = conn;
+
+ tx_power_adapters = g_slist_append(tx_power_adapters, ta);
+
+ /* Tx Power Service */
+ svc_added = gatt_service_add(adapter,
+ GATT_PRIM_SVC_UUID, &uuid,
+ /* Power level characteristic */
+ GATT_OPT_CHR_UUID, POWER_LEVEL_CHR_UUID,
+ GATT_OPT_CHR_PROPS, ATT_CHAR_PROPER_READ,
+ GATT_OPT_CHR_VALUE_CB, ATTRIB_READ,
+ tx_power_attrib_read, ta,
+ GATT_OPT_INVALID);
+
+ if (!svc_added)
+ goto err;
+
+ /* watch for connecting/disconnecting devices */
+ ta->watch = g_dbus_add_signal_watch(ta->conn,
+ BLUEZ_SERVICE, NULL, DEVICE_INTERFACE,
+ "PropertyChanged", handle_property_change,
+ ta, NULL);
+
+ DBG("Tx Power service added");
+ return;
+
+err:
+ error("Error adding Tx Power service");
+ unregister_tx_power(adapter);
+}
+
+static void remove_condev_list_item(gpointer data, gpointer user_data)
+{
+ struct connected_device *condev = data;
+
+ tx_power_remove_condev(condev);
+}
+
+void unregister_tx_power(struct btd_adapter *adapter)
+{
+ struct tx_power_adapter *ta = find_tx_power_adapter(adapter);
+ if (!ta)
+ return;
+
+ g_dbus_remove_watch(ta->conn, ta->watch);
+
+ g_slist_foreach(ta->connected_devices, remove_condev_list_item, NULL);
+ dbus_connection_unref(ta->conn);
+
+ tx_power_adapters = g_slist_remove(tx_power_adapters, ta);
+ g_free(ta);
+}
diff --git a/proximity/txpower.h b/proximity/txpower.h
new file mode 100644
index 0000000..91c9dcc
--- /dev/null
+++ b/proximity/txpower.h
@@ -0,0 +1,24 @@
+/*
+ *
+ * BlueZ - Bluetooth protocol stack for Linux
+ *
+ * Copyright (C) 2012 Texas Instruments Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ */
+
+void register_tx_power(struct btd_adapter *adapter, DBusConnection *conn);
+void unregister_tx_power(struct btd_adapter *adapter);
--
1.7.5.4


2012-03-11 09:23:18

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 2/3] adapter: implement API to read Tx power

A caller wishing to read Tx power registers a one time callback function.
This function is called when a Tx-power-read operation completes
successfully.

We do not introduce facilities to unregister the callback (in case
the caller exits) since the read operation is close to immediate.
---
plugins/mgmtops.c | 3 ++
src/adapter.c | 75 +++++++++++++++++++++++++++++++++++++++++++++++++++++
src/adapter.h | 11 ++++++++
3 files changed, 89 insertions(+), 0 deletions(-)

diff --git a/plugins/mgmtops.c b/plugins/mgmtops.c
index 317df24..7753989 100644
--- a/plugins/mgmtops.c
+++ b/plugins/mgmtops.c
@@ -1259,6 +1259,9 @@ static void read_tx_power_complete(int sk, uint16_t index, void *buf,
DBG("Adapter not found");
return;
}
+
+ adapter_tx_power_read_complete(adapter, rp->status, &rp->addr.bdaddr,
+ rp->level);
}

static void read_local_oob_data_complete(int sk, uint16_t index, void *buf,
diff --git a/src/adapter.c b/src/adapter.c
index b3ee9d7..0909b33 100644
--- a/src/adapter.c
+++ b/src/adapter.c
@@ -106,6 +106,12 @@ struct service_auth {
struct btd_adapter *adapter;
};

+struct tx_power_read_data {
+ adapter_tx_power_read_cb cb;
+ bdaddr_t bdaddr;
+ gpointer user_data;
+};
+
struct btd_adapter {
uint16_t dev_id;
gboolean up;
@@ -153,6 +159,8 @@ struct btd_adapter {
GSList *pin_callbacks;

GSList *loaded_drivers;
+
+ GSList *txpower_callbacks;
};

static void dev_info_free(void *data)
@@ -2521,6 +2529,7 @@ void adapter_remove(struct btd_adapter *adapter)
btd_adapter_gatt_server_stop(adapter);

g_slist_free(adapter->pin_callbacks);
+ g_slist_free(adapter->txpower_callbacks);

/* Return adapter to down state if it was not up on init */
adapter_ops->restore_powered(adapter->dev_id);
@@ -3594,3 +3603,69 @@ struct btd_device *adapter_get_device_by_path(struct btd_adapter *adapter,

return l->data;
}
+
+int adapter_read_tx_power(struct btd_adapter *adapter,
+ struct btd_device *device,
+ uint8_t type, adapter_tx_power_read_cb cb,
+ gpointer user_data)
+{
+ struct tx_power_read_data *data;
+ bdaddr_t addr;
+ addr_type_t addr_type;
+ int ret;
+
+ if (!adapter_ops->read_tx_power)
+ return -ENOTSUP;
+
+ device_get_address(device, &addr, &addr_type);
+
+ ret = adapter_ops->read_tx_power(adapter->dev_id, &addr, addr_type,
+ type);
+ if (ret)
+ return ret;
+
+ data = g_new0(struct tx_power_read_data, 1);
+ bacpy(&data->bdaddr, &addr);
+ data->cb = cb;
+ data->user_data = user_data;
+
+ adapter->txpower_callbacks =
+ g_slist_prepend(adapter->txpower_callbacks, data);
+
+ return 0;
+}
+
+void adapter_tx_power_read_complete(struct btd_adapter *adapter,
+ uint8_t status, bdaddr_t *bdaddr,
+ int8_t level)
+{
+ GSList *l;
+ struct tx_power_read_data *data = NULL;
+ struct btd_device *device;
+ char addr[18];
+
+ for (l = adapter->txpower_callbacks; l != NULL; l = g_slist_next(l)) {
+ data = l->data;
+ if (bacmp(&data->bdaddr, bdaddr) == 0)
+ break;
+ }
+
+ /* callback not found */
+ if (!l)
+ return;
+
+ /* don't call the cb if we failed, just remove it */
+ if (status)
+ goto out;
+
+ ba2str(bdaddr, addr);
+ device = adapter_find_device(adapter, addr);
+ if (!device)
+ goto out;
+
+ data->cb(adapter, device, level, data->user_data);
+out:
+ adapter->txpower_callbacks =
+ g_slist_remove(adapter->txpower_callbacks, data);
+ g_free(data);
+}
diff --git a/src/adapter.h b/src/adapter.h
index 1407c18..18e102d 100644
--- a/src/adapter.h
+++ b/src/adapter.h
@@ -150,6 +150,17 @@ void btd_adapter_unref(struct btd_adapter *adapter);
int btd_adapter_set_class(struct btd_adapter *adapter, uint8_t major,
uint8_t minor);

+typedef void (*adapter_tx_power_read_cb) (struct btd_adapter *adapter,
+ struct btd_device *dev, int8_t level,
+ gpointer user_data);
+int adapter_read_tx_power(struct btd_adapter *adapter,
+ struct btd_device *device,
+ uint8_t type, adapter_tx_power_read_cb cb,
+ gpointer user_data);
+void adapter_tx_power_read_complete(struct btd_adapter *adatper,
+ uint8_t status, bdaddr_t *bdaddr,
+ int8_t level);
+
struct btd_adapter_driver {
const char *name;
int (*probe) (struct btd_adapter *adapter);
--
1.7.5.4


2012-03-11 09:23:17

by Arik Nemtsov

[permalink] [raw]
Subject: [PATCH 1/3] mgmt: implement read tx power

Implement the read Tx power mgmt op and completion event. Based on a
similar patch by Bruna Moreira <[email protected]>
---
lib/mgmt.h | 11 +++++++++
plugins/mgmtops.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++++++++
src/adapter.h | 6 +++++
3 files changed, 78 insertions(+), 0 deletions(-)

diff --git a/lib/mgmt.h b/lib/mgmt.h
index f3bc6da..073752f 100644
--- a/lib/mgmt.h
+++ b/lib/mgmt.h
@@ -311,6 +311,17 @@ struct mgmt_cp_unblock_device {
struct mgmt_addr_info addr;
} __packed;

+#define MGMT_OP_READ_TX_POWER_LEVEL 0x0028
+struct mgmt_cp_read_tx_power_level {
+ struct mgmt_addr_info addr;
+ uint8_t type;
+} __packed;
+struct mgmt_rp_read_tx_power_level {
+ struct mgmt_addr_info addr;
+ uint8_t status;
+ int8_t level;
+} __packed;
+
#define MGMT_EV_CMD_COMPLETE 0x0001
struct mgmt_ev_cmd_complete {
uint16_t opcode;
diff --git a/plugins/mgmtops.c b/plugins/mgmtops.c
index 4aa38fe..317df24 100644
--- a/plugins/mgmtops.c
+++ b/plugins/mgmtops.c
@@ -1229,6 +1229,38 @@ static void set_local_name_complete(int sk, uint16_t index, void *buf,
adapter_name_changed(adapter, (char *) rp->name);
}

+static void read_tx_power_complete(int sk, uint16_t index, void *buf,
+ size_t len)
+{
+ struct mgmt_rp_read_tx_power_level *rp = buf;
+ struct controller_info *info;
+ struct btd_adapter *adapter;
+ char addr[18];
+
+ if (len != sizeof(*rp)) {
+ error("Too small tx power complete event");
+ return;
+ }
+
+ if (index > max_index) {
+ error("Unexpected index %u in read tx power complete", index);
+ return;
+ }
+
+ ba2str(&rp->addr.bdaddr, addr);
+
+ DBG("hci%d addr=%s status=%d level=%d", index, addr, rp->status,
+ rp->level);
+
+ info = &controllers[index];
+
+ adapter = manager_find_adapter(&info->bdaddr);
+ if (adapter == NULL) {
+ DBG("Adapter not found");
+ return;
+ }
+}
+
static void read_local_oob_data_complete(int sk, uint16_t index, void *buf,
size_t len)
{
@@ -1402,6 +1434,9 @@ static void mgmt_cmd_complete(int sk, uint16_t index, void *buf, size_t len)
case MGMT_OP_SET_LOCAL_NAME:
set_local_name_complete(sk, index, ev->data, len);
break;
+ case MGMT_OP_READ_TX_POWER_LEVEL:
+ read_tx_power_complete(sk, index, ev->data, len);
+ break;
case MGMT_OP_READ_LOCAL_OOB_DATA:
read_local_oob_data_complete(sk, index, ev->data, len);
break;
@@ -1969,6 +2004,31 @@ static int mgmt_read_clock(int index, bdaddr_t *bdaddr, int which, int timeout,
return -ENOSYS;
}

+static int mgmt_read_tx_power(int index, bdaddr_t *bdaddr,
+ addr_type_t addr_type, uint8_t type)
+{
+ char addr[18];
+ char buf[MGMT_HDR_SIZE + sizeof(struct mgmt_cp_read_tx_power_level)];
+ struct mgmt_hdr *hdr = (void *) buf;
+ struct mgmt_cp_read_tx_power_level *cp = (void *) &buf[sizeof(*hdr)];
+
+ ba2str(bdaddr, addr);
+ DBG("index %d addr %s read transmit power level", index, addr);
+ memset(buf, 0, sizeof(buf));
+ hdr->opcode = htobs(MGMT_OP_READ_TX_POWER_LEVEL);
+ hdr->len = htobs(sizeof(*cp));
+ hdr->index = htobs(index);
+
+ bacpy(&cp->addr.bdaddr, bdaddr);
+ cp->addr.type = mgmt_addr_type(addr_type);
+ cp->type = type;
+
+ if (write(mgmt_sock, buf, sizeof(buf)) < 0)
+ return -errno;
+
+ return 0;
+}
+
static int mgmt_read_bdaddr(int index, bdaddr_t *bdaddr)
{
char addr[18];
@@ -2436,6 +2496,7 @@ static struct btd_adapter_ops mgmt_ops = {
.remove_remote_oob_data = mgmt_remove_remote_oob_data,
.confirm_name = mgmt_confirm_name,
.load_ltks = mgmtops_load_ltks,
+ .read_tx_power = mgmt_read_tx_power,
};

static int mgmt_init(void)
diff --git a/src/adapter.h b/src/adapter.h
index 4933baf..1407c18 100644
--- a/src/adapter.h
+++ b/src/adapter.h
@@ -41,6 +41,10 @@
/* Invalid SSP passkey value used to indicate negative replies */
#define INVALID_PASSKEY 0xffffffff

+/* Tx Power measurement types */
+#define TX_POWER_CURRENT_POWER 0
+#define TX_POWER_MAX_POWER 1
+
typedef enum {
ADDR_TYPE_BREDR,
ADDR_TYPE_LE_PUBLIC,
@@ -229,6 +233,8 @@ struct btd_adapter_ops {
int (*confirm_name) (int index, bdaddr_t *bdaddr, addr_type_t type,
gboolean name_known);
int (*load_ltks) (int index, GSList *keys);
+ int (*read_tx_power) (int index, bdaddr_t *bdaddr,
+ addr_type_t addr_type, uint8_t type);
};

int btd_register_adapter_ops(struct btd_adapter_ops *ops, gboolean priority);
--
1.7.5.4