Current-Users archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

Re: Virtio Viocon driver - possible to backport from OpenBSD?



The attached patch adds viocon(4) to NetBSD.  Lightly tested under
qemu.  Review welcome!  I'm not very familiar with tty drivers so I'm
hoping someone who is will take a look and see if I did anything the
wrong way in copying from OpenBSD.

As in OpenBSD, it uses new device nodes /dev/ttyVI[0-9][0-9], where
the first digit is the unit number and the second digit is the port
number.  The port number is currently always zero but in principle it
might change.  These are now unconditionally included in the MI
MAKEDEV.tmpl, and ttyVI00 through ttyVI30 are created by default.
From 50c5c4d195f82ad7b665a7e0adfa922a448e9cfb Mon Sep 17 00:00:00 2001
From: Taylor R Campbell <riastradh%NetBSD.org@localhost>
Date: Tue, 9 Aug 2022 14:09:38 +0000
Subject: [PATCH] viocon(4): New virtio tty driver imported from OpenBSD.

viocon* at virtio?

Tested under qemu with:

qemu-system-aarch64 ... \
  -device virtio-serial \
  -chardev socket,path=/tmp/ttyVI00,server=on,wait=off,id=ttyVI00 \
  -device virtconsole,chardev=ttyVI00,name=org.NetBSD.dev.ttyVI00 \
  ...
---
 distrib/sets/lists/man/mi   |   3 +
 etc/MAKEDEV.tmpl            |   9 +
 share/man/man4/Makefile     |   2 +-
 share/man/man4/viocon.4     |  66 ++++
 sys/conf/majors             |   1 +
 sys/dev/virtio/files.virtio |   4 +
 sys/dev/virtio/viocon.c     | 632 ++++++++++++++++++++++++++++++++++++
 7 files changed, 716 insertions(+), 1 deletion(-)
 create mode 100644 share/man/man4/viocon.4
 create mode 100644 sys/dev/virtio/viocon.c

diff --git a/distrib/sets/lists/man/mi b/distrib/sets/lists/man/mi
index 3b354d653ca0..c56263b6dffc 100644
--- a/distrib/sets/lists/man/mi
+++ b/distrib/sets/lists/man/mi
@@ -2047,6 +2047,7 @@
 ./usr/share/man/cat4/video.0			man-sys-catman		.cat
 ./usr/share/man/cat4/vinum.0			man-obsolete		obsolete
 ./usr/share/man/cat4/vio9p.0			man-sys-catman		.cat
+./usr/share/man/cat4/viocon.0			man-sys-catman		.cat
 ./usr/share/man/cat4/vioif.0			man-sys-catman		.cat
 ./usr/share/man/cat4/viomb.0			man-sys-catman		.cat
 ./usr/share/man/cat4/viornd.0			man-sys-catman		.cat
@@ -5233,6 +5234,7 @@
 ./usr/share/man/html4/viaide.html		man-sys-htmlman		html
 ./usr/share/man/html4/video.html		man-sys-htmlman		html
 ./usr/share/man/html4/vio9p.html		man-sys-htmlman		html
+./usr/share/man/html4/viocon.html		man-sys-htmlman		html
 ./usr/share/man/html4/vioif.html		man-sys-htmlman		html
 ./usr/share/man/html4/viomb.html		man-sys-htmlman		html
 ./usr/share/man/html4/viornd.html		man-sys-htmlman		html
@@ -8351,6 +8353,7 @@
 ./usr/share/man/man4/video.4			man-sys-man		.man
 ./usr/share/man/man4/vinum.4			man-obsolete		obsolete
 ./usr/share/man/man4/vio9p.4			man-sys-man		.man
+./usr/share/man/man4/viocon.4			man-sys-man		.man
 ./usr/share/man/man4/vioif.4			man-sys-man		.man
 ./usr/share/man/man4/viomb.4			man-sys-man		.man
 ./usr/share/man/man4/viornd.4			man-sys-man		.man
diff --git a/etc/MAKEDEV.tmpl b/etc/MAKEDEV.tmpl
index 51fb821f244b..6576679045a8 100644
--- a/etc/MAKEDEV.tmpl
+++ b/etc/MAKEDEV.tmpl
@@ -148,6 +148,7 @@
 #	dmz*    UNIBUS DMZ32 (vax)
 #	dl*	UNIBUS DL11 (vax)
 #	xencons	Xen virtual console
+#	ttyVI??	viocon(4)
 #
 # Terminal multiplexors:
 #	dc*	4 channel serial interface (keyboard, mouse, modem, printer)
@@ -849,6 +850,7 @@ all)
 	makedev qemufwcfg
 	makedev sht3xtemp0
 	makedev scmd0
+	makedev ttyVI00 ttyVI10 ttyVI20 ttyVI30
 	makedev local # do this last
 	;;
 
@@ -2272,6 +2274,13 @@ scmd[0-9]*)
 	mkdev scmd$unit c %scmd_chr% $unit 666
 	;;
 
+ttyVI[0-9][0-9])
+	port=${i#ttyVI?}
+	devunit=${i%$port}
+	unit=${devunit#ttyVI}
+	mkdev ttyVI$unit$port c %viocon_chr% $((16*$unit + $port))
+	;;
+
 midevend)
 %MI_DEVICES_END%
 local)
diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile
index d98073dccd63..897dca9ebd64 100644
--- a/share/man/man4/Makefile
+++ b/share/man/man4/Makefile
@@ -68,7 +68,7 @@ MAN=	aac.4 ac97.4 acardide.4 aceride.4 acphy.4 \
 	uark.4 ubsec.4 udp.4 uep.4 ug.4 uha.4 uk.4 ukphy.4 umb.4 \
 	unix.4 userconf.4 \
 	vald.4 valz.4 veriexec.4 vga.4 vge.4 viaide.4 video.4 \
-	vio9p.4 vioif.4 viomb.4 viornd.4 vioscsi.4 virt.4 virtio.4 \
+	vio9p.4 viocon.4 vioif.4 viomb.4 viornd.4 vioscsi.4 virt.4 virtio.4 \
 	vether.4 vlan.4 vmmon.4 vmnet.4 vmt.4 vmx.4 vnd.4 voodoofb.4 vr.4 \
 	vte.4 \
 	wapbl.4 wb.4 wbsio.4 wd.4 wdc.4 wg.4 wi.4 wm.4 wpi.4 \
diff --git a/share/man/man4/viocon.4 b/share/man/man4/viocon.4
new file mode 100644
index 000000000000..e9946d0e124b
--- /dev/null
+++ b/share/man/man4/viocon.4
@@ -0,0 +1,66 @@
+.\"	$NetBSD$
+.\"     $OpenBSD: viocon.4,v 1.3 2017/06/21 08:21:14 akfaew Exp $
+.\"
+.\" Copyright (c) 2015 Stefan Fritsch <sf%sfritsch.de@localhost>
+.\"
+.\" Permission to use, copy, modify, and distribute this software for any
+.\" purpose with or without fee is hereby granted, provided that the above
+.\" copyright notice and this permission notice appear in all copies.
+.\"
+.\" THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+.\" WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+.\" ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+.\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.Dd $Mdocdate: June 21 2017 $
+.Dt VIOCON 4
+.Os
+.Sh NAME
+.Nm viocon
+.Nd VirtIO console device
+.Sh SYNOPSIS
+.Cd "viocon* at virtio?"
+.Sh DESCRIPTION
+The
+.Nm
+driver provides support for the
+.Xr virtio 4
+console interface provided by KVM, QEMU, and others.
+.Pp
+It provides serial ports that are attached as ttys.
+.Sh FILES
+.Bl -tag -width Pa -compact
+.It Pa /dev/ttyVI00
+.It Pa /dev/ttyVI10
+.It Pa /dev/ttyVI20
+.It Pa /dev/ttyVI30
+.It Pa /dev/ttyVI40
+.El
+.Sh SEE ALSO
+.Xr intro 4 ,
+.Xr tty 4 ,
+.Xr virtio 4
+.Sh HISTORY
+The
+.Nm
+driver first appeared in
+.Ox 5.9 .
+.Sh AUTHORS
+.An -nosplit
+The
+.Nm
+driver was written for
+.Ox
+by
+.An Stefan Fritsch Aq Mt sf%sfritsch.de@localhost .
+It was ported to
+.Nx 10.0 .
+.Sh BUGS
+Use as a kernel console for
+.Nx
+is not yet supported.
+.Pp
+The multiport feature is not yet supported.
diff --git a/sys/conf/majors b/sys/conf/majors
index 6498bc46a246..2aaaf847d117 100644
--- a/sys/conf/majors
+++ b/sys/conf/majors
@@ -95,3 +95,4 @@ device-major smbios    char 360            smbios
 device-major efi       char 361            efi
 device-major sht3xtemp char 362		   sht3xtemp
 device-major scmd      char 363		   scmd
+device-major viocon    char 364		   viocon
diff --git a/sys/dev/virtio/files.virtio b/sys/dev/virtio/files.virtio
index 4477ce08634f..88490ccb47b0 100644
--- a/sys/dev/virtio/files.virtio
+++ b/sys/dev/virtio/files.virtio
@@ -4,3 +4,7 @@
 include "dev/pci/files.virtio"
 
 file	dev/virtio/virtio_mmio.c	virtio_mmio
+
+device	viocon
+attach	viocon at virtio
+file	dev/virtio/viocon.c		viocon
diff --git a/sys/dev/virtio/viocon.c b/sys/dev/virtio/viocon.c
new file mode 100644
index 000000000000..3520395aadd5
--- /dev/null
+++ b/sys/dev/virtio/viocon.c
@@ -0,0 +1,632 @@
+/*	$NetSBD$	*/
+/*	$OpenBSD: viocon.c,v 1.8 2021/11/05 11:38:29 mpi Exp $	*/
+
+/*
+ * Copyright (c) 2013-2015 Stefan Fritsch <sf%sfritsch.de@localhost>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/cdefs.h>
+__KERNEL_RCSID(0, "$NetBSD$");
+
+#include <sys/param.h>
+#include <sys/types.h>
+
+#include <sys/bus.h>
+#include <sys/conf.h>
+#include <sys/device.h>
+#include <sys/kauth.h>
+#include <sys/kernel.h>
+#include <sys/kmem.h>
+#include <sys/lwp.h>
+#include <sys/systm.h>
+#include <sys/tty.h>
+
+#include <dev/pci/virtioreg.h>
+#include <dev/pci/virtiovar.h>
+
+#include "ioconf.h"
+
+/* OpenBSD compat shims */
+#define	ttymalloc(speed)	tty_alloc()
+#define	splassert(ipl)		__nothing
+#define	virtio_notify(vsc, vq)	virtio_enqueue_commit(vsc, vq, -1, true)
+#define	ttwakeupwr(tp)		__nothing
+
+/* features */
+#define	VIRTIO_CONSOLE_F_SIZE		(1ULL<<0)
+#define	VIRTIO_CONSOLE_F_MULTIPORT	(1ULL<<1)
+#define	VIRTIO_CONSOLE_F_EMERG_WRITE 	(1ULL<<2)
+
+/* config space */
+#define VIRTIO_CONSOLE_COLS		0	/* 16 bits */
+#define VIRTIO_CONSOLE_ROWS		2	/* 16 bits */
+#define VIRTIO_CONSOLE_MAX_NR_PORTS	4	/* 32 bits */
+#define VIRTIO_CONSOLE_EMERG_WR		8	/* 32 bits */
+
+#define VIOCON_DEBUG	0
+
+#if VIOCON_DEBUG
+#define DPRINTF(x...) printf(x)
+#else
+#define DPRINTF(x...)
+#endif
+
+#define	VIRTIO_CONSOLE_FLAG_BITS					      \
+	VIRTIO_COMMON_FLAG_BITS						      \
+	"b\x00" "SIZE\0"						      \
+	"b\x01" "MULTIPORT\0"						      \
+	"b\x02" "EMERG_WRITE\0"
+
+struct virtio_console_control {
+	uint32_t id;	/* Port number */
+
+#define	VIRTIO_CONSOLE_DEVICE_READY	0
+#define	VIRTIO_CONSOLE_PORT_ADD		1
+#define	VIRTIO_CONSOLE_PORT_REMOVE	2
+#define	VIRTIO_CONSOLE_PORT_READY	3
+#define	VIRTIO_CONSOLE_CONSOLE_PORT	4
+#define	VIRTIO_CONSOLE_RESIZE		5
+#define	VIRTIO_CONSOLE_PORT_OPEN	6
+#define	VIRTIO_CONSOLE_PORT_NAME	7
+	uint16_t event;
+
+	uint16_t value;
+};
+
+struct virtio_console_control_resize {
+	/* yes, the order is different than in config space */
+	uint16_t rows;
+	uint16_t cols;
+};
+
+#define	BUFSIZE		128
+
+#define VIOCONUNIT(x)	(minor(x) >> 4)
+#define VIOCONPORT(x)	(minor(x) & 0x0f)
+
+struct viocon_port {
+	struct viocon_softc	*vp_sc;
+	struct virtqueue	*vp_rx;
+	struct virtqueue	*vp_tx;
+	void			*vp_si;
+	struct tty		*vp_tty;
+	const char 		*vp_name;
+	bus_dma_segment_t	 vp_dmaseg;
+	bus_dmamap_t		 vp_dmamap;
+#ifdef NOTYET
+	unsigned int		 vp_host_open:1;	/* XXX needs F_MULTIPORT */
+	unsigned int		 vp_guest_open:1;	/* XXX needs F_MULTIPORT */
+	unsigned int		 vp_is_console:1;	/* XXX needs F_MULTIPORT */
+#endif
+	unsigned int		 vp_iflow:1;		/* rx flow control */
+	uint16_t		 vp_rows;
+	uint16_t		 vp_cols;
+	u_char			*vp_rx_buf;
+	u_char			*vp_tx_buf;
+};
+
+struct viocon_softc {
+	struct device		*sc_dev;
+	struct virtio_softc	*sc_virtio;
+	struct virtqueue	*sc_vqs;
+
+	struct virtqueue        *sc_c_vq_rx;
+	struct virtqueue        *sc_c_vq_tx;
+
+	unsigned int		 sc_max_ports;
+	struct viocon_port	**sc_ports;
+
+	bus_dmamap_t		 sc_dmamap;
+};
+
+int	viocon_match(struct device *, struct cfdata *, void *);
+void	viocon_attach(struct device *, struct device *, void *);
+int	viocon_tx_intr(struct virtqueue *);
+int	viocon_tx_drain(struct viocon_port *, struct virtqueue *vq);
+int	viocon_rx_intr(struct virtqueue *);
+void	viocon_rx_soft(void *);
+void	viocon_rx_fill(struct viocon_port *);
+int	viocon_port_create(struct viocon_softc *, int);
+void	vioconstart(struct tty *);
+int	vioconhwiflow(struct tty *, int);
+int	vioconparam(struct tty *, struct termios *);
+int	vioconopen(dev_t, int, int, struct lwp *);
+int	vioconclose(dev_t, int, int, struct lwp *);
+int	vioconread(dev_t, struct uio *, int);
+int	vioconwrite(dev_t, struct uio *, int);
+void	vioconstop(struct tty *, int);
+int	vioconioctl(dev_t, u_long, void *, int, struct lwp *);
+struct tty	*viocontty(dev_t dev);
+
+CFATTACH_DECL_NEW(viocon, sizeof(struct viocon_softc),
+    viocon_match, viocon_attach, /*detach*/NULL, /*activate*/NULL);
+
+const struct cdevsw viocon_cdevsw = {
+	.d_open = vioconopen,
+	.d_close = vioconclose,
+	.d_read = vioconread,
+	.d_write = vioconwrite,
+	.d_ioctl = vioconioctl,
+	.d_stop = vioconstop,
+	.d_tty = viocontty,
+	.d_poll = nopoll,	/* XXX */
+	.d_mmap = nommap,
+	.d_kqfilter = ttykqfilter,
+	.d_discard = nodiscard,
+	.d_flag = D_TTY,
+};
+
+static inline struct viocon_softc *
+dev2sc(dev_t dev)
+{
+	return device_lookup_private(&viocon_cd, VIOCONUNIT(dev));
+}
+
+static inline struct viocon_port *
+dev2port(dev_t dev)
+{
+	return dev2sc(dev)->sc_ports[VIOCONPORT(dev)];
+}
+
+int viocon_match(struct device *parent, struct cfdata *match, void *aux)
+{
+	struct virtio_attach_args *va = aux;
+	if (va->sc_childdevid == VIRTIO_DEVICE_ID_CONSOLE)
+		return 1;
+	return 0;
+}
+
+void
+viocon_attach(struct device *parent, struct device *self, void *aux)
+{
+	struct viocon_softc *sc = device_private(self);
+	struct virtio_softc *vsc = device_private(parent);
+	int maxports = 1;
+
+	sc->sc_dev = self;
+	if (virtio_child(vsc) != NULL) {
+		aprint_error(": parent %s already has a child\n",
+		    device_xname(parent));
+		return;
+	}
+	sc->sc_virtio = vsc;
+	sc->sc_max_ports = maxports;
+
+	sc->sc_vqs = kmem_zalloc(2 * (maxports + 1) * sizeof(sc->sc_vqs[0]),
+	    KM_SLEEP);
+	sc->sc_ports = kmem_zalloc(maxports * sizeof(sc->sc_ports[0]),
+	    KM_SLEEP);
+
+	virtio_child_attach_start(vsc, self, IPL_TTY, sc->sc_vqs,
+	    /*config_change*/NULL, virtio_vq_intr,
+	    /*req_flags*/0, /*req_features*/VIRTIO_CONSOLE_F_SIZE,
+	    VIRTIO_CONSOLE_FLAG_BITS);
+
+	DPRINTF("%s: softc: %p\n", __func__, sc);
+	if (viocon_port_create(sc, 0) != 0) {
+		printf("\n%s: viocon_port_create failed\n", __func__);
+		goto err;
+	}
+	viocon_rx_fill(sc->sc_ports[0]);
+
+	if (virtio_child_attach_finish(vsc) != 0)
+		goto err;
+
+	return;
+err:
+	kmem_free(sc->sc_vqs, 2 * (maxports + 1) * sizeof(sc->sc_vqs[0]));
+	kmem_free(sc->sc_ports, maxports * sizeof(sc->sc_ports[0]));
+	virtio_child_attach_failed(vsc);
+}
+
+int
+viocon_port_create(struct viocon_softc *sc, int portidx)
+{
+	struct virtio_softc *vsc = sc->sc_virtio;
+	int rxidx, txidx, allocsize, nsegs;
+	char name[6];
+	struct viocon_port *vp;
+	void *kva;
+	struct tty *tp;
+
+	vp = kmem_zalloc(sizeof(*vp), KM_SLEEP);
+	if (vp == NULL)
+		return ENOMEM;
+	sc->sc_ports[portidx] = vp;
+	vp->vp_sc = sc;
+	DPRINTF("%s: vp: %p\n", __func__, vp);
+
+	if (portidx == 0)
+		rxidx = 0;
+	else
+		rxidx = 2 * (portidx + 1);
+	txidx = rxidx + 1;
+
+	snprintf(name, sizeof(name), "p%drx", portidx);
+	if (virtio_alloc_vq(vsc, &sc->sc_vqs[rxidx], rxidx, BUFSIZE, 1,
+	    name) != 0) {
+		printf("\nCan't alloc %s virtqueue\n", name);
+		goto err;
+	}
+	vp->vp_rx = &sc->sc_vqs[rxidx];
+	vp->vp_rx->vq_done = viocon_rx_intr;
+	vp->vp_si = softint_establish(SOFTINT_SERIAL, viocon_rx_soft, vp);
+	DPRINTF("%s: rx: %p\n", __func__, vp->vp_rx);
+
+	snprintf(name, sizeof(name), "p%dtx", portidx);
+	if (virtio_alloc_vq(vsc, &sc->sc_vqs[txidx], txidx, BUFSIZE, 1,
+	    name) != 0) {
+		printf("\nCan't alloc %s virtqueue\n", name);
+		goto err;
+	}
+	vp->vp_tx = &sc->sc_vqs[txidx];
+	vp->vp_tx->vq_done = viocon_tx_intr;
+	DPRINTF("%s: tx: %p\n", __func__, vp->vp_tx);
+
+	allocsize = (vp->vp_rx->vq_num + vp->vp_tx->vq_num) * BUFSIZE;
+
+	if (bus_dmamap_create(virtio_dmat(vsc), allocsize, 1, allocsize, 0,
+	    BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &vp->vp_dmamap) != 0)
+		goto err;
+	if (bus_dmamem_alloc(virtio_dmat(vsc), allocsize, 8, 0, &vp->vp_dmaseg,
+	    1, &nsegs, BUS_DMA_NOWAIT) != 0)
+		goto err;
+	if (bus_dmamem_map(virtio_dmat(vsc), &vp->vp_dmaseg, nsegs,
+	    allocsize, &kva, BUS_DMA_NOWAIT) != 0)
+		goto err;
+	memset(kva, 0, allocsize);
+	if (bus_dmamap_load(virtio_dmat(vsc), vp->vp_dmamap, kva,
+	    allocsize, NULL, BUS_DMA_NOWAIT) != 0)
+		goto err;
+	vp->vp_rx_buf = (unsigned char *)kva;
+	/*
+	 * XXX use only a small circular tx buffer instead of many BUFSIZE buffers?
+	 */
+	vp->vp_tx_buf = vp->vp_rx_buf + vp->vp_rx->vq_num * BUFSIZE;
+
+	if (virtio_features(vsc) & VIRTIO_CONSOLE_F_SIZE) {
+		vp->vp_cols = virtio_read_device_config_2(vsc,
+		    VIRTIO_CONSOLE_COLS);
+		vp->vp_rows = virtio_read_device_config_2(vsc,
+		    VIRTIO_CONSOLE_ROWS);
+	}
+
+	tp = ttymalloc(1000000);
+	tp->t_oproc = vioconstart;
+	tp->t_param = vioconparam;
+	tp->t_hwiflow = vioconhwiflow;
+	tp->t_dev = (device_unit(sc->sc_dev) << 4) | portidx;
+	vp->vp_tty = tp;
+	DPRINTF("%s: tty: %p\n", __func__, tp);
+
+	virtio_start_vq_intr(vsc, vp->vp_rx);
+	virtio_start_vq_intr(vsc, vp->vp_tx);
+
+	return 0;
+err:
+	panic("%s failed", __func__);
+	return -1;
+}
+
+int
+viocon_tx_drain(struct viocon_port *vp, struct virtqueue *vq)
+{
+	struct virtio_softc *vsc = vq->vq_owner;
+	int ndone = 0, len, slot;
+
+	splassert(IPL_TTY);
+	while (virtio_dequeue(vsc, vq, &slot, &len) == 0) {
+		bus_dmamap_sync(virtio_dmat(vsc), vp->vp_dmamap,
+		    vp->vp_tx_buf - vp->vp_rx_buf + slot * BUFSIZE, BUFSIZE,
+		    BUS_DMASYNC_POSTREAD);
+		virtio_dequeue_commit(vsc, vq, slot);
+		ndone++;
+	}
+	return ndone;
+}
+
+int
+viocon_tx_intr(struct virtqueue *vq)
+{
+	struct virtio_softc *vsc = vq->vq_owner;
+	struct viocon_softc *sc = device_private(virtio_child(vsc));
+	int ndone = 0;
+	int portidx = (vq->vq_index - 1) / 2;
+	struct viocon_port *vp = sc->sc_ports[portidx];
+	struct tty *tp = vp->vp_tty;
+
+	splassert(IPL_TTY);
+	ndone = viocon_tx_drain(vp, vq);
+	if (ndone && ISSET(tp->t_state, TS_BUSY)) {
+		CLR(tp->t_state, TS_BUSY);
+		(*tp->t_linesw->l_start)(tp);
+	}
+
+	return 1;
+}
+
+void
+viocon_rx_fill(struct viocon_port *vp)
+{
+	struct virtqueue *vq = vp->vp_rx;
+	struct virtio_softc *vsc = vp->vp_sc->sc_virtio;
+	int r, slot, ndone = 0;
+
+	while ((r = virtio_enqueue_prep(vsc, vq, &slot)) == 0) {
+		if (virtio_enqueue_reserve(vsc, vq, slot, 1) != 0)
+			break;
+		bus_dmamap_sync(virtio_dmat(vsc), vp->vp_dmamap, slot * BUFSIZE,
+		    BUFSIZE, BUS_DMASYNC_PREREAD);
+		virtio_enqueue_p(vsc, vq, slot, vp->vp_dmamap, slot * BUFSIZE,
+		    BUFSIZE, 0);
+		virtio_enqueue_commit(vsc, vq, slot, 0);
+		ndone++;
+	}
+	KASSERT(r == 0 || r == EAGAIN);
+	if (ndone > 0)
+		virtio_notify(vsc, vq);
+}
+
+int
+viocon_rx_intr(struct virtqueue *vq)
+{
+	struct virtio_softc *vsc = vq->vq_owner;
+	struct viocon_softc *sc = device_private(virtio_child(vsc));
+	int portidx = (vq->vq_index - 1) / 2;
+	struct viocon_port *vp = sc->sc_ports[portidx];
+
+	softint_schedule(vp->vp_si);
+	return 1;
+}
+
+void
+viocon_rx_soft(void *arg)
+{
+	struct viocon_port *vp = arg;
+	struct virtqueue *vq = vp->vp_rx;
+	struct virtio_softc *vsc = vq->vq_owner;
+	struct tty *tp = vp->vp_tty;
+	int slot, len, i;
+	u_char *p;
+
+	while (!vp->vp_iflow && virtio_dequeue(vsc, vq, &slot, &len) == 0) {
+		bus_dmamap_sync(virtio_dmat(vsc), vp->vp_dmamap,
+		    slot * BUFSIZE, BUFSIZE, BUS_DMASYNC_POSTREAD);
+		p = vp->vp_rx_buf + slot * BUFSIZE;
+		for (i = 0; i < len; i++)
+			(*tp->t_linesw->l_rint)(*p++, tp);
+		virtio_dequeue_commit(vsc, vq, slot);
+	}
+
+	viocon_rx_fill(vp);
+
+	return;
+}
+
+void
+vioconstart(struct tty *tp)
+{
+	struct viocon_softc *sc = dev2sc(tp->t_dev);
+	struct virtio_softc *vsc;
+	struct viocon_port *vp = dev2port(tp->t_dev);
+	struct virtqueue *vq;
+	u_char *buf;
+	int s, cnt, slot, ret, ndone;
+
+	vsc = sc->sc_virtio;
+	vq = vp->vp_tx;
+
+	s = spltty();
+
+	ndone = viocon_tx_drain(vp, vq);
+	if (ISSET(tp->t_state, TS_BUSY)) {
+		if (ndone > 0)
+			CLR(tp->t_state, TS_BUSY);
+		else
+			goto out;
+	}
+	if (ISSET(tp->t_state, TS_TIMEOUT | TS_TTSTOP))
+		goto out;
+
+	if (tp->t_outq.c_cc == 0)
+		goto out;
+	ndone = 0;
+
+	while (tp->t_outq.c_cc > 0) {
+		ret = virtio_enqueue_prep(vsc, vq, &slot);
+		if (ret == EAGAIN) {
+			SET(tp->t_state, TS_BUSY);
+			break;
+		}
+		KASSERT(ret == 0);
+		ret = virtio_enqueue_reserve(vsc, vq, slot, 1);
+		KASSERT(ret == 0);
+		buf = vp->vp_tx_buf + slot * BUFSIZE;
+		cnt = q_to_b(&tp->t_outq, buf, BUFSIZE);
+		bus_dmamap_sync(virtio_dmat(vsc), vp->vp_dmamap,
+		    vp->vp_tx_buf - vp->vp_rx_buf + slot * BUFSIZE, cnt,
+		    BUS_DMASYNC_PREWRITE);
+		virtio_enqueue_p(vsc, vq, slot, vp->vp_dmamap,
+		    vp->vp_tx_buf - vp->vp_rx_buf + slot * BUFSIZE, cnt, 1);
+		virtio_enqueue_commit(vsc, vq, slot, 0);
+		ndone++;
+	}
+	if (ndone > 0)
+		virtio_notify(vsc, vq);
+	ttwakeupwr(tp);
+out:
+	splx(s);
+}
+
+int
+vioconhwiflow(struct tty *tp, int stop)
+{
+	struct viocon_port *vp = dev2port(tp->t_dev);
+	int s;
+
+	s = spltty();
+	vp->vp_iflow = stop;
+	if (stop) {
+		virtio_stop_vq_intr(vp->vp_sc->sc_virtio, vp->vp_rx);
+	} else {
+		virtio_start_vq_intr(vp->vp_sc->sc_virtio, vp->vp_rx);
+		softint_schedule(vp->vp_si);
+	}
+	splx(s);
+	return 1;
+}
+
+int
+vioconparam(struct tty *tp, struct termios *t)
+{
+	tp->t_ispeed = t->c_ispeed;
+	tp->t_ospeed = t->c_ospeed;
+	tp->t_cflag = t->c_cflag;
+
+	vioconstart(tp);
+	return 0;
+}
+
+int
+vioconopen(dev_t dev, int flag, int mode, struct lwp *l)
+{
+	int unit = VIOCONUNIT(dev);
+	int port = VIOCONPORT(dev);
+	struct viocon_softc *sc;
+	struct viocon_port *vp;
+	struct tty *tp;
+	int s, error;
+
+	sc = device_lookup_private(&viocon_cd, unit);
+	if (sc == NULL)
+		return (ENXIO);
+	if (!device_is_active(sc->sc_dev))
+		return (ENXIO);
+
+	s = spltty();
+	if (port >= sc->sc_max_ports) {
+		splx(s);
+		return (ENXIO);
+	}
+	vp = sc->sc_ports[port];
+	tp = vp->vp_tty;
+#ifdef NOTYET
+	vp->vp_guest_open = 1;
+#endif
+	splx(s);
+
+	if (kauth_authorize_device_tty(l->l_cred, KAUTH_DEVICE_TTY_OPEN, tp))
+		return (EBUSY);
+
+	s = spltty();
+	if (!ISSET(tp->t_state, TS_ISOPEN) && tp->t_wopen == 0) {
+		ttychars(tp);
+		tp->t_ispeed = 1000000;
+		tp->t_ospeed = 1000000;
+		tp->t_cflag = TTYDEF_CFLAG|CLOCAL|CRTSCTS;
+		tp->t_iflag = TTYDEF_IFLAG;
+		tp->t_oflag = TTYDEF_OFLAG;
+		tp->t_lflag = TTYDEF_LFLAG;
+		if (vp->vp_cols != 0) {
+			tp->t_winsize.ws_col = vp->vp_cols;
+			tp->t_winsize.ws_row = vp->vp_rows;
+		}
+
+		vioconparam(tp, &tp->t_termios);
+		ttsetwater(tp);
+	}
+	splx(s);
+
+	error = (*tp->t_linesw->l_open)(dev, tp);
+	return error;
+}
+
+int
+vioconclose(dev_t dev, int flag, int mode, struct lwp *l)
+{
+	struct viocon_port *vp = dev2port(dev);
+	struct tty *tp = vp->vp_tty;
+	int s;
+
+	if (!ISSET(tp->t_state, TS_ISOPEN))
+		return 0;
+
+	(*tp->t_linesw->l_close)(tp, flag);
+	s = spltty();
+#ifdef NOTYET
+	vp->vp_guest_open = 0;
+#endif
+	CLR(tp->t_state, TS_BUSY | TS_FLUSH);
+	ttyclose(tp);
+	splx(s);
+
+	return 0;
+}
+
+int
+vioconread(dev_t dev, struct uio *uio, int flag)
+{
+	struct viocon_port *vp = dev2port(dev);
+	struct tty *tp = vp->vp_tty;
+
+	return (*tp->t_linesw->l_read)(tp, uio, flag);
+}
+
+int
+vioconwrite(dev_t dev, struct uio *uio, int flag)
+{
+	struct viocon_port *vp = dev2port(dev);
+	struct tty *tp = vp->vp_tty;
+
+	return (*tp->t_linesw->l_write)(tp, uio, flag);
+}
+
+struct tty *
+viocontty(dev_t dev)
+{
+	struct viocon_port *vp = dev2port(dev);
+
+	return vp->vp_tty;
+}
+
+void
+vioconstop(struct tty *tp, int flag)
+{
+	int s;
+
+	s = spltty();
+	if (ISSET(tp->t_state, TS_BUSY))
+		if (!ISSET(tp->t_state, TS_TTSTOP))
+			SET(tp->t_state, TS_FLUSH);
+	splx(s);
+}
+
+int
+vioconioctl(dev_t dev, u_long cmd, void *data, int flag, struct lwp *l)
+{
+	struct viocon_port *vp = dev2port(dev);
+	struct tty *tp;
+	int error1, error2;
+
+	tp = vp->vp_tty;
+
+	error1 = (*tp->t_linesw->l_ioctl)(tp, cmd, data, flag, l);
+	if (error1 >= 0)
+		return error1;
+	error2 = ttioctl(tp, cmd, data, flag, l);
+	if (error2 >= 0)
+		return error2;
+	return ENOTTY;
+}


Home | Main Index | Thread Index | Old Index