drivers/virtio/virtio_input.c

Source file repositories/reference/linux-study-clean/drivers/virtio/virtio_input.c

File Facts

System
Linux kernel
Corpus path
drivers/virtio/virtio_input.c
Extension
.c
Size
11465 bytes
Lines
422
Domain
Driver Families
Bucket
drivers/virtio
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_input {
	struct virtio_device       *vdev;
	struct input_dev           *idev;
	char                       name[64];
	char                       serial[64];
	char                       phys[64];
	struct virtqueue           *evt, *sts;
	__dma_from_device_group_begin();
	struct virtio_input_event  evts[64];
	__dma_from_device_group_end();
	spinlock_t                 lock;
	bool                       ready;
};

static void virtinput_queue_evtbuf(struct virtio_input *vi,
				   struct virtio_input_event *evtbuf)
{
	struct scatterlist sg[1];

	sg_init_one(sg, evtbuf, sizeof(*evtbuf));
	virtqueue_add_inbuf_cache_clean(vi->evt, sg, 1, evtbuf, GFP_ATOMIC);
}

static void virtinput_recv_events(struct virtqueue *vq)
{
	struct virtio_input *vi = vq->vdev->priv;
	struct virtio_input_event *event;
	unsigned long flags;
	unsigned int len;

	spin_lock_irqsave(&vi->lock, flags);
	if (vi->ready) {
		while ((event = virtqueue_get_buf(vi->evt, &len)) != NULL) {
			spin_unlock_irqrestore(&vi->lock, flags);
			input_event(vi->idev,
				    le16_to_cpu(event->type),
				    le16_to_cpu(event->code),
				    le32_to_cpu(event->value));
			spin_lock_irqsave(&vi->lock, flags);
			virtinput_queue_evtbuf(vi, event);
		}
		virtqueue_kick(vq);
	}
	spin_unlock_irqrestore(&vi->lock, flags);
}

/*
 * On error we are losing the status update, which isn't critical as
 * this is typically used for stuff like keyboard leds.
 */
static int virtinput_send_status(struct virtio_input *vi,
				 u16 type, u16 code, s32 value)
{
	struct virtio_input_event *stsbuf;
	struct scatterlist sg[1];
	unsigned long flags;
	int rc;

	/*
	 * Since 29cc309d8bf1 (HID: hid-multitouch: forward MSC_TIMESTAMP),
	 * EV_MSC/MSC_TIMESTAMP is added to each before EV_SYN event.
	 * EV_MSC is configured as INPUT_PASS_TO_ALL.
	 * In case of touch device:
	 *   BE pass EV_MSC/MSC_TIMESTAMP to FE on receiving event from evdev.
	 *   FE pass EV_MSC/MSC_TIMESTAMP back to BE.
	 *   BE writes EV_MSC/MSC_TIMESTAMP to evdev due to INPUT_PASS_TO_ALL.
	 *   BE receives extra EV_MSC/MSC_TIMESTAMP and pass to FE.
	 *   >>> Each new frame becomes larger and larger.
	 * Disable EV_MSC/MSC_TIMESTAMP forwarding for MT.
	 */
	if (vi->idev->mt && type == EV_MSC && code == MSC_TIMESTAMP)
		return 0;

	stsbuf = kzalloc_obj(*stsbuf, GFP_ATOMIC);
	if (!stsbuf)
		return -ENOMEM;

	stsbuf->type  = cpu_to_le16(type);
	stsbuf->code  = cpu_to_le16(code);
	stsbuf->value = cpu_to_le32(value);
	sg_init_one(sg, stsbuf, sizeof(*stsbuf));

	spin_lock_irqsave(&vi->lock, flags);
	if (vi->ready) {
		rc = virtqueue_add_outbuf(vi->sts, sg, 1, stsbuf, GFP_ATOMIC);
		virtqueue_kick(vi->sts);
	} else {
		rc = -ENODEV;
	}
	spin_unlock_irqrestore(&vi->lock, flags);

Annotation

Implementation Notes