1273515Sbryanv/*-
2273515Sbryanv * Copyright (c) 2014, Bryan Venteicher <bryanv@FreeBSD.org>
3273515Sbryanv * All rights reserved.
4273515Sbryanv *
5273515Sbryanv * Redistribution and use in source and binary forms, with or without
6273515Sbryanv * modification, are permitted provided that the following conditions
7273515Sbryanv * are met:
8273515Sbryanv * 1. Redistributions of source code must retain the above copyright
9273515Sbryanv *    notice unmodified, this list of conditions, and the following
10273515Sbryanv *    disclaimer.
11273515Sbryanv * 2. Redistributions in binary form must reproduce the above copyright
12273515Sbryanv *    notice, this list of conditions and the following disclaimer in the
13273515Sbryanv *    documentation and/or other materials provided with the distribution.
14273515Sbryanv *
15273515Sbryanv * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16273515Sbryanv * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17273515Sbryanv * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18273515Sbryanv * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19273515Sbryanv * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20273515Sbryanv * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21273515Sbryanv * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22273515Sbryanv * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23273515Sbryanv * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24273515Sbryanv * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25273515Sbryanv */
26273515Sbryanv
27273515Sbryanv/* Driver for VirtIO console devices. */
28273515Sbryanv
29273515Sbryanv#include <sys/cdefs.h>
30273515Sbryanv__FBSDID("$FreeBSD$");
31273515Sbryanv
32273515Sbryanv#include <sys/param.h>
33273515Sbryanv#include <sys/systm.h>
34273515Sbryanv#include <sys/kernel.h>
35273515Sbryanv#include <sys/malloc.h>
36273515Sbryanv#include <sys/module.h>
37275273Sbryanv#include <sys/kdb.h>
38273515Sbryanv#include <sys/lock.h>
39273515Sbryanv#include <sys/mutex.h>
40273515Sbryanv#include <sys/sglist.h>
41273515Sbryanv#include <sys/sysctl.h>
42273515Sbryanv#include <sys/taskqueue.h>
43273515Sbryanv#include <sys/queue.h>
44273515Sbryanv
45273515Sbryanv#include <sys/conf.h>
46273515Sbryanv#include <sys/cons.h>
47273515Sbryanv#include <sys/tty.h>
48273515Sbryanv
49273515Sbryanv#include <machine/bus.h>
50273515Sbryanv#include <machine/resource.h>
51273515Sbryanv#include <sys/bus.h>
52273515Sbryanv
53273515Sbryanv#include <dev/virtio/virtio.h>
54273515Sbryanv#include <dev/virtio/virtqueue.h>
55273515Sbryanv#include <dev/virtio/console/virtio_console.h>
56273515Sbryanv
57273515Sbryanv#include "virtio_if.h"
58273515Sbryanv
59275273Sbryanv#define VTCON_MAX_PORTS 32
60273515Sbryanv#define VTCON_TTY_PREFIX "V"
61273515Sbryanv#define VTCON_BULK_BUFSZ 128
62273515Sbryanv
63275273Sbryanv/*
64275273Sbryanv * The buffer cannot cross more than one page boundary due to the
65275273Sbryanv * size of the sglist segment array used.
66275273Sbryanv */
67275273SbryanvCTASSERT(VTCON_BULK_BUFSZ <= PAGE_SIZE);
68275273Sbryanv
69273515Sbryanvstruct vtcon_softc;
70275273Sbryanvstruct vtcon_softc_port;
71273515Sbryanv
72273515Sbryanvstruct vtcon_port {
73275273Sbryanv	struct mtx			 vtcport_mtx;
74275273Sbryanv	struct vtcon_softc		*vtcport_sc;
75275273Sbryanv	struct vtcon_softc_port		*vtcport_scport;
76275273Sbryanv	struct tty			*vtcport_tty;
77275273Sbryanv	struct virtqueue		*vtcport_invq;
78275273Sbryanv	struct virtqueue		*vtcport_outvq;
79275273Sbryanv	int				 vtcport_id;
80275273Sbryanv	int				 vtcport_flags;
81275273Sbryanv#define VTCON_PORT_FLAG_GONE	0x01
82275273Sbryanv#define VTCON_PORT_FLAG_CONSOLE	0x02
83275273Sbryanv
84275273Sbryanv#if defined(KDB)
85275273Sbryanv	int				 vtcport_alt_break_state;
86275273Sbryanv#endif
87273515Sbryanv};
88273515Sbryanv
89275273Sbryanv#define VTCON_PORT_LOCK(_port)		mtx_lock(&(_port)->vtcport_mtx)
90275273Sbryanv#define VTCON_PORT_UNLOCK(_port)	mtx_unlock(&(_port)->vtcport_mtx)
91273515Sbryanv
92275273Sbryanvstruct vtcon_softc_port {
93275273Sbryanv	struct vtcon_softc	*vcsp_sc;
94275273Sbryanv	struct vtcon_port	*vcsp_port;
95275273Sbryanv	struct virtqueue	*vcsp_invq;
96275273Sbryanv	struct virtqueue	*vcsp_outvq;
97275273Sbryanv};
98275273Sbryanv
99273515Sbryanvstruct vtcon_softc {
100273515Sbryanv	device_t		 vtcon_dev;
101273515Sbryanv	struct mtx		 vtcon_mtx;
102273515Sbryanv	uint64_t		 vtcon_features;
103275273Sbryanv	uint32_t		 vtcon_max_ports;
104273515Sbryanv	uint32_t		 vtcon_flags;
105275273Sbryanv#define VTCON_FLAG_DETACHED	0x01
106275273Sbryanv#define VTCON_FLAG_SIZE		0x02
107275273Sbryanv#define VTCON_FLAG_MULTIPORT	0x04
108273515Sbryanv
109273515Sbryanv	/*
110273515Sbryanv	 * Ports can be added and removed during runtime, but we have
111273515Sbryanv	 * to allocate all the virtqueues during attach. This array is
112273515Sbryanv	 * indexed by the port ID.
113273515Sbryanv	 */
114275273Sbryanv	struct vtcon_softc_port	*vtcon_ports;
115275273Sbryanv
116275273Sbryanv	struct task		 vtcon_ctrl_task;
117275273Sbryanv	struct virtqueue	*vtcon_ctrl_rxvq;
118275273Sbryanv	struct virtqueue	*vtcon_ctrl_txvq;
119275273Sbryanv	struct mtx		 vtcon_ctrl_tx_mtx;
120273515Sbryanv};
121273515Sbryanv
122275273Sbryanv#define VTCON_LOCK(_sc)			mtx_lock(&(_sc)->vtcon_mtx)
123275273Sbryanv#define VTCON_UNLOCK(_sc)		mtx_unlock(&(_sc)->vtcon_mtx)
124275273Sbryanv#define VTCON_LOCK_ASSERT(_sc)		\
125275273Sbryanv    mtx_assert(&(_sc)->vtcon_mtx, MA_OWNED)
126275273Sbryanv#define VTCON_LOCK_ASSERT_NOTOWNED(_sc)	\
127275273Sbryanv    mtx_assert(&(_sc)->vtcon_mtx, MA_NOTOWNED)
128273515Sbryanv
129275273Sbryanv#define VTCON_CTRL_TX_LOCK(_sc)		mtx_lock(&(_sc)->vtcon_ctrl_tx_mtx)
130275273Sbryanv#define VTCON_CTRL_TX_UNLOCK(_sc)	mtx_unlock(&(_sc)->vtcon_ctrl_tx_mtx)
131275273Sbryanv
132273515Sbryanv#define VTCON_ASSERT_VALID_PORTID(_sc, _id)			\
133273515Sbryanv    KASSERT((_id) >= 0 && (_id) < (_sc)->vtcon_max_ports,	\
134273515Sbryanv        ("%s: port ID %d out of range", __func__, _id))
135273515Sbryanv
136275273Sbryanv#define VTCON_FEATURES  VIRTIO_CONSOLE_F_MULTIPORT
137273515Sbryanv
138273515Sbryanvstatic struct virtio_feature_desc vtcon_feature_desc[] = {
139273515Sbryanv	{ VIRTIO_CONSOLE_F_SIZE,	"ConsoleSize"	},
140273515Sbryanv	{ VIRTIO_CONSOLE_F_MULTIPORT,	"MultiplePorts"	},
141275273Sbryanv	{ VIRTIO_CONSOLE_F_EMERG_WRITE,	"EmergencyWrite" },
142273515Sbryanv
143273515Sbryanv	{ 0, NULL }
144273515Sbryanv};
145273515Sbryanv
146273515Sbryanvstatic int	 vtcon_modevent(module_t, int, void *);
147275273Sbryanvstatic void	 vtcon_drain_all(void);
148273515Sbryanv
149273515Sbryanvstatic int	 vtcon_probe(device_t);
150273515Sbryanvstatic int	 vtcon_attach(device_t);
151273515Sbryanvstatic int	 vtcon_detach(device_t);
152273515Sbryanvstatic int	 vtcon_config_change(device_t);
153273515Sbryanv
154275273Sbryanvstatic void	 vtcon_setup_features(struct vtcon_softc *);
155273515Sbryanvstatic void	 vtcon_negotiate_features(struct vtcon_softc *);
156275273Sbryanvstatic int	 vtcon_alloc_scports(struct vtcon_softc *);
157273515Sbryanvstatic int	 vtcon_alloc_virtqueues(struct vtcon_softc *);
158273515Sbryanvstatic void	 vtcon_read_config(struct vtcon_softc *,
159273515Sbryanv		     struct virtio_console_config *);
160273515Sbryanv
161273515Sbryanvstatic void	 vtcon_determine_max_ports(struct vtcon_softc *,
162273515Sbryanv		     struct virtio_console_config *);
163275273Sbryanvstatic void	 vtcon_destroy_ports(struct vtcon_softc *);
164273515Sbryanvstatic void	 vtcon_stop(struct vtcon_softc *);
165273515Sbryanv
166275273Sbryanvstatic int	 vtcon_ctrl_event_enqueue(struct vtcon_softc *,
167273515Sbryanv		     struct virtio_console_control *);
168275273Sbryanvstatic int	 vtcon_ctrl_event_create(struct vtcon_softc *);
169275273Sbryanvstatic void	 vtcon_ctrl_event_requeue(struct vtcon_softc *,
170273515Sbryanv		     struct virtio_console_control *);
171275273Sbryanvstatic int	 vtcon_ctrl_event_populate(struct vtcon_softc *);
172275273Sbryanvstatic void	 vtcon_ctrl_event_drain(struct vtcon_softc *);
173273515Sbryanvstatic int	 vtcon_ctrl_init(struct vtcon_softc *);
174273515Sbryanvstatic void	 vtcon_ctrl_deinit(struct vtcon_softc *);
175273515Sbryanvstatic void	 vtcon_ctrl_port_add_event(struct vtcon_softc *, int);
176273515Sbryanvstatic void	 vtcon_ctrl_port_remove_event(struct vtcon_softc *, int);
177273515Sbryanvstatic void	 vtcon_ctrl_port_console_event(struct vtcon_softc *, int);
178273515Sbryanvstatic void	 vtcon_ctrl_port_open_event(struct vtcon_softc *, int);
179275273Sbryanvstatic void	 vtcon_ctrl_process_event(struct vtcon_softc *,
180273515Sbryanv		     struct virtio_console_control *);
181273515Sbryanvstatic void	 vtcon_ctrl_task_cb(void *, int);
182275273Sbryanvstatic void	 vtcon_ctrl_event_intr(void *);
183275273Sbryanvstatic void	 vtcon_ctrl_poll(struct vtcon_softc *,
184275273Sbryanv		     struct virtio_console_control *control);
185275273Sbryanvstatic void	 vtcon_ctrl_send_control(struct vtcon_softc *, uint32_t,
186275273Sbryanv		     uint16_t, uint16_t);
187273515Sbryanv
188275273Sbryanvstatic int	 vtcon_port_enqueue_buf(struct vtcon_port *, void *, size_t);
189275273Sbryanvstatic int	 vtcon_port_create_buf(struct vtcon_port *);
190275273Sbryanvstatic void	 vtcon_port_requeue_buf(struct vtcon_port *, void *);
191273515Sbryanvstatic int	 vtcon_port_populate(struct vtcon_port *);
192273515Sbryanvstatic void	 vtcon_port_destroy(struct vtcon_port *);
193275273Sbryanvstatic int	 vtcon_port_create(struct vtcon_softc *, int);
194275273Sbryanvstatic void	 vtcon_port_drain_bufs(struct virtqueue *);
195275273Sbryanvstatic void	 vtcon_port_drain(struct vtcon_port *);
196275273Sbryanvstatic void	 vtcon_port_teardown(struct vtcon_port *);
197273515Sbryanvstatic void	 vtcon_port_change_size(struct vtcon_port *, uint16_t,
198273515Sbryanv		     uint16_t);
199275273Sbryanvstatic void	 vtcon_port_update_console_size(struct vtcon_softc *);
200273515Sbryanvstatic void	 vtcon_port_enable_intr(struct vtcon_port *);
201273515Sbryanvstatic void	 vtcon_port_disable_intr(struct vtcon_port *);
202275273Sbryanvstatic void	 vtcon_port_in(struct vtcon_port *);
203275273Sbryanvstatic void	 vtcon_port_intr(void *);
204275273Sbryanvstatic void	 vtcon_port_out(struct vtcon_port *, void *, int);
205275273Sbryanvstatic void	 vtcon_port_submit_event(struct vtcon_port *, uint16_t,
206273515Sbryanv		     uint16_t);
207273515Sbryanv
208273515Sbryanvstatic int	 vtcon_tty_open(struct tty *);
209273515Sbryanvstatic void	 vtcon_tty_close(struct tty *);
210273515Sbryanvstatic void	 vtcon_tty_outwakeup(struct tty *);
211273515Sbryanvstatic void	 vtcon_tty_free(void *);
212273515Sbryanv
213273515Sbryanvstatic void	 vtcon_get_console_size(struct vtcon_softc *, uint16_t *,
214273515Sbryanv		     uint16_t *);
215273515Sbryanv
216273515Sbryanvstatic void	 vtcon_enable_interrupts(struct vtcon_softc *);
217273515Sbryanvstatic void	 vtcon_disable_interrupts(struct vtcon_softc *);
218273515Sbryanv
219273515Sbryanvstatic int	 vtcon_pending_free;
220273515Sbryanv
221273515Sbryanvstatic struct ttydevsw vtcon_tty_class = {
222273515Sbryanv	.tsw_flags	= 0,
223273515Sbryanv	.tsw_open	= vtcon_tty_open,
224273515Sbryanv	.tsw_close	= vtcon_tty_close,
225273515Sbryanv	.tsw_outwakeup	= vtcon_tty_outwakeup,
226273515Sbryanv	.tsw_free	= vtcon_tty_free,
227273515Sbryanv};
228273515Sbryanv
229273515Sbryanvstatic device_method_t vtcon_methods[] = {
230273515Sbryanv	/* Device methods. */
231273515Sbryanv	DEVMETHOD(device_probe,		vtcon_probe),
232273515Sbryanv	DEVMETHOD(device_attach,	vtcon_attach),
233273515Sbryanv	DEVMETHOD(device_detach,	vtcon_detach),
234273515Sbryanv
235273515Sbryanv	/* VirtIO methods. */
236273515Sbryanv	DEVMETHOD(virtio_config_change,	vtcon_config_change),
237273515Sbryanv
238273515Sbryanv	DEVMETHOD_END
239273515Sbryanv};
240273515Sbryanv
241273515Sbryanvstatic driver_t vtcon_driver = {
242273515Sbryanv	"vtcon",
243273515Sbryanv	vtcon_methods,
244273515Sbryanv	sizeof(struct vtcon_softc)
245273515Sbryanv};
246273515Sbryanvstatic devclass_t vtcon_devclass;
247273515Sbryanv
248273515SbryanvDRIVER_MODULE(virtio_console, virtio_pci, vtcon_driver, vtcon_devclass,
249273515Sbryanv    vtcon_modevent, 0);
250273515SbryanvMODULE_VERSION(virtio_console, 1);
251273515SbryanvMODULE_DEPEND(virtio_console, virtio, 1, 1, 1);
252273515Sbryanv
253273515Sbryanvstatic int
254273515Sbryanvvtcon_modevent(module_t mod, int type, void *unused)
255273515Sbryanv{
256273515Sbryanv	int error;
257273515Sbryanv
258273515Sbryanv	switch (type) {
259273515Sbryanv	case MOD_LOAD:
260273515Sbryanv		error = 0;
261273515Sbryanv		break;
262273515Sbryanv	case MOD_QUIESCE:
263275273Sbryanv		error = 0;
264275273Sbryanv		break;
265273515Sbryanv	case MOD_UNLOAD:
266275273Sbryanv		vtcon_drain_all();
267275273Sbryanv		error = 0;
268273515Sbryanv		break;
269273515Sbryanv	case MOD_SHUTDOWN:
270273515Sbryanv		error = 0;
271273515Sbryanv		break;
272273515Sbryanv	default:
273273515Sbryanv		error = EOPNOTSUPP;
274273515Sbryanv		break;
275273515Sbryanv	}
276273515Sbryanv
277273515Sbryanv	return (error);
278273515Sbryanv}
279273515Sbryanv
280275273Sbryanvstatic void
281275273Sbryanvvtcon_drain_all(void)
282275273Sbryanv{
283275273Sbryanv	int first;
284275273Sbryanv
285275273Sbryanv	for (first = 1; vtcon_pending_free != 0; first = 0) {
286275273Sbryanv		if (first != 0) {
287275273Sbryanv			printf("virtio_console: Waiting for all detached TTY "
288275273Sbryanv			    "devices to have open fds closed.\n");
289275273Sbryanv		}
290275273Sbryanv		pause("vtcondra", hz);
291275273Sbryanv	}
292275273Sbryanv}
293275273Sbryanv
294273515Sbryanvstatic int
295273515Sbryanvvtcon_probe(device_t dev)
296273515Sbryanv{
297273515Sbryanv
298273515Sbryanv	if (virtio_get_device_type(dev) != VIRTIO_ID_CONSOLE)
299273515Sbryanv		return (ENXIO);
300273515Sbryanv
301273515Sbryanv	device_set_desc(dev, "VirtIO Console Adapter");
302273515Sbryanv
303273515Sbryanv	return (BUS_PROBE_DEFAULT);
304273515Sbryanv}
305273515Sbryanv
306273515Sbryanvstatic int
307273515Sbryanvvtcon_attach(device_t dev)
308273515Sbryanv{
309273515Sbryanv	struct vtcon_softc *sc;
310273515Sbryanv	struct virtio_console_config concfg;
311273515Sbryanv	int error;
312273515Sbryanv
313273515Sbryanv	sc = device_get_softc(dev);
314273515Sbryanv	sc->vtcon_dev = dev;
315273515Sbryanv
316275273Sbryanv	mtx_init(&sc->vtcon_mtx, "vtconmtx", NULL, MTX_DEF);
317275273Sbryanv	mtx_init(&sc->vtcon_ctrl_tx_mtx, "vtconctrlmtx", NULL, MTX_DEF);
318273515Sbryanv
319273515Sbryanv	virtio_set_feature_desc(dev, vtcon_feature_desc);
320275273Sbryanv	vtcon_setup_features(sc);
321273515Sbryanv
322273515Sbryanv	vtcon_read_config(sc, &concfg);
323273515Sbryanv	vtcon_determine_max_ports(sc, &concfg);
324273515Sbryanv
325275273Sbryanv	error = vtcon_alloc_scports(sc);
326275273Sbryanv	if (error) {
327275273Sbryanv		device_printf(dev, "cannot allocate softc port structures\n");
328275273Sbryanv		goto fail;
329275273Sbryanv	}
330275273Sbryanv
331273515Sbryanv	error = vtcon_alloc_virtqueues(sc);
332273515Sbryanv	if (error) {
333273515Sbryanv		device_printf(dev, "cannot allocate virtqueues\n");
334273515Sbryanv		goto fail;
335273515Sbryanv	}
336273515Sbryanv
337275273Sbryanv	if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) {
338275273Sbryanv		TASK_INIT(&sc->vtcon_ctrl_task, 0, vtcon_ctrl_task_cb, sc);
339273515Sbryanv		error = vtcon_ctrl_init(sc);
340275273Sbryanv		if (error)
341275273Sbryanv			goto fail;
342275273Sbryanv	} else {
343275273Sbryanv		error = vtcon_port_create(sc, 0);
344275273Sbryanv		if (error)
345275273Sbryanv			goto fail;
346275273Sbryanv		if (sc->vtcon_flags & VTCON_FLAG_SIZE)
347275273Sbryanv			vtcon_port_update_console_size(sc);
348275273Sbryanv	}
349273515Sbryanv
350273515Sbryanv	error = virtio_setup_intr(dev, INTR_TYPE_TTY);
351273515Sbryanv	if (error) {
352273515Sbryanv		device_printf(dev, "cannot setup virtqueue interrupts\n");
353273515Sbryanv		goto fail;
354273515Sbryanv	}
355273515Sbryanv
356273515Sbryanv	vtcon_enable_interrupts(sc);
357273515Sbryanv
358275273Sbryanv	vtcon_ctrl_send_control(sc, VIRTIO_CONSOLE_BAD_ID,
359273515Sbryanv	    VIRTIO_CONSOLE_DEVICE_READY, 1);
360273515Sbryanv
361273515Sbryanvfail:
362273515Sbryanv	if (error)
363273515Sbryanv		vtcon_detach(dev);
364273515Sbryanv
365273515Sbryanv	return (error);
366273515Sbryanv}
367273515Sbryanv
368273515Sbryanvstatic int
369273515Sbryanvvtcon_detach(device_t dev)
370273515Sbryanv{
371273515Sbryanv	struct vtcon_softc *sc;
372273515Sbryanv
373273515Sbryanv	sc = device_get_softc(dev);
374273515Sbryanv
375273515Sbryanv	VTCON_LOCK(sc);
376273515Sbryanv	sc->vtcon_flags |= VTCON_FLAG_DETACHED;
377273515Sbryanv	if (device_is_attached(dev))
378273515Sbryanv		vtcon_stop(sc);
379273515Sbryanv	VTCON_UNLOCK(sc);
380273515Sbryanv
381275273Sbryanv	if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) {
382275273Sbryanv		taskqueue_drain(taskqueue_thread, &sc->vtcon_ctrl_task);
383273515Sbryanv		vtcon_ctrl_deinit(sc);
384275273Sbryanv	}
385273515Sbryanv
386275273Sbryanv	vtcon_destroy_ports(sc);
387275273Sbryanv	mtx_destroy(&sc->vtcon_mtx);
388275273Sbryanv	mtx_destroy(&sc->vtcon_ctrl_tx_mtx);
389273515Sbryanv
390273515Sbryanv	return (0);
391273515Sbryanv}
392273515Sbryanv
393273515Sbryanvstatic int
394273515Sbryanvvtcon_config_change(device_t dev)
395273515Sbryanv{
396273515Sbryanv	struct vtcon_softc *sc;
397273515Sbryanv
398273515Sbryanv	sc = device_get_softc(dev);
399273515Sbryanv
400273515Sbryanv	/*
401275273Sbryanv	 * When the multiport feature is negotiated, all configuration
402275273Sbryanv	 * changes are done through control virtqueue events.
403273515Sbryanv	 */
404275273Sbryanv	if ((sc->vtcon_flags & VTCON_FLAG_MULTIPORT) == 0) {
405275273Sbryanv		if (sc->vtcon_flags & VTCON_FLAG_SIZE)
406275273Sbryanv			vtcon_port_update_console_size(sc);
407273515Sbryanv	}
408273515Sbryanv
409273515Sbryanv	return (0);
410273515Sbryanv}
411273515Sbryanv
412273515Sbryanvstatic void
413273515Sbryanvvtcon_negotiate_features(struct vtcon_softc *sc)
414273515Sbryanv{
415273515Sbryanv	device_t dev;
416273515Sbryanv	uint64_t features;
417273515Sbryanv
418273515Sbryanv	dev = sc->vtcon_dev;
419273515Sbryanv	features = VTCON_FEATURES;
420273515Sbryanv
421273515Sbryanv	sc->vtcon_features = virtio_negotiate_features(dev, features);
422273515Sbryanv}
423273515Sbryanv
424275273Sbryanvstatic void
425275273Sbryanvvtcon_setup_features(struct vtcon_softc *sc)
426275273Sbryanv{
427275273Sbryanv	device_t dev;
428275273Sbryanv
429275273Sbryanv	dev = sc->vtcon_dev;
430275273Sbryanv
431275273Sbryanv	vtcon_negotiate_features(sc);
432275273Sbryanv
433275273Sbryanv	if (virtio_with_feature(dev, VIRTIO_CONSOLE_F_SIZE))
434275273Sbryanv		sc->vtcon_flags |= VTCON_FLAG_SIZE;
435275273Sbryanv	if (virtio_with_feature(dev, VIRTIO_CONSOLE_F_MULTIPORT))
436275273Sbryanv		sc->vtcon_flags |= VTCON_FLAG_MULTIPORT;
437275273Sbryanv}
438275273Sbryanv
439273515Sbryanv#define VTCON_GET_CONFIG(_dev, _feature, _field, _cfg)			\
440273515Sbryanv	if (virtio_with_feature(_dev, _feature)) {			\
441273515Sbryanv		virtio_read_device_config(_dev,				\
442273515Sbryanv		    offsetof(struct virtio_console_config, _field),	\
443273515Sbryanv		    &(_cfg)->_field, sizeof((_cfg)->_field));		\
444273515Sbryanv	}
445273515Sbryanv
446273515Sbryanvstatic void
447273515Sbryanvvtcon_read_config(struct vtcon_softc *sc, struct virtio_console_config *concfg)
448273515Sbryanv{
449273515Sbryanv	device_t dev;
450273515Sbryanv
451273515Sbryanv	dev = sc->vtcon_dev;
452273515Sbryanv
453273515Sbryanv	bzero(concfg, sizeof(struct virtio_console_config));
454273515Sbryanv
455273515Sbryanv	VTCON_GET_CONFIG(dev, VIRTIO_CONSOLE_F_SIZE, cols, concfg);
456273515Sbryanv	VTCON_GET_CONFIG(dev, VIRTIO_CONSOLE_F_SIZE, rows, concfg);
457273515Sbryanv	VTCON_GET_CONFIG(dev, VIRTIO_CONSOLE_F_MULTIPORT, max_nr_ports, concfg);
458273515Sbryanv}
459273515Sbryanv
460273515Sbryanv#undef VTCON_GET_CONFIG
461273515Sbryanv
462273515Sbryanvstatic int
463275273Sbryanvvtcon_alloc_scports(struct vtcon_softc *sc)
464275273Sbryanv{
465275273Sbryanv	struct vtcon_softc_port *scport;
466275273Sbryanv	int max, i;
467275273Sbryanv
468275273Sbryanv	max = sc->vtcon_max_ports;
469275273Sbryanv
470275273Sbryanv	sc->vtcon_ports = malloc(sizeof(struct vtcon_softc_port) * max,
471275273Sbryanv	    M_DEVBUF, M_NOWAIT | M_ZERO);
472275273Sbryanv	if (sc->vtcon_ports == NULL)
473275273Sbryanv		return (ENOMEM);
474275273Sbryanv
475275273Sbryanv	for (i = 0; i < max; i++) {
476275273Sbryanv		scport = &sc->vtcon_ports[i];
477275273Sbryanv		scport->vcsp_sc = sc;
478275273Sbryanv	}
479275273Sbryanv
480275273Sbryanv	return (0);
481275273Sbryanv}
482275273Sbryanv
483275273Sbryanvstatic int
484273515Sbryanvvtcon_alloc_virtqueues(struct vtcon_softc *sc)
485273515Sbryanv{
486273515Sbryanv	device_t dev;
487273515Sbryanv	struct vq_alloc_info *info;
488275273Sbryanv	struct vtcon_softc_port *scport;
489273515Sbryanv	int i, idx, portidx, nvqs, error;
490273515Sbryanv
491273515Sbryanv	dev = sc->vtcon_dev;
492273515Sbryanv
493273515Sbryanv	nvqs = sc->vtcon_max_ports * 2;
494273515Sbryanv	if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT)
495273515Sbryanv		nvqs += 2;
496273515Sbryanv
497273515Sbryanv	info = malloc(sizeof(struct vq_alloc_info) * nvqs, M_TEMP, M_NOWAIT);
498273515Sbryanv	if (info == NULL)
499273515Sbryanv		return (ENOMEM);
500273515Sbryanv
501275273Sbryanv	for (i = 0, idx = 0, portidx = 0; i < nvqs / 2; i++, idx += 2) {
502273515Sbryanv
503273515Sbryanv		if (i == 1) {
504273515Sbryanv			/* The control virtqueues are after the first port. */
505273515Sbryanv			VQ_ALLOC_INFO_INIT(&info[idx], 0,
506275273Sbryanv			    vtcon_ctrl_event_intr, sc, &sc->vtcon_ctrl_rxvq,
507273515Sbryanv			    "%s-control rx", device_get_nameunit(dev));
508273515Sbryanv			VQ_ALLOC_INFO_INIT(&info[idx+1], 0,
509273515Sbryanv			    NULL, sc, &sc->vtcon_ctrl_txvq,
510273515Sbryanv			    "%s-control tx", device_get_nameunit(dev));
511273515Sbryanv			continue;
512273515Sbryanv		}
513273515Sbryanv
514275273Sbryanv		scport = &sc->vtcon_ports[portidx];
515273515Sbryanv
516275273Sbryanv		VQ_ALLOC_INFO_INIT(&info[idx], 0, vtcon_port_intr,
517275273Sbryanv		    scport, &scport->vcsp_invq, "%s-port%d in",
518275273Sbryanv		    device_get_nameunit(dev), i);
519273515Sbryanv		VQ_ALLOC_INFO_INIT(&info[idx+1], 0, NULL,
520275273Sbryanv		    NULL, &scport->vcsp_outvq, "%s-port%d out",
521275273Sbryanv		    device_get_nameunit(dev), i);
522273515Sbryanv
523273515Sbryanv		portidx++;
524273515Sbryanv	}
525273515Sbryanv
526273515Sbryanv	error = virtio_alloc_virtqueues(dev, 0, nvqs, info);
527273515Sbryanv	free(info, M_TEMP);
528273515Sbryanv
529273515Sbryanv	return (error);
530273515Sbryanv}
531273515Sbryanv
532273515Sbryanvstatic void
533273515Sbryanvvtcon_determine_max_ports(struct vtcon_softc *sc,
534273515Sbryanv    struct virtio_console_config *concfg)
535273515Sbryanv{
536273515Sbryanv
537273515Sbryanv	if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) {
538273515Sbryanv		sc->vtcon_max_ports =
539273515Sbryanv		    min(concfg->max_nr_ports, VTCON_MAX_PORTS);
540273515Sbryanv		if (sc->vtcon_max_ports == 0)
541273515Sbryanv			sc->vtcon_max_ports = 1;
542273515Sbryanv	} else
543273515Sbryanv		sc->vtcon_max_ports = 1;
544273515Sbryanv}
545273515Sbryanv
546273515Sbryanvstatic void
547275273Sbryanvvtcon_destroy_ports(struct vtcon_softc *sc)
548273515Sbryanv{
549275273Sbryanv	struct vtcon_softc_port *scport;
550275273Sbryanv	struct vtcon_port *port;
551275273Sbryanv	struct virtqueue *vq;
552275273Sbryanv	int i;
553273515Sbryanv
554275273Sbryanv	if (sc->vtcon_ports == NULL)
555275273Sbryanv		return;
556275273Sbryanv
557275273Sbryanv	VTCON_LOCK(sc);
558275273Sbryanv	for (i = 0; i < sc->vtcon_max_ports; i++) {
559275273Sbryanv		scport = &sc->vtcon_ports[i];
560275273Sbryanv
561275273Sbryanv		port = scport->vcsp_port;
562275273Sbryanv		if (port != NULL) {
563275273Sbryanv			scport->vcsp_port = NULL;
564275273Sbryanv			VTCON_PORT_LOCK(port);
565275273Sbryanv			VTCON_UNLOCK(sc);
566275273Sbryanv			vtcon_port_teardown(port);
567275273Sbryanv			VTCON_LOCK(sc);
568275273Sbryanv		}
569275273Sbryanv
570275273Sbryanv		vq = scport->vcsp_invq;
571275273Sbryanv		if (vq != NULL)
572275273Sbryanv			vtcon_port_drain_bufs(vq);
573273515Sbryanv	}
574275273Sbryanv	VTCON_UNLOCK(sc);
575273515Sbryanv
576275273Sbryanv	free(sc->vtcon_ports, M_DEVBUF);
577275273Sbryanv	sc->vtcon_ports = NULL;
578273515Sbryanv}
579273515Sbryanv
580273515Sbryanvstatic void
581273515Sbryanvvtcon_stop(struct vtcon_softc *sc)
582273515Sbryanv{
583273515Sbryanv
584273515Sbryanv	vtcon_disable_interrupts(sc);
585273515Sbryanv	virtio_stop(sc->vtcon_dev);
586273515Sbryanv}
587273515Sbryanv
588273515Sbryanvstatic int
589275273Sbryanvvtcon_ctrl_event_enqueue(struct vtcon_softc *sc,
590273515Sbryanv    struct virtio_console_control *control)
591273515Sbryanv{
592275273Sbryanv	struct sglist_seg segs[2];
593273515Sbryanv	struct sglist sg;
594273515Sbryanv	struct virtqueue *vq;
595275273Sbryanv	int error;
596273515Sbryanv
597273515Sbryanv	vq = sc->vtcon_ctrl_rxvq;
598273515Sbryanv
599275273Sbryanv	sglist_init(&sg, 2, segs);
600275273Sbryanv	error = sglist_append(&sg, control,
601275273Sbryanv	    sizeof(struct virtio_console_control));
602275273Sbryanv	KASSERT(error == 0, ("%s: error %d adding control to sglist",
603275273Sbryanv	    __func__, error));
604273515Sbryanv
605275273Sbryanv	return (virtqueue_enqueue(vq, control, &sg, 0, sg.sg_nseg));
606273515Sbryanv}
607273515Sbryanv
608273515Sbryanvstatic int
609275273Sbryanvvtcon_ctrl_event_create(struct vtcon_softc *sc)
610273515Sbryanv{
611273515Sbryanv	struct virtio_console_control *control;
612273515Sbryanv	int error;
613273515Sbryanv
614275273Sbryanv	control = malloc(sizeof(struct virtio_console_control), M_DEVBUF,
615275273Sbryanv	    M_ZERO | M_NOWAIT);
616273515Sbryanv	if (control == NULL)
617273515Sbryanv		return (ENOMEM);
618273515Sbryanv
619275273Sbryanv	error = vtcon_ctrl_event_enqueue(sc, control);
620273515Sbryanv	if (error)
621273515Sbryanv		free(control, M_DEVBUF);
622273515Sbryanv
623273515Sbryanv	return (error);
624273515Sbryanv}
625273515Sbryanv
626273515Sbryanvstatic void
627275273Sbryanvvtcon_ctrl_event_requeue(struct vtcon_softc *sc,
628273515Sbryanv    struct virtio_console_control *control)
629273515Sbryanv{
630273515Sbryanv	int error;
631273515Sbryanv
632275273Sbryanv	bzero(control, sizeof(struct virtio_console_control));
633273515Sbryanv
634275273Sbryanv	error = vtcon_ctrl_event_enqueue(sc, control);
635273515Sbryanv	KASSERT(error == 0,
636273515Sbryanv	    ("%s: cannot requeue control buffer %d", __func__, error));
637273515Sbryanv}
638273515Sbryanv
639273515Sbryanvstatic int
640275273Sbryanvvtcon_ctrl_event_populate(struct vtcon_softc *sc)
641273515Sbryanv{
642273515Sbryanv	struct virtqueue *vq;
643273515Sbryanv	int nbufs, error;
644273515Sbryanv
645273515Sbryanv	vq = sc->vtcon_ctrl_rxvq;
646273515Sbryanv	error = ENOSPC;
647273515Sbryanv
648273515Sbryanv	for (nbufs = 0; !virtqueue_full(vq); nbufs++) {
649275273Sbryanv		error = vtcon_ctrl_event_create(sc);
650273515Sbryanv		if (error)
651273515Sbryanv			break;
652273515Sbryanv	}
653273515Sbryanv
654273515Sbryanv	if (nbufs > 0) {
655273515Sbryanv		virtqueue_notify(vq);
656275273Sbryanv		error = 0;
657273515Sbryanv	}
658273515Sbryanv
659273515Sbryanv	return (error);
660273515Sbryanv}
661273515Sbryanv
662273515Sbryanvstatic void
663275273Sbryanvvtcon_ctrl_event_drain(struct vtcon_softc *sc)
664273515Sbryanv{
665275273Sbryanv	struct virtio_console_control *control;
666273515Sbryanv	struct virtqueue *vq;
667275273Sbryanv	int last;
668273515Sbryanv
669275273Sbryanv	vq = sc->vtcon_ctrl_rxvq;
670275273Sbryanv	last = 0;
671273515Sbryanv
672275273Sbryanv	if (vq == NULL)
673273515Sbryanv		return;
674273515Sbryanv
675275273Sbryanv	VTCON_LOCK(sc);
676275273Sbryanv	while ((control = virtqueue_drain(vq, &last)) != NULL)
677275273Sbryanv		free(control, M_DEVBUF);
678275273Sbryanv	VTCON_UNLOCK(sc);
679273515Sbryanv}
680273515Sbryanv
681273515Sbryanvstatic int
682273515Sbryanvvtcon_ctrl_init(struct vtcon_softc *sc)
683273515Sbryanv{
684273515Sbryanv	int error;
685273515Sbryanv
686275273Sbryanv	error = vtcon_ctrl_event_populate(sc);
687273515Sbryanv
688273515Sbryanv	return (error);
689273515Sbryanv}
690273515Sbryanv
691273515Sbryanvstatic void
692273515Sbryanvvtcon_ctrl_deinit(struct vtcon_softc *sc)
693273515Sbryanv{
694273515Sbryanv
695275273Sbryanv	vtcon_ctrl_event_drain(sc);
696273515Sbryanv}
697273515Sbryanv
698273515Sbryanvstatic void
699273515Sbryanvvtcon_ctrl_port_add_event(struct vtcon_softc *sc, int id)
700273515Sbryanv{
701273515Sbryanv	device_t dev;
702273515Sbryanv	int error;
703273515Sbryanv
704273515Sbryanv	dev = sc->vtcon_dev;
705273515Sbryanv
706275273Sbryanv	/* This single thread only way for ports to be created. */
707275273Sbryanv	if (sc->vtcon_ports[id].vcsp_port != NULL) {
708273515Sbryanv		device_printf(dev, "%s: adding port %d, but already exists\n",
709273515Sbryanv		    __func__, id);
710273515Sbryanv		return;
711273515Sbryanv	}
712273515Sbryanv
713275273Sbryanv	error = vtcon_port_create(sc, id);
714273515Sbryanv	if (error) {
715273515Sbryanv		device_printf(dev, "%s: cannot create port %d: %d\n",
716273515Sbryanv		    __func__, id, error);
717275273Sbryanv		vtcon_ctrl_send_control(sc, id, VIRTIO_CONSOLE_PORT_READY, 0);
718273515Sbryanv		return;
719273515Sbryanv	}
720273515Sbryanv}
721273515Sbryanv
722273515Sbryanvstatic void
723273515Sbryanvvtcon_ctrl_port_remove_event(struct vtcon_softc *sc, int id)
724273515Sbryanv{
725273515Sbryanv	device_t dev;
726275273Sbryanv	struct vtcon_softc_port *scport;
727273515Sbryanv	struct vtcon_port *port;
728273515Sbryanv
729273515Sbryanv	dev = sc->vtcon_dev;
730275273Sbryanv	scport = &sc->vtcon_ports[id];
731273515Sbryanv
732275273Sbryanv	VTCON_LOCK(sc);
733275273Sbryanv	port = scport->vcsp_port;
734273515Sbryanv	if (port == NULL) {
735275273Sbryanv		VTCON_UNLOCK(sc);
736273515Sbryanv		device_printf(dev, "%s: remove port %d, but does not exist\n",
737273515Sbryanv		    __func__, id);
738273515Sbryanv		return;
739273515Sbryanv	}
740273515Sbryanv
741275273Sbryanv	scport->vcsp_port = NULL;
742275273Sbryanv	VTCON_PORT_LOCK(port);
743275273Sbryanv	VTCON_UNLOCK(sc);
744275273Sbryanv	vtcon_port_teardown(port);
745273515Sbryanv}
746273515Sbryanv
747273515Sbryanvstatic void
748273515Sbryanvvtcon_ctrl_port_console_event(struct vtcon_softc *sc, int id)
749273515Sbryanv{
750273515Sbryanv	device_t dev;
751275273Sbryanv	struct vtcon_softc_port *scport;
752275273Sbryanv	struct vtcon_port *port;
753273515Sbryanv
754273515Sbryanv	dev = sc->vtcon_dev;
755275273Sbryanv	scport = &sc->vtcon_ports[id];
756273515Sbryanv
757275273Sbryanv	VTCON_LOCK(sc);
758275273Sbryanv	port = scport->vcsp_port;
759275273Sbryanv	if (port == NULL) {
760275273Sbryanv		VTCON_UNLOCK(sc);
761275273Sbryanv		device_printf(dev, "%s: console port %d, but does not exist\n",
762275273Sbryanv		    __func__, id);
763275273Sbryanv		return;
764275273Sbryanv	}
765275273Sbryanv
766275273Sbryanv	VTCON_PORT_LOCK(port);
767275273Sbryanv	VTCON_UNLOCK(sc);
768275273Sbryanv	port->vtcport_flags |= VTCON_PORT_FLAG_CONSOLE;
769275273Sbryanv	vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_OPEN, 1);
770275273Sbryanv	VTCON_PORT_UNLOCK(port);
771273515Sbryanv}
772273515Sbryanv
773273515Sbryanvstatic void
774273515Sbryanvvtcon_ctrl_port_open_event(struct vtcon_softc *sc, int id)
775273515Sbryanv{
776273515Sbryanv	device_t dev;
777275273Sbryanv	struct vtcon_softc_port *scport;
778273515Sbryanv	struct vtcon_port *port;
779273515Sbryanv
780273515Sbryanv	dev = sc->vtcon_dev;
781275273Sbryanv	scport = &sc->vtcon_ports[id];
782273515Sbryanv
783275273Sbryanv	VTCON_LOCK(sc);
784275273Sbryanv	port = scport->vcsp_port;
785273515Sbryanv	if (port == NULL) {
786275273Sbryanv		VTCON_UNLOCK(sc);
787273515Sbryanv		device_printf(dev, "%s: open port %d, but does not exist\n",
788273515Sbryanv		    __func__, id);
789273515Sbryanv		return;
790273515Sbryanv	}
791273515Sbryanv
792275273Sbryanv	VTCON_PORT_LOCK(port);
793275273Sbryanv	VTCON_UNLOCK(sc);
794273515Sbryanv	vtcon_port_enable_intr(port);
795275273Sbryanv	VTCON_PORT_UNLOCK(port);
796273515Sbryanv}
797273515Sbryanv
798273515Sbryanvstatic void
799275273Sbryanvvtcon_ctrl_process_event(struct vtcon_softc *sc,
800273515Sbryanv    struct virtio_console_control *control)
801273515Sbryanv{
802273515Sbryanv	device_t dev;
803273515Sbryanv	int id;
804273515Sbryanv
805273515Sbryanv	dev = sc->vtcon_dev;
806273515Sbryanv	id = control->id;
807273515Sbryanv
808273515Sbryanv	if (id < 0 || id >= sc->vtcon_max_ports) {
809273515Sbryanv		device_printf(dev, "%s: invalid port ID %d\n", __func__, id);
810273515Sbryanv		return;
811273515Sbryanv	}
812273515Sbryanv
813273515Sbryanv	switch (control->event) {
814273515Sbryanv	case VIRTIO_CONSOLE_PORT_ADD:
815273515Sbryanv		vtcon_ctrl_port_add_event(sc, id);
816273515Sbryanv		break;
817273515Sbryanv
818273515Sbryanv	case VIRTIO_CONSOLE_PORT_REMOVE:
819273515Sbryanv		vtcon_ctrl_port_remove_event(sc, id);
820273515Sbryanv		break;
821273515Sbryanv
822273515Sbryanv	case VIRTIO_CONSOLE_CONSOLE_PORT:
823273515Sbryanv		vtcon_ctrl_port_console_event(sc, id);
824273515Sbryanv		break;
825273515Sbryanv
826273515Sbryanv	case VIRTIO_CONSOLE_RESIZE:
827273515Sbryanv		break;
828273515Sbryanv
829273515Sbryanv	case VIRTIO_CONSOLE_PORT_OPEN:
830273515Sbryanv		vtcon_ctrl_port_open_event(sc, id);
831273515Sbryanv		break;
832273515Sbryanv
833273515Sbryanv	case VIRTIO_CONSOLE_PORT_NAME:
834273515Sbryanv		break;
835273515Sbryanv	}
836273515Sbryanv}
837273515Sbryanv
838273515Sbryanvstatic void
839273515Sbryanvvtcon_ctrl_task_cb(void *xsc, int pending)
840273515Sbryanv{
841273515Sbryanv	struct vtcon_softc *sc;
842273515Sbryanv	struct virtqueue *vq;
843273515Sbryanv	struct virtio_console_control *control;
844275273Sbryanv	int detached;
845273515Sbryanv
846273515Sbryanv	sc = xsc;
847273515Sbryanv	vq = sc->vtcon_ctrl_rxvq;
848273515Sbryanv
849273515Sbryanv	VTCON_LOCK(sc);
850275273Sbryanv
851275273Sbryanv	while ((detached = (sc->vtcon_flags & VTCON_FLAG_DETACHED)) == 0) {
852273515Sbryanv		control = virtqueue_dequeue(vq, NULL);
853273515Sbryanv		if (control == NULL)
854273515Sbryanv			break;
855273515Sbryanv
856273515Sbryanv		VTCON_UNLOCK(sc);
857275273Sbryanv		vtcon_ctrl_process_event(sc, control);
858273515Sbryanv		VTCON_LOCK(sc);
859275273Sbryanv		vtcon_ctrl_event_requeue(sc, control);
860273515Sbryanv	}
861275273Sbryanv
862275273Sbryanv	if (!detached) {
863275273Sbryanv		virtqueue_notify(vq);
864275273Sbryanv		if (virtqueue_enable_intr(vq) != 0)
865275273Sbryanv			taskqueue_enqueue(taskqueue_thread,
866275273Sbryanv			    &sc->vtcon_ctrl_task);
867275273Sbryanv	}
868275273Sbryanv
869273515Sbryanv	VTCON_UNLOCK(sc);
870275273Sbryanv}
871273515Sbryanv
872275273Sbryanvstatic void
873275273Sbryanvvtcon_ctrl_event_intr(void *xsc)
874275273Sbryanv{
875275273Sbryanv	struct vtcon_softc *sc;
876275273Sbryanv
877275273Sbryanv	sc = xsc;
878275273Sbryanv
879275273Sbryanv	/*
880275273Sbryanv	 * Only some events require us to potentially block, but it
881275273Sbryanv	 * easier to just defer all event handling to the taskqueue.
882275273Sbryanv	 */
883275273Sbryanv	taskqueue_enqueue(taskqueue_thread, &sc->vtcon_ctrl_task);
884273515Sbryanv}
885273515Sbryanv
886275273Sbryanvstatic void
887275273Sbryanvvtcon_ctrl_poll(struct vtcon_softc *sc,
888275273Sbryanv    struct virtio_console_control *control)
889275273Sbryanv{
890275273Sbryanv	struct sglist_seg segs[2];
891275273Sbryanv	struct sglist sg;
892275273Sbryanv	struct virtqueue *vq;
893275273Sbryanv	int error;
894275273Sbryanv
895275273Sbryanv	vq = sc->vtcon_ctrl_txvq;
896275273Sbryanv
897275273Sbryanv	sglist_init(&sg, 2, segs);
898275273Sbryanv	error = sglist_append(&sg, control,
899275273Sbryanv	    sizeof(struct virtio_console_control));
900275273Sbryanv	KASSERT(error == 0, ("%s: error %d adding control to sglist",
901275273Sbryanv	    __func__, error));
902275273Sbryanv
903275273Sbryanv	/*
904275273Sbryanv	 * We cannot use the softc lock to serialize access to this
905275273Sbryanv	 * virtqueue since this is called from the tty layer with the
906275273Sbryanv	 * port lock held. Acquiring the softc would violate our lock
907275273Sbryanv	 * ordering.
908275273Sbryanv	 */
909275273Sbryanv	VTCON_CTRL_TX_LOCK(sc);
910275273Sbryanv	KASSERT(virtqueue_empty(vq),
911275273Sbryanv	    ("%s: virtqueue is not emtpy", __func__));
912275273Sbryanv	error = virtqueue_enqueue(vq, control, &sg, sg.sg_nseg, 0);
913275273Sbryanv	if (error == 0) {
914275273Sbryanv		virtqueue_notify(vq);
915275273Sbryanv		virtqueue_poll(vq, NULL);
916275273Sbryanv	}
917275273Sbryanv	VTCON_CTRL_TX_UNLOCK(sc);
918275273Sbryanv}
919275273Sbryanv
920275273Sbryanvstatic void
921275273Sbryanvvtcon_ctrl_send_control(struct vtcon_softc *sc, uint32_t portid,
922275273Sbryanv    uint16_t event, uint16_t value)
923275273Sbryanv{
924275273Sbryanv	struct virtio_console_control control;
925275273Sbryanv
926275273Sbryanv	if ((sc->vtcon_flags & VTCON_FLAG_MULTIPORT) == 0)
927275273Sbryanv		return;
928275273Sbryanv
929275273Sbryanv	control.id = portid;
930275273Sbryanv	control.event = event;
931275273Sbryanv	control.value = value;
932275273Sbryanv
933275273Sbryanv	vtcon_ctrl_poll(sc, &control);
934275273Sbryanv}
935275273Sbryanv
936273515Sbryanvstatic int
937275273Sbryanvvtcon_port_enqueue_buf(struct vtcon_port *port, void *buf, size_t len)
938273515Sbryanv{
939275273Sbryanv	struct sglist_seg segs[2];
940273515Sbryanv	struct sglist sg;
941273515Sbryanv	struct virtqueue *vq;
942273515Sbryanv	int error;
943273515Sbryanv
944273515Sbryanv	vq = port->vtcport_invq;
945273515Sbryanv
946275273Sbryanv	sglist_init(&sg, 2, segs);
947273515Sbryanv	error = sglist_append(&sg, buf, len);
948275273Sbryanv	KASSERT(error == 0,
949273515Sbryanv	    ("%s: error %d adding buffer to sglist", __func__, error));
950273515Sbryanv
951275273Sbryanv	error = virtqueue_enqueue(vq, buf, &sg, 0, sg.sg_nseg);
952275273Sbryanv
953275273Sbryanv	return (error);
954273515Sbryanv}
955273515Sbryanv
956273515Sbryanvstatic int
957275273Sbryanvvtcon_port_create_buf(struct vtcon_port *port)
958273515Sbryanv{
959273515Sbryanv	void *buf;
960273515Sbryanv	int error;
961273515Sbryanv
962273515Sbryanv	buf = malloc(VTCON_BULK_BUFSZ, M_DEVBUF, M_ZERO | M_NOWAIT);
963273515Sbryanv	if (buf == NULL)
964273515Sbryanv		return (ENOMEM);
965273515Sbryanv
966275273Sbryanv	error = vtcon_port_enqueue_buf(port, buf, VTCON_BULK_BUFSZ);
967273515Sbryanv	if (error)
968273515Sbryanv		free(buf, M_DEVBUF);
969273515Sbryanv
970273515Sbryanv	return (error);
971273515Sbryanv}
972273515Sbryanv
973273515Sbryanvstatic void
974275273Sbryanvvtcon_port_requeue_buf(struct vtcon_port *port, void *buf)
975273515Sbryanv{
976275273Sbryanv	int error;
977273515Sbryanv
978275273Sbryanv	error = vtcon_port_enqueue_buf(port, buf, VTCON_BULK_BUFSZ);
979273515Sbryanv	KASSERT(error == 0,
980273515Sbryanv	    ("%s: cannot requeue input buffer %d", __func__, error));
981273515Sbryanv}
982273515Sbryanv
983273515Sbryanvstatic int
984273515Sbryanvvtcon_port_populate(struct vtcon_port *port)
985273515Sbryanv{
986273515Sbryanv	struct virtqueue *vq;
987273515Sbryanv	int nbufs, error;
988273515Sbryanv
989273515Sbryanv	vq = port->vtcport_invq;
990273515Sbryanv	error = ENOSPC;
991273515Sbryanv
992273515Sbryanv	for (nbufs = 0; !virtqueue_full(vq); nbufs++) {
993275273Sbryanv		error = vtcon_port_create_buf(port);
994273515Sbryanv		if (error)
995273515Sbryanv			break;
996273515Sbryanv	}
997273515Sbryanv
998273515Sbryanv	if (nbufs > 0) {
999273515Sbryanv		virtqueue_notify(vq);
1000275273Sbryanv		error = 0;
1001273515Sbryanv	}
1002273515Sbryanv
1003273515Sbryanv	return (error);
1004273515Sbryanv}
1005273515Sbryanv
1006273515Sbryanvstatic void
1007273515Sbryanvvtcon_port_destroy(struct vtcon_port *port)
1008273515Sbryanv{
1009273515Sbryanv
1010273515Sbryanv	port->vtcport_sc = NULL;
1011275273Sbryanv	port->vtcport_scport = NULL;
1012275273Sbryanv	port->vtcport_invq = NULL;
1013275273Sbryanv	port->vtcport_outvq = NULL;
1014273515Sbryanv	port->vtcport_id = -1;
1015275273Sbryanv	mtx_destroy(&port->vtcport_mtx);
1016273515Sbryanv	free(port, M_DEVBUF);
1017273515Sbryanv}
1018273515Sbryanv
1019273515Sbryanvstatic int
1020275273Sbryanvvtcon_port_init_vqs(struct vtcon_port *port)
1021273515Sbryanv{
1022275273Sbryanv	struct vtcon_softc_port *scport;
1023275273Sbryanv	int error;
1024275273Sbryanv
1025275273Sbryanv	scport = port->vtcport_scport;
1026275273Sbryanv
1027275273Sbryanv	port->vtcport_invq = scport->vcsp_invq;
1028275273Sbryanv	port->vtcport_outvq = scport->vcsp_outvq;
1029275273Sbryanv
1030275273Sbryanv	/*
1031275273Sbryanv	 * Free any data left over from when this virtqueue was in use by a
1032275273Sbryanv	 * prior port. We have not yet notified the host that the port is
1033275273Sbryanv	 * ready, so assume nothing in the virtqueue can be for us.
1034275273Sbryanv	 */
1035275273Sbryanv	vtcon_port_drain(port);
1036275273Sbryanv
1037275273Sbryanv	KASSERT(virtqueue_empty(port->vtcport_invq),
1038275273Sbryanv	    ("%s: in virtqueue is not empty", __func__));
1039275273Sbryanv	KASSERT(virtqueue_empty(port->vtcport_outvq),
1040275273Sbryanv	    ("%s: out virtqueue is not empty", __func__));
1041275273Sbryanv
1042275273Sbryanv	error = vtcon_port_populate(port);
1043275273Sbryanv	if (error)
1044275273Sbryanv		return (error);
1045275273Sbryanv
1046275273Sbryanv	return (0);
1047275273Sbryanv}
1048275273Sbryanv
1049275273Sbryanvstatic int
1050275273Sbryanvvtcon_port_create(struct vtcon_softc *sc, int id)
1051275273Sbryanv{
1052273515Sbryanv	device_t dev;
1053275273Sbryanv	struct vtcon_softc_port *scport;
1054273515Sbryanv	struct vtcon_port *port;
1055273515Sbryanv	int error;
1056273515Sbryanv
1057273515Sbryanv	dev = sc->vtcon_dev;
1058275273Sbryanv	scport = &sc->vtcon_ports[id];
1059273515Sbryanv
1060275273Sbryanv	VTCON_ASSERT_VALID_PORTID(sc, id);
1061275273Sbryanv	MPASS(scport->vcsp_port == NULL);
1062273515Sbryanv
1063273515Sbryanv	port = malloc(sizeof(struct vtcon_port), M_DEVBUF, M_NOWAIT | M_ZERO);
1064273515Sbryanv	if (port == NULL)
1065273515Sbryanv		return (ENOMEM);
1066273515Sbryanv
1067273515Sbryanv	port->vtcport_sc = sc;
1068275273Sbryanv	port->vtcport_scport = scport;
1069273515Sbryanv	port->vtcport_id = id;
1070275273Sbryanv	mtx_init(&port->vtcport_mtx, "vtcpmtx", NULL, MTX_DEF);
1071273515Sbryanv	port->vtcport_tty = tty_alloc_mutex(&vtcon_tty_class, port,
1072273515Sbryanv	    &port->vtcport_mtx);
1073273515Sbryanv
1074275273Sbryanv	error = vtcon_port_init_vqs(port);
1075273515Sbryanv	if (error) {
1076275273Sbryanv		VTCON_PORT_LOCK(port);
1077275273Sbryanv		vtcon_port_teardown(port);
1078273515Sbryanv		return (error);
1079273515Sbryanv	}
1080273515Sbryanv
1081273515Sbryanv	VTCON_LOCK(sc);
1082275273Sbryanv	VTCON_PORT_LOCK(port);
1083275273Sbryanv	scport->vcsp_port = port;
1084275273Sbryanv	vtcon_port_enable_intr(port);
1085275273Sbryanv	vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_READY, 1);
1086275273Sbryanv	VTCON_PORT_UNLOCK(port);
1087273515Sbryanv	VTCON_UNLOCK(sc);
1088273515Sbryanv
1089275273Sbryanv	tty_makedev(port->vtcport_tty, NULL, "%s%r.%r", VTCON_TTY_PREFIX,
1090275273Sbryanv	    device_get_unit(dev), id);
1091273515Sbryanv
1092273515Sbryanv	return (0);
1093273515Sbryanv}
1094273515Sbryanv
1095273515Sbryanvstatic void
1096275273Sbryanvvtcon_port_drain_bufs(struct virtqueue *vq)
1097273515Sbryanv{
1098273515Sbryanv	void *buf;
1099273515Sbryanv	int last;
1100273515Sbryanv
1101273515Sbryanv	last = 0;
1102273515Sbryanv
1103273515Sbryanv	while ((buf = virtqueue_drain(vq, &last)) != NULL)
1104273515Sbryanv		free(buf, M_DEVBUF);
1105273515Sbryanv}
1106273515Sbryanv
1107273515Sbryanvstatic void
1108275273Sbryanvvtcon_port_drain(struct vtcon_port *port)
1109273515Sbryanv{
1110275273Sbryanv
1111275273Sbryanv	vtcon_port_drain_bufs(port->vtcport_invq);
1112275273Sbryanv}
1113275273Sbryanv
1114275273Sbryanvstatic void
1115275273Sbryanvvtcon_port_teardown(struct vtcon_port *port)
1116275273Sbryanv{
1117273515Sbryanv	struct tty *tp;
1118273515Sbryanv
1119273515Sbryanv	tp = port->vtcport_tty;
1120273515Sbryanv
1121275273Sbryanv	port->vtcport_flags |= VTCON_PORT_FLAG_GONE;
1122273515Sbryanv
1123273515Sbryanv	if (tp != NULL) {
1124273515Sbryanv		atomic_add_int(&vtcon_pending_free, 1);
1125273515Sbryanv		tty_rel_gone(tp);
1126273515Sbryanv	} else
1127273515Sbryanv		vtcon_port_destroy(port);
1128273515Sbryanv}
1129273515Sbryanv
1130273515Sbryanvstatic void
1131273515Sbryanvvtcon_port_change_size(struct vtcon_port *port, uint16_t cols, uint16_t rows)
1132273515Sbryanv{
1133273515Sbryanv	struct tty *tp;
1134273515Sbryanv	struct winsize sz;
1135273515Sbryanv
1136273515Sbryanv	tp = port->vtcport_tty;
1137273515Sbryanv
1138273515Sbryanv	if (tp == NULL)
1139273515Sbryanv		return;
1140273515Sbryanv
1141273515Sbryanv	bzero(&sz, sizeof(struct winsize));
1142273515Sbryanv	sz.ws_col = cols;
1143273515Sbryanv	sz.ws_row = rows;
1144273515Sbryanv
1145273515Sbryanv	tty_set_winsize(tp, &sz);
1146273515Sbryanv}
1147273515Sbryanv
1148273515Sbryanvstatic void
1149275273Sbryanvvtcon_port_update_console_size(struct vtcon_softc *sc)
1150275273Sbryanv{
1151275273Sbryanv	struct vtcon_port *port;
1152275273Sbryanv	struct vtcon_softc_port *scport;
1153275273Sbryanv	uint16_t cols, rows;
1154275273Sbryanv
1155275273Sbryanv	vtcon_get_console_size(sc, &cols, &rows);
1156275273Sbryanv
1157275273Sbryanv	/*
1158275273Sbryanv	 * For now, assume the first (only) port is the console. Note
1159275273Sbryanv	 * QEMU does not implement this feature yet.
1160275273Sbryanv	 */
1161275273Sbryanv	scport = &sc->vtcon_ports[0];
1162275273Sbryanv
1163275273Sbryanv	VTCON_LOCK(sc);
1164275273Sbryanv	port = scport->vcsp_port;
1165275273Sbryanv
1166275273Sbryanv	if (port != NULL) {
1167275273Sbryanv		VTCON_PORT_LOCK(port);
1168275273Sbryanv		VTCON_UNLOCK(sc);
1169275273Sbryanv		vtcon_port_change_size(port, cols, rows);
1170275273Sbryanv		VTCON_PORT_UNLOCK(port);
1171275273Sbryanv	} else
1172275273Sbryanv		VTCON_UNLOCK(sc);
1173275273Sbryanv}
1174275273Sbryanv
1175275273Sbryanvstatic void
1176273515Sbryanvvtcon_port_enable_intr(struct vtcon_port *port)
1177273515Sbryanv{
1178273515Sbryanv
1179273515Sbryanv	/*
1180275273Sbryanv	 * NOTE: The out virtqueue is always polled, so its interupt
1181275273Sbryanv	 * kept disabled.
1182273515Sbryanv	 */
1183273515Sbryanv	virtqueue_enable_intr(port->vtcport_invq);
1184273515Sbryanv}
1185273515Sbryanv
1186273515Sbryanvstatic void
1187273515Sbryanvvtcon_port_disable_intr(struct vtcon_port *port)
1188273515Sbryanv{
1189273515Sbryanv
1190273515Sbryanv	if (port->vtcport_invq != NULL)
1191273515Sbryanv		virtqueue_disable_intr(port->vtcport_invq);
1192273515Sbryanv	if (port->vtcport_outvq != NULL)
1193273515Sbryanv		virtqueue_disable_intr(port->vtcport_outvq);
1194273515Sbryanv}
1195273515Sbryanv
1196273515Sbryanvstatic void
1197275273Sbryanvvtcon_port_in(struct vtcon_port *port)
1198273515Sbryanv{
1199275273Sbryanv	struct virtqueue *vq;
1200273515Sbryanv	struct tty *tp;
1201273515Sbryanv	char *buf;
1202273515Sbryanv	uint32_t len;
1203273515Sbryanv	int i, deq;
1204273515Sbryanv
1205273515Sbryanv	tp = port->vtcport_tty;
1206273515Sbryanv	vq = port->vtcport_invq;
1207273515Sbryanv
1208273515Sbryanvagain:
1209273515Sbryanv	deq = 0;
1210273515Sbryanv
1211273515Sbryanv	while ((buf = virtqueue_dequeue(vq, &len)) != NULL) {
1212275273Sbryanv		for (i = 0; i < len; i++) {
1213275273Sbryanv#if defined(KDB)
1214275273Sbryanv			if (port->vtcport_flags & VTCON_PORT_FLAG_CONSOLE)
1215275273Sbryanv				kdb_alt_break(buf[i],
1216275273Sbryanv				    &port->vtcport_alt_break_state);
1217275273Sbryanv#endif
1218275273Sbryanv			ttydisc_rint(tp, buf[i], 0);
1219275273Sbryanv		}
1220275273Sbryanv		vtcon_port_requeue_buf(port, buf);
1221273515Sbryanv		deq++;
1222273515Sbryanv	}
1223273515Sbryanv	ttydisc_rint_done(tp);
1224273515Sbryanv
1225273515Sbryanv	if (deq > 0)
1226273515Sbryanv		virtqueue_notify(vq);
1227273515Sbryanv
1228273515Sbryanv	if (virtqueue_enable_intr(vq) != 0)
1229273515Sbryanv		goto again;
1230273515Sbryanv}
1231273515Sbryanv
1232273515Sbryanvstatic void
1233275273Sbryanvvtcon_port_intr(void *scportx)
1234273515Sbryanv{
1235275273Sbryanv	struct vtcon_softc_port *scport;
1236275273Sbryanv	struct vtcon_softc *sc;
1237273515Sbryanv	struct vtcon_port *port;
1238273515Sbryanv
1239275273Sbryanv	scport = scportx;
1240275273Sbryanv	sc = scport->vcsp_sc;
1241273515Sbryanv
1242275273Sbryanv	VTCON_LOCK(sc);
1243275273Sbryanv	port = scport->vcsp_port;
1244275273Sbryanv	if (port == NULL) {
1245275273Sbryanv		VTCON_UNLOCK(sc);
1246275273Sbryanv		return;
1247275273Sbryanv	}
1248275273Sbryanv	VTCON_PORT_LOCK(port);
1249275273Sbryanv	VTCON_UNLOCK(sc);
1250275273Sbryanv	if ((port->vtcport_flags & VTCON_PORT_FLAG_GONE) == 0)
1251275273Sbryanv		vtcon_port_in(port);
1252275273Sbryanv	VTCON_PORT_UNLOCK(port);
1253273515Sbryanv}
1254273515Sbryanv
1255273515Sbryanvstatic void
1256275273Sbryanvvtcon_port_out(struct vtcon_port *port, void *buf, int bufsize)
1257273515Sbryanv{
1258275273Sbryanv	struct sglist_seg segs[2];
1259273515Sbryanv	struct sglist sg;
1260273515Sbryanv	struct virtqueue *vq;
1261273515Sbryanv	int error;
1262273515Sbryanv
1263273515Sbryanv	vq = port->vtcport_outvq;
1264275273Sbryanv	KASSERT(virtqueue_empty(vq),
1265275273Sbryanv	    ("%s: port %p out virtqueue not emtpy", __func__, port));
1266273515Sbryanv
1267275273Sbryanv	sglist_init(&sg, 2, segs);
1268273515Sbryanv	error = sglist_append(&sg, buf, bufsize);
1269275273Sbryanv	KASSERT(error == 0, ("%s: error %d adding buffer to sglist",
1270275273Sbryanv	    __func__, error));
1271273515Sbryanv
1272275273Sbryanv	error = virtqueue_enqueue(vq, buf, &sg, sg.sg_nseg, 0);
1273275273Sbryanv	if (error == 0) {
1274273515Sbryanv		virtqueue_notify(vq);
1275273515Sbryanv		virtqueue_poll(vq, NULL);
1276273515Sbryanv	}
1277273515Sbryanv}
1278273515Sbryanv
1279273515Sbryanvstatic void
1280275273Sbryanvvtcon_port_submit_event(struct vtcon_port *port, uint16_t event,
1281273515Sbryanv    uint16_t value)
1282273515Sbryanv{
1283273515Sbryanv	struct vtcon_softc *sc;
1284273515Sbryanv
1285273515Sbryanv	sc = port->vtcport_sc;
1286273515Sbryanv
1287275273Sbryanv	vtcon_ctrl_send_control(sc, port->vtcport_id, event, value);
1288273515Sbryanv}
1289273515Sbryanv
1290273515Sbryanvstatic int
1291273515Sbryanvvtcon_tty_open(struct tty *tp)
1292273515Sbryanv{
1293273515Sbryanv	struct vtcon_port *port;
1294273515Sbryanv
1295273515Sbryanv	port = tty_softc(tp);
1296273515Sbryanv
1297275273Sbryanv	if (port->vtcport_flags & VTCON_PORT_FLAG_GONE)
1298275273Sbryanv		return (ENXIO);
1299273515Sbryanv
1300275273Sbryanv	vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_OPEN, 1);
1301275273Sbryanv
1302273515Sbryanv	return (0);
1303273515Sbryanv}
1304273515Sbryanv
1305273515Sbryanvstatic void
1306273515Sbryanvvtcon_tty_close(struct tty *tp)
1307273515Sbryanv{
1308273515Sbryanv	struct vtcon_port *port;
1309273515Sbryanv
1310273515Sbryanv	port = tty_softc(tp);
1311273515Sbryanv
1312275273Sbryanv	if (port->vtcport_flags & VTCON_PORT_FLAG_GONE)
1313275273Sbryanv		return;
1314275273Sbryanv
1315275273Sbryanv	vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_OPEN, 0);
1316273515Sbryanv}
1317273515Sbryanv
1318273515Sbryanvstatic void
1319273515Sbryanvvtcon_tty_outwakeup(struct tty *tp)
1320273515Sbryanv{
1321273515Sbryanv	struct vtcon_port *port;
1322273515Sbryanv	char buf[VTCON_BULK_BUFSZ];
1323273515Sbryanv	int len;
1324273515Sbryanv
1325273515Sbryanv	port = tty_softc(tp);
1326273515Sbryanv
1327275273Sbryanv	if (port->vtcport_flags & VTCON_PORT_FLAG_GONE)
1328275273Sbryanv		return;
1329275273Sbryanv
1330273515Sbryanv	while ((len = ttydisc_getc(tp, buf, sizeof(buf))) != 0)
1331275273Sbryanv		vtcon_port_out(port, buf, len);
1332273515Sbryanv}
1333273515Sbryanv
1334273515Sbryanvstatic void
1335273515Sbryanvvtcon_tty_free(void *xport)
1336273515Sbryanv{
1337273515Sbryanv	struct vtcon_port *port;
1338273515Sbryanv
1339273515Sbryanv	port = xport;
1340273515Sbryanv
1341273515Sbryanv	vtcon_port_destroy(port);
1342273515Sbryanv	atomic_subtract_int(&vtcon_pending_free, 1);
1343273515Sbryanv}
1344273515Sbryanv
1345273515Sbryanvstatic void
1346273515Sbryanvvtcon_get_console_size(struct vtcon_softc *sc, uint16_t *cols, uint16_t *rows)
1347273515Sbryanv{
1348273515Sbryanv	struct virtio_console_config concfg;
1349273515Sbryanv
1350273515Sbryanv	KASSERT(sc->vtcon_flags & VTCON_FLAG_SIZE,
1351273515Sbryanv	    ("%s: size feature not negotiated", __func__));
1352273515Sbryanv
1353273515Sbryanv	vtcon_read_config(sc, &concfg);
1354273515Sbryanv
1355273515Sbryanv	*cols = concfg.cols;
1356273515Sbryanv	*rows = concfg.rows;
1357273515Sbryanv}
1358273515Sbryanv
1359273515Sbryanvstatic void
1360273515Sbryanvvtcon_enable_interrupts(struct vtcon_softc *sc)
1361273515Sbryanv{
1362275273Sbryanv	struct vtcon_softc_port *scport;
1363273515Sbryanv	struct vtcon_port *port;
1364275273Sbryanv	int i;
1365273515Sbryanv
1366275273Sbryanv	VTCON_LOCK(sc);
1367275273Sbryanv
1368273515Sbryanv	if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT)
1369273515Sbryanv		virtqueue_enable_intr(sc->vtcon_ctrl_rxvq);
1370273515Sbryanv
1371275273Sbryanv	for (i = 0; i < sc->vtcon_max_ports; i++) {
1372275273Sbryanv		scport = &sc->vtcon_ports[i];
1373275273Sbryanv
1374275273Sbryanv		port = scport->vcsp_port;
1375275273Sbryanv		if (port == NULL)
1376275273Sbryanv			continue;
1377275273Sbryanv
1378275273Sbryanv		VTCON_PORT_LOCK(port);
1379273515Sbryanv		vtcon_port_enable_intr(port);
1380275273Sbryanv		VTCON_PORT_UNLOCK(port);
1381275273Sbryanv	}
1382275273Sbryanv
1383275273Sbryanv	VTCON_UNLOCK(sc);
1384273515Sbryanv}
1385273515Sbryanv
1386273515Sbryanvstatic void
1387273515Sbryanvvtcon_disable_interrupts(struct vtcon_softc *sc)
1388273515Sbryanv{
1389275273Sbryanv	struct vtcon_softc_port *scport;
1390273515Sbryanv	struct vtcon_port *port;
1391275273Sbryanv	int i;
1392273515Sbryanv
1393275273Sbryanv	VTCON_LOCK_ASSERT(sc);
1394275273Sbryanv
1395273515Sbryanv	if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT)
1396273515Sbryanv		virtqueue_disable_intr(sc->vtcon_ctrl_rxvq);
1397273515Sbryanv
1398275273Sbryanv	for (i = 0; i < sc->vtcon_max_ports; i++) {
1399275273Sbryanv		scport = &sc->vtcon_ports[i];
1400275273Sbryanv
1401275273Sbryanv		port = scport->vcsp_port;
1402275273Sbryanv		if (port == NULL)
1403275273Sbryanv			continue;
1404275273Sbryanv
1405275273Sbryanv		VTCON_PORT_LOCK(port);
1406273515Sbryanv		vtcon_port_disable_intr(port);
1407275273Sbryanv		VTCON_PORT_UNLOCK(port);
1408275273Sbryanv	}
1409273515Sbryanv}
1410