@@ -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;
@@ -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: