1 /*
2  * Copyright (c) 2025 Antmicro <www.antmicro.com>
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT virtio_console
8 #include <zephyr/device.h>
9 #include <zephyr/drivers/virtio.h>
10 #include <zephyr/drivers/virtio/virtqueue.h>
11 #include <zephyr/spinlock.h>
12 #include <zephyr/drivers/uart.h>
13 #include <zephyr/sys/atomic.h>
14 #include <zephyr/sys/byteorder.h>
15 #include <zephyr/logging/log.h>
16 LOG_MODULE_REGISTER(virtio_console, CONFIG_UART_LOG_LEVEL);
17 
18 struct virtconsole_config {
19 	const struct device *vdev;
20 };
21 
22 struct _virtio_console_config {
23 	uint16_t cols;
24 	uint16_t rows;
25 	uint32_t max_nr_ports;
26 	uint32_t emerg_wr;
27 };
28 #ifdef CONFIG_UART_VIRTIO_CONSOLE_F_MULTIPORT
29 struct _virtio_console_control {
30 	uint32_t port;
31 	uint16_t event;
32 	uint16_t value;
33 	/* Device can give human-readable names to ports by sending */
34 	/* VIRTIO_CONSOLE_PORT_NAME immediately followed by a name */
35 	char name[CONFIG_UART_VIRTIO_CONSOLE_NAME_BUFSIZE];
36 };
37 struct _fifo_item_virtio_console_control {
38 	void *fifo_reserved;
39 	bool pending; /* true if message is awaiting transmission */
40 	struct _virtio_console_control msg;
41 };
42 #endif
43 
44 enum _flags {
45 	RX_IRQ_ENABLED
46 };
47 
48 enum _virtio_feature_bits {
49 	VIRTIO_CONSOLE_F_SIZE,
50 	VIRTIO_CONSOLE_F_MULTIPORT,
51 	VIRTIO_CONSOLE_F_EMERG_WRITE
52 };
53 
54 /* Virtqueues frequently used explicitly */
55 enum _named_virtqueues {
56 	VIRTQ_RX,
57 	VIRTQ_TX,
58 	VIRTQ_CONTROL_RX,
59 	VIRTQ_CONTROL_TX
60 };
61 #ifdef CONFIG_UART_VIRTIO_CONSOLE_F_MULTIPORT
62 enum _virtio_ctl_events {
63 	VIRTIO_CONSOLE_DEVICE_READY,
64 	VIRTIO_CONSOLE_DEVICE_ADD,
65 	VIRTIO_CONSOLE_DEVICE_REMOVE,
66 	VIRTIO_CONSOLE_PORT_READY,
67 	VIRTIO_CONSOLE_CONSOLE_PORT,
68 	VIRTIO_CONSOLE_RESIZE,
69 	VIRTIO_CONSOLE_PORT_OPEN,
70 	VIRTIO_CONSOLE_PORT_NAME
71 };
72 
73 struct _ctl_cb_data {
74 	struct virtconsole_data *data;
75 	int buf_no;
76 };
77 #endif
78 
79 /* This should be enough as QEMU only allows 31 */
80 #define VIRTIO_CONSOLE_MAX_PORTS 32
81 
82 /* Allows virtconsole_recv_cb to know which virtqueue it was called by */
83 struct _rx_cb_data {
84 	struct virtconsole_data *data;
85 	uint16_t port;
86 };
87 
88 /* Convert port numbers to virtqueue indices */
89 #define PORT_TO_RX_VQ_IDX(p) ((p) ? ((p + 1) * 2) : (VIRTQ_RX))
90 #define PORT_TO_TX_VQ_IDX(p) (PORT_TO_RX_VQ_IDX(p) + 1)
91 
92 /* Convert virtqueue index to port number */
vq_idx_to_port(uint16_t q)93 static int8_t vq_idx_to_port(uint16_t q)
94 {
95 	if (q % 2) { /* transmit queue (odd-numbered) */
96 		if (q == VIRTQ_TX) {
97 			return 0;
98 		} else if (q != VIRTQ_CONTROL_TX) {
99 			return (q / 2) - 1;
100 		}
101 	} else { /* receive queue (even-numbered) */
102 		if (q == VIRTQ_RX) {
103 			return 0;
104 		} else if (q != VIRTQ_CONTROL_RX) {
105 			return (q / 2) - 1;
106 		}
107 	}
108 	return -1; /* control queues are not assigned to any port */
109 }
110 
111 struct virtconsole_data {
112 	const struct device *dev;
113 #ifdef CONFIG_UART_VIRTIO_CONSOLE_F_MULTIPORT
114 	/* bitmask of ports to be used as console */
115 	uint32_t console_ports;
116 	int8_t n_console_ports;
117 	size_t txctlcurrent;
118 	struct _virtio_console_control rx_ctlbuf[CONFIG_UART_VIRTIO_CONSOLE_RX_CONTROL_BUFSIZE];
119 	struct _fifo_item_virtio_console_control
120 		tx_ctlbuf[CONFIG_UART_VIRTIO_CONSOLE_TX_CONTROL_BUFSIZE];
121 	struct k_fifo tx_ctlfifo;
122 	struct _ctl_cb_data ctl_cb_data[CONFIG_UART_VIRTIO_CONSOLE_RX_CONTROL_BUFSIZE];
123 	struct _rx_cb_data rx_cb_data[VIRTIO_CONSOLE_MAX_PORTS];
124 #else
125 	struct _rx_cb_data rx_cb_data[1];
126 #endif
127 	struct k_spinlock txsl;
128 	char rxbuf[CONFIG_UART_VIRTIO_CONSOLE_RX_BUFSIZE],
129 		txbuf[CONFIG_UART_VIRTIO_CONSOLE_TX_BUFSIZE];
130 	atomic_t flags;
131 	atomic_t rx_started, rx_ready;
132 	size_t rxcurrent, txcurrent;
133 	uart_irq_callback_user_data_t irq_cb;
134 	void *irq_cb_data;
135 	struct _virtio_console_config *virtio_devcfg;
136 };
137 
138 /* Return desired size for given virtqueue */
virtconsole_enum_queues_cb(uint16_t q_index,uint16_t q_size_max,void *)139 static uint16_t virtconsole_enum_queues_cb(uint16_t q_index, uint16_t q_size_max, void *)
140 {
141 	switch (q_index) {
142 #ifdef CONFIG_UART_VIRTIO_CONSOLE_F_MULTIPORT
143 	case VIRTQ_CONTROL_RX:
144 		return CONFIG_UART_VIRTIO_CONSOLE_RX_CONTROL_BUFSIZE;
145 	case VIRTQ_CONTROL_TX:
146 		return CONFIG_UART_VIRTIO_CONSOLE_TX_CONTROL_BUFSIZE;
147 #endif
148 	default:
149 		return 1;
150 	}
151 }
152 
virtconsole_recv_cb(void * priv,uint32_t len)153 static void virtconsole_recv_cb(void *priv, uint32_t len)
154 {
155 	struct _rx_cb_data *cbdata = priv;
156 	struct virtconsole_data *data = cbdata->data;
157 
158 	atomic_set_bit(&(data->rx_ready), cbdata->port);
159 	if (atomic_test_bit(&data->flags, RX_IRQ_ENABLED) && data->irq_cb) {
160 		data->irq_cb(data->dev, data->irq_cb_data);
161 	}
162 }
163 
virtconsole_recv_setup(const struct device * dev,uint16_t q_no,void * addr,uint32_t len,void (* recv_cb)(void *,uint32_t),void * cb_data)164 static void virtconsole_recv_setup(const struct device *dev, uint16_t q_no, void *addr,
165 				   uint32_t len, void (*recv_cb)(void *, uint32_t), void *cb_data)
166 {
167 	if (q_no % 2) {
168 		return; /* This should not be called on tx queues (odd-numbered) */
169 	}
170 	const struct virtconsole_config *config = dev->config;
171 	struct virtconsole_data *data = dev->data;
172 
173 	int port = vq_idx_to_port(q_no);
174 
175 	if ((port >= 0) && (port < VIRTIO_CONSOLE_MAX_PORTS)) {
176 		atomic_set_bit(&(data->rx_started), port);
177 	}
178 	struct virtq *vq = virtio_get_virtqueue(config->vdev, q_no);
179 
180 	if (vq == NULL) {
181 		LOG_ERR("could not access virtqueue %u", q_no);
182 		return;
183 	}
184 	struct virtq_buf vqbuf[] = {{.addr = addr, .len = len}};
185 
186 	if (virtq_add_buffer_chain(vq, vqbuf, 1, 0, recv_cb, cb_data, K_NO_WAIT)) {
187 		LOG_ERR("could not set up virtqueue %u for receiving", q_no);
188 		return;
189 	}
190 	virtio_notify_virtqueue(config->vdev, q_no);
191 }
192 #ifdef CONFIG_UART_VIRTIO_CONSOLE_F_MULTIPORT
virtconsole_control_tx_flush(void * priv,uint32_t len)193 static void virtconsole_control_tx_flush(void *priv, uint32_t len)
194 {
195 	struct virtconsole_data *data = priv;
196 	const struct device *dev = data->dev;
197 	const struct virtconsole_config *config = dev->config;
198 	struct _fifo_item_virtio_console_control *item;
199 	struct virtq *vq = virtio_get_virtqueue(config->vdev, VIRTQ_CONTROL_TX);
200 
201 	if (vq == NULL) {
202 		LOG_ERR("could not access virtqueue 3");
203 		return;
204 	}
205 	int i = vq->free_desc_n;
206 
207 	while ((i-- > 0) && (item = k_fifo_get(&data->tx_ctlfifo, K_NO_WAIT))) {
208 		struct virtq_buf vqbuf = {.addr = &item->msg, .len = sizeof(item->msg)};
209 
210 		int ret = virtq_add_buffer_chain(vq, &vqbuf, 1, 1, virtconsole_control_tx_flush,
211 						 priv, K_NO_WAIT);
212 
213 		if (ret) {
214 			LOG_ERR("could not send control message");
215 			return;
216 		}
217 		virtio_notify_virtqueue(config->vdev, VIRTQ_CONTROL_TX);
218 		item->pending = false;
219 	}
220 }
221 
virtconsole_send_control_msg(const struct device * dev,uint32_t port,uint16_t event,uint16_t value)222 static void virtconsole_send_control_msg(const struct device *dev, uint32_t port, uint16_t event,
223 					 uint16_t value)
224 {
225 	const struct virtconsole_config *config = dev->config;
226 	struct virtconsole_data *data = dev->data;
227 
228 	struct virtq *vq = virtio_get_virtqueue(config->vdev, VIRTQ_CONTROL_TX);
229 
230 	if (vq == NULL) {
231 		LOG_ERR("could not access virtqueue 3");
232 		return;
233 	}
234 	struct _fifo_item_virtio_console_control *item = &(data->tx_ctlbuf[data->txctlcurrent]);
235 	struct _virtio_console_control *msg = &(item->msg);
236 
237 	if (item->pending) {
238 		LOG_ERR("not enough free buffers for control message");
239 		return;
240 	}
241 	msg->port = sys_cpu_to_le32(port);
242 	msg->event = sys_cpu_to_le16(event);
243 	msg->value = sys_cpu_to_le16(value);
244 	struct virtq_buf vqbuf = {.addr = msg, .len = sizeof(*msg)};
245 
246 	int ret = virtq_add_buffer_chain(vq, &vqbuf, 1, 1, virtconsole_control_tx_flush, data,
247 					 K_NO_WAIT);
248 
249 	if (ret == -EBUSY) {
250 		/* put in FIFO to be sent later, mark buffer as occupied to prevent overwriting */
251 		k_fifo_put(&data->tx_ctlfifo, data->tx_ctlbuf + data->txctlcurrent);
252 		item->pending = true;
253 	} else if (ret == 0) {
254 		virtio_notify_virtqueue(config->vdev, VIRTQ_CONTROL_TX);
255 	} else {
256 		LOG_ERR("could not send control message");
257 		return;
258 	}
259 	data->txctlcurrent =
260 		(data->txctlcurrent + 1) % CONFIG_UART_VIRTIO_CONSOLE_TX_CONTROL_BUFSIZE;
261 }
262 
virtconsole_control_recv_cb(void * priv,uint32_t len)263 static void virtconsole_control_recv_cb(void *priv, uint32_t len)
264 {
265 	struct _ctl_cb_data *ctld = priv;
266 	struct virtconsole_data *data = ctld->data;
267 
268 	for (int i = 0; i < CONFIG_UART_VIRTIO_CONSOLE_RX_CONTROL_BUFSIZE; i++) {
269 		if (data->rx_ctlbuf[i].port == UINT32_MAX) {
270 			continue;
271 		}
272 		data->rx_ctlbuf[i].port = sys_le32_to_cpu(data->rx_ctlbuf[i].port);
273 		data->rx_ctlbuf[i].event = sys_le16_to_cpu(data->rx_ctlbuf[i].event);
274 		data->rx_ctlbuf[i].value = sys_le16_to_cpu(data->rx_ctlbuf[i].value);
275 
276 		switch (data->rx_ctlbuf[i].event) {
277 		case VIRTIO_CONSOLE_DEVICE_ADD:
278 			virtconsole_send_control_msg(
279 				data->dev, data->rx_ctlbuf[i].port, VIRTIO_CONSOLE_PORT_READY,
280 				(data->rx_ctlbuf[i].port) < VIRTIO_CONSOLE_MAX_PORTS);
281 			break;
282 		case VIRTIO_CONSOLE_DEVICE_REMOVE: {
283 			int port = data->rx_ctlbuf[i].port;
284 
285 			if ((port < VIRTIO_CONSOLE_MAX_PORTS) &&
286 			    IS_BIT_SET(data->console_ports, port)) {
287 				/* Remove console port (unset bit) */
288 				data->console_ports = ~(data->console_ports);
289 				data->console_ports |= BIT(port);
290 				data->console_ports = ~(data->console_ports);
291 				data->n_console_ports--;
292 			}
293 			break;
294 		}
295 		case VIRTIO_CONSOLE_CONSOLE_PORT: {
296 			int port = data->rx_ctlbuf[i].port;
297 
298 			if ((port < VIRTIO_CONSOLE_MAX_PORTS) &&
299 			    !IS_BIT_SET(data->console_ports, port)) {
300 				data->console_ports |= BIT(port);
301 				data->n_console_ports++;
302 			}
303 			virtconsole_send_control_msg(data->dev, data->rx_ctlbuf[i].port,
304 						     VIRTIO_CONSOLE_PORT_OPEN, 1);
305 
306 			if (atomic_test_bit(&data->flags, RX_IRQ_ENABLED)) {
307 				if (!atomic_test_bit(&(data->rx_started), port)) {
308 					uint16_t q_no = PORT_TO_RX_VQ_IDX(port);
309 
310 					virtconsole_recv_setup(data->dev, q_no,
311 							       data->rxbuf + data->rxcurrent,
312 							       sizeof(char), virtconsole_recv_cb,
313 							       data->rx_cb_data + port);
314 				}
315 			}
316 			break;
317 		}
318 		case VIRTIO_CONSOLE_RESIZE:
319 			/* Terminal sizes are not supported by Zephyr and the */
320 			/* VIRTIO_CONSOLE_F_SIZE feature was not enabled */
321 			LOG_WRN("device tried to set console size");
322 			break;
323 		case VIRTIO_CONSOLE_PORT_OPEN:
324 			LOG_INF("port %u is ready", data->rx_ctlbuf[i].port);
325 			break;
326 		case VIRTIO_CONSOLE_PORT_NAME:
327 			LOG_INF("port %u is named \"%.*s\"", data->rx_ctlbuf[i].port,
328 				(int)ARRAY_SIZE(data->rx_ctlbuf[i].name), data->rx_ctlbuf[i].name);
329 			break;
330 		default:
331 			break;
332 		}
333 		data->rx_ctlbuf[i].port = UINT32_MAX;
334 		memset(&(data->rx_ctlbuf[i].name), 0, ARRAY_SIZE(data->rx_ctlbuf[i].name));
335 	}
336 	virtconsole_recv_setup(data->dev, VIRTQ_CONTROL_RX, &data->rx_ctlbuf[ctld->buf_no],
337 			       sizeof(struct _virtio_console_control), virtconsole_control_recv_cb,
338 			       ctld);
339 }
340 #endif
virtconsole_poll_in(const struct device * dev,unsigned char * c)341 static int virtconsole_poll_in(const struct device *dev, unsigned char *c)
342 {
343 	struct virtconsole_data *data = dev->data;
344 	int ready = -1;
345 	int port = 0;
346 
347 #ifdef CONFIG_UART_VIRTIO_CONSOLE_F_MULTIPORT
348 	int n_ports_checked = 0;
349 
350 	for (; port < VIRTIO_CONSOLE_MAX_PORTS; port++) {
351 		if (!IS_BIT_SET(data->console_ports, port)) {
352 			continue;
353 		}
354 #endif
355 		uint16_t q_no = PORT_TO_RX_VQ_IDX(port);
356 
357 		if (!atomic_test_bit(&(data->rx_started), port)) {
358 			virtconsole_recv_setup(dev, q_no, data->rxbuf + data->rxcurrent,
359 					       sizeof(char), virtconsole_recv_cb,
360 					       data->rx_cb_data + port);
361 		}
362 		if (atomic_test_and_clear_bit(&(data->rx_ready), port)) {
363 			ready = q_no;
364 #ifdef CONFIG_UART_VIRTIO_CONSOLE_F_MULTIPORT
365 			break;
366 #endif
367 		}
368 #ifdef CONFIG_UART_VIRTIO_CONSOLE_F_MULTIPORT
369 		if ((++n_ports_checked) >= data->n_console_ports) {
370 			break;
371 		}
372 	}
373 #endif
374 	if (ready == -1) {
375 		return -1;
376 	}
377 	if (c) {
378 		*c = data->rxbuf[data->rxcurrent];
379 	}
380 	data->rxcurrent = (data->rxcurrent + 1) % CONFIG_UART_VIRTIO_CONSOLE_RX_BUFSIZE;
381 	virtconsole_recv_setup(dev, ready, data->rxbuf + data->rxcurrent, sizeof(char),
382 			       virtconsole_recv_cb, data->rx_cb_data + port);
383 	return 0;
384 }
385 
virtconsole_poll_out(const struct device * dev,unsigned char c)386 static void virtconsole_poll_out(const struct device *dev, unsigned char c)
387 {
388 	const struct virtconsole_config *config = dev->config;
389 	struct virtconsole_data *data = dev->data;
390 
391 	K_SPINLOCK(&(data->txsl)) {
392 		int port = 0;
393 
394 #ifdef CONFIG_UART_VIRTIO_CONSOLE_F_MULTIPORT
395 		int n_ports_checked = 0;
396 
397 		for (; port < VIRTIO_CONSOLE_MAX_PORTS; port++) {
398 			if (!IS_BIT_SET(data->console_ports, port)) {
399 				continue;
400 			}
401 #endif
402 			uint16_t q_no = PORT_TO_TX_VQ_IDX(port);
403 			struct virtq *vq = virtio_get_virtqueue(config->vdev, q_no);
404 
405 			if (vq == NULL) {
406 				LOG_ERR("could not access virtqueue %u", q_no);
407 				K_SPINLOCK_BREAK;
408 			}
409 			data->txbuf[data->txcurrent] = c;
410 			struct virtq_buf vqbuf = {.addr = data->txbuf + data->txcurrent,
411 						  .len = sizeof(char)};
412 
413 			if (virtq_add_buffer_chain(vq, &vqbuf, 1, 1, NULL, NULL, K_FOREVER)) {
414 				LOG_ERR("could not send character");
415 				K_SPINLOCK_BREAK;
416 			}
417 			virtio_notify_virtqueue(config->vdev, q_no);
418 #ifdef CONFIG_UART_VIRTIO_CONSOLE_F_MULTIPORT
419 			if ((++n_ports_checked) >= data->n_console_ports) {
420 				break;
421 			}
422 		}
423 #endif
424 		data->txcurrent = (data->txcurrent + 1) % CONFIG_UART_VIRTIO_CONSOLE_TX_BUFSIZE;
425 	}
426 }
427 
428 #ifdef CONFIG_UART_INTERRUPT_DRIVEN
virtconsole_fifo_fill(const struct device * dev,const uint8_t * tx_data,int size)429 static int virtconsole_fifo_fill(const struct device *dev, const uint8_t *tx_data, int size)
430 {
431 	int i = 0;
432 
433 	for (; i < size; i++) {
434 		virtconsole_poll_out(dev, tx_data[i]);
435 	}
436 	return i;
437 }
virtconsole_fifo_read(const struct device * dev,uint8_t * rx_data,const int size)438 static int virtconsole_fifo_read(const struct device *dev, uint8_t *rx_data, const int size)
439 {
440 	int i = 0;
441 
442 	for (; i < size; i++) {
443 		if (virtconsole_poll_in(dev, rx_data + i) == -1) {
444 			break;
445 		}
446 	}
447 	return i;
448 }
virtconsole_irq_tx_enable(const struct device * dev)449 static void virtconsole_irq_tx_enable(const struct device *dev)
450 {
451 	/* Only need to invoke the callback */
452 	struct virtconsole_data *data = dev->data;
453 
454 	if (data->irq_cb) {
455 		data->irq_cb(dev, data->irq_cb_data);
456 	}
457 }
virtconsole_irq_tx_ready(const struct device * dev)458 static int virtconsole_irq_tx_ready(const struct device *dev)
459 {
460 	/* Always ready to transmit characters, nothing to wait for */
461 	return 1;
462 }
virtconsole_irq_tx_complete(const struct device * dev)463 static int virtconsole_irq_tx_complete(const struct device *dev)
464 {
465 	/* Always complete, nothing to wait for */
466 	return 1;
467 }
virtconsole_irq_rx_enable(const struct device * dev)468 static void virtconsole_irq_rx_enable(const struct device *dev)
469 {
470 	struct virtconsole_data *data = dev->data;
471 
472 	/* Start receiving characters immediately */
473 	virtconsole_poll_in(dev, NULL);
474 	atomic_set_bit(&data->flags, RX_IRQ_ENABLED);
475 	if (data->irq_cb) {
476 		data->irq_cb(dev, data->irq_cb_data);
477 	}
478 }
virtconsole_irq_rx_ready(const struct device * dev)479 static int virtconsole_irq_rx_ready(const struct device *dev)
480 {
481 	struct virtconsole_data *data = dev->data;
482 
483 	/* True if any port has characters ready to read */
484 	return atomic_get(&(data->rx_ready));
485 }
virtconsole_irq_is_pending(const struct device * dev)486 static int virtconsole_irq_is_pending(const struct device *dev)
487 {
488 	return virtconsole_irq_rx_ready(dev);
489 }
virtconsole_irq_update(const struct device * dev)490 static int virtconsole_irq_update(const struct device *dev)
491 {
492 	/* Nothing to be done */
493 	return 1;
494 }
virtconsole_irq_callback_set(const struct device * dev,uart_irq_callback_user_data_t cb,void * user_data)495 static void virtconsole_irq_callback_set(const struct device *dev, uart_irq_callback_user_data_t cb,
496 					 void *user_data)
497 {
498 	struct virtconsole_data *data = dev->data;
499 
500 	data->irq_cb = cb;
501 	data->irq_cb_data = user_data;
502 }
503 #endif
504 
virtconsole_init(const struct device * dev)505 static int virtconsole_init(const struct device *dev)
506 {
507 	const struct virtconsole_config *config = dev->config;
508 	struct virtconsole_data *data = dev->data;
509 
510 	data->dev = dev;
511 	for (int i = 0; i < ARRAY_SIZE(data->rx_cb_data); i++) {
512 		data->rx_cb_data[i].data = data;
513 		data->rx_cb_data[i].port = i;
514 	}
515 	size_t n_queues = 2;
516 
517 	__maybe_unused bool multiport =
518 		virtio_read_device_feature_bit(config->vdev, VIRTIO_CONSOLE_F_MULTIPORT);
519 
520 	data->virtio_devcfg = virtio_get_device_specific_config(config->vdev);
521 
522 	if (data->virtio_devcfg == NULL) {
523 		LOG_WRN("could not get device-specific config");
524 #ifdef CONFIG_UART_VIRTIO_CONSOLE_F_MULTIPORT
525 		LOG_WRN("disabling multiport feature");
526 		multiport = false;
527 #endif
528 	}
529 #ifdef CONFIG_UART_VIRTIO_CONSOLE_F_MULTIPORT
530 	if (multiport) {
531 		if (virtio_write_driver_feature_bit(config->vdev, VIRTIO_CONSOLE_F_MULTIPORT, 1)) {
532 			multiport = false;
533 			LOG_WRN("could not enable multiport feature");
534 		}
535 		if (virtio_commit_feature_bits(config->vdev)) {
536 			multiport = false;
537 			LOG_WRN("could not commit feature bits; disabling multiport feature");
538 		} else {
539 			n_queues = (sys_le16_to_cpu(data->virtio_devcfg->max_nr_ports) + 1) * 2;
540 		}
541 	}
542 	if (!multiport) {
543 		/* If the multiport feature is off, use the default */
544 		data->n_console_ports = 1;
545 		data->console_ports = 1; /* Enable port 0 */
546 	}
547 #endif
548 	int ret = virtio_init_virtqueues(config->vdev, n_queues, virtconsole_enum_queues_cb, NULL);
549 
550 	if (ret) {
551 		LOG_ERR("error initializing virtqueues!");
552 		return ret;
553 	}
554 	virtio_finalize_init(config->vdev);
555 #ifdef CONFIG_UART_VIRTIO_CONSOLE_F_MULTIPORT
556 	if (multiport) {
557 		k_fifo_init(&data->tx_ctlfifo);
558 		for (int i = 0; i < CONFIG_UART_VIRTIO_CONSOLE_RX_CONTROL_BUFSIZE; i++) {
559 			data->ctl_cb_data[i].data = data;
560 			data->ctl_cb_data[i].buf_no = i;
561 			data->rx_ctlbuf[i].port = UINT32_MAX;
562 			virtconsole_recv_setup(data->dev, VIRTQ_CONTROL_RX, &data->rx_ctlbuf[i],
563 					       sizeof(struct _virtio_console_control),
564 					       virtconsole_control_recv_cb, &data->ctl_cb_data[i]);
565 		}
566 		virtconsole_send_control_msg(dev, 0, VIRTIO_CONSOLE_DEVICE_READY, 1);
567 	}
568 #endif
569 	return 0;
570 }
571 
572 static DEVICE_API(uart, virtconsole_api) = {
573 	.poll_in = virtconsole_poll_in,
574 	.poll_out = virtconsole_poll_out,
575 #ifdef CONFIG_UART_INTERRUPT_DRIVEN
576 	.fifo_fill = virtconsole_fifo_fill,
577 	.fifo_read = virtconsole_fifo_read,
578 	.irq_tx_enable = virtconsole_irq_tx_enable,
579 	.irq_tx_ready = virtconsole_irq_tx_ready,
580 	.irq_tx_complete = virtconsole_irq_tx_complete,
581 	.irq_rx_enable = virtconsole_irq_rx_enable,
582 	.irq_rx_ready = virtconsole_irq_rx_ready,
583 	.irq_is_pending = virtconsole_irq_is_pending,
584 	.irq_update = virtconsole_irq_update,
585 	.irq_callback_set = virtconsole_irq_callback_set,
586 #endif
587 };
588 
589 #define VIRTIO_CONSOLE_DEFINE(inst)                                                                \
590 	static struct virtconsole_data virtconsole_data_##inst;                                    \
591 	static const struct virtconsole_config virtconsole_config_##inst = {                       \
592 		.vdev = DEVICE_DT_GET(DT_PARENT(DT_DRV_INST(inst))),                               \
593 	};                                                                                         \
594 	DEVICE_DT_INST_DEFINE(inst, virtconsole_init, NULL, &virtconsole_data_##inst,              \
595 			      &virtconsole_config_##inst, POST_KERNEL,                             \
596 			      CONFIG_SERIAL_INIT_PRIORITY, &virtconsole_api);
597 
598 DT_INST_FOREACH_STATUS_OKAY(VIRTIO_CONSOLE_DEFINE)
599