Home | History | Annotate | Line # | Download | only in virtio
      1 /*	$NetBSD: viocon.c,v 1.10 2024/08/05 19:13:34 riastradh 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.10 2024/08/05 19:13:34 riastradh 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 
    226 	if (virtio_child_attach_finish(vsc, sc->sc_vqs, nvqs,
    227 	    /*config_change*/NULL, /*req_flags*/0) != 0)
    228 		goto err;
    229 
    230 	viocon_rx_fill(sc->sc_ports[0]);
    231 
    232 	return;
    233 err:
    234 	kmem_free(sc->sc_vqs, nvqs * sizeof(sc->sc_vqs[0]));
    235 	kmem_free(sc->sc_ports, maxports * sizeof(sc->sc_ports[0]));
    236 	virtio_child_attach_failed(vsc);
    237 }
    238 
    239 int
    240 viocon_port_create(struct viocon_softc *sc, int portidx)
    241 {
    242 	struct virtio_softc *vsc = sc->sc_virtio;
    243 	int rxidx, txidx, allocsize, nsegs;
    244 	char name[6];
    245 	struct viocon_port *vp;
    246 	void *kva;
    247 	struct tty *tp;
    248 
    249 	vp = kmem_zalloc(sizeof(*vp), KM_SLEEP);
    250 	if (vp == NULL)
    251 		return ENOMEM;
    252 	sc->sc_ports[portidx] = vp;
    253 	vp->vp_sc = sc;
    254 	DPRINTF("%s: vp: %p\n", __func__, vp);
    255 
    256 	rxidx = (portidx * VIOCON_PORT_NQS) + VIOCON_PORT_RX;
    257 	txidx = (portidx * VIOCON_PORT_NQS) + VIOCON_PORT_TX;
    258 
    259 	snprintf(name, sizeof(name), "p%drx", portidx);
    260 	virtio_init_vq_vqdone(vsc, &sc->sc_vqs[rxidx], rxidx,
    261 	    viocon_rx_intr);
    262 	if (virtio_alloc_vq(vsc, &sc->sc_vqs[rxidx], BUFSIZE, 1,
    263 	    name) != 0) {
    264 		printf("\nCan't alloc %s virtqueue\n", name);
    265 		goto err;
    266 	}
    267 	vp->vp_rx = &sc->sc_vqs[rxidx];
    268 	vp->vp_si = softint_establish(SOFTINT_SERIAL, viocon_rx_soft, vp);
    269 	DPRINTF("%s: rx: %p\n", __func__, vp->vp_rx);
    270 
    271 	snprintf(name, sizeof(name), "p%dtx", portidx);
    272 	virtio_init_vq_vqdone(vsc, &sc->sc_vqs[txidx], txidx,
    273 	    viocon_tx_intr);
    274 	if (virtio_alloc_vq(vsc, &sc->sc_vqs[txidx], BUFSIZE, 1,
    275 	    name) != 0) {
    276 		printf("\nCan't alloc %s virtqueue\n", name);
    277 		goto err;
    278 	}
    279 	vp->vp_tx = &sc->sc_vqs[txidx];
    280 	DPRINTF("%s: tx: %p\n", __func__, vp->vp_tx);
    281 
    282 	allocsize = (vp->vp_rx->vq_num + vp->vp_tx->vq_num) * BUFSIZE;
    283 
    284 	if (bus_dmamap_create(virtio_dmat(vsc), allocsize, 1, allocsize, 0,
    285 	    BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &vp->vp_dmamap) != 0)
    286 		goto err;
    287 	if (bus_dmamem_alloc(virtio_dmat(vsc), allocsize, 8, 0, &vp->vp_dmaseg,
    288 	    1, &nsegs, BUS_DMA_NOWAIT) != 0)
    289 		goto err;
    290 	if (bus_dmamem_map(virtio_dmat(vsc), &vp->vp_dmaseg, nsegs,
    291 	    allocsize, &kva, BUS_DMA_NOWAIT) != 0)
    292 		goto err;
    293 	memset(kva, 0, allocsize);
    294 	if (bus_dmamap_load(virtio_dmat(vsc), vp->vp_dmamap, kva,
    295 	    allocsize, NULL, BUS_DMA_NOWAIT) != 0)
    296 		goto err;
    297 	vp->vp_rx_buf = (unsigned char *)kva;
    298 	/*
    299 	 * XXX use only a small circular tx buffer instead of many BUFSIZE buffers?
    300 	 */
    301 	vp->vp_tx_buf = vp->vp_rx_buf + vp->vp_rx->vq_num * BUFSIZE;
    302 
    303 	if (virtio_features(vsc) & VIRTIO_CONSOLE_F_SIZE) {
    304 		vp->vp_cols = virtio_read_device_config_2(vsc,
    305 		    VIRTIO_CONSOLE_COLS);
    306 		vp->vp_rows = virtio_read_device_config_2(vsc,
    307 		    VIRTIO_CONSOLE_ROWS);
    308 	}
    309 
    310 	tp = ttymalloc(1000000);
    311 	tp->t_oproc = vioconstart;
    312 	tp->t_param = vioconparam;
    313 	tp->t_hwiflow = vioconhwiflow;
    314 	tp->t_dev = VIOCONDEV(device_unit(sc->sc_dev), portidx);
    315 	vp->vp_tty = tp;
    316 	DPRINTF("%s: tty: %p\n", __func__, tp);
    317 
    318 	virtio_start_vq_intr(vsc, vp->vp_rx);
    319 	virtio_start_vq_intr(vsc, vp->vp_tx);
    320 
    321 	return 0;
    322 err:
    323 	panic("%s failed", __func__);
    324 	return -1;
    325 }
    326 
    327 int
    328 viocon_tx_drain(struct viocon_port *vp, struct virtqueue *vq)
    329 {
    330 	struct virtio_softc *vsc = vq->vq_owner;
    331 	int ndone = 0, len, slot;
    332 
    333 	splassert(IPL_TTY);
    334 	while (virtio_dequeue(vsc, vq, &slot, &len) == 0) {
    335 		bus_dmamap_sync(virtio_dmat(vsc), vp->vp_dmamap,
    336 		    vp->vp_tx_buf - vp->vp_rx_buf + slot * BUFSIZE, BUFSIZE,
    337 		    BUS_DMASYNC_POSTWRITE);
    338 		virtio_dequeue_commit(vsc, vq, slot);
    339 		ndone++;
    340 	}
    341 	return ndone;
    342 }
    343 
    344 int
    345 viocon_tx_intr(struct virtqueue *vq)
    346 {
    347 	struct virtio_softc *vsc = vq->vq_owner;
    348 	struct viocon_softc *sc = device_private(virtio_child(vsc));
    349 	int ndone = 0;
    350 	int portidx = (vq->vq_index - 1) / 2;
    351 	struct viocon_port *vp = sc->sc_ports[portidx];
    352 	struct tty *tp = vp->vp_tty;
    353 
    354 	splassert(IPL_TTY);
    355 	ndone = viocon_tx_drain(vp, vq);
    356 	if (ndone && ISSET(tp->t_state, TS_BUSY)) {
    357 		CLR(tp->t_state, TS_BUSY);
    358 		(*tp->t_linesw->l_start)(tp);
    359 	}
    360 
    361 	return 1;
    362 }
    363 
    364 void
    365 viocon_rx_fill(struct viocon_port *vp)
    366 {
    367 	struct virtqueue *vq = vp->vp_rx;
    368 	struct virtio_softc *vsc = vp->vp_sc->sc_virtio;
    369 	int r, slot, ndone = 0;
    370 
    371 	while ((r = virtio_enqueue_prep(vsc, vq, &slot)) == 0) {
    372 		if (virtio_enqueue_reserve(vsc, vq, slot, 1) != 0)
    373 			break;
    374 		bus_dmamap_sync(virtio_dmat(vsc), vp->vp_dmamap, slot * BUFSIZE,
    375 		    BUFSIZE, BUS_DMASYNC_PREREAD);
    376 		virtio_enqueue_p(vsc, vq, slot, vp->vp_dmamap, slot * BUFSIZE,
    377 		    BUFSIZE, 0);
    378 		virtio_enqueue_commit(vsc, vq, slot, 0);
    379 		ndone++;
    380 	}
    381 	KASSERTMSG(r == 0 || r == EAGAIN, "r=%d", r);
    382 	if (ndone > 0)
    383 		virtio_notify(vsc, vq);
    384 }
    385 
    386 int
    387 viocon_rx_intr(struct virtqueue *vq)
    388 {
    389 	struct virtio_softc *vsc = vq->vq_owner;
    390 	struct viocon_softc *sc = device_private(virtio_child(vsc));
    391 	int portidx = (vq->vq_index - 1) / 2;
    392 	struct viocon_port *vp = sc->sc_ports[portidx];
    393 
    394 	softint_schedule(vp->vp_si);
    395 	return 1;
    396 }
    397 
    398 void
    399 viocon_rx_soft(void *arg)
    400 {
    401 	struct viocon_port *vp = arg;
    402 	struct virtqueue *vq = vp->vp_rx;
    403 	struct virtio_softc *vsc = vq->vq_owner;
    404 	struct tty *tp = vp->vp_tty;
    405 	int slot, len, i;
    406 	u_char *p;
    407 
    408 	while (!vp->vp_iflow && virtio_dequeue(vsc, vq, &slot, &len) == 0) {
    409 		bus_dmamap_sync(virtio_dmat(vsc), vp->vp_dmamap,
    410 		    slot * BUFSIZE, BUFSIZE, BUS_DMASYNC_POSTREAD);
    411 		p = vp->vp_rx_buf + slot * BUFSIZE;
    412 		for (i = 0; i < len; i++)
    413 			(*tp->t_linesw->l_rint)(*p++, tp);
    414 		virtio_dequeue_commit(vsc, vq, slot);
    415 	}
    416 
    417 	viocon_rx_fill(vp);
    418 
    419 	return;
    420 }
    421 
    422 void
    423 vioconstart(struct tty *tp)
    424 {
    425 	struct viocon_softc *sc = dev2sc(tp->t_dev);
    426 	struct virtio_softc *vsc;
    427 	struct viocon_port *vp = dev2port(tp->t_dev);
    428 	struct virtqueue *vq;
    429 	u_char *buf;
    430 	int s, cnt, slot, ret, ndone;
    431 
    432 	vsc = sc->sc_virtio;
    433 	vq = vp->vp_tx;
    434 
    435 	s = spltty();
    436 
    437 	ndone = viocon_tx_drain(vp, vq);
    438 	if (ISSET(tp->t_state, TS_BUSY)) {
    439 		if (ndone > 0)
    440 			CLR(tp->t_state, TS_BUSY);
    441 		else
    442 			goto out;
    443 	}
    444 	if (ISSET(tp->t_state, TS_TIMEOUT | TS_TTSTOP))
    445 		goto out;
    446 
    447 	if (tp->t_outq.c_cc == 0)
    448 		goto out;
    449 	ndone = 0;
    450 
    451 	while (tp->t_outq.c_cc > 0) {
    452 		ret = virtio_enqueue_prep(vsc, vq, &slot);
    453 		if (ret == EAGAIN) {
    454 			SET(tp->t_state, TS_BUSY);
    455 			break;
    456 		}
    457 		KASSERTMSG(ret == 0, "ret=%d", ret);
    458 		ret = virtio_enqueue_reserve(vsc, vq, slot, 1);
    459 		KASSERTMSG(ret == 0, "ret=%d", ret);
    460 		buf = vp->vp_tx_buf + slot * BUFSIZE;
    461 		cnt = q_to_b(&tp->t_outq, buf, BUFSIZE);
    462 		bus_dmamap_sync(virtio_dmat(vsc), vp->vp_dmamap,
    463 		    vp->vp_tx_buf - vp->vp_rx_buf + slot * BUFSIZE, cnt,
    464 		    BUS_DMASYNC_PREWRITE);
    465 		virtio_enqueue_p(vsc, vq, slot, vp->vp_dmamap,
    466 		    vp->vp_tx_buf - vp->vp_rx_buf + slot * BUFSIZE, cnt, 1);
    467 		virtio_enqueue_commit(vsc, vq, slot, 0);
    468 		ndone++;
    469 	}
    470 	if (ndone > 0)
    471 		virtio_notify(vsc, vq);
    472 	ttwakeupwr(tp);
    473 out:
    474 	splx(s);
    475 }
    476 
    477 int
    478 vioconhwiflow(struct tty *tp, int stop)
    479 {
    480 	struct viocon_port *vp = dev2port(tp->t_dev);
    481 	int s;
    482 
    483 	s = spltty();
    484 	vp->vp_iflow = stop;
    485 	if (stop) {
    486 		virtio_stop_vq_intr(vp->vp_sc->sc_virtio, vp->vp_rx);
    487 	} else {
    488 		virtio_start_vq_intr(vp->vp_sc->sc_virtio, vp->vp_rx);
    489 		softint_schedule(vp->vp_si);
    490 	}
    491 	splx(s);
    492 	return 1;
    493 }
    494 
    495 int
    496 vioconparam(struct tty *tp, struct termios *t)
    497 {
    498 	tp->t_ispeed = t->c_ispeed;
    499 	tp->t_ospeed = t->c_ospeed;
    500 	tp->t_cflag = t->c_cflag;
    501 
    502 	vioconstart(tp);
    503 	return 0;
    504 }
    505 
    506 int
    507 vioconopen(dev_t dev, int flag, int mode, struct lwp *l)
    508 {
    509 	int unit = VIOCONUNIT(dev);
    510 	int port = VIOCONPORT(dev);
    511 	struct viocon_softc *sc;
    512 	struct viocon_port *vp;
    513 	struct tty *tp;
    514 	int s, error;
    515 
    516 	sc = device_lookup_private(&viocon_cd, unit);
    517 	if (sc == NULL)
    518 		return (ENXIO);
    519 	if (!device_is_active(sc->sc_dev))
    520 		return (ENXIO);
    521 
    522 	s = spltty();
    523 	if (port >= sc->sc_max_ports) {
    524 		splx(s);
    525 		return (ENXIO);
    526 	}
    527 	vp = sc->sc_ports[port];
    528 	tp = vp->vp_tty;
    529 #ifdef NOTYET
    530 	vp->vp_guest_open = 1;
    531 #endif
    532 	splx(s);
    533 
    534 	if (kauth_authorize_device_tty(l->l_cred, KAUTH_DEVICE_TTY_OPEN, tp))
    535 		return (EBUSY);
    536 
    537 	s = spltty();
    538 	if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
    539 		ttychars(tp);
    540 		tp->t_ispeed = 1000000;
    541 		tp->t_ospeed = 1000000;
    542 		tp->t_cflag = TTYDEF_CFLAG|CLOCAL|CRTSCTS;
    543 		tp->t_iflag = TTYDEF_IFLAG;
    544 		tp->t_oflag = TTYDEF_OFLAG;
    545 		tp->t_lflag = TTYDEF_LFLAG;
    546 		if (vp->vp_cols != 0) {
    547 			tp->t_winsize.ws_col = vp->vp_cols;
    548 			tp->t_winsize.ws_row = vp->vp_rows;
    549 		}
    550 
    551 		vioconparam(tp, &tp->t_termios);
    552 		ttsetwater(tp);
    553 	}
    554 	splx(s);
    555 
    556 	error = (*tp->t_linesw->l_open)(dev, tp);
    557 	return error;
    558 }
    559 
    560 int
    561 vioconclose(dev_t dev, int flag, int mode, struct lwp *l)
    562 {
    563 	struct viocon_port *vp = dev2port(dev);
    564 	struct tty *tp = vp->vp_tty;
    565 	int s;
    566 
    567 	if (!ISSET(tp->t_state, TS_ISOPEN))
    568 		return 0;
    569 
    570 	(*tp->t_linesw->l_close)(tp, flag);
    571 	s = spltty();
    572 #ifdef NOTYET
    573 	vp->vp_guest_open = 0;
    574 #endif
    575 	CLR(tp->t_state, TS_BUSY | TS_FLUSH);
    576 	ttyclose(tp);
    577 	splx(s);
    578 
    579 	return 0;
    580 }
    581 
    582 int
    583 vioconread(dev_t dev, struct uio *uio, int flag)
    584 {
    585 	struct viocon_port *vp = dev2port(dev);
    586 	struct tty *tp = vp->vp_tty;
    587 
    588 	return (*tp->t_linesw->l_read)(tp, uio, flag);
    589 }
    590 
    591 int
    592 vioconwrite(dev_t dev, struct uio *uio, int flag)
    593 {
    594 	struct viocon_port *vp = dev2port(dev);
    595 	struct tty *tp = vp->vp_tty;
    596 
    597 	return (*tp->t_linesw->l_write)(tp, uio, flag);
    598 }
    599 
    600 struct tty *
    601 viocontty(dev_t dev)
    602 {
    603 	struct viocon_port *vp = dev2port(dev);
    604 
    605 	return vp->vp_tty;
    606 }
    607 
    608 void
    609 vioconstop(struct tty *tp, int flag)
    610 {
    611 	int s;
    612 
    613 	s = spltty();
    614 	if (ISSET(tp->t_state, TS_BUSY))
    615 		if (!ISSET(tp->t_state, TS_TTSTOP))
    616 			SET(tp->t_state, TS_FLUSH);
    617 	splx(s);
    618 }
    619 
    620 int
    621 vioconioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
    622 {
    623 	struct viocon_port *vp = dev2port(dev);
    624 	struct tty *tp;
    625 	int error1, error2;
    626 
    627 	tp = vp->vp_tty;
    628 
    629 	error1 = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, l);
    630 	if (error1 >= 0)
    631 		return error1;
    632 	error2 = ttioctl(tp, cmd, data, flag, l);
    633 	if (error2 >= 0)
    634 		return error2;
    635 	return ENOTTY;
    636 }
    637