NetBSD/sys/arch/mac68k/dev/ite.c

1011 lines
19 KiB
C

/* $NetBSD: ite.c,v 1.14 1995/04/21 02:47:57 briggs Exp $ */
/*
* Copyright (c) 1988 University of Utah.
* Copyright (c) 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* the Systems Programming Group of the University of Utah Computer
* Science Department.
*
* 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 the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University 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.
*
* from: Utah $Hdr: ite.c 1.28 92/12/20$
*
* @(#)ite.c 8.2 (Berkeley) 1/12/94
*/
/*
* ite.c
*
* The ite module handles the system console; that is, stuff printed
* by the kernel and by user programs while "desktop" and X aren't
* running. Some (very small) parts are based on hp300's 4.4 ite.c,
* hence the above copyright.
*
* -- Brad and Lawrence, June 26th, 1994
*
*/
#include <sys/param.h>
#include <sys/conf.h>
#include <sys/proc.h>
#include <sys/ioctl.h>
#include <sys/tty.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/device.h>
#include <dev/cons.h>
#include "../mac68k/via.h"
#include <machine/cpu.h>
#include <machine/frame.h>
#define KEYBOARD_ARRAY
#include <machine/keyboard.h>
#include <machine/adbsys.h>
#include <machine/iteioctl.h>
#include "6x10.h"
#define CHARWIDTH 6
#define CHARHEIGHT 10
#define dprintf if (0) printf
#define ATTR_NONE 0
#define ATTR_BOLD 1
#define ATTR_UNDER 2
#define ATTR_REVERSE 4
enum vt100state_e {
ESnormal, /* Nothing yet */
ESesc, /* Got ESC */
ESsquare, /* Got ESC [ */
ESgetpars, /* About to get or getting the parameters */
ESgotpars, /* Finished getting the parameters */
ESfunckey, /* Function key */
EShash, /* DEC-specific stuff (screen align, etc.) */
ESsetG0, /* Specify the G0 character set */
ESsetG1, /* Specify the G1 character set */
ESignore /* Ignore this sequence */
} vt100state = ESnormal;
/* Received from MacBSDBoot, stored by Locore: */
long videoaddr;
long videorowbytes;
long videobitdepth;
unsigned long videosize;
/* Calculated in itecninit(): */
static int width, height, scrrows, scrcols;
static void (*putpixel) (int x, int y, int *c, int num);
static void (*reversepixel) (int x, int y, int num);
/* VT100 state: */
#define MAXPARS 16
static int x = 0, y = 0, savex, savey;
static int par[MAXPARS], numpars, hanging_cursor, attr;
/* Our tty: */
struct tty *ite_tty;
/* For polled ADB mode: */
static int polledkey;
extern int adb_polling;
/* Misc */
void itestart();
static void ite_putchar(char ch);
/*
* Bitmap handling functions
*/
static inline void
putpixel1(int xx, int yy, int *c, int num)
{
unsigned int i, mask;
unsigned char *sc;
sc = (unsigned char *) videoaddr;
i = 7 - (xx & 7);
mask = ~(1 << i);
sc += yy * videorowbytes + (xx >> 3);
while (num--) {
*sc &= mask;
*sc |= (*c++ & 1) << i;
sc += videorowbytes;
}
}
static void
putpixel2(int xx, int yy, int *c, int num)
{
unsigned int i, mask;
unsigned char *sc;
sc = (unsigned char *) videoaddr;
i = 6 - ((xx & 3) << 1);
mask = ~(3 << i);
sc += yy * videorowbytes + (xx >> 2);
while (num--) {
*sc &= mask;
*sc |= (*c++ & 3) << i;
sc += videorowbytes;
}
}
static void
putpixel4(int xx, int yy, int *c, int num)
{
unsigned int i, mask;
unsigned char *sc;
sc = (unsigned char *) videoaddr;
i = 4 - ((xx & 1) << 2);
mask = ~(15 << i);
sc += yy * videorowbytes + (xx >> 1);
while (num--) {
*sc &= mask;
*sc |= (*c++ & 15) << i;
sc += videorowbytes;
}
}
static void
putpixel8(int xx, int yy, int *c, int num)
{
unsigned char *sc;
sc = (unsigned char *) videoaddr;
sc += yy * videorowbytes + xx;
while (num--) {
*sc = *c++ & 0xff;
sc += videorowbytes;
}
}
static void
reversepixel1(int xx, int yy, int num)
{
unsigned int mask;
unsigned char *sc;
sc = (unsigned char *) videoaddr;
mask = 0; /* Get rid of warning from compiler */
switch (videobitdepth) {
case 1:
mask = 1 << (7 - (xx & 7));
sc += yy * videorowbytes + (xx >> 3);
break;
case 2:
mask = 3 << (6 - ((xx & 3) << 1));
sc += yy * videorowbytes + (xx >> 2);
break;
case 4:
mask = 15 << (4 - ((xx & 1) << 2));
sc += yy * videorowbytes + (xx >> 1);
break;
case 8:
mask = 255;
sc += yy * videorowbytes + xx;
break;
default:
panic("reversepixel(): unsupported bit depth");
}
while (num--) {
*sc ^= mask;
sc += videorowbytes;
}
}
static void
writechar(char ch, int x, int y, int attr)
{
int i, j, mask, rev, col[CHARHEIGHT];
unsigned char *c;
ch &= 0x7F;
x *= CHARWIDTH;
y *= CHARHEIGHT;
rev = (attr & ATTR_REVERSE) ? 255 : 0;
c = &Font6x10[ch * CHARHEIGHT];
switch (videobitdepth) {
case 1:
for (j = 0; j < CHARWIDTH; j++) {
mask = 1 << (CHARWIDTH - 1 - j);
for (i = 0; i < CHARHEIGHT; i++) {
col[i] = ((c[i] & mask) ? 255 : 0) ^ rev;
}
putpixel1(x + j, y, col, CHARHEIGHT);
}
if (attr & ATTR_UNDER) {
col[0] = 255;
for (j = 0; j < CHARWIDTH; j++) {
putpixel1(x + j, y + CHARHEIGHT - 1, col, 1);
}
}
break;
case 2:
case 4:
case 8:
for (j = 0; j < CHARWIDTH; j++) {
mask = 1 << (CHARWIDTH - 1 - j);
for (i = 0; i < CHARHEIGHT; i++) {
col[i] = ((c[i] & mask) ? 255 : 0) ^ rev;
}
putpixel(x + j, y, col, CHARHEIGHT);
}
if (attr & ATTR_UNDER) {
col[0] = 255;
for (j = 0; j < CHARWIDTH; j++) {
putpixel(x + j, y + CHARHEIGHT - 1, col, 1);
}
}
break;
}
}
static void
drawcursor(void)
{
int j, X, Y;
X = x * CHARWIDTH;
Y = y * CHARHEIGHT;
for (j = 0; j < CHARWIDTH; j++) {
reversepixel(X + j, Y, CHARHEIGHT);
}
}
static void
erasecursor(void)
{
int j, X, Y;
X = x * CHARWIDTH;
Y = y * CHARHEIGHT;
for (j = 0; j < CHARWIDTH; j++) {
reversepixel(X + j, Y, CHARHEIGHT);
}
}
static void
scrollup(void)
{
unsigned long *from, *to;
int i, linelongs, tocopy, copying;
linelongs = videorowbytes * CHARHEIGHT / 4;
to = (unsigned long *) videoaddr;
from = to + linelongs;
tocopy = (scrrows - 1) * linelongs;
while (tocopy > 0) {
copying = (tocopy > 16383) ? 16383 : tocopy;
bcopy(from, to, copying * 4);
from += copying;
to += copying;
tocopy -= copying;
}
to = (unsigned long *) videoaddr;
bzero(to + (scrrows - 1) * linelongs, linelongs * sizeof(long));
}
static void
scrolldown(void)
{
unsigned long *from, *to;
int i, linelongs;
linelongs = videorowbytes * CHARHEIGHT / 4;
to = (unsigned long *) videoaddr + linelongs * scrrows;
from = to - linelongs;
for (i = (scrrows - 1) * linelongs; i > 0; i--) {
*--to = *--from;
}
for (i = linelongs; i > 0; i--) {
*--to = 0;
}
}
static void
clear_screen(int which)
{
unsigned long *p;
int i, linelongs;
p = (unsigned long *) videoaddr;
linelongs = videorowbytes * CHARHEIGHT / 4;
switch (which) {
case 0: /* To end of screen */
p += y * linelongs;
i = (scrrows - y) * linelongs;
break;
case 1: /* To start of screen */
i = y * linelongs;
break;
case 2: /* Whole screen */
i = scrrows * linelongs;
break;
}
bzero(p, i * sizeof(long));
}
static void
clear_line(int which)
{
int start, end, i;
/*
* This routine runs extremely slowly. I don't think it's
* used all that often, except for To end of line. I'll go
* back and speed this up when I speed up the whole ite
* module. --LK
*/
switch (which) {
case 0: /* To end of line */
start = x;
end = scrcols;
break;
case 1: /* To start of line */
start = 0;
end = x;
break;
case 2: /* Whole line */
start = 0;
end = scrcols;
break;
}
for (i = start; i < end; i++) {
writechar(' ', i, y, ATTR_NONE);
}
}
static void
putc_normal(char ch)
{
switch (ch) {
case '\a': /* Beep */
asc_ringbell();
break;
case 127: /* Delete */
case '\b': /* Backspace */
if (hanging_cursor) {
hanging_cursor = 0;
} else
if (x > 0) {
x--;
}
break;
case '\t': /* Tab */
do {
ite_putchar(' ');
} while (x % 8 != 0);
break;
case '\n': /* Line feed */
y++;
if (y >= scrrows) {
scrollup();
y = scrrows - 1;
}
break;
case '\r': /* Carriage return */
x = 0;
hanging_cursor = 0;
break;
case '\e': /* Escape */
vt100state = ESesc;
hanging_cursor = 0;
break;
default:
if (ch >= ' ') {
if (hanging_cursor) {
x = 0;
y++;
if (y >= scrrows) {
scrollup();
y = scrrows - 1;
}
hanging_cursor = 0;
}
writechar(ch, x, y, attr);
if (x == scrcols - 1) {
hanging_cursor = 1;
} else {
x++;
}
if (x >= scrcols) {
x = 0;
y++;
}
}
break;
}
}
static void
putc_esc(char ch)
{
vt100state = ESnormal;
switch (ch) {
case '[':
vt100state = ESsquare;
break;
case 'D': /* Line feed */
y++;
break;
case 'H': /* Set tab stop */
/* Not supported */
break;
case 'M': /* Cursor up */
if (y == 0) {
scrolldown();
} else {
y--;
}
break;
case '7': /* Save cursor */
savex = x;
savey = y;
break;
case '8': /* Restore cursor */
x = savex;
y = savey;
break;
default:
/* Rest not supported */
break;
}
}
static void
putc_gotpars(char ch)
{
int i;
vt100state = ESnormal;
switch (ch) {
case 'A': /* Up */
y--;
break;
case 'B': /* Down */
y++;
break;
case 'C': /* Right */
x++;
break;
case 'D': /* Left */
x--;
break;
case 'H': /* Set cursor position */
x = par[1] - 1;
y = par[0] - 1;
hanging_cursor = 0;
break;
case 'J': /* Clear part of screen */
clear_screen(par[0]);
break;
case 'K': /* Clear part of line */
clear_line(par[0]);
break;
case 'g': /* Clear tab stops */
/* Not supported */
break;
case 'm': /* Set attribute */
for (i = 0; i < numpars; i++) {
switch (par[i]) {
case 0:
attr = ATTR_NONE;
break;
case 1:
attr |= ATTR_BOLD;
break;
case 4:
attr |= ATTR_UNDER;
break;
case 7:
attr |= ATTR_REVERSE;
break;
}
}
break;
case 'r': /* Set scroll region */
/* Not supported */
break;
}
}
static void
putc_getpars(char ch)
{
if (ch == '?') {
/* Not supported */
return;
}
if (ch == '[') {
vt100state = ESnormal;
/* Not supported */
return;
}
if (ch == ';' && numpars < MAXPARS - 1) {
numpars++;
} else
if (ch >= '0' && ch <= '9') {
par[numpars] *= 10;
par[numpars] += ch - '0';
} else {
numpars++;
vt100state = ESgotpars;
putc_gotpars(ch);
}
}
static void
putc_square(char ch)
{
int i;
for (i = 0; i < MAXPARS; i++) {
par[i] = 0;
}
numpars = 0;
vt100state = ESgetpars;
putc_getpars(ch);
}
static void
ite_putchar(char ch)
{
switch (vt100state) {
default:vt100state = ESnormal; /* FALLTHROUGH */
case ESnormal:
putc_normal(ch);
break;
case ESesc:
putc_esc(ch);
break;
case ESsquare:
putc_square(ch);
break;
case ESgetpars:
putc_getpars(ch);
break;
case ESgotpars:
putc_gotpars(ch);
break;
}
if (x >= scrcols) {
x = scrcols - 1;
}
if (x < 0) {
x = 0;
}
if (y >= scrrows) {
y = scrrows - 1;
}
if (y < 0) {
y = 0;
}
}
/*
* Keyboard support functions
*/
static int
ite_dopollkey(int key)
{
polledkey = key;
return 0;
}
static int
ite_pollforchar(void)
{
int s;
register int intbits;
s = splhigh();
polledkey = -1;
adb_polling = 1;
/* pretend we're VIA interrupt dispatcher */
while (polledkey == -1) {
intbits = via_reg(VIA1, vIFR);
if (intbits & V1IF_ADBRDY) {
mrg_adbintr();
via_reg(VIA1, vIFR) = V1IF_ADBRDY;
}
if (intbits & 0x10) {
mrg_pmintr();
via_reg(VIA1, vIFR) = 0x10;
}
}
adb_polling = 0;
splx(s);
return polledkey;
}
/*
* Tty handling functions
*/
static void
iteattach(struct device * parent, struct device * dev, void *aux)
{
printf(" (minimal console)\n");
}
extern int matchbyname();
struct cfdriver itecd = {
NULL, "ite", matchbyname, iteattach, DV_TTY, sizeof(struct device)
};
int
iteopen(dev_t dev, int mode, int devtype, struct proc * p)
{
register struct tty *tp;
register int error;
int first = 0;
dprintf("iteopen(): enter(0x%x)\n", (int) dev);
if (ite_tty == NULL)
tp = ite_tty = ttymalloc();
else
tp = ite_tty;
if ((tp->t_state & (TS_ISOPEN | TS_XCLUDE)) == (TS_ISOPEN | TS_XCLUDE)
&& p->p_ucred->cr_uid != 0)
return (EBUSY);
tp->t_oproc = itestart;
tp->t_param = NULL;
tp->t_dev = dev;
if ((tp->t_state & TS_ISOPEN) == 0) {
ttychars(tp);
tp->t_iflag = TTYDEF_IFLAG;
tp->t_oflag = TTYDEF_OFLAG;
tp->t_cflag = CS8 | CREAD;
tp->t_lflag = TTYDEF_LFLAG;
tp->t_ispeed = tp->t_ospeed = TTYDEF_SPEED;
tp->t_state = TS_ISOPEN | TS_CARR_ON;
ttsetwater(tp);
}
error = (*linesw[tp->t_line].l_open) (dev, tp);
tp->t_winsize.ws_row = scrrows;
tp->t_winsize.ws_col = scrcols;
dprintf("iteopen(): exit(%d)\n", error);
return (error);
}
int
iteclose(dev_t dev, int flag, int mode, struct proc * p)
{
dprintf("iteclose: enter (%d)\n", (int) dev);
(*linesw[ite_tty->t_line].l_close) (ite_tty, flag);
ttyclose(ite_tty);
#if 0
ttyfree(ite_tty);
ite_tty = (struct tty *) 0;
#endif
dprintf("iteclose: exit\n");
return 0;
}
int
iteread(dev_t dev, struct uio * uio, int flag)
{
dprintf("iteread: enter\n");
return (*linesw[ite_tty->t_line].l_read) (ite_tty, uio, flag);
dprintf("iteread: exit\n");
}
int
itewrite(dev_t dev, struct uio * uio, int flag)
{
dprintf("itewrite: enter\n");
return (*linesw[ite_tty->t_line].l_write) (ite_tty, uio, flag);
dprintf("itewrite: exit\n");
}
struct tty *
itetty(dev)
dev_t dev;
{
return (ite_tty);
}
int
iteioctl(dev_t dev, int cmd, caddr_t addr, int flag, struct proc * p)
{
register struct tty *tp = ite_tty;
int error;
dprintf("iteioctl: enter(%d, 0x%x)\n", cmd, cmd);
error = (*linesw[tp->t_line].l_ioctl) (tp, cmd, addr, flag, p);
if (error >= 0) {
dprintf("iteioctl: exit(%d)\n", error);
return (error);
}
error = ttioctl(tp, cmd, addr, flag, p);
if (error >= 0) {
dprintf("iteioctl: exit(%d)\n", error);
return (error);
}
switch (cmd) {
case ITEIOC_RINGBELL:{
asc_ringbell();
return (0);
}
case ITEIOC_SETBELL:{
struct bellparams *bp = (void *) addr;
asc_setbellparams(bp->freq, bp->len, bp->vol);
return (0);
}
case ITEIOC_GETBELL:{
struct bellparams *bp = (void *) addr;
asc_getbellparams(&bp->freq, &bp->len, &bp->vol);
return (0);
}
}
dprintf("iteioctl: exit(ENOTTY)\n");
return (ENOTTY);
}
void
itestart(register struct tty * tp)
{
register int cc, s;
int hiwat = 0, hadcursor = 0;
s = spltty();
if (tp->t_state & (TS_TIMEOUT | TS_BUSY | TS_TTSTOP)) {
splx(s);
return;
}
tp->t_state |= TS_BUSY;
cc = tp->t_outq.c_cc;
splx(s);
erasecursor();
while (cc-- > 0) {
ite_putchar(getc(&tp->t_outq));
}
drawcursor();
s = spltty();
tp->t_state &= ~TS_BUSY;
splx(s);
}
void
itestop(struct tty * tp, int flag)
{
int s;
s = spltty();
if (tp->t_state & TS_BUSY) {
if ((tp->t_state & TS_TTSTOP) == 0) {
tp->t_state |= TS_FLUSH;
}
}
splx(s);
}
int
ite_intr(adb_event_t * event)
{
static int shift = 0, control = 0;
int key, press, val, state;
char str[10], *s;
key = event->u.k.key;
press = ADBK_PRESS(key);
val = ADBK_KEYVAL(key);
if (val == ADBK_SHIFT) {
shift = press;
} else
if (val == ADBK_CONTROL) {
control = press;
} else
if (press) {
switch (val) {
case ADBK_UP:
str[0] = '\e';
str[1] = 'O';
str[2] = 'A';
str[3] = '\0';
break;
case ADBK_DOWN:
str[0] = '\e';
str[1] = 'O';
str[2] = 'B';
str[3] = '\0';
break;
case ADBK_RIGHT:
str[0] = '\e';
str[1] = 'O';
str[2] = 'C';
str[3] = '\0';
break;
case ADBK_LEFT:
str[0] = '\e';
str[1] = 'O';
str[2] = 'D';
str[3] = '\0';
break;
default:
state = 0;
if (shift) {
state = 1;
}
if (control) {
state = 2;
}
str[0] = keyboard[val][state];
str[1] = '\0';
break;
}
if (adb_polling) {
polledkey = str[0];
} else {
for (s = str; *s; s++) {
(*linesw[ite_tty->t_line].l_rint) (*s, ite_tty);
}
}
}
}
/*
* Console functions
*/
int
itecnprobe(struct consdev * cp)
{
int maj, unit;
/* locate the major number */
for (maj = 0; maj < nchrdev; maj++) {
if (cdevsw[maj].d_open == iteopen) {
break;
}
}
if (maj == nchrdev) {
panic("itecnprobe(): did not find iteopen().");
}
unit = 0; /* hardcode first device as console. */
/* initialize required fields */
cp->cn_dev = makedev(maj, unit);
cp->cn_pri = CN_INTERNAL;
}
int
itecninit(struct consdev * cp)
{
width = videosize & 0xffff;
height = (videosize >> 16) & 0xffff;
scrrows = height / CHARHEIGHT;
scrcols = width / CHARWIDTH;
switch (videobitdepth) {
default:
case 1:
putpixel = putpixel2;
reversepixel = reversepixel1;
break;
case 2:
putpixel = putpixel2;
reversepixel = reversepixel1;
break;
case 4:
putpixel = putpixel4;
reversepixel = reversepixel1;
break;
case 8:
putpixel = putpixel8;
reversepixel = reversepixel1;
break;
}
iteon(cp->cn_dev, 0);
}
int
iteon(dev_t dev, int flags)
{
erasecursor();
clear_screen(2);
drawcursor();
}
int
iteoff(dev_t dev, int flags)
{
erasecursor();
clear_screen(2);
}
int
itecngetc(dev_t dev)
{
/* Oh, man... */
return ite_pollforchar();
}
int
itecnputc(dev_t dev, int c)
{
extern dev_t mac68k_serdev;
int s;
/* s = splhigh (); */
erasecursor();
ite_putchar(c);
drawcursor();
if (mac68k_machine.serial_boot_echo)
sercnputc(mac68k_serdev, c);
/* splx (s); */
}