xref: /openbmc/linux/drivers/bluetooth/virtio_bt.c (revision 5af2e235)
1afd2daa2SMarcel Holtmann // SPDX-License-Identifier: GPL-2.0-only
2afd2daa2SMarcel Holtmann 
3afd2daa2SMarcel Holtmann #include <linux/module.h>
4afd2daa2SMarcel Holtmann #include <linux/virtio.h>
5afd2daa2SMarcel Holtmann #include <linux/virtio_config.h>
6afd2daa2SMarcel Holtmann #include <linux/skbuff.h>
7afd2daa2SMarcel Holtmann 
8afd2daa2SMarcel Holtmann #include <uapi/linux/virtio_ids.h>
9afd2daa2SMarcel Holtmann #include <uapi/linux/virtio_bt.h>
10afd2daa2SMarcel Holtmann 
11afd2daa2SMarcel Holtmann #include <net/bluetooth/bluetooth.h>
12afd2daa2SMarcel Holtmann #include <net/bluetooth/hci_core.h>
13afd2daa2SMarcel Holtmann 
14afd2daa2SMarcel Holtmann #define VERSION "0.1"
15afd2daa2SMarcel Holtmann 
16afd2daa2SMarcel Holtmann enum {
17afd2daa2SMarcel Holtmann 	VIRTBT_VQ_TX,
18afd2daa2SMarcel Holtmann 	VIRTBT_VQ_RX,
19afd2daa2SMarcel Holtmann 	VIRTBT_NUM_VQS,
20afd2daa2SMarcel Holtmann };
21afd2daa2SMarcel Holtmann 
22afd2daa2SMarcel Holtmann struct virtio_bluetooth {
23afd2daa2SMarcel Holtmann 	struct virtio_device *vdev;
24afd2daa2SMarcel Holtmann 	struct virtqueue *vqs[VIRTBT_NUM_VQS];
25afd2daa2SMarcel Holtmann 	struct work_struct rx;
26afd2daa2SMarcel Holtmann 	struct hci_dev *hdev;
27afd2daa2SMarcel Holtmann };
28afd2daa2SMarcel Holtmann 
virtbt_add_inbuf(struct virtio_bluetooth * vbt)29afd2daa2SMarcel Holtmann static int virtbt_add_inbuf(struct virtio_bluetooth *vbt)
30afd2daa2SMarcel Holtmann {
31afd2daa2SMarcel Holtmann 	struct virtqueue *vq = vbt->vqs[VIRTBT_VQ_RX];
32afd2daa2SMarcel Holtmann 	struct scatterlist sg[1];
33afd2daa2SMarcel Holtmann 	struct sk_buff *skb;
34afd2daa2SMarcel Holtmann 	int err;
35afd2daa2SMarcel Holtmann 
36afd2daa2SMarcel Holtmann 	skb = alloc_skb(1000, GFP_KERNEL);
371cb027f2SColin Ian King 	if (!skb)
381cb027f2SColin Ian King 		return -ENOMEM;
391cb027f2SColin Ian King 
40afd2daa2SMarcel Holtmann 	sg_init_one(sg, skb->data, 1000);
41afd2daa2SMarcel Holtmann 
42afd2daa2SMarcel Holtmann 	err = virtqueue_add_inbuf(vq, sg, 1, skb, GFP_KERNEL);
43afd2daa2SMarcel Holtmann 	if (err < 0) {
44afd2daa2SMarcel Holtmann 		kfree_skb(skb);
45afd2daa2SMarcel Holtmann 		return err;
46afd2daa2SMarcel Holtmann 	}
47afd2daa2SMarcel Holtmann 
48afd2daa2SMarcel Holtmann 	return 0;
49afd2daa2SMarcel Holtmann }
50afd2daa2SMarcel Holtmann 
virtbt_open(struct hci_dev * hdev)51afd2daa2SMarcel Holtmann static int virtbt_open(struct hci_dev *hdev)
52afd2daa2SMarcel Holtmann {
53dc65b4b0SMichael S. Tsirkin 	return 0;
54dc65b4b0SMichael S. Tsirkin }
55afd2daa2SMarcel Holtmann 
virtbt_open_vdev(struct virtio_bluetooth * vbt)56dc65b4b0SMichael S. Tsirkin static int virtbt_open_vdev(struct virtio_bluetooth *vbt)
57dc65b4b0SMichael S. Tsirkin {
58afd2daa2SMarcel Holtmann 	if (virtbt_add_inbuf(vbt) < 0)
59afd2daa2SMarcel Holtmann 		return -EIO;
60afd2daa2SMarcel Holtmann 
61afd2daa2SMarcel Holtmann 	virtqueue_kick(vbt->vqs[VIRTBT_VQ_RX]);
62afd2daa2SMarcel Holtmann 	return 0;
63afd2daa2SMarcel Holtmann }
64afd2daa2SMarcel Holtmann 
virtbt_close(struct hci_dev * hdev)65afd2daa2SMarcel Holtmann static int virtbt_close(struct hci_dev *hdev)
66afd2daa2SMarcel Holtmann {
67dc65b4b0SMichael S. Tsirkin 	return 0;
68dc65b4b0SMichael S. Tsirkin }
69dc65b4b0SMichael S. Tsirkin 
virtbt_close_vdev(struct virtio_bluetooth * vbt)70dc65b4b0SMichael S. Tsirkin static int virtbt_close_vdev(struct virtio_bluetooth *vbt)
71dc65b4b0SMichael S. Tsirkin {
72afd2daa2SMarcel Holtmann 	int i;
73afd2daa2SMarcel Holtmann 
74afd2daa2SMarcel Holtmann 	cancel_work_sync(&vbt->rx);
75afd2daa2SMarcel Holtmann 
76afd2daa2SMarcel Holtmann 	for (i = 0; i < ARRAY_SIZE(vbt->vqs); i++) {
77afd2daa2SMarcel Holtmann 		struct virtqueue *vq = vbt->vqs[i];
78afd2daa2SMarcel Holtmann 		struct sk_buff *skb;
79afd2daa2SMarcel Holtmann 
80afd2daa2SMarcel Holtmann 		while ((skb = virtqueue_detach_unused_buf(vq)))
81afd2daa2SMarcel Holtmann 			kfree_skb(skb);
82*3845308fSXianting Tian 		cond_resched();
83afd2daa2SMarcel Holtmann 	}
84afd2daa2SMarcel Holtmann 
85afd2daa2SMarcel Holtmann 	return 0;
86afd2daa2SMarcel Holtmann }
87afd2daa2SMarcel Holtmann 
virtbt_flush(struct hci_dev * hdev)88afd2daa2SMarcel Holtmann static int virtbt_flush(struct hci_dev *hdev)
89afd2daa2SMarcel Holtmann {
90afd2daa2SMarcel Holtmann 	return 0;
91afd2daa2SMarcel Holtmann }
92afd2daa2SMarcel Holtmann 
virtbt_send_frame(struct hci_dev * hdev,struct sk_buff * skb)93afd2daa2SMarcel Holtmann static int virtbt_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
94afd2daa2SMarcel Holtmann {
95afd2daa2SMarcel Holtmann 	struct virtio_bluetooth *vbt = hci_get_drvdata(hdev);
96afd2daa2SMarcel Holtmann 	struct scatterlist sg[1];
97afd2daa2SMarcel Holtmann 	int err;
98afd2daa2SMarcel Holtmann 
99afd2daa2SMarcel Holtmann 	memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1);
100afd2daa2SMarcel Holtmann 
101afd2daa2SMarcel Holtmann 	sg_init_one(sg, skb->data, skb->len);
102afd2daa2SMarcel Holtmann 	err = virtqueue_add_outbuf(vbt->vqs[VIRTBT_VQ_TX], sg, 1, skb,
103afd2daa2SMarcel Holtmann 				   GFP_KERNEL);
104afd2daa2SMarcel Holtmann 	if (err) {
105afd2daa2SMarcel Holtmann 		kfree_skb(skb);
106afd2daa2SMarcel Holtmann 		return err;
107afd2daa2SMarcel Holtmann 	}
108afd2daa2SMarcel Holtmann 
109afd2daa2SMarcel Holtmann 	virtqueue_kick(vbt->vqs[VIRTBT_VQ_TX]);
110afd2daa2SMarcel Holtmann 	return 0;
111afd2daa2SMarcel Holtmann }
112afd2daa2SMarcel Holtmann 
virtbt_setup_zephyr(struct hci_dev * hdev)113afd2daa2SMarcel Holtmann static int virtbt_setup_zephyr(struct hci_dev *hdev)
114afd2daa2SMarcel Holtmann {
115afd2daa2SMarcel Holtmann 	struct sk_buff *skb;
116afd2daa2SMarcel Holtmann 
117afd2daa2SMarcel Holtmann 	/* Read Build Information */
118afd2daa2SMarcel Holtmann 	skb = __hci_cmd_sync(hdev, 0xfc08, 0, NULL, HCI_INIT_TIMEOUT);
119afd2daa2SMarcel Holtmann 	if (IS_ERR(skb))
120afd2daa2SMarcel Holtmann 		return PTR_ERR(skb);
121afd2daa2SMarcel Holtmann 
122afd2daa2SMarcel Holtmann 	bt_dev_info(hdev, "%s", (char *)(skb->data + 1));
123afd2daa2SMarcel Holtmann 
124afd2daa2SMarcel Holtmann 	hci_set_fw_info(hdev, "%s", skb->data + 1);
125afd2daa2SMarcel Holtmann 
126afd2daa2SMarcel Holtmann 	kfree_skb(skb);
127afd2daa2SMarcel Holtmann 	return 0;
128afd2daa2SMarcel Holtmann }
129afd2daa2SMarcel Holtmann 
virtbt_set_bdaddr_zephyr(struct hci_dev * hdev,const bdaddr_t * bdaddr)130afd2daa2SMarcel Holtmann static int virtbt_set_bdaddr_zephyr(struct hci_dev *hdev,
131afd2daa2SMarcel Holtmann 				    const bdaddr_t *bdaddr)
132afd2daa2SMarcel Holtmann {
133afd2daa2SMarcel Holtmann 	struct sk_buff *skb;
134afd2daa2SMarcel Holtmann 
135afd2daa2SMarcel Holtmann 	/* Write BD_ADDR */
136afd2daa2SMarcel Holtmann 	skb = __hci_cmd_sync(hdev, 0xfc06, 6, bdaddr, HCI_INIT_TIMEOUT);
137afd2daa2SMarcel Holtmann 	if (IS_ERR(skb))
138afd2daa2SMarcel Holtmann 		return PTR_ERR(skb);
139afd2daa2SMarcel Holtmann 
140afd2daa2SMarcel Holtmann 	kfree_skb(skb);
141afd2daa2SMarcel Holtmann 	return 0;
142afd2daa2SMarcel Holtmann }
143afd2daa2SMarcel Holtmann 
virtbt_setup_intel(struct hci_dev * hdev)144afd2daa2SMarcel Holtmann static int virtbt_setup_intel(struct hci_dev *hdev)
145afd2daa2SMarcel Holtmann {
146afd2daa2SMarcel Holtmann 	struct sk_buff *skb;
147afd2daa2SMarcel Holtmann 
148afd2daa2SMarcel Holtmann 	/* Intel Read Version */
149afd2daa2SMarcel Holtmann 	skb = __hci_cmd_sync(hdev, 0xfc05, 0, NULL, HCI_CMD_TIMEOUT);
150afd2daa2SMarcel Holtmann 	if (IS_ERR(skb))
151afd2daa2SMarcel Holtmann 		return PTR_ERR(skb);
152afd2daa2SMarcel Holtmann 
153afd2daa2SMarcel Holtmann 	kfree_skb(skb);
154afd2daa2SMarcel Holtmann 	return 0;
155afd2daa2SMarcel Holtmann }
156afd2daa2SMarcel Holtmann 
virtbt_set_bdaddr_intel(struct hci_dev * hdev,const bdaddr_t * bdaddr)157afd2daa2SMarcel Holtmann static int virtbt_set_bdaddr_intel(struct hci_dev *hdev, const bdaddr_t *bdaddr)
158afd2daa2SMarcel Holtmann {
159afd2daa2SMarcel Holtmann 	struct sk_buff *skb;
160afd2daa2SMarcel Holtmann 
161afd2daa2SMarcel Holtmann 	/* Intel Write BD Address */
162afd2daa2SMarcel Holtmann 	skb = __hci_cmd_sync(hdev, 0xfc31, 6, bdaddr, HCI_INIT_TIMEOUT);
163afd2daa2SMarcel Holtmann 	if (IS_ERR(skb))
164afd2daa2SMarcel Holtmann 		return PTR_ERR(skb);
165afd2daa2SMarcel Holtmann 
166afd2daa2SMarcel Holtmann 	kfree_skb(skb);
167afd2daa2SMarcel Holtmann 	return 0;
168afd2daa2SMarcel Holtmann }
169afd2daa2SMarcel Holtmann 
virtbt_setup_realtek(struct hci_dev * hdev)170afd2daa2SMarcel Holtmann static int virtbt_setup_realtek(struct hci_dev *hdev)
171afd2daa2SMarcel Holtmann {
172afd2daa2SMarcel Holtmann 	struct sk_buff *skb;
173afd2daa2SMarcel Holtmann 
174afd2daa2SMarcel Holtmann 	/* Read ROM Version */
175afd2daa2SMarcel Holtmann 	skb = __hci_cmd_sync(hdev, 0xfc6d, 0, NULL, HCI_INIT_TIMEOUT);
176afd2daa2SMarcel Holtmann 	if (IS_ERR(skb))
177afd2daa2SMarcel Holtmann 		return PTR_ERR(skb);
178afd2daa2SMarcel Holtmann 
179afd2daa2SMarcel Holtmann 	bt_dev_info(hdev, "ROM version %u", *((__u8 *) (skb->data + 1)));
180afd2daa2SMarcel Holtmann 
181afd2daa2SMarcel Holtmann 	kfree_skb(skb);
182afd2daa2SMarcel Holtmann 	return 0;
183afd2daa2SMarcel Holtmann }
184afd2daa2SMarcel Holtmann 
virtbt_shutdown_generic(struct hci_dev * hdev)185afd2daa2SMarcel Holtmann static int virtbt_shutdown_generic(struct hci_dev *hdev)
186afd2daa2SMarcel Holtmann {
187afd2daa2SMarcel Holtmann 	struct sk_buff *skb;
188afd2daa2SMarcel Holtmann 
189afd2daa2SMarcel Holtmann 	/* Reset */
190afd2daa2SMarcel Holtmann 	skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
191afd2daa2SMarcel Holtmann 	if (IS_ERR(skb))
192afd2daa2SMarcel Holtmann 		return PTR_ERR(skb);
193afd2daa2SMarcel Holtmann 
194afd2daa2SMarcel Holtmann 	kfree_skb(skb);
195afd2daa2SMarcel Holtmann 	return 0;
196afd2daa2SMarcel Holtmann }
197afd2daa2SMarcel Holtmann 
virtbt_rx_handle(struct virtio_bluetooth * vbt,struct sk_buff * skb)198afd2daa2SMarcel Holtmann static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb)
199afd2daa2SMarcel Holtmann {
200afd2daa2SMarcel Holtmann 	__u8 pkt_type;
201afd2daa2SMarcel Holtmann 
202afd2daa2SMarcel Holtmann 	pkt_type = *((__u8 *) skb->data);
203afd2daa2SMarcel Holtmann 	skb_pull(skb, 1);
204afd2daa2SMarcel Holtmann 
205afd2daa2SMarcel Holtmann 	switch (pkt_type) {
206afd2daa2SMarcel Holtmann 	case HCI_EVENT_PKT:
207afd2daa2SMarcel Holtmann 	case HCI_ACLDATA_PKT:
208afd2daa2SMarcel Holtmann 	case HCI_SCODATA_PKT:
209afd2daa2SMarcel Holtmann 	case HCI_ISODATA_PKT:
210afd2daa2SMarcel Holtmann 		hci_skb_pkt_type(skb) = pkt_type;
211afd2daa2SMarcel Holtmann 		hci_recv_frame(vbt->hdev, skb);
212afd2daa2SMarcel Holtmann 		break;
2131d068842SSoenke Huster 	default:
2141d068842SSoenke Huster 		kfree_skb(skb);
2151d068842SSoenke Huster 		break;
216afd2daa2SMarcel Holtmann 	}
217afd2daa2SMarcel Holtmann }
218afd2daa2SMarcel Holtmann 
virtbt_rx_work(struct work_struct * work)219afd2daa2SMarcel Holtmann static void virtbt_rx_work(struct work_struct *work)
220afd2daa2SMarcel Holtmann {
221afd2daa2SMarcel Holtmann 	struct virtio_bluetooth *vbt = container_of(work,
222afd2daa2SMarcel Holtmann 						    struct virtio_bluetooth, rx);
223afd2daa2SMarcel Holtmann 	struct sk_buff *skb;
224afd2daa2SMarcel Holtmann 	unsigned int len;
225afd2daa2SMarcel Holtmann 
226afd2daa2SMarcel Holtmann 	skb = virtqueue_get_buf(vbt->vqs[VIRTBT_VQ_RX], &len);
227afd2daa2SMarcel Holtmann 	if (!skb)
228afd2daa2SMarcel Holtmann 		return;
229afd2daa2SMarcel Holtmann 
230160fbcf3SSoenke Huster 	skb_put(skb, len);
231afd2daa2SMarcel Holtmann 	virtbt_rx_handle(vbt, skb);
232afd2daa2SMarcel Holtmann 
233afd2daa2SMarcel Holtmann 	if (virtbt_add_inbuf(vbt) < 0)
234afd2daa2SMarcel Holtmann 		return;
235afd2daa2SMarcel Holtmann 
236afd2daa2SMarcel Holtmann 	virtqueue_kick(vbt->vqs[VIRTBT_VQ_RX]);
237afd2daa2SMarcel Holtmann }
238afd2daa2SMarcel Holtmann 
virtbt_tx_done(struct virtqueue * vq)239afd2daa2SMarcel Holtmann static void virtbt_tx_done(struct virtqueue *vq)
240afd2daa2SMarcel Holtmann {
241afd2daa2SMarcel Holtmann 	struct sk_buff *skb;
242afd2daa2SMarcel Holtmann 	unsigned int len;
243afd2daa2SMarcel Holtmann 
244afd2daa2SMarcel Holtmann 	while ((skb = virtqueue_get_buf(vq, &len)))
245afd2daa2SMarcel Holtmann 		kfree_skb(skb);
246afd2daa2SMarcel Holtmann }
247afd2daa2SMarcel Holtmann 
virtbt_rx_done(struct virtqueue * vq)248afd2daa2SMarcel Holtmann static void virtbt_rx_done(struct virtqueue *vq)
249afd2daa2SMarcel Holtmann {
250afd2daa2SMarcel Holtmann 	struct virtio_bluetooth *vbt = vq->vdev->priv;
251afd2daa2SMarcel Holtmann 
252afd2daa2SMarcel Holtmann 	schedule_work(&vbt->rx);
253afd2daa2SMarcel Holtmann }
254afd2daa2SMarcel Holtmann 
virtbt_probe(struct virtio_device * vdev)255afd2daa2SMarcel Holtmann static int virtbt_probe(struct virtio_device *vdev)
256afd2daa2SMarcel Holtmann {
257afd2daa2SMarcel Holtmann 	vq_callback_t *callbacks[VIRTBT_NUM_VQS] = {
258afd2daa2SMarcel Holtmann 		[VIRTBT_VQ_TX] = virtbt_tx_done,
259afd2daa2SMarcel Holtmann 		[VIRTBT_VQ_RX] = virtbt_rx_done,
260afd2daa2SMarcel Holtmann 	};
261afd2daa2SMarcel Holtmann 	const char *names[VIRTBT_NUM_VQS] = {
262afd2daa2SMarcel Holtmann 		[VIRTBT_VQ_TX] = "tx",
263afd2daa2SMarcel Holtmann 		[VIRTBT_VQ_RX] = "rx",
264afd2daa2SMarcel Holtmann 	};
265afd2daa2SMarcel Holtmann 	struct virtio_bluetooth *vbt;
266afd2daa2SMarcel Holtmann 	struct hci_dev *hdev;
267afd2daa2SMarcel Holtmann 	int err;
268afd2daa2SMarcel Holtmann 	__u8 type;
269afd2daa2SMarcel Holtmann 
270afd2daa2SMarcel Holtmann 	if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1))
271afd2daa2SMarcel Holtmann 		return -ENODEV;
272afd2daa2SMarcel Holtmann 
273afd2daa2SMarcel Holtmann 	type = virtio_cread8(vdev, offsetof(struct virtio_bt_config, type));
274afd2daa2SMarcel Holtmann 
275afd2daa2SMarcel Holtmann 	switch (type) {
276afd2daa2SMarcel Holtmann 	case VIRTIO_BT_CONFIG_TYPE_PRIMARY:
277afd2daa2SMarcel Holtmann 		break;
278afd2daa2SMarcel Holtmann 	default:
279afd2daa2SMarcel Holtmann 		return -EINVAL;
280afd2daa2SMarcel Holtmann 	}
281afd2daa2SMarcel Holtmann 
282afd2daa2SMarcel Holtmann 	vbt = kzalloc(sizeof(*vbt), GFP_KERNEL);
283afd2daa2SMarcel Holtmann 	if (!vbt)
284afd2daa2SMarcel Holtmann 		return -ENOMEM;
285afd2daa2SMarcel Holtmann 
286afd2daa2SMarcel Holtmann 	vdev->priv = vbt;
287afd2daa2SMarcel Holtmann 	vbt->vdev = vdev;
288afd2daa2SMarcel Holtmann 
289afd2daa2SMarcel Holtmann 	INIT_WORK(&vbt->rx, virtbt_rx_work);
290afd2daa2SMarcel Holtmann 
291afd2daa2SMarcel Holtmann 	err = virtio_find_vqs(vdev, VIRTBT_NUM_VQS, vbt->vqs, callbacks,
292afd2daa2SMarcel Holtmann 			      names, NULL);
293afd2daa2SMarcel Holtmann 	if (err)
294afd2daa2SMarcel Holtmann 		return err;
295afd2daa2SMarcel Holtmann 
296afd2daa2SMarcel Holtmann 	hdev = hci_alloc_dev();
297afd2daa2SMarcel Holtmann 	if (!hdev) {
298afd2daa2SMarcel Holtmann 		err = -ENOMEM;
299afd2daa2SMarcel Holtmann 		goto failed;
300afd2daa2SMarcel Holtmann 	}
301afd2daa2SMarcel Holtmann 
302afd2daa2SMarcel Holtmann 	vbt->hdev = hdev;
303afd2daa2SMarcel Holtmann 
304afd2daa2SMarcel Holtmann 	hdev->bus = HCI_VIRTIO;
305afd2daa2SMarcel Holtmann 	hci_set_drvdata(hdev, vbt);
306afd2daa2SMarcel Holtmann 
307afd2daa2SMarcel Holtmann 	hdev->open  = virtbt_open;
308afd2daa2SMarcel Holtmann 	hdev->close = virtbt_close;
309afd2daa2SMarcel Holtmann 	hdev->flush = virtbt_flush;
310afd2daa2SMarcel Holtmann 	hdev->send  = virtbt_send_frame;
311afd2daa2SMarcel Holtmann 
312afd2daa2SMarcel Holtmann 	if (virtio_has_feature(vdev, VIRTIO_BT_F_VND_HCI)) {
313afd2daa2SMarcel Holtmann 		__u16 vendor;
314afd2daa2SMarcel Holtmann 
31547c50853SIgor Skalkin 		if (virtio_has_feature(vdev, VIRTIO_BT_F_CONFIG_V2))
31647c50853SIgor Skalkin 			virtio_cread(vdev, struct virtio_bt_config_v2,
31747c50853SIgor Skalkin 				     vendor, &vendor);
31847c50853SIgor Skalkin 		else
31947c50853SIgor Skalkin 			virtio_cread(vdev, struct virtio_bt_config,
32047c50853SIgor Skalkin 				     vendor, &vendor);
321afd2daa2SMarcel Holtmann 
322afd2daa2SMarcel Holtmann 		switch (vendor) {
323afd2daa2SMarcel Holtmann 		case VIRTIO_BT_CONFIG_VENDOR_ZEPHYR:
324afd2daa2SMarcel Holtmann 			hdev->manufacturer = 1521;
325afd2daa2SMarcel Holtmann 			hdev->setup = virtbt_setup_zephyr;
326afd2daa2SMarcel Holtmann 			hdev->shutdown = virtbt_shutdown_generic;
327afd2daa2SMarcel Holtmann 			hdev->set_bdaddr = virtbt_set_bdaddr_zephyr;
328afd2daa2SMarcel Holtmann 			break;
329afd2daa2SMarcel Holtmann 
330afd2daa2SMarcel Holtmann 		case VIRTIO_BT_CONFIG_VENDOR_INTEL:
331afd2daa2SMarcel Holtmann 			hdev->manufacturer = 2;
332afd2daa2SMarcel Holtmann 			hdev->setup = virtbt_setup_intel;
333afd2daa2SMarcel Holtmann 			hdev->shutdown = virtbt_shutdown_generic;
334afd2daa2SMarcel Holtmann 			hdev->set_bdaddr = virtbt_set_bdaddr_intel;
335afd2daa2SMarcel Holtmann 			set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks);
336afd2daa2SMarcel Holtmann 			set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
337afd2daa2SMarcel Holtmann 			set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks);
338afd2daa2SMarcel Holtmann 			break;
339afd2daa2SMarcel Holtmann 
340afd2daa2SMarcel Holtmann 		case VIRTIO_BT_CONFIG_VENDOR_REALTEK:
341afd2daa2SMarcel Holtmann 			hdev->manufacturer = 93;
342afd2daa2SMarcel Holtmann 			hdev->setup = virtbt_setup_realtek;
343afd2daa2SMarcel Holtmann 			hdev->shutdown = virtbt_shutdown_generic;
344afd2daa2SMarcel Holtmann 			set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks);
345afd2daa2SMarcel Holtmann 			set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks);
346afd2daa2SMarcel Holtmann 			break;
347afd2daa2SMarcel Holtmann 		}
348afd2daa2SMarcel Holtmann 	}
349afd2daa2SMarcel Holtmann 
350afd2daa2SMarcel Holtmann 	if (virtio_has_feature(vdev, VIRTIO_BT_F_MSFT_EXT)) {
351afd2daa2SMarcel Holtmann 		__u16 msft_opcode;
352afd2daa2SMarcel Holtmann 
35347c50853SIgor Skalkin 		if (virtio_has_feature(vdev, VIRTIO_BT_F_CONFIG_V2))
35447c50853SIgor Skalkin 			virtio_cread(vdev, struct virtio_bt_config_v2,
35547c50853SIgor Skalkin 				     msft_opcode, &msft_opcode);
35647c50853SIgor Skalkin 		else
357afd2daa2SMarcel Holtmann 			virtio_cread(vdev, struct virtio_bt_config,
358afd2daa2SMarcel Holtmann 				     msft_opcode, &msft_opcode);
359afd2daa2SMarcel Holtmann 
360afd2daa2SMarcel Holtmann 		hci_set_msft_opcode(hdev, msft_opcode);
361afd2daa2SMarcel Holtmann 	}
362afd2daa2SMarcel Holtmann 
363afd2daa2SMarcel Holtmann 	if (virtio_has_feature(vdev, VIRTIO_BT_F_AOSP_EXT))
364afd2daa2SMarcel Holtmann 		hci_set_aosp_capable(hdev);
365afd2daa2SMarcel Holtmann 
366afd2daa2SMarcel Holtmann 	if (hci_register_dev(hdev) < 0) {
367afd2daa2SMarcel Holtmann 		hci_free_dev(hdev);
368afd2daa2SMarcel Holtmann 		err = -EBUSY;
369afd2daa2SMarcel Holtmann 		goto failed;
370afd2daa2SMarcel Holtmann 	}
371afd2daa2SMarcel Holtmann 
372dc65b4b0SMichael S. Tsirkin 	virtio_device_ready(vdev);
373dc65b4b0SMichael S. Tsirkin 	err = virtbt_open_vdev(vbt);
374dc65b4b0SMichael S. Tsirkin 	if (err)
375dc65b4b0SMichael S. Tsirkin 		goto open_failed;
376dc65b4b0SMichael S. Tsirkin 
377afd2daa2SMarcel Holtmann 	return 0;
378afd2daa2SMarcel Holtmann 
379dc65b4b0SMichael S. Tsirkin open_failed:
380dc65b4b0SMichael S. Tsirkin 	hci_free_dev(hdev);
381afd2daa2SMarcel Holtmann failed:
382afd2daa2SMarcel Holtmann 	vdev->config->del_vqs(vdev);
383afd2daa2SMarcel Holtmann 	return err;
384afd2daa2SMarcel Holtmann }
385afd2daa2SMarcel Holtmann 
virtbt_remove(struct virtio_device * vdev)386afd2daa2SMarcel Holtmann static void virtbt_remove(struct virtio_device *vdev)
387afd2daa2SMarcel Holtmann {
388afd2daa2SMarcel Holtmann 	struct virtio_bluetooth *vbt = vdev->priv;
389afd2daa2SMarcel Holtmann 	struct hci_dev *hdev = vbt->hdev;
390afd2daa2SMarcel Holtmann 
391afd2daa2SMarcel Holtmann 	hci_unregister_dev(hdev);
392d9679d00SMichael S. Tsirkin 	virtio_reset_device(vdev);
393dc65b4b0SMichael S. Tsirkin 	virtbt_close_vdev(vbt);
394afd2daa2SMarcel Holtmann 
395afd2daa2SMarcel Holtmann 	hci_free_dev(hdev);
396afd2daa2SMarcel Holtmann 	vbt->hdev = NULL;
397afd2daa2SMarcel Holtmann 
398afd2daa2SMarcel Holtmann 	vdev->config->del_vqs(vdev);
399afd2daa2SMarcel Holtmann 	kfree(vbt);
400afd2daa2SMarcel Holtmann }
401afd2daa2SMarcel Holtmann 
402afd2daa2SMarcel Holtmann static struct virtio_device_id virtbt_table[] = {
403afd2daa2SMarcel Holtmann 	{ VIRTIO_ID_BT, VIRTIO_DEV_ANY_ID },
404afd2daa2SMarcel Holtmann 	{ 0 },
405afd2daa2SMarcel Holtmann };
406afd2daa2SMarcel Holtmann 
407afd2daa2SMarcel Holtmann MODULE_DEVICE_TABLE(virtio, virtbt_table);
408afd2daa2SMarcel Holtmann 
409afd2daa2SMarcel Holtmann static const unsigned int virtbt_features[] = {
410afd2daa2SMarcel Holtmann 	VIRTIO_BT_F_VND_HCI,
411afd2daa2SMarcel Holtmann 	VIRTIO_BT_F_MSFT_EXT,
412afd2daa2SMarcel Holtmann 	VIRTIO_BT_F_AOSP_EXT,
41347c50853SIgor Skalkin 	VIRTIO_BT_F_CONFIG_V2,
414afd2daa2SMarcel Holtmann };
415afd2daa2SMarcel Holtmann 
416afd2daa2SMarcel Holtmann static struct virtio_driver virtbt_driver = {
417afd2daa2SMarcel Holtmann 	.driver.name         = KBUILD_MODNAME,
418afd2daa2SMarcel Holtmann 	.driver.owner        = THIS_MODULE,
419afd2daa2SMarcel Holtmann 	.feature_table       = virtbt_features,
420afd2daa2SMarcel Holtmann 	.feature_table_size  = ARRAY_SIZE(virtbt_features),
421afd2daa2SMarcel Holtmann 	.id_table            = virtbt_table,
422afd2daa2SMarcel Holtmann 	.probe               = virtbt_probe,
423afd2daa2SMarcel Holtmann 	.remove              = virtbt_remove,
424afd2daa2SMarcel Holtmann };
425afd2daa2SMarcel Holtmann 
426afd2daa2SMarcel Holtmann module_virtio_driver(virtbt_driver);
427afd2daa2SMarcel Holtmann 
428afd2daa2SMarcel Holtmann MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>");
429afd2daa2SMarcel Holtmann MODULE_DESCRIPTION("Generic Bluetooth VIRTIO driver ver " VERSION);
430afd2daa2SMarcel Holtmann MODULE_VERSION(VERSION);
431afd2daa2SMarcel Holtmann MODULE_LICENSE("GPL");
432