diff mbox series

[Bluez,v2,07/10] advertising: Query LE TX range at manager initialization

Message ID 20200918151041.Bluez.v2.7.I187f71748b9bd93f6bf97ec4a195216109c3ea06@changeid (mailing list archive)
State New, archived
Headers show
Series Bluetooth: Add new MGMT interface for advertising add | expand

Commit Message

Daniel Winkler Sept. 18, 2020, 10:13 p.m. UTC
This patch calls the new MGMT command to get controller capabilities,
and parses the min and max LE tx power range when the manager is
initialized. This will be used to populate a client-facing dbus entry so
that the client will know the advertising capabilities of the controller
before registering an advertisement.

This patch is tested by manually verifying the data is parsed correctly
from the MGMT response.

Reviewed-by: Sonny Sasaka <sonnysasaka@chromium.org>
Reviewed-by: Alain Michaud <alainm@chromium.org>
---

Changes in v2: None

 lib/mgmt.h        |  8 +++++++
 src/advertising.c | 59 +++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 67 insertions(+)
diff mbox series

Patch

diff --git a/lib/mgmt.h b/lib/mgmt.h
index 56f46ce93..558452e9c 100644
--- a/lib/mgmt.h
+++ b/lib/mgmt.h
@@ -744,6 +744,14 @@  struct mgmt_rp_add_ext_adv_data {
 	uint8_t	instance;
 } __packed;
 
+#define MGMT_CAP_LE_TX_PWR_MIN	0x0000
+#define MGMT_CAP_LE_TX_PWR_MAX	0x0001
+
+#define MGMT_OP_READ_CONTROLLER_CAP	0x0056
+struct mgmt_rp_read_controller_cap {
+	uint8_t capabilities[0]; /* mgmt_tlv */
+} __packed;
+
 #define MGMT_EV_CMD_COMPLETE		0x0001
 struct mgmt_ev_cmd_complete {
 	uint16_t opcode;
diff --git a/src/advertising.c b/src/advertising.c
index c7deda1ad..a3e7f3d59 100644
--- a/src/advertising.c
+++ b/src/advertising.c
@@ -58,6 +58,8 @@  struct btd_adv_manager {
 	uint32_t supported_flags;
 	unsigned int instance_bitmap;
 	bool extended_add_cmds;
+	int8_t min_tx_power;
+	int8_t max_tx_power;
 };
 
 #define AD_TYPE_BROADCAST 0
@@ -1708,6 +1710,54 @@  static void tx_power_selected(uint16_t index, uint16_t length,
 				DBUS_TYPE_INT16, &tx_power, NULL, NULL, NULL);
 }
 
+static void read_controller_cap_complete(uint8_t status, uint16_t length,
+					const void *param, void *user_data)
+{
+	struct btd_adv_manager *manager = user_data;
+	const uint8_t *ptr = param;
+	uint16_t offset = 0;
+
+	/* Both capabilities we care about are stored as int8_t. If we later
+	 * want to track other types, this structure will need to change
+	 */
+	const struct {
+		struct mgmt_tlv entry;
+		int8_t value;
+	} __packed * cap;
+
+	while (offset < length) {
+		/* Since TLV entries can have variable length, offset tracks how
+		 * far into the member we are, so that cap is always pointing
+		 * to the beginning of a valid struct
+		 */
+		cap = (void *)&ptr[offset];
+		switch (cap->entry.type) {
+		case MGMT_CAP_LE_TX_PWR_MIN:
+			if (cap->entry.length !=
+					sizeof(manager->min_tx_power)) {
+				error("TX power had unexpected length %d",
+					cap->entry.length);
+				break;
+			}
+			memcpy(&manager->min_tx_power, &cap->value,
+					cap->entry.length);
+			break;
+		case MGMT_CAP_LE_TX_PWR_MAX:
+			if (cap->entry.length !=
+					sizeof(manager->min_tx_power)) {
+				error("TX power had unexpected length %d",
+					cap->entry.length);
+				break;
+			}
+			memcpy(&manager->max_tx_power, &cap->value,
+					cap->entry.length);
+			break;
+		}
+
+		offset += sizeof(cap->entry) + cap->entry.length;
+	}
+}
+
 static struct btd_adv_manager *manager_create(struct btd_adapter *adapter,
 						struct mgmt *mgmt)
 {
@@ -1729,6 +1779,8 @@  static struct btd_adv_manager *manager_create(struct btd_adapter *adapter,
 	manager->supported_flags = MGMT_ADV_FLAG_LOCAL_NAME;
 	manager->extended_add_cmds =
 			btd_has_kernel_features(KERNEL_HAS_EXT_ADV_ADD_CMDS);
+	manager->min_tx_power = ADV_TX_POWER_NO_PREFERENCE;
+	manager->max_tx_power = ADV_TX_POWER_NO_PREFERENCE;
 
 	if (!g_dbus_register_interface(btd_get_dbus_connection(),
 					adapter_get_path(manager->adapter),
@@ -1750,6 +1802,13 @@  static struct btd_adv_manager *manager_create(struct btd_adapter *adapter,
 			      manager->mgmt_index, tx_power_selected,
 			      manager, NULL);
 
+	/* Query controller capabilities. This will be used to display valid
+	 * advertising tx power range to the client.
+	 */
+	mgmt_send(manager->mgmt, MGMT_OP_READ_CONTROLLER_CAP,
+			manager->mgmt_index, 0, NULL,
+			read_controller_cap_complete, manager, NULL);
+
 	return manager;
 
 fail: