diff mbox series

Bluetooth: Add support for virtio transport driver

Message ID 20210406141258.258544-1-marcel@holtmann.org (mailing list archive)
State Accepted
Delegated to: Luiz Von Dentz
Headers show
Series Bluetooth: Add support for virtio transport driver | expand

Commit Message

Marcel Holtmann April 6, 2021, 2:12 p.m. UTC
This adds support for Bluetooth HCI transport over virtio.

Signed-off-by: Marcel Holtmann <marcel@holtmann.org>
---
 drivers/bluetooth/Kconfig       |  10 +
 drivers/bluetooth/Makefile      |   2 +
 drivers/bluetooth/virtio_bt.c   | 401 ++++++++++++++++++++++++++++++++
 include/uapi/linux/virtio_bt.h  |  31 +++
 include/uapi/linux/virtio_ids.h |   1 +
 5 files changed, 445 insertions(+)
 create mode 100644 drivers/bluetooth/virtio_bt.c
 create mode 100644 include/uapi/linux/virtio_bt.h

Comments

bluez.test.bot@gmail.com April 6, 2021, 3:11 p.m. UTC | #1
This is automated email and please do not reply to this email!

Dear submitter,

Thank you for submitting the patches to the linux bluetooth mailing list.
This is a CI test results with your patch series:
PW Link:https://patchwork.kernel.org/project/bluetooth/list/?series=461599

---Test result---

##############################
Test: CheckPatch - FAIL
Bluetooth: Add support for virtio transport driver
WARNING: added, moved or deleted file(s), does MAINTAINERS need updating?
#43: 
new file mode 100644

WARNING: Improper SPDX comment style for 'include/uapi/linux/virtio_bt.h', please use '/*' instead
#455: FILE: include/uapi/linux/virtio_bt.h:1:
+// SPDX-License-Identifier: BSD-3-Clause

WARNING: Missing or malformed SPDX-License-Identifier tag in line 1
#455: FILE: include/uapi/linux/virtio_bt.h:1:
+// SPDX-License-Identifier: BSD-3-Clause

total: 0 errors, 3 warnings, 461 lines checked

NOTE: For some of the reported defects, checkpatch may be able to
      mechanically convert to the typical style using --fix or --fix-inplace.

"[PATCH] Bluetooth: Add support for virtio transport driver" has style problems, please review.

NOTE: If any of the errors are false positives, please report
      them to the maintainer, see CHECKPATCH in MAINTAINERS.


##############################
Test: CheckGitLint - PASS


##############################
Test: CheckBuildK - PASS


##############################
Test: CheckTestRunner: Setup - PASS


##############################
Test: CheckTestRunner: l2cap-tester - PASS
Total: 40, Passed: 34 (85.0%), Failed: 0, Not Run: 6

##############################
Test: CheckTestRunner: bnep-tester - PASS
Total: 1, Passed: 1 (100.0%), Failed: 0, Not Run: 0

##############################
Test: CheckTestRunner: mgmt-tester - PASS
Total: 416, Passed: 402 (96.6%), Failed: 0, Not Run: 14

##############################
Test: CheckTestRunner: rfcomm-tester - PASS
Total: 9, Passed: 9 (100.0%), Failed: 0, Not Run: 0

##############################
Test: CheckTestRunner: sco-tester - PASS
Total: 8, Passed: 8 (100.0%), Failed: 0, Not Run: 0

##############################
Test: CheckTestRunner: smp-tester - PASS
Total: 8, Passed: 8 (100.0%), Failed: 0, Not Run: 0

##############################
Test: CheckTestRunner: userchan-tester - PASS
Total: 3, Passed: 3 (100.0%), Failed: 0, Not Run: 0



---
Regards,
Linux Bluetooth
kernel test robot April 6, 2021, 9:45 p.m. UTC | #2
Hi Marcel,

I love your patch! Perhaps something to improve:

[auto build test WARNING on bluetooth-next/master]
[also build test WARNING on linus/master v5.12-rc6 next-20210406]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Marcel-Holtmann/Bluetooth-Add-support-for-virtio-transport-driver/20210406-221514
base:   https://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git master
config: x86_64-randconfig-a011-20210406 (attached as .config)
compiler: clang version 13.0.0 (https://github.com/llvm/llvm-project a46f59a747a7273cc439efaf3b4f98d8b63d2f20)
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # install x86_64 cross compiling tool for clang build
        # apt-get install binutils-x86-64-linux-gnu
        # https://github.com/0day-ci/linux/commit/189912fb9343a7f898dbab721e7c4a70957e235b
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Marcel-Holtmann/Bluetooth-Add-support-for-virtio-transport-driver/20210406-221514
        git checkout 189912fb9343a7f898dbab721e7c4a70957e235b
        # save the attached .config to linux build tree
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=clang make.cross ARCH=x86_64 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

   In file included from <built-in>:1:
>> ./usr/include/linux/virtio_bt.h:1:1: warning: // comments are not allowed in this language [-Wcomment]
   // SPDX-License-Identifier: BSD-3-Clause
   ^
   1 warning generated.

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
kernel test robot April 6, 2021, 9:58 p.m. UTC | #3
Hi Marcel,

I love your patch! Yet something to improve:

[auto build test ERROR on bluetooth-next/master]
[also build test ERROR on linus/master v5.12-rc6 next-20210406]
[cannot apply to bluetooth/master]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]

url:    https://github.com/0day-ci/linux/commits/Marcel-Holtmann/Bluetooth-Add-support-for-virtio-transport-driver/20210406-221514
base:   https://git.kernel.org/pub/scm/linux/kernel/git/bluetooth/bluetooth-next.git master
config: um-allmodconfig (attached as .config)
compiler: gcc-9 (Debian 9.3.0-22) 9.3.0
reproduce (this is a W=1 build):
        # https://github.com/0day-ci/linux/commit/189912fb9343a7f898dbab721e7c4a70957e235b
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Marcel-Holtmann/Bluetooth-Add-support-for-virtio-transport-driver/20210406-221514
        git checkout 189912fb9343a7f898dbab721e7c4a70957e235b
        # save the attached .config to linux build tree
        make W=1 ARCH=um 

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All errors (new ones prefixed by >>):

   cc1: warning: arch/um/include/uapi: No such file or directory [-Wmissing-include-dirs]
   drivers/bluetooth/virtio_bt.c: In function 'virtbt_probe':
>> drivers/bluetooth/virtio_bt.c:343:3: error: implicit declaration of function 'hci_set_aosp_capable'; did you mean 'lmp_transp_capable'? [-Werror=implicit-function-declaration]
     343 |   hci_set_aosp_capable(hdev);
         |   ^~~~~~~~~~~~~~~~~~~~
         |   lmp_transp_capable
   cc1: some warnings being treated as errors


vim +343 drivers/bluetooth/virtio_bt.c

   240	
   241	static int virtbt_probe(struct virtio_device *vdev)
   242	{
   243		vq_callback_t *callbacks[VIRTBT_NUM_VQS] = {
   244			[VIRTBT_VQ_TX] = virtbt_tx_done,
   245			[VIRTBT_VQ_RX] = virtbt_rx_done,
   246		};
   247		const char *names[VIRTBT_NUM_VQS] = {
   248			[VIRTBT_VQ_TX] = "tx",
   249			[VIRTBT_VQ_RX] = "rx",
   250		};
   251		struct virtio_bluetooth *vbt;
   252		struct hci_dev *hdev;
   253		int err;
   254		__u8 type;
   255	
   256		if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1))
   257			return -ENODEV;
   258	
   259		type = virtio_cread8(vdev, offsetof(struct virtio_bt_config, type));
   260	
   261		switch (type) {
   262		case VIRTIO_BT_CONFIG_TYPE_PRIMARY:
   263		case VIRTIO_BT_CONFIG_TYPE_AMP:
   264			break;
   265		default:
   266			return -EINVAL;
   267		}
   268	
   269		vbt = kzalloc(sizeof(*vbt), GFP_KERNEL);
   270		if (!vbt)
   271			return -ENOMEM;
   272	
   273		vdev->priv = vbt;
   274		vbt->vdev = vdev;
   275	
   276		INIT_WORK(&vbt->rx, virtbt_rx_work);
   277	
   278		err = virtio_find_vqs(vdev, VIRTBT_NUM_VQS, vbt->vqs, callbacks,
   279				      names, NULL);
   280		if (err)
   281			return err;
   282	
   283		hdev = hci_alloc_dev();
   284		if (!hdev) {
   285			err = -ENOMEM;
   286			goto failed;
   287		}
   288	
   289		vbt->hdev = hdev;
   290	
   291		hdev->bus = HCI_VIRTIO;
   292		hdev->dev_type = type;
   293		hci_set_drvdata(hdev, vbt);
   294	
   295		hdev->open  = virtbt_open;
   296		hdev->close = virtbt_close;
   297		hdev->flush = virtbt_flush;
   298		hdev->send  = virtbt_send_frame;
   299	
   300		if (virtio_has_feature(vdev, VIRTIO_BT_F_VND_HCI)) {
   301			__u16 vendor;
   302	
   303			virtio_cread(vdev, struct virtio_bt_config, vendor, &vendor);
   304	
   305			switch (vendor) {
   306			case VIRTIO_BT_CONFIG_VENDOR_ZEPHYR:
   307				hdev->manufacturer = 1521;
   308				hdev->setup = virtbt_setup_zephyr;
   309				hdev->shutdown = virtbt_shutdown_generic;
   310				hdev->set_bdaddr = virtbt_set_bdaddr_zephyr;
   311				break;
   312	
   313			case VIRTIO_BT_CONFIG_VENDOR_INTEL:
   314				hdev->manufacturer = 2;
   315				hdev->setup = virtbt_setup_intel;
   316				hdev->shutdown = virtbt_shutdown_generic;
   317				hdev->set_bdaddr = virtbt_set_bdaddr_intel;
   318				set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
   319				set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
   320				set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks);
   321				break;
   322	
   323			case VIRTIO_BT_CONFIG_VENDOR_REALTEK:
   324				hdev->manufacturer = 93;
   325				hdev->setup = virtbt_setup_realtek;
   326				hdev->shutdown = virtbt_shutdown_generic;
   327				set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
   328				set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks);
   329				break;
   330			}
   331		}
   332	
   333		if (virtio_has_feature(vdev, VIRTIO_BT_F_MSFT_EXT)) {
   334			__u16 msft_opcode;
   335	
   336			virtio_cread(vdev, struct virtio_bt_config,
   337				     msft_opcode, &msft_opcode);
   338	
   339			hci_set_msft_opcode(hdev, msft_opcode);
   340		}
   341	
   342		if (virtio_has_feature(vdev, VIRTIO_BT_F_AOSP_EXT))
 > 343			hci_set_aosp_capable(hdev);
   344	
   345		if (hci_register_dev(hdev) < 0) {
   346			hci_free_dev(hdev);
   347			err = -EBUSY;
   348			goto failed;
   349		}
   350	
   351		return 0;
   352	
   353	failed:
   354		vdev->config->del_vqs(vdev);
   355		return err;
   356	}
   357	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all@lists.01.org
Jean-Philippe Brucker May 25, 2021, 9:22 a.m. UTC | #4
Hi Marcel,

On Tue, Apr 06, 2021 at 04:12:58PM +0200, Marcel Holtmann wrote:
> diff --git a/include/uapi/linux/virtio_bt.h b/include/uapi/linux/virtio_bt.h
> new file mode 100644
> index 000000000000..0cedceaacf88
> --- /dev/null
> +++ b/include/uapi/linux/virtio_bt.h
> @@ -0,0 +1,31 @@
> +// SPDX-License-Identifier: BSD-3-Clause
> +
> +#ifndef _UAPI_LINUX_VIRTIO_BT_H
> +#define _UAPI_LINUX_VIRTIO_BT_H
> +
> +#include <linux/virtio_types.h>
> +
> +/* Feature bits */
> +#define VIRTIO_BT_F_VND_HCI	0	/* Indicates vendor command support */
> +#define VIRTIO_BT_F_MSFT_EXT	1	/* Indicates MSFT vendor support */
> +#define VIRTIO_BT_F_AOSP_EXT	2	/* Indicates AOSP vendor support */
> +
> +enum virtio_bt_config_type {
> +	VIRTIO_BT_CONFIG_TYPE_PRIMARY	= 0,
> +	VIRTIO_BT_CONFIG_TYPE_AMP	= 1,
> +};
> +
> +enum virtio_bt_config_vendor {
> +	VIRTIO_BT_CONFIG_VENDOR_NONE	= 0,
> +	VIRTIO_BT_CONFIG_VENDOR_ZEPHYR	= 1,
> +	VIRTIO_BT_CONFIG_VENDOR_INTEL	= 2,
> +	VIRTIO_BT_CONFIG_VENDOR_REALTEK	= 3,
> +};
> +
> +struct virtio_bt_config {
> +	__u8  type;
> +	__u16 vendor;
> +	__u16 msft_opcode;
> +} __attribute__((packed));

Config fields should be naturally aligned, because the virtio spec says
(4.1.3.1 Driver Requirements: PCI Device Layout).

	For device configuration access, the driver MUST use 8-bit wide accesses
	for 8-bit wide fields, 16-bit wide and aligned accesses for 16-bit wide
	fields and 32-bit wide and aligned accesses for 32-bit and 64-bit wide
	fields.

> +
> +#endif /* _UAPI_LINUX_VIRTIO_BT_H */
> diff --git a/include/uapi/linux/virtio_ids.h b/include/uapi/linux/virtio_ids.h
> index bc1c0621f5ed..b4f468e9441d 100644
> --- a/include/uapi/linux/virtio_ids.h
> +++ b/include/uapi/linux/virtio_ids.h
> @@ -53,6 +53,7 @@
>  #define VIRTIO_ID_MEM			24 /* virtio mem */
>  #define VIRTIO_ID_FS			26 /* virtio filesystem */
>  #define VIRTIO_ID_PMEM			27 /* virtio pmem */
> +#define VIRTIO_ID_BT			28 /* virtio bluetooth */

ID 28 is already reserved for virtio-rpmb:
https://github.com/oasis-tcs/virtio-spec/issues/58

To avoid major compatibility pains going forward I think this should be
changed to ID 40 before v5.13. It won't be easy to fix afterwards since
it's UAPI.

Probably a good idea to fix or revert virtio_bt.h as well before it's too
late. Patches to the virtio headers should Cc
virtualization@lists.linux-foundation.org and perhaps
virtio-dev@lists.oasis-open.org because they can provide guidance about
this.

Thanks,
Jean

>  #define VIRTIO_ID_MAC80211_HWSIM	29 /* virtio mac80211-hwsim */
>  
>  #endif /* _LINUX_VIRTIO_IDS_H */
> -- 
> 2.30.2
>
diff mbox series

Patch

diff --git a/drivers/bluetooth/Kconfig b/drivers/bluetooth/Kconfig
index 4e73a531b377..851842372c9b 100644
--- a/drivers/bluetooth/Kconfig
+++ b/drivers/bluetooth/Kconfig
@@ -425,4 +425,14 @@  config BT_HCIRSI
 	  Say Y here to compile support for HCI over Redpine into the
 	  kernel or say M to compile as a module.
 
+config BT_VIRTIO
+	tristate "Virtio Bluetooth driver"
+	depends on VIRTIO
+	help
+	  Virtio Bluetooth support driver.
+	  This driver supports Virtio Bluetooth devices.
+
+	  Say Y here to compile support for HCI over Virtio into the
+	  kernel or say M to compile as a module.
+
 endmenu
diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
index 1a58a3ae142c..16286ea2655d 100644
--- a/drivers/bluetooth/Makefile
+++ b/drivers/bluetooth/Makefile
@@ -26,6 +26,8 @@  obj-$(CONFIG_BT_BCM)		+= btbcm.o
 obj-$(CONFIG_BT_RTL)		+= btrtl.o
 obj-$(CONFIG_BT_QCA)		+= btqca.o
 
+obj-$(CONFIG_BT_VIRTIO)		+= virtio_bt.o
+
 obj-$(CONFIG_BT_HCIUART_NOKIA)	+= hci_nokia.o
 
 obj-$(CONFIG_BT_HCIRSI)		+= btrsi.o
diff --git a/drivers/bluetooth/virtio_bt.c b/drivers/bluetooth/virtio_bt.c
new file mode 100644
index 000000000000..c804db7e90f8
--- /dev/null
+++ b/drivers/bluetooth/virtio_bt.c
@@ -0,0 +1,401 @@ 
+// SPDX-License-Identifier: GPL-2.0-only
+
+#include <linux/module.h>
+#include <linux/virtio.h>
+#include <linux/virtio_config.h>
+#include <linux/skbuff.h>
+
+#include <uapi/linux/virtio_ids.h>
+#include <uapi/linux/virtio_bt.h>
+
+#include <net/bluetooth/bluetooth.h>
+#include <net/bluetooth/hci_core.h>
+
+#define VERSION "0.1"
+
+enum {
+	VIRTBT_VQ_TX,
+	VIRTBT_VQ_RX,
+	VIRTBT_NUM_VQS,
+};
+
+struct virtio_bluetooth {
+	struct virtio_device *vdev;
+	struct virtqueue *vqs[VIRTBT_NUM_VQS];
+	struct work_struct rx;
+	struct hci_dev *hdev;
+};
+
+static int virtbt_add_inbuf(struct virtio_bluetooth *vbt)
+{
+	struct virtqueue *vq = vbt->vqs[VIRTBT_VQ_RX];
+	struct scatterlist sg[1];
+	struct sk_buff *skb;
+	int err;
+
+	skb = alloc_skb(1000, GFP_KERNEL);
+	sg_init_one(sg, skb->data, 1000);
+
+	err = virtqueue_add_inbuf(vq, sg, 1, skb, GFP_KERNEL);
+	if (err < 0) {
+		kfree_skb(skb);
+		return err;
+	}
+
+	return 0;
+}
+
+static int virtbt_open(struct hci_dev *hdev)
+{
+	struct virtio_bluetooth *vbt = hci_get_drvdata(hdev);
+
+	if (virtbt_add_inbuf(vbt) < 0)
+		return -EIO;
+
+	virtqueue_kick(vbt->vqs[VIRTBT_VQ_RX]);
+	return 0;
+}
+
+static int virtbt_close(struct hci_dev *hdev)
+{
+	struct virtio_bluetooth *vbt = hci_get_drvdata(hdev);
+	int i;
+
+	cancel_work_sync(&vbt->rx);
+
+	for (i = 0; i < ARRAY_SIZE(vbt->vqs); i++) {
+		struct virtqueue *vq = vbt->vqs[i];
+		struct sk_buff *skb;
+
+		while ((skb = virtqueue_detach_unused_buf(vq)))
+			kfree_skb(skb);
+	}
+
+	return 0;
+}
+
+static int virtbt_flush(struct hci_dev *hdev)
+{
+	return 0;
+}
+
+static int virtbt_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
+{
+	struct virtio_bluetooth *vbt = hci_get_drvdata(hdev);
+	struct scatterlist sg[1];
+	int err;
+
+	memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
+
+	sg_init_one(sg, skb->data, skb->len);
+	err = virtqueue_add_outbuf(vbt->vqs[VIRTBT_VQ_TX], sg, 1, skb,
+				   GFP_KERNEL);
+	if (err) {
+		kfree_skb(skb);
+		return err;
+	}
+
+	virtqueue_kick(vbt->vqs[VIRTBT_VQ_TX]);
+	return 0;
+}
+
+static int virtbt_setup_zephyr(struct hci_dev *hdev)
+{
+	struct sk_buff *skb;
+
+	/* Read Build Information */
+	skb = __hci_cmd_sync(hdev, 0xfc08, 0, NULL, HCI_INIT_TIMEOUT);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	bt_dev_info(hdev, "%s", (char *)(skb->data + 1));
+
+	hci_set_fw_info(hdev, "%s", skb->data + 1);
+
+	kfree_skb(skb);
+	return 0;
+}
+
+static int virtbt_set_bdaddr_zephyr(struct hci_dev *hdev,
+				    const bdaddr_t *bdaddr)
+{
+	struct sk_buff *skb;
+
+	/* Write BD_ADDR */
+	skb = __hci_cmd_sync(hdev, 0xfc06, 6, bdaddr, HCI_INIT_TIMEOUT);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	kfree_skb(skb);
+	return 0;
+}
+
+static int virtbt_setup_intel(struct hci_dev *hdev)
+{
+	struct sk_buff *skb;
+
+	/* Intel Read Version */
+	skb = __hci_cmd_sync(hdev, 0xfc05, 0, NULL, HCI_CMD_TIMEOUT);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	kfree_skb(skb);
+	return 0;
+}
+
+static int virtbt_set_bdaddr_intel(struct hci_dev *hdev, const bdaddr_t *bdaddr)
+{
+	struct sk_buff *skb;
+
+	/* Intel Write BD Address */
+	skb = __hci_cmd_sync(hdev, 0xfc31, 6, bdaddr, HCI_INIT_TIMEOUT);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	kfree_skb(skb);
+	return 0;
+}
+
+static int virtbt_setup_realtek(struct hci_dev *hdev)
+{
+	struct sk_buff *skb;
+
+	/* Read ROM Version */
+	skb = __hci_cmd_sync(hdev, 0xfc6d, 0, NULL, HCI_INIT_TIMEOUT);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	bt_dev_info(hdev, "ROM version %u", *((__u8 *) (skb->data + 1)));
+
+	kfree_skb(skb);
+	return 0;
+}
+
+static int virtbt_shutdown_generic(struct hci_dev *hdev)
+{
+	struct sk_buff *skb;
+
+	/* Reset */
+	skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
+	if (IS_ERR(skb))
+		return PTR_ERR(skb);
+
+	kfree_skb(skb);
+	return 0;
+}
+
+static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb)
+{
+	__u8 pkt_type;
+
+	pkt_type = *((__u8 *) skb->data);
+	skb_pull(skb, 1);
+
+	switch (pkt_type) {
+	case HCI_EVENT_PKT:
+	case HCI_ACLDATA_PKT:
+	case HCI_SCODATA_PKT:
+	case HCI_ISODATA_PKT:
+		hci_skb_pkt_type(skb) = pkt_type;
+		hci_recv_frame(vbt->hdev, skb);
+		break;
+	}
+}
+
+static void virtbt_rx_work(struct work_struct *work)
+{
+	struct virtio_bluetooth *vbt = container_of(work,
+						    struct virtio_bluetooth, rx);
+	struct sk_buff *skb;
+	unsigned int len;
+
+	skb = virtqueue_get_buf(vbt->vqs[VIRTBT_VQ_RX], &len);
+	if (!skb)
+		return;
+
+	skb->len = len;
+	virtbt_rx_handle(vbt, skb);
+
+	if (virtbt_add_inbuf(vbt) < 0)
+		return;
+
+	virtqueue_kick(vbt->vqs[VIRTBT_VQ_RX]);
+}
+
+static void virtbt_tx_done(struct virtqueue *vq)
+{
+	struct sk_buff *skb;
+	unsigned int len;
+
+	while ((skb = virtqueue_get_buf(vq, &len)))
+		kfree_skb(skb);
+}
+
+static void virtbt_rx_done(struct virtqueue *vq)
+{
+	struct virtio_bluetooth *vbt = vq->vdev->priv;
+
+	schedule_work(&vbt->rx);
+}
+
+static int virtbt_probe(struct virtio_device *vdev)
+{
+	vq_callback_t *callbacks[VIRTBT_NUM_VQS] = {
+		[VIRTBT_VQ_TX] = virtbt_tx_done,
+		[VIRTBT_VQ_RX] = virtbt_rx_done,
+	};
+	const char *names[VIRTBT_NUM_VQS] = {
+		[VIRTBT_VQ_TX] = "tx",
+		[VIRTBT_VQ_RX] = "rx",
+	};
+	struct virtio_bluetooth *vbt;
+	struct hci_dev *hdev;
+	int err;
+	__u8 type;
+
+	if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1))
+		return -ENODEV;
+
+	type = virtio_cread8(vdev, offsetof(struct virtio_bt_config, type));
+
+	switch (type) {
+	case VIRTIO_BT_CONFIG_TYPE_PRIMARY:
+	case VIRTIO_BT_CONFIG_TYPE_AMP:
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	vbt = kzalloc(sizeof(*vbt), GFP_KERNEL);
+	if (!vbt)
+		return -ENOMEM;
+
+	vdev->priv = vbt;
+	vbt->vdev = vdev;
+
+	INIT_WORK(&vbt->rx, virtbt_rx_work);
+
+	err = virtio_find_vqs(vdev, VIRTBT_NUM_VQS, vbt->vqs, callbacks,
+			      names, NULL);
+	if (err)
+		return err;
+
+	hdev = hci_alloc_dev();
+	if (!hdev) {
+		err = -ENOMEM;
+		goto failed;
+	}
+
+	vbt->hdev = hdev;
+
+	hdev->bus = HCI_VIRTIO;
+	hdev->dev_type = type;
+	hci_set_drvdata(hdev, vbt);
+
+	hdev->open  = virtbt_open;
+	hdev->close = virtbt_close;
+	hdev->flush = virtbt_flush;
+	hdev->send  = virtbt_send_frame;
+
+	if (virtio_has_feature(vdev, VIRTIO_BT_F_VND_HCI)) {
+		__u16 vendor;
+
+		virtio_cread(vdev, struct virtio_bt_config, vendor, &vendor);
+
+		switch (vendor) {
+		case VIRTIO_BT_CONFIG_VENDOR_ZEPHYR:
+			hdev->manufacturer = 1521;
+			hdev->setup = virtbt_setup_zephyr;
+			hdev->shutdown = virtbt_shutdown_generic;
+			hdev->set_bdaddr = virtbt_set_bdaddr_zephyr;
+			break;
+
+		case VIRTIO_BT_CONFIG_VENDOR_INTEL:
+			hdev->manufacturer = 2;
+			hdev->setup = virtbt_setup_intel;
+			hdev->shutdown = virtbt_shutdown_generic;
+			hdev->set_bdaddr = virtbt_set_bdaddr_intel;
+			set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
+			set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
+			set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks);
+			break;
+
+		case VIRTIO_BT_CONFIG_VENDOR_REALTEK:
+			hdev->manufacturer = 93;
+			hdev->setup = virtbt_setup_realtek;
+			hdev->shutdown = virtbt_shutdown_generic;
+			set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
+			set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks);
+			break;
+		}
+	}
+
+	if (virtio_has_feature(vdev, VIRTIO_BT_F_MSFT_EXT)) {
+		__u16 msft_opcode;
+
+		virtio_cread(vdev, struct virtio_bt_config,
+			     msft_opcode, &msft_opcode);
+
+		hci_set_msft_opcode(hdev, msft_opcode);
+	}
+
+	if (virtio_has_feature(vdev, VIRTIO_BT_F_AOSP_EXT))
+		hci_set_aosp_capable(hdev);
+
+	if (hci_register_dev(hdev) < 0) {
+		hci_free_dev(hdev);
+		err = -EBUSY;
+		goto failed;
+	}
+
+	return 0;
+
+failed:
+	vdev->config->del_vqs(vdev);
+	return err;
+}
+
+static void virtbt_remove(struct virtio_device *vdev)
+{
+	struct virtio_bluetooth *vbt = vdev->priv;
+	struct hci_dev *hdev = vbt->hdev;
+
+	hci_unregister_dev(hdev);
+	vdev->config->reset(vdev);
+
+	hci_free_dev(hdev);
+	vbt->hdev = NULL;
+
+	vdev->config->del_vqs(vdev);
+	kfree(vbt);
+}
+
+static struct virtio_device_id virtbt_table[] = {
+	{ VIRTIO_ID_BT, VIRTIO_DEV_ANY_ID },
+	{ 0 },
+};
+
+MODULE_DEVICE_TABLE(virtio, virtbt_table);
+
+static const unsigned int virtbt_features[] = {
+	VIRTIO_BT_F_VND_HCI,
+	VIRTIO_BT_F_MSFT_EXT,
+	VIRTIO_BT_F_AOSP_EXT,
+};
+
+static struct virtio_driver virtbt_driver = {
+	.driver.name         = KBUILD_MODNAME,
+	.driver.owner        = THIS_MODULE,
+	.feature_table       = virtbt_features,
+	.feature_table_size  = ARRAY_SIZE(virtbt_features),
+	.id_table            = virtbt_table,
+	.probe               = virtbt_probe,
+	.remove              = virtbt_remove,
+};
+
+module_virtio_driver(virtbt_driver);
+
+MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
+MODULE_DESCRIPTION("Generic Bluetooth VIRTIO driver ver " VERSION);
+MODULE_VERSION(VERSION);
+MODULE_LICENSE("GPL");
diff --git a/include/uapi/linux/virtio_bt.h b/include/uapi/linux/virtio_bt.h
new file mode 100644
index 000000000000..0cedceaacf88
--- /dev/null
+++ b/include/uapi/linux/virtio_bt.h
@@ -0,0 +1,31 @@ 
+// SPDX-License-Identifier: BSD-3-Clause
+
+#ifndef _UAPI_LINUX_VIRTIO_BT_H
+#define _UAPI_LINUX_VIRTIO_BT_H
+
+#include <linux/virtio_types.h>
+
+/* Feature bits */
+#define VIRTIO_BT_F_VND_HCI	0	/* Indicates vendor command support */
+#define VIRTIO_BT_F_MSFT_EXT	1	/* Indicates MSFT vendor support */
+#define VIRTIO_BT_F_AOSP_EXT	2	/* Indicates AOSP vendor support */
+
+enum virtio_bt_config_type {
+	VIRTIO_BT_CONFIG_TYPE_PRIMARY	= 0,
+	VIRTIO_BT_CONFIG_TYPE_AMP	= 1,
+};
+
+enum virtio_bt_config_vendor {
+	VIRTIO_BT_CONFIG_VENDOR_NONE	= 0,
+	VIRTIO_BT_CONFIG_VENDOR_ZEPHYR	= 1,
+	VIRTIO_BT_CONFIG_VENDOR_INTEL	= 2,
+	VIRTIO_BT_CONFIG_VENDOR_REALTEK	= 3,
+};
+
+struct virtio_bt_config {
+	__u8  type;
+	__u16 vendor;
+	__u16 msft_opcode;
+} __attribute__((packed));
+
+#endif /* _UAPI_LINUX_VIRTIO_BT_H */
diff --git a/include/uapi/linux/virtio_ids.h b/include/uapi/linux/virtio_ids.h
index bc1c0621f5ed..b4f468e9441d 100644
--- a/include/uapi/linux/virtio_ids.h
+++ b/include/uapi/linux/virtio_ids.h
@@ -53,6 +53,7 @@ 
 #define VIRTIO_ID_MEM			24 /* virtio mem */
 #define VIRTIO_ID_FS			26 /* virtio filesystem */
 #define VIRTIO_ID_PMEM			27 /* virtio pmem */
+#define VIRTIO_ID_BT			28 /* virtio bluetooth */
 #define VIRTIO_ID_MAC80211_HWSIM	29 /* virtio mac80211-hwsim */
 
 #endif /* _LINUX_VIRTIO_IDS_H */