virtio_console.c revision 273515
1/*-
2 * Copyright (c) 2014, Bryan Venteicher <bryanv@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice unmodified, this list of conditions, and the following
10 *    disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 *    notice, this list of conditions and the following disclaimer in the
13 *    documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
20 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
21 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
22 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
24 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27/* Driver for VirtIO console devices. */
28
29#include <sys/cdefs.h>
30__FBSDID("$FreeBSD: head/sys/dev/virtio/console/virtio_console.c 273515 2014-10-23 04:47:32Z bryanv $");
31
32#include <sys/param.h>
33#include <sys/systm.h>
34#include <sys/kernel.h>
35#include <sys/malloc.h>
36#include <sys/module.h>
37#include <sys/lock.h>
38#include <sys/mutex.h>
39#include <sys/sglist.h>
40#include <sys/sysctl.h>
41#include <sys/taskqueue.h>
42#include <sys/queue.h>
43
44#include <sys/conf.h>
45#include <sys/cons.h>
46#include <sys/tty.h>
47
48#include <machine/bus.h>
49#include <machine/resource.h>
50#include <sys/bus.h>
51
52#include <dev/virtio/virtio.h>
53#include <dev/virtio/virtqueue.h>
54#include <dev/virtio/console/virtio_console.h>
55
56#include "virtio_if.h"
57
58#define VTCON_MAX_PORTS	1
59#define VTCON_TTY_PREFIX "V"
60#define VTCON_BULK_BUFSZ 128
61
62struct vtcon_softc;
63
64struct vtcon_port {
65	struct vtcon_softc	*vtcport_sc;
66	TAILQ_ENTRY(vtcon_port)  vtcport_next;
67	struct mtx		 vtcport_mtx;
68	int			 vtcport_id;
69	struct tty		*vtcport_tty;
70	struct virtqueue	*vtcport_invq;
71	struct virtqueue	*vtcport_outvq;
72	char			 vtcport_name[16];
73};
74
75#define VTCON_PORT_MTX(_port)		&(_port)->vtcport_mtx
76#define VTCON_PORT_LOCK_INIT(_port) \
77    mtx_init(VTCON_PORT_MTX((_port)), (_port)->vtcport_name, NULL, MTX_DEF)
78#define VTCON_PORT_LOCK(_port)		mtx_lock(VTCON_PORT_MTX((_port)))
79#define VTCON_PORT_UNLOCK(_port)	mtx_unlock(VTCON_PORT_MTX((_port)))
80#define VTCON_PORT_LOCK_DESTROY(_port)	mtx_destroy(VTCON_PORT_MTX((_port)))
81#define VTCON_PORT_LOCK_ASSERT(_port) \
82    mtx_assert(VTCON_PORT_MTX((_port)), MA_OWNED)
83#define VTCON_PORT_LOCK_ASSERT_NOTOWNED(_port) \
84    mtx_assert(VTCON_PORT_MTX((_port)), MA_NOTOWNED)
85
86struct vtcon_softc {
87	device_t		 vtcon_dev;
88	struct mtx		 vtcon_mtx;
89	uint64_t		 vtcon_features;
90	uint32_t		 vtcon_flags;
91#define VTCON_FLAG_DETACHED	0x0001
92#define VTCON_FLAG_SIZE		0x0010
93#define VTCON_FLAG_MULTIPORT	0x0020
94
95	struct task		 vtcon_ctrl_task;
96	struct virtqueue	*vtcon_ctrl_rxvq;
97	struct virtqueue	*vtcon_ctrl_txvq;
98
99	uint32_t		 vtcon_max_ports;
100	TAILQ_HEAD(, vtcon_port)
101				 vtcon_ports;
102
103	/*
104	 * Ports can be added and removed during runtime, but we have
105	 * to allocate all the virtqueues during attach. This array is
106	 * indexed by the port ID.
107	 */
108	struct vtcon_port_extra {
109		struct vtcon_port	*port;
110		struct virtqueue	*invq;
111		struct virtqueue	*outvq;
112	}			*vtcon_portsx;
113};
114
115#define VTCON_MTX(_sc)		&(_sc)->vtcon_mtx
116#define VTCON_LOCK_INIT(_sc, _name) \
117    mtx_init(VTCON_MTX((_sc)), (_name), NULL, MTX_DEF)
118#define VTCON_LOCK(_sc)		mtx_lock(VTCON_MTX((_sc)))
119#define VTCON_UNLOCK(_sc)	mtx_unlock(VTCON_MTX((_sc)))
120#define VTCON_LOCK_DESTROY(_sc)	mtx_destroy(VTCON_MTX((_sc)))
121#define VTCON_LOCK_ASSERT(_sc)	mtx_assert(VTCON_MTX((_sc)), MA_OWNED)
122#define VTCON_LOCK_ASSERT_NOTOWNED(_sc) \
123    mtx_assert(VTCON_MTX((_sc)), MA_NOTOWNED)
124
125#define VTCON_ASSERT_VALID_PORTID(_sc, _id)			\
126    KASSERT((_id) >= 0 && (_id) < (_sc)->vtcon_max_ports,	\
127        ("%s: port ID %d out of range", __func__, _id))
128
129#define VTCON_FEATURES  0
130
131static struct virtio_feature_desc vtcon_feature_desc[] = {
132	{ VIRTIO_CONSOLE_F_SIZE,	"ConsoleSize"	},
133	{ VIRTIO_CONSOLE_F_MULTIPORT,	"MultiplePorts"	},
134
135	{ 0, NULL }
136};
137
138static int	 vtcon_modevent(module_t, int, void *);
139
140static int	 vtcon_probe(device_t);
141static int	 vtcon_attach(device_t);
142static int	 vtcon_detach(device_t);
143static int	 vtcon_config_change(device_t);
144
145static void	 vtcon_negotiate_features(struct vtcon_softc *);
146static int	 vtcon_alloc_virtqueues(struct vtcon_softc *);
147static void	 vtcon_read_config(struct vtcon_softc *,
148		     struct virtio_console_config *);
149
150static void	 vtcon_determine_max_ports(struct vtcon_softc *,
151		     struct virtio_console_config *);
152static void	 vtcon_deinit_ports(struct vtcon_softc *);
153static void	 vtcon_stop(struct vtcon_softc *);
154
155static void	 vtcon_ctrl_rx_vq_intr(void *);
156static int	 vtcon_ctrl_enqueue_msg(struct vtcon_softc *,
157		     struct virtio_console_control *);
158static int	 vtcon_ctrl_add_msg(struct vtcon_softc *);
159static void	 vtcon_ctrl_readd_msg(struct vtcon_softc *,
160		     struct virtio_console_control *);
161static int	 vtcon_ctrl_populate(struct vtcon_softc *);
162static void	 vtcon_ctrl_send_msg(struct vtcon_softc *,
163		     struct virtio_console_control *control);
164static void	 vtcon_ctrl_send_event(struct vtcon_softc *, uint32_t,
165		     uint16_t, uint16_t);
166static int	 vtcon_ctrl_init(struct vtcon_softc *);
167static void	 vtcon_ctrl_drain(struct vtcon_softc *);
168static void	 vtcon_ctrl_deinit(struct vtcon_softc *);
169static void	 vtcon_ctrl_port_add_event(struct vtcon_softc *, int);
170static void	 vtcon_ctrl_port_remove_event(struct vtcon_softc *, int);
171static void	 vtcon_ctrl_port_console_event(struct vtcon_softc *, int);
172static void	 vtcon_ctrl_port_open_event(struct vtcon_softc *, int);
173static void	 vtcon_ctrl_process_msg(struct vtcon_softc *,
174		     struct virtio_console_control *);
175static void	 vtcon_ctrl_task_cb(void *, int);
176
177static int	 vtcon_port_add_inbuf(struct vtcon_port *);
178static void	 vtcon_port_readd_inbuf(struct vtcon_port *, void *);
179static int	 vtcon_port_populate(struct vtcon_port *);
180static void	 vtcon_port_destroy(struct vtcon_port *);
181static int	 vtcon_port_create(struct vtcon_softc *, int,
182		     struct vtcon_port **);
183static void	 vtcon_port_drain_inbufs(struct vtcon_port *);
184static void	 vtcon_port_teardown(struct vtcon_port *, int);
185static void	 vtcon_port_change_size(struct vtcon_port *, uint16_t,
186		     uint16_t);
187static void	 vtcon_port_enable_intr(struct vtcon_port *);
188static void	 vtcon_port_disable_intr(struct vtcon_port *);
189static void	 vtcon_port_intr(struct vtcon_port *);
190static void	 vtcon_port_in_vq_intr(void *);
191static void	 vtcon_port_put(struct vtcon_port *, void *, int);
192static void	 vtcon_port_send_ctrl_msg(struct vtcon_port *, uint16_t,
193		     uint16_t);
194static struct vtcon_port *vtcon_port_lookup_by_id(struct vtcon_softc *, int);
195
196static int	 vtcon_tty_open(struct tty *);
197static void	 vtcon_tty_close(struct tty *);
198static void	 vtcon_tty_outwakeup(struct tty *);
199static void	 vtcon_tty_free(void *);
200
201static void	 vtcon_get_console_size(struct vtcon_softc *, uint16_t *,
202		     uint16_t *);
203
204static void	 vtcon_enable_interrupts(struct vtcon_softc *);
205static void	 vtcon_disable_interrupts(struct vtcon_softc *);
206
207static int	 vtcon_pending_free;
208
209static struct ttydevsw vtcon_tty_class = {
210	.tsw_flags	= 0,
211	.tsw_open	= vtcon_tty_open,
212	.tsw_close	= vtcon_tty_close,
213	.tsw_outwakeup	= vtcon_tty_outwakeup,
214	.tsw_free	= vtcon_tty_free,
215};
216
217static device_method_t vtcon_methods[] = {
218	/* Device methods. */
219	DEVMETHOD(device_probe,		vtcon_probe),
220	DEVMETHOD(device_attach,	vtcon_attach),
221	DEVMETHOD(device_detach,	vtcon_detach),
222
223	/* VirtIO methods. */
224	DEVMETHOD(virtio_config_change,	vtcon_config_change),
225
226	DEVMETHOD_END
227};
228
229static driver_t vtcon_driver = {
230	"vtcon",
231	vtcon_methods,
232	sizeof(struct vtcon_softc)
233};
234static devclass_t vtcon_devclass;
235
236DRIVER_MODULE(virtio_console, virtio_pci, vtcon_driver, vtcon_devclass,
237    vtcon_modevent, 0);
238MODULE_VERSION(virtio_console, 1);
239MODULE_DEPEND(virtio_console, virtio, 1, 1, 1);
240
241static int
242vtcon_modevent(module_t mod, int type, void *unused)
243{
244	int error;
245
246	switch (type) {
247	case MOD_LOAD:
248		error = 0;
249		break;
250	case MOD_QUIESCE:
251	case MOD_UNLOAD:
252		error = vtcon_pending_free != 0 ? EBUSY : 0;
253		/* error = EOPNOTSUPP; */
254		break;
255	case MOD_SHUTDOWN:
256		error = 0;
257		break;
258	default:
259		error = EOPNOTSUPP;
260		break;
261	}
262
263	return (error);
264}
265
266static int
267vtcon_probe(device_t dev)
268{
269
270	if (virtio_get_device_type(dev) != VIRTIO_ID_CONSOLE)
271		return (ENXIO);
272
273	device_set_desc(dev, "VirtIO Console Adapter");
274
275	return (BUS_PROBE_DEFAULT);
276}
277
278static int
279vtcon_attach(device_t dev)
280{
281	struct vtcon_softc *sc;
282	struct virtio_console_config concfg;
283	int error;
284
285	sc = device_get_softc(dev);
286	sc->vtcon_dev = dev;
287
288	VTCON_LOCK_INIT(sc, device_get_nameunit(dev));
289	TASK_INIT(&sc->vtcon_ctrl_task, 0, vtcon_ctrl_task_cb, sc);
290	TAILQ_INIT(&sc->vtcon_ports);
291
292	virtio_set_feature_desc(dev, vtcon_feature_desc);
293	vtcon_negotiate_features(sc);
294
295	if (virtio_with_feature(dev, VIRTIO_CONSOLE_F_SIZE))
296		sc->vtcon_flags |= VTCON_FLAG_SIZE;
297	if (virtio_with_feature(dev, VIRTIO_CONSOLE_F_MULTIPORT))
298		sc->vtcon_flags |= VTCON_FLAG_MULTIPORT;
299
300	vtcon_read_config(sc, &concfg);
301	vtcon_determine_max_ports(sc, &concfg);
302
303	error = vtcon_alloc_virtqueues(sc);
304	if (error) {
305		device_printf(dev, "cannot allocate virtqueues\n");
306		goto fail;
307	}
308
309	if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT)
310		error = vtcon_ctrl_init(sc);
311	else
312		error = vtcon_port_create(sc, 0, NULL);
313	if (error)
314		goto fail;
315
316	error = virtio_setup_intr(dev, INTR_TYPE_TTY);
317	if (error) {
318		device_printf(dev, "cannot setup virtqueue interrupts\n");
319		goto fail;
320	}
321
322	vtcon_enable_interrupts(sc);
323
324	vtcon_ctrl_send_event(sc, VIRTIO_CONSOLE_BAD_ID,
325	    VIRTIO_CONSOLE_DEVICE_READY, 1);
326
327fail:
328	if (error)
329		vtcon_detach(dev);
330
331	return (error);
332}
333
334static int
335vtcon_detach(device_t dev)
336{
337	struct vtcon_softc *sc;
338
339	sc = device_get_softc(dev);
340
341	VTCON_LOCK(sc);
342	sc->vtcon_flags |= VTCON_FLAG_DETACHED;
343	if (device_is_attached(dev))
344		vtcon_stop(sc);
345	VTCON_UNLOCK(sc);
346
347	taskqueue_drain(taskqueue_thread, &sc->vtcon_ctrl_task);
348
349	if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT)
350		vtcon_ctrl_deinit(sc);
351
352	vtcon_deinit_ports(sc);
353
354	VTCON_LOCK_DESTROY(sc);
355
356	return (0);
357}
358
359static int
360vtcon_config_change(device_t dev)
361{
362	struct vtcon_softc *sc;
363	struct vtcon_port *port;
364	uint16_t cols, rows;
365
366	sc = device_get_softc(dev);
367
368	/*
369	 * With the multiport feature, all configuration changes are
370	 * done through control virtqueue events. This is a spurious
371	 * interrupt.
372	 */
373	if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT)
374		return (0);
375
376	if (sc->vtcon_flags & VTCON_FLAG_SIZE) {
377		/*
378		 * For now, assume the first (only) port is the 'console'.
379		 * Note QEMU does not implement this feature yet.
380		 */
381		VTCON_LOCK(sc);
382		if ((port = vtcon_port_lookup_by_id(sc, 0)) != NULL) {
383			vtcon_get_console_size(sc, &cols, &rows);
384			vtcon_port_change_size(port, cols, rows);
385		}
386		VTCON_UNLOCK(sc);
387	}
388
389	return (0);
390}
391
392static void
393vtcon_negotiate_features(struct vtcon_softc *sc)
394{
395	device_t dev;
396	uint64_t features;
397
398	dev = sc->vtcon_dev;
399	features = VTCON_FEATURES;
400
401	sc->vtcon_features = virtio_negotiate_features(dev, features);
402}
403
404#define VTCON_GET_CONFIG(_dev, _feature, _field, _cfg)			\
405	if (virtio_with_feature(_dev, _feature)) {			\
406		virtio_read_device_config(_dev,				\
407		    offsetof(struct virtio_console_config, _field),	\
408		    &(_cfg)->_field, sizeof((_cfg)->_field));		\
409	}
410
411static void
412vtcon_read_config(struct vtcon_softc *sc, struct virtio_console_config *concfg)
413{
414	device_t dev;
415
416	dev = sc->vtcon_dev;
417
418	bzero(concfg, sizeof(struct virtio_console_config));
419
420	/* Read the configuration if the feature was negotiated. */
421	VTCON_GET_CONFIG(dev, VIRTIO_CONSOLE_F_SIZE, cols, concfg);
422	VTCON_GET_CONFIG(dev, VIRTIO_CONSOLE_F_SIZE, rows, concfg);
423	VTCON_GET_CONFIG(dev, VIRTIO_CONSOLE_F_MULTIPORT, max_nr_ports, concfg);
424}
425
426#undef VTCON_GET_CONFIG
427
428static int
429vtcon_alloc_virtqueues(struct vtcon_softc *sc)
430{
431	device_t dev;
432	struct vq_alloc_info *info;
433	struct vtcon_port_extra *portx;
434	int i, idx, portidx, nvqs, error;
435
436	dev = sc->vtcon_dev;
437
438	sc->vtcon_portsx = malloc(sizeof(struct vtcon_port_extra) *
439	    sc->vtcon_max_ports, M_DEVBUF, M_NOWAIT | M_ZERO);
440	if (sc->vtcon_portsx == NULL)
441		return (ENOMEM);
442
443	nvqs = sc->vtcon_max_ports * 2;
444	if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT)
445		nvqs += 2;
446
447	info = malloc(sizeof(struct vq_alloc_info) * nvqs, M_TEMP, M_NOWAIT);
448	if (info == NULL)
449		return (ENOMEM);
450
451	for (i = 0, idx = 0, portidx = 0; i < nvqs / 2; i++, idx+=2) {
452
453		if (i == 1) {
454			/* The control virtqueues are after the first port. */
455			VQ_ALLOC_INFO_INIT(&info[idx], 0,
456			    vtcon_ctrl_rx_vq_intr, sc, &sc->vtcon_ctrl_rxvq,
457			    "%s-control rx", device_get_nameunit(dev));
458			VQ_ALLOC_INFO_INIT(&info[idx+1], 0,
459			    NULL, sc, &sc->vtcon_ctrl_txvq,
460			    "%s-control tx", device_get_nameunit(dev));
461			continue;
462		}
463
464		portx = &sc->vtcon_portsx[portidx];
465
466		VQ_ALLOC_INFO_INIT(&info[idx], 0, vtcon_port_in_vq_intr,
467		    portx, &portx->invq, "%s-port%d in",
468		    device_get_nameunit(dev), portidx);
469		VQ_ALLOC_INFO_INIT(&info[idx+1], 0, NULL,
470		    NULL, &portx->outvq, "%s-port%d out",
471		    device_get_nameunit(dev), portidx);
472
473		portidx++;
474	}
475
476	error = virtio_alloc_virtqueues(dev, 0, nvqs, info);
477	free(info, M_TEMP);
478
479	return (error);
480}
481
482static void
483vtcon_determine_max_ports(struct vtcon_softc *sc,
484    struct virtio_console_config *concfg)
485{
486
487	if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) {
488		sc->vtcon_max_ports =
489		    min(concfg->max_nr_ports, VTCON_MAX_PORTS);
490		if (sc->vtcon_max_ports == 0)
491			sc->vtcon_max_ports = 1;
492	} else
493		sc->vtcon_max_ports = 1;
494}
495
496static void
497vtcon_deinit_ports(struct vtcon_softc *sc)
498{
499	struct vtcon_port *port, *tmp;
500
501	TAILQ_FOREACH_SAFE(port, &sc->vtcon_ports, vtcport_next, tmp) {
502		vtcon_port_teardown(port, 1);
503	}
504
505	if (sc->vtcon_portsx != NULL) {
506		free(sc->vtcon_portsx, M_DEVBUF);
507		sc->vtcon_portsx = NULL;
508	}
509}
510
511static void
512vtcon_stop(struct vtcon_softc *sc)
513{
514
515	vtcon_disable_interrupts(sc);
516	virtio_stop(sc->vtcon_dev);
517}
518
519static void
520vtcon_ctrl_rx_vq_intr(void *xsc)
521{
522	struct vtcon_softc *sc;
523
524	sc = xsc;
525
526	/*
527	 * Some events require us to potentially block, but it easier
528	 * to just defer all event handling to a seperate thread.
529	 */
530	taskqueue_enqueue(taskqueue_thread, &sc->vtcon_ctrl_task);
531}
532
533static int
534vtcon_ctrl_enqueue_msg(struct vtcon_softc *sc,
535    struct virtio_console_control *control)
536{
537	struct sglist_seg segs[1];
538	struct sglist sg;
539	struct virtqueue *vq;
540	int error __unused;
541
542	vq = sc->vtcon_ctrl_rxvq;
543
544	sglist_init(&sg, 1, segs);
545	error = sglist_append(&sg, control, sizeof(*control));
546	KASSERT(error == 0 && sg.sg_nseg == 1,
547	    ("%s: error %d adding control msg to sglist", __func__, error));
548
549	return (virtqueue_enqueue(vq, control, &sg, 0, 1));
550}
551
552static int
553vtcon_ctrl_add_msg(struct vtcon_softc *sc)
554{
555	struct virtio_console_control *control;
556	int error;
557
558	control = malloc(sizeof(*control), M_DEVBUF, M_ZERO | M_NOWAIT);
559	if (control == NULL)
560		return (ENOMEM);
561
562	error = vtcon_ctrl_enqueue_msg(sc, control);
563	if (error)
564		free(control, M_DEVBUF);
565
566	return (error);
567}
568
569static void
570vtcon_ctrl_readd_msg(struct vtcon_softc *sc,
571    struct virtio_console_control *control)
572{
573	int error;
574
575	bzero(control, sizeof(*control));
576
577	error = vtcon_ctrl_enqueue_msg(sc, control);
578	KASSERT(error == 0,
579	    ("%s: cannot requeue control buffer %d", __func__, error));
580}
581
582static int
583vtcon_ctrl_populate(struct vtcon_softc *sc)
584{
585	struct virtqueue *vq;
586	int nbufs, error;
587
588	vq = sc->vtcon_ctrl_rxvq;
589	error = ENOSPC;
590
591	for (nbufs = 0; !virtqueue_full(vq); nbufs++) {
592		error = vtcon_ctrl_add_msg(sc);
593		if (error)
594			break;
595	}
596
597	if (nbufs > 0) {
598		virtqueue_notify(vq);
599		/*
600		 * EMSGSIZE signifies the virtqueue did not have enough
601		 * entries available to hold the last buf. This is not
602		 * an error.
603		 */
604		if (error == EMSGSIZE)
605			error = 0;
606	}
607
608	return (error);
609}
610
611static void
612vtcon_ctrl_send_msg(struct vtcon_softc *sc,
613    struct virtio_console_control *control)
614{
615	struct sglist_seg segs[1];
616	struct sglist sg;
617	struct virtqueue *vq;
618	int error;
619
620	vq = sc->vtcon_ctrl_txvq;
621	KASSERT(virtqueue_empty(vq),
622	    ("%s: virtqueue is not emtpy", __func__));
623
624	sglist_init(&sg, 1, segs);
625	error = sglist_append(&sg, control, sizeof(*control));
626	KASSERT(error == 0 && sg.sg_nseg == 1,
627	    ("%s: error %d adding control msg to sglist", __func__, error));
628
629	error = virtqueue_enqueue(vq, control, &sg, 1, 0);
630	if (error == 0) {
631		virtqueue_notify(vq);
632		virtqueue_poll(vq, NULL);
633	}
634}
635
636static void
637vtcon_ctrl_send_event(struct vtcon_softc *sc, uint32_t portid, uint16_t event,
638    uint16_t value)
639{
640	struct virtio_console_control control;
641
642	if ((sc->vtcon_flags & VTCON_FLAG_MULTIPORT) == 0)
643		return;
644
645	control.id = portid;
646	control.event = event;
647	control.value = value;
648
649	vtcon_ctrl_send_msg(sc, &control);
650}
651
652static int
653vtcon_ctrl_init(struct vtcon_softc *sc)
654{
655	int error;
656
657	error = vtcon_ctrl_populate(sc);
658
659	return (error);
660}
661
662static void
663vtcon_ctrl_drain(struct vtcon_softc *sc)
664{
665	struct virtio_console_control *control;
666	struct virtqueue *vq;
667	int last;
668
669	vq = sc->vtcon_ctrl_rxvq;
670	last = 0;
671
672	if (vq == NULL)
673		return;
674
675	while ((control = virtqueue_drain(vq, &last)) != NULL)
676		free(control, M_DEVBUF);
677}
678
679static void
680vtcon_ctrl_deinit(struct vtcon_softc *sc)
681{
682
683	vtcon_ctrl_drain(sc);
684}
685
686static void
687vtcon_ctrl_port_add_event(struct vtcon_softc *sc, int id)
688{
689	device_t dev;
690	struct vtcon_port *port;
691	int error;
692
693	dev = sc->vtcon_dev;
694
695	if (vtcon_port_lookup_by_id(sc, id) != NULL) {
696		device_printf(dev, "%s: adding port %d, but already exists\n",
697		    __func__, id);
698		return;
699	}
700
701	error = vtcon_port_create(sc, id, &port);
702	if (error) {
703		device_printf(dev, "%s: cannot create port %d: %d\n",
704		    __func__, id, error);
705		return;
706	}
707
708	vtcon_port_send_ctrl_msg(port, VIRTIO_CONSOLE_PORT_READY, 1);
709}
710
711static void
712vtcon_ctrl_port_remove_event(struct vtcon_softc *sc, int id)
713{
714	device_t dev;
715	struct vtcon_port *port;
716
717	dev = sc->vtcon_dev;
718
719	port = vtcon_port_lookup_by_id(sc, id);
720	if (port == NULL) {
721		device_printf(dev, "%s: remove port %d, but does not exist\n",
722		    __func__, id);
723		return;
724	}
725
726	vtcon_port_teardown(port, 1);
727}
728
729static void
730vtcon_ctrl_port_console_event(struct vtcon_softc *sc, int id)
731{
732	device_t dev;
733
734	dev = sc->vtcon_dev;
735
736	/*
737	 * BMV: I don't think we need to do anything.
738	 */
739	device_printf(dev, "%s: port %d console event\n", __func__, id);
740}
741
742static void
743vtcon_ctrl_port_open_event(struct vtcon_softc *sc, int id)
744{
745	device_t dev;
746	struct vtcon_port *port;
747
748	dev = sc->vtcon_dev;
749
750	port = vtcon_port_lookup_by_id(sc, id);
751	if (port == NULL) {
752		device_printf(dev, "%s: open port %d, but does not exist\n",
753		    __func__, id);
754		return;
755	}
756
757	vtcon_port_enable_intr(port);
758}
759
760static void
761vtcon_ctrl_process_msg(struct vtcon_softc *sc,
762    struct virtio_console_control *control)
763{
764	device_t dev;
765	int id;
766
767	dev = sc->vtcon_dev;
768	id = control->id;
769
770	if (id < 0 || id >= sc->vtcon_max_ports) {
771		device_printf(dev, "%s: invalid port ID %d\n", __func__, id);
772		return;
773	}
774
775	switch (control->event) {
776	case VIRTIO_CONSOLE_PORT_ADD:
777		vtcon_ctrl_port_add_event(sc, id);
778		break;
779
780	case VIRTIO_CONSOLE_PORT_REMOVE:
781		vtcon_ctrl_port_remove_event(sc, id);
782		break;
783
784	case VIRTIO_CONSOLE_CONSOLE_PORT:
785		vtcon_ctrl_port_console_event(sc, id);
786		break;
787
788	case VIRTIO_CONSOLE_RESIZE:
789		break;
790
791	case VIRTIO_CONSOLE_PORT_OPEN:
792		vtcon_ctrl_port_open_event(sc, id);
793		break;
794
795	case VIRTIO_CONSOLE_PORT_NAME:
796		break;
797	}
798}
799
800static void
801vtcon_ctrl_task_cb(void *xsc, int pending)
802{
803	struct vtcon_softc *sc;
804	struct virtqueue *vq;
805	struct virtio_console_control *control;
806
807	sc = xsc;
808	vq = sc->vtcon_ctrl_rxvq;
809
810	VTCON_LOCK(sc);
811	while ((sc->vtcon_flags & VTCON_FLAG_DETACHED) == 0) {
812		control = virtqueue_dequeue(vq, NULL);
813		if (control == NULL)
814			break;
815
816		VTCON_UNLOCK(sc);
817		vtcon_ctrl_process_msg(sc, control);
818		VTCON_LOCK(sc);
819		vtcon_ctrl_readd_msg(sc, control);
820	}
821	VTCON_UNLOCK(sc);
822
823	if (virtqueue_enable_intr(vq) != 0)
824		taskqueue_enqueue(taskqueue_thread, &sc->vtcon_ctrl_task);
825}
826
827static int
828vtcon_port_enqueue_inbuf(struct vtcon_port *port, void *buf, size_t len)
829{
830	struct sglist_seg segs[1];
831	struct sglist sg;
832	struct virtqueue *vq;
833	int error;
834
835	vq = port->vtcport_invq;
836
837	sglist_init(&sg, 1, segs);
838	error = sglist_append(&sg, buf, len);
839	KASSERT(error == 0 && sg.sg_nseg == 1,
840	    ("%s: error %d adding buffer to sglist", __func__, error));
841
842	return (virtqueue_enqueue(vq, buf, &sg, 0, 1));
843}
844
845static int
846vtcon_port_add_inbuf(struct vtcon_port *port)
847{
848	void *buf;
849	int error;
850
851	buf = malloc(VTCON_BULK_BUFSZ, M_DEVBUF, M_ZERO | M_NOWAIT);
852	if (buf == NULL)
853		return (ENOMEM);
854
855	error = vtcon_port_enqueue_inbuf(port, buf, VTCON_BULK_BUFSZ);
856	if (error)
857		free(buf, M_DEVBUF);
858
859	return (error);
860}
861
862static void
863vtcon_port_readd_inbuf(struct vtcon_port *port, void *buf)
864{
865	int error __unused;
866
867	error = vtcon_port_enqueue_inbuf(port, buf, VTCON_BULK_BUFSZ);
868	KASSERT(error == 0,
869	    ("%s: cannot requeue input buffer %d", __func__, error));
870}
871
872static int
873vtcon_port_populate(struct vtcon_port *port)
874{
875	struct virtqueue *vq;
876	int nbufs, error;
877
878	vq = port->vtcport_invq;
879	error = ENOSPC;
880
881	for (nbufs = 0; !virtqueue_full(vq); nbufs++) {
882		error = vtcon_port_add_inbuf(port);
883		if (error)
884			break;
885	}
886
887	if (nbufs > 0) {
888		virtqueue_notify(vq);
889		/*
890		 * EMSGSIZE signifies the virtqueue did not have enough
891		 * entries available to hold the last buf. This is not
892		 * an error.
893		 */
894		if (error == EMSGSIZE)
895			error = 0;
896	}
897
898	return (error);
899}
900
901static void
902vtcon_port_destroy(struct vtcon_port *port)
903{
904
905	port->vtcport_sc = NULL;
906	port->vtcport_id = -1;
907	VTCON_PORT_LOCK_DESTROY(port);
908	free(port, M_DEVBUF);
909}
910
911static int
912vtcon_port_create(struct vtcon_softc *sc, int id, struct vtcon_port **portp)
913{
914	device_t dev;
915	struct vtcon_port_extra *portx;
916	struct vtcon_port *port;
917	int error;
918
919	MPASS(id < sc->vtcon_max_ports);
920	dev = sc->vtcon_dev;
921	portx = &sc->vtcon_portsx[id];
922
923	if (portx->port != NULL)
924		return (EEXIST);
925
926	port = malloc(sizeof(struct vtcon_port), M_DEVBUF, M_NOWAIT | M_ZERO);
927	if (port == NULL)
928		return (ENOMEM);
929
930	port->vtcport_sc = sc;
931	port->vtcport_id = id;
932	snprintf(port->vtcport_name, sizeof(port->vtcport_name), "%s-port%d",
933	    device_get_nameunit(dev), id);
934	VTCON_PORT_LOCK_INIT(port);
935	port->vtcport_tty = tty_alloc_mutex(&vtcon_tty_class, port,
936	    &port->vtcport_mtx);
937
938	/*
939	 * Assign virtqueues saved from attach. To be safe, clear the
940	 * virtqueue too.
941	 */
942	port->vtcport_invq = portx->invq;
943	port->vtcport_outvq = portx->outvq;
944	vtcon_port_drain_inbufs(port);
945
946	error = vtcon_port_populate(port);
947	if (error) {
948		vtcon_port_teardown(port, 0);
949		return (error);
950	}
951
952	tty_makedev(port->vtcport_tty, NULL, "%s%r.%r", VTCON_TTY_PREFIX,
953	    device_get_unit(dev), id);
954
955	VTCON_LOCK(sc);
956	portx->port = port;
957	TAILQ_INSERT_TAIL(&sc->vtcon_ports, port, vtcport_next);
958	VTCON_UNLOCK(sc);
959
960	if (portp != NULL)
961		*portp = port;
962
963	return (0);
964}
965
966static void
967vtcon_port_drain_inbufs(struct vtcon_port *port)
968{
969	struct virtqueue *vq;
970	void *buf;
971	int last;
972
973	vq = port->vtcport_invq;
974	last = 0;
975
976	if (vq == NULL)
977		return;
978
979	while ((buf = virtqueue_drain(vq, &last)) != NULL)
980		free(buf, M_DEVBUF);
981}
982
983static void
984vtcon_port_teardown(struct vtcon_port *port, int ontailq)
985{
986	struct vtcon_softc *sc;
987	struct vtcon_port_extra *portx;
988	struct tty *tp;
989	int id;
990
991	sc = port->vtcport_sc;
992	id = port->vtcport_id;
993	tp = port->vtcport_tty;
994
995	VTCON_ASSERT_VALID_PORTID(sc, id);
996	portx = &sc->vtcon_portsx[id];
997
998	VTCON_PORT_LOCK(port);
999	vtcon_port_drain_inbufs(port);
1000	VTCON_PORT_UNLOCK(port);
1001
1002	VTCON_LOCK(sc);
1003	KASSERT(portx->port == NULL || portx->port == port,
1004	    ("%s: port %d mismatch %p/%p", __func__, id, portx->port, port));
1005	portx->port = NULL;
1006	if (ontailq != 0)
1007		TAILQ_REMOVE(&sc->vtcon_ports, port, vtcport_next);
1008	VTCON_UNLOCK(sc);
1009
1010	if (tp != NULL) {
1011		port->vtcport_tty = NULL;
1012		atomic_add_int(&vtcon_pending_free, 1);
1013
1014		VTCON_PORT_LOCK(port);
1015		tty_rel_gone(tp);
1016	} else
1017		vtcon_port_destroy(port);
1018}
1019
1020static void
1021vtcon_port_change_size(struct vtcon_port *port, uint16_t cols, uint16_t rows)
1022{
1023	struct tty *tp;
1024	struct winsize sz;
1025
1026	tp = port->vtcport_tty;
1027
1028	if (tp == NULL)
1029		return;
1030
1031	bzero(&sz, sizeof(struct winsize));
1032	sz.ws_col = cols;
1033	sz.ws_row = rows;
1034
1035	VTCON_PORT_LOCK(port);
1036	tty_set_winsize(tp, &sz);
1037	VTCON_PORT_UNLOCK(port);
1038}
1039
1040static void
1041vtcon_port_enable_intr(struct vtcon_port *port)
1042{
1043
1044	/*
1045	 * NOTE: The out virtqueue is always polled, so we keep its
1046	 * interupt disabled.
1047	 */
1048
1049	virtqueue_enable_intr(port->vtcport_invq);
1050}
1051
1052static void
1053vtcon_port_disable_intr(struct vtcon_port *port)
1054{
1055
1056	if (port->vtcport_invq != NULL)
1057		virtqueue_disable_intr(port->vtcport_invq);
1058	if (port->vtcport_outvq != NULL)
1059		virtqueue_disable_intr(port->vtcport_outvq);
1060}
1061
1062static void
1063vtcon_port_intr(struct vtcon_port *port)
1064{
1065	struct tty *tp;
1066	struct virtqueue *vq;
1067	char *buf;
1068	uint32_t len;
1069	int i, deq;
1070
1071	tp = port->vtcport_tty;
1072	vq = port->vtcport_invq;
1073
1074again:
1075	deq = 0;
1076
1077	VTCON_PORT_LOCK(port);
1078	while ((buf = virtqueue_dequeue(vq, &len)) != NULL) {
1079		deq++;
1080		for (i = 0; i < len; i++)
1081			ttydisc_rint(tp, buf[i], 0);
1082		vtcon_port_readd_inbuf(port, buf);
1083	}
1084	ttydisc_rint_done(tp);
1085	VTCON_PORT_UNLOCK(port);
1086
1087	if (deq > 0)
1088		virtqueue_notify(vq);
1089
1090	if (virtqueue_enable_intr(vq) != 0)
1091		goto again;
1092}
1093
1094static void
1095vtcon_port_in_vq_intr(void *xportx)
1096{
1097	struct vtcon_port_extra *portx;
1098	struct vtcon_port *port;
1099
1100	portx = xportx;
1101	port = portx->port;
1102
1103	if (port != NULL)
1104		vtcon_port_intr(port);
1105}
1106
1107static void
1108vtcon_port_put(struct vtcon_port *port, void *buf, int bufsize)
1109{
1110	struct sglist_seg segs[1];
1111	struct sglist sg;
1112	struct virtqueue *vq;
1113	int error;
1114
1115	vq = port->vtcport_outvq;
1116
1117	sglist_init(&sg, 1, segs);
1118	error = sglist_append(&sg, buf, bufsize);
1119	KASSERT(error == 0 && sg.sg_nseg == 1,
1120	    ("%s: error %d adding buffer to sglist", __func__, error));
1121
1122	KASSERT(virtqueue_empty(vq), ("%s: port %p virtqueue not emtpy",
1123	     __func__, port));
1124
1125	if (virtqueue_enqueue(vq, buf, &sg, 1, 0) == 0) {
1126		virtqueue_notify(vq);
1127		virtqueue_poll(vq, NULL);
1128	}
1129}
1130
1131static void
1132vtcon_port_send_ctrl_msg(struct vtcon_port *port, uint16_t event,
1133    uint16_t value)
1134{
1135	struct vtcon_softc *sc;
1136
1137	sc = port->vtcport_sc;
1138
1139	if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT)
1140		vtcon_ctrl_send_event(sc, port->vtcport_id, event, value);
1141}
1142
1143static struct vtcon_port *
1144vtcon_port_lookup_by_id(struct vtcon_softc *sc, int id)
1145{
1146	struct vtcon_port *port;
1147
1148	TAILQ_FOREACH(port, &sc->vtcon_ports, vtcport_next) {
1149		if (port->vtcport_id == id)
1150			break;
1151	}
1152
1153	return (port);
1154}
1155
1156static int
1157vtcon_tty_open(struct tty *tp)
1158{
1159	struct vtcon_port *port;
1160
1161	port = tty_softc(tp);
1162
1163	vtcon_port_send_ctrl_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 1);
1164
1165	return (0);
1166}
1167
1168static void
1169vtcon_tty_close(struct tty *tp)
1170{
1171	struct vtcon_port *port;
1172
1173	port = tty_softc(tp);
1174
1175	vtcon_port_send_ctrl_msg(port, VIRTIO_CONSOLE_PORT_OPEN, 0);
1176}
1177
1178static void
1179vtcon_tty_outwakeup(struct tty *tp)
1180{
1181	struct vtcon_port *port;
1182	char buf[VTCON_BULK_BUFSZ];
1183	int len;
1184
1185	port = tty_softc(tp);
1186
1187	while ((len = ttydisc_getc(tp, buf, sizeof(buf))) != 0)
1188		vtcon_port_put(port, buf, len);
1189}
1190
1191static void
1192vtcon_tty_free(void *xport)
1193{
1194	struct vtcon_port *port;
1195
1196	port = xport;
1197
1198	vtcon_port_destroy(port);
1199	atomic_subtract_int(&vtcon_pending_free, 1);
1200}
1201
1202static void
1203vtcon_get_console_size(struct vtcon_softc *sc, uint16_t *cols, uint16_t *rows)
1204{
1205	struct virtio_console_config concfg;
1206
1207	KASSERT(sc->vtcon_flags & VTCON_FLAG_SIZE,
1208	    ("%s: size feature not negotiated", __func__));
1209
1210	vtcon_read_config(sc, &concfg);
1211
1212	*cols = concfg.cols;
1213	*rows = concfg.rows;
1214}
1215
1216static void
1217vtcon_enable_interrupts(struct vtcon_softc *sc)
1218{
1219	struct vtcon_port *port;
1220
1221	if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT)
1222		virtqueue_enable_intr(sc->vtcon_ctrl_rxvq);
1223
1224	TAILQ_FOREACH(port, &sc->vtcon_ports, vtcport_next)
1225		vtcon_port_enable_intr(port);
1226}
1227
1228static void
1229vtcon_disable_interrupts(struct vtcon_softc *sc)
1230{
1231	struct vtcon_port *port;
1232
1233	if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT)
1234		virtqueue_disable_intr(sc->vtcon_ctrl_rxvq);
1235
1236	TAILQ_FOREACH(port, &sc->vtcon_ports, vtcport_next)
1237		vtcon_port_disable_intr(port);
1238}
1239