diff options
author | Luiz Augusto von Dentz <luiz.von.dentz@intel.com> | 2021-10-14 01:17:01 +0300 |
---|---|---|
committer | Marcel Holtmann <marcel@holtmann.org> | 2021-10-14 20:44:11 +0300 |
commit | b8f5482c9638915c19963f06d2f4094e276aaab6 (patch) | |
tree | 67d1b572806a6529fb6f463471bb831064bf5b1c /drivers/bluetooth/hci_vhci.c | |
parent | 60c6a63a3d3080a62f3e0e20084f58dbeff16748 (diff) | |
download | linux-b8f5482c9638915c19963f06d2f4094e276aaab6.tar.xz |
Bluetooth: vhci: Add support for setting msft_opcode and aosp_capable
This adds a debugfs entries to set msft_opcode and aosp_capable enabling
vhci to emulate controllers with MSFT/AOSP extension support.
Signed-off-by: Luiz Augusto von Dentz <luiz.von.dentz@intel.com>
Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
Diffstat (limited to 'drivers/bluetooth/hci_vhci.c')
-rw-r--r-- | drivers/bluetooth/hci_vhci.c | 94 |
1 files changed, 94 insertions, 0 deletions
diff --git a/drivers/bluetooth/hci_vhci.c b/drivers/bluetooth/hci_vhci.c index 56c6b22be10b..9cb7c8fafbf9 100644 --- a/drivers/bluetooth/hci_vhci.c +++ b/drivers/bluetooth/hci_vhci.c @@ -42,6 +42,8 @@ struct vhci_data { bool suspended; bool wakeup; + __u16 msft_opcode; + bool aosp_capable; }; static int vhci_open_dev(struct hci_dev *hdev) @@ -194,6 +196,88 @@ static const struct file_operations force_wakeup_fops = { .llseek = default_llseek, }; +static int msft_opcode_set(void *data, u64 val) +{ + struct vhci_data *vhci = data; + + if (val > 0xffff || (val & 0xffff >> 10) != 0x3f) + return -EINVAL; + + if (vhci->msft_opcode) + return -EALREADY; + + vhci->msft_opcode = val; + + return 0; +} + +static int msft_opcode_get(void *data, u64 *val) +{ + struct vhci_data *vhci = data; + + *val = vhci->msft_opcode; + + return 0; +} + +DEFINE_DEBUGFS_ATTRIBUTE(msft_opcode_fops, msft_opcode_get, msft_opcode_set, + "%llu\n"); + +static ssize_t aosp_capable_read(struct file *file, char __user *user_buf, + size_t count, loff_t *ppos) +{ + struct vhci_data *vhci = file->private_data; + char buf[3]; + + buf[0] = vhci->aosp_capable ? 'Y' : 'N'; + buf[1] = '\n'; + buf[2] = '\0'; + return simple_read_from_buffer(user_buf, count, ppos, buf, 2); +} + +static ssize_t aosp_capable_write(struct file *file, + const char __user *user_buf, size_t count, + loff_t *ppos) +{ + struct vhci_data *vhci = file->private_data; + bool enable; + int err; + + err = kstrtobool_from_user(user_buf, count, &enable); + if (err) + return err; + + if (!enable) + return -EINVAL; + + if (vhci->aosp_capable) + return -EALREADY; + + vhci->aosp_capable = enable; + + return count; +} + +static const struct file_operations aosp_capable_fops = { + .open = simple_open, + .read = aosp_capable_read, + .write = aosp_capable_write, + .llseek = default_llseek, +}; + +static int vhci_setup(struct hci_dev *hdev) +{ + struct vhci_data *vhci = hci_get_drvdata(hdev); + + if (vhci->msft_opcode) + hci_set_msft_opcode(hdev, vhci->msft_opcode); + + if (vhci->aosp_capable) + hci_set_aosp_capable(hdev); + + return 0; +} + static int __vhci_create_device(struct vhci_data *data, __u8 opcode) { struct hci_dev *hdev; @@ -236,6 +320,8 @@ static int __vhci_create_device(struct vhci_data *data, __u8 opcode) hdev->get_data_path_id = vhci_get_data_path_id; hdev->get_codec_config_data = vhci_get_codec_config_data; hdev->wakeup = vhci_wakeup; + hdev->setup = vhci_setup; + set_bit(HCI_QUIRK_NON_PERSISTENT_SETUP, &hdev->quirks); /* bit 6 is for external configuration */ if (opcode & 0x40) @@ -259,6 +345,14 @@ static int __vhci_create_device(struct vhci_data *data, __u8 opcode) debugfs_create_file("force_wakeup", 0644, hdev->debugfs, data, &force_wakeup_fops); + if (IS_ENABLED(CONFIG_BT_MSFTEXT)) + debugfs_create_file("msft_opcode", 0644, hdev->debugfs, data, + &msft_opcode_fops); + + if (IS_ENABLED(CONFIG_BT_AOSPEXT)) + debugfs_create_file("aosp_capable", 0644, hdev->debugfs, data, + &aosp_capable_fops); + hci_skb_pkt_type(skb) = HCI_VENDOR_PKT; skb_put_u8(skb, 0xff); |