]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - net/bluetooth/hci_core.c
Merge remote-tracking branch 'block/for-next'
[karo-tx-linux.git] / net / bluetooth / hci_core.c
index 384b9db16f694a5e1613436150190bffee8f6f6d..6ccc4eb9e55e4958f3eb070894a7668b05c98b5b 100644 (file)
@@ -58,6 +58,71 @@ static void hci_notify(struct hci_dev *hdev, int event)
 
 /* ---- HCI debugfs entries ---- */
 
+static ssize_t dut_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_DUT_MODE, &hdev->dev_flags) ? 'Y': 'N';
+       buf[1] = '\n';
+       buf[2] = '\0';
+       return simple_read_from_buffer(user_buf, count, ppos, buf, 2);
+}
+
+static ssize_t dut_mode_write(struct file *file, const char __user *user_buf,
+                             size_t count, loff_t *ppos)
+{
+       struct hci_dev *hdev = file->private_data;
+       struct sk_buff *skb;
+       char buf[32];
+       size_t buf_size = min(count, (sizeof(buf)-1));
+       bool enable;
+       int err;
+
+       if (!test_bit(HCI_UP, &hdev->flags))
+               return -ENETDOWN;
+
+       if (copy_from_user(buf, user_buf, buf_size))
+               return -EFAULT;
+
+       buf[buf_size] = '\0';
+       if (strtobool(buf, &enable))
+               return -EINVAL;
+
+       if (enable == test_bit(HCI_DUT_MODE, &hdev->dev_flags))
+               return -EALREADY;
+
+       hci_req_lock(hdev);
+       if (enable)
+               skb = __hci_cmd_sync(hdev, HCI_OP_ENABLE_DUT_MODE, 0, NULL,
+                                    HCI_CMD_TIMEOUT);
+       else
+               skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL,
+                                    HCI_CMD_TIMEOUT);
+       hci_req_unlock(hdev);
+
+       if (IS_ERR(skb))
+               return PTR_ERR(skb);
+
+       err = -bt_to_errno(skb->data[0]);
+       kfree_skb(skb);
+
+       if (err < 0)
+               return err;
+
+       change_bit(HCI_DUT_MODE, &hdev->dev_flags);
+
+       return count;
+}
+
+static const struct file_operations dut_mode_fops = {
+       .open           = simple_open,
+       .read           = dut_mode_read,
+       .write          = dut_mode_write,
+       .llseek         = default_llseek,
+};
+
 static int features_show(struct seq_file *f, void *ptr)
 {
        struct hci_dev *hdev = f->private;
@@ -128,18 +193,16 @@ static int uuids_show(struct seq_file *f, void *p)
 
        hci_dev_lock(hdev);
        list_for_each_entry(uuid, &hdev->uuids, list) {
-               u32 data0, data5;
-               u16 data1, data2, data3, data4;
+               u8 i, val[16];
 
-               data5 = get_unaligned_le32(uuid);
-               data4 = get_unaligned_le16(uuid + 4);
-               data3 = get_unaligned_le16(uuid + 6);
-               data2 = get_unaligned_le16(uuid + 8);
-               data1 = get_unaligned_le16(uuid + 10);
-               data0 = get_unaligned_le32(uuid + 12);
+               /* The Bluetooth UUID values are stored in big endian,
+                * but with reversed byte order. So convert them into
+                * the right order for the %pUb modifier.
+                */
+               for (i = 0; i < 16; i++)
+                       val[i] = uuid->uuid[15 - i];
 
-               seq_printf(f, "%.8x-%.4x-%.4x-%.4x-%.4x%.8x\n",
-                          data0, data1, data2, data3, data4, data5);
+               seq_printf(f, "%pUb\n", val);
        }
        hci_dev_unlock(hdev);
 
@@ -303,6 +366,55 @@ 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 int ssp_debug_mode_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+       struct sk_buff *skb;
+       __u8 mode;
+       int err;
+
+       if (val != 0 && val != 1)
+               return -EINVAL;
+
+       if (!test_bit(HCI_UP, &hdev->flags))
+               return -ENETDOWN;
+
+       hci_req_lock(hdev);
+       mode = val;
+       skb = __hci_cmd_sync(hdev, HCI_OP_WRITE_SSP_DEBUG_MODE, sizeof(mode),
+                            &mode, HCI_CMD_TIMEOUT);
+       hci_req_unlock(hdev);
+
+       if (IS_ERR(skb))
+               return PTR_ERR(skb);
+
+       err = -bt_to_errno(skb->data[0]);
+       kfree_skb(skb);
+
+       if (err < 0)
+               return err;
+
+       hci_dev_lock(hdev);
+       hdev->ssp_debug_mode = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int ssp_debug_mode_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->ssp_debug_mode;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(ssp_debug_mode_fops, ssp_debug_mode_get,
+                       ssp_debug_mode_set, "%llu\n");
+
 static int idle_timeout_set(void *data, u64 val)
 {
        struct hci_dev *hdev = data;
@@ -311,7 +423,7 @@ static int idle_timeout_set(void *data, u64 val)
                return -EINVAL;
 
        hci_dev_lock(hdev);
-       hdev->idle_timeout= val;
+       hdev->idle_timeout = val;
        hci_dev_unlock(hdev);
 
        return 0;
@@ -339,7 +451,7 @@ static int sniff_min_interval_set(void *data, u64 val)
                return -EINVAL;
 
        hci_dev_lock(hdev);
-       hdev->sniff_min_interval= val;
+       hdev->sniff_min_interval = val;
        hci_dev_unlock(hdev);
 
        return 0;
@@ -367,7 +479,7 @@ static int sniff_max_interval_set(void *data, u64 val)
                return -EINVAL;
 
        hci_dev_lock(hdev);
-       hdev->sniff_max_interval= val;
+       hdev->sniff_max_interval = val;
        hci_dev_unlock(hdev);
 
        return 0;
@@ -468,6 +580,62 @@ static const struct file_operations long_term_keys_fops = {
        .release        = single_release,
 };
 
+static int conn_min_interval_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+
+       if (val < 0x0006 || val > 0x0c80 || val > hdev->le_conn_max_interval)
+               return -EINVAL;
+
+       hci_dev_lock(hdev);
+       hdev->le_conn_min_interval = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int conn_min_interval_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->le_conn_min_interval;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(conn_min_interval_fops, conn_min_interval_get,
+                       conn_min_interval_set, "%llu\n");
+
+static int conn_max_interval_set(void *data, u64 val)
+{
+       struct hci_dev *hdev = data;
+
+       if (val < 0x0006 || val > 0x0c80 || val < hdev->le_conn_min_interval)
+               return -EINVAL;
+
+       hci_dev_lock(hdev);
+       hdev->le_conn_max_interval = val;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+static int conn_max_interval_get(void *data, u64 *val)
+{
+       struct hci_dev *hdev = data;
+
+       hci_dev_lock(hdev);
+       *val = hdev->le_conn_max_interval;
+       hci_dev_unlock(hdev);
+
+       return 0;
+}
+
+DEFINE_SIMPLE_ATTRIBUTE(conn_max_interval_fops, conn_max_interval_get,
+                       conn_max_interval_set, "%llu\n");
+
 /* ---- HCI requests ---- */
 
 static void hci_req_sync_complete(struct hci_dev *hdev, u8 result)
@@ -1151,6 +1319,14 @@ static int __hci_init(struct hci_dev *hdev)
        if (err < 0)
                return err;
 
+       /* The Device Under Test (DUT) mode is special and available for
+        * all controller types. So just create it early on.
+        */
+       if (test_bit(HCI_SETUP, &hdev->dev_flags)) {
+               debugfs_create_file("dut_mode", 0644, hdev->debugfs, hdev,
+                                   &dut_mode_fops);
+       }
+
        /* HCI_BREDR covers both single-mode LE, BR/EDR and dual-mode
         * BR/EDR/LE type controllers. AMP controllers only need the
         * first stage init.
@@ -1199,9 +1375,12 @@ static int __hci_init(struct hci_dev *hdev)
                                    hdev, &voice_setting_fops);
        }
 
-       if (lmp_ssp_capable(hdev))
+       if (lmp_ssp_capable(hdev)) {
                debugfs_create_file("auto_accept_delay", 0644, hdev->debugfs,
                                    hdev, &auto_accept_delay_fops);
+               debugfs_create_file("ssp_debug_mode", 0644, hdev->debugfs,
+                                   hdev, &ssp_debug_mode_fops);
+       }
 
        if (lmp_sniff_capable(hdev)) {
                debugfs_create_file("idle_timeout", 0644, hdev->debugfs,
@@ -1221,6 +1400,10 @@ static int __hci_init(struct hci_dev *hdev)
                                    hdev, &own_address_type_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);
        }
 
        return 0;
@@ -2686,6 +2869,8 @@ struct hci_dev *hci_alloc_dev(void)
 
        hdev->le_scan_interval = 0x0060;
        hdev->le_scan_window = 0x0030;
+       hdev->le_conn_min_interval = 0x0028;
+       hdev->le_conn_max_interval = 0x0038;
 
        mutex_init(&hdev->lock);
        mutex_init(&hdev->req_lock);