NetBSD/sys/arch/alpha/wscons/wscons.c

499 lines
10 KiB
C

/* $NetBSD: wscons.c,v 1.18 1998/11/19 15:38:21 mrg Exp $ */
/*
* Copyright (c) 1995, 1996 Carnegie-Mellon University.
* All rights reserved.
*
* Author: Chris G. Demetriou
*
* Permission to use, copy, modify and distribute this software and
* its documentation is hereby granted, provided that both the copyright
* notice and this permission notice appear in all copies of the
* software, derivative works or modified versions, and any portions
* thereof, and that both notices appear in supporting documentation.
*
* CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
* CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND
* FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
*
* Carnegie Mellon requests users of this software to return to
*
* Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
* School of Computer Science
* Carnegie Mellon University
* Pittsburgh PA 15213-3890
*
* any improvements or extensions that they make and grant Carnegie the
* rights to redistribute these changes.
*/
#include <sys/cdefs.h> /* RCS ID & Copyright macro defns */
__KERNEL_RCSID(0, "$NetBSD: wscons.c,v 1.18 1998/11/19 15:38:21 mrg Exp $");
#include <sys/param.h>
#include <sys/device.h>
#include <sys/kernel.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/ioctl.h>
#include <sys/proc.h>
#include <sys/tty.h>
#include <sys/termios.h>
#include <machine/conf.h>
#include <dev/cons.h>
#include <alpha/wscons/wsconsvar.h>
#include <alpha/wscons/wscons_emul.h>
#include <machine/wsconsio.h>
cdev_decl(wscons);
/* Macros to clear/set/test flags. */
#define SET(t, f) (t) |= (f)
#define CLR(t, f) (t) &= ~(f)
#define ISSET(t, f) ((t) & (f))
/*
* Autoconfiguration glue.
*/
struct wscons_softc {
struct device sc_dev;
struct wscons_emul_data *sc_emul_data;
struct tty *sc_tty;
void *sc_fn_cookie;
wscons_ioctl_t sc_ioctl;
wscons_mmap_t sc_mmap;
};
int wsconsmatch __P((struct device *, struct cfdata *, void *));
void wsconsattach __P((struct device *, struct device *, void *));
struct cfattach wscons_ca = {
sizeof (struct wscons_softc), wsconsmatch, wsconsattach,
};
extern struct cfdriver wscons_cd;
/*
* Console handing functions and variables.
*/
int wscons_console_attached; /* polled console fns attached */
int wscons_console_unit = -1;
struct wscons_emul_data wscons_console_emul_data;
int wscons_cngetc __P((dev_t));
void wscons_cnputc __P((dev_t, int));
void wscons_cnpollc __P((dev_t, int));
struct consdev wscons_consdev =
{ NULL, NULL, wscons_cngetc, wscons_cnputc, wscons_cnpollc, NODEV, 1 };
/*
* Input focus handling: where characters from the keyboard are sent.
*/
int wscons_input_focus = -1;
/*
* TTY interface helpers.
*/
#define WSCUNIT(dev) minor(dev)
void wsconsstart __P((struct tty *));
int wsconsparam __P((struct tty *, struct termios *));
/*
* Output device selection and attachment.
*/
int
wsconsmatch(parent, cf, aux)
struct device *parent;
struct cfdata *cf;
void *aux;
{
struct wscons_attach_args *waa = aux;
if (waa->waa_isconsole && wscons_console_unit != -1)
panic("wsconsmatch: multiple consoles?");
/* If console-ness specified... */
if (cf->wsconscf_console != -1) {
/*
* If exact match, return match with high priority,
* else return don't match.
*/
if ((cf->wsconscf_console && waa->waa_isconsole) ||
(!cf->wsconscf_console && !waa->waa_isconsole))
return (10);
else
return (0);
}
/* Otherwise match with low priority. */
return (1);
}
void
wsconsattach(parent, self, aux)
struct device *parent, *self;
void *aux;
{
struct wscons_attach_args *waa = aux;
struct wscons_softc *sc = (struct wscons_softc *)self;
int console;
console = waa->waa_isconsole;
if (console)
printf(": console");
/*
* If output has already been set up, record it now. Otherwise,
* do the setup.
*/
if (console) {
sc->sc_emul_data = &wscons_console_emul_data;
wscons_console_unit = sc->sc_dev.dv_unit;
wscons_consdev.cn_dev =
makedev(25, wscons_console_unit); /* XXX */
} else {
sc->sc_emul_data = malloc(sizeof(*sc->sc_emul_data), M_DEVBUF,
M_WAITOK);
wscons_emul_attach(sc->sc_emul_data, &waa->waa_odev_spec);
}
/*
* Set wscons input focus if this is the console device,
* or if we've not yet set the input focus.
*/
if (console || wscons_input_focus == -1)
wscons_input_focus = sc->sc_dev.dv_unit;
/*
* Set up the device's tty structure.
*/
sc->sc_tty = ttymalloc();
tty_attach(sc->sc_tty);
/*
* Record other relevant information: ioctl and mmap functions.
*/
sc->sc_fn_cookie = waa->waa_odev_spec.wo_miscfuncs_cookie;
sc->sc_ioctl = waa->waa_odev_spec.wo_ioctl;
sc->sc_mmap = waa->waa_odev_spec.wo_mmap;
printf("\n");
}
/*
* Keyboard input handling.
*/
void
wscons_input(cp)
char *cp;
{
struct wscons_softc *sc;
struct tty *tp;
if (wscons_input_focus == -1)
return;
#ifdef DIAGNOSTIC
if (wscons_input_focus >= wscons_cd.cd_ndevs)
panic("wscons_input: bogus input focus");
sc = wscons_cd.cd_devs[wscons_input_focus];
if (sc == NULL)
panic("wscons_input: null input device");
tp = sc->sc_tty;
if (tp == NULL)
panic("wscons_input: no tty");
#else
sc = wscons_cd.cd_devs[wscons_input_focus];
tp = sc->sc_tty;
#endif
while (*cp)
(*linesw[tp->t_line].l_rint)(*cp++, tp);
}
/*
* Console (output) tty-handling functions.
*/
int
wsconsopen(dev, flag, mode, p)
dev_t dev;
int flag, mode;
struct proc *p;
{
struct wscons_softc *sc;
int unit = WSCUNIT(dev), newopen, rv;
struct tty *tp;
if (unit >= wscons_cd.cd_ndevs)
return ENXIO;
sc = wscons_cd.cd_devs[unit];
if (sc == NULL)
return ENXIO;
#ifdef DIAGNOSTIC
if (!sc->sc_tty)
panic("wscopen: no tty!");
#endif
tp = sc->sc_tty;
tp->t_oproc = wsconsstart;
tp->t_param = wsconsparam;
tp->t_dev = dev;
newopen = (tp->t_state & TS_ISOPEN) == 0;
if (newopen) {
ttychars(tp);
tp->t_iflag = TTYDEF_IFLAG;
tp->t_oflag = TTYDEF_OFLAG;
tp->t_cflag = TTYDEF_CFLAG;
tp->t_lflag = TTYDEF_LFLAG;
tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
wsconsparam(tp, &tp->t_termios);
ttsetwater(tp);
} else if ((tp->t_state & TS_XCLUDE) != 0 && p->p_ucred->cr_uid != 0)
return EBUSY;
tp->t_state |= TS_CARR_ON;
rv = ((*linesw[tp->t_line].l_open)(dev, tp));
if (newopen && (rv == 0)) {
/* set window sizes to be correct */
tp->t_winsize.ws_row = sc->sc_emul_data->ac_nrow;
tp->t_winsize.ws_col = sc->sc_emul_data->ac_ncol;
}
return (rv);
}
int
wsconsclose(dev, flag, mode, p)
dev_t dev;
int flag, mode;
struct proc *p;
{
struct wscons_softc *sc = wscons_cd.cd_devs[WSCUNIT(dev)];
struct tty *tp = sc->sc_tty;
(*linesw[tp->t_line].l_close)(tp, flag);
ttyclose(tp);
return(0);
}
int
wsconsread(dev, uio, flag)
dev_t dev;
struct uio *uio;
int flag;
{
struct wscons_softc *sc = wscons_cd.cd_devs[WSCUNIT(dev)];
struct tty *tp = sc->sc_tty;
return ((*linesw[tp->t_line].l_read)(tp, uio, flag));
}
int
wsconswrite(dev, uio, flag)
dev_t dev;
struct uio *uio;
int flag;
{
struct wscons_softc *sc = wscons_cd.cd_devs[WSCUNIT(dev)];
struct tty *tp = sc->sc_tty;
return ((*linesw[tp->t_line].l_write)(tp, uio, flag));
}
struct tty *
wsconstty(dev)
dev_t dev;
{
struct wscons_softc *sc = wscons_cd.cd_devs[WSCUNIT(dev)];
struct tty *tp = sc->sc_tty;
return (tp);
}
int
wsconsioctl(dev, cmd, data, flag, p)
dev_t dev;
u_long cmd;
caddr_t data;
int flag;
struct proc *p;
{
struct wscons_softc *sc = wscons_cd.cd_devs[WSCUNIT(dev)];
struct tty *tp = sc->sc_tty;
int error;
/* do the line discipline ioctls first */
error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag, p);
if (error >= 0)
return error;
/* then the tty ioctls */
error = ttioctl(tp, cmd, data, flag, p);
if (error >= 0)
return error;
/* then the underlying frame buffer device ioctls */
if (sc->sc_ioctl != NULL)
error = (*sc->sc_ioctl)(sc->sc_fn_cookie, cmd, data,
flag, p);
if (error >= 0)
return error;
/*
* then the keyboard ioctls, if we have input focus.
* This is done last because it's a special case: it will
* return ENOTTY (not -1) if it can't figure out what
* to do with the request.
*/
if (WSCUNIT(dev) == wscons_input_focus)
error = kbdioctl(0, cmd, data, flag, p); /* XXX dev */
else
error = ENOTTY;
return (error);
}
int
wsconsmmap(dev, offset, prot)
dev_t dev;
int offset; /* XXX */
int prot;
{
struct wscons_softc *sc = wscons_cd.cd_devs[WSCUNIT(dev)];
if (sc->sc_ioctl != NULL && offset >= 0)
return (*sc->sc_mmap)(sc->sc_dev.dv_parent, offset, prot);
else
return -1;
}
void
wsconsstart(tp)
register struct tty *tp;
{
struct wscons_softc *sc;
register int s, n, i;
char buf[OBUFSIZ];
s = spltty();
if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP)) {
splx(s);
return;
}
tp->t_state |= TS_BUSY;
splx(s);
n = q_to_b(&tp->t_outq, buf, sizeof(buf));
for (i = 0; i < n; ++i)
buf[i] &= 0177; /* strip parity (argh) */
sc = wscons_cd.cd_devs[WSCUNIT(tp->t_dev)];
wscons_emul_input(sc->sc_emul_data, buf, n);
s = spltty();
tp->t_state &= ~TS_BUSY;
/* Come back if there's more to do */
if (tp->t_outq.c_cc) {
tp->t_state |= TS_TIMEOUT;
timeout(ttrstrt, tp, 1);
}
if (tp->t_outq.c_cc <= tp->t_lowat) {
if (tp->t_state&TS_ASLEEP) {
tp->t_state &= ~TS_ASLEEP;
wakeup((caddr_t)&tp->t_outq);
}
selwakeup(&tp->t_wsel);
}
splx(s);
}
void
wsconsstop(tp, flag)
struct tty *tp;
int flag;
{
int s;
/* XXX ??? */
s = spltty();
if (ISSET(tp->t_state, TS_BUSY))
if (!ISSET(tp->t_state, TS_TTSTOP))
SET(tp->t_state, TS_FLUSH);
splx(s);
}
/*
* Set line parameters.
*/
int
wsconsparam(tp, t)
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;
}
/*
* Polled-console handing setup and manipulation.
*/
void
wscons_attach_console(wo)
const struct wscons_odev_spec *wo;
{
if (wscons_console_attached)
panic("wscons_attach_console: multiple times");
wscons_emul_attach(&wscons_console_emul_data, wo);
cn_tab = &wscons_consdev;
wscons_console_attached = 1;
}
void
wscons_cnputc(dev, ic)
dev_t dev;
int ic;
{
char c = ic;
if (wscons_console_attached)
wscons_emul_input(&wscons_console_emul_data, &c, 1);
}
int
wscons_cngetc(dev)
dev_t dev;
{
return kbd_cngetc(dev); /* XXX XXX */
}
void
wscons_cnpollc(dev, i)
dev_t dev;
int i;
{
kbd_cnpollc(dev, i); /* XXX XXX */
}