xref: /freebsd/sys/dev/virtio/console/virtio_console.c (revision cb9813432ac6e8d91d369b66a78c8d68086ac29c)
16f744ddeSBryan Venteicher /*-
26f744ddeSBryan Venteicher  * Copyright (c) 2014, Bryan Venteicher <bryanv@FreeBSD.org>
36f744ddeSBryan Venteicher  * All rights reserved.
46f744ddeSBryan Venteicher  *
56f744ddeSBryan Venteicher  * Redistribution and use in source and binary forms, with or without
66f744ddeSBryan Venteicher  * modification, are permitted provided that the following conditions
76f744ddeSBryan Venteicher  * are met:
86f744ddeSBryan Venteicher  * 1. Redistributions of source code must retain the above copyright
96f744ddeSBryan Venteicher  *    notice unmodified, this list of conditions, and the following
106f744ddeSBryan Venteicher  *    disclaimer.
116f744ddeSBryan Venteicher  * 2. Redistributions in binary form must reproduce the above copyright
126f744ddeSBryan Venteicher  *    notice, this list of conditions and the following disclaimer in the
136f744ddeSBryan Venteicher  *    documentation and/or other materials provided with the distribution.
146f744ddeSBryan Venteicher  *
156f744ddeSBryan Venteicher  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
166f744ddeSBryan Venteicher  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
176f744ddeSBryan Venteicher  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
186f744ddeSBryan Venteicher  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
196f744ddeSBryan Venteicher  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
206f744ddeSBryan Venteicher  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
216f744ddeSBryan Venteicher  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
226f744ddeSBryan Venteicher  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
236f744ddeSBryan Venteicher  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
246f744ddeSBryan Venteicher  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
256f744ddeSBryan Venteicher  */
266f744ddeSBryan Venteicher 
276f744ddeSBryan Venteicher /* Driver for VirtIO console devices. */
286f744ddeSBryan Venteicher 
296f744ddeSBryan Venteicher #include <sys/cdefs.h>
306f744ddeSBryan Venteicher __FBSDID("$FreeBSD$");
316f744ddeSBryan Venteicher 
326f744ddeSBryan Venteicher #include <sys/param.h>
336f744ddeSBryan Venteicher #include <sys/systm.h>
346f744ddeSBryan Venteicher #include <sys/kernel.h>
356f744ddeSBryan Venteicher #include <sys/malloc.h>
366f744ddeSBryan Venteicher #include <sys/module.h>
37b84b3efdSBryan Venteicher #include <sys/kdb.h>
386f744ddeSBryan Venteicher #include <sys/lock.h>
396f744ddeSBryan Venteicher #include <sys/mutex.h>
406f744ddeSBryan Venteicher #include <sys/sglist.h>
416f744ddeSBryan Venteicher #include <sys/sysctl.h>
426f744ddeSBryan Venteicher #include <sys/taskqueue.h>
436f744ddeSBryan Venteicher #include <sys/queue.h>
446f744ddeSBryan Venteicher 
456f744ddeSBryan Venteicher #include <sys/conf.h>
466f744ddeSBryan Venteicher #include <sys/cons.h>
476f744ddeSBryan Venteicher #include <sys/tty.h>
486f744ddeSBryan Venteicher 
496f744ddeSBryan Venteicher #include <machine/bus.h>
506f744ddeSBryan Venteicher #include <machine/resource.h>
516f744ddeSBryan Venteicher #include <sys/bus.h>
526f744ddeSBryan Venteicher 
536f744ddeSBryan Venteicher #include <dev/virtio/virtio.h>
546f744ddeSBryan Venteicher #include <dev/virtio/virtqueue.h>
556f744ddeSBryan Venteicher #include <dev/virtio/console/virtio_console.h>
566f744ddeSBryan Venteicher 
576f744ddeSBryan Venteicher #include "virtio_if.h"
586f744ddeSBryan Venteicher 
5904434c94SBryan Venteicher #define VTCON_MAX_PORTS 32
606f744ddeSBryan Venteicher #define VTCON_TTY_PREFIX "V"
616f744ddeSBryan Venteicher #define VTCON_BULK_BUFSZ 128
626f744ddeSBryan Venteicher 
6304434c94SBryan Venteicher /*
6404434c94SBryan Venteicher  * The buffer cannot cross more than one page boundary due to the
6504434c94SBryan Venteicher  * size of the sglist segment array used.
6604434c94SBryan Venteicher  */
6704434c94SBryan Venteicher CTASSERT(VTCON_BULK_BUFSZ <= PAGE_SIZE);
6804434c94SBryan Venteicher 
696f744ddeSBryan Venteicher struct vtcon_softc;
7004434c94SBryan Venteicher struct vtcon_softc_port;
716f744ddeSBryan Venteicher 
726f744ddeSBryan Venteicher struct vtcon_port {
736f744ddeSBryan Venteicher 	struct mtx			 vtcport_mtx;
7404434c94SBryan Venteicher 	struct vtcon_softc		*vtcport_sc;
7504434c94SBryan Venteicher 	struct vtcon_softc_port		*vtcport_scport;
766f744ddeSBryan Venteicher 	struct tty			*vtcport_tty;
776f744ddeSBryan Venteicher 	struct virtqueue		*vtcport_invq;
786f744ddeSBryan Venteicher 	struct virtqueue		*vtcport_outvq;
7904434c94SBryan Venteicher 	int				 vtcport_id;
8004434c94SBryan Venteicher 	int				 vtcport_flags;
8104434c94SBryan Venteicher #define VTCON_PORT_FLAG_GONE	0x01
82b84b3efdSBryan Venteicher #define VTCON_PORT_FLAG_CONSOLE	0x02
83b84b3efdSBryan Venteicher 
84b84b3efdSBryan Venteicher #if defined(KDB)
85b84b3efdSBryan Venteicher 	int				 vtcport_alt_break_state;
86b84b3efdSBryan Venteicher #endif
876f744ddeSBryan Venteicher };
886f744ddeSBryan Venteicher 
8904434c94SBryan Venteicher #define VTCON_PORT_LOCK(_port)		mtx_lock(&(_port)->vtcport_mtx)
9004434c94SBryan Venteicher #define VTCON_PORT_UNLOCK(_port)	mtx_unlock(&(_port)->vtcport_mtx)
9104434c94SBryan Venteicher 
9204434c94SBryan Venteicher struct vtcon_softc_port {
9304434c94SBryan Venteicher 	struct vtcon_softc	*vcsp_sc;
9404434c94SBryan Venteicher 	struct vtcon_port	*vcsp_port;
9504434c94SBryan Venteicher 	struct virtqueue	*vcsp_invq;
9604434c94SBryan Venteicher 	struct virtqueue	*vcsp_outvq;
9704434c94SBryan Venteicher };
986f744ddeSBryan Venteicher 
996f744ddeSBryan Venteicher struct vtcon_softc {
1006f744ddeSBryan Venteicher 	device_t		 vtcon_dev;
1016f744ddeSBryan Venteicher 	struct mtx		 vtcon_mtx;
1026f744ddeSBryan Venteicher 	uint64_t		 vtcon_features;
1036f744ddeSBryan Venteicher 	uint32_t		 vtcon_max_ports;
104b84b3efdSBryan Venteicher 	uint32_t		 vtcon_flags;
105b84b3efdSBryan Venteicher #define VTCON_FLAG_DETACHED	0x01
106b84b3efdSBryan Venteicher #define VTCON_FLAG_SIZE		0x02
107b84b3efdSBryan Venteicher #define VTCON_FLAG_MULTIPORT	0x04
1086f744ddeSBryan Venteicher 
1096f744ddeSBryan Venteicher 	/*
1106f744ddeSBryan Venteicher 	 * Ports can be added and removed during runtime, but we have
1116f744ddeSBryan Venteicher 	 * to allocate all the virtqueues during attach. This array is
1126f744ddeSBryan Venteicher 	 * indexed by the port ID.
1136f744ddeSBryan Venteicher 	 */
11404434c94SBryan Venteicher 	struct vtcon_softc_port	*vtcon_ports;
115b84b3efdSBryan Venteicher 
116b84b3efdSBryan Venteicher 	struct task		 vtcon_ctrl_task;
117b84b3efdSBryan Venteicher 	struct virtqueue	*vtcon_ctrl_rxvq;
118b84b3efdSBryan Venteicher 	struct virtqueue	*vtcon_ctrl_txvq;
119b84b3efdSBryan Venteicher 	struct mtx		 vtcon_ctrl_tx_mtx;
1206f744ddeSBryan Venteicher };
1216f744ddeSBryan Venteicher 
12204434c94SBryan Venteicher #define VTCON_LOCK(_sc)			mtx_lock(&(_sc)->vtcon_mtx)
12304434c94SBryan Venteicher #define VTCON_UNLOCK(_sc)		mtx_unlock(&(_sc)->vtcon_mtx)
12404434c94SBryan Venteicher #define VTCON_LOCK_ASSERT(_sc)		\
12504434c94SBryan Venteicher     mtx_assert(&(_sc)->vtcon_mtx, MA_OWNED)
1266f744ddeSBryan Venteicher #define VTCON_LOCK_ASSERT_NOTOWNED(_sc)	\
12704434c94SBryan Venteicher     mtx_assert(&(_sc)->vtcon_mtx, MA_NOTOWNED)
12804434c94SBryan Venteicher 
12904434c94SBryan Venteicher #define VTCON_CTRL_TX_LOCK(_sc)		mtx_lock(&(_sc)->vtcon_ctrl_tx_mtx)
13004434c94SBryan Venteicher #define VTCON_CTRL_TX_UNLOCK(_sc)	mtx_unlock(&(_sc)->vtcon_ctrl_tx_mtx)
1316f744ddeSBryan Venteicher 
1326f744ddeSBryan Venteicher #define VTCON_ASSERT_VALID_PORTID(_sc, _id)			\
1336f744ddeSBryan Venteicher     KASSERT((_id) >= 0 && (_id) < (_sc)->vtcon_max_ports,	\
1346f744ddeSBryan Venteicher         ("%s: port ID %d out of range", __func__, _id))
1356f744ddeSBryan Venteicher 
13604434c94SBryan Venteicher #define VTCON_FEATURES  VIRTIO_CONSOLE_F_MULTIPORT
1376f744ddeSBryan Venteicher 
1386f744ddeSBryan Venteicher static struct virtio_feature_desc vtcon_feature_desc[] = {
1396f744ddeSBryan Venteicher 	{ VIRTIO_CONSOLE_F_SIZE,	"ConsoleSize"	},
1406f744ddeSBryan Venteicher 	{ VIRTIO_CONSOLE_F_MULTIPORT,	"MultiplePorts"	},
141b84b3efdSBryan Venteicher 	{ VIRTIO_CONSOLE_F_EMERG_WRITE,	"EmergencyWrite" },
1426f744ddeSBryan Venteicher 
1436f744ddeSBryan Venteicher 	{ 0, NULL }
1446f744ddeSBryan Venteicher };
1456f744ddeSBryan Venteicher 
1466f744ddeSBryan Venteicher static int	 vtcon_modevent(module_t, int, void *);
14704434c94SBryan Venteicher static void	 vtcon_drain_all(void);
1486f744ddeSBryan Venteicher 
1496f744ddeSBryan Venteicher static int	 vtcon_probe(device_t);
1506f744ddeSBryan Venteicher static int	 vtcon_attach(device_t);
1516f744ddeSBryan Venteicher static int	 vtcon_detach(device_t);
1526f744ddeSBryan Venteicher static int	 vtcon_config_change(device_t);
1536f744ddeSBryan Venteicher 
15404434c94SBryan Venteicher static void	 vtcon_setup_features(struct vtcon_softc *);
1556f744ddeSBryan Venteicher static void	 vtcon_negotiate_features(struct vtcon_softc *);
15604434c94SBryan Venteicher static int	 vtcon_alloc_scports(struct vtcon_softc *);
1576f744ddeSBryan Venteicher static int	 vtcon_alloc_virtqueues(struct vtcon_softc *);
1586f744ddeSBryan Venteicher static void	 vtcon_read_config(struct vtcon_softc *,
1596f744ddeSBryan Venteicher 		     struct virtio_console_config *);
1606f744ddeSBryan Venteicher 
1616f744ddeSBryan Venteicher static void	 vtcon_determine_max_ports(struct vtcon_softc *,
1626f744ddeSBryan Venteicher 		     struct virtio_console_config *);
16304434c94SBryan Venteicher static void	 vtcon_destroy_ports(struct vtcon_softc *);
1646f744ddeSBryan Venteicher static void	 vtcon_stop(struct vtcon_softc *);
1656f744ddeSBryan Venteicher 
16604434c94SBryan Venteicher static int	 vtcon_ctrl_event_enqueue(struct vtcon_softc *,
1676f744ddeSBryan Venteicher 		     struct virtio_console_control *);
16804434c94SBryan Venteicher static int	 vtcon_ctrl_event_create(struct vtcon_softc *);
16904434c94SBryan Venteicher static void	 vtcon_ctrl_event_requeue(struct vtcon_softc *,
1706f744ddeSBryan Venteicher 		     struct virtio_console_control *);
17104434c94SBryan Venteicher static int	 vtcon_ctrl_event_populate(struct vtcon_softc *);
17204434c94SBryan Venteicher static void	 vtcon_ctrl_event_drain(struct vtcon_softc *);
1736f744ddeSBryan Venteicher static int	 vtcon_ctrl_init(struct vtcon_softc *);
1746f744ddeSBryan Venteicher static void	 vtcon_ctrl_deinit(struct vtcon_softc *);
1756f744ddeSBryan Venteicher static void	 vtcon_ctrl_port_add_event(struct vtcon_softc *, int);
1766f744ddeSBryan Venteicher static void	 vtcon_ctrl_port_remove_event(struct vtcon_softc *, int);
1776f744ddeSBryan Venteicher static void	 vtcon_ctrl_port_console_event(struct vtcon_softc *, int);
1786f744ddeSBryan Venteicher static void	 vtcon_ctrl_port_open_event(struct vtcon_softc *, int);
179*cb981343SJakub Wojciech Klama static void	 vtcon_ctrl_port_name_event(struct vtcon_softc *, int,
180*cb981343SJakub Wojciech Klama 		     const char *, size_t);
18104434c94SBryan Venteicher static void	 vtcon_ctrl_process_event(struct vtcon_softc *,
182*cb981343SJakub Wojciech Klama 		     struct virtio_console_control *, void *, size_t);
1836f744ddeSBryan Venteicher static void	 vtcon_ctrl_task_cb(void *, int);
18404434c94SBryan Venteicher static void	 vtcon_ctrl_event_intr(void *);
18504434c94SBryan Venteicher static void	 vtcon_ctrl_poll(struct vtcon_softc *,
18604434c94SBryan Venteicher 		     struct virtio_console_control *control);
18704434c94SBryan Venteicher static void	 vtcon_ctrl_send_control(struct vtcon_softc *, uint32_t,
18804434c94SBryan Venteicher 		     uint16_t, uint16_t);
1896f744ddeSBryan Venteicher 
19004434c94SBryan Venteicher static int	 vtcon_port_enqueue_buf(struct vtcon_port *, void *, size_t);
19104434c94SBryan Venteicher static int	 vtcon_port_create_buf(struct vtcon_port *);
19204434c94SBryan Venteicher static void	 vtcon_port_requeue_buf(struct vtcon_port *, void *);
1936f744ddeSBryan Venteicher static int	 vtcon_port_populate(struct vtcon_port *);
1946f744ddeSBryan Venteicher static void	 vtcon_port_destroy(struct vtcon_port *);
19504434c94SBryan Venteicher static int	 vtcon_port_create(struct vtcon_softc *, int);
19604434c94SBryan Venteicher static void	 vtcon_port_drain_bufs(struct virtqueue *);
19704434c94SBryan Venteicher static void	 vtcon_port_drain(struct vtcon_port *);
19804434c94SBryan Venteicher static void	 vtcon_port_teardown(struct vtcon_port *);
1996f744ddeSBryan Venteicher static void	 vtcon_port_change_size(struct vtcon_port *, uint16_t,
2006f744ddeSBryan Venteicher 		     uint16_t);
20104434c94SBryan Venteicher static void	 vtcon_port_update_console_size(struct vtcon_softc *);
2026f744ddeSBryan Venteicher static void	 vtcon_port_enable_intr(struct vtcon_port *);
2036f744ddeSBryan Venteicher static void	 vtcon_port_disable_intr(struct vtcon_port *);
20404434c94SBryan Venteicher static void	 vtcon_port_in(struct vtcon_port *);
20504434c94SBryan Venteicher static void	 vtcon_port_intr(void *);
20604434c94SBryan Venteicher static void	 vtcon_port_out(struct vtcon_port *, void *, int);
20704434c94SBryan Venteicher static void	 vtcon_port_submit_event(struct vtcon_port *, uint16_t,
2086f744ddeSBryan Venteicher 		     uint16_t);
2096f744ddeSBryan Venteicher 
2106f744ddeSBryan Venteicher static int	 vtcon_tty_open(struct tty *);
2116f744ddeSBryan Venteicher static void	 vtcon_tty_close(struct tty *);
2126f744ddeSBryan Venteicher static void	 vtcon_tty_outwakeup(struct tty *);
2136f744ddeSBryan Venteicher static void	 vtcon_tty_free(void *);
2146f744ddeSBryan Venteicher 
2156f744ddeSBryan Venteicher static void	 vtcon_get_console_size(struct vtcon_softc *, uint16_t *,
2166f744ddeSBryan Venteicher 		     uint16_t *);
2176f744ddeSBryan Venteicher 
2186f744ddeSBryan Venteicher static void	 vtcon_enable_interrupts(struct vtcon_softc *);
2196f744ddeSBryan Venteicher static void	 vtcon_disable_interrupts(struct vtcon_softc *);
2206f744ddeSBryan Venteicher 
2216f744ddeSBryan Venteicher static int	 vtcon_pending_free;
2226f744ddeSBryan Venteicher 
2236f744ddeSBryan Venteicher static struct ttydevsw vtcon_tty_class = {
2246f744ddeSBryan Venteicher 	.tsw_flags	= 0,
2256f744ddeSBryan Venteicher 	.tsw_open	= vtcon_tty_open,
2266f744ddeSBryan Venteicher 	.tsw_close	= vtcon_tty_close,
2276f744ddeSBryan Venteicher 	.tsw_outwakeup	= vtcon_tty_outwakeup,
2286f744ddeSBryan Venteicher 	.tsw_free	= vtcon_tty_free,
2296f744ddeSBryan Venteicher };
2306f744ddeSBryan Venteicher 
2316f744ddeSBryan Venteicher static device_method_t vtcon_methods[] = {
2326f744ddeSBryan Venteicher 	/* Device methods. */
2336f744ddeSBryan Venteicher 	DEVMETHOD(device_probe,		vtcon_probe),
2346f744ddeSBryan Venteicher 	DEVMETHOD(device_attach,	vtcon_attach),
2356f744ddeSBryan Venteicher 	DEVMETHOD(device_detach,	vtcon_detach),
2366f744ddeSBryan Venteicher 
2376f744ddeSBryan Venteicher 	/* VirtIO methods. */
2386f744ddeSBryan Venteicher 	DEVMETHOD(virtio_config_change,	vtcon_config_change),
2396f744ddeSBryan Venteicher 
2406f744ddeSBryan Venteicher 	DEVMETHOD_END
2416f744ddeSBryan Venteicher };
2426f744ddeSBryan Venteicher 
2436f744ddeSBryan Venteicher static driver_t vtcon_driver = {
2446f744ddeSBryan Venteicher 	"vtcon",
2456f744ddeSBryan Venteicher 	vtcon_methods,
2466f744ddeSBryan Venteicher 	sizeof(struct vtcon_softc)
2476f744ddeSBryan Venteicher };
2486f744ddeSBryan Venteicher static devclass_t vtcon_devclass;
2496f744ddeSBryan Venteicher 
2506f744ddeSBryan Venteicher DRIVER_MODULE(virtio_console, virtio_pci, vtcon_driver, vtcon_devclass,
2516f744ddeSBryan Venteicher     vtcon_modevent, 0);
2526f744ddeSBryan Venteicher MODULE_VERSION(virtio_console, 1);
2536f744ddeSBryan Venteicher MODULE_DEPEND(virtio_console, virtio, 1, 1, 1);
2546f744ddeSBryan Venteicher 
2556f744ddeSBryan Venteicher static int
2566f744ddeSBryan Venteicher vtcon_modevent(module_t mod, int type, void *unused)
2576f744ddeSBryan Venteicher {
2586f744ddeSBryan Venteicher 	int error;
2596f744ddeSBryan Venteicher 
2606f744ddeSBryan Venteicher 	switch (type) {
2616f744ddeSBryan Venteicher 	case MOD_LOAD:
2626f744ddeSBryan Venteicher 		error = 0;
2636f744ddeSBryan Venteicher 		break;
2646f744ddeSBryan Venteicher 	case MOD_QUIESCE:
26504434c94SBryan Venteicher 		error = 0;
26604434c94SBryan Venteicher 		break;
2676f744ddeSBryan Venteicher 	case MOD_UNLOAD:
26804434c94SBryan Venteicher 		vtcon_drain_all();
26904434c94SBryan Venteicher 		error = 0;
2706f744ddeSBryan Venteicher 		break;
2716f744ddeSBryan Venteicher 	case MOD_SHUTDOWN:
2726f744ddeSBryan Venteicher 		error = 0;
2736f744ddeSBryan Venteicher 		break;
2746f744ddeSBryan Venteicher 	default:
2756f744ddeSBryan Venteicher 		error = EOPNOTSUPP;
2766f744ddeSBryan Venteicher 		break;
2776f744ddeSBryan Venteicher 	}
2786f744ddeSBryan Venteicher 
2796f744ddeSBryan Venteicher 	return (error);
2806f744ddeSBryan Venteicher }
2816f744ddeSBryan Venteicher 
28204434c94SBryan Venteicher static void
28304434c94SBryan Venteicher vtcon_drain_all(void)
28404434c94SBryan Venteicher {
28504434c94SBryan Venteicher 	int first;
28604434c94SBryan Venteicher 
28704434c94SBryan Venteicher 	for (first = 1; vtcon_pending_free != 0; first = 0) {
28804434c94SBryan Venteicher 		if (first != 0) {
28904434c94SBryan Venteicher 			printf("virtio_console: Waiting for all detached TTY "
29004434c94SBryan Venteicher 			    "devices to have open fds closed.\n");
29104434c94SBryan Venteicher 		}
29204434c94SBryan Venteicher 		pause("vtcondra", hz);
29304434c94SBryan Venteicher 	}
29404434c94SBryan Venteicher }
29504434c94SBryan Venteicher 
2966f744ddeSBryan Venteicher static int
2976f744ddeSBryan Venteicher vtcon_probe(device_t dev)
2986f744ddeSBryan Venteicher {
2996f744ddeSBryan Venteicher 
3006f744ddeSBryan Venteicher 	if (virtio_get_device_type(dev) != VIRTIO_ID_CONSOLE)
3016f744ddeSBryan Venteicher 		return (ENXIO);
3026f744ddeSBryan Venteicher 
3036f744ddeSBryan Venteicher 	device_set_desc(dev, "VirtIO Console Adapter");
3046f744ddeSBryan Venteicher 
3056f744ddeSBryan Venteicher 	return (BUS_PROBE_DEFAULT);
3066f744ddeSBryan Venteicher }
3076f744ddeSBryan Venteicher 
3086f744ddeSBryan Venteicher static int
3096f744ddeSBryan Venteicher vtcon_attach(device_t dev)
3106f744ddeSBryan Venteicher {
3116f744ddeSBryan Venteicher 	struct vtcon_softc *sc;
3126f744ddeSBryan Venteicher 	struct virtio_console_config concfg;
3136f744ddeSBryan Venteicher 	int error;
3146f744ddeSBryan Venteicher 
3156f744ddeSBryan Venteicher 	sc = device_get_softc(dev);
3166f744ddeSBryan Venteicher 	sc->vtcon_dev = dev;
3176f744ddeSBryan Venteicher 
31804434c94SBryan Venteicher 	mtx_init(&sc->vtcon_mtx, "vtconmtx", NULL, MTX_DEF);
31904434c94SBryan Venteicher 	mtx_init(&sc->vtcon_ctrl_tx_mtx, "vtconctrlmtx", NULL, MTX_DEF);
3206f744ddeSBryan Venteicher 
3216f744ddeSBryan Venteicher 	virtio_set_feature_desc(dev, vtcon_feature_desc);
32204434c94SBryan Venteicher 	vtcon_setup_features(sc);
3236f744ddeSBryan Venteicher 
3246f744ddeSBryan Venteicher 	vtcon_read_config(sc, &concfg);
3256f744ddeSBryan Venteicher 	vtcon_determine_max_ports(sc, &concfg);
3266f744ddeSBryan Venteicher 
32704434c94SBryan Venteicher 	error = vtcon_alloc_scports(sc);
32804434c94SBryan Venteicher 	if (error) {
32904434c94SBryan Venteicher 		device_printf(dev, "cannot allocate softc port structures\n");
33004434c94SBryan Venteicher 		goto fail;
33104434c94SBryan Venteicher 	}
33204434c94SBryan Venteicher 
3336f744ddeSBryan Venteicher 	error = vtcon_alloc_virtqueues(sc);
3346f744ddeSBryan Venteicher 	if (error) {
3356f744ddeSBryan Venteicher 		device_printf(dev, "cannot allocate virtqueues\n");
3366f744ddeSBryan Venteicher 		goto fail;
3376f744ddeSBryan Venteicher 	}
3386f744ddeSBryan Venteicher 
33904434c94SBryan Venteicher 	if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) {
34004434c94SBryan Venteicher 		TASK_INIT(&sc->vtcon_ctrl_task, 0, vtcon_ctrl_task_cb, sc);
3416f744ddeSBryan Venteicher 		error = vtcon_ctrl_init(sc);
342b84b3efdSBryan Venteicher 		if (error)
343b84b3efdSBryan Venteicher 			goto fail;
344b84b3efdSBryan Venteicher 	} else {
34504434c94SBryan Venteicher 		error = vtcon_port_create(sc, 0);
3466f744ddeSBryan Venteicher 		if (error)
3476f744ddeSBryan Venteicher 			goto fail;
348b84b3efdSBryan Venteicher 		if (sc->vtcon_flags & VTCON_FLAG_SIZE)
349b84b3efdSBryan Venteicher 			vtcon_port_update_console_size(sc);
350b84b3efdSBryan Venteicher 	}
3516f744ddeSBryan Venteicher 
3526f744ddeSBryan Venteicher 	error = virtio_setup_intr(dev, INTR_TYPE_TTY);
3536f744ddeSBryan Venteicher 	if (error) {
3546f744ddeSBryan Venteicher 		device_printf(dev, "cannot setup virtqueue interrupts\n");
3556f744ddeSBryan Venteicher 		goto fail;
3566f744ddeSBryan Venteicher 	}
3576f744ddeSBryan Venteicher 
3586f744ddeSBryan Venteicher 	vtcon_enable_interrupts(sc);
3596f744ddeSBryan Venteicher 
36004434c94SBryan Venteicher 	vtcon_ctrl_send_control(sc, VIRTIO_CONSOLE_BAD_ID,
3616f744ddeSBryan Venteicher 	    VIRTIO_CONSOLE_DEVICE_READY, 1);
3626f744ddeSBryan Venteicher 
3636f744ddeSBryan Venteicher fail:
3646f744ddeSBryan Venteicher 	if (error)
3656f744ddeSBryan Venteicher 		vtcon_detach(dev);
3666f744ddeSBryan Venteicher 
3676f744ddeSBryan Venteicher 	return (error);
3686f744ddeSBryan Venteicher }
3696f744ddeSBryan Venteicher 
3706f744ddeSBryan Venteicher static int
3716f744ddeSBryan Venteicher vtcon_detach(device_t dev)
3726f744ddeSBryan Venteicher {
3736f744ddeSBryan Venteicher 	struct vtcon_softc *sc;
3746f744ddeSBryan Venteicher 
3756f744ddeSBryan Venteicher 	sc = device_get_softc(dev);
3766f744ddeSBryan Venteicher 
3776f744ddeSBryan Venteicher 	VTCON_LOCK(sc);
3786f744ddeSBryan Venteicher 	sc->vtcon_flags |= VTCON_FLAG_DETACHED;
3796f744ddeSBryan Venteicher 	if (device_is_attached(dev))
3806f744ddeSBryan Venteicher 		vtcon_stop(sc);
3816f744ddeSBryan Venteicher 	VTCON_UNLOCK(sc);
3826f744ddeSBryan Venteicher 
38304434c94SBryan Venteicher 	if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) {
3846f744ddeSBryan Venteicher 		taskqueue_drain(taskqueue_thread, &sc->vtcon_ctrl_task);
3856f744ddeSBryan Venteicher 		vtcon_ctrl_deinit(sc);
38604434c94SBryan Venteicher 	}
3876f744ddeSBryan Venteicher 
38804434c94SBryan Venteicher 	vtcon_destroy_ports(sc);
38904434c94SBryan Venteicher 	mtx_destroy(&sc->vtcon_mtx);
39004434c94SBryan Venteicher 	mtx_destroy(&sc->vtcon_ctrl_tx_mtx);
3916f744ddeSBryan Venteicher 
3926f744ddeSBryan Venteicher 	return (0);
3936f744ddeSBryan Venteicher }
3946f744ddeSBryan Venteicher 
3956f744ddeSBryan Venteicher static int
3966f744ddeSBryan Venteicher vtcon_config_change(device_t dev)
3976f744ddeSBryan Venteicher {
3986f744ddeSBryan Venteicher 	struct vtcon_softc *sc;
3996f744ddeSBryan Venteicher 
4006f744ddeSBryan Venteicher 	sc = device_get_softc(dev);
4016f744ddeSBryan Venteicher 
4026f744ddeSBryan Venteicher 	/*
40304434c94SBryan Venteicher 	 * When the multiport feature is negotiated, all configuration
40404434c94SBryan Venteicher 	 * changes are done through control virtqueue events.
4056f744ddeSBryan Venteicher 	 */
40604434c94SBryan Venteicher 	if ((sc->vtcon_flags & VTCON_FLAG_MULTIPORT) == 0) {
40704434c94SBryan Venteicher 		if (sc->vtcon_flags & VTCON_FLAG_SIZE)
40804434c94SBryan Venteicher 			vtcon_port_update_console_size(sc);
4096f744ddeSBryan Venteicher 	}
4106f744ddeSBryan Venteicher 
4116f744ddeSBryan Venteicher 	return (0);
4126f744ddeSBryan Venteicher }
4136f744ddeSBryan Venteicher 
4146f744ddeSBryan Venteicher static void
4156f744ddeSBryan Venteicher vtcon_negotiate_features(struct vtcon_softc *sc)
4166f744ddeSBryan Venteicher {
4176f744ddeSBryan Venteicher 	device_t dev;
4186f744ddeSBryan Venteicher 	uint64_t features;
4196f744ddeSBryan Venteicher 
4206f744ddeSBryan Venteicher 	dev = sc->vtcon_dev;
4216f744ddeSBryan Venteicher 	features = VTCON_FEATURES;
4226f744ddeSBryan Venteicher 
4236f744ddeSBryan Venteicher 	sc->vtcon_features = virtio_negotiate_features(dev, features);
4246f744ddeSBryan Venteicher }
4256f744ddeSBryan Venteicher 
42604434c94SBryan Venteicher static void
42704434c94SBryan Venteicher vtcon_setup_features(struct vtcon_softc *sc)
42804434c94SBryan Venteicher {
42904434c94SBryan Venteicher 	device_t dev;
43004434c94SBryan Venteicher 
43104434c94SBryan Venteicher 	dev = sc->vtcon_dev;
43204434c94SBryan Venteicher 
43304434c94SBryan Venteicher 	vtcon_negotiate_features(sc);
43404434c94SBryan Venteicher 
43504434c94SBryan Venteicher 	if (virtio_with_feature(dev, VIRTIO_CONSOLE_F_SIZE))
43604434c94SBryan Venteicher 		sc->vtcon_flags |= VTCON_FLAG_SIZE;
43704434c94SBryan Venteicher 	if (virtio_with_feature(dev, VIRTIO_CONSOLE_F_MULTIPORT))
43804434c94SBryan Venteicher 		sc->vtcon_flags |= VTCON_FLAG_MULTIPORT;
43904434c94SBryan Venteicher }
44004434c94SBryan Venteicher 
4416f744ddeSBryan Venteicher #define VTCON_GET_CONFIG(_dev, _feature, _field, _cfg)			\
4426f744ddeSBryan Venteicher 	if (virtio_with_feature(_dev, _feature)) {			\
4436f744ddeSBryan Venteicher 		virtio_read_device_config(_dev,				\
4446f744ddeSBryan Venteicher 		    offsetof(struct virtio_console_config, _field),	\
4456f744ddeSBryan Venteicher 		    &(_cfg)->_field, sizeof((_cfg)->_field));		\
4466f744ddeSBryan Venteicher 	}
4476f744ddeSBryan Venteicher 
4486f744ddeSBryan Venteicher static void
4496f744ddeSBryan Venteicher vtcon_read_config(struct vtcon_softc *sc, struct virtio_console_config *concfg)
4506f744ddeSBryan Venteicher {
4516f744ddeSBryan Venteicher 	device_t dev;
4526f744ddeSBryan Venteicher 
4536f744ddeSBryan Venteicher 	dev = sc->vtcon_dev;
4546f744ddeSBryan Venteicher 
4556f744ddeSBryan Venteicher 	bzero(concfg, sizeof(struct virtio_console_config));
4566f744ddeSBryan Venteicher 
4576f744ddeSBryan Venteicher 	VTCON_GET_CONFIG(dev, VIRTIO_CONSOLE_F_SIZE, cols, concfg);
4586f744ddeSBryan Venteicher 	VTCON_GET_CONFIG(dev, VIRTIO_CONSOLE_F_SIZE, rows, concfg);
4596f744ddeSBryan Venteicher 	VTCON_GET_CONFIG(dev, VIRTIO_CONSOLE_F_MULTIPORT, max_nr_ports, concfg);
4606f744ddeSBryan Venteicher }
4616f744ddeSBryan Venteicher 
4626f744ddeSBryan Venteicher #undef VTCON_GET_CONFIG
4636f744ddeSBryan Venteicher 
4646f744ddeSBryan Venteicher static int
46504434c94SBryan Venteicher vtcon_alloc_scports(struct vtcon_softc *sc)
46604434c94SBryan Venteicher {
46704434c94SBryan Venteicher 	struct vtcon_softc_port *scport;
46804434c94SBryan Venteicher 	int max, i;
46904434c94SBryan Venteicher 
47004434c94SBryan Venteicher 	max = sc->vtcon_max_ports;
47104434c94SBryan Venteicher 
47204434c94SBryan Venteicher 	sc->vtcon_ports = malloc(sizeof(struct vtcon_softc_port) * max,
47304434c94SBryan Venteicher 	    M_DEVBUF, M_NOWAIT | M_ZERO);
47404434c94SBryan Venteicher 	if (sc->vtcon_ports == NULL)
47504434c94SBryan Venteicher 		return (ENOMEM);
47604434c94SBryan Venteicher 
47704434c94SBryan Venteicher 	for (i = 0; i < max; i++) {
47804434c94SBryan Venteicher 		scport = &sc->vtcon_ports[i];
47904434c94SBryan Venteicher 		scport->vcsp_sc = sc;
48004434c94SBryan Venteicher 	}
48104434c94SBryan Venteicher 
48204434c94SBryan Venteicher 	return (0);
48304434c94SBryan Venteicher }
48404434c94SBryan Venteicher 
48504434c94SBryan Venteicher static int
4866f744ddeSBryan Venteicher vtcon_alloc_virtqueues(struct vtcon_softc *sc)
4876f744ddeSBryan Venteicher {
4886f744ddeSBryan Venteicher 	device_t dev;
4896f744ddeSBryan Venteicher 	struct vq_alloc_info *info;
49004434c94SBryan Venteicher 	struct vtcon_softc_port *scport;
4916f744ddeSBryan Venteicher 	int i, idx, portidx, nvqs, error;
4926f744ddeSBryan Venteicher 
4936f744ddeSBryan Venteicher 	dev = sc->vtcon_dev;
4946f744ddeSBryan Venteicher 
4956f744ddeSBryan Venteicher 	nvqs = sc->vtcon_max_ports * 2;
4966f744ddeSBryan Venteicher 	if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT)
4976f744ddeSBryan Venteicher 		nvqs += 2;
4986f744ddeSBryan Venteicher 
4996f744ddeSBryan Venteicher 	info = malloc(sizeof(struct vq_alloc_info) * nvqs, M_TEMP, M_NOWAIT);
5006f744ddeSBryan Venteicher 	if (info == NULL)
5016f744ddeSBryan Venteicher 		return (ENOMEM);
5026f744ddeSBryan Venteicher 
5036f744ddeSBryan Venteicher 	for (i = 0, idx = 0, portidx = 0; i < nvqs / 2; i++, idx += 2) {
5046f744ddeSBryan Venteicher 
5056f744ddeSBryan Venteicher 		if (i == 1) {
5066f744ddeSBryan Venteicher 			/* The control virtqueues are after the first port. */
5076f744ddeSBryan Venteicher 			VQ_ALLOC_INFO_INIT(&info[idx], 0,
50804434c94SBryan Venteicher 			    vtcon_ctrl_event_intr, sc, &sc->vtcon_ctrl_rxvq,
5096f744ddeSBryan Venteicher 			    "%s-control rx", device_get_nameunit(dev));
5106f744ddeSBryan Venteicher 			VQ_ALLOC_INFO_INIT(&info[idx+1], 0,
5116f744ddeSBryan Venteicher 			    NULL, sc, &sc->vtcon_ctrl_txvq,
5126f744ddeSBryan Venteicher 			    "%s-control tx", device_get_nameunit(dev));
5136f744ddeSBryan Venteicher 			continue;
5146f744ddeSBryan Venteicher 		}
5156f744ddeSBryan Venteicher 
51604434c94SBryan Venteicher 		scport = &sc->vtcon_ports[portidx];
5176f744ddeSBryan Venteicher 
51804434c94SBryan Venteicher 		VQ_ALLOC_INFO_INIT(&info[idx], 0, vtcon_port_intr,
51904434c94SBryan Venteicher 		    scport, &scport->vcsp_invq, "%s-port%d in",
52004434c94SBryan Venteicher 		    device_get_nameunit(dev), i);
5216f744ddeSBryan Venteicher 		VQ_ALLOC_INFO_INIT(&info[idx+1], 0, NULL,
52204434c94SBryan Venteicher 		    NULL, &scport->vcsp_outvq, "%s-port%d out",
52304434c94SBryan Venteicher 		    device_get_nameunit(dev), i);
5246f744ddeSBryan Venteicher 
5256f744ddeSBryan Venteicher 		portidx++;
5266f744ddeSBryan Venteicher 	}
5276f744ddeSBryan Venteicher 
5286f744ddeSBryan Venteicher 	error = virtio_alloc_virtqueues(dev, 0, nvqs, info);
5296f744ddeSBryan Venteicher 	free(info, M_TEMP);
5306f744ddeSBryan Venteicher 
5316f744ddeSBryan Venteicher 	return (error);
5326f744ddeSBryan Venteicher }
5336f744ddeSBryan Venteicher 
5346f744ddeSBryan Venteicher static void
5356f744ddeSBryan Venteicher vtcon_determine_max_ports(struct vtcon_softc *sc,
5366f744ddeSBryan Venteicher     struct virtio_console_config *concfg)
5376f744ddeSBryan Venteicher {
5386f744ddeSBryan Venteicher 
5396f744ddeSBryan Venteicher 	if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) {
5406f744ddeSBryan Venteicher 		sc->vtcon_max_ports =
5416f744ddeSBryan Venteicher 		    min(concfg->max_nr_ports, VTCON_MAX_PORTS);
5426f744ddeSBryan Venteicher 		if (sc->vtcon_max_ports == 0)
5436f744ddeSBryan Venteicher 			sc->vtcon_max_ports = 1;
5446f744ddeSBryan Venteicher 	} else
5456f744ddeSBryan Venteicher 		sc->vtcon_max_ports = 1;
5466f744ddeSBryan Venteicher }
5476f744ddeSBryan Venteicher 
5486f744ddeSBryan Venteicher static void
54904434c94SBryan Venteicher vtcon_destroy_ports(struct vtcon_softc *sc)
5506f744ddeSBryan Venteicher {
55104434c94SBryan Venteicher 	struct vtcon_softc_port *scport;
55204434c94SBryan Venteicher 	struct vtcon_port *port;
55304434c94SBryan Venteicher 	struct virtqueue *vq;
55404434c94SBryan Venteicher 	int i;
5556f744ddeSBryan Venteicher 
55604434c94SBryan Venteicher 	if (sc->vtcon_ports == NULL)
55704434c94SBryan Venteicher 		return;
55804434c94SBryan Venteicher 
55904434c94SBryan Venteicher 	VTCON_LOCK(sc);
56004434c94SBryan Venteicher 	for (i = 0; i < sc->vtcon_max_ports; i++) {
56104434c94SBryan Venteicher 		scport = &sc->vtcon_ports[i];
56204434c94SBryan Venteicher 
56304434c94SBryan Venteicher 		port = scport->vcsp_port;
56404434c94SBryan Venteicher 		if (port != NULL) {
56504434c94SBryan Venteicher 			scport->vcsp_port = NULL;
56604434c94SBryan Venteicher 			VTCON_PORT_LOCK(port);
56704434c94SBryan Venteicher 			VTCON_UNLOCK(sc);
56804434c94SBryan Venteicher 			vtcon_port_teardown(port);
56904434c94SBryan Venteicher 			VTCON_LOCK(sc);
5706f744ddeSBryan Venteicher 		}
5716f744ddeSBryan Venteicher 
57204434c94SBryan Venteicher 		vq = scport->vcsp_invq;
57304434c94SBryan Venteicher 		if (vq != NULL)
57404434c94SBryan Venteicher 			vtcon_port_drain_bufs(vq);
5756f744ddeSBryan Venteicher 	}
57604434c94SBryan Venteicher 	VTCON_UNLOCK(sc);
57704434c94SBryan Venteicher 
57804434c94SBryan Venteicher 	free(sc->vtcon_ports, M_DEVBUF);
57904434c94SBryan Venteicher 	sc->vtcon_ports = NULL;
5806f744ddeSBryan Venteicher }
5816f744ddeSBryan Venteicher 
5826f744ddeSBryan Venteicher static void
5836f744ddeSBryan Venteicher vtcon_stop(struct vtcon_softc *sc)
5846f744ddeSBryan Venteicher {
5856f744ddeSBryan Venteicher 
5866f744ddeSBryan Venteicher 	vtcon_disable_interrupts(sc);
5876f744ddeSBryan Venteicher 	virtio_stop(sc->vtcon_dev);
5886f744ddeSBryan Venteicher }
5896f744ddeSBryan Venteicher 
5906f744ddeSBryan Venteicher static int
59104434c94SBryan Venteicher vtcon_ctrl_event_enqueue(struct vtcon_softc *sc,
5926f744ddeSBryan Venteicher     struct virtio_console_control *control)
5936f744ddeSBryan Venteicher {
59404434c94SBryan Venteicher 	struct sglist_seg segs[2];
5956f744ddeSBryan Venteicher 	struct sglist sg;
5966f744ddeSBryan Venteicher 	struct virtqueue *vq;
59704434c94SBryan Venteicher 	int error;
5986f744ddeSBryan Venteicher 
5996f744ddeSBryan Venteicher 	vq = sc->vtcon_ctrl_rxvq;
6006f744ddeSBryan Venteicher 
60104434c94SBryan Venteicher 	sglist_init(&sg, 2, segs);
60204434c94SBryan Venteicher 	error = sglist_append(&sg, control,
60304434c94SBryan Venteicher 	    sizeof(struct virtio_console_control));
60404434c94SBryan Venteicher 	KASSERT(error == 0, ("%s: error %d adding control to sglist",
60504434c94SBryan Venteicher 	    __func__, error));
6066f744ddeSBryan Venteicher 
60704434c94SBryan Venteicher 	return (virtqueue_enqueue(vq, control, &sg, 0, sg.sg_nseg));
6086f744ddeSBryan Venteicher }
6096f744ddeSBryan Venteicher 
6106f744ddeSBryan Venteicher static int
61104434c94SBryan Venteicher vtcon_ctrl_event_create(struct vtcon_softc *sc)
6126f744ddeSBryan Venteicher {
6136f744ddeSBryan Venteicher 	struct virtio_console_control *control;
6146f744ddeSBryan Venteicher 	int error;
6156f744ddeSBryan Venteicher 
616*cb981343SJakub Wojciech Klama 	control = malloc(
617*cb981343SJakub Wojciech Klama 	    sizeof(struct virtio_console_control) + VTCON_BULK_BUFSZ,
618*cb981343SJakub Wojciech Klama 	    M_DEVBUF, M_ZERO | M_NOWAIT);
619*cb981343SJakub Wojciech Klama 
6206f744ddeSBryan Venteicher 	if (control == NULL)
6216f744ddeSBryan Venteicher 		return (ENOMEM);
6226f744ddeSBryan Venteicher 
62304434c94SBryan Venteicher 	error = vtcon_ctrl_event_enqueue(sc, control);
6246f744ddeSBryan Venteicher 	if (error)
6256f744ddeSBryan Venteicher 		free(control, M_DEVBUF);
6266f744ddeSBryan Venteicher 
6276f744ddeSBryan Venteicher 	return (error);
6286f744ddeSBryan Venteicher }
6296f744ddeSBryan Venteicher 
6306f744ddeSBryan Venteicher static void
63104434c94SBryan Venteicher vtcon_ctrl_event_requeue(struct vtcon_softc *sc,
6326f744ddeSBryan Venteicher     struct virtio_console_control *control)
6336f744ddeSBryan Venteicher {
6346f744ddeSBryan Venteicher 	int error;
6356f744ddeSBryan Venteicher 
63604434c94SBryan Venteicher 	bzero(control, sizeof(struct virtio_console_control));
6376f744ddeSBryan Venteicher 
63804434c94SBryan Venteicher 	error = vtcon_ctrl_event_enqueue(sc, control);
6396f744ddeSBryan Venteicher 	KASSERT(error == 0,
6406f744ddeSBryan Venteicher 	    ("%s: cannot requeue control buffer %d", __func__, error));
6416f744ddeSBryan Venteicher }
6426f744ddeSBryan Venteicher 
6436f744ddeSBryan Venteicher static int
64404434c94SBryan Venteicher vtcon_ctrl_event_populate(struct vtcon_softc *sc)
6456f744ddeSBryan Venteicher {
6466f744ddeSBryan Venteicher 	struct virtqueue *vq;
6476f744ddeSBryan Venteicher 	int nbufs, error;
6486f744ddeSBryan Venteicher 
6496f744ddeSBryan Venteicher 	vq = sc->vtcon_ctrl_rxvq;
6506f744ddeSBryan Venteicher 	error = ENOSPC;
6516f744ddeSBryan Venteicher 
6526f744ddeSBryan Venteicher 	for (nbufs = 0; !virtqueue_full(vq); nbufs++) {
65304434c94SBryan Venteicher 		error = vtcon_ctrl_event_create(sc);
6546f744ddeSBryan Venteicher 		if (error)
6556f744ddeSBryan Venteicher 			break;
6566f744ddeSBryan Venteicher 	}
6576f744ddeSBryan Venteicher 
6586f744ddeSBryan Venteicher 	if (nbufs > 0) {
6596f744ddeSBryan Venteicher 		virtqueue_notify(vq);
6606f744ddeSBryan Venteicher 		error = 0;
6616f744ddeSBryan Venteicher 	}
6626f744ddeSBryan Venteicher 
6636f744ddeSBryan Venteicher 	return (error);
6646f744ddeSBryan Venteicher }
6656f744ddeSBryan Venteicher 
6666f744ddeSBryan Venteicher static void
66704434c94SBryan Venteicher vtcon_ctrl_event_drain(struct vtcon_softc *sc)
6686f744ddeSBryan Venteicher {
6696f744ddeSBryan Venteicher 	struct virtio_console_control *control;
6706f744ddeSBryan Venteicher 	struct virtqueue *vq;
6716f744ddeSBryan Venteicher 	int last;
6726f744ddeSBryan Venteicher 
6736f744ddeSBryan Venteicher 	vq = sc->vtcon_ctrl_rxvq;
6746f744ddeSBryan Venteicher 	last = 0;
6756f744ddeSBryan Venteicher 
6766f744ddeSBryan Venteicher 	if (vq == NULL)
6776f744ddeSBryan Venteicher 		return;
6786f744ddeSBryan Venteicher 
67904434c94SBryan Venteicher 	VTCON_LOCK(sc);
6806f744ddeSBryan Venteicher 	while ((control = virtqueue_drain(vq, &last)) != NULL)
6816f744ddeSBryan Venteicher 		free(control, M_DEVBUF);
68204434c94SBryan Venteicher 	VTCON_UNLOCK(sc);
68304434c94SBryan Venteicher }
68404434c94SBryan Venteicher 
68504434c94SBryan Venteicher static int
68604434c94SBryan Venteicher vtcon_ctrl_init(struct vtcon_softc *sc)
68704434c94SBryan Venteicher {
68804434c94SBryan Venteicher 	int error;
68904434c94SBryan Venteicher 
69004434c94SBryan Venteicher 	error = vtcon_ctrl_event_populate(sc);
69104434c94SBryan Venteicher 
69204434c94SBryan Venteicher 	return (error);
6936f744ddeSBryan Venteicher }
6946f744ddeSBryan Venteicher 
6956f744ddeSBryan Venteicher static void
6966f744ddeSBryan Venteicher vtcon_ctrl_deinit(struct vtcon_softc *sc)
6976f744ddeSBryan Venteicher {
6986f744ddeSBryan Venteicher 
69904434c94SBryan Venteicher 	vtcon_ctrl_event_drain(sc);
7006f744ddeSBryan Venteicher }
7016f744ddeSBryan Venteicher 
7026f744ddeSBryan Venteicher static void
7036f744ddeSBryan Venteicher vtcon_ctrl_port_add_event(struct vtcon_softc *sc, int id)
7046f744ddeSBryan Venteicher {
7056f744ddeSBryan Venteicher 	device_t dev;
7066f744ddeSBryan Venteicher 	int error;
7076f744ddeSBryan Venteicher 
7086f744ddeSBryan Venteicher 	dev = sc->vtcon_dev;
7096f744ddeSBryan Venteicher 
71004434c94SBryan Venteicher 	/* This single thread only way for ports to be created. */
71104434c94SBryan Venteicher 	if (sc->vtcon_ports[id].vcsp_port != NULL) {
7126f744ddeSBryan Venteicher 		device_printf(dev, "%s: adding port %d, but already exists\n",
7136f744ddeSBryan Venteicher 		    __func__, id);
7146f744ddeSBryan Venteicher 		return;
7156f744ddeSBryan Venteicher 	}
7166f744ddeSBryan Venteicher 
71704434c94SBryan Venteicher 	error = vtcon_port_create(sc, id);
7186f744ddeSBryan Venteicher 	if (error) {
7196f744ddeSBryan Venteicher 		device_printf(dev, "%s: cannot create port %d: %d\n",
7206f744ddeSBryan Venteicher 		    __func__, id, error);
721b84b3efdSBryan Venteicher 		vtcon_ctrl_send_control(sc, id, VIRTIO_CONSOLE_PORT_READY, 0);
7226f744ddeSBryan Venteicher 		return;
7236f744ddeSBryan Venteicher 	}
7246f744ddeSBryan Venteicher }
7256f744ddeSBryan Venteicher 
7266f744ddeSBryan Venteicher static void
7276f744ddeSBryan Venteicher vtcon_ctrl_port_remove_event(struct vtcon_softc *sc, int id)
7286f744ddeSBryan Venteicher {
7296f744ddeSBryan Venteicher 	device_t dev;
73004434c94SBryan Venteicher 	struct vtcon_softc_port *scport;
7316f744ddeSBryan Venteicher 	struct vtcon_port *port;
7326f744ddeSBryan Venteicher 
7336f744ddeSBryan Venteicher 	dev = sc->vtcon_dev;
73404434c94SBryan Venteicher 	scport = &sc->vtcon_ports[id];
7356f744ddeSBryan Venteicher 
73604434c94SBryan Venteicher 	VTCON_LOCK(sc);
73704434c94SBryan Venteicher 	port = scport->vcsp_port;
7386f744ddeSBryan Venteicher 	if (port == NULL) {
73904434c94SBryan Venteicher 		VTCON_UNLOCK(sc);
7406f744ddeSBryan Venteicher 		device_printf(dev, "%s: remove port %d, but does not exist\n",
7416f744ddeSBryan Venteicher 		    __func__, id);
7426f744ddeSBryan Venteicher 		return;
7436f744ddeSBryan Venteicher 	}
7446f744ddeSBryan Venteicher 
74504434c94SBryan Venteicher 	scport->vcsp_port = NULL;
74604434c94SBryan Venteicher 	VTCON_PORT_LOCK(port);
74704434c94SBryan Venteicher 	VTCON_UNLOCK(sc);
74804434c94SBryan Venteicher 	vtcon_port_teardown(port);
7496f744ddeSBryan Venteicher }
7506f744ddeSBryan Venteicher 
7516f744ddeSBryan Venteicher static void
7526f744ddeSBryan Venteicher vtcon_ctrl_port_console_event(struct vtcon_softc *sc, int id)
7536f744ddeSBryan Venteicher {
754b84b3efdSBryan Venteicher 	device_t dev;
755b84b3efdSBryan Venteicher 	struct vtcon_softc_port *scport;
756b84b3efdSBryan Venteicher 	struct vtcon_port *port;
7576f744ddeSBryan Venteicher 
758b84b3efdSBryan Venteicher 	dev = sc->vtcon_dev;
759b84b3efdSBryan Venteicher 	scport = &sc->vtcon_ports[id];
760b84b3efdSBryan Venteicher 
761b84b3efdSBryan Venteicher 	VTCON_LOCK(sc);
762b84b3efdSBryan Venteicher 	port = scport->vcsp_port;
763b84b3efdSBryan Venteicher 	if (port == NULL) {
764b84b3efdSBryan Venteicher 		VTCON_UNLOCK(sc);
765b84b3efdSBryan Venteicher 		device_printf(dev, "%s: console port %d, but does not exist\n",
76604434c94SBryan Venteicher 		    __func__, id);
767b84b3efdSBryan Venteicher 		return;
768b84b3efdSBryan Venteicher 	}
769b84b3efdSBryan Venteicher 
770b84b3efdSBryan Venteicher 	VTCON_PORT_LOCK(port);
771b84b3efdSBryan Venteicher 	VTCON_UNLOCK(sc);
772b84b3efdSBryan Venteicher 	port->vtcport_flags |= VTCON_PORT_FLAG_CONSOLE;
773b84b3efdSBryan Venteicher 	vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_OPEN, 1);
774b84b3efdSBryan Venteicher 	VTCON_PORT_UNLOCK(port);
7756f744ddeSBryan Venteicher }
7766f744ddeSBryan Venteicher 
7776f744ddeSBryan Venteicher static void
7786f744ddeSBryan Venteicher vtcon_ctrl_port_open_event(struct vtcon_softc *sc, int id)
7796f744ddeSBryan Venteicher {
7806f744ddeSBryan Venteicher 	device_t dev;
78104434c94SBryan Venteicher 	struct vtcon_softc_port *scport;
7826f744ddeSBryan Venteicher 	struct vtcon_port *port;
7836f744ddeSBryan Venteicher 
7846f744ddeSBryan Venteicher 	dev = sc->vtcon_dev;
78504434c94SBryan Venteicher 	scport = &sc->vtcon_ports[id];
7866f744ddeSBryan Venteicher 
78704434c94SBryan Venteicher 	VTCON_LOCK(sc);
78804434c94SBryan Venteicher 	port = scport->vcsp_port;
7896f744ddeSBryan Venteicher 	if (port == NULL) {
79004434c94SBryan Venteicher 		VTCON_UNLOCK(sc);
7916f744ddeSBryan Venteicher 		device_printf(dev, "%s: open port %d, but does not exist\n",
7926f744ddeSBryan Venteicher 		    __func__, id);
7936f744ddeSBryan Venteicher 		return;
7946f744ddeSBryan Venteicher 	}
7956f744ddeSBryan Venteicher 
79604434c94SBryan Venteicher 	VTCON_PORT_LOCK(port);
79704434c94SBryan Venteicher 	VTCON_UNLOCK(sc);
7986f744ddeSBryan Venteicher 	vtcon_port_enable_intr(port);
79904434c94SBryan Venteicher 	VTCON_PORT_UNLOCK(port);
8006f744ddeSBryan Venteicher }
8016f744ddeSBryan Venteicher 
8026f744ddeSBryan Venteicher static void
803*cb981343SJakub Wojciech Klama vtcon_ctrl_port_name_event(struct vtcon_softc *sc, int id, const char *name,
804*cb981343SJakub Wojciech Klama     size_t len)
805*cb981343SJakub Wojciech Klama {
806*cb981343SJakub Wojciech Klama 	device_t dev;
807*cb981343SJakub Wojciech Klama 	struct vtcon_softc_port *scport;
808*cb981343SJakub Wojciech Klama 	struct vtcon_port *port;
809*cb981343SJakub Wojciech Klama 
810*cb981343SJakub Wojciech Klama 	dev = sc->vtcon_dev;
811*cb981343SJakub Wojciech Klama 	scport = &sc->vtcon_ports[id];
812*cb981343SJakub Wojciech Klama 
813*cb981343SJakub Wojciech Klama 	port = scport->vcsp_port;
814*cb981343SJakub Wojciech Klama 	if (port == NULL) {
815*cb981343SJakub Wojciech Klama 		device_printf(dev, "%s: name port %d, but does not exist\n",
816*cb981343SJakub Wojciech Klama 		    __func__, id);
817*cb981343SJakub Wojciech Klama 		return;
818*cb981343SJakub Wojciech Klama 	}
819*cb981343SJakub Wojciech Klama 
820*cb981343SJakub Wojciech Klama 	tty_makealias(port->vtcport_tty, "vtcon/%*s", (int)len, name);
821*cb981343SJakub Wojciech Klama }
822*cb981343SJakub Wojciech Klama 
823*cb981343SJakub Wojciech Klama static void
82404434c94SBryan Venteicher vtcon_ctrl_process_event(struct vtcon_softc *sc,
825*cb981343SJakub Wojciech Klama     struct virtio_console_control *control, void *payload, size_t plen)
8266f744ddeSBryan Venteicher {
8276f744ddeSBryan Venteicher 	device_t dev;
8286f744ddeSBryan Venteicher 	int id;
8296f744ddeSBryan Venteicher 
8306f744ddeSBryan Venteicher 	dev = sc->vtcon_dev;
8316f744ddeSBryan Venteicher 	id = control->id;
8326f744ddeSBryan Venteicher 
8336f744ddeSBryan Venteicher 	if (id < 0 || id >= sc->vtcon_max_ports) {
8346f744ddeSBryan Venteicher 		device_printf(dev, "%s: invalid port ID %d\n", __func__, id);
8356f744ddeSBryan Venteicher 		return;
8366f744ddeSBryan Venteicher 	}
8376f744ddeSBryan Venteicher 
8386f744ddeSBryan Venteicher 	switch (control->event) {
8396f744ddeSBryan Venteicher 	case VIRTIO_CONSOLE_PORT_ADD:
8406f744ddeSBryan Venteicher 		vtcon_ctrl_port_add_event(sc, id);
8416f744ddeSBryan Venteicher 		break;
8426f744ddeSBryan Venteicher 
8436f744ddeSBryan Venteicher 	case VIRTIO_CONSOLE_PORT_REMOVE:
8446f744ddeSBryan Venteicher 		vtcon_ctrl_port_remove_event(sc, id);
8456f744ddeSBryan Venteicher 		break;
8466f744ddeSBryan Venteicher 
8476f744ddeSBryan Venteicher 	case VIRTIO_CONSOLE_CONSOLE_PORT:
8486f744ddeSBryan Venteicher 		vtcon_ctrl_port_console_event(sc, id);
8496f744ddeSBryan Venteicher 		break;
8506f744ddeSBryan Venteicher 
8516f744ddeSBryan Venteicher 	case VIRTIO_CONSOLE_RESIZE:
8526f744ddeSBryan Venteicher 		break;
8536f744ddeSBryan Venteicher 
8546f744ddeSBryan Venteicher 	case VIRTIO_CONSOLE_PORT_OPEN:
8556f744ddeSBryan Venteicher 		vtcon_ctrl_port_open_event(sc, id);
8566f744ddeSBryan Venteicher 		break;
8576f744ddeSBryan Venteicher 
8586f744ddeSBryan Venteicher 	case VIRTIO_CONSOLE_PORT_NAME:
859*cb981343SJakub Wojciech Klama 		if (payload != NULL && plen > 0)
860*cb981343SJakub Wojciech Klama 			vtcon_ctrl_port_name_event(sc, id,
861*cb981343SJakub Wojciech Klama 			    (const char *)payload, plen);
8626f744ddeSBryan Venteicher 		break;
8636f744ddeSBryan Venteicher 	}
8646f744ddeSBryan Venteicher }
8656f744ddeSBryan Venteicher 
8666f744ddeSBryan Venteicher static void
8676f744ddeSBryan Venteicher vtcon_ctrl_task_cb(void *xsc, int pending)
8686f744ddeSBryan Venteicher {
8696f744ddeSBryan Venteicher 	struct vtcon_softc *sc;
8706f744ddeSBryan Venteicher 	struct virtqueue *vq;
8716f744ddeSBryan Venteicher 	struct virtio_console_control *control;
87204434c94SBryan Venteicher 	int detached;
873*cb981343SJakub Wojciech Klama 	uint32_t len;
874*cb981343SJakub Wojciech Klama 	size_t plen;
875*cb981343SJakub Wojciech Klama 	void *payload;
8766f744ddeSBryan Venteicher 
8776f744ddeSBryan Venteicher 	sc = xsc;
8786f744ddeSBryan Venteicher 	vq = sc->vtcon_ctrl_rxvq;
8796f744ddeSBryan Venteicher 
8806f744ddeSBryan Venteicher 	VTCON_LOCK(sc);
88104434c94SBryan Venteicher 
88204434c94SBryan Venteicher 	while ((detached = (sc->vtcon_flags & VTCON_FLAG_DETACHED)) == 0) {
883*cb981343SJakub Wojciech Klama 		control = virtqueue_dequeue(vq, &len);
884*cb981343SJakub Wojciech Klama 		payload = NULL;
885*cb981343SJakub Wojciech Klama 		plen = 0;
886*cb981343SJakub Wojciech Klama 
8876f744ddeSBryan Venteicher 		if (control == NULL)
8886f744ddeSBryan Venteicher 			break;
8896f744ddeSBryan Venteicher 
890*cb981343SJakub Wojciech Klama 		if (len > sizeof(control)) {
891*cb981343SJakub Wojciech Klama 			payload = (void *)(control + 1);
892*cb981343SJakub Wojciech Klama 			plen = len - sizeof(control);
893*cb981343SJakub Wojciech Klama 		}
894*cb981343SJakub Wojciech Klama 
8956f744ddeSBryan Venteicher 		VTCON_UNLOCK(sc);
896*cb981343SJakub Wojciech Klama 		vtcon_ctrl_process_event(sc, control, payload, plen);
8976f744ddeSBryan Venteicher 		VTCON_LOCK(sc);
89804434c94SBryan Venteicher 		vtcon_ctrl_event_requeue(sc, control);
8996f744ddeSBryan Venteicher 	}
9006f744ddeSBryan Venteicher 
90104434c94SBryan Venteicher 	if (!detached) {
90204434c94SBryan Venteicher 		virtqueue_notify(vq);
9036f744ddeSBryan Venteicher 		if (virtqueue_enable_intr(vq) != 0)
90404434c94SBryan Venteicher 			taskqueue_enqueue(taskqueue_thread,
90504434c94SBryan Venteicher 			    &sc->vtcon_ctrl_task);
90604434c94SBryan Venteicher 	}
90704434c94SBryan Venteicher 
90804434c94SBryan Venteicher 	VTCON_UNLOCK(sc);
90904434c94SBryan Venteicher }
91004434c94SBryan Venteicher 
91104434c94SBryan Venteicher static void
91204434c94SBryan Venteicher vtcon_ctrl_event_intr(void *xsc)
91304434c94SBryan Venteicher {
91404434c94SBryan Venteicher 	struct vtcon_softc *sc;
91504434c94SBryan Venteicher 
91604434c94SBryan Venteicher 	sc = xsc;
91704434c94SBryan Venteicher 
91804434c94SBryan Venteicher 	/*
91904434c94SBryan Venteicher 	 * Only some events require us to potentially block, but it
92004434c94SBryan Venteicher 	 * easier to just defer all event handling to the taskqueue.
92104434c94SBryan Venteicher 	 */
9226f744ddeSBryan Venteicher 	taskqueue_enqueue(taskqueue_thread, &sc->vtcon_ctrl_task);
9236f744ddeSBryan Venteicher }
9246f744ddeSBryan Venteicher 
92504434c94SBryan Venteicher static void
92604434c94SBryan Venteicher vtcon_ctrl_poll(struct vtcon_softc *sc,
92704434c94SBryan Venteicher     struct virtio_console_control *control)
9286f744ddeSBryan Venteicher {
92904434c94SBryan Venteicher 	struct sglist_seg segs[2];
93004434c94SBryan Venteicher 	struct sglist sg;
93104434c94SBryan Venteicher 	struct virtqueue *vq;
93204434c94SBryan Venteicher 	int error;
93304434c94SBryan Venteicher 
93404434c94SBryan Venteicher 	vq = sc->vtcon_ctrl_txvq;
93504434c94SBryan Venteicher 
93604434c94SBryan Venteicher 	sglist_init(&sg, 2, segs);
93704434c94SBryan Venteicher 	error = sglist_append(&sg, control,
93804434c94SBryan Venteicher 	    sizeof(struct virtio_console_control));
93904434c94SBryan Venteicher 	KASSERT(error == 0, ("%s: error %d adding control to sglist",
94004434c94SBryan Venteicher 	    __func__, error));
94104434c94SBryan Venteicher 
94204434c94SBryan Venteicher 	/*
94304434c94SBryan Venteicher 	 * We cannot use the softc lock to serialize access to this
94404434c94SBryan Venteicher 	 * virtqueue since this is called from the tty layer with the
94504434c94SBryan Venteicher 	 * port lock held. Acquiring the softc would violate our lock
94604434c94SBryan Venteicher 	 * ordering.
94704434c94SBryan Venteicher 	 */
94804434c94SBryan Venteicher 	VTCON_CTRL_TX_LOCK(sc);
94904434c94SBryan Venteicher 	KASSERT(virtqueue_empty(vq),
95004434c94SBryan Venteicher 	    ("%s: virtqueue is not emtpy", __func__));
95104434c94SBryan Venteicher 	error = virtqueue_enqueue(vq, control, &sg, sg.sg_nseg, 0);
95204434c94SBryan Venteicher 	if (error == 0) {
95304434c94SBryan Venteicher 		virtqueue_notify(vq);
95404434c94SBryan Venteicher 		virtqueue_poll(vq, NULL);
95504434c94SBryan Venteicher 	}
95604434c94SBryan Venteicher 	VTCON_CTRL_TX_UNLOCK(sc);
95704434c94SBryan Venteicher }
95804434c94SBryan Venteicher 
95904434c94SBryan Venteicher static void
96004434c94SBryan Venteicher vtcon_ctrl_send_control(struct vtcon_softc *sc, uint32_t portid,
96104434c94SBryan Venteicher     uint16_t event, uint16_t value)
96204434c94SBryan Venteicher {
96304434c94SBryan Venteicher 	struct virtio_console_control control;
96404434c94SBryan Venteicher 
96504434c94SBryan Venteicher 	if ((sc->vtcon_flags & VTCON_FLAG_MULTIPORT) == 0)
96604434c94SBryan Venteicher 		return;
96704434c94SBryan Venteicher 
96804434c94SBryan Venteicher 	control.id = portid;
96904434c94SBryan Venteicher 	control.event = event;
97004434c94SBryan Venteicher 	control.value = value;
97104434c94SBryan Venteicher 
97204434c94SBryan Venteicher 	vtcon_ctrl_poll(sc, &control);
97304434c94SBryan Venteicher }
97404434c94SBryan Venteicher 
97504434c94SBryan Venteicher static int
97604434c94SBryan Venteicher vtcon_port_enqueue_buf(struct vtcon_port *port, void *buf, size_t len)
97704434c94SBryan Venteicher {
97804434c94SBryan Venteicher 	struct sglist_seg segs[2];
9796f744ddeSBryan Venteicher 	struct sglist sg;
9806f744ddeSBryan Venteicher 	struct virtqueue *vq;
9816f744ddeSBryan Venteicher 	int error;
9826f744ddeSBryan Venteicher 
9836f744ddeSBryan Venteicher 	vq = port->vtcport_invq;
9846f744ddeSBryan Venteicher 
98504434c94SBryan Venteicher 	sglist_init(&sg, 2, segs);
9866f744ddeSBryan Venteicher 	error = sglist_append(&sg, buf, len);
98704434c94SBryan Venteicher 	KASSERT(error == 0,
9886f744ddeSBryan Venteicher 	    ("%s: error %d adding buffer to sglist", __func__, error));
9896f744ddeSBryan Venteicher 
99004434c94SBryan Venteicher 	error = virtqueue_enqueue(vq, buf, &sg, 0, sg.sg_nseg);
99104434c94SBryan Venteicher 
99204434c94SBryan Venteicher 	return (error);
9936f744ddeSBryan Venteicher }
9946f744ddeSBryan Venteicher 
9956f744ddeSBryan Venteicher static int
99604434c94SBryan Venteicher vtcon_port_create_buf(struct vtcon_port *port)
9976f744ddeSBryan Venteicher {
9986f744ddeSBryan Venteicher 	void *buf;
9996f744ddeSBryan Venteicher 	int error;
10006f744ddeSBryan Venteicher 
10016f744ddeSBryan Venteicher 	buf = malloc(VTCON_BULK_BUFSZ, M_DEVBUF, M_ZERO | M_NOWAIT);
10026f744ddeSBryan Venteicher 	if (buf == NULL)
10036f744ddeSBryan Venteicher 		return (ENOMEM);
10046f744ddeSBryan Venteicher 
100504434c94SBryan Venteicher 	error = vtcon_port_enqueue_buf(port, buf, VTCON_BULK_BUFSZ);
10066f744ddeSBryan Venteicher 	if (error)
10076f744ddeSBryan Venteicher 		free(buf, M_DEVBUF);
10086f744ddeSBryan Venteicher 
10096f744ddeSBryan Venteicher 	return (error);
10106f744ddeSBryan Venteicher }
10116f744ddeSBryan Venteicher 
10126f744ddeSBryan Venteicher static void
101304434c94SBryan Venteicher vtcon_port_requeue_buf(struct vtcon_port *port, void *buf)
10146f744ddeSBryan Venteicher {
101504434c94SBryan Venteicher 	int error;
10166f744ddeSBryan Venteicher 
101704434c94SBryan Venteicher 	error = vtcon_port_enqueue_buf(port, buf, VTCON_BULK_BUFSZ);
10186f744ddeSBryan Venteicher 	KASSERT(error == 0,
10196f744ddeSBryan Venteicher 	    ("%s: cannot requeue input buffer %d", __func__, error));
10206f744ddeSBryan Venteicher }
10216f744ddeSBryan Venteicher 
10226f744ddeSBryan Venteicher static int
10236f744ddeSBryan Venteicher vtcon_port_populate(struct vtcon_port *port)
10246f744ddeSBryan Venteicher {
10256f744ddeSBryan Venteicher 	struct virtqueue *vq;
10266f744ddeSBryan Venteicher 	int nbufs, error;
10276f744ddeSBryan Venteicher 
10286f744ddeSBryan Venteicher 	vq = port->vtcport_invq;
10296f744ddeSBryan Venteicher 	error = ENOSPC;
10306f744ddeSBryan Venteicher 
10316f744ddeSBryan Venteicher 	for (nbufs = 0; !virtqueue_full(vq); nbufs++) {
103204434c94SBryan Venteicher 		error = vtcon_port_create_buf(port);
10336f744ddeSBryan Venteicher 		if (error)
10346f744ddeSBryan Venteicher 			break;
10356f744ddeSBryan Venteicher 	}
10366f744ddeSBryan Venteicher 
10376f744ddeSBryan Venteicher 	if (nbufs > 0) {
10386f744ddeSBryan Venteicher 		virtqueue_notify(vq);
10396f744ddeSBryan Venteicher 		error = 0;
10406f744ddeSBryan Venteicher 	}
10416f744ddeSBryan Venteicher 
10426f744ddeSBryan Venteicher 	return (error);
10436f744ddeSBryan Venteicher }
10446f744ddeSBryan Venteicher 
10456f744ddeSBryan Venteicher static void
10466f744ddeSBryan Venteicher vtcon_port_destroy(struct vtcon_port *port)
10476f744ddeSBryan Venteicher {
10486f744ddeSBryan Venteicher 
10496f744ddeSBryan Venteicher 	port->vtcport_sc = NULL;
105004434c94SBryan Venteicher 	port->vtcport_scport = NULL;
105104434c94SBryan Venteicher 	port->vtcport_invq = NULL;
105204434c94SBryan Venteicher 	port->vtcport_outvq = NULL;
10536f744ddeSBryan Venteicher 	port->vtcport_id = -1;
105404434c94SBryan Venteicher 	mtx_destroy(&port->vtcport_mtx);
10556f744ddeSBryan Venteicher 	free(port, M_DEVBUF);
10566f744ddeSBryan Venteicher }
10576f744ddeSBryan Venteicher 
10586f744ddeSBryan Venteicher static int
105904434c94SBryan Venteicher vtcon_port_init_vqs(struct vtcon_port *port)
106004434c94SBryan Venteicher {
106104434c94SBryan Venteicher 	struct vtcon_softc_port *scport;
106204434c94SBryan Venteicher 	int error;
106304434c94SBryan Venteicher 
106404434c94SBryan Venteicher 	scport = port->vtcport_scport;
106504434c94SBryan Venteicher 
106604434c94SBryan Venteicher 	port->vtcport_invq = scport->vcsp_invq;
106704434c94SBryan Venteicher 	port->vtcport_outvq = scport->vcsp_outvq;
106804434c94SBryan Venteicher 
106904434c94SBryan Venteicher 	/*
107004434c94SBryan Venteicher 	 * Free any data left over from when this virtqueue was in use by a
107104434c94SBryan Venteicher 	 * prior port. We have not yet notified the host that the port is
107204434c94SBryan Venteicher 	 * ready, so assume nothing in the virtqueue can be for us.
107304434c94SBryan Venteicher 	 */
107404434c94SBryan Venteicher 	vtcon_port_drain(port);
107504434c94SBryan Venteicher 
107604434c94SBryan Venteicher 	KASSERT(virtqueue_empty(port->vtcport_invq),
107704434c94SBryan Venteicher 	    ("%s: in virtqueue is not empty", __func__));
107804434c94SBryan Venteicher 	KASSERT(virtqueue_empty(port->vtcport_outvq),
107904434c94SBryan Venteicher 	    ("%s: out virtqueue is not empty", __func__));
108004434c94SBryan Venteicher 
108104434c94SBryan Venteicher 	error = vtcon_port_populate(port);
108204434c94SBryan Venteicher 	if (error)
108304434c94SBryan Venteicher 		return (error);
108404434c94SBryan Venteicher 
108504434c94SBryan Venteicher 	return (0);
108604434c94SBryan Venteicher }
108704434c94SBryan Venteicher 
108804434c94SBryan Venteicher static int
108904434c94SBryan Venteicher vtcon_port_create(struct vtcon_softc *sc, int id)
10906f744ddeSBryan Venteicher {
10916f744ddeSBryan Venteicher 	device_t dev;
109204434c94SBryan Venteicher 	struct vtcon_softc_port *scport;
10936f744ddeSBryan Venteicher 	struct vtcon_port *port;
10946f744ddeSBryan Venteicher 	int error;
10956f744ddeSBryan Venteicher 
10966f744ddeSBryan Venteicher 	dev = sc->vtcon_dev;
109704434c94SBryan Venteicher 	scport = &sc->vtcon_ports[id];
10986f744ddeSBryan Venteicher 
109904434c94SBryan Venteicher 	VTCON_ASSERT_VALID_PORTID(sc, id);
110004434c94SBryan Venteicher 	MPASS(scport->vcsp_port == NULL);
11016f744ddeSBryan Venteicher 
11026f744ddeSBryan Venteicher 	port = malloc(sizeof(struct vtcon_port), M_DEVBUF, M_NOWAIT | M_ZERO);
11036f744ddeSBryan Venteicher 	if (port == NULL)
11046f744ddeSBryan Venteicher 		return (ENOMEM);
11056f744ddeSBryan Venteicher 
11066f744ddeSBryan Venteicher 	port->vtcport_sc = sc;
110704434c94SBryan Venteicher 	port->vtcport_scport = scport;
11086f744ddeSBryan Venteicher 	port->vtcport_id = id;
110904434c94SBryan Venteicher 	mtx_init(&port->vtcport_mtx, "vtcpmtx", NULL, MTX_DEF);
11106f744ddeSBryan Venteicher 	port->vtcport_tty = tty_alloc_mutex(&vtcon_tty_class, port,
11116f744ddeSBryan Venteicher 	    &port->vtcport_mtx);
11126f744ddeSBryan Venteicher 
111304434c94SBryan Venteicher 	error = vtcon_port_init_vqs(port);
11146f744ddeSBryan Venteicher 	if (error) {
111504434c94SBryan Venteicher 		VTCON_PORT_LOCK(port);
111604434c94SBryan Venteicher 		vtcon_port_teardown(port);
11176f744ddeSBryan Venteicher 		return (error);
11186f744ddeSBryan Venteicher 	}
11196f744ddeSBryan Venteicher 
11206f744ddeSBryan Venteicher 	VTCON_LOCK(sc);
112104434c94SBryan Venteicher 	VTCON_PORT_LOCK(port);
112204434c94SBryan Venteicher 	scport->vcsp_port = port;
112346822c48SBryan Venteicher 	vtcon_port_enable_intr(port);
112404434c94SBryan Venteicher 	vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_READY, 1);
112504434c94SBryan Venteicher 	VTCON_PORT_UNLOCK(port);
11266f744ddeSBryan Venteicher 	VTCON_UNLOCK(sc);
11276f744ddeSBryan Venteicher 
112846822c48SBryan Venteicher 	tty_makedev(port->vtcport_tty, NULL, "%s%r.%r", VTCON_TTY_PREFIX,
112946822c48SBryan Venteicher 	    device_get_unit(dev), id);
113046822c48SBryan Venteicher 
11316f744ddeSBryan Venteicher 	return (0);
11326f744ddeSBryan Venteicher }
11336f744ddeSBryan Venteicher 
11346f744ddeSBryan Venteicher static void
113504434c94SBryan Venteicher vtcon_port_drain_bufs(struct virtqueue *vq)
11366f744ddeSBryan Venteicher {
11376f744ddeSBryan Venteicher 	void *buf;
11386f744ddeSBryan Venteicher 	int last;
11396f744ddeSBryan Venteicher 
11406f744ddeSBryan Venteicher 	last = 0;
11416f744ddeSBryan Venteicher 
11426f744ddeSBryan Venteicher 	while ((buf = virtqueue_drain(vq, &last)) != NULL)
11436f744ddeSBryan Venteicher 		free(buf, M_DEVBUF);
11446f744ddeSBryan Venteicher }
11456f744ddeSBryan Venteicher 
11466f744ddeSBryan Venteicher static void
114704434c94SBryan Venteicher vtcon_port_drain(struct vtcon_port *port)
11486f744ddeSBryan Venteicher {
11496f744ddeSBryan Venteicher 
115004434c94SBryan Venteicher 	vtcon_port_drain_bufs(port->vtcport_invq);
115104434c94SBryan Venteicher }
115204434c94SBryan Venteicher 
115304434c94SBryan Venteicher static void
115404434c94SBryan Venteicher vtcon_port_teardown(struct vtcon_port *port)
115504434c94SBryan Venteicher {
115604434c94SBryan Venteicher 	struct tty *tp;
115704434c94SBryan Venteicher 
11586f744ddeSBryan Venteicher 	tp = port->vtcport_tty;
11596f744ddeSBryan Venteicher 
116004434c94SBryan Venteicher 	port->vtcport_flags |= VTCON_PORT_FLAG_GONE;
11616f744ddeSBryan Venteicher 
11626f744ddeSBryan Venteicher 	if (tp != NULL) {
11636f744ddeSBryan Venteicher 		atomic_add_int(&vtcon_pending_free, 1);
11646f744ddeSBryan Venteicher 		tty_rel_gone(tp);
11656f744ddeSBryan Venteicher 	} else
11666f744ddeSBryan Venteicher 		vtcon_port_destroy(port);
11676f744ddeSBryan Venteicher }
11686f744ddeSBryan Venteicher 
11696f744ddeSBryan Venteicher static void
11706f744ddeSBryan Venteicher vtcon_port_change_size(struct vtcon_port *port, uint16_t cols, uint16_t rows)
11716f744ddeSBryan Venteicher {
11726f744ddeSBryan Venteicher 	struct tty *tp;
11736f744ddeSBryan Venteicher 	struct winsize sz;
11746f744ddeSBryan Venteicher 
11756f744ddeSBryan Venteicher 	tp = port->vtcport_tty;
11766f744ddeSBryan Venteicher 
11776f744ddeSBryan Venteicher 	if (tp == NULL)
11786f744ddeSBryan Venteicher 		return;
11796f744ddeSBryan Venteicher 
11806f744ddeSBryan Venteicher 	bzero(&sz, sizeof(struct winsize));
11816f744ddeSBryan Venteicher 	sz.ws_col = cols;
11826f744ddeSBryan Venteicher 	sz.ws_row = rows;
11836f744ddeSBryan Venteicher 
11846f744ddeSBryan Venteicher 	tty_set_winsize(tp, &sz);
118504434c94SBryan Venteicher }
118604434c94SBryan Venteicher 
118704434c94SBryan Venteicher static void
118804434c94SBryan Venteicher vtcon_port_update_console_size(struct vtcon_softc *sc)
118904434c94SBryan Venteicher {
119004434c94SBryan Venteicher 	struct vtcon_port *port;
119104434c94SBryan Venteicher 	struct vtcon_softc_port *scport;
119204434c94SBryan Venteicher 	uint16_t cols, rows;
119304434c94SBryan Venteicher 
119404434c94SBryan Venteicher 	vtcon_get_console_size(sc, &cols, &rows);
119504434c94SBryan Venteicher 
119604434c94SBryan Venteicher 	/*
119704434c94SBryan Venteicher 	 * For now, assume the first (only) port is the console. Note
119804434c94SBryan Venteicher 	 * QEMU does not implement this feature yet.
119904434c94SBryan Venteicher 	 */
120004434c94SBryan Venteicher 	scport = &sc->vtcon_ports[0];
120104434c94SBryan Venteicher 
120204434c94SBryan Venteicher 	VTCON_LOCK(sc);
120304434c94SBryan Venteicher 	port = scport->vcsp_port;
120404434c94SBryan Venteicher 
120504434c94SBryan Venteicher 	if (port != NULL) {
120604434c94SBryan Venteicher 		VTCON_PORT_LOCK(port);
120704434c94SBryan Venteicher 		VTCON_UNLOCK(sc);
120804434c94SBryan Venteicher 		vtcon_port_change_size(port, cols, rows);
12096f744ddeSBryan Venteicher 		VTCON_PORT_UNLOCK(port);
121004434c94SBryan Venteicher 	} else
121104434c94SBryan Venteicher 		VTCON_UNLOCK(sc);
12126f744ddeSBryan Venteicher }
12136f744ddeSBryan Venteicher 
12146f744ddeSBryan Venteicher static void
12156f744ddeSBryan Venteicher vtcon_port_enable_intr(struct vtcon_port *port)
12166f744ddeSBryan Venteicher {
12176f744ddeSBryan Venteicher 
12186f744ddeSBryan Venteicher 	/*
1219453130d9SPedro F. Giffuni 	 * NOTE: The out virtqueue is always polled, so its interrupt
122004434c94SBryan Venteicher 	 * kept disabled.
12216f744ddeSBryan Venteicher 	 */
12226f744ddeSBryan Venteicher 	virtqueue_enable_intr(port->vtcport_invq);
12236f744ddeSBryan Venteicher }
12246f744ddeSBryan Venteicher 
12256f744ddeSBryan Venteicher static void
12266f744ddeSBryan Venteicher vtcon_port_disable_intr(struct vtcon_port *port)
12276f744ddeSBryan Venteicher {
12286f744ddeSBryan Venteicher 
12296f744ddeSBryan Venteicher 	if (port->vtcport_invq != NULL)
12306f744ddeSBryan Venteicher 		virtqueue_disable_intr(port->vtcport_invq);
12316f744ddeSBryan Venteicher 	if (port->vtcport_outvq != NULL)
12326f744ddeSBryan Venteicher 		virtqueue_disable_intr(port->vtcport_outvq);
12336f744ddeSBryan Venteicher }
12346f744ddeSBryan Venteicher 
12356f744ddeSBryan Venteicher static void
123604434c94SBryan Venteicher vtcon_port_in(struct vtcon_port *port)
12376f744ddeSBryan Venteicher {
12386f744ddeSBryan Venteicher 	struct virtqueue *vq;
123904434c94SBryan Venteicher 	struct tty *tp;
12406f744ddeSBryan Venteicher 	char *buf;
12416f744ddeSBryan Venteicher 	uint32_t len;
12426f744ddeSBryan Venteicher 	int i, deq;
12436f744ddeSBryan Venteicher 
12446f744ddeSBryan Venteicher 	tp = port->vtcport_tty;
12456f744ddeSBryan Venteicher 	vq = port->vtcport_invq;
12466f744ddeSBryan Venteicher 
12476f744ddeSBryan Venteicher again:
12486f744ddeSBryan Venteicher 	deq = 0;
12496f744ddeSBryan Venteicher 
12506f744ddeSBryan Venteicher 	while ((buf = virtqueue_dequeue(vq, &len)) != NULL) {
1251b84b3efdSBryan Venteicher 		for (i = 0; i < len; i++) {
1252b84b3efdSBryan Venteicher #if defined(KDB)
1253b84b3efdSBryan Venteicher 			if (port->vtcport_flags & VTCON_PORT_FLAG_CONSOLE)
1254b84b3efdSBryan Venteicher 				kdb_alt_break(buf[i],
1255b84b3efdSBryan Venteicher 				    &port->vtcport_alt_break_state);
1256b84b3efdSBryan Venteicher #endif
12576f744ddeSBryan Venteicher 			ttydisc_rint(tp, buf[i], 0);
1258b84b3efdSBryan Venteicher 		}
125904434c94SBryan Venteicher 		vtcon_port_requeue_buf(port, buf);
126004434c94SBryan Venteicher 		deq++;
12616f744ddeSBryan Venteicher 	}
12626f744ddeSBryan Venteicher 	ttydisc_rint_done(tp);
12636f744ddeSBryan Venteicher 
12646f744ddeSBryan Venteicher 	if (deq > 0)
12656f744ddeSBryan Venteicher 		virtqueue_notify(vq);
12666f744ddeSBryan Venteicher 
12676f744ddeSBryan Venteicher 	if (virtqueue_enable_intr(vq) != 0)
12686f744ddeSBryan Venteicher 		goto again;
12696f744ddeSBryan Venteicher }
12706f744ddeSBryan Venteicher 
12716f744ddeSBryan Venteicher static void
127204434c94SBryan Venteicher vtcon_port_intr(void *scportx)
12736f744ddeSBryan Venteicher {
127404434c94SBryan Venteicher 	struct vtcon_softc_port *scport;
127504434c94SBryan Venteicher 	struct vtcon_softc *sc;
12766f744ddeSBryan Venteicher 	struct vtcon_port *port;
12776f744ddeSBryan Venteicher 
127804434c94SBryan Venteicher 	scport = scportx;
127904434c94SBryan Venteicher 	sc = scport->vcsp_sc;
12806f744ddeSBryan Venteicher 
128104434c94SBryan Venteicher 	VTCON_LOCK(sc);
128204434c94SBryan Venteicher 	port = scport->vcsp_port;
128304434c94SBryan Venteicher 	if (port == NULL) {
128404434c94SBryan Venteicher 		VTCON_UNLOCK(sc);
128504434c94SBryan Venteicher 		return;
128604434c94SBryan Venteicher 	}
128704434c94SBryan Venteicher 	VTCON_PORT_LOCK(port);
128804434c94SBryan Venteicher 	VTCON_UNLOCK(sc);
128904434c94SBryan Venteicher 	if ((port->vtcport_flags & VTCON_PORT_FLAG_GONE) == 0)
129004434c94SBryan Venteicher 		vtcon_port_in(port);
129104434c94SBryan Venteicher 	VTCON_PORT_UNLOCK(port);
12926f744ddeSBryan Venteicher }
12936f744ddeSBryan Venteicher 
12946f744ddeSBryan Venteicher static void
129504434c94SBryan Venteicher vtcon_port_out(struct vtcon_port *port, void *buf, int bufsize)
12966f744ddeSBryan Venteicher {
129704434c94SBryan Venteicher 	struct sglist_seg segs[2];
12986f744ddeSBryan Venteicher 	struct sglist sg;
12996f744ddeSBryan Venteicher 	struct virtqueue *vq;
13006f744ddeSBryan Venteicher 	int error;
13016f744ddeSBryan Venteicher 
13026f744ddeSBryan Venteicher 	vq = port->vtcport_outvq;
130304434c94SBryan Venteicher 	KASSERT(virtqueue_empty(vq),
130404434c94SBryan Venteicher 	    ("%s: port %p out virtqueue not emtpy", __func__, port));
13056f744ddeSBryan Venteicher 
130604434c94SBryan Venteicher 	sglist_init(&sg, 2, segs);
13076f744ddeSBryan Venteicher 	error = sglist_append(&sg, buf, bufsize);
130804434c94SBryan Venteicher 	KASSERT(error == 0, ("%s: error %d adding buffer to sglist",
130904434c94SBryan Venteicher 	    __func__, error));
13106f744ddeSBryan Venteicher 
131104434c94SBryan Venteicher 	error = virtqueue_enqueue(vq, buf, &sg, sg.sg_nseg, 0);
131204434c94SBryan Venteicher 	if (error == 0) {
13136f744ddeSBryan Venteicher 		virtqueue_notify(vq);
13146f744ddeSBryan Venteicher 		virtqueue_poll(vq, NULL);
13156f744ddeSBryan Venteicher 	}
13166f744ddeSBryan Venteicher }
13176f744ddeSBryan Venteicher 
13186f744ddeSBryan Venteicher static void
131904434c94SBryan Venteicher vtcon_port_submit_event(struct vtcon_port *port, uint16_t event,
13206f744ddeSBryan Venteicher     uint16_t value)
13216f744ddeSBryan Venteicher {
13226f744ddeSBryan Venteicher 	struct vtcon_softc *sc;
13236f744ddeSBryan Venteicher 
13246f744ddeSBryan Venteicher 	sc = port->vtcport_sc;
13256f744ddeSBryan Venteicher 
132604434c94SBryan Venteicher 	vtcon_ctrl_send_control(sc, port->vtcport_id, event, value);
13276f744ddeSBryan Venteicher }
13286f744ddeSBryan Venteicher 
13296f744ddeSBryan Venteicher static int
13306f744ddeSBryan Venteicher vtcon_tty_open(struct tty *tp)
13316f744ddeSBryan Venteicher {
13326f744ddeSBryan Venteicher 	struct vtcon_port *port;
13336f744ddeSBryan Venteicher 
13346f744ddeSBryan Venteicher 	port = tty_softc(tp);
13356f744ddeSBryan Venteicher 
133604434c94SBryan Venteicher 	if (port->vtcport_flags & VTCON_PORT_FLAG_GONE)
133704434c94SBryan Venteicher 		return (ENXIO);
133804434c94SBryan Venteicher 
133904434c94SBryan Venteicher 	vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_OPEN, 1);
13406f744ddeSBryan Venteicher 
13416f744ddeSBryan Venteicher 	return (0);
13426f744ddeSBryan Venteicher }
13436f744ddeSBryan Venteicher 
13446f744ddeSBryan Venteicher static void
13456f744ddeSBryan Venteicher vtcon_tty_close(struct tty *tp)
13466f744ddeSBryan Venteicher {
13476f744ddeSBryan Venteicher 	struct vtcon_port *port;
13486f744ddeSBryan Venteicher 
13496f744ddeSBryan Venteicher 	port = tty_softc(tp);
13506f744ddeSBryan Venteicher 
135104434c94SBryan Venteicher 	if (port->vtcport_flags & VTCON_PORT_FLAG_GONE)
135204434c94SBryan Venteicher 		return;
135304434c94SBryan Venteicher 
135404434c94SBryan Venteicher 	vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_OPEN, 0);
13556f744ddeSBryan Venteicher }
13566f744ddeSBryan Venteicher 
13576f744ddeSBryan Venteicher static void
13586f744ddeSBryan Venteicher vtcon_tty_outwakeup(struct tty *tp)
13596f744ddeSBryan Venteicher {
13606f744ddeSBryan Venteicher 	struct vtcon_port *port;
13616f744ddeSBryan Venteicher 	char buf[VTCON_BULK_BUFSZ];
13626f744ddeSBryan Venteicher 	int len;
13636f744ddeSBryan Venteicher 
13646f744ddeSBryan Venteicher 	port = tty_softc(tp);
13656f744ddeSBryan Venteicher 
136604434c94SBryan Venteicher 	if (port->vtcport_flags & VTCON_PORT_FLAG_GONE)
136704434c94SBryan Venteicher 		return;
136804434c94SBryan Venteicher 
13696f744ddeSBryan Venteicher 	while ((len = ttydisc_getc(tp, buf, sizeof(buf))) != 0)
137004434c94SBryan Venteicher 		vtcon_port_out(port, buf, len);
13716f744ddeSBryan Venteicher }
13726f744ddeSBryan Venteicher 
13736f744ddeSBryan Venteicher static void
13746f744ddeSBryan Venteicher vtcon_tty_free(void *xport)
13756f744ddeSBryan Venteicher {
13766f744ddeSBryan Venteicher 	struct vtcon_port *port;
13776f744ddeSBryan Venteicher 
13786f744ddeSBryan Venteicher 	port = xport;
13796f744ddeSBryan Venteicher 
13806f744ddeSBryan Venteicher 	vtcon_port_destroy(port);
13816f744ddeSBryan Venteicher 	atomic_subtract_int(&vtcon_pending_free, 1);
13826f744ddeSBryan Venteicher }
13836f744ddeSBryan Venteicher 
13846f744ddeSBryan Venteicher static void
13856f744ddeSBryan Venteicher vtcon_get_console_size(struct vtcon_softc *sc, uint16_t *cols, uint16_t *rows)
13866f744ddeSBryan Venteicher {
13876f744ddeSBryan Venteicher 	struct virtio_console_config concfg;
13886f744ddeSBryan Venteicher 
13896f744ddeSBryan Venteicher 	KASSERT(sc->vtcon_flags & VTCON_FLAG_SIZE,
13906f744ddeSBryan Venteicher 	    ("%s: size feature not negotiated", __func__));
13916f744ddeSBryan Venteicher 
13926f744ddeSBryan Venteicher 	vtcon_read_config(sc, &concfg);
13936f744ddeSBryan Venteicher 
13946f744ddeSBryan Venteicher 	*cols = concfg.cols;
13956f744ddeSBryan Venteicher 	*rows = concfg.rows;
13966f744ddeSBryan Venteicher }
13976f744ddeSBryan Venteicher 
13986f744ddeSBryan Venteicher static void
13996f744ddeSBryan Venteicher vtcon_enable_interrupts(struct vtcon_softc *sc)
14006f744ddeSBryan Venteicher {
140104434c94SBryan Venteicher 	struct vtcon_softc_port *scport;
14026f744ddeSBryan Venteicher 	struct vtcon_port *port;
140304434c94SBryan Venteicher 	int i;
140404434c94SBryan Venteicher 
140504434c94SBryan Venteicher 	VTCON_LOCK(sc);
14066f744ddeSBryan Venteicher 
14076f744ddeSBryan Venteicher 	if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT)
14086f744ddeSBryan Venteicher 		virtqueue_enable_intr(sc->vtcon_ctrl_rxvq);
14096f744ddeSBryan Venteicher 
141004434c94SBryan Venteicher 	for (i = 0; i < sc->vtcon_max_ports; i++) {
141104434c94SBryan Venteicher 		scport = &sc->vtcon_ports[i];
141204434c94SBryan Venteicher 
141304434c94SBryan Venteicher 		port = scport->vcsp_port;
141404434c94SBryan Venteicher 		if (port == NULL)
141504434c94SBryan Venteicher 			continue;
141604434c94SBryan Venteicher 
141704434c94SBryan Venteicher 		VTCON_PORT_LOCK(port);
14186f744ddeSBryan Venteicher 		vtcon_port_enable_intr(port);
141904434c94SBryan Venteicher 		VTCON_PORT_UNLOCK(port);
142004434c94SBryan Venteicher 	}
142104434c94SBryan Venteicher 
142204434c94SBryan Venteicher 	VTCON_UNLOCK(sc);
14236f744ddeSBryan Venteicher }
14246f744ddeSBryan Venteicher 
14256f744ddeSBryan Venteicher static void
14266f744ddeSBryan Venteicher vtcon_disable_interrupts(struct vtcon_softc *sc)
14276f744ddeSBryan Venteicher {
142804434c94SBryan Venteicher 	struct vtcon_softc_port *scport;
14296f744ddeSBryan Venteicher 	struct vtcon_port *port;
143004434c94SBryan Venteicher 	int i;
143104434c94SBryan Venteicher 
143204434c94SBryan Venteicher 	VTCON_LOCK_ASSERT(sc);
14336f744ddeSBryan Venteicher 
14346f744ddeSBryan Venteicher 	if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT)
14356f744ddeSBryan Venteicher 		virtqueue_disable_intr(sc->vtcon_ctrl_rxvq);
14366f744ddeSBryan Venteicher 
143704434c94SBryan Venteicher 	for (i = 0; i < sc->vtcon_max_ports; i++) {
143804434c94SBryan Venteicher 		scport = &sc->vtcon_ports[i];
143904434c94SBryan Venteicher 
144004434c94SBryan Venteicher 		port = scport->vcsp_port;
144104434c94SBryan Venteicher 		if (port == NULL)
144204434c94SBryan Venteicher 			continue;
144304434c94SBryan Venteicher 
144404434c94SBryan Venteicher 		VTCON_PORT_LOCK(port);
14456f744ddeSBryan Venteicher 		vtcon_port_disable_intr(port);
144604434c94SBryan Venteicher 		VTCON_PORT_UNLOCK(port);
144704434c94SBryan Venteicher 	}
14486f744ddeSBryan Venteicher }
1449