drivers/bluetooth/virtio_bt.c

Source file repositories/reference/linux-study-clean/drivers/bluetooth/virtio_bt.c

File Facts

System
Linux kernel
Corpus path
drivers/bluetooth/virtio_bt.c
Extension
.c
Size
9462 bytes
Lines
451
Domain
Driver Families
Bucket
drivers/bluetooth
Inferred role
Driver Families: implementation source
Status
source implementation candidate

Why This File Exists

Repeatable hardware-adapter layer. Deep compatibility for every driver is out of scope; this atlas records patterns, probe lifecycles, bus glue, IRQ/DMA usage, and links back to core abstractions.

Dependency Surface

Detected Declarations

Annotated Snippet

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(VIRTBT_RX_BUF_SIZE, GFP_KERNEL);
	if (!skb)
		return -ENOMEM;

	sg_init_one(sg, skb->data, VIRTBT_RX_BUF_SIZE);

	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)
{
	return 0;
}

static int virtbt_open_vdev(struct virtio_bluetooth *vbt)
{
	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)
{
	return 0;
}

static int virtbt_close_vdev(struct virtio_bluetooth *vbt)
{
	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);
		cond_resched();
	}

	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;
}

Annotation

Implementation Notes