USG_PG3/usr/source/io2/tty.c
#
/*
*/
/*
* general TTY subroutines
*/
#include "../head/param.h"
#include "../head/systm.h"
#include "../head/user.h"
#include "../head/userx.h"
#include "../head/tty.h"
#include "../head/proc.h"
#include "../head/procx.h"
#include "../head/inode.h"
#include "../head/inodex.h"
#include "../head/file.h"
#include "../head/filex.h"
#include "../head/reg.h"
#include "../head/conf.h"
int nltype; /* number of line disciplines */
char partab[];
#define SSPEED 7 /* standard speed: 300 baud */
#define DLDELAY 4 /* Extra delay for DL's (double buff) */
/*
* Input mapping table-- if an entry is non-zero, when the
* corresponding character is typed preceded by "\" the escape
* sequence is replaced by the table value. Mostly used for
* upper-case only terminals.
*/
char maptab[]
{
000,000,000,000,CEOT,00,000,000,
000,000,000,000,000,000,000,000,
000,000,000,000,000,000,000,000,
000,000,000,000,000,000,000,000,
000,'|',000,'#',000,000,000,'`',
'{','}',000,000,000,000,000,000,
000,000,000,000,000,000,000,000,
000,000,000,000,000,000,000,000,
'@',000,000,000,000,000,000,000,
000,000,000,000,000,000,000,000,
000,000,000,000,000,000,000,000,
000,000,000,000,000,000,'~',000,
000,'A','B','C','D','E','F','G',
'H','I','J','K','L','M','N','O',
'P','Q','R','S','T','U','V','W',
'X','Y','Z',000,000,000,000,000,
};
/*
* The actual structure of a clist block manipulated by
* getc and putc (mch.s)
*/
struct cblock {
struct cblock *c_next;
char info[6];
};
/* The character lists-- space for 6*NCLIST characters */
struct cblock cfree[NCLIST];
/* List head for unused character blocks. */
struct cblock *cfreelist;
/*
* structure of device registers for KL, DL, and DC
* interfaces-- more particularly, those for which the
* SSTART bit is off and can be treated by general routines
* (that is, not DH).
*/
struct {
int ttrcsr;
int ttrbuf;
int tttcsr;
int tttbuf;
};
/*
* Routine to open a tty line. Initialize and wait
* for carrier to come on. Establish a process
* group for the distribution of quits and interrupts
* from the tty.
*/
ttopen(dev, atp)
struct tty *atp;
{
register struct tty *tp;
register struct proc *pp;
tp = atp;
tp->t_state =| WOPEN;
if ((tp->t_state&ISOPEN) == 0) {
tp->t_erase = CERASE;
tp->t_kill = CKILL;
tp->t_speeds = SSPEED | (SSPEED<<8);
tp->t_flags = ODDP|EVENP|ECHO;
(*ctlsw[tp->t_dtype].d_param)(dev, tp);
} else if (tp->t_state&XCLUDE && u.u_uid!=0) {
u.u_error = EBUSY;
return;
}
ttmopen(dev, tp);
pp = u.u_procp;
if(pp->p_pgrp == 0) {
if(tp->t_pgrp)
pp->p_pgrp = tp->t_pgrp;
else {
tp->t_pgrp = pp->p_pid;
pp->p_pgrp = pp->p_pid;
}
u.u_ttyp = tp;
u.u_ttyd = dev;
}
tp->t_state =& ~WOPEN;
tp->t_state =| ISOPEN;
}
/*
* Modem open for tty lines. Set up modem to answer phone
* and transmit main carrier. Wait until carrier is received
* from other side.
*/
ttmopen(dev, atp)
struct tty *atp;
{
register struct tty *tp;
register dtype;
tp = atp;
dtype = tp->t_dtype;
(*ctlsw[dtype].d_mctl)(dev, TURNON|RQS);
spl5();
if((*ctlsw[dtype].d_mctl)(dev, FSTATUS) & CARRIER)
tp->t_state =| CARR_ON;
else
tp->t_state =& ~CARR_ON;
while((tp->t_state&CARR_ON)==0)
sleep(&tp->t_rawq, TTIPRI);
spl0();
}
/*
* Close a tty line.
*/
ttclose(dev, atp)
struct tty *atp;
{
register struct tty *tp;
tp = atp;
if (tp->t_state&ISOPEN) {
if (tp->t_flags&HUPCL)
(*ctlsw[tp->t_dtype].d_mctl)(dev, HUP);
tp->t_state =& (CARR_ON|SSTART);
tp->t_pgrp = 0;
if(tp->t_state&CARR_ON)
wflushtty(tp);
else
flushtty(tp);
}
}
/*
* Routine implementing the gtty system call.
* Translate format and handle using ioctl.
*/
gtty()
{
u.u_arg[1] = u.u_arg[0];
u.u_arg[0] = ('t'<<8)|GTTY;
ioctl();
}
/*
* The routine implementing the stty system call.
* Translate format and handle using ioctl.
*/
stty()
{
u.u_arg[1] = u.u_arg[0];
u.u_arg[0] = ('t'<<8)|STTY;
ioctl();
}
/*
* Routine implementing the ioctl system call. Check
* legality and switch out to individual device routine.
*/
ioctl()
{
register struct file *fp;
register struct inode *ip;
register flag;
if ((fp = getf(u.u_ar0[R0])) == NULL)
return;
ip = fp->f_inode;
if ((ip->i_mode&IFMT) != IFCHR) {
u.u_error = ENOTTY;
return;
}
flag = fp->f_flag&(FREAD|FWRITE);
(*cdevsw[ip->i_addr[0].d_major].d_sgtty)(ip->i_addr[0], flag);
}
/*
* ioctl for line discipline devices. Handle setting and
* fetching of line discipline. Switch out to line discipline
* routines for actual work.
*/
ttioctl(dev, atp, flag)
struct tty *atp;
{
register struct tty *tp;
register int *v;
register cmd;
int word0;
tp = atp;
if (u.u_arg[0].hibyte != 't') {
u.u_error = EBIOCTL;
return;
}
v = u.u_arg[1];
u.u_arg[2] = flag;
switch (cmd = u.u_arg[0].lobyte&0377) {
case SET:
case UNSET:
u.u_error = EBIOCTL;
break;
/*
* Get line discipline number
*/
case GETD:
suword(v, tp->t_discp<<8);
break;
/*
* Change to specified line discipline if not same
* as current one. Execute subcommand.
*/
case CHNGD:
word0 = fuword(v);
if(word0.hibyte >= nltype) {
u.u_error = EBIOCTL;
break;
}
if (flag==0 && (tp->t_state&ISOPEN)==0) {
tp->t_discp = word0.hibyte;
break;
}
if (word0.hibyte != tp->t_discp) {
(*linesw[tp->t_discp].l_sgtty)(dev, tp, UNSET);
tp->t_discp = word0.hibyte;
(*linesw[tp->t_discp].l_sgtty)(dev, tp, SET);
break;
}
default:
(*linesw[tp->t_discp].l_sgtty)(dev, tp, cmd);
}
}
/*
* Wait for output to drain, then flush input waiting.
*/
wflushtty(atp)
struct tty *atp;
{
register struct tty *tp;
tp = atp;
spl5();
while (tp->t_outq.c_cc) {
tp->t_state =| ASLEEP;
sleep(&tp->t_outq, TTOPRI);
}
flushtty(tp);
spl0();
}
/*
* Initialize clist by freeing all character blocks, then count
* number of character devices, number of line disciplines and
* number of terminal subdisciplines. (Once-only routine)
*/
cinit()
{
register int ccp;
register struct cblock *cp;
register struct cdevsw *cdp;
struct linesw *lsp;
ccp = cfree;
for (cp=(ccp+07)&~07; cp <= &cfree[NCLIST-1]; cp++) {
cp->c_next = cfreelist;
cfreelist = cp;
}
ccp = 0;
for(cdp = cdevsw; cdp->d_open; cdp++)
ccp++;
nchrdev = ccp;
ccp = 0;
for(lsp = linesw; lsp->l_open; lsp++)
ccp++;
nltype = ccp;
}
/*
* flush all TTY queues
*/
flushtty(atp)
struct tty *atp;
{
register struct tty *tp;
register int sps;
tp = atp;
while (getc(&tp->t_canq) >= 0);
while (getc(&tp->t_outq) >= 0);
wakeup(&tp->t_rawq);
wakeup(&tp->t_outq);
sps = PS->integ;
spl5();
while (getc(&tp->t_rawq) >= 0);
tp->t_delct = 0;
PS->integ = sps;
}
/*
* transfer raw input list to canonical list,
* doing erase-kill processing and handling escapes.
* It waits until a full line has been typed in cooked mode,
* or until any character has been typed in raw mode.
*/
canon(atp)
struct tty *atp;
{
register char *bp;
char *bp1;
register struct tty *tp;
register int c;
int mc;
tp = atp;
spl5();
while ((tp->t_flags&RAW)==0 && tp->t_delct==0
|| (tp->t_flags&RAW)!=0 && tp->t_rawq.c_cc==0) {
if ((tp->t_state&CARR_ON)==0) {
spl0();
return(0);
}
sleep(&tp->t_rawq, TTIPRI);
}
spl0();
loop:
bp = &canonb[2];
while ((c=getc(&tp->t_rawq)) >= 0) {
if ((tp->t_flags&RAW)==0) {
if (c==0377) {
tp->t_delct--;
break;
}
if (bp[-1]!='\\') {
if (c==tp->t_erase) {
if (bp > &canonb[2])
bp--;
continue;
}
if (c==tp->t_kill)
goto loop;
if (c==CEOT)
continue;
} else {
mc = maptab[c];
if (c==tp->t_erase || c==tp->t_kill)
mc = c;
if (mc && (mc==c || (tp->t_flags&LCASE))) {
if (bp[-2] != '\\')
c = mc;
bp--;
}
}
}
*bp++ = c;
if (bp>=canonb+CANBSIZ)
break;
}
bp1 = bp;
bp = &canonb[2];
c = &tp->t_canq;
while (bp<bp1)
putc(*bp++, c);
return(1);
}
/*
* Place a character on raw TTY input queue, putting in delimiters
* and waking up top half as needed.
* Also echo if required.
* The arguments are the character and the appropriate
* tty structure.
*/
ttyinput(ac, atp)
struct tty *atp;
{
register int t_flags, c;
register struct tty *tp;
tk_nin =+ 1;
tp = atp;
c = ac;
t_flags = tp->t_flags;
if((tp->t_state&ISOPEN)==0 || (c & OVERRUN)) {
wakeup(&tp->t_rawq);
return;
}
if (c&FRERROR) /* break */
if (t_flags&RAW)
c = 0; /* null (for getty) */
else
c = 0177; /* DEL (intr) */
if (c&PERROR)
if ((t_flags&(EVENP|ODDP))==EVENP
|| (t_flags&(EVENP|ODDP))==ODDP )
return;
if((c =& 0177) == '\r' && t_flags & CRMOD)
c = '\n';
if((t_flags&RAW)==0) {
if(c==CQUIT || c==CINTR) {
signal(tp->t_pgrp, c==CINTR? SIGINT:SIGQIT);
flushtty(tp);
return;
} else
if(c==CXSTOP) {
if(tp->t_state&XMTSTOP) {
tp->t_state =& ~XMTSTOP;
ttstart(tp);
} else
tp->t_state =| XMTSTOP;
return;
}
}
if (tp->t_rawq.c_cc>TTYHOG) {
flushtty(tp);
return;
}
if (t_flags&LCASE && c>='A' && c<='Z')
c =+ 'a'-'A';
putc(c, &tp->t_rawq);
if (t_flags&RAW || (c=='\n' || c==CEOT)) {
wakeup(&tp->t_rawq);
if ((t_flags&RAW)==0 && putc(0377, &tp->t_rawq)==0)
tp->t_delct++;
}
if (t_flags&ECHO) {
ttyoutput(c, tp);
tp->t_state =& ~XMTSTOP;
if (c==tp->t_kill && (t_flags&RAW)==0)
ttyoutput('\n', tp);
ttstart(tp);
}
}
/*
* put character on TTY output queue, adding delays,
* expanding tabs, and handling the CR/NL bit.
* It is called both from the top half for output, and from
* interrupt level for echoing.
* The arguments are the character and the tty structure.
*/
ttyoutput(ac, tp)
struct tty *tp;
{
register int c;
register struct tty *rtp;
register char *colp;
int ctype;
tk_nout =+ 1;
rtp = tp;
c = ac & 0177;
/*
* Ignore EOT in normal mode to avoid hanging up
* certain terminals.
* In raw mode dump the char unchanged.
*/
if(c == CEOT && (rtp->t_flags&RAW) == 0)
return;
/*
* Turn tabs to spaces as required
*/
if (c=='\t' && rtp->t_flags&XTABS) {
do
ttyoutput(' ', rtp);
while (rtp->t_col&07);
return;
}
/*
* for upper-case-only terminals,
* generate escapes.
*/
if (rtp->t_flags&LCASE) {
colp = "({)}!|^~'`";
while(*colp++)
if(c == *colp++) {
ttyoutput('\\', rtp);
c = colp[-2];
break;
}
if ('a'<=c && c<='z')
c =+ 'A' - 'a';
}
/*
* turn <nl> to <cr><lf> if desired.
*/
if (c=='\n' && rtp->t_flags&CRMOD)
ttyoutput('\r', rtp);
putc(c, &rtp->t_outq);
/*
* Calculate delays.
* The numbers here represent clock ticks
* and are not necessarily optimal for all terminals.
* The delays are indicated by characters above 0200.
* In raw mode there are no delays and the
* transmission path is 8 bits wide.
*/
colp = &rtp->t_col;
ctype = partab[c];
c = 0;
switch (ctype&077) {
/* ordinary */
case 0:
(*colp)++;
/* non-printing */
case 1:
break;
/* backspace */
case 2:
if (*colp)
(*colp)--;
break;
/* newline */
case 3:
ctype = (rtp->t_flags >> 8) & 03;
if(ctype == 1) { /* tty 37 */
if (*colp)
c = max((*colp>>4) + 3, 6);
} else
if(ctype == 2) {
c = 2;
} else
if(ctype == 3) { /* TTY 40 printer */
if(*colp <= 34) {
if(*colp)
c = (34 - *colp)>>1;
else
c = 2;
}
}
*colp = 0;
break;
/* tab */
case 4:
ctype = (rtp->t_flags >> 10) & 03;
if(ctype == 1) { /* tty 37 */
c = 1 - (*colp | ~07);
if(c < 5)
c = 0;
}
*colp =| 07;
(*colp)++;
break;
/* vertical motion */
case 5:
if(rtp->t_flags & VTDELAY) /* tty 37 */
c = 0177;
break;
/* carriage return */
case 6:
ctype = (rtp->t_flags >> 12) & 03;
if(ctype == 1) { /* tn 300 */
c = 4;
} else
if(ctype == 2) {
c = 4;
} else
if(ctype == 3) {
c = 10;
}
*colp = 0;
}
if(c)
putc(c|0200, &rtp->t_outq);
}
/*
* Restart typewriter output following a delay
* timeout.
* The name of the routine is passed to the timeout
* subroutine and it is called during a clock interrupt.
*/
ttrstrt(atp)
{
register struct tty *tp;
tp = atp;
tp->t_state =& ~TIMEOUT;
ttstart(tp);
}
/*
* Start output on the typewriter. It is used from the top half
* after some characters have been put on the output queue,
* from the interrupt routine to transmit the next
* character, and after a timeout has finished.
* If the SSTART bit is off for the tty the work is done here,
* using the protocol of the single-line interfaces (KL, DL, DC);
* otherwise the address word of the tty structure is
* taken to be the name of the device-dependent startup routine.
*/
ttstart(atp)
struct tty *atp;
{
register int *addr, c;
int sps;
register struct tty *tp;
struct { int (*func)(); };
tp = atp;
addr = tp->t_addr;
if (tp->t_state&SSTART) {
(*addr.func)(tp);
return;
}
sps = PS->integ;
spl5();
if ((addr->tttcsr&DONE)==0 || tp->t_state&TIMEOUT)
goto out;
if((c = (*linesw[tp->t_discp].l_xmtd)(tp))< 0)
addr->tttbuf = c;
else if (c&CTOUT) {
timeout(ttrstrt, tp, (c&0177) + DLDELAY);
tp->t_state =| TIMEOUT;
}
out:
PS->integ = sps;
}
/*
* Get next character for transmission to a tty.
* Accessed via l_xmtd entry in linesw table in conf.c
*/
ttxmtd(atp)
struct tty *atp;
{
register struct tty *tp;
register c;
tp = atp;
if(tp->t_state&XMTSTOP || (c = getc(&tp->t_outq)) < 0)
return(0);
if (c<=0177)
c =| CPRES|(partab[c]&0200);
else
c =| CTOUT;
/*
* If the writer was sleeping on output overflow,
* wake him when low tide is reached.
*/
if (tp->t_outq.c_cc<=TTLOWAT && tp->t_state&ASLEEP) {
tp->t_state =& ~ASLEEP;
wakeup(&tp->t_outq);
}
return(c);
}
/*
* Called from device's read routine after it has
* calculated the tty-structure given as argument.
* The pc is backed up for the duration of this call.
* In case of a caught interrupt, an RTI will re-execute.
*/
ttread(atp)
struct tty *atp;
{
register struct tty *tp;
tp = atp;
if ((tp->t_state&CARR_ON)==0)
return;
if (tp->t_canq.c_cc || canon(tp))
while (tp->t_canq.c_cc && passc(getc(&tp->t_canq))>=0);
}
/*
* Called from the device's write routine after it has
* calculated the tty-structure given as argument.
*/
ttwrite(atp)
struct tty *atp;
{
register struct tty *tp;
register int c;
tp = atp;
if ((tp->t_state&CARR_ON)==0)
return;
while ((c=cpass())>=0) {
spl5();
while (tp->t_outq.c_cc > TTHIWAT) {
ttstart(tp);
tp->t_state =| ASLEEP;
sleep(&tp->t_outq, TTOPRI);
}
spl0();
ttyoutput(c, tp);
}
ttstart(tp);
}
/*
* Common code for tty line disciplines
*/
ttstty(dev, atp, cmd)
struct tty *atp;
{
register struct tty *tp;
register int *v;
int word1;
tp = atp;
v = u.u_arg[1];
switch(cmd) {
case UNSET:
ttclose(dev, tp);
break;
case SET:
ttopen(dev, tp);
case CHNGD:
break;
case STTY:
wflushtty(tp);
case STTYNF:
tp->t_speeds = fuword(v++);
tp->t_flags = (fuword(++v))&077777;
if (tp->t_speeds.lobyte == 0) {
tp->t_flags =| HUPCL;
(*ctlsw[tp->t_dtype].d_mctl)(dev, HUP);
break;
}
(*ctlsw[tp->t_dtype].d_param)(dev, tp);
break;
case GTTY:
suword(v++, tp->t_speeds);
suword(++v, tp->t_flags);
break;
/*
* Prevent opens on channel.
*/
case XCLD:
tp->t_state =| XCLUDE;
break;
case FEK:
word1.lobyte = tp->t_erase;
word1.hibyte = tp->t_kill;
suword(++v, word1);
break;
case SEK:
word1 = fuword(++v);
tp->t_erase = word1.lobyte;
tp->t_kill = word1.hibyte;
break;
default:
u.u_error = EBIOCTL;
}
}
/*
* Modem transition handling. If carrier is
* off and open is not in progress, hang up the
* local dataset and signal a hangup.
*/
ttmt(atp, status)
struct tty *atp;
{
register struct tty *tp;
register action;
tp = atp;
action = 0;
wakeup(&tp->t_rawq);
if ((status&CARRIER)==0) {
if ((tp->t_state&WOPEN)==0) {
signal(tp->t_pgrp, SIGHUP);
action = DISABLE;
flushtty(tp);
}
tp->t_state =& ~CARR_ON;
} else
tp->t_state =| CARR_ON;
return(action);
}