121308Sache/*-
221308Sache * Copyright (c) 2014, Bryan Venteicher <bryanv@FreeBSD.org>
321308Sache * All rights reserved.
421308Sache *
521308Sache * Redistribution and use in source and binary forms, with or without
621308Sache * modification, are permitted provided that the following conditions
721308Sache * are met:
821308Sache * 1. Redistributions of source code must retain the above copyright
921308Sache *    notice unmodified, this list of conditions, and the following
1021308Sache *    disclaimer.
1121308Sache * 2. Redistributions in binary form must reproduce the above copyright
1221308Sache *    notice, this list of conditions and the following disclaimer in the
1321308Sache *    documentation and/or other materials provided with the distribution.
1421308Sache *
1521308Sache * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
1621308Sache * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
1721308Sache * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
1821308Sache * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
1921308Sache * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
2021308Sache * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
2121308Sache * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
2221308Sache * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
2321308Sache * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
2421308Sache * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
2521308Sache */
2621308Sache
2721308Sache/* Driver for VirtIO console devices. */
2821308Sache
2921308Sache#include <sys/cdefs.h>
3021308Sache__FBSDID("$FreeBSD: releng/10.3/sys/dev/virtio/console/virtio_console.c 275273 2014-11-29 22:48:40Z bryanv $");
3121308Sache
3221308Sache#include <sys/param.h>
3321308Sache#include <sys/systm.h>
3421308Sache#include <sys/kernel.h>
3521308Sache#include <sys/malloc.h>
3621308Sache#include <sys/module.h>
3721308Sache#include <sys/kdb.h>
3821308Sache#include <sys/lock.h>
3921308Sache#include <sys/mutex.h>
4021308Sache#include <sys/sglist.h>
4121308Sache#include <sys/sysctl.h>
4221308Sache#include <sys/taskqueue.h>
4321308Sache#include <sys/queue.h>
4421308Sache
4521308Sache#include <sys/conf.h>
4621308Sache#include <sys/cons.h>
4721308Sache#include <sys/tty.h>
4821308Sache
4921308Sache#include <machine/bus.h>
5021308Sache#include <machine/resource.h>
5121308Sache#include <sys/bus.h>
5221308Sache
5321308Sache#include <dev/virtio/virtio.h>
5421308Sache#include <dev/virtio/virtqueue.h>
5521308Sache#include <dev/virtio/console/virtio_console.h>
5626497Sache
5721308Sache#include "virtio_if.h"
5821308Sache
5921308Sache#define VTCON_MAX_PORTS 32
6021308Sache#define VTCON_TTY_PREFIX "V"
6121308Sache#define VTCON_BULK_BUFSZ 128
6221308Sache
6321308Sache/*
6421308Sache * The buffer cannot cross more than one page boundary due to the
6521308Sache * size of the sglist segment array used.
6621308Sache */
6721308SacheCTASSERT(VTCON_BULK_BUFSZ <= PAGE_SIZE);
6821308Sache
6921308Sachestruct vtcon_softc;
7021308Sachestruct vtcon_softc_port;
7121308Sache
7221308Sachestruct vtcon_port {
7321308Sache	struct mtx			 vtcport_mtx;
7421308Sache	struct vtcon_softc		*vtcport_sc;
7521308Sache	struct vtcon_softc_port		*vtcport_scport;
7621308Sache	struct tty			*vtcport_tty;
7721308Sache	struct virtqueue		*vtcport_invq;
7821308Sache	struct virtqueue		*vtcport_outvq;
7921308Sache	int				 vtcport_id;
8021308Sache	int				 vtcport_flags;
8121308Sache#define VTCON_PORT_FLAG_GONE	0x01
8221308Sache#define VTCON_PORT_FLAG_CONSOLE	0x02
8321308Sache
8421308Sache#if defined(KDB)
8521308Sache	int				 vtcport_alt_break_state;
8621308Sache#endif
8721308Sache};
8821308Sache
8921308Sache#define VTCON_PORT_LOCK(_port)		mtx_lock(&(_port)->vtcport_mtx)
9021308Sache#define VTCON_PORT_UNLOCK(_port)	mtx_unlock(&(_port)->vtcport_mtx)
9121308Sache
9221308Sachestruct vtcon_softc_port {
9321308Sache	struct vtcon_softc	*vcsp_sc;
9421308Sache	struct vtcon_port	*vcsp_port;
9521308Sache	struct virtqueue	*vcsp_invq;
9621308Sache	struct virtqueue	*vcsp_outvq;
9721308Sache};
9821308Sache
9921308Sachestruct vtcon_softc {
10021308Sache	device_t		 vtcon_dev;
10121308Sache	struct mtx		 vtcon_mtx;
10221308Sache	uint64_t		 vtcon_features;
10321308Sache	uint32_t		 vtcon_max_ports;
10421308Sache	uint32_t		 vtcon_flags;
10521308Sache#define VTCON_FLAG_DETACHED	0x01
10621308Sache#define VTCON_FLAG_SIZE		0x02
10721308Sache#define VTCON_FLAG_MULTIPORT	0x04
10821308Sache
10921308Sache	/*
11021308Sache	 * Ports can be added and removed during runtime, but we have
11121308Sache	 * to allocate all the virtqueues during attach. This array is
11221308Sache	 * indexed by the port ID.
11321308Sache	 */
11421308Sache	struct vtcon_softc_port	*vtcon_ports;
11521308Sache
11621308Sache	struct task		 vtcon_ctrl_task;
11721308Sache	struct virtqueue	*vtcon_ctrl_rxvq;
11821308Sache	struct virtqueue	*vtcon_ctrl_txvq;
11921308Sache	struct mtx		 vtcon_ctrl_tx_mtx;
12021308Sache};
12121308Sache
12221308Sache#define VTCON_LOCK(_sc)			mtx_lock(&(_sc)->vtcon_mtx)
12321308Sache#define VTCON_UNLOCK(_sc)		mtx_unlock(&(_sc)->vtcon_mtx)
12421308Sache#define VTCON_LOCK_ASSERT(_sc)		\
12521308Sache    mtx_assert(&(_sc)->vtcon_mtx, MA_OWNED)
12621308Sache#define VTCON_LOCK_ASSERT_NOTOWNED(_sc)	\
12721308Sache    mtx_assert(&(_sc)->vtcon_mtx, MA_NOTOWNED)
12821308Sache
12921308Sache#define VTCON_CTRL_TX_LOCK(_sc)		mtx_lock(&(_sc)->vtcon_ctrl_tx_mtx)
13021308Sache#define VTCON_CTRL_TX_UNLOCK(_sc)	mtx_unlock(&(_sc)->vtcon_ctrl_tx_mtx)
13121308Sache
13221308Sache#define VTCON_ASSERT_VALID_PORTID(_sc, _id)			\
13321308Sache    KASSERT((_id) >= 0 && (_id) < (_sc)->vtcon_max_ports,	\
13421308Sache        ("%s: port ID %d out of range", __func__, _id))
13521308Sache
13621308Sache#define VTCON_FEATURES  VIRTIO_CONSOLE_F_MULTIPORT
13721308Sache
13821308Sachestatic struct virtio_feature_desc vtcon_feature_desc[] = {
13921308Sache	{ VIRTIO_CONSOLE_F_SIZE,	"ConsoleSize"	},
14021308Sache	{ VIRTIO_CONSOLE_F_MULTIPORT,	"MultiplePorts"	},
14121308Sache	{ VIRTIO_CONSOLE_F_EMERG_WRITE,	"EmergencyWrite" },
14221308Sache
14321308Sache	{ 0, NULL }
14421308Sache};
14521308Sache
14621308Sachestatic int	 vtcon_modevent(module_t, int, void *);
14721308Sachestatic void	 vtcon_drain_all(void);
14821308Sache
14921308Sachestatic int	 vtcon_probe(device_t);
15021308Sachestatic int	 vtcon_attach(device_t);
15121308Sachestatic int	 vtcon_detach(device_t);
15221308Sachestatic int	 vtcon_config_change(device_t);
15321308Sache
15421308Sachestatic void	 vtcon_setup_features(struct vtcon_softc *);
15521308Sachestatic void	 vtcon_negotiate_features(struct vtcon_softc *);
15621308Sachestatic int	 vtcon_alloc_scports(struct vtcon_softc *);
15721308Sachestatic int	 vtcon_alloc_virtqueues(struct vtcon_softc *);
15821308Sachestatic void	 vtcon_read_config(struct vtcon_softc *,
15921308Sache		     struct virtio_console_config *);
16021308Sache
16121308Sachestatic void	 vtcon_determine_max_ports(struct vtcon_softc *,
16221308Sache		     struct virtio_console_config *);
16321308Sachestatic void	 vtcon_destroy_ports(struct vtcon_softc *);
16421308Sachestatic void	 vtcon_stop(struct vtcon_softc *);
16521308Sache
16621308Sachestatic int	 vtcon_ctrl_event_enqueue(struct vtcon_softc *,
16721308Sache		     struct virtio_console_control *);
16821308Sachestatic int	 vtcon_ctrl_event_create(struct vtcon_softc *);
16921308Sachestatic void	 vtcon_ctrl_event_requeue(struct vtcon_softc *,
17021308Sache		     struct virtio_console_control *);
17121308Sachestatic int	 vtcon_ctrl_event_populate(struct vtcon_softc *);
17221308Sachestatic void	 vtcon_ctrl_event_drain(struct vtcon_softc *);
17321308Sachestatic int	 vtcon_ctrl_init(struct vtcon_softc *);
17421308Sachestatic void	 vtcon_ctrl_deinit(struct vtcon_softc *);
17521308Sachestatic void	 vtcon_ctrl_port_add_event(struct vtcon_softc *, int);
17621308Sachestatic void	 vtcon_ctrl_port_remove_event(struct vtcon_softc *, int);
17721308Sachestatic void	 vtcon_ctrl_port_console_event(struct vtcon_softc *, int);
17821308Sachestatic void	 vtcon_ctrl_port_open_event(struct vtcon_softc *, int);
17921308Sachestatic void	 vtcon_ctrl_process_event(struct vtcon_softc *,
18021308Sache		     struct virtio_console_control *);
18121308Sachestatic void	 vtcon_ctrl_task_cb(void *, int);
18221308Sachestatic void	 vtcon_ctrl_event_intr(void *);
18321308Sachestatic void	 vtcon_ctrl_poll(struct vtcon_softc *,
18421308Sache		     struct virtio_console_control *control);
18521308Sachestatic void	 vtcon_ctrl_send_control(struct vtcon_softc *, uint32_t,
18621308Sache		     uint16_t, uint16_t);
18721308Sache
18821308Sachestatic int	 vtcon_port_enqueue_buf(struct vtcon_port *, void *, size_t);
18921308Sachestatic int	 vtcon_port_create_buf(struct vtcon_port *);
19021308Sachestatic void	 vtcon_port_requeue_buf(struct vtcon_port *, void *);
19121308Sachestatic int	 vtcon_port_populate(struct vtcon_port *);
19221308Sachestatic void	 vtcon_port_destroy(struct vtcon_port *);
19321308Sachestatic int	 vtcon_port_create(struct vtcon_softc *, int);
19421308Sachestatic void	 vtcon_port_drain_bufs(struct virtqueue *);
19521308Sachestatic void	 vtcon_port_drain(struct vtcon_port *);
19621308Sachestatic void	 vtcon_port_teardown(struct vtcon_port *);
19721308Sachestatic void	 vtcon_port_change_size(struct vtcon_port *, uint16_t,
19821308Sache		     uint16_t);
19921308Sachestatic void	 vtcon_port_update_console_size(struct vtcon_softc *);
20021308Sachestatic void	 vtcon_port_enable_intr(struct vtcon_port *);
20121308Sachestatic void	 vtcon_port_disable_intr(struct vtcon_port *);
20221308Sachestatic void	 vtcon_port_in(struct vtcon_port *);
20321308Sachestatic void	 vtcon_port_intr(void *);
20421308Sachestatic void	 vtcon_port_out(struct vtcon_port *, void *, int);
20521308Sachestatic void	 vtcon_port_submit_event(struct vtcon_port *, uint16_t,
20621308Sache		     uint16_t);
20721308Sache
20821308Sachestatic int	 vtcon_tty_open(struct tty *);
20921308Sachestatic void	 vtcon_tty_close(struct tty *);
21021308Sachestatic void	 vtcon_tty_outwakeup(struct tty *);
21121308Sachestatic void	 vtcon_tty_free(void *);
21221308Sache
21321308Sachestatic void	 vtcon_get_console_size(struct vtcon_softc *, uint16_t *,
21421308Sache		     uint16_t *);
21521308Sache
21621308Sachestatic void	 vtcon_enable_interrupts(struct vtcon_softc *);
21721308Sachestatic void	 vtcon_disable_interrupts(struct vtcon_softc *);
21821308Sache
21921308Sachestatic int	 vtcon_pending_free;
22021308Sache
22121308Sachestatic struct ttydevsw vtcon_tty_class = {
22221308Sache	.tsw_flags	= 0,
22321308Sache	.tsw_open	= vtcon_tty_open,
22421308Sache	.tsw_close	= vtcon_tty_close,
22521308Sache	.tsw_outwakeup	= vtcon_tty_outwakeup,
22621308Sache	.tsw_free	= vtcon_tty_free,
22721308Sache};
22821308Sache
22921308Sachestatic device_method_t vtcon_methods[] = {
23021308Sache	/* Device methods. */
23121308Sache	DEVMETHOD(device_probe,		vtcon_probe),
23221308Sache	DEVMETHOD(device_attach,	vtcon_attach),
23321308Sache	DEVMETHOD(device_detach,	vtcon_detach),
23421308Sache
23521308Sache	/* VirtIO methods. */
23621308Sache	DEVMETHOD(virtio_config_change,	vtcon_config_change),
23721308Sache
23821308Sache	DEVMETHOD_END
23921308Sache};
24021308Sache
24121308Sachestatic driver_t vtcon_driver = {
24221308Sache	"vtcon",
24321308Sache	vtcon_methods,
24421308Sache	sizeof(struct vtcon_softc)
24521308Sache};
24621308Sachestatic devclass_t vtcon_devclass;
24721308Sache
24821308SacheDRIVER_MODULE(virtio_console, virtio_pci, vtcon_driver, vtcon_devclass,
24921308Sache    vtcon_modevent, 0);
25021308SacheMODULE_VERSION(virtio_console, 1);
25121308SacheMODULE_DEPEND(virtio_console, virtio, 1, 1, 1);
25221308Sache
25321308Sachestatic int
25421308Sachevtcon_modevent(module_t mod, int type, void *unused)
25521308Sache{
25621308Sache	int error;
25721308Sache
25821308Sache	switch (type) {
25921308Sache	case MOD_LOAD:
26021308Sache		error = 0;
26121308Sache		break;
26221308Sache	case MOD_QUIESCE:
26321308Sache		error = 0;
26421308Sache		break;
26521308Sache	case MOD_UNLOAD:
26621308Sache		vtcon_drain_all();
26721308Sache		error = 0;
26821308Sache		break;
26921308Sache	case MOD_SHUTDOWN:
27021308Sache		error = 0;
27121308Sache		break;
27221308Sache	default:
27321308Sache		error = EOPNOTSUPP;
27421308Sache		break;
27521308Sache	}
27621308Sache
27721308Sache	return (error);
27821308Sache}
27921308Sache
28021308Sachestatic void
28121308Sachevtcon_drain_all(void)
28221308Sache{
28321308Sache	int first;
28421308Sache
28521308Sache	for (first = 1; vtcon_pending_free != 0; first = 0) {
28621308Sache		if (first != 0) {
28721308Sache			printf("virtio_console: Waiting for all detached TTY "
28821308Sache			    "devices to have open fds closed.\n");
28921308Sache		}
29021308Sache		pause("vtcondra", hz);
29121308Sache	}
29221308Sache}
29321308Sache
29421308Sachestatic int
29521308Sachevtcon_probe(device_t dev)
29621308Sache{
29721308Sache
29821308Sache	if (virtio_get_device_type(dev) != VIRTIO_ID_CONSOLE)
29921308Sache		return (ENXIO);
30021308Sache
30121308Sache	device_set_desc(dev, "VirtIO Console Adapter");
30221308Sache
30321308Sache	return (BUS_PROBE_DEFAULT);
30421308Sache}
30521308Sache
30621308Sachestatic int
30721308Sachevtcon_attach(device_t dev)
30821308Sache{
30921308Sache	struct vtcon_softc *sc;
31021308Sache	struct virtio_console_config concfg;
31121308Sache	int error;
31221308Sache
31321308Sache	sc = device_get_softc(dev);
31421308Sache	sc->vtcon_dev = dev;
31521308Sache
31621308Sache	mtx_init(&sc->vtcon_mtx, "vtconmtx", NULL, MTX_DEF);
31721308Sache	mtx_init(&sc->vtcon_ctrl_tx_mtx, "vtconctrlmtx", NULL, MTX_DEF);
31821308Sache
31921308Sache	virtio_set_feature_desc(dev, vtcon_feature_desc);
32021308Sache	vtcon_setup_features(sc);
32121308Sache
32221308Sache	vtcon_read_config(sc, &concfg);
32321308Sache	vtcon_determine_max_ports(sc, &concfg);
32421308Sache
32521308Sache	error = vtcon_alloc_scports(sc);
32621308Sache	if (error) {
32721308Sache		device_printf(dev, "cannot allocate softc port structures\n");
32821308Sache		goto fail;
32921308Sache	}
33021308Sache
33121308Sache	error = vtcon_alloc_virtqueues(sc);
33221308Sache	if (error) {
33321308Sache		device_printf(dev, "cannot allocate virtqueues\n");
33421308Sache		goto fail;
33521308Sache	}
33621308Sache
33721308Sache	if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) {
33821308Sache		TASK_INIT(&sc->vtcon_ctrl_task, 0, vtcon_ctrl_task_cb, sc);
33921308Sache		error = vtcon_ctrl_init(sc);
34021308Sache		if (error)
34121308Sache			goto fail;
34221308Sache	} else {
34321308Sache		error = vtcon_port_create(sc, 0);
34421308Sache		if (error)
34521308Sache			goto fail;
34621308Sache		if (sc->vtcon_flags & VTCON_FLAG_SIZE)
34721308Sache			vtcon_port_update_console_size(sc);
34821308Sache	}
34921308Sache
35021308Sache	error = virtio_setup_intr(dev, INTR_TYPE_TTY);
35121308Sache	if (error) {
35221308Sache		device_printf(dev, "cannot setup virtqueue interrupts\n");
35321308Sache		goto fail;
35421308Sache	}
35521308Sache
35621308Sache	vtcon_enable_interrupts(sc);
35721308Sache
35821308Sache	vtcon_ctrl_send_control(sc, VIRTIO_CONSOLE_BAD_ID,
35921308Sache	    VIRTIO_CONSOLE_DEVICE_READY, 1);
36021308Sache
36121308Sachefail:
36221308Sache	if (error)
36321308Sache		vtcon_detach(dev);
36421308Sache
36521308Sache	return (error);
36621308Sache}
36721308Sache
36821308Sachestatic int
36921308Sachevtcon_detach(device_t dev)
37021308Sache{
37121308Sache	struct vtcon_softc *sc;
37221308Sache
37321308Sache	sc = device_get_softc(dev);
37421308Sache
37521308Sache	VTCON_LOCK(sc);
37621308Sache	sc->vtcon_flags |= VTCON_FLAG_DETACHED;
37721308Sache	if (device_is_attached(dev))
37821308Sache		vtcon_stop(sc);
37921308Sache	VTCON_UNLOCK(sc);
38021308Sache
38121308Sache	if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) {
38221308Sache		taskqueue_drain(taskqueue_thread, &sc->vtcon_ctrl_task);
38321308Sache		vtcon_ctrl_deinit(sc);
38421308Sache	}
38521308Sache
38621308Sache	vtcon_destroy_ports(sc);
38721308Sache	mtx_destroy(&sc->vtcon_mtx);
38826497Sache	mtx_destroy(&sc->vtcon_ctrl_tx_mtx);
38926497Sache
39026497Sache	return (0);
39126497Sache}
39226497Sache
39321308Sachestatic int
39421308Sachevtcon_config_change(device_t dev)
39521308Sache{
39621308Sache	struct vtcon_softc *sc;
39721308Sache
39821308Sache	sc = device_get_softc(dev);
39921308Sache
40021308Sache	/*
40121308Sache	 * When the multiport feature is negotiated, all configuration
40221308Sache	 * changes are done through control virtqueue events.
40321308Sache	 */
40421308Sache	if ((sc->vtcon_flags & VTCON_FLAG_MULTIPORT) == 0) {
40521308Sache		if (sc->vtcon_flags & VTCON_FLAG_SIZE)
40621308Sache			vtcon_port_update_console_size(sc);
40721308Sache	}
40821308Sache
40921308Sache	return (0);
41021308Sache}
41121308Sache
41221308Sachestatic void
41321308Sachevtcon_negotiate_features(struct vtcon_softc *sc)
41421308Sache{
41521308Sache	device_t dev;
41621308Sache	uint64_t features;
41721308Sache
41821308Sache	dev = sc->vtcon_dev;
41921308Sache	features = VTCON_FEATURES;
42021308Sache
42121308Sache	sc->vtcon_features = virtio_negotiate_features(dev, features);
42221308Sache}
42321308Sache
42421308Sachestatic void
42521308Sachevtcon_setup_features(struct vtcon_softc *sc)
42621308Sache{
42721308Sache	device_t dev;
42821308Sache
42921308Sache	dev = sc->vtcon_dev;
43021308Sache
43121308Sache	vtcon_negotiate_features(sc);
43221308Sache
43321308Sache	if (virtio_with_feature(dev, VIRTIO_CONSOLE_F_SIZE))
43421308Sache		sc->vtcon_flags |= VTCON_FLAG_SIZE;
43521308Sache	if (virtio_with_feature(dev, VIRTIO_CONSOLE_F_MULTIPORT))
43621308Sache		sc->vtcon_flags |= VTCON_FLAG_MULTIPORT;
43721308Sache}
43821308Sache
43921308Sache#define VTCON_GET_CONFIG(_dev, _feature, _field, _cfg)			\
44021308Sache	if (virtio_with_feature(_dev, _feature)) {			\
44121308Sache		virtio_read_device_config(_dev,				\
44221308Sache		    offsetof(struct virtio_console_config, _field),	\
44321308Sache		    &(_cfg)->_field, sizeof((_cfg)->_field));		\
44421308Sache	}
44521308Sache
44621308Sachestatic void
44721308Sachevtcon_read_config(struct vtcon_softc *sc, struct virtio_console_config *concfg)
44821308Sache{
44921308Sache	device_t dev;
45021308Sache
45121308Sache	dev = sc->vtcon_dev;
45221308Sache
45321308Sache	bzero(concfg, sizeof(struct virtio_console_config));
45421308Sache
45521308Sache	VTCON_GET_CONFIG(dev, VIRTIO_CONSOLE_F_SIZE, cols, concfg);
45621308Sache	VTCON_GET_CONFIG(dev, VIRTIO_CONSOLE_F_SIZE, rows, concfg);
45721308Sache	VTCON_GET_CONFIG(dev, VIRTIO_CONSOLE_F_MULTIPORT, max_nr_ports, concfg);
45821308Sache}
45921308Sache
46021308Sache#undef VTCON_GET_CONFIG
46121308Sache
46221308Sachestatic int
46321308Sachevtcon_alloc_scports(struct vtcon_softc *sc)
46421308Sache{
46521308Sache	struct vtcon_softc_port *scport;
46621308Sache	int max, i;
46721308Sache
46821308Sache	max = sc->vtcon_max_ports;
46921308Sache
47021308Sache	sc->vtcon_ports = malloc(sizeof(struct vtcon_softc_port) * max,
47121308Sache	    M_DEVBUF, M_NOWAIT | M_ZERO);
47221308Sache	if (sc->vtcon_ports == NULL)
47321308Sache		return (ENOMEM);
47421308Sache
47521308Sache	for (i = 0; i < max; i++) {
47621308Sache		scport = &sc->vtcon_ports[i];
47721308Sache		scport->vcsp_sc = sc;
47821308Sache	}
47921308Sache
48021308Sache	return (0);
48121308Sache}
48221308Sache
48321308Sachestatic int
48421308Sachevtcon_alloc_virtqueues(struct vtcon_softc *sc)
48521308Sache{
48621308Sache	device_t dev;
48721308Sache	struct vq_alloc_info *info;
48821308Sache	struct vtcon_softc_port *scport;
48921308Sache	int i, idx, portidx, nvqs, error;
49021308Sache
49121308Sache	dev = sc->vtcon_dev;
49221308Sache
49321308Sache	nvqs = sc->vtcon_max_ports * 2;
49421308Sache	if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT)
49521308Sache		nvqs += 2;
49621308Sache
49721308Sache	info = malloc(sizeof(struct vq_alloc_info) * nvqs, M_TEMP, M_NOWAIT);
49821308Sache	if (info == NULL)
49921308Sache		return (ENOMEM);
50021308Sache
50121308Sache	for (i = 0, idx = 0, portidx = 0; i < nvqs / 2; i++, idx += 2) {
50221308Sache
50321308Sache		if (i == 1) {
50421308Sache			/* The control virtqueues are after the first port. */
50521308Sache			VQ_ALLOC_INFO_INIT(&info[idx], 0,
50621308Sache			    vtcon_ctrl_event_intr, sc, &sc->vtcon_ctrl_rxvq,
50721308Sache			    "%s-control rx", device_get_nameunit(dev));
50821308Sache			VQ_ALLOC_INFO_INIT(&info[idx+1], 0,
50921308Sache			    NULL, sc, &sc->vtcon_ctrl_txvq,
51021308Sache			    "%s-control tx", device_get_nameunit(dev));
51121308Sache			continue;
51221308Sache		}
51321308Sache
51421308Sache		scport = &sc->vtcon_ports[portidx];
51521308Sache
51621308Sache		VQ_ALLOC_INFO_INIT(&info[idx], 0, vtcon_port_intr,
51721308Sache		    scport, &scport->vcsp_invq, "%s-port%d in",
51821308Sache		    device_get_nameunit(dev), i);
51921308Sache		VQ_ALLOC_INFO_INIT(&info[idx+1], 0, NULL,
52021308Sache		    NULL, &scport->vcsp_outvq, "%s-port%d out",
52121308Sache		    device_get_nameunit(dev), i);
52221308Sache
52321308Sache		portidx++;
52421308Sache	}
52521308Sache
52621308Sache	error = virtio_alloc_virtqueues(dev, 0, nvqs, info);
52721308Sache	free(info, M_TEMP);
52821308Sache
52926497Sache	return (error);
53021308Sache}
53121308Sache
53221308Sachestatic void
53321308Sachevtcon_determine_max_ports(struct vtcon_softc *sc,
53421308Sache    struct virtio_console_config *concfg)
53521308Sache{
53621308Sache
53721308Sache	if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT) {
53821308Sache		sc->vtcon_max_ports =
53921308Sache		    min(concfg->max_nr_ports, VTCON_MAX_PORTS);
54021308Sache		if (sc->vtcon_max_ports == 0)
54121308Sache			sc->vtcon_max_ports = 1;
54221308Sache	} else
54321308Sache		sc->vtcon_max_ports = 1;
54421308Sache}
54521308Sache
54621308Sachestatic void
54721308Sachevtcon_destroy_ports(struct vtcon_softc *sc)
54821308Sache{
54921308Sache	struct vtcon_softc_port *scport;
55021308Sache	struct vtcon_port *port;
55121308Sache	struct virtqueue *vq;
55221308Sache	int i;
553
554	if (sc->vtcon_ports == NULL)
555		return;
556
557	VTCON_LOCK(sc);
558	for (i = 0; i < sc->vtcon_max_ports; i++) {
559		scport = &sc->vtcon_ports[i];
560
561		port = scport->vcsp_port;
562		if (port != NULL) {
563			scport->vcsp_port = NULL;
564			VTCON_PORT_LOCK(port);
565			VTCON_UNLOCK(sc);
566			vtcon_port_teardown(port);
567			VTCON_LOCK(sc);
568		}
569
570		vq = scport->vcsp_invq;
571		if (vq != NULL)
572			vtcon_port_drain_bufs(vq);
573	}
574	VTCON_UNLOCK(sc);
575
576	free(sc->vtcon_ports, M_DEVBUF);
577	sc->vtcon_ports = NULL;
578}
579
580static void
581vtcon_stop(struct vtcon_softc *sc)
582{
583
584	vtcon_disable_interrupts(sc);
585	virtio_stop(sc->vtcon_dev);
586}
587
588static int
589vtcon_ctrl_event_enqueue(struct vtcon_softc *sc,
590    struct virtio_console_control *control)
591{
592	struct sglist_seg segs[2];
593	struct sglist sg;
594	struct virtqueue *vq;
595	int error;
596
597	vq = sc->vtcon_ctrl_rxvq;
598
599	sglist_init(&sg, 2, segs);
600	error = sglist_append(&sg, control,
601	    sizeof(struct virtio_console_control));
602	KASSERT(error == 0, ("%s: error %d adding control to sglist",
603	    __func__, error));
604
605	return (virtqueue_enqueue(vq, control, &sg, 0, sg.sg_nseg));
606}
607
608static int
609vtcon_ctrl_event_create(struct vtcon_softc *sc)
610{
611	struct virtio_console_control *control;
612	int error;
613
614	control = malloc(sizeof(struct virtio_console_control), M_DEVBUF,
615	    M_ZERO | M_NOWAIT);
616	if (control == NULL)
617		return (ENOMEM);
618
619	error = vtcon_ctrl_event_enqueue(sc, control);
620	if (error)
621		free(control, M_DEVBUF);
622
623	return (error);
624}
625
626static void
627vtcon_ctrl_event_requeue(struct vtcon_softc *sc,
628    struct virtio_console_control *control)
629{
630	int error;
631
632	bzero(control, sizeof(struct virtio_console_control));
633
634	error = vtcon_ctrl_event_enqueue(sc, control);
635	KASSERT(error == 0,
636	    ("%s: cannot requeue control buffer %d", __func__, error));
637}
638
639static int
640vtcon_ctrl_event_populate(struct vtcon_softc *sc)
641{
642	struct virtqueue *vq;
643	int nbufs, error;
644
645	vq = sc->vtcon_ctrl_rxvq;
646	error = ENOSPC;
647
648	for (nbufs = 0; !virtqueue_full(vq); nbufs++) {
649		error = vtcon_ctrl_event_create(sc);
650		if (error)
651			break;
652	}
653
654	if (nbufs > 0) {
655		virtqueue_notify(vq);
656		error = 0;
657	}
658
659	return (error);
660}
661
662static void
663vtcon_ctrl_event_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	VTCON_LOCK(sc);
676	while ((control = virtqueue_drain(vq, &last)) != NULL)
677		free(control, M_DEVBUF);
678	VTCON_UNLOCK(sc);
679}
680
681static int
682vtcon_ctrl_init(struct vtcon_softc *sc)
683{
684	int error;
685
686	error = vtcon_ctrl_event_populate(sc);
687
688	return (error);
689}
690
691static void
692vtcon_ctrl_deinit(struct vtcon_softc *sc)
693{
694
695	vtcon_ctrl_event_drain(sc);
696}
697
698static void
699vtcon_ctrl_port_add_event(struct vtcon_softc *sc, int id)
700{
701	device_t dev;
702	int error;
703
704	dev = sc->vtcon_dev;
705
706	/* This single thread only way for ports to be created. */
707	if (sc->vtcon_ports[id].vcsp_port != NULL) {
708		device_printf(dev, "%s: adding port %d, but already exists\n",
709		    __func__, id);
710		return;
711	}
712
713	error = vtcon_port_create(sc, id);
714	if (error) {
715		device_printf(dev, "%s: cannot create port %d: %d\n",
716		    __func__, id, error);
717		vtcon_ctrl_send_control(sc, id, VIRTIO_CONSOLE_PORT_READY, 0);
718		return;
719	}
720}
721
722static void
723vtcon_ctrl_port_remove_event(struct vtcon_softc *sc, int id)
724{
725	device_t dev;
726	struct vtcon_softc_port *scport;
727	struct vtcon_port *port;
728
729	dev = sc->vtcon_dev;
730	scport = &sc->vtcon_ports[id];
731
732	VTCON_LOCK(sc);
733	port = scport->vcsp_port;
734	if (port == NULL) {
735		VTCON_UNLOCK(sc);
736		device_printf(dev, "%s: remove port %d, but does not exist\n",
737		    __func__, id);
738		return;
739	}
740
741	scport->vcsp_port = NULL;
742	VTCON_PORT_LOCK(port);
743	VTCON_UNLOCK(sc);
744	vtcon_port_teardown(port);
745}
746
747static void
748vtcon_ctrl_port_console_event(struct vtcon_softc *sc, int id)
749{
750	device_t dev;
751	struct vtcon_softc_port *scport;
752	struct vtcon_port *port;
753
754	dev = sc->vtcon_dev;
755	scport = &sc->vtcon_ports[id];
756
757	VTCON_LOCK(sc);
758	port = scport->vcsp_port;
759	if (port == NULL) {
760		VTCON_UNLOCK(sc);
761		device_printf(dev, "%s: console port %d, but does not exist\n",
762		    __func__, id);
763		return;
764	}
765
766	VTCON_PORT_LOCK(port);
767	VTCON_UNLOCK(sc);
768	port->vtcport_flags |= VTCON_PORT_FLAG_CONSOLE;
769	vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_OPEN, 1);
770	VTCON_PORT_UNLOCK(port);
771}
772
773static void
774vtcon_ctrl_port_open_event(struct vtcon_softc *sc, int id)
775{
776	device_t dev;
777	struct vtcon_softc_port *scport;
778	struct vtcon_port *port;
779
780	dev = sc->vtcon_dev;
781	scport = &sc->vtcon_ports[id];
782
783	VTCON_LOCK(sc);
784	port = scport->vcsp_port;
785	if (port == NULL) {
786		VTCON_UNLOCK(sc);
787		device_printf(dev, "%s: open port %d, but does not exist\n",
788		    __func__, id);
789		return;
790	}
791
792	VTCON_PORT_LOCK(port);
793	VTCON_UNLOCK(sc);
794	vtcon_port_enable_intr(port);
795	VTCON_PORT_UNLOCK(port);
796}
797
798static void
799vtcon_ctrl_process_event(struct vtcon_softc *sc,
800    struct virtio_console_control *control)
801{
802	device_t dev;
803	int id;
804
805	dev = sc->vtcon_dev;
806	id = control->id;
807
808	if (id < 0 || id >= sc->vtcon_max_ports) {
809		device_printf(dev, "%s: invalid port ID %d\n", __func__, id);
810		return;
811	}
812
813	switch (control->event) {
814	case VIRTIO_CONSOLE_PORT_ADD:
815		vtcon_ctrl_port_add_event(sc, id);
816		break;
817
818	case VIRTIO_CONSOLE_PORT_REMOVE:
819		vtcon_ctrl_port_remove_event(sc, id);
820		break;
821
822	case VIRTIO_CONSOLE_CONSOLE_PORT:
823		vtcon_ctrl_port_console_event(sc, id);
824		break;
825
826	case VIRTIO_CONSOLE_RESIZE:
827		break;
828
829	case VIRTIO_CONSOLE_PORT_OPEN:
830		vtcon_ctrl_port_open_event(sc, id);
831		break;
832
833	case VIRTIO_CONSOLE_PORT_NAME:
834		break;
835	}
836}
837
838static void
839vtcon_ctrl_task_cb(void *xsc, int pending)
840{
841	struct vtcon_softc *sc;
842	struct virtqueue *vq;
843	struct virtio_console_control *control;
844	int detached;
845
846	sc = xsc;
847	vq = sc->vtcon_ctrl_rxvq;
848
849	VTCON_LOCK(sc);
850
851	while ((detached = (sc->vtcon_flags & VTCON_FLAG_DETACHED)) == 0) {
852		control = virtqueue_dequeue(vq, NULL);
853		if (control == NULL)
854			break;
855
856		VTCON_UNLOCK(sc);
857		vtcon_ctrl_process_event(sc, control);
858		VTCON_LOCK(sc);
859		vtcon_ctrl_event_requeue(sc, control);
860	}
861
862	if (!detached) {
863		virtqueue_notify(vq);
864		if (virtqueue_enable_intr(vq) != 0)
865			taskqueue_enqueue(taskqueue_thread,
866			    &sc->vtcon_ctrl_task);
867	}
868
869	VTCON_UNLOCK(sc);
870}
871
872static void
873vtcon_ctrl_event_intr(void *xsc)
874{
875	struct vtcon_softc *sc;
876
877	sc = xsc;
878
879	/*
880	 * Only some events require us to potentially block, but it
881	 * easier to just defer all event handling to the taskqueue.
882	 */
883	taskqueue_enqueue(taskqueue_thread, &sc->vtcon_ctrl_task);
884}
885
886static void
887vtcon_ctrl_poll(struct vtcon_softc *sc,
888    struct virtio_console_control *control)
889{
890	struct sglist_seg segs[2];
891	struct sglist sg;
892	struct virtqueue *vq;
893	int error;
894
895	vq = sc->vtcon_ctrl_txvq;
896
897	sglist_init(&sg, 2, segs);
898	error = sglist_append(&sg, control,
899	    sizeof(struct virtio_console_control));
900	KASSERT(error == 0, ("%s: error %d adding control to sglist",
901	    __func__, error));
902
903	/*
904	 * We cannot use the softc lock to serialize access to this
905	 * virtqueue since this is called from the tty layer with the
906	 * port lock held. Acquiring the softc would violate our lock
907	 * ordering.
908	 */
909	VTCON_CTRL_TX_LOCK(sc);
910	KASSERT(virtqueue_empty(vq),
911	    ("%s: virtqueue is not emtpy", __func__));
912	error = virtqueue_enqueue(vq, control, &sg, sg.sg_nseg, 0);
913	if (error == 0) {
914		virtqueue_notify(vq);
915		virtqueue_poll(vq, NULL);
916	}
917	VTCON_CTRL_TX_UNLOCK(sc);
918}
919
920static void
921vtcon_ctrl_send_control(struct vtcon_softc *sc, uint32_t portid,
922    uint16_t event, uint16_t value)
923{
924	struct virtio_console_control control;
925
926	if ((sc->vtcon_flags & VTCON_FLAG_MULTIPORT) == 0)
927		return;
928
929	control.id = portid;
930	control.event = event;
931	control.value = value;
932
933	vtcon_ctrl_poll(sc, &control);
934}
935
936static int
937vtcon_port_enqueue_buf(struct vtcon_port *port, void *buf, size_t len)
938{
939	struct sglist_seg segs[2];
940	struct sglist sg;
941	struct virtqueue *vq;
942	int error;
943
944	vq = port->vtcport_invq;
945
946	sglist_init(&sg, 2, segs);
947	error = sglist_append(&sg, buf, len);
948	KASSERT(error == 0,
949	    ("%s: error %d adding buffer to sglist", __func__, error));
950
951	error = virtqueue_enqueue(vq, buf, &sg, 0, sg.sg_nseg);
952
953	return (error);
954}
955
956static int
957vtcon_port_create_buf(struct vtcon_port *port)
958{
959	void *buf;
960	int error;
961
962	buf = malloc(VTCON_BULK_BUFSZ, M_DEVBUF, M_ZERO | M_NOWAIT);
963	if (buf == NULL)
964		return (ENOMEM);
965
966	error = vtcon_port_enqueue_buf(port, buf, VTCON_BULK_BUFSZ);
967	if (error)
968		free(buf, M_DEVBUF);
969
970	return (error);
971}
972
973static void
974vtcon_port_requeue_buf(struct vtcon_port *port, void *buf)
975{
976	int error;
977
978	error = vtcon_port_enqueue_buf(port, buf, VTCON_BULK_BUFSZ);
979	KASSERT(error == 0,
980	    ("%s: cannot requeue input buffer %d", __func__, error));
981}
982
983static int
984vtcon_port_populate(struct vtcon_port *port)
985{
986	struct virtqueue *vq;
987	int nbufs, error;
988
989	vq = port->vtcport_invq;
990	error = ENOSPC;
991
992	for (nbufs = 0; !virtqueue_full(vq); nbufs++) {
993		error = vtcon_port_create_buf(port);
994		if (error)
995			break;
996	}
997
998	if (nbufs > 0) {
999		virtqueue_notify(vq);
1000		error = 0;
1001	}
1002
1003	return (error);
1004}
1005
1006static void
1007vtcon_port_destroy(struct vtcon_port *port)
1008{
1009
1010	port->vtcport_sc = NULL;
1011	port->vtcport_scport = NULL;
1012	port->vtcport_invq = NULL;
1013	port->vtcport_outvq = NULL;
1014	port->vtcport_id = -1;
1015	mtx_destroy(&port->vtcport_mtx);
1016	free(port, M_DEVBUF);
1017}
1018
1019static int
1020vtcon_port_init_vqs(struct vtcon_port *port)
1021{
1022	struct vtcon_softc_port *scport;
1023	int error;
1024
1025	scport = port->vtcport_scport;
1026
1027	port->vtcport_invq = scport->vcsp_invq;
1028	port->vtcport_outvq = scport->vcsp_outvq;
1029
1030	/*
1031	 * Free any data left over from when this virtqueue was in use by a
1032	 * prior port. We have not yet notified the host that the port is
1033	 * ready, so assume nothing in the virtqueue can be for us.
1034	 */
1035	vtcon_port_drain(port);
1036
1037	KASSERT(virtqueue_empty(port->vtcport_invq),
1038	    ("%s: in virtqueue is not empty", __func__));
1039	KASSERT(virtqueue_empty(port->vtcport_outvq),
1040	    ("%s: out virtqueue is not empty", __func__));
1041
1042	error = vtcon_port_populate(port);
1043	if (error)
1044		return (error);
1045
1046	return (0);
1047}
1048
1049static int
1050vtcon_port_create(struct vtcon_softc *sc, int id)
1051{
1052	device_t dev;
1053	struct vtcon_softc_port *scport;
1054	struct vtcon_port *port;
1055	int error;
1056
1057	dev = sc->vtcon_dev;
1058	scport = &sc->vtcon_ports[id];
1059
1060	VTCON_ASSERT_VALID_PORTID(sc, id);
1061	MPASS(scport->vcsp_port == NULL);
1062
1063	port = malloc(sizeof(struct vtcon_port), M_DEVBUF, M_NOWAIT | M_ZERO);
1064	if (port == NULL)
1065		return (ENOMEM);
1066
1067	port->vtcport_sc = sc;
1068	port->vtcport_scport = scport;
1069	port->vtcport_id = id;
1070	mtx_init(&port->vtcport_mtx, "vtcpmtx", NULL, MTX_DEF);
1071	port->vtcport_tty = tty_alloc_mutex(&vtcon_tty_class, port,
1072	    &port->vtcport_mtx);
1073
1074	error = vtcon_port_init_vqs(port);
1075	if (error) {
1076		VTCON_PORT_LOCK(port);
1077		vtcon_port_teardown(port);
1078		return (error);
1079	}
1080
1081	VTCON_LOCK(sc);
1082	VTCON_PORT_LOCK(port);
1083	scport->vcsp_port = port;
1084	vtcon_port_enable_intr(port);
1085	vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_READY, 1);
1086	VTCON_PORT_UNLOCK(port);
1087	VTCON_UNLOCK(sc);
1088
1089	tty_makedev(port->vtcport_tty, NULL, "%s%r.%r", VTCON_TTY_PREFIX,
1090	    device_get_unit(dev), id);
1091
1092	return (0);
1093}
1094
1095static void
1096vtcon_port_drain_bufs(struct virtqueue *vq)
1097{
1098	void *buf;
1099	int last;
1100
1101	last = 0;
1102
1103	while ((buf = virtqueue_drain(vq, &last)) != NULL)
1104		free(buf, M_DEVBUF);
1105}
1106
1107static void
1108vtcon_port_drain(struct vtcon_port *port)
1109{
1110
1111	vtcon_port_drain_bufs(port->vtcport_invq);
1112}
1113
1114static void
1115vtcon_port_teardown(struct vtcon_port *port)
1116{
1117	struct tty *tp;
1118
1119	tp = port->vtcport_tty;
1120
1121	port->vtcport_flags |= VTCON_PORT_FLAG_GONE;
1122
1123	if (tp != NULL) {
1124		atomic_add_int(&vtcon_pending_free, 1);
1125		tty_rel_gone(tp);
1126	} else
1127		vtcon_port_destroy(port);
1128}
1129
1130static void
1131vtcon_port_change_size(struct vtcon_port *port, uint16_t cols, uint16_t rows)
1132{
1133	struct tty *tp;
1134	struct winsize sz;
1135
1136	tp = port->vtcport_tty;
1137
1138	if (tp == NULL)
1139		return;
1140
1141	bzero(&sz, sizeof(struct winsize));
1142	sz.ws_col = cols;
1143	sz.ws_row = rows;
1144
1145	tty_set_winsize(tp, &sz);
1146}
1147
1148static void
1149vtcon_port_update_console_size(struct vtcon_softc *sc)
1150{
1151	struct vtcon_port *port;
1152	struct vtcon_softc_port *scport;
1153	uint16_t cols, rows;
1154
1155	vtcon_get_console_size(sc, &cols, &rows);
1156
1157	/*
1158	 * For now, assume the first (only) port is the console. Note
1159	 * QEMU does not implement this feature yet.
1160	 */
1161	scport = &sc->vtcon_ports[0];
1162
1163	VTCON_LOCK(sc);
1164	port = scport->vcsp_port;
1165
1166	if (port != NULL) {
1167		VTCON_PORT_LOCK(port);
1168		VTCON_UNLOCK(sc);
1169		vtcon_port_change_size(port, cols, rows);
1170		VTCON_PORT_UNLOCK(port);
1171	} else
1172		VTCON_UNLOCK(sc);
1173}
1174
1175static void
1176vtcon_port_enable_intr(struct vtcon_port *port)
1177{
1178
1179	/*
1180	 * NOTE: The out virtqueue is always polled, so its interupt
1181	 * kept disabled.
1182	 */
1183	virtqueue_enable_intr(port->vtcport_invq);
1184}
1185
1186static void
1187vtcon_port_disable_intr(struct vtcon_port *port)
1188{
1189
1190	if (port->vtcport_invq != NULL)
1191		virtqueue_disable_intr(port->vtcport_invq);
1192	if (port->vtcport_outvq != NULL)
1193		virtqueue_disable_intr(port->vtcport_outvq);
1194}
1195
1196static void
1197vtcon_port_in(struct vtcon_port *port)
1198{
1199	struct virtqueue *vq;
1200	struct tty *tp;
1201	char *buf;
1202	uint32_t len;
1203	int i, deq;
1204
1205	tp = port->vtcport_tty;
1206	vq = port->vtcport_invq;
1207
1208again:
1209	deq = 0;
1210
1211	while ((buf = virtqueue_dequeue(vq, &len)) != NULL) {
1212		for (i = 0; i < len; i++) {
1213#if defined(KDB)
1214			if (port->vtcport_flags & VTCON_PORT_FLAG_CONSOLE)
1215				kdb_alt_break(buf[i],
1216				    &port->vtcport_alt_break_state);
1217#endif
1218			ttydisc_rint(tp, buf[i], 0);
1219		}
1220		vtcon_port_requeue_buf(port, buf);
1221		deq++;
1222	}
1223	ttydisc_rint_done(tp);
1224
1225	if (deq > 0)
1226		virtqueue_notify(vq);
1227
1228	if (virtqueue_enable_intr(vq) != 0)
1229		goto again;
1230}
1231
1232static void
1233vtcon_port_intr(void *scportx)
1234{
1235	struct vtcon_softc_port *scport;
1236	struct vtcon_softc *sc;
1237	struct vtcon_port *port;
1238
1239	scport = scportx;
1240	sc = scport->vcsp_sc;
1241
1242	VTCON_LOCK(sc);
1243	port = scport->vcsp_port;
1244	if (port == NULL) {
1245		VTCON_UNLOCK(sc);
1246		return;
1247	}
1248	VTCON_PORT_LOCK(port);
1249	VTCON_UNLOCK(sc);
1250	if ((port->vtcport_flags & VTCON_PORT_FLAG_GONE) == 0)
1251		vtcon_port_in(port);
1252	VTCON_PORT_UNLOCK(port);
1253}
1254
1255static void
1256vtcon_port_out(struct vtcon_port *port, void *buf, int bufsize)
1257{
1258	struct sglist_seg segs[2];
1259	struct sglist sg;
1260	struct virtqueue *vq;
1261	int error;
1262
1263	vq = port->vtcport_outvq;
1264	KASSERT(virtqueue_empty(vq),
1265	    ("%s: port %p out virtqueue not emtpy", __func__, port));
1266
1267	sglist_init(&sg, 2, segs);
1268	error = sglist_append(&sg, buf, bufsize);
1269	KASSERT(error == 0, ("%s: error %d adding buffer to sglist",
1270	    __func__, error));
1271
1272	error = virtqueue_enqueue(vq, buf, &sg, sg.sg_nseg, 0);
1273	if (error == 0) {
1274		virtqueue_notify(vq);
1275		virtqueue_poll(vq, NULL);
1276	}
1277}
1278
1279static void
1280vtcon_port_submit_event(struct vtcon_port *port, uint16_t event,
1281    uint16_t value)
1282{
1283	struct vtcon_softc *sc;
1284
1285	sc = port->vtcport_sc;
1286
1287	vtcon_ctrl_send_control(sc, port->vtcport_id, event, value);
1288}
1289
1290static int
1291vtcon_tty_open(struct tty *tp)
1292{
1293	struct vtcon_port *port;
1294
1295	port = tty_softc(tp);
1296
1297	if (port->vtcport_flags & VTCON_PORT_FLAG_GONE)
1298		return (ENXIO);
1299
1300	vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_OPEN, 1);
1301
1302	return (0);
1303}
1304
1305static void
1306vtcon_tty_close(struct tty *tp)
1307{
1308	struct vtcon_port *port;
1309
1310	port = tty_softc(tp);
1311
1312	if (port->vtcport_flags & VTCON_PORT_FLAG_GONE)
1313		return;
1314
1315	vtcon_port_submit_event(port, VIRTIO_CONSOLE_PORT_OPEN, 0);
1316}
1317
1318static void
1319vtcon_tty_outwakeup(struct tty *tp)
1320{
1321	struct vtcon_port *port;
1322	char buf[VTCON_BULK_BUFSZ];
1323	int len;
1324
1325	port = tty_softc(tp);
1326
1327	if (port->vtcport_flags & VTCON_PORT_FLAG_GONE)
1328		return;
1329
1330	while ((len = ttydisc_getc(tp, buf, sizeof(buf))) != 0)
1331		vtcon_port_out(port, buf, len);
1332}
1333
1334static void
1335vtcon_tty_free(void *xport)
1336{
1337	struct vtcon_port *port;
1338
1339	port = xport;
1340
1341	vtcon_port_destroy(port);
1342	atomic_subtract_int(&vtcon_pending_free, 1);
1343}
1344
1345static void
1346vtcon_get_console_size(struct vtcon_softc *sc, uint16_t *cols, uint16_t *rows)
1347{
1348	struct virtio_console_config concfg;
1349
1350	KASSERT(sc->vtcon_flags & VTCON_FLAG_SIZE,
1351	    ("%s: size feature not negotiated", __func__));
1352
1353	vtcon_read_config(sc, &concfg);
1354
1355	*cols = concfg.cols;
1356	*rows = concfg.rows;
1357}
1358
1359static void
1360vtcon_enable_interrupts(struct vtcon_softc *sc)
1361{
1362	struct vtcon_softc_port *scport;
1363	struct vtcon_port *port;
1364	int i;
1365
1366	VTCON_LOCK(sc);
1367
1368	if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT)
1369		virtqueue_enable_intr(sc->vtcon_ctrl_rxvq);
1370
1371	for (i = 0; i < sc->vtcon_max_ports; i++) {
1372		scport = &sc->vtcon_ports[i];
1373
1374		port = scport->vcsp_port;
1375		if (port == NULL)
1376			continue;
1377
1378		VTCON_PORT_LOCK(port);
1379		vtcon_port_enable_intr(port);
1380		VTCON_PORT_UNLOCK(port);
1381	}
1382
1383	VTCON_UNLOCK(sc);
1384}
1385
1386static void
1387vtcon_disable_interrupts(struct vtcon_softc *sc)
1388{
1389	struct vtcon_softc_port *scport;
1390	struct vtcon_port *port;
1391	int i;
1392
1393	VTCON_LOCK_ASSERT(sc);
1394
1395	if (sc->vtcon_flags & VTCON_FLAG_MULTIPORT)
1396		virtqueue_disable_intr(sc->vtcon_ctrl_rxvq);
1397
1398	for (i = 0; i < sc->vtcon_max_ports; i++) {
1399		scport = &sc->vtcon_ports[i];
1400
1401		port = scport->vcsp_port;
1402		if (port == NULL)
1403			continue;
1404
1405		VTCON_PORT_LOCK(port);
1406		vtcon_port_disable_intr(port);
1407		VTCON_PORT_UNLOCK(port);
1408	}
1409}
1410