@@ -278,6 +278,7 @@ enum {
HCI_LE_ENABLED,
HCI_ADVERTISING,
HCI_ADVERTISING_CONNECTABLE,
+ HCI_ADVERTISING_INTERVALS,
HCI_CONNECTABLE,
HCI_DISCOVERABLE,
HCI_LIMITED_DISCOVERABLE,
@@ -220,6 +220,17 @@ struct adv_info {
#define HCI_MAX_ADV_INSTANCES 5
#define HCI_DEFAULT_ADV_DURATION 2
+/*
+ * Refer to BLUETOOTH SPECIFICATION Version 5.2 [Vol 4, Part E]
+ * Section 7.8.5 about
+ * - the default min/max intervals, and
+ * - the valid range of min/max intervals.
+ */
+#define HCI_DEFAULT_LE_ADV_MIN_INTERVAL 0x0800
+#define HCI_DEFAULT_LE_ADV_MAX_INTERVAL 0x0800
+#define HCI_VALID_LE_ADV_MIN_INTERVAL 0x0020
+#define HCI_VALID_LE_ADV_MAX_INTERVAL 0x4000
+
#define HCI_MAX_SHORT_NAME_LENGTH 10
/* Min encryption key size to match with SMP */
@@ -103,6 +103,7 @@ struct mgmt_rp_read_index_list {
#define MGMT_SETTING_STATIC_ADDRESS 0x00008000
#define MGMT_SETTING_PHY_CONFIGURATION 0x00010000
#define MGMT_SETTING_WIDEBAND_SPEECH 0x00020000
+#define MGMT_SETTING_ADVERTISING_INTERVALS 0x00040000
#define MGMT_OP_READ_INFO 0x0004
#define MGMT_READ_INFO_SIZE 0
@@ -674,6 +675,13 @@ struct mgmt_cp_set_blocked_keys {
#define MGMT_OP_SET_WIDEBAND_SPEECH 0x0047
+#define MGMT_OP_SET_ADVERTISING_INTERVALS 0x0048
+struct mgmt_cp_set_advertising_intervals {
+ __le16 min_interval;
+ __le16 max_interval;
+} __packed;
+#define MGMT_SET_ADVERTISING_INTERVALS_SIZE 4
+
#define MGMT_EV_CMD_COMPLETE 0x0001
struct mgmt_ev_cmd_complete {
__le16 opcode;
@@ -3382,8 +3382,8 @@ struct hci_dev *hci_alloc_dev(void)
hdev->sniff_min_interval = 80;
hdev->le_adv_channel_map = 0x07;
- hdev->le_adv_min_interval = 0x0800;
- hdev->le_adv_max_interval = 0x0800;
+ hdev->le_adv_min_interval = HCI_DEFAULT_LE_ADV_MIN_INTERVAL;
+ hdev->le_adv_max_interval = HCI_DEFAULT_LE_ADV_MAX_INTERVAL;
hdev->le_scan_interval = 0x0060;
hdev->le_scan_window = 0x0030;
hdev->le_conn_min_interval = 0x0018;
@@ -108,6 +108,7 @@ static const u16 mgmt_commands[] = {
MGMT_OP_SET_APPEARANCE,
MGMT_OP_SET_BLOCKED_KEYS,
MGMT_OP_SET_WIDEBAND_SPEECH,
+ MGMT_OP_SET_ADVERTISING_INTERVALS,
};
static const u16 mgmt_events[] = {
@@ -775,6 +776,7 @@ static u32 get_supported_settings(struct hci_dev *hdev)
settings |= MGMT_SETTING_SECURE_CONN;
settings |= MGMT_SETTING_PRIVACY;
settings |= MGMT_SETTING_STATIC_ADDRESS;
+ settings |= MGMT_SETTING_ADVERTISING_INTERVALS;
}
if (test_bit(HCI_QUIRK_EXTERNAL_CONFIG, &hdev->quirks) ||
@@ -854,6 +856,9 @@ static u32 get_current_settings(struct hci_dev *hdev)
if (hci_dev_test_flag(hdev, HCI_WIDEBAND_SPEECH_ENABLED))
settings |= MGMT_SETTING_WIDEBAND_SPEECH;
+ if (hci_dev_test_flag(hdev, HCI_ADVERTISING_INTERVALS))
+ settings |= MGMT_SETTING_ADVERTISING_INTERVALS;
+
return settings;
}
@@ -4768,6 +4773,147 @@ static int set_fast_connectable(struct sock *sk, struct hci_dev *hdev,
return err;
}
+static void set_advertising_intervals_complete(struct hci_dev *hdev,
+ u8 status, u16 opcode)
+{
+ struct cmd_lookup match = { NULL, hdev };
+ struct hci_request req;
+ u8 instance;
+ struct adv_info *adv_instance;
+ int err;
+
+ hci_dev_lock(hdev);
+
+ if (status) {
+ u8 mgmt_err = mgmt_status(status);
+
+ mgmt_pending_foreach(MGMT_OP_SET_ADVERTISING_INTERVALS, hdev,
+ cmd_status_rsp, &mgmt_err);
+ goto unlock;
+ }
+
+ if (hci_dev_test_flag(hdev, HCI_LE_ADV))
+ hci_dev_set_flag(hdev, HCI_ADVERTISING);
+ else
+ hci_dev_clear_flag(hdev, HCI_ADVERTISING);
+
+ mgmt_pending_foreach(MGMT_OP_SET_ADVERTISING_INTERVALS, hdev,
+ settings_rsp, &match);
+
+ new_settings(hdev, match.sk);
+
+ if (match.sk)
+ sock_put(match.sk);
+
+ /* If "Set Advertising" was just disabled and instance advertising was
+ * set up earlier, then re-enable multi-instance advertising.
+ */
+ if (hci_dev_test_flag(hdev, HCI_ADVERTISING) ||
+ list_empty(&hdev->adv_instances))
+ goto unlock;
+
+ instance = hdev->cur_adv_instance;
+ if (!instance) {
+ adv_instance = list_first_entry_or_null(&hdev->adv_instances,
+ struct adv_info, list);
+ if (!adv_instance)
+ goto unlock;
+
+ instance = adv_instance->instance;
+ }
+
+ hci_req_init(&req, hdev);
+
+ err = __hci_req_schedule_adv_instance(&req, instance, true);
+ if (!err)
+ err = hci_req_run(&req, enable_advertising_instance);
+ else
+ BT_ERR("Failed to re-configure advertising intervals");
+
+unlock:
+ hci_dev_unlock(hdev);
+}
+
+static int _reenable_advertising(struct sock *sk, struct hci_dev *hdev,
+ void *data, u16 len)
+{
+ struct mgmt_pending_cmd *cmd;
+ struct hci_request req;
+ int err;
+
+ if (pending_find(MGMT_OP_SET_ADVERTISING_INTERVALS, hdev)) {
+ return mgmt_cmd_status(sk, hdev->id,
+ MGMT_OP_SET_ADVERTISING_INTERVALS,
+ MGMT_STATUS_BUSY);
+ }
+
+ cmd = mgmt_pending_add(sk, MGMT_OP_SET_ADVERTISING_INTERVALS, hdev,
+ data, len);
+ if (!cmd)
+ return -ENOMEM;
+
+ hci_req_init(&req, hdev);
+ cancel_adv_timeout(hdev);
+
+ /* Switch to instance "0" for the Set Advertising setting.
+ * We cannot use update_[adv|scan_rsp]_data() here as the
+ * HCI_ADVERTISING flag is not yet set.
+ */
+ hdev->cur_adv_instance = 0x00;
+ /* This function disables advertising before enabling it. */
+ __hci_req_enable_advertising(&req);
+
+ err = hci_req_run(&req, set_advertising_intervals_complete);
+ if (err < 0)
+ mgmt_pending_remove(cmd);
+
+ return err;
+}
+
+static int set_advertising_intervals(struct sock *sk, struct hci_dev *hdev,
+ void *data, u16 len)
+{
+ struct mgmt_cp_set_advertising_intervals *cp = data;
+ int err;
+
+ BT_DBG("%s", hdev->name);
+
+ /* This method is intended for LE devices only.*/
+ if (!hci_dev_test_flag(hdev, HCI_LE_ENABLED))
+ return mgmt_cmd_status(sk, hdev->id,
+ MGMT_OP_SET_ADVERTISING_INTERVALS,
+ MGMT_STATUS_REJECTED);
+
+ /* Check the validity of the intervals. */
+ if (cp->min_interval < HCI_VALID_LE_ADV_MIN_INTERVAL ||
+ cp->max_interval > HCI_VALID_LE_ADV_MAX_INTERVAL ||
+ cp->min_interval > cp->max_interval) {
+ return mgmt_cmd_status(sk, hdev->id,
+ MGMT_OP_SET_ADVERTISING_INTERVALS,
+ MGMT_STATUS_INVALID_PARAMS);
+ }
+
+ hci_dev_lock(hdev);
+
+ hci_dev_set_flag(hdev, HCI_ADVERTISING_INTERVALS);
+ hdev->le_adv_min_interval = cp->min_interval;
+ hdev->le_adv_max_interval = cp->max_interval;
+
+ /* Re-enable advertising only when it is already on. */
+ if (hci_dev_test_flag(hdev, HCI_LE_ADV)) {
+ err = _reenable_advertising(sk, hdev, data, len);
+ goto unlock;
+ }
+
+ err = send_settings_rsp(sk, MGMT_OP_SET_ADVERTISING_INTERVALS, hdev);
+ new_settings(hdev, sk);
+
+unlock:
+ hci_dev_unlock(hdev);
+
+ return err;
+}
+
static void set_bredr_complete(struct hci_dev *hdev, u8 status, u16 opcode)
{
struct mgmt_pending_cmd *cmd;
@@ -7099,6 +7245,7 @@ static const struct hci_mgmt_handler mgmt_handlers[] = {
{ set_blocked_keys, MGMT_OP_SET_BLOCKED_KEYS_SIZE,
HCI_MGMT_VAR_LEN },
{ set_wideband_speech, MGMT_SETTING_SIZE },
+ { set_advertising_intervals, MGMT_SET_ADVERTISING_INTERVALS_SIZE },
};
void mgmt_index_added(struct hci_dev *hdev)