Merge tag 'docs-for-linus' of git://git.lwn.net/linux-2.6
[firefly-linux-kernel-4.4.55.git] / net / bluetooth / hci_debugfs.c
index 435f091301cd254e384c224899dbfb7da23b846e..65261e5d4b84bbbdc1da1d11083b9f32b6233d7a 100644 (file)
@@ -156,6 +156,35 @@ static const struct file_operations uuids_fops = {
        .release        = single_release,
 };
 
+static int remote_oob_show(struct seq_file *f, void *ptr)
+{
+       struct hci_dev *hdev = f->private;
+       struct oob_data *data;
+
+       hci_dev_lock(hdev);
+       list_for_each_entry(data, &hdev->remote_oob_data, list) {
+               seq_printf(f, "%pMR (type %u) %u %*phN %*phN %*phN %*phN\n",
+                          &data->bdaddr, data->bdaddr_type, data->present,
+                          16, data->hash192, 16, data->rand192,
+                          16, data->hash256, 19, data->rand256);
+       }
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int remote_oob_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, remote_oob_show, inode->i_private);
+}
+
+static const struct file_operations remote_oob_fops = {
+       .open           = remote_oob_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
 static int conn_info_min_age_set(void *data, u64 val)
 {
        struct hci_dev *hdev = data;
@@ -212,6 +241,42 @@ static int conn_info_max_age_get(void *data, u64 *val)
 DEFINE_SIMPLE_ATTRIBUTE(conn_info_max_age_fops, conn_info_max_age_get,
                        conn_info_max_age_set, "%llu\n");
 
+static ssize_t use_debug_keys_read(struct file *file, char __user *user_buf,
+                                  size_t count, loff_t *ppos)
+{
+       struct hci_dev *hdev = file->private_data;
+       char buf[3];
+
+       buf[0] = test_bit(HCI_USE_DEBUG_KEYS, &hdev->dev_flags) ? 'Y': 'N';
+       buf[1] = '\n';
+       buf[2] = '\0';
+       return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+}
+
+static const struct file_operations use_debug_keys_fops = {
+       .open           = simple_open,
+       .read           = use_debug_keys_read,
+       .llseek         = default_llseek,
+};
+
+static ssize_t sc_only_mode_read(struct file *file, char __user *user_buf,
+                                size_t count, loff_t *ppos)
+{
+       struct hci_dev *hdev = file->private_data;
+       char buf[3];
+
+       buf[0] = test_bit(HCI_SC_ONLY, &hdev->dev_flags) ? 'Y': 'N';
+       buf[1] = '\n';
+       buf[2] = '\0';
+       return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+}
+
+static const struct file_operations sc_only_mode_fops = {
+       .open           = simple_open,
+       .read           = sc_only_mode_read,
+       .llseek         = default_llseek,
+};
+
 void hci_debugfs_create_common(struct hci_dev *hdev)
 {
        debugfs_create_file("features", 0444, hdev->debugfs, hdev,
@@ -220,16 +285,29 @@ void hci_debugfs_create_common(struct hci_dev *hdev)
                           &hdev->manufacturer);
        debugfs_create_u8("hci_version", 0444, hdev->debugfs, &hdev->hci_ver);
        debugfs_create_u16("hci_revision", 0444, hdev->debugfs, &hdev->hci_rev);
+       debugfs_create_u8("hardware_error", 0444, hdev->debugfs,
+                         &hdev->hw_error_code);
+
        debugfs_create_file("device_list", 0444, hdev->debugfs, hdev,
                            &device_list_fops);
        debugfs_create_file("blacklist", 0444, hdev->debugfs, hdev,
                            &blacklist_fops);
        debugfs_create_file("uuids", 0444, hdev->debugfs, hdev, &uuids_fops);
+       debugfs_create_file("remote_oob", 0400, hdev->debugfs, hdev,
+                           &remote_oob_fops);
 
        debugfs_create_file("conn_info_min_age", 0644, hdev->debugfs, hdev,
                            &conn_info_min_age_fops);
        debugfs_create_file("conn_info_max_age", 0644, hdev->debugfs, hdev,
                            &conn_info_max_age_fops);
+
+       if (lmp_ssp_capable(hdev) || lmp_le_capable(hdev))
+               debugfs_create_file("use_debug_keys", 0444, hdev->debugfs,
+                                   hdev, &use_debug_keys_fops);
+
+       if (lmp_sc_capable(hdev) || lmp_le_capable(hdev))
+               debugfs_create_file("sc_only_mode", 0444, hdev->debugfs,
+                                   hdev, &sc_only_mode_fops);
 }
 
 static int inquiry_cache_show(struct seq_file *f, void *p)
@@ -332,6 +410,24 @@ static int voice_setting_get(void *data, u64 *val)
 DEFINE_SIMPLE_ATTRIBUTE(voice_setting_fops, voice_setting_get,
                        NULL, "0x%4.4llx\n");
 
+static ssize_t ssp_debug_mode_read(struct file *file, char __user *user_buf,
+                                  size_t count, loff_t *ppos)
+{
+       struct hci_dev *hdev = file->private_data;
+       char buf[3];
+
+       buf[0] = hdev->ssp_debug_mode ? 'Y': 'N';
+       buf[1] = '\n';
+       buf[2] = '\0';
+       return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+}
+
+static const struct file_operations ssp_debug_mode_fops = {
+       .open           = simple_open,
+       .read           = ssp_debug_mode_read,
+       .llseek         = default_llseek,
+};
+
 static int auto_accept_delay_set(void *data, u64 val)
 {
        struct hci_dev *hdev = data;
@@ -357,39 +453,241 @@ static int auto_accept_delay_get(void *data, u64 *val)
 DEFINE_SIMPLE_ATTRIBUTE(auto_accept_delay_fops, auto_accept_delay_get,
                        auto_accept_delay_set, "%llu\n");
 
-static ssize_t sc_only_mode_read(struct file *file, char __user *user_buf,
-                                size_t count, loff_t *ppos)
+static int idle_timeout_set(void *data, u64 val)
 {
-       struct hci_dev *hdev = file->private_data;
-       char buf[3];
+       struct hci_dev *hdev = data;
 
-       buf[0] = test_bit(HCI_SC_ONLY, &hdev->dev_flags) ? 'Y': 'N';
-       buf[1] = '\n';
-       buf[2] = '\0';
-       return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+       if (val != 0 && (val < 500 || val > 3600000))
+               return -EINVAL;
+
+       hci_dev_lock(hdev);
+       hdev->idle_timeout = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
 }
 
-static const struct file_operations sc_only_mode_fops = {
-       .open           = simple_open,
-       .read           = sc_only_mode_read,
-       .llseek         = default_llseek,
+static int idle_timeout_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->idle_timeout;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(idle_timeout_fops, idle_timeout_get,
+                       idle_timeout_set, "%llu\n");
+
+static int sniff_min_interval_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+
+       if (val == 0 || val % 2 || val > hdev->sniff_max_interval)
+               return -EINVAL;
+
+       hci_dev_lock(hdev);
+       hdev->sniff_min_interval = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int sniff_min_interval_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->sniff_min_interval;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(sniff_min_interval_fops, sniff_min_interval_get,
+                       sniff_min_interval_set, "%llu\n");
+
+static int sniff_max_interval_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+
+       if (val == 0 || val % 2 || val < hdev->sniff_min_interval)
+               return -EINVAL;
+
+       hci_dev_lock(hdev);
+       hdev->sniff_max_interval = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int sniff_max_interval_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->sniff_max_interval;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(sniff_max_interval_fops, sniff_max_interval_get,
+                       sniff_max_interval_set, "%llu\n");
+
+void hci_debugfs_create_bredr(struct hci_dev *hdev)
+{
+       debugfs_create_file("inquiry_cache", 0444, hdev->debugfs, hdev,
+                           &inquiry_cache_fops);
+       debugfs_create_file("link_keys", 0400, hdev->debugfs, hdev,
+                           &link_keys_fops);
+       debugfs_create_file("dev_class", 0444, hdev->debugfs, hdev,
+                           &dev_class_fops);
+       debugfs_create_file("voice_setting", 0444, hdev->debugfs, hdev,
+                           &voice_setting_fops);
+
+       if (lmp_ssp_capable(hdev)) {
+               debugfs_create_file("ssp_debug_mode", 0444, hdev->debugfs,
+                                   hdev, &ssp_debug_mode_fops);
+               debugfs_create_file("auto_accept_delay", 0644, hdev->debugfs,
+                                   hdev, &auto_accept_delay_fops);
+       }
+
+       if (lmp_sniff_capable(hdev)) {
+               debugfs_create_file("idle_timeout", 0644, hdev->debugfs,
+                                   hdev, &idle_timeout_fops);
+               debugfs_create_file("sniff_min_interval", 0644, hdev->debugfs,
+                                   hdev, &sniff_min_interval_fops);
+               debugfs_create_file("sniff_max_interval", 0644, hdev->debugfs,
+                                   hdev, &sniff_max_interval_fops);
+       }
+}
+
+static int identity_show(struct seq_file *f, void *p)
+{
+       struct hci_dev *hdev = f->private;
+       bdaddr_t addr;
+       u8 addr_type;
+
+       hci_dev_lock(hdev);
+
+       hci_copy_identity_address(hdev, &addr, &addr_type);
+
+       seq_printf(f, "%pMR (type %u) %*phN %pMR\n", &addr, addr_type,
+                  16, hdev->irk, &hdev->rpa);
+
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int identity_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, identity_show, inode->i_private);
+}
+
+static const struct file_operations identity_fops = {
+       .open           = identity_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int rpa_timeout_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+
+       /* Require the RPA timeout to be at least 30 seconds and at most
+        * 24 hours.
+        */
+       if (val < 30 || val > (60 * 60 * 24))
+               return -EINVAL;
+
+       hci_dev_lock(hdev);
+       hdev->rpa_timeout = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int rpa_timeout_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->rpa_timeout;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(rpa_timeout_fops, rpa_timeout_get,
+                       rpa_timeout_set, "%llu\n");
+
+static int random_address_show(struct seq_file *f, void *p)
+{
+       struct hci_dev *hdev = f->private;
+
+       hci_dev_lock(hdev);
+       seq_printf(f, "%pMR\n", &hdev->random_addr);
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int random_address_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, random_address_show, inode->i_private);
+}
+
+static const struct file_operations random_address_fops = {
+       .open           = random_address_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int static_address_show(struct seq_file *f, void *p)
+{
+       struct hci_dev *hdev = f->private;
+
+       hci_dev_lock(hdev);
+       seq_printf(f, "%pMR\n", &hdev->static_addr);
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int static_address_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, static_address_show, inode->i_private);
+}
+
+static const struct file_operations static_address_fops = {
+       .open           = static_address_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
 };
 
-static ssize_t force_sc_support_read(struct file *file, char __user *user_buf,
-                                    size_t count, loff_t *ppos)
+static ssize_t force_static_address_read(struct file *file,
+                                        char __user *user_buf,
+                                        size_t count, loff_t *ppos)
 {
        struct hci_dev *hdev = file->private_data;
        char buf[3];
 
-       buf[0] = test_bit(HCI_FORCE_SC, &hdev->dbg_flags) ? 'Y': 'N';
+       buf[0] = test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags) ? 'Y': 'N';
        buf[1] = '\n';
        buf[2] = '\0';
        return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
 }
 
-static ssize_t force_sc_support_write(struct file *file,
-                                     const char __user *user_buf,
-                                     size_t count, loff_t *ppos)
+static ssize_t force_static_address_write(struct file *file,
+                                         const char __user *user_buf,
+                                         size_t count, loff_t *ppos)
 {
        struct hci_dev *hdev = file->private_data;
        char buf[32];
@@ -406,185 +704,353 @@ static ssize_t force_sc_support_write(struct file *file,
        if (strtobool(buf, &enable))
                return -EINVAL;
 
-       if (enable == test_bit(HCI_FORCE_SC, &hdev->dbg_flags))
+       if (enable == test_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags))
                return -EALREADY;
 
-       change_bit(HCI_FORCE_SC, &hdev->dbg_flags);
+       change_bit(HCI_FORCE_STATIC_ADDR, &hdev->dbg_flags);
 
        return count;
 }
 
-static const struct file_operations force_sc_support_fops = {
+static const struct file_operations force_static_address_fops = {
        .open           = simple_open,
-       .read           = force_sc_support_read,
-       .write          = force_sc_support_write,
+       .read           = force_static_address_read,
+       .write          = force_static_address_write,
        .llseek         = default_llseek,
 };
 
-static ssize_t force_lesc_support_read(struct file *file,
-                                      char __user *user_buf,
-                                      size_t count, loff_t *ppos)
+static int white_list_show(struct seq_file *f, void *ptr)
 {
-       struct hci_dev *hdev = file->private_data;
-       char buf[3];
+       struct hci_dev *hdev = f->private;
+       struct bdaddr_list *b;
 
-       buf[0] = test_bit(HCI_FORCE_LESC, &hdev->dbg_flags) ? 'Y': 'N';
-       buf[1] = '\n';
-       buf[2] = '\0';
-       return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+       hci_dev_lock(hdev);
+       list_for_each_entry(b, &hdev->le_white_list, list)
+               seq_printf(f, "%pMR (type %u)\n", &b->bdaddr, b->bdaddr_type);
+       hci_dev_unlock(hdev);
+
+       return 0;
 }
 
-static ssize_t force_lesc_support_write(struct file *file,
-                                       const char __user *user_buf,
-                                       size_t count, loff_t *ppos)
+static int white_list_open(struct inode *inode, struct file *file)
 {
-       struct hci_dev *hdev = file->private_data;
-       char buf[32];
-       size_t buf_size = min(count, (sizeof(buf)-1));
-       bool enable;
+       return single_open(file, white_list_show, inode->i_private);
+}
 
-       if (copy_from_user(buf, user_buf, buf_size))
-               return -EFAULT;
+static const struct file_operations white_list_fops = {
+       .open           = white_list_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
 
-       buf[buf_size] = '\0';
-       if (strtobool(buf, &enable))
-               return -EINVAL;
+static int identity_resolving_keys_show(struct seq_file *f, void *ptr)
+{
+       struct hci_dev *hdev = f->private;
+       struct smp_irk *irk;
 
-       if (enable == test_bit(HCI_FORCE_LESC, &hdev->dbg_flags))
-               return -EALREADY;
+       rcu_read_lock();
+       list_for_each_entry_rcu(irk, &hdev->identity_resolving_keys, list) {
+               seq_printf(f, "%pMR (type %u) %*phN %pMR\n",
+                          &irk->bdaddr, irk->addr_type,
+                          16, irk->val, &irk->rpa);
+       }
+       rcu_read_unlock();
 
-       change_bit(HCI_FORCE_LESC, &hdev->dbg_flags);
+       return 0;
+}
 
-       return count;
+static int identity_resolving_keys_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, identity_resolving_keys_show,
+                          inode->i_private);
 }
 
-static const struct file_operations force_lesc_support_fops = {
-       .open           = simple_open,
-       .read           = force_lesc_support_read,
-       .write          = force_lesc_support_write,
-       .llseek         = default_llseek,
+static const struct file_operations identity_resolving_keys_fops = {
+       .open           = identity_resolving_keys_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
 };
 
-static int idle_timeout_set(void *data, u64 val)
+static int long_term_keys_show(struct seq_file *f, void *ptr)
+{
+       struct hci_dev *hdev = f->private;
+       struct smp_ltk *ltk;
+
+       rcu_read_lock();
+       list_for_each_entry_rcu(ltk, &hdev->long_term_keys, list)
+               seq_printf(f, "%pMR (type %u) %u 0x%02x %u %.4x %.16llx %*phN\n",
+                          &ltk->bdaddr, ltk->bdaddr_type, ltk->authenticated,
+                          ltk->type, ltk->enc_size, __le16_to_cpu(ltk->ediv),
+                          __le64_to_cpu(ltk->rand), 16, ltk->val);
+       rcu_read_unlock();
+
+       return 0;
+}
+
+static int long_term_keys_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, long_term_keys_show, inode->i_private);
+}
+
+static const struct file_operations long_term_keys_fops = {
+       .open           = long_term_keys_open,
+       .read           = seq_read,
+       .llseek         = seq_lseek,
+       .release        = single_release,
+};
+
+static int conn_min_interval_set(void *data, u64 val)
 {
        struct hci_dev *hdev = data;
 
-       if (val != 0 && (val < 500 || val > 3600000))
+       if (val < 0x0006 || val > 0x0c80 || val > hdev->le_conn_max_interval)
                return -EINVAL;
 
        hci_dev_lock(hdev);
-       hdev->idle_timeout = val;
+       hdev->le_conn_min_interval = val;
        hci_dev_unlock(hdev);
 
        return 0;
 }
 
-static int idle_timeout_get(void *data, u64 *val)
+static int conn_min_interval_get(void *data, u64 *val)
 {
        struct hci_dev *hdev = data;
 
        hci_dev_lock(hdev);
-       *val = hdev->idle_timeout;
+       *val = hdev->le_conn_min_interval;
        hci_dev_unlock(hdev);
 
        return 0;
 }
 
-DEFINE_SIMPLE_ATTRIBUTE(idle_timeout_fops, idle_timeout_get,
-                       idle_timeout_set, "%llu\n");
+DEFINE_SIMPLE_ATTRIBUTE(conn_min_interval_fops, conn_min_interval_get,
+                       conn_min_interval_set, "%llu\n");
 
-static int sniff_min_interval_set(void *data, u64 val)
+static int conn_max_interval_set(void *data, u64 val)
 {
        struct hci_dev *hdev = data;
 
-       if (val == 0 || val % 2 || val > hdev->sniff_max_interval)
+       if (val < 0x0006 || val > 0x0c80 || val < hdev->le_conn_min_interval)
                return -EINVAL;
 
        hci_dev_lock(hdev);
-       hdev->sniff_min_interval = val;
+       hdev->le_conn_max_interval = val;
        hci_dev_unlock(hdev);
 
        return 0;
 }
 
-static int sniff_min_interval_get(void *data, u64 *val)
+static int conn_max_interval_get(void *data, u64 *val)
 {
        struct hci_dev *hdev = data;
 
        hci_dev_lock(hdev);
-       *val = hdev->sniff_min_interval;
+       *val = hdev->le_conn_max_interval;
        hci_dev_unlock(hdev);
 
        return 0;
 }
 
-DEFINE_SIMPLE_ATTRIBUTE(sniff_min_interval_fops, sniff_min_interval_get,
-                       sniff_min_interval_set, "%llu\n");
+DEFINE_SIMPLE_ATTRIBUTE(conn_max_interval_fops, conn_max_interval_get,
+                       conn_max_interval_set, "%llu\n");
 
-static int sniff_max_interval_set(void *data, u64 val)
+static int conn_latency_set(void *data, u64 val)
 {
        struct hci_dev *hdev = data;
 
-       if (val == 0 || val % 2 || val < hdev->sniff_min_interval)
+       if (val > 0x01f3)
                return -EINVAL;
 
        hci_dev_lock(hdev);
-       hdev->sniff_max_interval = val;
+       hdev->le_conn_latency = val;
        hci_dev_unlock(hdev);
 
        return 0;
 }
 
-static int sniff_max_interval_get(void *data, u64 *val)
+static int conn_latency_get(void *data, u64 *val)
 {
        struct hci_dev *hdev = data;
 
        hci_dev_lock(hdev);
-       *val = hdev->sniff_max_interval;
+       *val = hdev->le_conn_latency;
        hci_dev_unlock(hdev);
 
        return 0;
 }
 
-DEFINE_SIMPLE_ATTRIBUTE(sniff_max_interval_fops, sniff_max_interval_get,
-                       sniff_max_interval_set, "%llu\n");
+DEFINE_SIMPLE_ATTRIBUTE(conn_latency_fops, conn_latency_get,
+                       conn_latency_set, "%llu\n");
 
-void hci_debugfs_create_bredr(struct hci_dev *hdev)
+static int supervision_timeout_set(void *data, u64 val)
 {
-       debugfs_create_file("inquiry_cache", 0444, hdev->debugfs, hdev,
-                           &inquiry_cache_fops);
-       debugfs_create_file("link_keys", 0400, hdev->debugfs, hdev,
-                           &link_keys_fops);
-       debugfs_create_file("dev_class", 0444, hdev->debugfs, hdev,
-                           &dev_class_fops);
-       debugfs_create_file("voice_setting", 0444, hdev->debugfs, hdev,
-                           &voice_setting_fops);
+       struct hci_dev *hdev = data;
 
-       if (lmp_ssp_capable(hdev)) {
-               debugfs_create_file("auto_accept_delay", 0644, hdev->debugfs,
-                                   hdev, &auto_accept_delay_fops);
-               debugfs_create_file("sc_only_mode", 0444, hdev->debugfs,
-                                   hdev, &sc_only_mode_fops);
+       if (val < 0x000a || val > 0x0c80)
+               return -EINVAL;
 
-               debugfs_create_file("force_sc_support", 0644, hdev->debugfs,
-                                   hdev, &force_sc_support_fops);
+       hci_dev_lock(hdev);
+       hdev->le_supv_timeout = val;
+       hci_dev_unlock(hdev);
 
-               if (lmp_le_capable(hdev))
-                       debugfs_create_file("force_lesc_support", 0644,
-                                           hdev->debugfs, hdev,
-                                           &force_lesc_support_fops);
-       }
+       return 0;
+}
 
-       if (lmp_sniff_capable(hdev)) {
-               debugfs_create_file("idle_timeout", 0644, hdev->debugfs,
-                                   hdev, &idle_timeout_fops);
-               debugfs_create_file("sniff_min_interval", 0644, hdev->debugfs,
-                                   hdev, &sniff_min_interval_fops);
-               debugfs_create_file("sniff_max_interval", 0644, hdev->debugfs,
-                                   hdev, &sniff_max_interval_fops);
-       }
+static int supervision_timeout_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->le_supv_timeout;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(supervision_timeout_fops, supervision_timeout_get,
+                       supervision_timeout_set, "%llu\n");
+
+static int adv_channel_map_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+
+       if (val < 0x01 || val > 0x07)
+               return -EINVAL;
+
+       hci_dev_lock(hdev);
+       hdev->le_adv_channel_map = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int adv_channel_map_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->le_adv_channel_map;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(adv_channel_map_fops, adv_channel_map_get,
+                       adv_channel_map_set, "%llu\n");
+
+static int adv_min_interval_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+
+       if (val < 0x0020 || val > 0x4000 || val > hdev->le_adv_max_interval)
+               return -EINVAL;
+
+       hci_dev_lock(hdev);
+       hdev->le_adv_min_interval = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int adv_min_interval_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->le_adv_min_interval;
+       hci_dev_unlock(hdev);
+
+       return 0;
 }
 
+DEFINE_SIMPLE_ATTRIBUTE(adv_min_interval_fops, adv_min_interval_get,
+                       adv_min_interval_set, "%llu\n");
+
+static int adv_max_interval_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+
+       if (val < 0x0020 || val > 0x4000 || val < hdev->le_adv_min_interval)
+               return -EINVAL;
+
+       hci_dev_lock(hdev);
+       hdev->le_adv_max_interval = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int adv_max_interval_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->le_adv_max_interval;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(adv_max_interval_fops, adv_max_interval_get,
+                       adv_max_interval_set, "%llu\n");
+
 void hci_debugfs_create_le(struct hci_dev *hdev)
 {
+       debugfs_create_file("identity", 0400, hdev->debugfs, hdev,
+                           &identity_fops);
+       debugfs_create_file("rpa_timeout", 0644, hdev->debugfs, hdev,
+                           &rpa_timeout_fops);
+       debugfs_create_file("random_address", 0444, hdev->debugfs, hdev,
+                           &random_address_fops);
+       debugfs_create_file("static_address", 0444, hdev->debugfs, hdev,
+                           &static_address_fops);
+
+       /* For controllers with a public address, provide a debug
+        * option to force the usage of the configured static
+        * address. By default the public address is used.
+        */
+       if (bacmp(&hdev->bdaddr, BDADDR_ANY))
+               debugfs_create_file("force_static_address", 0644,
+                                   hdev->debugfs, hdev,
+                                   &force_static_address_fops);
+
+       debugfs_create_u8("white_list_size", 0444, hdev->debugfs,
+                         &hdev->le_white_list_size);
+       debugfs_create_file("white_list", 0444, hdev->debugfs, hdev,
+                           &white_list_fops);
+       debugfs_create_file("identity_resolving_keys", 0400, hdev->debugfs,
+                           hdev, &identity_resolving_keys_fops);
+       debugfs_create_file("long_term_keys", 0400, hdev->debugfs, hdev,
+                           &long_term_keys_fops);
+       debugfs_create_file("conn_min_interval", 0644, hdev->debugfs, hdev,
+                           &conn_min_interval_fops);
+       debugfs_create_file("conn_max_interval", 0644, hdev->debugfs, hdev,
+                           &conn_max_interval_fops);
+       debugfs_create_file("conn_latency", 0644, hdev->debugfs, hdev,
+                           &conn_latency_fops);
+       debugfs_create_file("supervision_timeout", 0644, hdev->debugfs, hdev,
+                           &supervision_timeout_fops);
+       debugfs_create_file("adv_channel_map", 0644, hdev->debugfs, hdev,
+                           &adv_channel_map_fops);
+       debugfs_create_file("adv_min_interval", 0644, hdev->debugfs, hdev,
+                           &adv_min_interval_fops);
+       debugfs_create_file("adv_max_interval", 0644, hdev->debugfs, hdev,
+                           &adv_max_interval_fops);
+       debugfs_create_u16("discov_interleaved_timeout", 0644, hdev->debugfs,
+                          &hdev->discov_interleaved_timeout);
+}
+
+void hci_debugfs_create_conn(struct hci_conn *conn)
+{
+       struct hci_dev *hdev = conn->hdev;
+       char name[6];
+
+       if (IS_ERR_OR_NULL(hdev->debugfs))
+               return;
+
+       snprintf(name, sizeof(name), "%u", conn->handle);
+       conn->debugfs = debugfs_create_dir(name, hdev->debugfs);
 }