Home | History | Annotate | Line # | Download | only in virtio
viocon.c revision 1.8
      1 /*	$NetBSD: viocon.c,v 1.8 2023/03/23 03:55:11 yamaguchi Exp $	*/
      2 /*	$OpenBSD: viocon.c,v 1.8 2021/11/05 11:38:29 mpi Exp $	*/
      3 
      4 /*
      5  * Copyright (c) 2013-2015 Stefan Fritsch <sf (at) sfritsch.de>
      6  *
      7  * Permission to use, copy, modify, and distribute this software for any
      8  * purpose with or without fee is hereby granted, provided that the above
      9  * copyright notice and this permission notice appear in all copies.
     10  *
     11  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     12  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     13  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     14  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     15  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     16  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     17  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     18  */
     19 
     20 #include <sys/cdefs.h>
     21 __KERNEL_RCSID(0, "$NetBSD: viocon.c,v 1.8 2023/03/23 03:55:11 yamaguchi Exp $");
     22 
     23 #include <sys/param.h>
     24 #include <sys/types.h>
     25 
     26 #include <sys/bus.h>
     27 #include <sys/conf.h>
     28 #include <sys/device.h>
     29 #include <sys/kauth.h>
     30 #include <sys/kernel.h>
     31 #include <sys/kmem.h>
     32 #include <sys/lwp.h>
     33 #include <sys/systm.h>
     34 #include <sys/tty.h>
     35 
     36 #include <dev/pci/virtioreg.h>
     37 #include <dev/pci/virtiovar.h>
     38 
     39 #include "ioconf.h"
     40 
     41 /* OpenBSD compat shims */
     42 #define	ttymalloc(speed)	tty_alloc()
     43 #define	splassert(ipl)		__nothing
     44 #define	virtio_notify(vsc, vq)	virtio_enqueue_commit(vsc, vq, -1, true)
     45 #define	ttwakeupwr(tp)		__nothing
     46 
     47 /* features */
     48 #define	VIRTIO_CONSOLE_F_SIZE		(1ULL<<0)
     49 #define	VIRTIO_CONSOLE_F_MULTIPORT	(1ULL<<1)
     50 #define	VIRTIO_CONSOLE_F_EMERG_WRITE 	(1ULL<<2)
     51 
     52 /* config space */
     53 #define VIRTIO_CONSOLE_COLS		0	/* 16 bits */
     54 #define VIRTIO_CONSOLE_ROWS		2	/* 16 bits */
     55 #define VIRTIO_CONSOLE_MAX_NR_PORTS	4	/* 32 bits */
     56 #define VIRTIO_CONSOLE_EMERG_WR		8	/* 32 bits */
     57 
     58 #define VIOCON_DEBUG	0
     59 
     60 #if VIOCON_DEBUG
     61 #define DPRINTF(x...) printf(x)
     62 #else
     63 #define DPRINTF(x...)
     64 #endif
     65 
     66 #define	VIRTIO_CONSOLE_FLAG_BITS					      \
     67 	VIRTIO_COMMON_FLAG_BITS						      \
     68 	"b\x00" "SIZE\0"						      \
     69 	"b\x01" "MULTIPORT\0"						      \
     70 	"b\x02" "EMERG_WRITE\0"
     71 
     72 struct virtio_console_control {
     73 	uint32_t id;	/* Port number */
     74 
     75 #define	VIRTIO_CONSOLE_DEVICE_READY	0
     76 #define	VIRTIO_CONSOLE_PORT_ADD		1
     77 #define	VIRTIO_CONSOLE_PORT_REMOVE	2
     78 #define	VIRTIO_CONSOLE_PORT_READY	3
     79 #define	VIRTIO_CONSOLE_CONSOLE_PORT	4
     80 #define	VIRTIO_CONSOLE_RESIZE		5
     81 #define	VIRTIO_CONSOLE_PORT_OPEN	6
     82 #define	VIRTIO_CONSOLE_PORT_NAME	7
     83 	uint16_t event;
     84 
     85 	uint16_t value;
     86 };
     87 
     88 struct virtio_console_control_resize {
     89 	/* yes, the order is different than in config space */
     90 	uint16_t rows;
     91 	uint16_t cols;
     92 };
     93 
     94 #define	BUFSIZE		128
     95 
     96 #define	VIOCONDEV(u,p)	makedev(cdevsw_lookup_major(&viocon_cdevsw),	      \
     97 			    ((u) << 4) | (p))
     98 #define VIOCONUNIT(x)	(minor(x) >> 4)
     99 #define VIOCONPORT(x)	(minor(x) & 0x0f)
    100 
    101 struct viocon_port {
    102 	struct viocon_softc	*vp_sc;
    103 	struct virtqueue	*vp_rx;
    104 	struct virtqueue	*vp_tx;
    105 	void			*vp_si;
    106 	struct tty		*vp_tty;
    107 	const char 		*vp_name;
    108 	bus_dma_segment_t	 vp_dmaseg;
    109 	bus_dmamap_t		 vp_dmamap;
    110 #ifdef NOTYET
    111 	unsigned int		 vp_host_open:1;	/* XXX needs F_MULTIPORT */
    112 	unsigned int		 vp_guest_open:1;	/* XXX needs F_MULTIPORT */
    113 	unsigned int		 vp_is_console:1;	/* XXX needs F_MULTIPORT */
    114 #endif
    115 	unsigned int		 vp_iflow:1;		/* rx flow control */
    116 	uint16_t		 vp_rows;
    117 	uint16_t		 vp_cols;
    118 	u_char			*vp_rx_buf;
    119 	u_char			*vp_tx_buf;
    120 };
    121 
    122 struct viocon_softc {
    123 	struct device		*sc_dev;
    124 	struct virtio_softc	*sc_virtio;
    125 	struct virtqueue	*sc_vqs;
    126 #define VIOCON_PORT_RX	0
    127 #define VIOCON_PORT_TX	1
    128 #define VIOCON_PORT_NQS	2
    129 
    130 	struct virtqueue        *sc_c_vq_rx;
    131 	struct virtqueue        *sc_c_vq_tx;
    132 
    133 	unsigned int		 sc_max_ports;
    134 	struct viocon_port	**sc_ports;
    135 };
    136 
    137 int	viocon_match(struct device *, struct cfdata *, void *);
    138 void	viocon_attach(struct device *, struct device *, void *);
    139 int	viocon_tx_intr(struct virtqueue *);
    140 int	viocon_tx_drain(struct viocon_port *, struct virtqueue *vq);
    141 int	viocon_rx_intr(struct virtqueue *);
    142 void	viocon_rx_soft(void *);
    143 void	viocon_rx_fill(struct viocon_port *);
    144 int	viocon_port_create(struct viocon_softc *, int);
    145 void	vioconstart(struct tty *);
    146 int	vioconhwiflow(struct tty *, int);
    147 int	vioconparam(struct tty *, struct termios *);
    148 int	vioconopen(dev_t, int, int, struct lwp *);
    149 int	vioconclose(dev_t, int, int, struct lwp *);
    150 int	vioconread(dev_t, struct uio *, int);
    151 int	vioconwrite(dev_t, struct uio *, int);
    152 void	vioconstop(struct tty *, int);
    153 int	vioconioctl(dev_t, u_long, void *, int, struct lwp *);
    154 struct tty	*viocontty(dev_t dev);
    155 
    156 CFATTACH_DECL_NEW(viocon, sizeof(struct viocon_softc),
    157     viocon_match, viocon_attach, /*detach*/NULL, /*activate*/NULL);
    158 
    159 const struct cdevsw viocon_cdevsw = {
    160 	.d_open = vioconopen,
    161 	.d_close = vioconclose,
    162 	.d_read = vioconread,
    163 	.d_write = vioconwrite,
    164 	.d_ioctl = vioconioctl,
    165 	.d_stop = vioconstop,
    166 	.d_tty = viocontty,
    167 	.d_poll = nopoll,	/* XXX */
    168 	.d_mmap = nommap,
    169 	.d_kqfilter = ttykqfilter,
    170 	.d_discard = nodiscard,
    171 	.d_flag = D_TTY,
    172 };
    173 
    174 static inline struct viocon_softc *
    175 dev2sc(dev_t dev)
    176 {
    177 	return device_lookup_private(&viocon_cd, VIOCONUNIT(dev));
    178 }
    179 
    180 static inline struct viocon_port *
    181 dev2port(dev_t dev)
    182 {
    183 	return dev2sc(dev)->sc_ports[VIOCONPORT(dev)];
    184 }
    185 
    186 int viocon_match(struct device *parent, struct cfdata *match, void *aux)
    187 {
    188 	struct virtio_attach_args *va = aux;
    189 	if (va->sc_childdevid == VIRTIO_DEVICE_ID_CONSOLE)
    190 		return 1;
    191 	return 0;
    192 }
    193 
    194 void
    195 viocon_attach(struct device *parent, struct device *self, void *aux)
    196 {
    197 	struct viocon_softc *sc = device_private(self);
    198 	struct virtio_softc *vsc = device_private(parent);
    199 	int maxports = 1;
    200 	size_t nvqs;
    201 
    202 	sc->sc_dev = self;
    203 	if (virtio_child(vsc) != NULL) {
    204 		aprint_error(": parent %s already has a child\n",
    205 		    device_xname(parent));
    206 		return;
    207 	}
    208 	sc->sc_virtio = vsc;
    209 	sc->sc_max_ports = maxports;
    210 	nvqs = VIOCON_PORT_NQS * maxports;
    211 
    212 	sc->sc_vqs = kmem_zalloc(nvqs * sizeof(sc->sc_vqs[0]),
    213 	    KM_SLEEP);
    214 	sc->sc_ports = kmem_zalloc(maxports * sizeof(sc->sc_ports[0]),
    215 	    KM_SLEEP);
    216 
    217 	virtio_child_attach_start(vsc, self, IPL_TTY,
    218 	    /*req_features*/VIRTIO_CONSOLE_F_SIZE, VIRTIO_CONSOLE_FLAG_BITS);
    219 
    220 	DPRINTF("%s: softc: %p\n", __func__, sc);
    221 	if (viocon_port_create(sc, 0) != 0) {
    222 		printf("\n%s: viocon_port_create failed\n", __func__);
    223 		goto err;
    224 	}
    225 	viocon_rx_fill(sc->sc_ports[0]);
    226 
    227 	if (virtio_child_attach_finish(vsc, sc->sc_vqs, nvqs,
    228 	    /*config_change*/NULL, /*req_flags*/0) != 0)
    229 		goto err;
    230 
    231 	return;
    232 err:
    233 	kmem_free(sc->sc_vqs, nvqs * sizeof(sc->sc_vqs[0]));
    234 	kmem_free(sc->sc_ports, maxports * sizeof(sc->sc_ports[0]));
    235 	virtio_child_attach_failed(vsc);
    236 }
    237 
    238 int
    239 viocon_port_create(struct viocon_softc *sc, int portidx)
    240 {
    241 	struct virtio_softc *vsc = sc->sc_virtio;
    242 	int rxidx, txidx, allocsize, nsegs;
    243 	char name[6];
    244 	struct viocon_port *vp;
    245 	void *kva;
    246 	struct tty *tp;
    247 
    248 	vp = kmem_zalloc(sizeof(*vp), KM_SLEEP);
    249 	if (vp == NULL)
    250 		return ENOMEM;
    251 	sc->sc_ports[portidx] = vp;
    252 	vp->vp_sc = sc;
    253 	DPRINTF("%s: vp: %p\n", __func__, vp);
    254 
    255 	rxidx = (portidx * VIOCON_PORT_NQS) + VIOCON_PORT_RX;
    256 	txidx = (portidx * VIOCON_PORT_NQS) + VIOCON_PORT_TX;
    257 
    258 	snprintf(name, sizeof(name), "p%drx", portidx);
    259 	virtio_init_vq_vqdone(vsc, &sc->sc_vqs[rxidx], rxidx,
    260 	    viocon_rx_intr);
    261 	if (virtio_alloc_vq(vsc, &sc->sc_vqs[rxidx], BUFSIZE, 1,
    262 	    name) != 0) {
    263 		printf("\nCan't alloc %s virtqueue\n", name);
    264 		goto err;
    265 	}
    266 	vp->vp_rx = &sc->sc_vqs[rxidx];
    267 	vp->vp_si = softint_establish(SOFTINT_SERIAL, viocon_rx_soft, vp);
    268 	DPRINTF("%s: rx: %p\n", __func__, vp->vp_rx);
    269 
    270 	snprintf(name, sizeof(name), "p%dtx", portidx);
    271 	virtio_init_vq_vqdone(vsc, &sc->sc_vqs[txidx], txidx,
    272 	    viocon_tx_intr);
    273 	if (virtio_alloc_vq(vsc, &sc->sc_vqs[txidx], BUFSIZE, 1,
    274 	    name) != 0) {
    275 		printf("\nCan't alloc %s virtqueue\n", name);
    276 		goto err;
    277 	}
    278 	vp->vp_tx = &sc->sc_vqs[txidx];
    279 	DPRINTF("%s: tx: %p\n", __func__, vp->vp_tx);
    280 
    281 	allocsize = (vp->vp_rx->vq_num + vp->vp_tx->vq_num) * BUFSIZE;
    282 
    283 	if (bus_dmamap_create(virtio_dmat(vsc), allocsize, 1, allocsize, 0,
    284 	    BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &vp->vp_dmamap) != 0)
    285 		goto err;
    286 	if (bus_dmamem_alloc(virtio_dmat(vsc), allocsize, 8, 0, &vp->vp_dmaseg,
    287 	    1, &nsegs, BUS_DMA_NOWAIT) != 0)
    288 		goto err;
    289 	if (bus_dmamem_map(virtio_dmat(vsc), &vp->vp_dmaseg, nsegs,
    290 	    allocsize, &kva, BUS_DMA_NOWAIT) != 0)
    291 		goto err;
    292 	memset(kva, 0, allocsize);
    293 	if (bus_dmamap_load(virtio_dmat(vsc), vp->vp_dmamap, kva,
    294 	    allocsize, NULL, BUS_DMA_NOWAIT) != 0)
    295 		goto err;
    296 	vp->vp_rx_buf = (unsigned char *)kva;
    297 	/*
    298 	 * XXX use only a small circular tx buffer instead of many BUFSIZE buffers?
    299 	 */
    300 	vp->vp_tx_buf = vp->vp_rx_buf + vp->vp_rx->vq_num * BUFSIZE;
    301 
    302 	if (virtio_features(vsc) & VIRTIO_CONSOLE_F_SIZE) {
    303 		vp->vp_cols = virtio_read_device_config_2(vsc,
    304 		    VIRTIO_CONSOLE_COLS);
    305 		vp->vp_rows = virtio_read_device_config_2(vsc,
    306 		    VIRTIO_CONSOLE_ROWS);
    307 	}
    308 
    309 	tp = ttymalloc(1000000);
    310 	tp->t_oproc = vioconstart;
    311 	tp->t_param = vioconparam;
    312 	tp->t_hwiflow = vioconhwiflow;
    313 	tp->t_dev = VIOCONDEV(device_unit(sc->sc_dev), portidx);
    314 	vp->vp_tty = tp;
    315 	DPRINTF("%s: tty: %p\n", __func__, tp);
    316 
    317 	virtio_start_vq_intr(vsc, vp->vp_rx);
    318 	virtio_start_vq_intr(vsc, vp->vp_tx);
    319 
    320 	return 0;
    321 err:
    322 	panic("%s failed", __func__);
    323 	return -1;
    324 }
    325 
    326 int
    327 viocon_tx_drain(struct viocon_port *vp, struct virtqueue *vq)
    328 {
    329 	struct virtio_softc *vsc = vq->vq_owner;
    330 	int ndone = 0, len, slot;
    331 
    332 	splassert(IPL_TTY);
    333 	while (virtio_dequeue(vsc, vq, &slot, &len) == 0) {
    334 		bus_dmamap_sync(virtio_dmat(vsc), vp->vp_dmamap,
    335 		    vp->vp_tx_buf - vp->vp_rx_buf + slot * BUFSIZE, BUFSIZE,
    336 		    BUS_DMASYNC_POSTWRITE);
    337 		virtio_dequeue_commit(vsc, vq, slot);
    338 		ndone++;
    339 	}
    340 	return ndone;
    341 }
    342 
    343 int
    344 viocon_tx_intr(struct virtqueue *vq)
    345 {
    346 	struct virtio_softc *vsc = vq->vq_owner;
    347 	struct viocon_softc *sc = device_private(virtio_child(vsc));
    348 	int ndone = 0;
    349 	int portidx = (vq->vq_index - 1) / 2;
    350 	struct viocon_port *vp = sc->sc_ports[portidx];
    351 	struct tty *tp = vp->vp_tty;
    352 
    353 	splassert(IPL_TTY);
    354 	ndone = viocon_tx_drain(vp, vq);
    355 	if (ndone && ISSET(tp->t_state, TS_BUSY)) {
    356 		CLR(tp->t_state, TS_BUSY);
    357 		(*tp->t_linesw->l_start)(tp);
    358 	}
    359 
    360 	return 1;
    361 }
    362 
    363 void
    364 viocon_rx_fill(struct viocon_port *vp)
    365 {
    366 	struct virtqueue *vq = vp->vp_rx;
    367 	struct virtio_softc *vsc = vp->vp_sc->sc_virtio;
    368 	int r, slot, ndone = 0;
    369 
    370 	while ((r = virtio_enqueue_prep(vsc, vq, &slot)) == 0) {
    371 		if (virtio_enqueue_reserve(vsc, vq, slot, 1) != 0)
    372 			break;
    373 		bus_dmamap_sync(virtio_dmat(vsc), vp->vp_dmamap, slot * BUFSIZE,
    374 		    BUFSIZE, BUS_DMASYNC_PREREAD);
    375 		virtio_enqueue_p(vsc, vq, slot, vp->vp_dmamap, slot * BUFSIZE,
    376 		    BUFSIZE, 0);
    377 		virtio_enqueue_commit(vsc, vq, slot, 0);
    378 		ndone++;
    379 	}
    380 	KASSERT(r == 0 || r == EAGAIN);
    381 	if (ndone > 0)
    382 		virtio_notify(vsc, vq);
    383 }
    384 
    385 int
    386 viocon_rx_intr(struct virtqueue *vq)
    387 {
    388 	struct virtio_softc *vsc = vq->vq_owner;
    389 	struct viocon_softc *sc = device_private(virtio_child(vsc));
    390 	int portidx = (vq->vq_index - 1) / 2;
    391 	struct viocon_port *vp = sc->sc_ports[portidx];
    392 
    393 	softint_schedule(vp->vp_si);
    394 	return 1;
    395 }
    396 
    397 void
    398 viocon_rx_soft(void *arg)
    399 {
    400 	struct viocon_port *vp = arg;
    401 	struct virtqueue *vq = vp->vp_rx;
    402 	struct virtio_softc *vsc = vq->vq_owner;
    403 	struct tty *tp = vp->vp_tty;
    404 	int slot, len, i;
    405 	u_char *p;
    406 
    407 	while (!vp->vp_iflow && virtio_dequeue(vsc, vq, &slot, &len) == 0) {
    408 		bus_dmamap_sync(virtio_dmat(vsc), vp->vp_dmamap,
    409 		    slot * BUFSIZE, BUFSIZE, BUS_DMASYNC_POSTREAD);
    410 		p = vp->vp_rx_buf + slot * BUFSIZE;
    411 		for (i = 0; i < len; i++)
    412 			(*tp->t_linesw->l_rint)(*p++, tp);
    413 		virtio_dequeue_commit(vsc, vq, slot);
    414 	}
    415 
    416 	viocon_rx_fill(vp);
    417 
    418 	return;
    419 }
    420 
    421 void
    422 vioconstart(struct tty *tp)
    423 {
    424 	struct viocon_softc *sc = dev2sc(tp->t_dev);
    425 	struct virtio_softc *vsc;
    426 	struct viocon_port *vp = dev2port(tp->t_dev);
    427 	struct virtqueue *vq;
    428 	u_char *buf;
    429 	int s, cnt, slot, ret, ndone;
    430 
    431 	vsc = sc->sc_virtio;
    432 	vq = vp->vp_tx;
    433 
    434 	s = spltty();
    435 
    436 	ndone = viocon_tx_drain(vp, vq);
    437 	if (ISSET(tp->t_state, TS_BUSY)) {
    438 		if (ndone > 0)
    439 			CLR(tp->t_state, TS_BUSY);
    440 		else
    441 			goto out;
    442 	}
    443 	if (ISSET(tp->t_state, TS_TIMEOUT | TS_TTSTOP))
    444 		goto out;
    445 
    446 	if (tp->t_outq.c_cc == 0)
    447 		goto out;
    448 	ndone = 0;
    449 
    450 	while (tp->t_outq.c_cc > 0) {
    451 		ret = virtio_enqueue_prep(vsc, vq, &slot);
    452 		if (ret == EAGAIN) {
    453 			SET(tp->t_state, TS_BUSY);
    454 			break;
    455 		}
    456 		KASSERT(ret == 0);
    457 		ret = virtio_enqueue_reserve(vsc, vq, slot, 1);
    458 		KASSERT(ret == 0);
    459 		buf = vp->vp_tx_buf + slot * BUFSIZE;
    460 		cnt = q_to_b(&tp->t_outq, buf, BUFSIZE);
    461 		bus_dmamap_sync(virtio_dmat(vsc), vp->vp_dmamap,
    462 		    vp->vp_tx_buf - vp->vp_rx_buf + slot * BUFSIZE, cnt,
    463 		    BUS_DMASYNC_PREWRITE);
    464 		virtio_enqueue_p(vsc, vq, slot, vp->vp_dmamap,
    465 		    vp->vp_tx_buf - vp->vp_rx_buf + slot * BUFSIZE, cnt, 1);
    466 		virtio_enqueue_commit(vsc, vq, slot, 0);
    467 		ndone++;
    468 	}
    469 	if (ndone > 0)
    470 		virtio_notify(vsc, vq);
    471 	ttwakeupwr(tp);
    472 out:
    473 	splx(s);
    474 }
    475 
    476 int
    477 vioconhwiflow(struct tty *tp, int stop)
    478 {
    479 	struct viocon_port *vp = dev2port(tp->t_dev);
    480 	int s;
    481 
    482 	s = spltty();
    483 	vp->vp_iflow = stop;
    484 	if (stop) {
    485 		virtio_stop_vq_intr(vp->vp_sc->sc_virtio, vp->vp_rx);
    486 	} else {
    487 		virtio_start_vq_intr(vp->vp_sc->sc_virtio, vp->vp_rx);
    488 		softint_schedule(vp->vp_si);
    489 	}
    490 	splx(s);
    491 	return 1;
    492 }
    493 
    494 int
    495 vioconparam(struct tty *tp, struct termios *t)
    496 {
    497 	tp->t_ispeed = t->c_ispeed;
    498 	tp->t_ospeed = t->c_ospeed;
    499 	tp->t_cflag = t->c_cflag;
    500 
    501 	vioconstart(tp);
    502 	return 0;
    503 }
    504 
    505 int
    506 vioconopen(dev_t dev, int flag, int mode, struct lwp *l)
    507 {
    508 	int unit = VIOCONUNIT(dev);
    509 	int port = VIOCONPORT(dev);
    510 	struct viocon_softc *sc;
    511 	struct viocon_port *vp;
    512 	struct tty *tp;
    513 	int s, error;
    514 
    515 	sc = device_lookup_private(&viocon_cd, unit);
    516 	if (sc == NULL)
    517 		return (ENXIO);
    518 	if (!device_is_active(sc->sc_dev))
    519 		return (ENXIO);
    520 
    521 	s = spltty();
    522 	if (port >= sc->sc_max_ports) {
    523 		splx(s);
    524 		return (ENXIO);
    525 	}
    526 	vp = sc->sc_ports[port];
    527 	tp = vp->vp_tty;
    528 #ifdef NOTYET
    529 	vp->vp_guest_open = 1;
    530 #endif
    531 	splx(s);
    532 
    533 	if (kauth_authorize_device_tty(l->l_cred, KAUTH_DEVICE_TTY_OPEN, tp))
    534 		return (EBUSY);
    535 
    536 	s = spltty();
    537 	if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
    538 		ttychars(tp);
    539 		tp->t_ispeed = 1000000;
    540 		tp->t_ospeed = 1000000;
    541 		tp->t_cflag = TTYDEF_CFLAG|CLOCAL|CRTSCTS;
    542 		tp->t_iflag = TTYDEF_IFLAG;
    543 		tp->t_oflag = TTYDEF_OFLAG;
    544 		tp->t_lflag = TTYDEF_LFLAG;
    545 		if (vp->vp_cols != 0) {
    546 			tp->t_winsize.ws_col = vp->vp_cols;
    547 			tp->t_winsize.ws_row = vp->vp_rows;
    548 		}
    549 
    550 		vioconparam(tp, &tp->t_termios);
    551 		ttsetwater(tp);
    552 	}
    553 	splx(s);
    554 
    555 	error = (*tp->t_linesw->l_open)(dev, tp);
    556 	return error;
    557 }
    558 
    559 int
    560 vioconclose(dev_t dev, int flag, int mode, struct lwp *l)
    561 {
    562 	struct viocon_port *vp = dev2port(dev);
    563 	struct tty *tp = vp->vp_tty;
    564 	int s;
    565 
    566 	if (!ISSET(tp->t_state, TS_ISOPEN))
    567 		return 0;
    568 
    569 	(*tp->t_linesw->l_close)(tp, flag);
    570 	s = spltty();
    571 #ifdef NOTYET
    572 	vp->vp_guest_open = 0;
    573 #endif
    574 	CLR(tp->t_state, TS_BUSY | TS_FLUSH);
    575 	ttyclose(tp);
    576 	splx(s);
    577 
    578 	return 0;
    579 }
    580 
    581 int
    582 vioconread(dev_t dev, struct uio *uio, int flag)
    583 {
    584 	struct viocon_port *vp = dev2port(dev);
    585 	struct tty *tp = vp->vp_tty;
    586 
    587 	return (*tp->t_linesw->l_read)(tp, uio, flag);
    588 }
    589 
    590 int
    591 vioconwrite(dev_t dev, struct uio *uio, int flag)
    592 {
    593 	struct viocon_port *vp = dev2port(dev);
    594 	struct tty *tp = vp->vp_tty;
    595 
    596 	return (*tp->t_linesw->l_write)(tp, uio, flag);
    597 }
    598 
    599 struct tty *
    600 viocontty(dev_t dev)
    601 {
    602 	struct viocon_port *vp = dev2port(dev);
    603 
    604 	return vp->vp_tty;
    605 }
    606 
    607 void
    608 vioconstop(struct tty *tp, int flag)
    609 {
    610 	int s;
    611 
    612 	s = spltty();
    613 	if (ISSET(tp->t_state, TS_BUSY))
    614 		if (!ISSET(tp->t_state, TS_TTSTOP))
    615 			SET(tp->t_state, TS_FLUSH);
    616 	splx(s);
    617 }
    618 
    619 int
    620 vioconioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
    621 {
    622 	struct viocon_port *vp = dev2port(dev);
    623 	struct tty *tp;
    624 	int error1, error2;
    625 
    626 	tp = vp->vp_tty;
    627 
    628 	error1 = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, l);
    629 	if (error1 >= 0)
    630 		return error1;
    631 	error2 = ttioctl(tp, cmd, data, flag, l);
    632 	if (error2 >= 0)
    633 		return error2;
    634 	return ENOTTY;
    635 }
    636