tech-kern 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