viocon(4): New virtio tty driver imported from OpenBSD.

viocon* at virtio?

/dev/ttyVI??

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 \
  ...

I updated MAKEDEV.conf to create /dev/ttyVI?? on all ports where it
looks likely to work based on:
(a) having pci or a non-pci virtio attachment,
(b) `qemu-system-$ARCH -M ?' mentioned something resembling the port,
    and
(c) `qemu-system-$ARCH -device virtio-serial' launched without
    complaining about the virtio-serial device.

(Criterion (c) excluded sparc and sparc64.)
This commit is contained in:
riastradh 2022-08-12 11:15:40 +00:00
parent 2f5368b82e
commit cc725e3507
15 changed files with 740 additions and 14 deletions

View File

@ -1,4 +1,4 @@
# $NetBSD: mi,v 1.1752 2022/07/10 19:49:24 nia Exp $ # $NetBSD: mi,v 1.1753 2022/08/12 11:15:40 riastradh Exp $
# #
# Note: don't delete entries from here - mark them as "obsolete" instead. # Note: don't delete entries from here - mark them as "obsolete" instead.
# #
@ -2047,6 +2047,7 @@
./usr/share/man/cat4/video.0 man-sys-catman .cat ./usr/share/man/cat4/video.0 man-sys-catman .cat
./usr/share/man/cat4/vinum.0 man-obsolete obsolete ./usr/share/man/cat4/vinum.0 man-obsolete obsolete
./usr/share/man/cat4/vio9p.0 man-sys-catman .cat ./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/vioif.0 man-sys-catman .cat
./usr/share/man/cat4/viomb.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 ./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/viaide.html man-sys-htmlman html
./usr/share/man/html4/video.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/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/vioif.html man-sys-htmlman html
./usr/share/man/html4/viomb.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 ./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/video.4 man-sys-man .man
./usr/share/man/man4/vinum.4 man-obsolete obsolete ./usr/share/man/man4/vinum.4 man-obsolete obsolete
./usr/share/man/man4/vio9p.4 man-sys-man .man ./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/vioif.4 man-sys-man .man
./usr/share/man/man4/viomb.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 ./usr/share/man/man4/viornd.4 man-sys-man .man

View File

@ -1,5 +1,5 @@
#!/bin/sh - #!/bin/sh -
# $NetBSD: MAKEDEV.tmpl,v 1.230 2022/05/17 11:41:41 uwe Exp $ # $NetBSD: MAKEDEV.tmpl,v 1.231 2022/08/12 11:15:40 riastradh Exp $
# #
# Copyright (c) 2003,2007,2008 The NetBSD Foundation, Inc. # Copyright (c) 2003,2007,2008 The NetBSD Foundation, Inc.
# All rights reserved. # All rights reserved.
@ -148,6 +148,7 @@
# dmz* UNIBUS DMZ32 (vax) # dmz* UNIBUS DMZ32 (vax)
# dl* UNIBUS DL11 (vax) # dl* UNIBUS DL11 (vax)
# xencons Xen virtual console # xencons Xen virtual console
# ttyVI?? viocon(4)
# #
# Terminal multiplexors: # Terminal multiplexors:
# dc* 4 channel serial interface (keyboard, mouse, modem, printer) # dc* 4 channel serial interface (keyboard, mouse, modem, printer)
@ -2272,6 +2273,17 @@ scmd[0-9]*)
mkdev scmd$unit c %scmd_chr% $unit 666 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))
;;
ttyVI)
makedev ttyVI00 ttyVI10 ttyVI20 ttyVI30
;;
midevend) midevend)
%MI_DEVICES_END% %MI_DEVICES_END%
local) local)

View File

@ -1,4 +1,4 @@
# $NetBSD: MAKEDEV.conf,v 1.14 2020/04/05 14:09:17 jdolecek Exp $ # $NetBSD: MAKEDEV.conf,v 1.15 2022/08/12 11:15:41 riastradh Exp $
all_md) all_md)
makedev wscons stic0 sd0 sd1 sd2 sd3 sd4 makedev wscons stic0 sd0 sd1 sd2 sd3 sd4
@ -20,6 +20,7 @@ all_md)
makedev bktr makedev bktr
makedev radio makedev radio
makedev kttcp makedev kttcp
makedev ttyVI
;; ;;
minimal) minimal)

View File

@ -1,4 +1,4 @@
# $NetBSD: MAKEDEV.conf,v 1.33 2021/07/24 11:39:18 jmcneill Exp $ # $NetBSD: MAKEDEV.conf,v 1.34 2022/08/12 11:15:41 riastradh Exp $
# As of 2003-04-17, the "init" case must not create more than 890 entries. # As of 2003-04-17, the "init" case must not create more than 890 entries.
all_md) all_md)
@ -47,6 +47,7 @@ all_md)
makedev xmm0 makedev xmm0
makedev acpi makedev acpi
makedev smbios makedev smbios
makedev ttyVI
;; ;;
xen) xen)

View File

@ -1,4 +1,4 @@
# $NetBSD: MAKEDEV.conf,v 1.22 2021/10/10 13:03:09 jmcneill Exp $ # $NetBSD: MAKEDEV.conf,v 1.23 2022/08/12 11:15:41 riastradh Exp $
all_md) all_md)
makedev wscons fd0 fd1 wd0 wd1 wd2 wd3 sd0 sd1 sd2 sd3 makedev wscons fd0 fd1 wd0 wd1 wd2 wd3 sd0 sd1 sd2 sd3
@ -28,6 +28,7 @@ all_md)
makedev bpf makedev bpf
makedev openfirm makedev openfirm
makedev acpi smbios efi makedev acpi smbios efi
makedev ttyVI
;; ;;
ramdisk|floppy) ramdisk|floppy)

View File

@ -1,4 +1,4 @@
# $NetBSD: MAKEDEV.conf,v 1.11 2020/07/24 12:07:09 jmcneill Exp $ # $NetBSD: MAKEDEV.conf,v 1.12 2022/08/12 11:15:41 riastradh Exp $
# When creating a /dev file system on MFS, init makes a FS that leaves # When creating a /dev file system on MFS, init makes a FS that leaves
# only 890 (or so) inodes free. Therefore the "init" case (used by # only 890 (or so) inodes free. Therefore the "init" case (used by
@ -36,6 +36,7 @@ all_md)
makedev iic0 iic1 iic2 iic3 iic4 makedev iic0 iic1 iic2 iic3 iic4
makedev kttcp makedev kttcp
makedev openfirm makedev openfirm
makedev ttyVI
;; ;;
minimal) minimal)

View File

@ -1,4 +1,4 @@
# $NetBSD: MAKEDEV.conf,v 1.12 2020/11/10 11:22:22 rin Exp $ # $NetBSD: MAKEDEV.conf,v 1.13 2022/08/12 11:15:41 riastradh Exp $
all_md) all_md)
makedev wscons sd0 sd1 sd2 st0 st1 cd0 cd1 wd0 wd1 makedev wscons sd0 sd1 sd2 st0 st1 cd0 cd1 wd0 wd1
@ -23,6 +23,7 @@ all_md)
makedev kttcp makedev kttcp
makedev xlcom0 makedev xlcom0
makedev cfs makedev cfs
makedev ttyVI
;; ;;
# No bpf? # No bpf?

View File

@ -1,4 +1,4 @@
# $NetBSD: MAKEDEV.conf,v 1.2 2016/12/08 11:31:14 nat Exp $ # $NetBSD: MAKEDEV.conf,v 1.3 2022/08/12 11:15:41 riastradh Exp $
all_md) all_md)
makedev wscons makedev wscons
@ -12,4 +12,5 @@ all_md)
makedev pci0 pci1 pci2 pci3 makedev pci0 pci1 pci2 pci3
makedev raid0 raid1 raid2 raid3 makedev raid0 raid1 raid2 raid3
makedev sysmon makedev sysmon
makedev ttyVI
;; ;;

View File

@ -1,4 +1,4 @@
# $NetBSD: MAKEDEV.conf,v 1.34 2021/07/24 11:39:19 jmcneill Exp $ # $NetBSD: MAKEDEV.conf,v 1.35 2022/08/12 11:15:41 riastradh Exp $
# As of 2005-03-15, the "init" case must not create more than 1024 entries. # As of 2005-03-15, the "init" case must not create more than 1024 entries.
all_md) all_md)
@ -51,6 +51,7 @@ all_md)
makedev cfs makedev cfs
makedev acpi makedev acpi
makedev smbios makedev smbios
makedev ttyVI
;; ;;
xen) xen)

View File

@ -1,4 +1,4 @@
# $NetBSD: MAKEDEV.conf,v 1.13 2020/04/05 14:36:43 sevan Exp $ # $NetBSD: MAKEDEV.conf,v 1.14 2022/08/12 11:15:41 riastradh Exp $
all_md) all_md)
makedev wscons sd0 sd1 sd2 st0 st1 cd0 cd1 wd0 wd1 wd2 wd3 makedev wscons sd0 sd1 sd2 st0 st1 cd0 cd1 wd0 wd1 wd2 wd3
@ -19,6 +19,7 @@ all_md)
makedev radio makedev radio
makedev kttcp makedev kttcp
makedev cfs makedev cfs
makedev ttyVI
;; ;;
floppy) floppy)

View File

@ -1,4 +1,4 @@
# $NetBSD: Makefile,v 1.727 2022/07/10 19:49:24 nia Exp $ # $NetBSD: Makefile,v 1.728 2022/08/12 11:15:41 riastradh Exp $
# @(#)Makefile 8.1 (Berkeley) 6/18/93 # @(#)Makefile 8.1 (Berkeley) 6/18/93
MAN= aac.4 ac97.4 acardide.4 aceride.4 acphy.4 \ MAN= aac.4 ac97.4 acardide.4 aceride.4 acphy.4 \
@ -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 \ uark.4 ubsec.4 udp.4 uep.4 ug.4 uha.4 uk.4 ukphy.4 umb.4 \
unix.4 userconf.4 \ unix.4 userconf.4 \
vald.4 valz.4 veriexec.4 vga.4 vge.4 viaide.4 video.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 \ vether.4 vlan.4 vmmon.4 vmnet.4 vmt.4 vmx.4 vnd.4 voodoofb.4 vr.4 \
vte.4 \ vte.4 \
wapbl.4 wb.4 wbsio.4 wd.4 wdc.4 wg.4 wi.4 wm.4 wpi.4 \ wapbl.4 wb.4 wbsio.4 wd.4 wdc.4 wg.4 wi.4 wm.4 wpi.4 \

66
share/man/man4/viocon.4 Normal file
View File

@ -0,0 +1,66 @@
.\" $NetBSD: viocon.4,v 1.1 2022/08/12 11:15:41 riastradh Exp $
.\" $OpenBSD: viocon.4,v 1.3 2017/06/21 08:21:14 akfaew Exp $
.\"
.\" Copyright (c) 2015 Stefan Fritsch <sf@sfritsch.de>
.\"
.\" 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 .
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.

View File

@ -1,4 +1,4 @@
# $NetBSD: majors,v 1.101 2021/12/07 17:39:54 brad Exp $ # $NetBSD: majors,v 1.102 2022/08/12 11:15:42 riastradh Exp $
# #
# Device majors for Machine-Independent drivers. # Device majors for Machine-Independent drivers.
# #
@ -95,3 +95,4 @@ device-major smbios char 360 smbios
device-major efi char 361 efi device-major efi char 361 efi
device-major sht3xtemp char 362 sht3xtemp device-major sht3xtemp char 362 sht3xtemp
device-major scmd char 363 scmd device-major scmd char 363 scmd
device-major viocon char 364 viocon

View File

@ -1,6 +1,10 @@
# $NetBSD: files.virtio,v 1.4 2018/07/12 13:05:39 jakllsch Exp $ # $NetBSD: files.virtio,v 1.5 2022/08/12 11:15:42 riastradh Exp $
# XXX the contents of the following included file should be moved here # XXX the contents of the following included file should be moved here
include "dev/pci/files.virtio" include "dev/pci/files.virtio"
file dev/virtio/virtio_mmio.c virtio_mmio file dev/virtio/virtio_mmio.c virtio_mmio
device viocon
attach viocon at virtio
file dev/virtio/viocon.c viocon

632
sys/dev/virtio/viocon.c Normal file
View File

@ -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>
*
* 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: viocon.c,v 1.1 2022/08/12 11:15:42 riastradh Exp $");
#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;
}