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