Files
Arquivotheca.SunOS-4.1.4/sys/old/su.c
seta75D ff309bfe1c Init
2021-10-11 18:37:13 -03:00

581 lines
12 KiB
C

#ifndef lint
static char sccsid[] = "@(#)su.c 1.1 94/10/31 Copyr 1983 Sun Micro";
#endif
/*
* Copyright (c) 1983 by Sun Microsystems, Inc.
*/
#include "su.h"
#if NSU > 0
/*
* Sun-1 on-board UART driver.
*
* NOTE:
* This driver is specific to the Sun-1.
* Actually, is it a driver for the Intel 8274
* Multi-Protocol Serial Controller chip.
*
* TODO:
* test modem control
* try status-affects-vector mode
*/
#include "bk.h"
#include "../h/param.h"
#include "../h/systm.h"
#include "../h/ioctl.h"
#include "../h/tty.h"
#include "../h/dir.h"
#include "../h/user.h"
#include "../h/proc.h"
#include "../h/buf.h"
#include "../h/conf.h"
#include "../h/bk.h"
#include "../h/file.h"
#include "../h/uio.h"
#include "../sun/clock.h"
#include "../sun/mmu.h"
#include "../sun/cpu.h"
#include "../sundev/mbvar.h"
#include "../sundev/sureg.h"
#include <mon/sunmon.h>
/*
* Driver information for auto-configuration stuff.
*/
int suprobe(), suattach(), suintr();
struct mb_device *suinfo[NSU];
u_long sustd[] = { SUADDR, 0 };
struct mb_driver sudriver = {
suprobe, 0, suattach, 0, 0, suintr, sustd, 0, 0,
"su", suinfo, 0, 0, MDR_OBIO
};
#define NSULINE (NSU*2)
int sustart();
int ttrstrt();
struct tty su_tty[NSULINE];
int su_cnt = { NSULINE };
int suact;
#ifdef BIGBUFFER
/*
* Buffer and pointers used by asm code in locore.s
* to scan for input characters every clock tick.
*/
char suibuf[100*3]; /* input buffer (100 chars * 3 bytes/char) */
char *suwptr = suibuf; /* write pointer */
char *surptr = suibuf; /* read pointer */
char *suend = &suibuf[sizeof (suibuf)];
int su_timer; /* input timer started? */
int supoll();
#endif
/*
* Software copy of write only registers
*/
char su_wr5[NSULINE]; /* modem control, mostly */
short su_clk[NSULINE]; /* clock rate (speed) */
char susoftCAR[NSU];
/*
* Disable line if used for console I/O
*/
extern char sudisable[NSULINE];
short su_speeds[] =
{ 0,3072,2048,1396,1142,1024,768,512,256,128,85,64,32,16,8,4 };
#define ISPEED B9600
#define IFLAGS (EVENP|ODDP|ECHO)
#ifdef BIGBUFFER
#define splsu spl6
#else
#define splsu spl5
#endif
suprobe(reg)
caddr_t reg;
{
if (*RomVecPtr->v_InSource == IoUARTA)
sudisable[0] = 1;
else if (*RomVecPtr->v_InSource == IoUARTB)
sudisable[1] = 1;
return (cpu == SUN_1);
}
suattach(md)
register struct mb_device *md;
{
register struct tty *tp = &su_tty[md->md_unit*2];
register struct device *suaddr = (struct device *)md->md_addr;
register int i;
for (i = 0; i < 2; i++, tp++, suaddr++) {
tp->t_addr = (caddr_t)suaddr;
suaddr->sucsr = SUWR0_RESET_ALL;
DELAY(4);
suaddr->sucsr = 2;
if (i == 0) /* channel A only */
suaddr->sucsr = SUWR2_INIT;
else /* channel B only */
suaddr->sucsr = 1;
/*
* Set up enough characteristics so
* monitor "ut" will still work.
*/
suaddr->sucsr = 4;
suaddr->sucsr = SUWR4_1_STOP|SUWR4_X16_CLK;
suaddr->sucsr = 3;
suaddr->sucsr = SUWR3_RX_8|SUWR3_INIT;
suaddr->sucsr = 5;
suaddr->sucsr = SUWR5_TX_8|SUWR5_TXENABLE|SUWR5_DTR|SUWR5_RTS;
}
susoftCAR[md->md_unit] = md->md_flags;
#ifdef BIGBUFFER
if (su_timer == 0) {
su_timer = 1;
timeout(supoll, (caddr_t)0, 1);
}
#endif
}
/*ARGSUSED*/
suopen(dev, flag)
dev_t dev;
{
register struct tty *tp;
register int unit;
unit = minor(dev);
if (unit >= su_cnt)
return (ENXIO);
tp = &su_tty[unit];
tp->t_oproc = sustart;
tp->t_state |= TS_WOPEN;
if ((tp->t_state & TS_ISOPEN) == 0) {
ttychars(tp);
tp->t_ospeed = tp->t_ispeed = ISPEED;
tp->t_flags = IFLAGS;
/* tp->t_state |= TS_HUPCLS; */
suparam(unit);
} else if (tp->t_state&TS_XCLUDE && u.u_uid != 0)
return (EBUSY);
(void) sumctl(dev, SU_ON, DMSET);
(void) splsu();
if ((susoftCAR[unit>>1]&(1<<(unit&1))) ||
(sumctl(dev, 0, DMGET)&SURR0_CD))
tp->t_state |= TS_CARR_ON;
while ((tp->t_state & TS_CARR_ON) == 0) {
tp->t_state |= TS_WOPEN;
sleep((caddr_t)&tp->t_rawq, TTIPRI);
}
(void) spl0();
return ((*linesw[tp->t_line].l_open)(dev, tp));
}
/*ARGSUSED*/
suclose(dev, flag)
dev_t dev;
{
register struct tty *tp;
register int unit;
register struct device *suaddr;
unit = minor(dev);
tp = &su_tty[unit];
(*linesw[tp->t_line].l_close)(tp);
suaddr = (struct device *)tp->t_addr;
(void) splsu();
suaddr->sucsr = 5;
suaddr->sucsr = (su_wr5[unit] &= ~SUWR5_BREAK);
(void) spl0();
if ((tp->t_state&(TS_HUPCLS|TS_WOPEN)) || (tp->t_state&TS_ISOPEN) == 0)
(void) sumctl(dev, SU_OFF, DMSET);
ttyclose(tp);
(void) splsu();
if ((tp->t_state & (TS_ISOPEN|TS_WOPEN)) == 0) {
suaddr->sucsr = 1;
suaddr->sucsr = SUWR1_INIT & ~SUWR1_RIE;
}
(void) spl0();
}
suread(dev, uio)
dev_t dev;
struct uio *uio;
{
register struct tty *tp;
tp = &su_tty[minor(dev)];
(*linesw[tp->t_line].l_read)(tp, uio);
}
suwrite(dev, uio)
dev_t dev;
struct uio *uio;
{
register struct tty *tp;
tp = &su_tty[minor(dev)];
(*linesw[tp->t_line].l_write)(tp, uio);
}
#ifdef BIGBUFFER
supoll()
{
register struct tty *tp;
register int c, s1;
register int unit;
struct device *suaddr;
int s;
while (surptr != suwptr) {
unit = *surptr++;
tp = &su_tty[unit];
c = *surptr++;
s1 = *surptr++;
if ((tp->t_state & TS_ISOPEN) == 0) {
wakeup((caddr_t)&tp->t_rawq);
#ifdef PORTSELECTOR
if ((tp->t_state&TS_WOPEN) == 0)
#endif
goto cont;
}
if (s1 & SURR1_FE)
if (tp->t_flags & RAW)
c = 0;
else
c = tp->t_intrc;
if (s1&SURR1_DO) {
s = splsu();
suaddr = (struct device *)tp->t_addr;
suaddr->sucsr = 1;
suaddr->sucsr = SUWR1_INIT;
(void) splx(s);
/* printf("su,%c: silo overflow\n", 'a'+unit); */
}
if (s1&SURR1_PE)
if (((tp->t_flags & (EVENP|ODDP)) == EVENP)
|| ((tp->t_flags & (EVENP|ODDP)) == ODDP))
goto cont;
#if NBK > 0
if (tp->t_line == NETLDISC) {
c &= 0177;
BKINPUT(c, tp);
} else
#endif
(*linesw[tp->t_line].l_rint)(c, tp);
cont:
if (surptr >= suend)
surptr = suibuf;
}
timeout(supoll, (caddr_t)0, 1);
}
#endif
suintr()
{
register struct tty *tp;
register int c, s0, s1;
register struct device *suaddr;
register int unit, st, overrun, serviced = 0;
for (tp = su_tty, unit = 0; unit < NSULINE; tp++, unit++) {
if (sudisable[unit])
continue;
suaddr = (struct device *)tp->t_addr;
suaddr->sucsr = SUWR0_RESET_STATUS;
if (suaddr->sucsr & SURR0_CD) { /* carrier up? */
if ((tp->t_state&TS_CARR_ON) == 0) {
wakeup((caddr_t)&tp->t_rawq);
tp->t_state |= TS_CARR_ON;
}
} else { /* no carrier */
if (tp->t_state&TS_CARR_ON) {
gsignal(tp->t_pgrp, SIGHUP);
gsignal(tp->t_pgrp, SIGCONT);
flushtty(tp, FREAD|FWRITE);
}
tp->t_state &= ~TS_CARR_ON;
}
#ifndef BIGBUFFER
overrun = 0;
st = SURR0_DONE|SURR0_BREAK;
while ((s0 = suaddr->sucsr) & st) {
c = suaddr->subuf;
suaddr->sucsr = 1;
s1 = suaddr->sucsr;
suaddr->sucsr = SUWR0_RESET_ERRORS;
st = SURR0_DONE; /* don't look for break again */
serviced++;
if ((tp->t_state & TS_ISOPEN) == 0) {
wakeup((caddr_t)&tp->t_rawq);
#ifdef PORTSELECTOR
if ((tp->t_state&TS_WOPEN) == 0)
#endif
continue;
}
if ((s0 & SURR0_BREAK) /* || (s1 & SURR1_FE) */)
if (tp->t_flags & RAW)
c = 0;
else
c = tp->t_intrc;
if ((s1&SURR1_DO) /* && overrun == 0 */) {
suaddr->sucsr = 1;
suaddr->sucsr = SUWR1_INIT;
if (overrun == 0) {
/* printf("su,%c: silo overflow\n", 'a'+unit); */
overrun = 1;
}
}
if (s1&SURR1_PE)
if (((tp->t_flags & (EVENP|ODDP)) == EVENP)
|| ((tp->t_flags & (EVENP|ODDP)) == ODDP))
continue;
#if NBK > 0
if (tp->t_line == NETLDISC) {
c &= 0177;
BKINPUT(c, tp);
} else
#endif
(*linesw[tp->t_line].l_rint)(c, tp);
}
}
for (tp = su_tty, unit = 0; unit < NSULINE; tp++, unit++) {
if (sudisable[unit])
continue;
suaddr = (struct device *)tp->t_addr;
#endif BIGBUFFER
if (suaddr->sucsr&SURR0_READY) {
suaddr->sucsr = SUWR0_RESET_TXINT;
serviced++;
tp->t_state &= ~TS_BUSY;
if (tp->t_line)
(*linesw[tp->t_line].l_start)(tp);
else
sustart(tp);
}
}
return (serviced);
}
/*ARGSUSED*/
suioctl(dev, cmd, data, flag)
dev_t dev;
caddr_t data;
{
register struct tty *tp;
register int unit = minor(dev);
register struct device *suaddr;
int error;
tp = &su_tty[unit];
suaddr = (struct device *)tp->t_addr;
error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, data, flag);
if (error >= 0)
return (error);
error = ttioctl(tp, cmd, data, flag);
if (error >= 0) {
if (cmd == TIOCSETP || cmd == TIOCSETN)
suparam(unit);
return (error);
}
switch(cmd) {
case TIOCSBRK:
(void) splsu();
suaddr->sucsr = 5;
suaddr->sucsr = (su_wr5[unit] |= SUWR5_BREAK);
(void) spl0();
break;
case TIOCCBRK:
(void) splsu();
suaddr->sucsr = 5;
suaddr->sucsr = (su_wr5[unit] &= ~SUWR5_BREAK);
(void) spl0();
break;
case TIOCSDTR:
(void) sumctl(dev, SU_ON, DMBIS);
break;
case TIOCCDTR:
(void) sumctl(dev, SU_OFF, DMBIC);
break;
case TIOCMSET:
(void) sumctl(dev, dmtosu(*(int *)data), DMSET);
break;
case TIOCMBIS:
(void) sumctl(dev, dmtosu(*(int *)data), DMBIS);
break;
case TIOCMBIC:
(void) sumctl(dev, dmtosu(*(int *)data), DMBIC);
break;
case TIOCMGET:
*(int *)data = sutodm(sumctl(dev, 0, DMGET));
break;
default:
return (ENOTTY);
}
return (0);
}
dmtosu(bits)
register int bits;
{
register int b = 0;
if (bits & DML_CAR) b |= SURR0_CD;
if (bits & DML_CTS) b |= SURR0_CTS;
if (bits & DML_RTS) b |= SUWR5_RTS;
if (bits & DML_DTR) b |= SUWR5_DTR;
return (b);
}
sutodm(bits)
register int bits;
{
register int b = 0;
if (bits & SURR0_CD) b |= DML_CAR;
if (bits & SURR0_CTS) b |= DML_CTS;
if (bits & SUWR5_RTS) b |= DML_RTS;
if (bits & SUWR5_DTR) b |= DML_DTR;
return (b);
}
suparam(unit)
register int unit;
{
register struct tty *tp;
register struct device *suaddr;
register int wr3, wr4, wr5, speed;
int s;
tp = &su_tty[unit];
suaddr = (struct device *)tp->t_addr;
s = splsu();
suaddr->sucsr = 1;
suaddr->sucsr = SUWR1_INIT;
if (tp->t_ispeed == 0) {
(void) sumctl(unit, SU_OFF, DMSET); /* hang up line */
(void) splx(s);
return;
}
wr3 = SUWR3_INIT;
wr4 = SUWR4_X16_CLK;
wr5 = (su_wr5[unit] & (SUWR5_RTS|SUWR5_DTR)) | SUWR5_TXENABLE;
if (tp->t_ispeed == B134) { /* what a joke! */
wr3 |= SUWR3_RX_6;
wr4 |= SUWR4_PARITY_ENABLE | SUWR4_PARITY_EVEN;
wr5 |= SUWR5_TX_6;
} else if (tp->t_flags&(RAW|LITOUT)) {
wr3 |= SUWR3_RX_8;
wr5 |= SUWR5_TX_8;
} else {
wr3 |= SUWR3_RX_7;
wr4 |= SUWR4_PARITY_ENABLE;
wr5 |= SUWR5_TX_7;
}
if (tp->t_flags & EVENP)
wr4 |= SUWR4_PARITY_EVEN;
if (tp->t_ispeed == B110)
wr4 |= SUWR4_2_STOP;
else if (tp->t_ispeed == B134)
wr4 |= SUWR4_1_5_STOP;
else
wr4 |= SUWR4_1_STOP;
suaddr->sucsr = 4; suaddr->sucsr = wr4;
suaddr->sucsr = 3; suaddr->sucsr = wr3;
suaddr->sucsr = 5; suaddr->sucsr = wr5;
su_wr5[unit] = wr5;
speed = su_speeds[tp->t_ispeed&017];
if (su_clk[unit] != speed) {
CLKADDR->clk_cmd = CLK_LMODE | (UARTTIMER+unit);
CLKADDR->clk_data = CLK_UART_MODE;
CLKADDR->clk_cmd = CLK_LLOAD | (UARTTIMER+unit);
CLKADDR->clk_data = speed;
CLKADDR->clk_cmd = CLK_GO | (1<<(UARTTIMER-1+unit));
su_clk[unit] = speed;
}
(void) splx(s);
}
sustart(tp)
register struct tty *tp;
{
register struct device *suaddr;
register int c, s;
suaddr = (struct device *)tp->t_addr;
s = splsu();
if (tp->t_state & (TS_TIMEOUT|TS_BUSY|TS_TTSTOP))
goto out;
if (tp->t_outq.c_cc <= TTLOWAT(tp)) {
if (tp->t_state&TS_ASLEEP) {
tp->t_state &= ~TS_ASLEEP;
wakeup((caddr_t)&tp->t_outq);
}
if (tp->t_wsel) {
selwakeup(tp->t_wsel, tp->t_state & TS_WCOLL);
tp->t_wsel = 0;
tp->t_state &= ~TS_WCOLL;
}
}
if (tp->t_outq.c_cc == 0)
goto out;
c = getc(&tp->t_outq);
if (c <= 0177 || (tp->t_flags&(RAW|LITOUT))) {
suaddr->subuf = c; /* 8274 adds parity if desired */
tp->t_state |= TS_BUSY;
} else {
timeout(ttrstrt, (caddr_t)tp, c&0177);
tp->t_state |= TS_TIMEOUT;
}
out:
(void) splx(s);
}
sumctl(dev, bits, how)
dev_t dev;
int bits, how;
{
register struct device *suaddr;
register int unit, mbits, s;
unit = minor(dev);
suaddr = (struct device *)su_tty[unit].t_addr;
s = splsu();
mbits = su_wr5[unit] & (SUWR5_RTS|SUWR5_DTR);
suaddr->sucsr = SUWR0_RESET_STATUS;
mbits |= suaddr->sucsr & (SURR0_CD|SURR0_CTS);
switch (how) {
case DMSET:
mbits = bits;
break;
case DMBIS:
mbits |= bits;
break;
case DMBIC:
mbits &= ~bits;
break;
case DMGET:
(void) splx(s);
return (mbits);
}
su_wr5[unit] &= ~(SUWR5_RTS|SUWR5_DTR);
suaddr->sucsr = 5;
suaddr->sucsr = (su_wr5[unit] |= mbits&(SUWR5_RTS|SUWR5_DTR));
(void) splx(s);
return (mbits);
}
#endif