@@ -599,10 +599,16 @@ struct mgmt_cp_set_blocked_keys {
struct mgmt_blocked_key_info keys[0];
} __packed;
-#define MGMT_OP_READ_SECURITY_INFO 0x0048
-struct mgmt_rp_read_security_info {
- uint16_t sec_len;
- uint8_t sec[0];
+#define MGMT_CAP_SEC_FLAGS 0x01
+#define MGMT_CAP_MAX_ENC_KEY_SIZE 0x02
+#define MGMT_CAP_SMP_MAX_ENC_KEY_SIZE 0x03
+#define MGMT_CAP_LE_TX_PWR 0x04
+
+#define MGMT_OP_READ_CONTROLLER_CAP 0x0048
+#define MGMT_READ_CONTROLLER_CAP_SIZE 0
+struct mgmt_rp_read_controller_cap {
+ uint16_t cap_len;
+ uint8_t cap[0];
} __packed;
#define MGMT_OP_READ_EXP_FEATURES_INFO 0x0049
@@ -9644,6 +9644,10 @@ static void read_commands_complete(uint8_t status, uint16_t length,
DBG("kernel supports ext adv commands");
kernel_features |= KERNEL_HAS_EXT_ADV_ADD_CMDS;
break;
+ case MGMT_OP_READ_CONTROLLER_CAP:
+ DBG("kernel supports controller cap command");
+ kernel_features |= KERNEL_HAS_CONTROLLER_CAP_CMD;
+ break;
default:
break;
}
@@ -234,6 +234,7 @@ enum kernel_features {
KERNEL_EXP_FEATURES = 1 << 3,
KERNEL_HAS_RESUME_EVT = 1 << 4,
KERNEL_HAS_EXT_ADV_ADD_CMDS = 1 << 5,
+ KERNEL_HAS_CONTROLLER_CAP_CMD = 1 << 6,
};
bool btd_has_kernel_features(uint32_t feature);
@@ -49,6 +49,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
@@ -1701,6 +1703,49 @@ static void read_adv_features_callback(uint8_t status, uint16_t length,
remove_advertising(manager, 0);
}
+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 struct mgmt_rp_read_controller_cap *rp = param;
+ const uint8_t *ptr = rp->cap;
+ size_t offset = 0;
+ uint8_t tag_len;
+ uint8_t tag_type;
+
+ if (status || !param) {
+ error("Failed to read advertising features: %s (0x%02x)",
+ mgmt_errstr(status), status);
+ return;
+ }
+
+ if (sizeof(rp->cap_len) + rp->cap_len != length) {
+ error("Controller capabilities malformed, size %lu != %u",
+ sizeof(rp->cap_len) + rp->cap_len, length);
+ return;
+ }
+
+ while (offset < rp->cap_len) {
+ tag_len = ptr[offset++];
+ tag_type = ptr[offset++];
+
+ switch (tag_type) {
+ case MGMT_CAP_LE_TX_PWR:
+ if ((tag_len - sizeof(tag_type)) !=
+ 2*sizeof(manager->min_tx_power)) {
+ error("TX power had unexpected length %d",
+ tag_len);
+ break;
+ }
+ memcpy(&manager->min_tx_power, &ptr[offset], tag_len);
+ memcpy(&manager->max_tx_power, &ptr[offset+1], tag_len);
+ }
+
+ /* Step to the next entry */
+ offset += (tag_len - sizeof(tag_type));
+ }
+}
+
static struct btd_adv_manager *manager_create(struct btd_adapter *adapter,
struct mgmt *mgmt)
{
@@ -1722,6 +1767,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),
@@ -1738,6 +1785,15 @@ static struct btd_adv_manager *manager_create(struct btd_adapter *adapter,
goto fail;
}
+ /* Query controller capabilities. This will be used to display valid
+ * advertising tx power range to the client.
+ */
+ if (g_dbus_get_flags() & G_DBUS_FLAG_ENABLE_EXPERIMENTAL &&
+ btd_has_kernel_features(KERNEL_HAS_CONTROLLER_CAP_CMD))
+ mgmt_send(manager->mgmt, MGMT_OP_READ_CONTROLLER_CAP,
+ manager->mgmt_index, 0, NULL,
+ read_controller_cap_complete, manager, NULL);
+
return manager;
fail:
@@ -1518,7 +1518,7 @@ static void cmd_extinfo(int argc, char **argv)
static void sec_info_rsp(uint8_t status, uint16_t len, const void *param,
void *user_data)
{
- const struct mgmt_rp_read_security_info *rp = param;
+ const struct mgmt_rp_read_controller_cap *rp = param;
uint16_t index = PTR_TO_UINT(user_data);
if (status != 0) {
@@ -1533,7 +1533,7 @@ static void sec_info_rsp(uint8_t status, uint16_t len, const void *param,
}
print("Primary controller (hci%u)", index);
- print("\tSecurity info length: %u", le16_to_cpu(rp->sec_len));
+ print("\tSecurity info length: %u", le16_to_cpu(rp->cap_len));
done:
pending_index--;
@@ -1576,11 +1576,11 @@ static void sec_index_rsp(uint8_t status, uint16_t len, const void *param,
if (rp->entry[i].type != 0x00)
continue;
- if (!mgmt_send(mgmt, MGMT_OP_READ_SECURITY_INFO,
+ if (!mgmt_send(mgmt, MGMT_OP_READ_CONTROLLER_CAP,
index, 0, NULL, sec_info_rsp,
UINT_TO_PTR(index), NULL)) {
- error("Unable to send read_security_info cmd");
- return bt_shell_noninteractive_quit(EXIT_FAILURE);
+ error("Unable to send read_security_info cmd");
+ return bt_shell_noninteractive_quit(EXIT_FAILURE);
}
pending_index++;
}
@@ -1602,7 +1602,7 @@ static void cmd_secinfo(int argc, char **argv)
return;
}
- if (!mgmt_send(mgmt, MGMT_OP_READ_SECURITY_INFO, mgmt_index, 0, NULL,
+ if (!mgmt_send(mgmt, MGMT_OP_READ_CONTROLLER_CAP, mgmt_index, 0, NULL,
sec_info_rsp,
UINT_TO_PTR(mgmt_index), NULL)) {
error("Unable to send read_security_info cmd");