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.
This commit is contained in:
parent
7248844c9b
commit
c96f6e4f1d
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -41,6 +41,8 @@ MAN= aac.4 \
|
||||
cue.4 \
|
||||
da.4 \
|
||||
dc.4 \
|
||||
dcons.4 \
|
||||
dcons_crom.4 \
|
||||
ddb.4 \
|
||||
de.4 \
|
||||
devctl.4 \
|
||||
|
112
share/man/man4/dcons.4
Normal file
112
share/man/man4/dcons.4
Normal file
@ -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
|
59
share/man/man4/dcons_crom.4
Normal file
59
share/man/man4/dcons_crom.4
Normal file
@ -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
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
648
sys/dev/dcons/dcons.c
Normal file
648
sys/dev/dcons/dcons.c
Normal file
@ -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);
|
97
sys/dev/dcons/dcons.h
Normal file
97
sys/dev/dcons/dcons.h
Normal file
@ -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
|
224
sys/dev/dcons/dcons_crom.c
Normal file
224
sys/dev/dcons/dcons_crom.c
Normal file
@ -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);
|
@ -32,6 +32,8 @@ SUBDIR= accf_data \
|
||||
${_cryptodev} \
|
||||
cue \
|
||||
dc \
|
||||
dcons \
|
||||
dcons_crom \
|
||||
de \
|
||||
digi \
|
||||
dummynet \
|
||||
|
18
sys/modules/dcons/Makefile
Normal file
18
sys/modules/dcons/Makefile
Normal file
@ -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>
|
14
sys/modules/dcons_crom/Makefile
Normal file
14
sys/modules/dcons_crom/Makefile
Normal file
@ -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>
|
@ -25,6 +25,7 @@ SUBDIR= IPXrouted \
|
||||
crunch \
|
||||
ctm \
|
||||
daemon \
|
||||
dconschat \
|
||||
devinfo \
|
||||
digictl \
|
||||
diskinfo \
|
||||
|
12
usr.sbin/dconschat/Makefile
Normal file
12
usr.sbin/dconschat/Makefile
Normal file
@ -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>
|
229
usr.sbin/dconschat/dconschat.8
Normal file
229
usr.sbin/dconschat/dconschat.8
Normal file
@ -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
|
959
usr.sbin/dconschat/dconschat.c
Normal file
959
usr.sbin/dconschat/dconschat.c
Normal file
@ -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 = ≻
|
||||
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 = ≻
|
||||
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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user