1 /*
2  * Copyright (c) 2025 Antmicro <www.antmicro.com>
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/logging/log.h>
8 #include <zephyr/sys/byteorder.h>
9 #include <zephyr/drivers/virtio.h>
10 #include <zephyr/drivers/virtio/virtqueue.h>
11 #include "virtio_common.h"
12 
13 LOG_MODULE_REGISTER(virtio_common, CONFIG_VIRTIO_LOG_LEVEL);
14 
virtio_isr(const struct device * dev,uint8_t isr_status,uint16_t virtqueue_count)15 void virtio_isr(const struct device *dev, uint8_t isr_status, uint16_t virtqueue_count)
16 {
17 	if (isr_status & VIRTIO_QUEUE_INTERRUPT) {
18 		for (int i = 0; i < virtqueue_count; i++) {
19 			struct virtq *vq = virtio_get_virtqueue(dev, i);
20 			uint16_t used_idx = sys_le16_to_cpu(vq->used->idx);
21 
22 			while (vq->last_used_idx != used_idx) {
23 				uint16_t idx = vq->last_used_idx % vq->num;
24 				uint16_t idx_le = sys_cpu_to_le16(idx);
25 				uint16_t chain_head_le = vq->used->ring[idx_le].id;
26 				uint16_t chain_head = sys_le16_to_cpu(chain_head_le);
27 				uint32_t used_len = sys_le32_to_cpu(
28 					vq->used->ring[idx_le].len
29 				);
30 
31 				/*
32 				 * We are making a copy here, because chain will be
33 				 * returned before invoking the callback and may be
34 				 * overwritten by the time callback is called. This
35 				 * is to allow callback to immediately place the
36 				 * descriptors back in the avail_ring
37 				 */
38 				struct virtq_receive_callback_entry cbe =
39 					vq->recv_cbs[chain_head];
40 
41 				uint16_t next = chain_head;
42 				bool last = false;
43 
44 				/*
45 				 * We are done processing the descriptor chain, and
46 				 * we can add used descriptors back to the free stack.
47 				 * The only thing left to do is calling the callback
48 				 * associated with the chain, but it was saved above on
49 				 * the stack, so other code is free to use the descriptors
50 				 */
51 				while (!last) {
52 					uint16_t curr = next;
53 					uint16_t curr_le = sys_cpu_to_le16(curr);
54 
55 					next = vq->desc[curr_le].next;
56 					last = !(vq->desc[curr_le].flags & VIRTQ_DESC_F_NEXT);
57 					virtq_add_free_desc(vq, curr);
58 				}
59 
60 				vq->last_used_idx++;
61 
62 				if (cbe.cb) {
63 					cbe.cb(cbe.opaque, used_len);
64 				}
65 			}
66 		}
67 	}
68 	if (isr_status & VIRTIO_DEVICE_CONFIGURATION_INTERRUPT) {
69 		LOG_ERR("device configuration change interrupt is currently unsupported");
70 	}
71 }
72