summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorsimokawa <simokawa@FreeBSD.org>2003-10-24 15:44:10 +0000
committersimokawa <simokawa@FreeBSD.org>2003-10-24 15:44:10 +0000
commitc96f6e4f1d827076592526a3f1f3e7887a9732a4 (patch)
tree2bed02c82f165be07e877314c53ec276a84990ec
parent7248844c9b0757577411885916b1314dfedc3cae (diff)
downloadFreeBSD-src-c96f6e4f1d827076592526a3f1f3e7887a9732a4.zip
FreeBSD-src-c96f6e4f1d827076592526a3f1f3e7887a9732a4.tar.gz
Add dumb console driver and related bits.
dcons(4): very simple console and gdb port driver dcons_crom(4): FireWire attachment dconschat(8): User interface to dcons Tested with: i386, i386-PAE, and sparc64.
-rw-r--r--etc/etc.alpha/ttys2
-rw-r--r--etc/etc.amd64/ttys2
-rw-r--r--etc/etc.i386/ttys2
-rw-r--r--etc/etc.ia64/ttys2
-rw-r--r--etc/etc.sparc64/ttys2
-rw-r--r--share/man/man4/Makefile2
-rw-r--r--share/man/man4/dcons.4112
-rw-r--r--share/man/man4/dcons_crom.459
-rw-r--r--sys/conf/NOTES16
-rw-r--r--sys/conf/files2
-rw-r--r--sys/conf/options6
-rw-r--r--sys/dev/dcons/dcons.c648
-rw-r--r--sys/dev/dcons/dcons.h97
-rw-r--r--sys/dev/dcons/dcons_crom.c224
-rw-r--r--sys/modules/Makefile2
-rw-r--r--sys/modules/dcons/Makefile18
-rw-r--r--sys/modules/dcons_crom/Makefile14
-rw-r--r--usr.sbin/Makefile1
-rw-r--r--usr.sbin/dconschat/Makefile12
-rw-r--r--usr.sbin/dconschat/dconschat.8229
-rw-r--r--usr.sbin/dconschat/dconschat.c959
21 files changed, 2408 insertions, 3 deletions
diff --git a/etc/etc.alpha/ttys b/etc/etc.alpha/ttys
index 169d40c..0a65514 100644
--- a/etc/etc.alpha/ttys
+++ b/etc/etc.alpha/ttys
@@ -50,6 +50,8 @@ ttyd0 "/usr/libexec/getty std.9600" vt100 on secure
ttyd1 "/usr/libexec/getty std.9600" dialup off secure
ttyd2 "/usr/libexec/getty std.9600" dialup off secure
ttyd3 "/usr/libexec/getty std.9600" dialup off secure
+# Dumb console
+dcons "/usr/libexec/getty std.9600" vt100 off secure
# Pseudo terminals
ttyp0 none network
ttyp1 none network
diff --git a/etc/etc.amd64/ttys b/etc/etc.amd64/ttys
index 1e63d4b..fe6a7b1 100644
--- a/etc/etc.amd64/ttys
+++ b/etc/etc.amd64/ttys
@@ -48,6 +48,8 @@ ttyd0 "/usr/libexec/getty std.9600" dialup off secure
ttyd1 "/usr/libexec/getty std.9600" dialup off secure
ttyd2 "/usr/libexec/getty std.9600" dialup off secure
ttyd3 "/usr/libexec/getty std.9600" dialup off secure
+# Dumb console
+dcons "/usr/libexec/getty std.9600" vt100 off secure
# Pseudo terminals
ttyp0 none network
ttyp1 none network
diff --git a/etc/etc.i386/ttys b/etc/etc.i386/ttys
index 1e63d4b..fe6a7b1 100644
--- a/etc/etc.i386/ttys
+++ b/etc/etc.i386/ttys
@@ -48,6 +48,8 @@ ttyd0 "/usr/libexec/getty std.9600" dialup off secure
ttyd1 "/usr/libexec/getty std.9600" dialup off secure
ttyd2 "/usr/libexec/getty std.9600" dialup off secure
ttyd3 "/usr/libexec/getty std.9600" dialup off secure
+# Dumb console
+dcons "/usr/libexec/getty std.9600" vt100 off secure
# Pseudo terminals
ttyp0 none network
ttyp1 none network
diff --git a/etc/etc.ia64/ttys b/etc/etc.ia64/ttys
index cda4be5..d8bdb7e 100644
--- a/etc/etc.ia64/ttys
+++ b/etc/etc.ia64/ttys
@@ -48,6 +48,8 @@ ttyu0 "/usr/libexec/getty std.9600" vt100 on secure
ttyu1 "/usr/libexec/getty std.9600" dialup off secure
ttyu2 "/usr/libexec/getty std.9600" dialup off secure
ttyu3 "/usr/libexec/getty std.9600" dialup off secure
+# Dumb console
+dcons "/usr/libexec/getty std.9600" vt100 off secure
# Pseudo terminals.
ttyp0 none network
ttyp1 none network
diff --git a/etc/etc.sparc64/ttys b/etc/etc.sparc64/ttys
index 54d3578..637e648 100644
--- a/etc/etc.sparc64/ttys
+++ b/etc/etc.sparc64/ttys
@@ -52,6 +52,8 @@ ttyu0 "/usr/libexec/getty std.9600" vt100 on secure
ttyu1 "/usr/libexec/getty std.9600" vt100 on secure
ttyu2 "/usr/libexec/getty std.9600" vt100 off secure
ttyu3 "/usr/libexec/getty std.9600" vt100 off secure
+# Dumb console
+dcons "/usr/libexec/getty std.9600" vt100 off secure
# Pseudo terminals
ttyp0 none network
ttyp1 none network
diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile
index f4a315d..88853f8 100644
--- a/share/man/man4/Makefile
+++ b/share/man/man4/Makefile
@@ -41,6 +41,8 @@ MAN= aac.4 \
cue.4 \
da.4 \
dc.4 \
+ dcons.4 \
+ dcons_crom.4 \
ddb.4 \
de.4 \
devctl.4 \
diff --git a/share/man/man4/dcons.4 b/share/man/man4/dcons.4
new file mode 100644
index 0000000..e89eeba
--- /dev/null
+++ b/share/man/man4/dcons.4
@@ -0,0 +1,112 @@
+.\" Copyright (c) 2003 Hidetoshi Shimokawa
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+.\" DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+.\" INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+.\" (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+.\" SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+.\" ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.\"
+.Dd February 11, 2003
+.Dt DCONS 4
+.Os
+.Sh NAME
+.Nm dcons
+.Nd dumb console device driver
+.Sh SYNOPSIS
+.Cd device dcons
+.Pp
+.Cd options DDB
+.Cd options ALT_BREAK_TO_DEBUGGER
+.Pp
+.Cd device firewire
+.Sh DESCRIPTION
+The
+.Nm
+device is the simple console device which just reads from and writes to
+an allocated buffer for input and output respectivly.
+It is no use by itself and it is supposed that the buffer is accessed
+via a bus like
+.Xr FireWire 4
+for interaction.
+.Pp
+The buffer consists of 4 channels.
+There are 2 ports, one for console tty and other is GDB ports then each port
+has a input channel and a output channel.
+The physical address of the buffer is sometimes neccesary to acess the buffer.
+You can get the address by
+.Xr sysctl 8
+or
+.Xr dmesg 8
+.
+.Sh EXAMPLE
+If you want to run
+.Xr getty 8
+on dcons, insert following line into
+.Xr /etc/ttys 5
+and
+send a HUP signal to
+.Xr init 8
+using
+.Xr kill 1 .
+.Bd -literal -offset indent
+dcons "/usr/libexec/getty std.9600" vt100 on secure
+.Ed
+.Pp
+You can use either of the following commands to obtain physical
+address of the buffer.
+.Bd -literal -offset indent
+% sysctl kern.dcons.paddr
+kern.dcons.paddr: 4732704
+% dmesg | grep dcons: | tail -1
+dcons: virtual 0xc0483720 physical 0x483720 quad 0x120dc8
+.Ed
+.Pp
+In this example, the buffer is located at 4732704 in decimal
+and 0x483720 in hex.
+.Pp
+Once
+.Xr fwochi 4
+device is initialized to allow physical access,
+the buffer can be accessed from another host via FireWire bus using
+.Xr fwchat 8
+application. See
+.Xr fwchat 8
+for more details.
+.Pp
+.Sh FILES
+.Bl -tag -width indent -compact
+.It Pa /dev/dcons
+.It Pa /dev/dconsctl
+.It Pa /etc/ttys
+.El
+.Sh SEE ALSO
+.Xr ddb 4 ,
+.Xr firewire 4 ,
+.Xr fwohci 4 ,
+.Xr fwchat 8 ,
+.Xr fwcontrol 8 ,
+.Xr ttys 5
+.Sh AUTHORS
+.An Hidetoshi Shimokawa Aq simokawa@FreeBSD.org
+.Sh BUGS
+This driver is still under development.
+.Pp
diff --git a/share/man/man4/dcons_crom.4 b/share/man/man4/dcons_crom.4
new file mode 100644
index 0000000..a537f66
--- /dev/null
+++ b/share/man/man4/dcons_crom.4
@@ -0,0 +1,59 @@
+.\" Copyright (c) 2003 Hidetoshi Shimokawa
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+.\" DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+.\" INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+.\" (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+.\" SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+.\" ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.\"
+.Dd June 16, 2003
+.Dt DCONS_CROM 4
+.Os
+.Sh NAME
+.Nm dcons_crom
+.Nd Configuration ROM stub for
+.Xr dcons 4
+.Sh SYNOPSIS
+.Cd device dcons_crom
+.Cd device dcons
+.Cd device firewire
+.Pp
+.Sh DESCRIPTION
+The
+.Nm
+exposes buffer address of
+.Xr dcons 4
+via Configuration ROM which is accessed by
+.Xr FireWire 4
+.
+.El
+.Sh SEE ALSO
+.Xr dcons 4 ,
+.Xr firewire 4 ,
+.Xr fwohci 4 ,
+.Xr fwchat 8 ,
+.Xr fwcontrol 8 ,
+.Sh AUTHORS
+.An Hidetoshi Shimokawa Aq simokawa@FreeBSD.org
+.Sh BUGS
+This driver is still under development.
+.Pp
diff --git a/sys/conf/NOTES b/sys/conf/NOTES
index ecc03d9..7fe40fd 100644
--- a/sys/conf/NOTES
+++ b/sys/conf/NOTES
@@ -2241,11 +2241,21 @@ makeoptions UKBD_DFLT_KEYMAP=it.iso
options UVSCOM_DEFAULT_OPKTSIZE=8 # default output packet size
#####################################################################
-# Firewire support
+# FireWire support
-device firewire # Firewire bus code
+device firewire # FireWire bus code
device sbp # SCSI over Firewire (Requires scbus and da)
-device fwe # Ethernet over Firewire (non-standard!)
+device fwe # Ethernet over FireWire (non-standard!)
+
+#####################################################################
+# dcons support (Dumb Console Device)
+
+device dcons # dumb console driver
+device dcons_crom # FireWire attachment
+options DCONS_BUF_SIZE=16384 # buffer size
+options DCONS_POLL_HZ=100 # polling rate
+options DCONS_FORCE_CONSOLE=0 # force to be the primary console
+options DCONS_FORCE_GDB=1 # force to be the gdb device
#####################################################################
# crypto subsystem
diff --git a/sys/conf/files b/sys/conf/files
index 223dcd5..65ea27d 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -351,6 +351,8 @@ dev/cs/if_cs.c optional cs
dev/cs/if_cs_isa.c optional cs isa
dev/cs/if_cs_pccard.c optional cs card
dev/cs/if_cs_pccard.c optional cs pccard
+dev/dcons/dcons.c optional dcons
+dev/dcons/dcons_crom.c optional dcons_crom
dev/digi/digi.c optional digi
dev/digi/digi_isa.c optional digi isa
dev/digi/digi_pci.c optional digi pci
diff --git a/sys/conf/options b/sys/conf/options
index f08e6a8..0d51447 100644
--- a/sys/conf/options
+++ b/sys/conf/options
@@ -673,3 +673,9 @@ AH_SUPPORT_AR5212 opt_ah.h
AH_DEBUG opt_ah.h
AH_DEBUG_ALQ opt_ah.h
AH_ASSERT opt_ah.h
+
+# dcons options
+DCONS_BUF_SIZE opt_dcons.h
+DCONS_POLL_HZ opt_dcons.h
+DCONS_FORCE_CONSOLE opt_dcons.h
+DCONS_FORCE_GDB opt_dcons.h
diff --git a/sys/dev/dcons/dcons.c b/sys/dev/dcons/dcons.c
new file mode 100644
index 0000000..4cea5ca
--- /dev/null
+++ b/sys/dev/dcons/dcons.c
@@ -0,0 +1,648 @@
+/*
+ * Copyright (C) 2003
+ * Hidetoshi Shimokawa. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ *
+ * This product includes software developed by Hidetoshi Shimokawa.
+ *
+ * 4. Neither the name of the author nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: dcons.c,v 1.65 2003/10/24 03:24:55 simokawa Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/types.h>
+#include <sys/conf.h>
+#include <sys/cons.h>
+#include <sys/consio.h>
+#include <sys/tty.h>
+#include <sys/malloc.h>
+#include <sys/proc.h>
+#include <sys/ucred.h>
+
+#include <machine/bus.h>
+
+#include <dev/dcons/dcons.h>
+
+#include <ddb/ddb.h>
+#include <sys/reboot.h>
+
+#include <sys/sysctl.h>
+
+#include "opt_ddb.h"
+#include "opt_comconsole.h"
+#include "opt_dcons.h"
+
+#ifndef DCONS_POLL_HZ
+#define DCONS_POLL_HZ 100
+#endif
+
+#ifndef DCONS_BUF_SIZE
+#define DCONS_BUF_SIZE (16*1024)
+#endif
+
+#ifndef DCONS_FORCE_CONSOLE
+#define DCONS_FORCE_CONSOLE 0 /* mostly for FreeBSD-4 */
+#endif
+
+#ifndef DCONS_FORCE_GDB
+#define DCONS_FORCE_GDB 1
+#endif
+
+#if __FreeBSD_version >= 500101
+#define CONS_NODEV 1 /* for latest current */
+static struct consdev gdbconsdev;
+#endif
+
+#define CDEV_MAJOR 184
+
+static d_open_t dcons_open;
+static d_close_t dcons_close;
+static d_ioctl_t dcons_ioctl;
+
+static struct cdevsw dcons_cdevsw = {
+#if __FreeBSD_version >= 500104
+ .d_open = dcons_open,
+ .d_close = dcons_close,
+ .d_read = ttyread,
+ .d_write = ttywrite,
+ .d_ioctl = dcons_ioctl,
+ .d_poll = ttypoll,
+ .d_name = "dcons",
+ .d_maj = CDEV_MAJOR,
+#else
+ /* open */ dcons_open,
+ /* close */ dcons_close,
+ /* read */ ttyread,
+ /* write */ ttywrite,
+ /* ioctl */ dcons_ioctl,
+ /* poll */ ttypoll,
+ /* mmap */ nommap,
+ /* strategy */ nostrategy,
+ /* name */ "dcons",
+ /* major */ CDEV_MAJOR,
+ /* dump */ nodump,
+ /* psize */ nopsize,
+ /* flags */ 0,
+#endif
+};
+
+#ifndef KLD_MODULE
+static char bssbuf[DCONS_BUF_SIZE]; /* buf in bss */
+#endif
+struct dcons_buf *dcons_buf;
+size_t dcons_bufsize;
+bus_dma_tag_t dcons_dma_tag = NULL;
+bus_dmamap_t dcons_dma_map = NULL;
+
+static int poll_hz = DCONS_POLL_HZ;
+SYSCTL_NODE(_kern, OID_AUTO, dcons, CTLFLAG_RD, 0, "Dumb Console");
+SYSCTL_INT(_kern_dcons, OID_AUTO, poll_hz, CTLFLAG_RW, &poll_hz, 0,
+ "dcons polling rate");
+
+static int drv_init = 0;
+static struct callout dcons_callout;
+
+/* per device data */
+static struct dcons_softc {
+ dev_t dev;
+ struct dcons_ch o, i;
+ int brk_state;
+#define DC_GDB 1
+ int flags;
+} sc[DCONS_NPORT];
+static void dcons_tty_start(struct tty *);
+static int dcons_tty_param(struct tty *, struct termios *);
+static void dcons_timeout(void *);
+static int dcons_drv_init(int);
+static int dcons_getc(struct dcons_softc *);
+static int dcons_checkc(struct dcons_softc *);
+static void dcons_putc(struct dcons_softc *, int);
+
+static cn_probe_t dcons_cnprobe;
+static cn_init_t dcons_cninit;
+static cn_getc_t dcons_cngetc;
+static cn_checkc_t dcons_cncheckc;
+static cn_putc_t dcons_cnputc;
+
+CONS_DRIVER(dcons, dcons_cnprobe, dcons_cninit, NULL, dcons_cngetc,
+ dcons_cncheckc, dcons_cnputc, NULL);
+
+#if __FreeBSD_version < 500000
+#define THREAD proc
+#else
+#define THREAD thread
+#endif
+
+static int
+dcons_open(dev_t dev, int flag, int mode, struct THREAD *td)
+{
+ struct tty *tp;
+ int unit, error, s;
+
+ unit = minor(dev);
+ if (unit != 0)
+ return (ENXIO);
+
+ tp = dev->si_tty = ttymalloc(dev->si_tty);
+ tp->t_oproc = dcons_tty_start;
+ tp->t_param = dcons_tty_param;
+ tp->t_stop = nottystop;
+ tp->t_dev = dev;
+
+ error = 0;
+
+ s = spltty();
+ if ((tp->t_state & TS_ISOPEN) == 0) {
+ tp->t_state |= TS_CARR_ON;
+ ttychars(tp);
+ tp->t_iflag = TTYDEF_IFLAG;
+ tp->t_oflag = TTYDEF_OFLAG;
+ tp->t_cflag = TTYDEF_CFLAG|CLOCAL;
+ tp->t_lflag = TTYDEF_LFLAG;
+ tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
+ ttsetwater(tp);
+ } else if ((tp->t_state & TS_XCLUDE) && suser(td)) {
+ splx(s);
+ return (EBUSY);
+ }
+ splx(s);
+
+ error = (*linesw[tp->t_line].l_open)(dev, tp);
+
+ return (error);
+}
+
+static int
+dcons_close(dev_t dev, int flag, int mode, struct THREAD *td)
+{
+ int unit;
+ struct tty *tp;
+
+ unit = minor(dev);
+ if (unit != 0)
+ return (ENXIO);
+
+ tp = dev->si_tty;
+ if (tp->t_state & TS_ISOPEN) {
+ (*linesw[tp->t_line].l_close)(tp, flag);
+ ttyclose(tp);
+ }
+
+ return (0);
+}
+
+static int
+dcons_ioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct THREAD *td)
+{
+ int unit;
+ struct tty *tp;
+ int error;
+
+ unit = minor(dev);
+ if (unit != 0)
+ return (ENXIO);
+
+ tp = dev->si_tty;
+ error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, td);
+ if (error != ENOIOCTL)
+ return (error);
+
+ error = ttioctl(tp, cmd, data, flag);
+ if (error != ENOIOCTL)
+ return (error);
+
+ return (ENOTTY);
+}
+
+static int
+dcons_tty_param(struct tty *tp, struct termios *t)
+{
+ tp->t_ispeed = t->c_ispeed;
+ tp->t_ospeed = t->c_ospeed;
+ tp->t_cflag = t->c_cflag;
+ return 0;
+}
+
+static void
+dcons_tty_start(struct tty *tp)
+{
+ struct dcons_softc *dc;
+ int s;
+
+ dc = (struct dcons_softc *)tp->t_dev->si_drv1;
+ s = spltty();
+ if (tp->t_state & (TS_TIMEOUT | TS_TTSTOP)) {
+ ttwwakeup(tp);
+ return;
+ }
+
+ tp->t_state |= TS_BUSY;
+ while (tp->t_outq.c_cc != 0)
+ dcons_putc(dc, getc(&tp->t_outq));
+ tp->t_state &= ~TS_BUSY;
+
+ ttwwakeup(tp);
+ splx(s);
+}
+
+static void
+dcons_timeout(void *v)
+{
+ struct tty *tp;
+ struct dcons_softc *dc;
+ int i, c, polltime;
+
+ for (i = 0; i < DCONS_NPORT; i ++) {
+ dc = &sc[i];
+ tp = dc->dev->si_tty;
+ while ((c = dcons_checkc(dc)) != -1)
+ if (tp->t_state & TS_ISOPEN)
+ (*linesw[tp->t_line].l_rint)(c, tp);
+ }
+ polltime = hz / poll_hz;
+ if (polltime < 1)
+ polltime = 1;
+ callout_reset(&dcons_callout, polltime, dcons_timeout, tp);
+}
+
+static void
+dcons_cnprobe(struct consdev *cp)
+{
+#if __FreeBSD_version >= 501109
+ sprintf(cp->cn_name, "dcons");
+#else
+ cp->cn_dev = makedev(CDEV_MAJOR, DCONS_CON);
+#endif
+#if DCONS_FORCE_CONSOLE
+ cp->cn_pri = CN_REMOTE;
+#else
+ cp->cn_pri = CN_NORMAL;
+#endif
+}
+
+static void
+dcons_cninit(struct consdev *cp)
+{
+ dcons_drv_init(0);
+#if CONS_NODEV
+ cp->cn_arg
+#else
+ cp->cn_dev->si_drv1
+#endif
+ = (void *)&sc[DCONS_CON]; /* share port0 with unit0 */
+}
+
+#if CONS_NODEV
+static int
+dcons_cngetc(struct consdev *cp)
+{
+ return(dcons_getc((struct dcons_softc *)cp->cn_arg));
+}
+static int
+dcons_cncheckc(struct consdev *cp)
+{
+ return(dcons_checkc((struct dcons_softc *)cp->cn_arg));
+}
+static void
+dcons_cnputc(struct consdev *cp, int c)
+{
+ dcons_putc((struct dcons_softc *)cp->cn_arg, c);
+}
+#else
+static int
+dcons_cngetc(dev_t dev)
+{
+ return(dcons_getc((struct dcons_softc *)dev->si_drv1));
+}
+static int
+dcons_cncheckc(dev_t dev)
+{
+ return(dcons_checkc((struct dcons_softc *)dev->si_drv1));
+}
+static void
+dcons_cnputc(dev_t dev, int c)
+{
+ dcons_putc((struct dcons_softc *)dev->si_drv1, c);
+}
+#endif
+
+static int
+dcons_getc(struct dcons_softc *dc)
+{
+ int c;
+
+ while ((c = dcons_checkc(dc)) == -1);
+
+ return (c & 0xff);
+}
+
+static int
+dcons_checkc(struct dcons_softc *dc)
+{
+ unsigned char c;
+ u_int32_t ptr, pos, gen, next_gen;
+ struct dcons_ch *ch;
+
+ ch = &dc->i;
+
+ if (dcons_dma_tag != NULL)
+ bus_dmamap_sync(dcons_dma_tag, dcons_dma_map,
+ BUS_DMASYNC_POSTREAD);
+ ptr = ntohl(*ch->ptr);
+ gen = ptr >> DCONS_GEN_SHIFT;
+ pos = ptr & DCONS_POS_MASK;
+ if (gen == ch->gen && pos == ch->pos)
+ return (-1);
+
+ next_gen = DCONS_NEXT_GEN(ch->gen);
+ /* XXX sanity check */
+ if ((gen != ch->gen && gen != next_gen)
+ || (gen == ch->gen && pos < ch->pos)) {
+ /* generation skipped !! */
+ /* XXX discard */
+ ch->gen = gen;
+ ch->pos = pos;
+ return (-1);
+ }
+
+ c = ch->buf[ch->pos];
+ ch->pos ++;
+ if (ch->pos >= ch->size) {
+ ch->gen = next_gen;
+ ch->pos = 0;
+ }
+
+#if DDB && ALT_BREAK_TO_DEBUGGER
+ switch (dc->brk_state) {
+ case STATE1:
+ if (c == KEY_TILDE)
+ dc->brk_state = STATE2;
+ else
+ dc->brk_state = STATE0;
+ break;
+ case STATE2:
+ dc->brk_state = STATE0;
+ if (c == KEY_CTRLB) {
+#if DCONS_FORCE_GDB
+ if (dc->flags & DC_GDB)
+ boothowto |= RB_GDB;
+#endif
+ breakpoint();
+ }
+ }
+ if (c == KEY_CR)
+ dc->brk_state = STATE1;
+#endif
+ return (c);
+}
+
+static void
+dcons_putc(struct dcons_softc *dc, int c)
+{
+ struct dcons_ch *ch;
+
+ ch = &dc->o;
+
+ ch->buf[ch->pos] = c;
+ ch->pos ++;
+ if (ch->pos >= ch->size) {
+ ch->gen = DCONS_NEXT_GEN(ch->gen);
+ ch->pos = 0;
+ }
+ *ch->ptr = DCONS_MAKE_PTR(ch);
+ if (dcons_dma_tag != NULL)
+ bus_dmamap_sync(dcons_dma_tag, dcons_dma_map,
+ BUS_DMASYNC_PREWRITE);
+}
+
+static int
+dcons_init_port(int port, int offset, int size)
+{
+ int osize;
+ struct dcons_softc *dc;
+
+ dc = &sc[port];
+
+ osize = size * 3 / 4;
+
+ dc->o.size = osize;
+ dc->i.size = size - osize;
+ dc->o.buf = (char *)dcons_buf + offset;
+ dc->i.buf = dc->o.buf + osize;
+ dc->o.gen = dc->i.gen = 0;
+ dc->o.pos = dc->i.pos = 0;
+ dc->o.ptr = &dcons_buf->optr[port];
+ dc->i.ptr = &dcons_buf->iptr[port];
+ dc->brk_state = STATE0;
+ dcons_buf->osize[port] = htonl(osize);
+ dcons_buf->isize[port] = htonl(size - osize);
+ dcons_buf->ooffset[port] = htonl(offset);
+ dcons_buf->ioffset[port] = htonl(offset + osize);
+ dcons_buf->optr[port] = DCONS_MAKE_PTR(&dc->o);
+ dcons_buf->iptr[port] = DCONS_MAKE_PTR(&dc->i);
+
+ return(0);
+}
+
+static int
+dcons_drv_init(int stage)
+{
+ int size, size0, offset;
+
+ if (drv_init)
+ return(drv_init);
+
+ drv_init = -1;
+
+ dcons_bufsize = DCONS_BUF_SIZE;
+
+#ifndef KLD_MODULE
+ if (stage == 0) /* XXX or cold */
+ /*
+ * DCONS_FORCE_CONSOLE == 1 and statically linked.
+ * called from cninit(). can't use contigmalloc yet .
+ */
+ dcons_buf = (struct dcons_buf *) bssbuf;
+ else
+#endif
+ /*
+ * DCONS_FORCE_CONSOLE == 0 or kernel module case.
+ * if the module is loaded after boot,
+ * dcons_buf could be non-continuous.
+ */
+ dcons_buf = (struct dcons_buf *) contigmalloc(dcons_bufsize,
+ M_DEVBUF, 0, 0x10000, 0xffffffff, PAGE_SIZE, 0ul);
+
+ offset = DCONS_HEADER_SIZE;
+ size = (dcons_bufsize - offset);
+ size0 = size * 3 / 4;
+
+ dcons_init_port(0, offset, size0);
+ offset += size0;
+ dcons_init_port(1, offset, size - size0);
+ dcons_buf->version = htonl(DCONS_VERSION);
+ dcons_buf->magic = ntohl(DCONS_MAGIC);
+
+#if DDB && DCONS_FORCE_GDB
+#if CONS_NODEV
+ gdbconsdev.cn_arg = (void *)&sc[DCONS_GDB];
+#if __FreeBSD_version >= 501109
+ sprintf(gdbconsdev.cn_name, "dgdb");
+#endif
+ gdb_arg = &gdbconsdev;
+#else
+ gdbdev = makedev(CDEV_MAJOR, DCONS_GDB);
+#endif
+ gdb_getc = dcons_cngetc;
+ gdb_putc = dcons_cnputc;
+#endif
+ drv_init = 1;
+
+ return 0;
+}
+
+
+static int
+dcons_attach_port(int port, char *name, int flags)
+{
+ struct dcons_softc *dc;
+ struct tty *tp;
+
+ dc = &sc[port];
+ dc->flags = flags;
+ dc->dev = make_dev(&dcons_cdevsw, port,
+ UID_ROOT, GID_WHEEL, 0600, name);
+ tp = ttymalloc(NULL);
+
+ dc->dev->si_drv1 = (void *)dc;
+ dc->dev->si_tty = tp;
+
+ tp->t_oproc = dcons_tty_start;
+ tp->t_param = dcons_tty_param;
+ tp->t_stop = nottystop;
+ tp->t_dev = dc->dev;
+
+ return(0);
+}
+
+static int
+dcons_attach(void)
+{
+ int polltime;
+
+ dcons_attach_port(DCONS_CON, "dcons", 0);
+ dcons_attach_port(DCONS_GDB, "dgdb", DC_GDB);
+#if __FreeBSD_version < 500000
+ callout_init(&dcons_callout);
+#else
+ callout_init(&dcons_callout, 0);
+#endif
+ polltime = hz / poll_hz;
+ if (polltime < 1)
+ polltime = 1;
+ callout_reset(&dcons_callout, polltime, dcons_timeout, NULL);
+ return(0);
+}
+
+static int
+dcons_detach(int port)
+{
+ struct tty *tp;
+ struct dcons_softc *dc;
+
+ dc = &sc[port];
+
+ tp = dc->dev->si_tty;
+
+ if (tp->t_state & TS_ISOPEN) {
+ printf("dcons: still opened\n");
+ (*linesw[tp->t_line].l_close)(tp, 0);
+ tp->t_gen++;
+ ttyclose(tp);
+ ttwakeup(tp);
+ ttwwakeup(tp);
+ }
+ /* XXX
+ * must wait until all device are closed.
+ */
+ tsleep((void *)dc, PWAIT, "dcodtc", hz/4);
+ destroy_dev(dc->dev);
+
+ return(0);
+}
+
+
+/* cnXXX works only for FreeBSD-5 */
+static int
+dcons_modevent(module_t mode, int type, void *data)
+{
+ int err = 0, ret;
+
+ switch (type) {
+ case MOD_LOAD:
+ ret = dcons_drv_init(1);
+ dcons_attach();
+#if __FreeBSD_version >= 500000
+ if (ret == 0) {
+ dcons_cnprobe(&dcons_consdev);
+ dcons_cninit(&dcons_consdev);
+ cnadd(&dcons_consdev);
+ }
+#endif
+ break;
+ case MOD_UNLOAD:
+ printf("dcons: unload\n");
+ callout_stop(&dcons_callout);
+#if DDB && DCONS_FORCE_GDB
+#if CONS_NODEV
+ gdb_arg = NULL;
+#else
+ gdbdev = NODEV;
+#endif
+#endif
+#if __FreeBSD_version >= 500000
+ cnremove(&dcons_consdev);
+#endif
+ dcons_detach(DCONS_CON);
+ dcons_detach(DCONS_GDB);
+ dcons_buf->magic = 0;
+
+ contigfree(dcons_buf, DCONS_BUF_SIZE, M_DEVBUF);
+
+ break;
+ case MOD_SHUTDOWN:
+ break;
+ }
+ return(err);
+}
+
+DEV_MODULE(dcons, dcons_modevent, NULL);
+MODULE_VERSION(dcons, DCONS_VERSION);
diff --git a/sys/dev/dcons/dcons.h b/sys/dev/dcons/dcons.h
new file mode 100644
index 0000000..73265bc
--- /dev/null
+++ b/sys/dev/dcons/dcons.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2002
+ * Hidetoshi Shimokawa. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ *
+ * This product includes software developed by Hidetoshi Shimokawa.
+ *
+ * 4. Neither the name of the author nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: dcons.h,v 1.15 2003/10/23 15:05:31 simokawa Exp $
+ * $FreeBSD$
+ */
+
+#ifdef _KERNEL
+#define V volatile
+#else
+#define V
+#endif
+
+#define DCONS_NPORT 2
+#define DCONS_CON 0
+#define DCONS_GDB 1
+
+struct dcons_buf {
+#define DCONS_VERSION 2
+ V u_int32_t version;
+ V u_int32_t ooffset[DCONS_NPORT];
+ V u_int32_t ioffset[DCONS_NPORT];
+ V u_int32_t osize[DCONS_NPORT];
+ V u_int32_t isize[DCONS_NPORT];
+#define DCONS_MAGIC 0x64636f6e /* "dcon" */
+ V u_int32_t magic;
+#define DCONS_GEN_SHIFT (24)
+#define DCONS_GEN_MASK (0xff)
+#define DCONS_POS_MASK ((1<< DCONS_GEN_SHIFT) - 1)
+ V u_int32_t optr[DCONS_NPORT];
+ V u_int32_t iptr[DCONS_NPORT];
+ V char buf[0];
+};
+
+#define DCONS_CSR_VAL_VER 0x64636f /* "dco" */
+#define DCONS_CSR_KEY_HI 0x3a
+#define DCONS_CSR_KEY_LO 0x3b
+
+#define DCONS_HEADER_SIZE sizeof(struct dcons_buf)
+#define DCONS_MAKE_PTR(x) htonl(((x)->gen << DCONS_GEN_SHIFT) | (x)->pos)
+#define DCONS_NEXT_GEN(x) (((x) + 1) & DCONS_GEN_MASK)
+
+struct dcons_ch {
+ u_int32_t size;
+ u_int32_t gen;
+ u_int32_t pos;
+#ifdef _KERNEL
+ V u_int32_t *ptr;
+ V char *buf;
+#else
+ off_t buf;
+#endif
+};
+
+#define KEY_CTRLB 2 /* ^B */
+#define KEY_CR 13 /* CR '\r' */
+#define KEY_TILDE 126 /* ~ */
+#define STATE0 0
+#define STATE1 1
+#define STATE2 2
+
+#ifdef _KERNEL
+extern struct dcons_buf *dcons_buf;
+extern size_t dcons_bufsize;
+extern bus_dma_tag_t dcons_dma_tag;
+extern bus_dmamap_t dcons_dma_map;
+#endif
diff --git a/sys/dev/dcons/dcons_crom.c b/sys/dev/dcons/dcons_crom.c
new file mode 100644
index 0000000..d7cd502
--- /dev/null
+++ b/sys/dev/dcons/dcons_crom.c
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2003
+ * Hidetoshi Shimokawa. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ *
+ * This product includes software developed by Hidetoshi Shimokawa.
+ *
+ * 4. Neither the name of the author nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: dcons_crom.c,v 1.8 2003/10/23 15:47:21 simokawa Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/systm.h>
+#include <sys/types.h>
+#include <sys/conf.h>
+#include <sys/malloc.h>
+
+#include <sys/bus.h>
+#include <machine/bus.h>
+
+#include <dev/firewire/firewire.h>
+#include <dev/firewire/firewirereg.h>
+#include <dev/firewire/iec13213.h>
+#include <dev/dcons/dcons.h>
+
+static bus_addr_t dcons_paddr;
+
+#ifndef CSRVAL_VENDOR_PRIVATE
+#define NEED_NEW_DRIVER
+#endif
+
+#define ADDR_HI(x) (((x) >> 24) & 0xffffff)
+#define ADDR_LO(x) ((x) & 0xffffff)
+
+struct dcons_crom_softc {
+ struct firewire_dev_comm fd;
+ struct crom_chunk unit;
+ struct crom_chunk spec;
+ struct crom_chunk ver;
+ bus_dma_tag_t dma_tag;
+ bus_dmamap_t dma_map;
+ bus_addr_t bus_addr;
+};
+
+static void
+dcons_crom_identify(driver_t *driver, device_t parent)
+{
+ BUS_ADD_CHILD(parent, 0, "dcons_crom", device_get_unit(parent));
+}
+
+static int
+dcons_crom_probe(device_t dev)
+{
+ device_t pa;
+
+ pa = device_get_parent(dev);
+ if(device_get_unit(dev) != device_get_unit(pa)){
+ return(ENXIO);
+ }
+
+ device_set_desc(dev, "dcons configuration ROM");
+ return (0);
+}
+
+#ifndef NEED_NEW_DRIVER
+static void
+dcons_crom_post_busreset(void *arg)
+{
+ struct dcons_crom_softc *sc;
+ struct crom_src *src;
+ struct crom_chunk *root;
+
+ sc = (struct dcons_crom_softc *) arg;
+ src = sc->fd.fc->crom_src;
+ root = sc->fd.fc->crom_root;
+
+ bzero(&sc->unit, sizeof(struct crom_chunk));
+
+ crom_add_chunk(src, root, &sc->unit, CROM_UDIR);
+ crom_add_entry(&sc->unit, CSRKEY_SPEC, CSRVAL_VENDOR_PRIVATE);
+ crom_add_simple_text(src, &sc->unit, &sc->spec, "FreeBSD");
+ crom_add_entry(&sc->unit, CSRKEY_VER, DCONS_CSR_VAL_VER);
+ crom_add_simple_text(src, &sc->unit, &sc->ver, "dcons");
+ crom_add_entry(&sc->unit, DCONS_CSR_KEY_HI, ADDR_HI(dcons_paddr));
+ crom_add_entry(&sc->unit, DCONS_CSR_KEY_LO, ADDR_LO(dcons_paddr));
+}
+#endif
+
+static void
+dmamap_cb(void *arg, bus_dma_segment_t *segments, int seg, int error)
+{
+ bus_addr_t *ptr;
+
+ if (error)
+ printf("dcons_dmamap_cb: error=%d\n", error);
+
+ ptr = (bus_addr_t *) arg;
+ *ptr = segments[0].ds_addr;
+}
+
+static int
+dcons_crom_attach(device_t dev)
+{
+#ifdef NEED_NEW_DRIVER
+ printf("dcons_crom: you need newer firewire driver\n");
+ return (-1);
+#else
+ struct dcons_crom_softc *sc;
+
+ sc = (struct dcons_crom_softc *) device_get_softc(dev);
+ sc->fd.fc = device_get_ivars(dev);
+ sc->fd.dev = dev;
+ sc->fd.post_explore = NULL;
+ sc->fd.post_busreset = (void *) dcons_crom_post_busreset;
+
+ /* map dcons buffer */
+ bus_dma_tag_create(
+ /*parent*/ sc->fd.fc->dmat,
+ /*alignment*/ sizeof(u_int32_t),
+ /*boundary*/ 0,
+ /*lowaddr*/ BUS_SPACE_MAXADDR,
+ /*highaddr*/ BUS_SPACE_MAXADDR,
+ /*filter*/NULL, /*filterarg*/NULL,
+ /*maxsize*/ dcons_bufsize,
+ /*nsegments*/ 1,
+ /*maxsegsz*/ BUS_SPACE_MAXSIZE_32BIT,
+ /*flags*/ BUS_DMA_ALLOCNOW,
+#if __FreeBSD_version >= 501102
+ /*lockfunc*/busdma_lock_mutex,
+ /*lockarg*/&Giant,
+#endif
+ &sc->dma_tag);
+ bus_dmamap_create(sc->dma_tag, 0, &sc->dma_map);
+ bus_dmamap_load(sc->dma_tag, sc->dma_map,
+ (void *)dcons_buf, dcons_bufsize,
+ dmamap_cb, &sc->bus_addr, 0);
+ bus_dmamap_sync(sc->dma_tag, sc->dma_map, BUS_DMASYNC_PREWRITE);
+ device_printf(sc->fd.dev,
+#if __FreeBSD_version < 500000
+ "bus_addr 0x%x\n", sc->bus_addr);
+#else
+ "bus_addr 0x%jx\n", (uintmax_t)sc->bus_addr);
+#endif
+ if (dcons_paddr != 0) {
+ /* XXX */
+ device_printf(sc->fd.dev, "dcons_paddr is already set\n");
+ return (0);
+ }
+ dcons_dma_tag = sc->dma_tag;
+ dcons_dma_map = sc->dma_map;
+ dcons_paddr = sc->bus_addr;
+ return (0);
+#endif
+}
+
+static int
+dcons_crom_detach(device_t dev)
+{
+ struct dcons_crom_softc *sc;
+
+ sc = (struct dcons_crom_softc *) device_get_softc(dev);
+ sc->fd.post_busreset = NULL;
+
+ /* XXX */
+ if (dcons_dma_tag == sc->dma_tag)
+ dcons_dma_tag = NULL;
+
+ bus_dmamap_unload(sc->dma_tag, sc->dma_map);
+ bus_dmamap_destroy(sc->dma_tag, sc->dma_map);
+ bus_dma_tag_destroy(sc->dma_tag);
+
+ return 0;
+}
+
+static devclass_t dcons_crom_devclass;
+
+static device_method_t dcons_crom_methods[] = {
+ /* device interface */
+ DEVMETHOD(device_identify, dcons_crom_identify),
+ DEVMETHOD(device_probe, dcons_crom_probe),
+ DEVMETHOD(device_attach, dcons_crom_attach),
+ DEVMETHOD(device_detach, dcons_crom_detach),
+ { 0, 0 }
+};
+
+static driver_t dcons_crom_driver = {
+ "dcons_crom",
+ dcons_crom_methods,
+ sizeof(struct dcons_crom_softc),
+};
+
+DRIVER_MODULE(dcons_crom, firewire, dcons_crom_driver,
+ dcons_crom_devclass, 0, 0);
+MODULE_VERSION(dcons_crom, 1);
+MODULE_DEPEND(dcons_crom, dcons,
+ DCONS_VERSION, DCONS_VERSION, DCONS_VERSION);
+MODULE_DEPEND(dcons_crom, firewire, 1, 1, 1);
diff --git a/sys/modules/Makefile b/sys/modules/Makefile
index fe84da9..f2762d2 100644
--- a/sys/modules/Makefile
+++ b/sys/modules/Makefile
@@ -32,6 +32,8 @@ SUBDIR= accf_data \
${_cryptodev} \
cue \
dc \
+ dcons \
+ dcons_crom \
de \
digi \
dummynet \
diff --git a/sys/modules/dcons/Makefile b/sys/modules/dcons/Makefile
new file mode 100644
index 0000000..e063f61
--- /dev/null
+++ b/sys/modules/dcons/Makefile
@@ -0,0 +1,18 @@
+# $Id: Makefile,v 1.6 2003/10/24 15:41:26 simokawa Exp $
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../dev/dcons
+
+KMOD = dcons
+SRCS = dcons.c dcons.h \
+ opt_dcons.h opt_ddb.h opt_comconsole.h
+
+opt_ddb.h:
+ echo "#define DDB 1" > $@
+
+opt_comconsole.h:
+ echo "#define ALT_BREAK_TO_DEBUGGER 1" > $@
+
+CFLAGS+= -I${.CURDIR}/../..
+
+.include <bsd.kmod.mk>
diff --git a/sys/modules/dcons_crom/Makefile b/sys/modules/dcons_crom/Makefile
new file mode 100644
index 0000000..74c168e
--- /dev/null
+++ b/sys/modules/dcons_crom/Makefile
@@ -0,0 +1,14 @@
+# $Id: Makefile,v 1.6 2003/10/24 15:43:24 simokawa Exp $
+# $FreeBSD$
+
+.PATH: ${.CURDIR}/../../dev/dcons
+
+KMOD = dcons_crom
+SRCS = dcons_crom.c dcons.h \
+ bus_if.h device_if.h
+
+#KMODDEPS = firewire dcons
+
+CFLAGS+= -I${.CURDIR}/../..
+
+.include <bsd.kmod.mk>
diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile
index ab24b69..2348224 100644
--- a/usr.sbin/Makefile
+++ b/usr.sbin/Makefile
@@ -25,6 +25,7 @@ SUBDIR= IPXrouted \
crunch \
ctm \
daemon \
+ dconschat \
devinfo \
digictl \
diskinfo \
diff --git a/usr.sbin/dconschat/Makefile b/usr.sbin/dconschat/Makefile
new file mode 100644
index 0000000..9993ece
--- /dev/null
+++ b/usr.sbin/dconschat/Makefile
@@ -0,0 +1,12 @@
+# $FreeBSD$
+
+PROG= dconschat
+SRCS= dconschat.c
+MAN= dconschat.8
+
+CFLAGS+= -I${.CURDIR}/../../sys
+
+DPADD= ${LIBKVM}
+LDADD= -lkvm
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/dconschat/dconschat.8 b/usr.sbin/dconschat/dconschat.8
new file mode 100644
index 0000000..4ed9d44
--- /dev/null
+++ b/usr.sbin/dconschat/dconschat.8
@@ -0,0 +1,229 @@
+.\" Copyright (c) 2003 Hidetoshi Shimokawa
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+.\" DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+.\" INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+.\" (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+.\" SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+.\" ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.\"
+.Dd February 11, 2003
+.Dt DCONSCHAT 8
+.Os
+.Sh NAME
+.Nm dconschat
+.Nd user interface to dcons
+.Sh SYNOPSIS
+.Nm
+.Op Fl brvwRT1
+.Op Fl h Ar hz
+.Op Fl C Ar console_port
+.Op Fl G Ar gdb_port
+.Op Fl M Ar core
+.Op Fl N Ar system
+.Nm
+.Op Fl brvwR1
+.Op Fl h Ar hz
+.Op Fl C Ar console_port
+.Op Fl G Ar gdb_port
+.Op Fl a Ar address
+.Op Fl u Ar bus_num
+.Fl t Ar target_eui64
+.Sh DESCRIPTION
+The
+.Nm
+utility is designed to provide a way for users to access
+.Xr dcons 4
+(dumb console device) on a local or remote system.
+The
+.Nm
+interacts with
+.Xr dcons 4
+using
+.Xr kvm 3
+or
+.Xr firewire 4
+and interact with a user over tty or TCP/IP.
+To access remote
+.Xr dcons 4
+using
+.Xr firewire 4 ,
+you have to specify target EUI64 address by
+.Fl t
+option.
+.Pp
+The
+.Nm
+and
+.Xr dcons 4
+communicate using 2 port, one for console port and the other for
+remote gdb port.
+Users are supposed to access
+.Nm
+using tty, telnet and gdb.
+You can specify listen ports for console and gdb port by
+.Fl C
+and
+.Fl G
+options respectively. The port number 0 has special meaning that
+current tty(stdin/out) is used instead of TCP/IP.
+A negative port number will disable the port.
+To quit dconschat, send a CR + '~' + '.' sequence to the console port
+or send signal to the process.
+.Pp
+By analogy with
+.Xr pty 4
+device, the
+.Xr dcons 4
+acts as a slave device and
+.Nm
+acts as a master device with
+.Xr telnetd 8 .
+.Pp
+.Bl -tag -width indent
+.It Fl b
+Translate Ctrl-C to ALT_BREAK(CR + '~' + Ctrl-B) on gdb port.
+.It Fl r
+Replay old buffer on connection.
+.It Fl v
+Verbose debug output. Multiple '-v' increase verbosity.
+.It Fl w
+Listen on wildcard address rather than localhost.
+.It Fl R
+Read-only. Don't write anything on dcons buffer.
+.It Fl T
+Enable ad hoc workaround for telnet protocol to
+remove unnecessary byte sequences.
+It should be set when you access dconschat using telnet.
+.It Fl 1
+One-Shot. Read available buffer then exit. This implies
+.Fl r
+option.
+.It Fl h Ar hz
+Specify polling rate. The default value is 100.
+.It Fl C Ar console_port
+Specify console port. The default value is 0(stdin/stdout).
+.It Fl G Ar gdb_port
+Specify gdb port.. The default value is -1(disabled).
+.It Fl M Ar core
+Specify core file.
+.It Fl N Ar system
+Specify system file such as /boot/kernel/kernel.
+.It Fl t Ar target_eui64
+Specify the 64bit extended unique identifier of the target and use FireWire to access remote
+.Xr dcons 4 .
+.It Fl a Ar address
+Specify the physical/IO address of the dcons buffer. See
+.Xr dcons 4
+for details.
+If this option is not specified,
+.Nm
+tries to get the address from the Configuration ROM on the target.
+You are supposed to enable
+.Xr dcons_crom 4
+on the target to omit this option.
+.It Fl u Ar bus_num
+Specify FireWire bus number. The default is 0.
+.El
+.Sh EXAMPLE
+To use
+.Nm
+with FireWire for remote
+.Xr dcons ,
+you have to specify the eui64 of the target.
+You can obtain EUI64 by running
+.Xr fwcontorl 4
+without options.
+The first EUI64 is of the host running fwcontrol and others on the
+bus follow.
+.Bd -literal -offset indent
+# fwcontrol
+2 devices (info_len=2)
+node EUI64 status
+ 1 0x7766554433221100 0
+ 0 0x0011223344556677 1
+.Ed
+.Pp
+The EUI64 doesn't change unless you change the hardware
+as the ethernet address.
+.Pp
+Now we can run the
+.Nm .
+.Bd -literal -offset indent
+# dconschat -br -G 12345 -t 0x00112233445566677
+.Ed
+.Pp
+You'll get console output of the target and login prompt if a getty is
+running on dcons. You can break to DDB with ALT_BREAK (CR + '~' + Ctrl-B)
+if DDB and ALT_BREAK_TO_DEBUGGER is enabled in the target kernel.
+To quit the session, type CR + '~' + '.' in the console port.
+.Pp
+Using gdb port is almost the same as remote gdb over serial line except
+using TCP/IP instead of /dev/cu*. See
+"On-line Kernel Debugging Using Remote GDB"
+section of The FreeBSD Developers Handbook.
+.Bd -literal -offset indent
+% gdb -k kernel.debug
+(kgdb) target remote :12345
+.Ed
+.Pp
+Once gdb is attached and you specified '-b' option to dconschat,
+typing "Ctrl-C" on gdb causes break to debugger.
+.Pp
+The following command get console log from crash dump:
+.Bd -literal -offset indent
+# dconschat -1 -M vmcore.0 -N kernel.0
+.Ed
+.Pp
+If you want access to the console using telnet, try the following:
+.Bd -literal -offset indent
+# dconschat -rTC 5555 &
+# telnet localhost 5555
+.Ed
+.Pp
+You may want to keep logging console output of several machines. Conserve-com
+in the ports collection should help you. Insert the following lines
+in the conserver.cf
+.Bd -literal -offset indent
+local:|/usr/sbin/dconschat -rh 25::&:
+remote:|/usr/sbin/dconschat -rh 25 -t 0x00112233445566677::&:
+.Ed
+.Sh FILES
+.Bl -tag -width indent
+.It Pa /dev/fwmem0.0
+.It Pa /dev/mem
+.It Pa /dev/kmem
+.El
+.Sh SEE ALSO
+.Xr gdb 1 ,
+.Xr telnet 1 ,
+.Xr kvm 3 ,
+.Xr dcons 4 ,
+.Xr dcons_crom 4 ,
+.Xr ddb 4 ,
+.Xr firewire 4 ,
+.Xr fwohci 4 ,
+.Xr fwcontrol 8
+.Sh AUTHORS
+.An Hidetoshi Shimokawa Aq simokawa@FreeBSD.org
+.Sh BUGS
+This utility is still under development.
+.Pp
diff --git a/usr.sbin/dconschat/dconschat.c b/usr.sbin/dconschat/dconschat.c
new file mode 100644
index 0000000..2a982ab
--- /dev/null
+++ b/usr.sbin/dconschat/dconschat.c
@@ -0,0 +1,959 @@
+/*
+ * Copyright (C) 2003
+ * Hidetoshi Shimokawa. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ *
+ * This product includes software developed by Hidetoshi Shimokawa.
+ *
+ * 4. Neither the name of the author nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: dconschat.c,v 1.76 2003/10/23 06:21:13 simokawa Exp $
+ * $FreeBSD$
+ */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/uio.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <termios.h>
+#include <dev/dcons/dcons.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#include <err.h>
+#include <string.h>
+#include <sys/event.h>
+#include <sys/time.h>
+#include <arpa/telnet.h>
+
+#include <sys/ioccom.h>
+#include <dev/firewire/firewire.h>
+#include <dev/firewire/iec13213.h>
+
+#include <kvm.h>
+#include <nlist.h>
+
+#include <sys/errno.h>
+
+#define DCONS_POLL_HZ 100
+
+#define RETRY 3
+
+#ifdef CSRVAL_VENDOR_PRIVATE
+#define USE_CROM 1
+#else
+#define USE_CROM 0
+#endif
+
+int verbose = 0;
+int tc_set = 0;
+
+#define IS_CONSOLE(p) ((p)->port == 0)
+#define IS_GDB(p) ((p)->port == 1)
+
+static struct dcons_state {
+ int fd;
+ kvm_t *kd;
+ int kq;
+ off_t paddr;
+#define F_READY (1 << 1)
+#define F_RD_ONLY (1 << 2)
+#define F_ALT_BREAK (1 << 3)
+#define F_TELNET (1 << 4)
+#define F_USE_CROM (1 << 5)
+#define F_ONE_SHOT (1 << 6)
+#define F_REPLAY (1 << 7)
+ int flags;
+ enum {
+ TYPE_KVM,
+ TYPE_FW
+ } type;
+ int escape_state;
+ struct dcons_port {
+ int port;
+ struct dcons_ch o;
+ struct dcons_ch i;
+ u_int32_t optr;
+ u_int32_t iptr;
+ int s;
+ int infd;
+ int outfd;
+ struct addrinfo *res;
+ int skip_read;
+ } port[DCONS_NPORT];
+ struct timespec to;
+ struct timespec zero;
+ struct termios tsave;
+} sc;
+
+static int
+dread(struct dcons_state *dc, void *buf, size_t n, off_t offset)
+{
+ switch (dc->type) {
+ case TYPE_FW:
+ return (pread(dc->fd, buf, n, offset));
+ case TYPE_KVM:
+ return (kvm_read(dc->kd, offset, buf, n));
+ }
+ return (-1);
+}
+
+static int
+dwrite(struct dcons_state *dc, void *buf, size_t n, off_t offset)
+{
+ if ((dc->flags & F_RD_ONLY) != 0)
+ return (n);
+
+ switch (dc->type) {
+ case TYPE_FW:
+ return (pwrite(dc->fd, buf, n, offset));
+ case TYPE_KVM:
+ return (kvm_write(dc->kd, offset, buf, n));
+ }
+ return (-1);
+}
+
+static void
+dconschat_cleanup(int sig)
+{
+ struct dcons_state *dc;
+
+ dc = &sc;
+ if (tc_set != 0)
+ tcsetattr(STDIN_FILENO, TCSADRAIN, &dc->tsave);
+
+ if (sig > 0)
+ printf("\ndconschat exiting with signal %d ...\n", sig);
+ else
+ printf("\ndconschat exiting...\n");
+ exit(0);
+}
+
+#if USE_CROM
+static int
+dconschat_get_crom(struct dcons_state *dc)
+{
+ off_t addr;
+ int i, state = 0;
+ u_int32_t buf, hi = 0, lo = 0;
+ struct csrreg *reg;
+
+ reg = (struct csrreg *)&buf;
+ addr = 0xffff;
+ addr = (addr << 32) | 0xf0000400;
+ for (i = 20; i < 0x400; i += 4) {
+ if (dread(dc, &buf, 4, addr + i) < 0) {
+ if (verbose)
+ warn("crom read faild");
+ return (-1);
+ }
+ buf = ntohl(buf);
+ if (verbose)
+ printf("%d %02x %06x\n", state, reg->key, reg->val);
+ switch (state) {
+ case 0:
+ if (reg->key == CSRKEY_SPEC &&
+ reg->val == CSRVAL_VENDOR_PRIVATE)
+ state = 1;
+ break;
+ case 1:
+ if (reg->key == CSRKEY_VER &&
+ reg->val == DCONS_CSR_VAL_VER)
+ state = 2;
+ break;
+ case 2:
+ if (reg->key == DCONS_CSR_KEY_HI)
+ hi = reg->val;
+ else if (reg->key == DCONS_CSR_KEY_LO) {
+ lo = reg->val;
+ goto out;
+ }
+ break;
+ }
+ }
+ /* not found */
+ return (-1);
+out:
+ if (verbose)
+ printf("addr: %06x %06x\n", hi, lo);
+ dc->paddr = ((off_t)hi << 24) | lo;
+ return (0);
+}
+#endif
+
+static void
+dconschat_ready(struct dcons_state *dc, int ready, char *reason)
+{
+ static char oldreason[64] = "";
+ int old;
+
+ old = (dc->flags & F_READY) ? 1 : 0;
+
+ if (ready) {
+ dc->flags |= F_READY;
+ if (ready != old)
+ printf("[dcons connected]\r\n");
+ oldreason[0] = 0;
+ } else {
+ dc->flags &= ~F_READY;
+ if (strncmp(oldreason, reason, sizeof(oldreason)) != 0) {
+ printf("[dcons disconnected (%s)]\r\n", reason);
+ strlcpy(oldreason, reason, sizeof(oldreason));
+ }
+ }
+}
+
+static int
+dconschat_fetch_header(struct dcons_state *dc)
+{
+ char ebuf[64];
+ struct dcons_buf dbuf;
+ int j;
+
+#if USE_CROM
+ if (dc->paddr == 0 && (dc->flags & F_USE_CROM) != 0) {
+ if (dconschat_get_crom(dc)) {
+ dconschat_ready(dc, 0, "get crom failed");
+ return (-1);
+ }
+ }
+#endif
+
+ if (dread(dc, &dbuf, DCONS_HEADER_SIZE, dc->paddr) < 0) {
+ dconschat_ready(dc, 0, "read header failed");
+ return (-1);
+ }
+ if (dbuf.magic != htonl(DCONS_MAGIC)) {
+ if ((dc->flags & F_USE_CROM) !=0)
+ dc->paddr = 0;
+ snprintf(ebuf, sizeof(ebuf), "wrong magic 0x%08x", dbuf.magic);
+ dconschat_ready(dc, 0, ebuf);
+ return (-1);
+ }
+ if (ntohl(dbuf.version) != DCONS_VERSION) {
+ snprintf(ebuf, sizeof(ebuf),
+#if __FreeBSD_version < 500000
+ "wrong version %ld,%d",
+#else
+ "wrong version %d,%d",
+#endif
+ ntohl(dbuf.version), DCONS_VERSION);
+ /* XXX exit? */
+ dconschat_ready(dc, 0, ebuf);
+ return (-1);
+ }
+
+ for (j = 0; j < DCONS_NPORT; j++) {
+ struct dcons_ch *o, *i;
+ off_t newbuf;
+ int new = 0;
+
+ o = &dc->port[j].o;
+ newbuf = dc->paddr + ntohl(dbuf.ooffset[j]);
+ o->size = ntohl(dbuf.osize[j]);
+
+ if (newbuf != o->buf) {
+ /* buffer address has changes */
+ new = 1;
+ o->gen = ntohl(dbuf.optr[j]) >> DCONS_GEN_SHIFT;
+ o->pos = ntohl(dbuf.optr[j]) & DCONS_POS_MASK;
+ o->buf = newbuf;
+ }
+
+ i = &dc->port[j].i;
+ i->size = ntohl(dbuf.isize[j]);
+ i->gen = ntohl(dbuf.iptr[j]) >> DCONS_GEN_SHIFT;
+ i->pos = ntohl(dbuf.iptr[j]) & DCONS_POS_MASK;
+ i->buf = dc->paddr + ntohl(dbuf.ioffset[j]);
+
+ if (verbose) {
+ printf("port %d size offset gen pos\n", j);
+#if __FreeBSD_version < 500000
+ printf("output: %5d %6ld %5d %5d\n"
+ "input : %5d %6ld %5d %5d\n",
+#else
+ printf("output: %5d %6d %5d %5d\n"
+ "input : %5d %6d %5d %5d\n",
+#endif
+ o->size, ntohl(dbuf.ooffset[j]), o->gen, o->pos,
+ i->size, ntohl(dbuf.ioffset[j]), i->gen, i->pos);
+ }
+
+ if (IS_CONSOLE(&dc->port[j]) && new &&
+ (dc->flags & F_REPLAY) !=0) {
+ if (o->gen > 0)
+ o->gen --;
+ else
+ o->pos = 0;
+ }
+ }
+ dconschat_ready(dc, 1, NULL);
+ return(0);
+}
+
+static int
+dconschat_get_ptr (struct dcons_state *dc) {
+ int dlen, i;
+ u_int32_t ptr[DCONS_NPORT*2+1];
+ static int retry = RETRY;
+
+again:
+ dlen = dread(dc, &ptr, sizeof(ptr),
+ dc->paddr + __offsetof(struct dcons_buf, magic));
+
+ if (dlen < 0) {
+ if (errno == ETIMEDOUT)
+ if (retry -- > 0)
+ goto again;
+ dconschat_ready(dc, 0, "get ptr failed");
+ return(-1);
+ }
+ if (ptr[0] != htonl(DCONS_MAGIC)) {
+ dconschat_ready(dc, 0, "wrong magic");
+ return(-1);
+ }
+ retry = RETRY;
+ for (i = 0; i < DCONS_NPORT; i ++) {
+ dc->port[i].optr = ntohl(ptr[i + 1]);
+ dc->port[i].iptr = ntohl(ptr[DCONS_NPORT + i + 1]);
+ }
+ return(0);
+}
+
+#define MAX_XFER 2048
+static int
+dconschat_read_dcons(struct dcons_state *dc, int port, char *buf, int len)
+{
+ struct dcons_ch *ch;
+ u_int32_t ptr, pos, gen, next_gen;
+ int rlen, dlen, lost;
+ int retry = RETRY;
+
+ ch = &dc->port[port].o;
+ ptr = dc->port[port].optr;
+ gen = ptr >> DCONS_GEN_SHIFT;
+ pos = ptr & DCONS_POS_MASK;
+ if (gen == ch->gen && pos == ch->pos)
+ return (-1);
+
+ next_gen = DCONS_NEXT_GEN(ch->gen);
+ /* XXX sanity check */
+ if (gen == ch->gen) {
+ if (pos > ch->pos)
+ goto ok;
+ lost = ch->size * DCONS_GEN_MASK - ch->pos;
+ ch->pos = 0;
+ } else if (gen == next_gen) {
+ if (pos <= ch->pos)
+ goto ok;
+ lost = pos - ch->pos;
+ ch->pos = pos;
+ } else {
+ lost = gen - ch->gen;
+ if (lost < 0)
+ lost += DCONS_GEN_MASK;
+ if (verbose)
+ printf("[genskip %d]", lost);
+ lost = lost * ch->size - ch->pos;
+ ch->pos = 0;
+ ch->gen = gen;
+ }
+ /* generation skipped !! */
+ /* XXX discard */
+ if (verbose)
+ printf("[lost %d]", lost);
+ok:
+ if (gen == ch->gen)
+ rlen = pos - ch->pos;
+ else
+ rlen = ch->size - ch->pos;
+
+ if (rlen > MAX_XFER)
+ rlen = MAX_XFER;
+ if (rlen > len)
+ rlen = len;
+
+#if 1
+ if (verbose)
+ printf("[%d]", rlen); fflush(stdout);
+#endif
+
+again:
+ dlen = dread(dc, buf, rlen, ch->buf + ch->pos);
+ if (dlen < 0) {
+ if (errno == ETIMEDOUT)
+ if (retry -- > 0)
+ goto again;
+ dconschat_ready(dc, 0, "read buffer failed");
+ return(-1);
+ }
+ if (dlen != rlen)
+ warnx("dlen(%d) != rlen(%d)\n", dlen, rlen);
+ ch->pos += dlen;
+ if (ch->pos >= ch->size) {
+ ch->gen = next_gen;
+ ch->pos = 0;
+ if (verbose)
+ printf("read_dcons: gen=%d", ch->gen);
+ }
+ return (dlen);
+}
+
+static int
+dconschat_write_dcons(struct dcons_state *dc, int port, char *buf, int blen)
+{
+ struct dcons_ch *ch;
+ u_int32_t ptr;
+ int len, wlen;
+ int retry = RETRY;
+
+ ch = &dc->port[port].i;
+ ptr = dc->port[port].iptr;
+
+ /* the others may advance the pointer sync with it */
+ ch->gen = ptr >> DCONS_GEN_SHIFT;
+ ch->pos = ptr & DCONS_POS_MASK;
+
+ while(blen > 0) {
+ wlen = MIN(blen, ch->size - ch->pos);
+ wlen = MIN(wlen, MAX_XFER);
+ len = dwrite(dc, buf, wlen, ch->buf + ch->pos);
+ if (len < 0) {
+ if (errno == ETIMEDOUT)
+ if (retry -- > 0)
+ continue; /* try again */
+ dconschat_ready(dc, 0, "write buffer failed");
+ return(-1);
+ }
+ ch->pos += len;
+ buf += len;
+ blen -= len;
+ if (ch->pos >= ch->size) {
+ ch->gen = DCONS_NEXT_GEN(ch->gen);
+ ch->pos = 0;
+ if (verbose)
+ printf("write_dcons: gen=%d", ch->gen);
+
+ }
+ }
+
+ ptr = DCONS_MAKE_PTR(ch);
+ dc->port[port].iptr = ptr;
+
+ if (verbose > 2)
+ printf("(iptr: 0x%x)", ptr);
+again:
+ len = dwrite(dc, &ptr, sizeof(u_int32_t),
+ dc->paddr + __offsetof(struct dcons_buf, iptr[port]));
+ if (len < 0) {
+ if (errno == ETIMEDOUT)
+ if (retry -- > 0)
+ goto again;
+ dconschat_ready(dc, 0, "write ptr failed");
+ return(-1);
+ }
+ return(0);
+}
+
+static int
+dconschat_write_socket(int fd, char *buf, int len)
+{
+ write(fd, buf, len);
+ if (verbose > 1) {
+ buf[len] = 0;
+ printf("[%s]", buf);
+ }
+ return (0);
+}
+
+static void
+dconschat_init_socket(struct dcons_state *dc, int port, char *host, int sport)
+{
+ struct addrinfo hints, *res;
+ int on = 1, error;
+ char service[10];
+ struct kevent kev;
+ struct dcons_port *p;
+
+ p = &dc->port[port];
+ p->port = port;
+ p->infd = p->outfd = -1;
+
+ if (sport < 0)
+ return;
+
+ if (sport == 0) {
+
+ /* Use stdin and stdout */
+ p->infd = STDIN_FILENO;
+ p->outfd = STDOUT_FILENO;
+ p->s = -1;
+ if (tc_set == 0 &&
+ tcgetattr(STDIN_FILENO, &dc->tsave) == 0) {
+ struct termios traw;
+
+ traw = dc->tsave;
+ cfmakeraw(&traw);
+ tcsetattr(STDIN_FILENO, TCSADRAIN, &traw);
+ tc_set = 1;
+ }
+ EV_SET(&kev, p->infd, EVFILT_READ, EV_ADD, NOTE_LOWAT, 1,
+ (void *)p);
+ kevent(dc->kq, &kev, 1, NULL, 0, &dc->zero);
+ return;
+ }
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = AI_PASSIVE;
+#if 1 /* gdb can talk v4 only */
+ hints.ai_family = PF_INET;
+#else
+ hints.ai_family = PF_UNSPEC;
+#endif
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_protocol = 0;
+
+ if (verbose)
+ printf("%s:%d for port %d\n",
+ host == NULL ? "*" : host, sport, port);
+ snprintf(service, sizeof(service), "%d", sport);
+ error = getaddrinfo(host, service, &hints, &res);
+ if (error)
+ errx(1, "tcp/%s: %s\n", service, gai_strerror(error));
+ p->res = res;
+ p->s = socket(res->ai_family, res->ai_socktype, res->ai_protocol);
+ if (p->s < 0)
+ err(1, "socket");
+ setsockopt(p->s, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
+
+ if (bind(p->s, p->res->ai_addr, p->res->ai_addrlen) < 0) {
+ err(1, "bind");
+ }
+ if (listen(p->s, 1) < 0)
+ err(1, "listen");
+ EV_SET(&kev, p->s, EVFILT_READ, EV_ADD | EV_ONESHOT, 0, 0, (void *)p);
+ error = kevent(dc->kq, &kev, 1, NULL, 0, &dc->to);
+ if (error < 0)
+ err(1, "kevent");
+ return;
+}
+
+static int
+dconschat_accept_socket(struct dcons_state *dc, struct dcons_port *p)
+{
+ int foo, ns, flags;
+ struct kevent kev;
+
+ /* accept connection */
+ foo = p->res->ai_addrlen;
+ ns = accept(p->s, p->res->ai_addr, &foo);
+ if (ns < 0)
+ err(1, "accept");
+ if (verbose)
+ printf("port%d accepted\n", p->port);
+
+ flags = fcntl(ns, F_GETFL, 0);
+ flags |= O_NDELAY;
+ fcntl(ns, F_SETFL, flags);
+#if 1
+ if (IS_CONSOLE(p) && (dc->flags & F_TELNET) != 0) {
+ char sga[] = {IAC, WILL, TELOPT_SGA};
+ char linemode[] = {IAC, DONT, TELOPT_LINEMODE};
+ char echo[] = {IAC, WILL, TELOPT_ECHO};
+ char bin[] = {IAC, DO, TELOPT_BINARY};
+
+ write(ns, sga, sizeof(sga));
+ write(ns, linemode, sizeof(linemode));
+ write(ns, echo, sizeof(echo));
+ write(ns, bin, sizeof(bin));
+ p->skip_read = 0;
+ }
+#endif
+
+ p->infd = p->outfd = ns;
+ EV_SET(&kev, ns, EVFILT_READ, EV_ADD, NOTE_LOWAT, 1, (void *)p);
+ kevent(dc->kq, &kev, 1, NULL, 0, &dc->zero);
+ return(0);
+}
+
+static int
+dconschat_read_filter(struct dcons_state *dc, struct dcons_port *p,
+ u_char *sp, int slen, u_char *dp, int *dlen)
+{
+ static u_char abreak[3] = {13 /* CR */, 126 /* ~ */, 2 /* ^B */};
+
+ while (slen > 0) {
+ if (IS_CONSOLE(p)) {
+ if ((dc->flags & F_TELNET) != 0) {
+ /* XXX Telent workarounds */
+ if (p->skip_read -- > 0) {
+ sp ++;
+ slen --;
+ continue;
+ }
+ if (*sp == IAC) {
+ if (verbose)
+ printf("(IAC)");
+ p->skip_read = 2;
+ sp ++;
+ slen --;
+ continue;
+ }
+ if (*sp == 0) {
+ if (verbose)
+ printf("(0 stripped)");
+ sp ++;
+ slen --;
+ continue;
+ }
+ }
+ switch (dc->escape_state) {
+ case STATE1:
+ if (*sp == KEY_TILDE)
+ dc->escape_state = STATE2;
+ else
+ dc->escape_state = STATE0;
+ break;
+ case STATE2:
+ dc->escape_state = STATE0;
+ if (*sp == '.')
+ dconschat_cleanup(0);
+ }
+ if (*sp == KEY_CR)
+ dc->escape_state = STATE1;
+ } else if (IS_GDB(p)) {
+ /* GDB: ^C -> CR+~+^B */
+ if (*sp == 0x3 && (dc->flags & F_ALT_BREAK) != 0) {
+ bcopy(abreak, dp, 3);
+ dp += 3;
+ sp ++;
+ *dlen += 3;
+ /* discard rest of the packet */
+ slen = 0;
+ break;
+ }
+ }
+ *dp++ = *sp++;
+ (*dlen) ++;
+ slen --;
+ }
+ return (*dlen);
+
+}
+
+static int
+dconschat_read_socket(struct dcons_state *dc, struct dcons_port *p)
+{
+ struct kevent kev;
+ int len, wlen;
+ char rbuf[MAX_XFER], wbuf[MAX_XFER+2];
+
+ if ((len = read(p->infd, rbuf, sizeof(rbuf))) > 0) {
+ wlen = 0;
+ dconschat_read_filter(dc, p, rbuf, len, wbuf, &wlen);
+ /* XXX discard if not ready*/
+ if (wlen > 0 && (dc->flags & F_READY) != 0) {
+ dconschat_write_dcons(dc, p->port, wbuf, wlen);
+ if (verbose > 1) {
+ wbuf[wlen] = 0;
+ printf("(%s)\n", wbuf);
+ }
+ if (verbose) {
+ printf("(%d)", wlen);
+ fflush(stdout);
+ }
+ }
+ } else {
+ if (verbose) {
+ if (len == 0)
+ warnx("port%d: closed", p->port);
+ else
+ warn("port%d: read", p->port);
+ }
+ EV_SET(&kev, p->infd, EVFILT_READ,
+ EV_DELETE, 0, 0, NULL);
+ kevent(dc->kq, &kev, 1, NULL, 0, &dc->zero);
+ close(p->infd);
+ close(p->outfd);
+ /* XXX exit for pipe case XXX */
+ EV_SET(&kev, p->s, EVFILT_READ,
+ EV_ADD | EV_ONESHOT, 0, 0, (void *) p);
+ kevent(dc->kq, &kev, 1, NULL, 0, &dc->zero);
+ p->infd = p->outfd = -1;
+ }
+ return(0);
+}
+#define NEVENT 5
+static int
+dconschat_proc_socket(struct dcons_state *dc)
+{
+ struct kevent elist[NEVENT], *e;
+ int i, n;
+ struct dcons_port *p;
+
+ n = kevent(dc->kq, NULL, 0, elist, NEVENT, &dc->to);
+ for (i = 0; i < n; i ++) {
+ e = &elist[i];
+ p = (struct dcons_port *)e->udata;
+ if (e->ident == p->s) {
+ dconschat_accept_socket(dc, p);
+ } else {
+ dconschat_read_socket(dc, p);
+ }
+ }
+ return(0);
+}
+
+static int
+dconschat_proc_dcons(struct dcons_state *dc)
+{
+ int port, len, err;
+ char buf[MAX_XFER];
+ struct dcons_port *p;
+
+ err = dconschat_get_ptr(dc);
+ if (err) {
+ /* XXX we should stop write operation too. */
+ return err;
+ }
+ for (port = 0; port < DCONS_NPORT; port ++) {
+ p = &dc->port[port];
+ if (p->infd < 0)
+ continue;
+ while ((len = dconschat_read_dcons(dc, port, buf,
+ sizeof(buf))) > 0) {
+ dconschat_write_socket(p->outfd, buf, len);
+ dconschat_get_ptr(dc);
+ }
+ if ((dc->flags & F_ONE_SHOT) != 0 && len <= 0)
+ dconschat_cleanup(0);
+ }
+ return 0;
+}
+
+static int
+dconschat_start_session(struct dcons_state *dc)
+{
+ int counter = 0;
+
+ while (1) {
+ if ((dc->flags & F_READY) == 0 && (++counter % 200) == 0)
+ dconschat_fetch_header(dc);
+ if ((dc->flags & F_READY) != 0)
+ dconschat_proc_dcons(dc);
+ dconschat_proc_socket(dc);
+ }
+ return (0);
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr,
+ "usage: dconschat [-brvwRT1] [-h hz] [-C port] [-G port]\n"
+ "\t\t\t[-M core] [-N system]\n"
+ "\t\t\t[-u unit] [-a address] [-t target_eui64]\n"
+ "\t-b translate ctrl-C to CR+~+ctrl-B on gdb port\n"
+ "\t-v verbose\n"
+ "\t-w listen on wildcard address rather than localhost\n"
+ "\t-r replay old buffer on connection\n"
+ "\t-R read-only\n"
+ "\t-T enable Telnet protocol workaround on console port\n"
+ "\t-1 one shot: read buffer and exit\n"
+ "\t-h polling rate\n"
+ "\t-C port number for console port\n"
+ "\t-G port number for gdb port\n"
+ "\t(for KVM)\n"
+ "\t-M core file\n"
+ "\t-N system file\n"
+ "\t(for FireWire)\n"
+ "\t-u specify unit number of the bus\n"
+ "\t-t EUI64 of target host (must be specified)\n"
+ "\t-a physical address of dcons buffer on target host\n"
+ );
+ exit(0);
+}
+int
+main(int argc, char **argv)
+{
+ struct dcons_state *dc;
+ struct fw_eui64 eui;
+ char devname[256], *core = NULL, *system = NULL;
+ int i, ch, error;
+ int unit=0, wildcard=0, poll_hz = DCONS_POLL_HZ;
+ int port[DCONS_NPORT];
+ u_int64_t target = 0;
+
+ bzero(&sc, sizeof(sc));
+ dc = &sc;
+ dc->flags |= USE_CROM ? F_USE_CROM : 0;
+
+ /* defualt ports */
+ port[0] = 0; /* stdin/out for console */
+ port[1] = -1; /* disable gdb port */
+
+ while ((ch = getopt(argc, argv, "a:bh:rt:u:vwC:G:M:N:RT1")) != -1) {
+ switch(ch) {
+ case 'a':
+ dc->paddr = strtoull(optarg, NULL, 0);
+ dc->flags &= ~F_USE_CROM;
+ break;
+ case 'b':
+ dc->flags |= F_ALT_BREAK;
+ break;
+ case 'h':
+ poll_hz = strtoul(optarg, NULL, 0);
+ if (poll_hz == 0)
+ poll_hz = DCONS_POLL_HZ;
+ printf("poll_hz = %d\n", poll_hz);
+ break;
+ case 'r':
+ dc->flags |= F_REPLAY;
+ break;
+ case 't':
+ target = strtoull(optarg, NULL, 0);
+ eui.hi = target >> 32;
+ eui.lo = target & (((u_int64_t)1 << 32) - 1);
+ dc->type = TYPE_FW;
+ break;
+ case 'u':
+ unit = strtol(optarg, NULL, 0);
+ break;
+ case 'v':
+ verbose ++;
+ break;
+ case 'w':
+ wildcard = 1;
+ break;
+ case 'C':
+ port[0] = strtol(optarg, NULL, 0);
+ break;
+ case 'G':
+ port[1] = strtol(optarg, NULL, 0);
+ break;
+ case 'M':
+ core = optarg;
+ break;
+ case 'N':
+ system = optarg;
+ break;
+ case 'R':
+ dc->flags |= F_RD_ONLY;
+ break;
+ case 'T':
+ dc->flags |= F_TELNET;
+ break;
+ case '1':
+ dc->flags |= F_ONE_SHOT | F_REPLAY;
+ break;
+ default:
+ usage();
+ }
+ }
+ if (dc->paddr == 0 && (dc->flags & F_USE_CROM) == 0) {
+ warnx("no address specified");
+ usage();
+ }
+
+ if (port[0] < 0 && port[1] < 0) {
+ warnx("no port specified");
+ usage();
+ }
+
+ /* set signal handler */
+ signal(SIGHUP, dconschat_cleanup);
+ signal(SIGINT, dconschat_cleanup);
+ signal(SIGPIPE, dconschat_cleanup);
+ signal(SIGTERM, dconschat_cleanup);
+
+ /* init firewire */
+ switch (dc->type) {
+ case TYPE_FW:
+#define MAXDEV 4
+ for (i = 0; i < MAXDEV; i ++) {
+ snprintf(devname, sizeof(devname),
+ "/dev/fwmem%d.%d", unit, i);
+ dc->fd = open(devname, O_RDWR);
+ if (dc->fd >= 0)
+ goto found;
+ }
+ err(1, "open");
+found:
+ error = ioctl(dc->fd, FW_SDEUI64, &eui);
+ if (error)
+ err(1, "ioctl");
+ break;
+ case TYPE_KVM:
+ {
+ struct nlist nl[] = {{"dcons_buf"}, {""}};
+ void *dcons_buf;
+
+ dc->kd = kvm_open(system, core, NULL,
+ (dc->flags & F_RD_ONLY) ? O_RDONLY : O_RDWR, "dconschat");
+ if (dc->kd == NULL)
+ errx(1, "kvm_open");
+
+ if (kvm_nlist(dc->kd, nl) < 0)
+ errx(1, "kvm_nlist: %s", kvm_geterr(dc->kd));
+
+ if (kvm_read(dc->kd, nl[0].n_value, &dcons_buf,
+ sizeof(void *)) < 0)
+ errx(1, "kvm_read: %s", kvm_geterr(dc->kd));
+ dc->paddr = (uintptr_t)dcons_buf;
+ if (verbose)
+ printf("dcons_buf: 0x%x\n", (uint)dc->paddr);
+ break;
+ }
+ }
+ dconschat_fetch_header(dc);
+
+ /* iniit sockets */
+ dc->kq = kqueue();
+ if (poll_hz == 1) {
+ dc->to.tv_sec = 1;
+ dc->to.tv_nsec = 0;
+ } else {
+ dc->to.tv_sec = 0;
+ dc->to.tv_nsec = 1000 * 1000 * 1000 / poll_hz;
+ }
+ dc->zero.tv_sec = 0;
+ dc->zero.tv_nsec = 0;
+ for (i = 0; i < DCONS_NPORT; i++)
+ dconschat_init_socket(dc, i,
+ wildcard ? NULL : "localhost", port[i]);
+
+ dconschat_start_session(dc);
+
+ for (i = 0; i < DCONS_NPORT; i++) {
+ freeaddrinfo(dc->port[i].res);
+ }
+ return (0);
+}
OpenPOWER on IntegriCloud