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:
parent
2f5368b82e
commit
cc725e3507
|
@ -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.
|
||||
#
|
||||
|
@ -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
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
#!/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.
|
||||
# All rights reserved.
|
||||
|
@ -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)
|
||||
|
@ -2272,6 +2273,17 @@ 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))
|
||||
;;
|
||||
|
||||
ttyVI)
|
||||
makedev ttyVI00 ttyVI10 ttyVI20 ttyVI30
|
||||
;;
|
||||
|
||||
midevend)
|
||||
%MI_DEVICES_END%
|
||||
local)
|
||||
|
|
|
@ -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)
|
||||
makedev wscons stic0 sd0 sd1 sd2 sd3 sd4
|
||||
|
@ -20,6 +20,7 @@ all_md)
|
|||
makedev bktr
|
||||
makedev radio
|
||||
makedev kttcp
|
||||
makedev ttyVI
|
||||
;;
|
||||
|
||||
minimal)
|
||||
|
|
|
@ -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.
|
||||
all_md)
|
||||
|
@ -47,6 +47,7 @@ all_md)
|
|||
makedev xmm0
|
||||
makedev acpi
|
||||
makedev smbios
|
||||
makedev ttyVI
|
||||
;;
|
||||
|
||||
xen)
|
||||
|
|
|
@ -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)
|
||||
makedev wscons fd0 fd1 wd0 wd1 wd2 wd3 sd0 sd1 sd2 sd3
|
||||
|
@ -28,6 +28,7 @@ all_md)
|
|||
makedev bpf
|
||||
makedev openfirm
|
||||
makedev acpi smbios efi
|
||||
makedev ttyVI
|
||||
;;
|
||||
|
||||
ramdisk|floppy)
|
||||
|
|
|
@ -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
|
||||
# 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 kttcp
|
||||
makedev openfirm
|
||||
makedev ttyVI
|
||||
;;
|
||||
|
||||
minimal)
|
||||
|
|
|
@ -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)
|
||||
makedev wscons sd0 sd1 sd2 st0 st1 cd0 cd1 wd0 wd1
|
||||
|
@ -23,6 +23,7 @@ all_md)
|
|||
makedev kttcp
|
||||
makedev xlcom0
|
||||
makedev cfs
|
||||
makedev ttyVI
|
||||
;;
|
||||
|
||||
# No bpf?
|
||||
|
|
|
@ -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)
|
||||
makedev wscons
|
||||
|
@ -12,4 +12,5 @@ all_md)
|
|||
makedev pci0 pci1 pci2 pci3
|
||||
makedev raid0 raid1 raid2 raid3
|
||||
makedev sysmon
|
||||
makedev ttyVI
|
||||
;;
|
||||
|
|
|
@ -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.
|
||||
all_md)
|
||||
|
@ -51,6 +51,7 @@ all_md)
|
|||
makedev cfs
|
||||
makedev acpi
|
||||
makedev smbios
|
||||
makedev ttyVI
|
||||
;;
|
||||
|
||||
xen)
|
||||
|
|
|
@ -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)
|
||||
makedev wscons sd0 sd1 sd2 st0 st1 cd0 cd1 wd0 wd1 wd2 wd3
|
||||
|
@ -19,6 +19,7 @@ all_md)
|
|||
makedev radio
|
||||
makedev kttcp
|
||||
makedev cfs
|
||||
makedev ttyVI
|
||||
;;
|
||||
|
||||
floppy)
|
||||
|
|
|
@ -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
|
||||
|
||||
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 \
|
||||
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 \
|
||||
|
|
|
@ -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.
|
|
@ -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.
|
||||
#
|
||||
|
@ -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
|
||||
|
|
|
@ -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
|
||||
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
|
||||
|
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue