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