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 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 51afd2daa2SMarcel Holtmann static int virtbt_open(struct hci_dev *hdev) 52afd2daa2SMarcel Holtmann { 53dc65b4b0SMichael S. Tsirkin return 0; 54dc65b4b0SMichael S. Tsirkin } 55afd2daa2SMarcel Holtmann 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 65afd2daa2SMarcel Holtmann static int virtbt_close(struct hci_dev *hdev) 66afd2daa2SMarcel Holtmann { 67dc65b4b0SMichael S. Tsirkin return 0; 68dc65b4b0SMichael S. Tsirkin } 69dc65b4b0SMichael S. Tsirkin 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); 82afd2daa2SMarcel Holtmann } 83afd2daa2SMarcel Holtmann 84afd2daa2SMarcel Holtmann return 0; 85afd2daa2SMarcel Holtmann } 86afd2daa2SMarcel Holtmann 87afd2daa2SMarcel Holtmann static int virtbt_flush(struct hci_dev *hdev) 88afd2daa2SMarcel Holtmann { 89afd2daa2SMarcel Holtmann return 0; 90afd2daa2SMarcel Holtmann } 91afd2daa2SMarcel Holtmann 92afd2daa2SMarcel Holtmann static int virtbt_send_frame(struct hci_dev *hdev, struct sk_buff *skb) 93afd2daa2SMarcel Holtmann { 94afd2daa2SMarcel Holtmann struct virtio_bluetooth *vbt = hci_get_drvdata(hdev); 95afd2daa2SMarcel Holtmann struct scatterlist sg[1]; 96afd2daa2SMarcel Holtmann int err; 97afd2daa2SMarcel Holtmann 98afd2daa2SMarcel Holtmann memcpy(skb_push(skb, 1), &hci_skb_pkt_type(skb), 1); 99afd2daa2SMarcel Holtmann 100afd2daa2SMarcel Holtmann sg_init_one(sg, skb->data, skb->len); 101afd2daa2SMarcel Holtmann err = virtqueue_add_outbuf(vbt->vqs[VIRTBT_VQ_TX], sg, 1, skb, 102afd2daa2SMarcel Holtmann GFP_KERNEL); 103afd2daa2SMarcel Holtmann if (err) { 104afd2daa2SMarcel Holtmann kfree_skb(skb); 105afd2daa2SMarcel Holtmann return err; 106afd2daa2SMarcel Holtmann } 107afd2daa2SMarcel Holtmann 108afd2daa2SMarcel Holtmann virtqueue_kick(vbt->vqs[VIRTBT_VQ_TX]); 109afd2daa2SMarcel Holtmann return 0; 110afd2daa2SMarcel Holtmann } 111afd2daa2SMarcel Holtmann 112afd2daa2SMarcel Holtmann static int virtbt_setup_zephyr(struct hci_dev *hdev) 113afd2daa2SMarcel Holtmann { 114afd2daa2SMarcel Holtmann struct sk_buff *skb; 115afd2daa2SMarcel Holtmann 116afd2daa2SMarcel Holtmann /* Read Build Information */ 117afd2daa2SMarcel Holtmann skb = __hci_cmd_sync(hdev, 0xfc08, 0, NULL, HCI_INIT_TIMEOUT); 118afd2daa2SMarcel Holtmann if (IS_ERR(skb)) 119afd2daa2SMarcel Holtmann return PTR_ERR(skb); 120afd2daa2SMarcel Holtmann 121afd2daa2SMarcel Holtmann bt_dev_info(hdev, "%s", (char *)(skb->data + 1)); 122afd2daa2SMarcel Holtmann 123afd2daa2SMarcel Holtmann hci_set_fw_info(hdev, "%s", skb->data + 1); 124afd2daa2SMarcel Holtmann 125afd2daa2SMarcel Holtmann kfree_skb(skb); 126afd2daa2SMarcel Holtmann return 0; 127afd2daa2SMarcel Holtmann } 128afd2daa2SMarcel Holtmann 129afd2daa2SMarcel Holtmann static int virtbt_set_bdaddr_zephyr(struct hci_dev *hdev, 130afd2daa2SMarcel Holtmann const bdaddr_t *bdaddr) 131afd2daa2SMarcel Holtmann { 132afd2daa2SMarcel Holtmann struct sk_buff *skb; 133afd2daa2SMarcel Holtmann 134afd2daa2SMarcel Holtmann /* Write BD_ADDR */ 135afd2daa2SMarcel Holtmann skb = __hci_cmd_sync(hdev, 0xfc06, 6, bdaddr, HCI_INIT_TIMEOUT); 136afd2daa2SMarcel Holtmann if (IS_ERR(skb)) 137afd2daa2SMarcel Holtmann return PTR_ERR(skb); 138afd2daa2SMarcel Holtmann 139afd2daa2SMarcel Holtmann kfree_skb(skb); 140afd2daa2SMarcel Holtmann return 0; 141afd2daa2SMarcel Holtmann } 142afd2daa2SMarcel Holtmann 143afd2daa2SMarcel Holtmann static int virtbt_setup_intel(struct hci_dev *hdev) 144afd2daa2SMarcel Holtmann { 145afd2daa2SMarcel Holtmann struct sk_buff *skb; 146afd2daa2SMarcel Holtmann 147afd2daa2SMarcel Holtmann /* Intel Read Version */ 148afd2daa2SMarcel Holtmann skb = __hci_cmd_sync(hdev, 0xfc05, 0, NULL, HCI_CMD_TIMEOUT); 149afd2daa2SMarcel Holtmann if (IS_ERR(skb)) 150afd2daa2SMarcel Holtmann return PTR_ERR(skb); 151afd2daa2SMarcel Holtmann 152afd2daa2SMarcel Holtmann kfree_skb(skb); 153afd2daa2SMarcel Holtmann return 0; 154afd2daa2SMarcel Holtmann } 155afd2daa2SMarcel Holtmann 156afd2daa2SMarcel Holtmann static int virtbt_set_bdaddr_intel(struct hci_dev *hdev, const bdaddr_t *bdaddr) 157afd2daa2SMarcel Holtmann { 158afd2daa2SMarcel Holtmann struct sk_buff *skb; 159afd2daa2SMarcel Holtmann 160afd2daa2SMarcel Holtmann /* Intel Write BD Address */ 161afd2daa2SMarcel Holtmann skb = __hci_cmd_sync(hdev, 0xfc31, 6, bdaddr, HCI_INIT_TIMEOUT); 162afd2daa2SMarcel Holtmann if (IS_ERR(skb)) 163afd2daa2SMarcel Holtmann return PTR_ERR(skb); 164afd2daa2SMarcel Holtmann 165afd2daa2SMarcel Holtmann kfree_skb(skb); 166afd2daa2SMarcel Holtmann return 0; 167afd2daa2SMarcel Holtmann } 168afd2daa2SMarcel Holtmann 169afd2daa2SMarcel Holtmann static int virtbt_setup_realtek(struct hci_dev *hdev) 170afd2daa2SMarcel Holtmann { 171afd2daa2SMarcel Holtmann struct sk_buff *skb; 172afd2daa2SMarcel Holtmann 173afd2daa2SMarcel Holtmann /* Read ROM Version */ 174afd2daa2SMarcel Holtmann skb = __hci_cmd_sync(hdev, 0xfc6d, 0, NULL, HCI_INIT_TIMEOUT); 175afd2daa2SMarcel Holtmann if (IS_ERR(skb)) 176afd2daa2SMarcel Holtmann return PTR_ERR(skb); 177afd2daa2SMarcel Holtmann 178afd2daa2SMarcel Holtmann bt_dev_info(hdev, "ROM version %u", *((__u8 *) (skb->data + 1))); 179afd2daa2SMarcel Holtmann 180afd2daa2SMarcel Holtmann kfree_skb(skb); 181afd2daa2SMarcel Holtmann return 0; 182afd2daa2SMarcel Holtmann } 183afd2daa2SMarcel Holtmann 184afd2daa2SMarcel Holtmann static int virtbt_shutdown_generic(struct hci_dev *hdev) 185afd2daa2SMarcel Holtmann { 186afd2daa2SMarcel Holtmann struct sk_buff *skb; 187afd2daa2SMarcel Holtmann 188afd2daa2SMarcel Holtmann /* Reset */ 189afd2daa2SMarcel Holtmann skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT); 190afd2daa2SMarcel Holtmann if (IS_ERR(skb)) 191afd2daa2SMarcel Holtmann return PTR_ERR(skb); 192afd2daa2SMarcel Holtmann 193afd2daa2SMarcel Holtmann kfree_skb(skb); 194afd2daa2SMarcel Holtmann return 0; 195afd2daa2SMarcel Holtmann } 196afd2daa2SMarcel Holtmann 197afd2daa2SMarcel Holtmann static void virtbt_rx_handle(struct virtio_bluetooth *vbt, struct sk_buff *skb) 198afd2daa2SMarcel Holtmann { 199afd2daa2SMarcel Holtmann __u8 pkt_type; 200afd2daa2SMarcel Holtmann 201afd2daa2SMarcel Holtmann pkt_type = *((__u8 *) skb->data); 202afd2daa2SMarcel Holtmann skb_pull(skb, 1); 203afd2daa2SMarcel Holtmann 204afd2daa2SMarcel Holtmann switch (pkt_type) { 205afd2daa2SMarcel Holtmann case HCI_EVENT_PKT: 206afd2daa2SMarcel Holtmann case HCI_ACLDATA_PKT: 207afd2daa2SMarcel Holtmann case HCI_SCODATA_PKT: 208afd2daa2SMarcel Holtmann case HCI_ISODATA_PKT: 209afd2daa2SMarcel Holtmann hci_skb_pkt_type(skb) = pkt_type; 210afd2daa2SMarcel Holtmann hci_recv_frame(vbt->hdev, skb); 211afd2daa2SMarcel Holtmann break; 2121d068842SSoenke Huster default: 2131d068842SSoenke Huster kfree_skb(skb); 2141d068842SSoenke Huster break; 215afd2daa2SMarcel Holtmann } 216afd2daa2SMarcel Holtmann } 217afd2daa2SMarcel Holtmann 218afd2daa2SMarcel Holtmann static void virtbt_rx_work(struct work_struct *work) 219afd2daa2SMarcel Holtmann { 220afd2daa2SMarcel Holtmann struct virtio_bluetooth *vbt = container_of(work, 221afd2daa2SMarcel Holtmann struct virtio_bluetooth, rx); 222afd2daa2SMarcel Holtmann struct sk_buff *skb; 223afd2daa2SMarcel Holtmann unsigned int len; 224afd2daa2SMarcel Holtmann 225afd2daa2SMarcel Holtmann skb = virtqueue_get_buf(vbt->vqs[VIRTBT_VQ_RX], &len); 226afd2daa2SMarcel Holtmann if (!skb) 227afd2daa2SMarcel Holtmann return; 228afd2daa2SMarcel Holtmann 229160fbcf3SSoenke Huster skb_put(skb, len); 230afd2daa2SMarcel Holtmann virtbt_rx_handle(vbt, skb); 231afd2daa2SMarcel Holtmann 232afd2daa2SMarcel Holtmann if (virtbt_add_inbuf(vbt) < 0) 233afd2daa2SMarcel Holtmann return; 234afd2daa2SMarcel Holtmann 235afd2daa2SMarcel Holtmann virtqueue_kick(vbt->vqs[VIRTBT_VQ_RX]); 236afd2daa2SMarcel Holtmann } 237afd2daa2SMarcel Holtmann 238afd2daa2SMarcel Holtmann static void virtbt_tx_done(struct virtqueue *vq) 239afd2daa2SMarcel Holtmann { 240afd2daa2SMarcel Holtmann struct sk_buff *skb; 241afd2daa2SMarcel Holtmann unsigned int len; 242afd2daa2SMarcel Holtmann 243afd2daa2SMarcel Holtmann while ((skb = virtqueue_get_buf(vq, &len))) 244afd2daa2SMarcel Holtmann kfree_skb(skb); 245afd2daa2SMarcel Holtmann } 246afd2daa2SMarcel Holtmann 247afd2daa2SMarcel Holtmann static void virtbt_rx_done(struct virtqueue *vq) 248afd2daa2SMarcel Holtmann { 249afd2daa2SMarcel Holtmann struct virtio_bluetooth *vbt = vq->vdev->priv; 250afd2daa2SMarcel Holtmann 251afd2daa2SMarcel Holtmann schedule_work(&vbt->rx); 252afd2daa2SMarcel Holtmann } 253afd2daa2SMarcel Holtmann 254afd2daa2SMarcel Holtmann static int virtbt_probe(struct virtio_device *vdev) 255afd2daa2SMarcel Holtmann { 256afd2daa2SMarcel Holtmann vq_callback_t *callbacks[VIRTBT_NUM_VQS] = { 257afd2daa2SMarcel Holtmann [VIRTBT_VQ_TX] = virtbt_tx_done, 258afd2daa2SMarcel Holtmann [VIRTBT_VQ_RX] = virtbt_rx_done, 259afd2daa2SMarcel Holtmann }; 260afd2daa2SMarcel Holtmann const char *names[VIRTBT_NUM_VQS] = { 261afd2daa2SMarcel Holtmann [VIRTBT_VQ_TX] = "tx", 262afd2daa2SMarcel Holtmann [VIRTBT_VQ_RX] = "rx", 263afd2daa2SMarcel Holtmann }; 264afd2daa2SMarcel Holtmann struct virtio_bluetooth *vbt; 265afd2daa2SMarcel Holtmann struct hci_dev *hdev; 266afd2daa2SMarcel Holtmann int err; 267afd2daa2SMarcel Holtmann __u8 type; 268afd2daa2SMarcel Holtmann 269afd2daa2SMarcel Holtmann if (!virtio_has_feature(vdev, VIRTIO_F_VERSION_1)) 270afd2daa2SMarcel Holtmann return -ENODEV; 271afd2daa2SMarcel Holtmann 272afd2daa2SMarcel Holtmann type = virtio_cread8(vdev, offsetof(struct virtio_bt_config, type)); 273afd2daa2SMarcel Holtmann 274afd2daa2SMarcel Holtmann switch (type) { 275afd2daa2SMarcel Holtmann case VIRTIO_BT_CONFIG_TYPE_PRIMARY: 276afd2daa2SMarcel Holtmann case VIRTIO_BT_CONFIG_TYPE_AMP: 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 hdev->dev_type = type; 306afd2daa2SMarcel Holtmann hci_set_drvdata(hdev, vbt); 307afd2daa2SMarcel Holtmann 308afd2daa2SMarcel Holtmann hdev->open = virtbt_open; 309afd2daa2SMarcel Holtmann hdev->close = virtbt_close; 310afd2daa2SMarcel Holtmann hdev->flush = virtbt_flush; 311afd2daa2SMarcel Holtmann hdev->send = virtbt_send_frame; 312afd2daa2SMarcel Holtmann 313afd2daa2SMarcel Holtmann if (virtio_has_feature(vdev, VIRTIO_BT_F_VND_HCI)) { 314afd2daa2SMarcel Holtmann __u16 vendor; 315afd2daa2SMarcel Holtmann 316*47c50853SIgor Skalkin if (virtio_has_feature(vdev, VIRTIO_BT_F_CONFIG_V2)) 317*47c50853SIgor Skalkin virtio_cread(vdev, struct virtio_bt_config_v2, 318*47c50853SIgor Skalkin vendor, &vendor); 319*47c50853SIgor Skalkin else 320*47c50853SIgor Skalkin virtio_cread(vdev, struct virtio_bt_config, 321*47c50853SIgor Skalkin vendor, &vendor); 322afd2daa2SMarcel Holtmann 323afd2daa2SMarcel Holtmann switch (vendor) { 324afd2daa2SMarcel Holtmann case VIRTIO_BT_CONFIG_VENDOR_ZEPHYR: 325afd2daa2SMarcel Holtmann hdev->manufacturer = 1521; 326afd2daa2SMarcel Holtmann hdev->setup = virtbt_setup_zephyr; 327afd2daa2SMarcel Holtmann hdev->shutdown = virtbt_shutdown_generic; 328afd2daa2SMarcel Holtmann hdev->set_bdaddr = virtbt_set_bdaddr_zephyr; 329afd2daa2SMarcel Holtmann break; 330afd2daa2SMarcel Holtmann 331afd2daa2SMarcel Holtmann case VIRTIO_BT_CONFIG_VENDOR_INTEL: 332afd2daa2SMarcel Holtmann hdev->manufacturer = 2; 333afd2daa2SMarcel Holtmann hdev->setup = virtbt_setup_intel; 334afd2daa2SMarcel Holtmann hdev->shutdown = virtbt_shutdown_generic; 335afd2daa2SMarcel Holtmann hdev->set_bdaddr = virtbt_set_bdaddr_intel; 336afd2daa2SMarcel Holtmann set_bit(HCI_QUIRK_STRICT_DUPLICATE_FILTER, &hdev->quirks); 337afd2daa2SMarcel Holtmann set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks); 338afd2daa2SMarcel Holtmann set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks); 339afd2daa2SMarcel Holtmann break; 340afd2daa2SMarcel Holtmann 341afd2daa2SMarcel Holtmann case VIRTIO_BT_CONFIG_VENDOR_REALTEK: 342afd2daa2SMarcel Holtmann hdev->manufacturer = 93; 343afd2daa2SMarcel Holtmann hdev->setup = virtbt_setup_realtek; 344afd2daa2SMarcel Holtmann hdev->shutdown = virtbt_shutdown_generic; 345afd2daa2SMarcel Holtmann set_bit(HCI_QUIRK_SIMULTANEOUS_DISCOVERY, &hdev->quirks); 346afd2daa2SMarcel Holtmann set_bit(HCI_QUIRK_WIDEBAND_SPEECH_SUPPORTED, &hdev->quirks); 347afd2daa2SMarcel Holtmann break; 348afd2daa2SMarcel Holtmann } 349afd2daa2SMarcel Holtmann } 350afd2daa2SMarcel Holtmann 351afd2daa2SMarcel Holtmann if (virtio_has_feature(vdev, VIRTIO_BT_F_MSFT_EXT)) { 352afd2daa2SMarcel Holtmann __u16 msft_opcode; 353afd2daa2SMarcel Holtmann 354*47c50853SIgor Skalkin if (virtio_has_feature(vdev, VIRTIO_BT_F_CONFIG_V2)) 355*47c50853SIgor Skalkin virtio_cread(vdev, struct virtio_bt_config_v2, 356*47c50853SIgor Skalkin msft_opcode, &msft_opcode); 357*47c50853SIgor Skalkin else 358afd2daa2SMarcel Holtmann virtio_cread(vdev, struct virtio_bt_config, 359afd2daa2SMarcel Holtmann msft_opcode, &msft_opcode); 360afd2daa2SMarcel Holtmann 361afd2daa2SMarcel Holtmann hci_set_msft_opcode(hdev, msft_opcode); 362afd2daa2SMarcel Holtmann } 363afd2daa2SMarcel Holtmann 364afd2daa2SMarcel Holtmann if (virtio_has_feature(vdev, VIRTIO_BT_F_AOSP_EXT)) 365afd2daa2SMarcel Holtmann hci_set_aosp_capable(hdev); 366afd2daa2SMarcel Holtmann 367afd2daa2SMarcel Holtmann if (hci_register_dev(hdev) < 0) { 368afd2daa2SMarcel Holtmann hci_free_dev(hdev); 369afd2daa2SMarcel Holtmann err = -EBUSY; 370afd2daa2SMarcel Holtmann goto failed; 371afd2daa2SMarcel Holtmann } 372afd2daa2SMarcel Holtmann 373dc65b4b0SMichael S. Tsirkin virtio_device_ready(vdev); 374dc65b4b0SMichael S. Tsirkin err = virtbt_open_vdev(vbt); 375dc65b4b0SMichael S. Tsirkin if (err) 376dc65b4b0SMichael S. Tsirkin goto open_failed; 377dc65b4b0SMichael S. Tsirkin 378afd2daa2SMarcel Holtmann return 0; 379afd2daa2SMarcel Holtmann 380dc65b4b0SMichael S. Tsirkin open_failed: 381dc65b4b0SMichael S. Tsirkin hci_free_dev(hdev); 382afd2daa2SMarcel Holtmann failed: 383afd2daa2SMarcel Holtmann vdev->config->del_vqs(vdev); 384afd2daa2SMarcel Holtmann return err; 385afd2daa2SMarcel Holtmann } 386afd2daa2SMarcel Holtmann 387afd2daa2SMarcel Holtmann static void virtbt_remove(struct virtio_device *vdev) 388afd2daa2SMarcel Holtmann { 389afd2daa2SMarcel Holtmann struct virtio_bluetooth *vbt = vdev->priv; 390afd2daa2SMarcel Holtmann struct hci_dev *hdev = vbt->hdev; 391afd2daa2SMarcel Holtmann 392afd2daa2SMarcel Holtmann hci_unregister_dev(hdev); 393d9679d00SMichael S. Tsirkin virtio_reset_device(vdev); 394dc65b4b0SMichael S. Tsirkin virtbt_close_vdev(vbt); 395afd2daa2SMarcel Holtmann 396afd2daa2SMarcel Holtmann hci_free_dev(hdev); 397afd2daa2SMarcel Holtmann vbt->hdev = NULL; 398afd2daa2SMarcel Holtmann 399afd2daa2SMarcel Holtmann vdev->config->del_vqs(vdev); 400afd2daa2SMarcel Holtmann kfree(vbt); 401afd2daa2SMarcel Holtmann } 402afd2daa2SMarcel Holtmann 403afd2daa2SMarcel Holtmann static struct virtio_device_id virtbt_table[] = { 404afd2daa2SMarcel Holtmann { VIRTIO_ID_BT, VIRTIO_DEV_ANY_ID }, 405afd2daa2SMarcel Holtmann { 0 }, 406afd2daa2SMarcel Holtmann }; 407afd2daa2SMarcel Holtmann 408afd2daa2SMarcel Holtmann MODULE_DEVICE_TABLE(virtio, virtbt_table); 409afd2daa2SMarcel Holtmann 410afd2daa2SMarcel Holtmann static const unsigned int virtbt_features[] = { 411afd2daa2SMarcel Holtmann VIRTIO_BT_F_VND_HCI, 412afd2daa2SMarcel Holtmann VIRTIO_BT_F_MSFT_EXT, 413afd2daa2SMarcel Holtmann VIRTIO_BT_F_AOSP_EXT, 414*47c50853SIgor Skalkin VIRTIO_BT_F_CONFIG_V2, 415afd2daa2SMarcel Holtmann }; 416afd2daa2SMarcel Holtmann 417afd2daa2SMarcel Holtmann static struct virtio_driver virtbt_driver = { 418afd2daa2SMarcel Holtmann .driver.name = KBUILD_MODNAME, 419afd2daa2SMarcel Holtmann .driver.owner = THIS_MODULE, 420afd2daa2SMarcel Holtmann .feature_table = virtbt_features, 421afd2daa2SMarcel Holtmann .feature_table_size = ARRAY_SIZE(virtbt_features), 422afd2daa2SMarcel Holtmann .id_table = virtbt_table, 423afd2daa2SMarcel Holtmann .probe = virtbt_probe, 424afd2daa2SMarcel Holtmann .remove = virtbt_remove, 425afd2daa2SMarcel Holtmann }; 426afd2daa2SMarcel Holtmann 427afd2daa2SMarcel Holtmann module_virtio_driver(virtbt_driver); 428afd2daa2SMarcel Holtmann 429afd2daa2SMarcel Holtmann MODULE_AUTHOR("Marcel Holtmann <marcel@holtmann.org>"); 430afd2daa2SMarcel Holtmann MODULE_DESCRIPTION("Generic Bluetooth VIRTIO driver ver " VERSION); 431afd2daa2SMarcel Holtmann MODULE_VERSION(VERSION); 432afd2daa2SMarcel Holtmann MODULE_LICENSE("GPL"); 433