Home | History | Annotate | Line # | Download | only in virtio
viocon.c revision 1.1
      1 /*	$NetSBD$	*/
      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.1 2022/08/12 11:15:42 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 VIOCONUNIT(x)	(minor(x) >> 4)
     97 #define VIOCONPORT(x)	(minor(x) & 0x0f)
     98 
     99 struct viocon_port {
    100 	struct viocon_softc	*vp_sc;
    101 	struct virtqueue	*vp_rx;
    102 	struct virtqueue	*vp_tx;
    103 	void			*vp_si;
    104 	struct tty		*vp_tty;
    105 	const char 		*vp_name;
    106 	bus_dma_segment_t	 vp_dmaseg;
    107 	bus_dmamap_t		 vp_dmamap;
    108 #ifdef NOTYET
    109 	unsigned int		 vp_host_open:1;	/* XXX needs F_MULTIPORT */
    110 	unsigned int		 vp_guest_open:1;	/* XXX needs F_MULTIPORT */
    111 	unsigned int		 vp_is_console:1;	/* XXX needs F_MULTIPORT */
    112 #endif
    113 	unsigned int		 vp_iflow:1;		/* rx flow control */
    114 	uint16_t		 vp_rows;
    115 	uint16_t		 vp_cols;
    116 	u_char			*vp_rx_buf;
    117 	u_char			*vp_tx_buf;
    118 };
    119 
    120 struct viocon_softc {
    121 	struct device		*sc_dev;
    122 	struct virtio_softc	*sc_virtio;
    123 	struct virtqueue	*sc_vqs;
    124 
    125 	struct virtqueue        *sc_c_vq_rx;
    126 	struct virtqueue        *sc_c_vq_tx;
    127 
    128 	unsigned int		 sc_max_ports;
    129 	struct viocon_port	**sc_ports;
    130 
    131 	bus_dmamap_t		 sc_dmamap;
    132 };
    133 
    134 int	viocon_match(struct device *, struct cfdata *, void *);
    135 void	viocon_attach(struct device *, struct device *, void *);
    136 int	viocon_tx_intr(struct virtqueue *);
    137 int	viocon_tx_drain(struct viocon_port *, struct virtqueue *vq);
    138 int	viocon_rx_intr(struct virtqueue *);
    139 void	viocon_rx_soft(void *);
    140 void	viocon_rx_fill(struct viocon_port *);
    141 int	viocon_port_create(struct viocon_softc *, int);
    142 void	vioconstart(struct tty *);
    143 int	vioconhwiflow(struct tty *, int);
    144 int	vioconparam(struct tty *, struct termios *);
    145 int	vioconopen(dev_t, int, int, struct lwp *);
    146 int	vioconclose(dev_t, int, int, struct lwp *);
    147 int	vioconread(dev_t, struct uio *, int);
    148 int	vioconwrite(dev_t, struct uio *, int);
    149 void	vioconstop(struct tty *, int);
    150 int	vioconioctl(dev_t, u_long, void *, int, struct lwp *);
    151 struct tty	*viocontty(dev_t dev);
    152 
    153 CFATTACH_DECL_NEW(viocon, sizeof(struct viocon_softc),
    154     viocon_match, viocon_attach, /*detach*/NULL, /*activate*/NULL);
    155 
    156 const struct cdevsw viocon_cdevsw = {
    157 	.d_open = vioconopen,
    158 	.d_close = vioconclose,
    159 	.d_read = vioconread,
    160 	.d_write = vioconwrite,
    161 	.d_ioctl = vioconioctl,
    162 	.d_stop = vioconstop,
    163 	.d_tty = viocontty,
    164 	.d_poll = nopoll,	/* XXX */
    165 	.d_mmap = nommap,
    166 	.d_kqfilter = ttykqfilter,
    167 	.d_discard = nodiscard,
    168 	.d_flag = D_TTY,
    169 };
    170 
    171 static inline struct viocon_softc *
    172 dev2sc(dev_t dev)
    173 {
    174 	return device_lookup_private(&viocon_cd, VIOCONUNIT(dev));
    175 }
    176 
    177 static inline struct viocon_port *
    178 dev2port(dev_t dev)
    179 {
    180 	return dev2sc(dev)->sc_ports[VIOCONPORT(dev)];
    181 }
    182 
    183 int viocon_match(struct device *parent, struct cfdata *match, void *aux)
    184 {
    185 	struct virtio_attach_args *va = aux;
    186 	if (va->sc_childdevid == VIRTIO_DEVICE_ID_CONSOLE)
    187 		return 1;
    188 	return 0;
    189 }
    190 
    191 void
    192 viocon_attach(struct device *parent, struct device *self, void *aux)
    193 {
    194 	struct viocon_softc *sc = device_private(self);
    195 	struct virtio_softc *vsc = device_private(parent);
    196 	int maxports = 1;
    197 
    198 	sc->sc_dev = self;
    199 	if (virtio_child(vsc) != NULL) {
    200 		aprint_error(": parent %s already has a child\n",
    201 		    device_xname(parent));
    202 		return;
    203 	}
    204 	sc->sc_virtio = vsc;
    205 	sc->sc_max_ports = maxports;
    206 
    207 	sc->sc_vqs = kmem_zalloc(2 * (maxports + 1) * sizeof(sc->sc_vqs[0]),
    208 	    KM_SLEEP);
    209 	sc->sc_ports = kmem_zalloc(maxports * sizeof(sc->sc_ports[0]),
    210 	    KM_SLEEP);
    211 
    212 	virtio_child_attach_start(vsc, self, IPL_TTY, sc->sc_vqs,
    213 	    /*config_change*/NULL, virtio_vq_intr,
    214 	    /*req_flags*/0, /*req_features*/VIRTIO_CONSOLE_F_SIZE,
    215 	    VIRTIO_CONSOLE_FLAG_BITS);
    216 
    217 	DPRINTF("%s: softc: %p\n", __func__, sc);
    218 	if (viocon_port_create(sc, 0) != 0) {
    219 		printf("\n%s: viocon_port_create failed\n", __func__);
    220 		goto err;
    221 	}
    222 	viocon_rx_fill(sc->sc_ports[0]);
    223 
    224 	if (virtio_child_attach_finish(vsc) != 0)
    225 		goto err;
    226 
    227 	return;
    228 err:
    229 	kmem_free(sc->sc_vqs, 2 * (maxports + 1) * sizeof(sc->sc_vqs[0]));
    230 	kmem_free(sc->sc_ports, maxports * sizeof(sc->sc_ports[0]));
    231 	virtio_child_attach_failed(vsc);
    232 }
    233 
    234 int
    235 viocon_port_create(struct viocon_softc *sc, int portidx)
    236 {
    237 	struct virtio_softc *vsc = sc->sc_virtio;
    238 	int rxidx, txidx, allocsize, nsegs;
    239 	char name[6];
    240 	struct viocon_port *vp;
    241 	void *kva;
    242 	struct tty *tp;
    243 
    244 	vp = kmem_zalloc(sizeof(*vp), KM_SLEEP);
    245 	if (vp == NULL)
    246 		return ENOMEM;
    247 	sc->sc_ports[portidx] = vp;
    248 	vp->vp_sc = sc;
    249 	DPRINTF("%s: vp: %p\n", __func__, vp);
    250 
    251 	if (portidx == 0)
    252 		rxidx = 0;
    253 	else
    254 		rxidx = 2 * (portidx + 1);
    255 	txidx = rxidx + 1;
    256 
    257 	snprintf(name, sizeof(name), "p%drx", portidx);
    258 	if (virtio_alloc_vq(vsc, &sc->sc_vqs[rxidx], rxidx, BUFSIZE, 1,
    259 	    name) != 0) {
    260 		printf("\nCan't alloc %s virtqueue\n", name);
    261 		goto err;
    262 	}
    263 	vp->vp_rx = &sc->sc_vqs[rxidx];
    264 	vp->vp_rx->vq_done = viocon_rx_intr;
    265 	vp->vp_si = softint_establish(SOFTINT_SERIAL, viocon_rx_soft, vp);
    266 	DPRINTF("%s: rx: %p\n", __func__, vp->vp_rx);
    267 
    268 	snprintf(name, sizeof(name), "p%dtx", portidx);
    269 	if (virtio_alloc_vq(vsc, &sc->sc_vqs[txidx], txidx, BUFSIZE, 1,
    270 	    name) != 0) {
    271 		printf("\nCan't alloc %s virtqueue\n", name);
    272 		goto err;
    273 	}
    274 	vp->vp_tx = &sc->sc_vqs[txidx];
    275 	vp->vp_tx->vq_done = viocon_tx_intr;
    276 	DPRINTF("%s: tx: %p\n", __func__, vp->vp_tx);
    277 
    278 	allocsize = (vp->vp_rx->vq_num + vp->vp_tx->vq_num) * BUFSIZE;
    279 
    280 	if (bus_dmamap_create(virtio_dmat(vsc), allocsize, 1, allocsize, 0,
    281 	    BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &vp->vp_dmamap) != 0)
    282 		goto err;
    283 	if (bus_dmamem_alloc(virtio_dmat(vsc), allocsize, 8, 0, &vp->vp_dmaseg,
    284 	    1, &nsegs, BUS_DMA_NOWAIT) != 0)
    285 		goto err;
    286 	if (bus_dmamem_map(virtio_dmat(vsc), &vp->vp_dmaseg, nsegs,
    287 	    allocsize, &kva, BUS_DMA_NOWAIT) != 0)
    288 		goto err;
    289 	memset(kva, 0, allocsize);
    290 	if (bus_dmamap_load(virtio_dmat(vsc), vp->vp_dmamap, kva,
    291 	    allocsize, NULL, BUS_DMA_NOWAIT) != 0)
    292 		goto err;
    293 	vp->vp_rx_buf = (unsigned char *)kva;
    294 	/*
    295 	 * XXX use only a small circular tx buffer instead of many BUFSIZE buffers?
    296 	 */
    297 	vp->vp_tx_buf = vp->vp_rx_buf + vp->vp_rx->vq_num * BUFSIZE;
    298 
    299 	if (virtio_features(vsc) & VIRTIO_CONSOLE_F_SIZE) {
    300 		vp->vp_cols = virtio_read_device_config_2(vsc,
    301 		    VIRTIO_CONSOLE_COLS);
    302 		vp->vp_rows = virtio_read_device_config_2(vsc,
    303 		    VIRTIO_CONSOLE_ROWS);
    304 	}
    305 
    306 	tp = ttymalloc(1000000);
    307 	tp->t_oproc = vioconstart;
    308 	tp->t_param = vioconparam;
    309 	tp->t_hwiflow = vioconhwiflow;
    310 	tp->t_dev = (device_unit(sc->sc_dev) << 4) | portidx;
    311 	vp->vp_tty = tp;
    312 	DPRINTF("%s: tty: %p\n", __func__, tp);
    313 
    314 	virtio_start_vq_intr(vsc, vp->vp_rx);
    315 	virtio_start_vq_intr(vsc, vp->vp_tx);
    316 
    317 	return 0;
    318 err:
    319 	panic("%s failed", __func__);
    320 	return -1;
    321 }
    322 
    323 int
    324 viocon_tx_drain(struct viocon_port *vp, struct virtqueue *vq)
    325 {
    326 	struct virtio_softc *vsc = vq->vq_owner;
    327 	int ndone = 0, len, slot;
    328 
    329 	splassert(IPL_TTY);
    330 	while (virtio_dequeue(vsc, vq, &slot, &len) == 0) {
    331 		bus_dmamap_sync(virtio_dmat(vsc), vp->vp_dmamap,
    332 		    vp->vp_tx_buf - vp->vp_rx_buf + slot * BUFSIZE, BUFSIZE,
    333 		    BUS_DMASYNC_POSTREAD);
    334 		virtio_dequeue_commit(vsc, vq, slot);
    335 		ndone++;
    336 	}
    337 	return ndone;
    338 }
    339 
    340 int
    341 viocon_tx_intr(struct virtqueue *vq)
    342 {
    343 	struct virtio_softc *vsc = vq->vq_owner;
    344 	struct viocon_softc *sc = device_private(virtio_child(vsc));
    345 	int ndone = 0;
    346 	int portidx = (vq->vq_index - 1) / 2;
    347 	struct viocon_port *vp = sc->sc_ports[portidx];
    348 	struct tty *tp = vp->vp_tty;
    349 
    350 	splassert(IPL_TTY);
    351 	ndone = viocon_tx_drain(vp, vq);
    352 	if (ndone && ISSET(tp->t_state, TS_BUSY)) {
    353 		CLR(tp->t_state, TS_BUSY);
    354 		(*tp->t_linesw->l_start)(tp);
    355 	}
    356 
    357 	return 1;
    358 }
    359 
    360 void
    361 viocon_rx_fill(struct viocon_port *vp)
    362 {
    363 	struct virtqueue *vq = vp->vp_rx;
    364 	struct virtio_softc *vsc = vp->vp_sc->sc_virtio;
    365 	int r, slot, ndone = 0;
    366 
    367 	while ((r = virtio_enqueue_prep(vsc, vq, &slot)) == 0) {
    368 		if (virtio_enqueue_reserve(vsc, vq, slot, 1) != 0)
    369 			break;
    370 		bus_dmamap_sync(virtio_dmat(vsc), vp->vp_dmamap, slot * BUFSIZE,
    371 		    BUFSIZE, BUS_DMASYNC_PREREAD);
    372 		virtio_enqueue_p(vsc, vq, slot, vp->vp_dmamap, slot * BUFSIZE,
    373 		    BUFSIZE, 0);
    374 		virtio_enqueue_commit(vsc, vq, slot, 0);
    375 		ndone++;
    376 	}
    377 	KASSERT(r == 0 || r == EAGAIN);
    378 	if (ndone > 0)
    379 		virtio_notify(vsc, vq);
    380 }
    381 
    382 int
    383 viocon_rx_intr(struct virtqueue *vq)
    384 {
    385 	struct virtio_softc *vsc = vq->vq_owner;
    386 	struct viocon_softc *sc = device_private(virtio_child(vsc));
    387 	int portidx = (vq->vq_index - 1) / 2;
    388 	struct viocon_port *vp = sc->sc_ports[portidx];
    389 
    390 	softint_schedule(vp->vp_si);
    391 	return 1;
    392 }
    393 
    394 void
    395 viocon_rx_soft(void *arg)
    396 {
    397 	struct viocon_port *vp = arg;
    398 	struct virtqueue *vq = vp->vp_rx;
    399 	struct virtio_softc *vsc = vq->vq_owner;
    400 	struct tty *tp = vp->vp_tty;
    401 	int slot, len, i;
    402 	u_char *p;
    403 
    404 	while (!vp->vp_iflow && virtio_dequeue(vsc, vq, &slot, &len) == 0) {
    405 		bus_dmamap_sync(virtio_dmat(vsc), vp->vp_dmamap,
    406 		    slot * BUFSIZE, BUFSIZE, BUS_DMASYNC_POSTREAD);
    407 		p = vp->vp_rx_buf + slot * BUFSIZE;
    408 		for (i = 0; i < len; i++)
    409 			(*tp->t_linesw->l_rint)(*p++, tp);
    410 		virtio_dequeue_commit(vsc, vq, slot);
    411 	}
    412 
    413 	viocon_rx_fill(vp);
    414 
    415 	return;
    416 }
    417 
    418 void
    419 vioconstart(struct tty *tp)
    420 {
    421 	struct viocon_softc *sc = dev2sc(tp->t_dev);
    422 	struct virtio_softc *vsc;
    423 	struct viocon_port *vp = dev2port(tp->t_dev);
    424 	struct virtqueue *vq;
    425 	u_char *buf;
    426 	int s, cnt, slot, ret, ndone;
    427 
    428 	vsc = sc->sc_virtio;
    429 	vq = vp->vp_tx;
    430 
    431 	s = spltty();
    432 
    433 	ndone = viocon_tx_drain(vp, vq);
    434 	if (ISSET(tp->t_state, TS_BUSY)) {
    435 		if (ndone > 0)
    436 			CLR(tp->t_state, TS_BUSY);
    437 		else
    438 			goto out;
    439 	}
    440 	if (ISSET(tp->t_state, TS_TIMEOUT | TS_TTSTOP))
    441 		goto out;
    442 
    443 	if (tp->t_outq.c_cc == 0)
    444 		goto out;
    445 	ndone = 0;
    446 
    447 	while (tp->t_outq.c_cc > 0) {
    448 		ret = virtio_enqueue_prep(vsc, vq, &slot);
    449 		if (ret == EAGAIN) {
    450 			SET(tp->t_state, TS_BUSY);
    451 			break;
    452 		}
    453 		KASSERT(ret == 0);
    454 		ret = virtio_enqueue_reserve(vsc, vq, slot, 1);
    455 		KASSERT(ret == 0);
    456 		buf = vp->vp_tx_buf + slot * BUFSIZE;
    457 		cnt = q_to_b(&tp->t_outq, buf, BUFSIZE);
    458 		bus_dmamap_sync(virtio_dmat(vsc), vp->vp_dmamap,
    459 		    vp->vp_tx_buf - vp->vp_rx_buf + slot * BUFSIZE, cnt,
    460 		    BUS_DMASYNC_PREWRITE);
    461 		virtio_enqueue_p(vsc, vq, slot, vp->vp_dmamap,
    462 		    vp->vp_tx_buf - vp->vp_rx_buf + slot * BUFSIZE, cnt, 1);
    463 		virtio_enqueue_commit(vsc, vq, slot, 0);
    464 		ndone++;
    465 	}
    466 	if (ndone > 0)
    467 		virtio_notify(vsc, vq);
    468 	ttwakeupwr(tp);
    469 out:
    470 	splx(s);
    471 }
    472 
    473 int
    474 vioconhwiflow(struct tty *tp, int stop)
    475 {
    476 	struct viocon_port *vp = dev2port(tp->t_dev);
    477 	int s;
    478 
    479 	s = spltty();
    480 	vp->vp_iflow = stop;
    481 	if (stop) {
    482 		virtio_stop_vq_intr(vp->vp_sc->sc_virtio, vp->vp_rx);
    483 	} else {
    484 		virtio_start_vq_intr(vp->vp_sc->sc_virtio, vp->vp_rx);
    485 		softint_schedule(vp->vp_si);
    486 	}
    487 	splx(s);
    488 	return 1;
    489 }
    490 
    491 int
    492 vioconparam(struct tty *tp, struct termios *t)
    493 {
    494 	tp->t_ispeed = t->c_ispeed;
    495 	tp->t_ospeed = t->c_ospeed;
    496 	tp->t_cflag = t->c_cflag;
    497 
    498 	vioconstart(tp);
    499 	return 0;
    500 }
    501 
    502 int
    503 vioconopen(dev_t dev, int flag, int mode, struct lwp *l)
    504 {
    505 	int unit = VIOCONUNIT(dev);
    506 	int port = VIOCONPORT(dev);
    507 	struct viocon_softc *sc;
    508 	struct viocon_port *vp;
    509 	struct tty *tp;
    510 	int s, error;
    511 
    512 	sc = device_lookup_private(&viocon_cd, unit);
    513 	if (sc == NULL)
    514 		return (ENXIO);
    515 	if (!device_is_active(sc->sc_dev))
    516 		return (ENXIO);
    517 
    518 	s = spltty();
    519 	if (port >= sc->sc_max_ports) {
    520 		splx(s);
    521 		return (ENXIO);
    522 	}
    523 	vp = sc->sc_ports[port];
    524 	tp = vp->vp_tty;
    525 #ifdef NOTYET
    526 	vp->vp_guest_open = 1;
    527 #endif
    528 	splx(s);
    529 
    530 	if (kauth_authorize_device_tty(l->l_cred, KAUTH_DEVICE_TTY_OPEN, tp))
    531 		return (EBUSY);
    532 
    533 	s = spltty();
    534 	if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
    535 		ttychars(tp);
    536 		tp->t_ispeed = 1000000;
    537 		tp->t_ospeed = 1000000;
    538 		tp->t_cflag = TTYDEF_CFLAG|CLOCAL|CRTSCTS;
    539 		tp->t_iflag = TTYDEF_IFLAG;
    540 		tp->t_oflag = TTYDEF_OFLAG;
    541 		tp->t_lflag = TTYDEF_LFLAG;
    542 		if (vp->vp_cols != 0) {
    543 			tp->t_winsize.ws_col = vp->vp_cols;
    544 			tp->t_winsize.ws_row = vp->vp_rows;
    545 		}
    546 
    547 		vioconparam(tp, &tp->t_termios);
    548 		ttsetwater(tp);
    549 	}
    550 	splx(s);
    551 
    552 	error = (*tp->t_linesw->l_open)(dev, tp);
    553 	return error;
    554 }
    555 
    556 int
    557 vioconclose(dev_t dev, int flag, int mode, struct lwp *l)
    558 {
    559 	struct viocon_port *vp = dev2port(dev);
    560 	struct tty *tp = vp->vp_tty;
    561 	int s;
    562 
    563 	if (!ISSET(tp->t_state, TS_ISOPEN))
    564 		return 0;
    565 
    566 	(*tp->t_linesw->l_close)(tp, flag);
    567 	s = spltty();
    568 #ifdef NOTYET
    569 	vp->vp_guest_open = 0;
    570 #endif
    571 	CLR(tp->t_state, TS_BUSY | TS_FLUSH);
    572 	ttyclose(tp);
    573 	splx(s);
    574 
    575 	return 0;
    576 }
    577 
    578 int
    579 vioconread(dev_t dev, struct uio *uio, int flag)
    580 {
    581 	struct viocon_port *vp = dev2port(dev);
    582 	struct tty *tp = vp->vp_tty;
    583 
    584 	return (*tp->t_linesw->l_read)(tp, uio, flag);
    585 }
    586 
    587 int
    588 vioconwrite(dev_t dev, struct uio *uio, int flag)
    589 {
    590 	struct viocon_port *vp = dev2port(dev);
    591 	struct tty *tp = vp->vp_tty;
    592 
    593 	return (*tp->t_linesw->l_write)(tp, uio, flag);
    594 }
    595 
    596 struct tty *
    597 viocontty(dev_t dev)
    598 {
    599 	struct viocon_port *vp = dev2port(dev);
    600 
    601 	return vp->vp_tty;
    602 }
    603 
    604 void
    605 vioconstop(struct tty *tp, int flag)
    606 {
    607 	int s;
    608 
    609 	s = spltty();
    610 	if (ISSET(tp->t_state, TS_BUSY))
    611 		if (!ISSET(tp->t_state, TS_TTSTOP))
    612 			SET(tp->t_state, TS_FLUSH);
    613 	splx(s);
    614 }
    615 
    616 int
    617 vioconioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
    618 {
    619 	struct viocon_port *vp = dev2port(dev);
    620 	struct tty *tp;
    621 	int error1, error2;
    622 
    623 	tp = vp->vp_tty;
    624 
    625 	error1 = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, l);
    626 	if (error1 >= 0)
    627 		return error1;
    628 	error2 = ttioctl(tp, cmd, data, flag, l);
    629 	if (error2 >= 0)
    630 		return error2;
    631 	return ENOTTY;
    632 }
    633