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