Commit b8f5482c authored by Luiz Augusto von Dentz's avatar Luiz Augusto von Dentz Committed by Marcel Holtmann
Browse files

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: default avatarLuiz Augusto von Dentz <luiz.von.dentz@intel.com>
Signed-off-by: default avatarMarcel Holtmann <marcel@holtmann.org>
parent 60c6a63a
Loading
Loading
Loading
Loading
+94 −0
Original line number Diff line number Diff line
@@ -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);