USG_PG3/usr/source/io1/hd.c
#
/*
* Copyright 1973 Bell Telephone Laboratories Inc
*/
#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"
#define DISCON 300
#define DLAY 10
#define DLAY1 30
#define M40STP 90
#define M40GO 30
#define QESC 037
#define QBRK 0
#define HSPEED 9
/* half-duplex state bits: t_lword */
#define LOCAL 01
#define PEVENT 02
#define CURSET 04
#define QDELIM 0200
#define WRTON 0400
#define CRLCHA 01000
#define TIMER1 02000
#define TIMER2 04000
#define EXTND 010000
#define ESCHA 020000
#define SNDRFLG 040000
#define SOLICIT 0100000
/* Indicates state of comm. channel: t_linest*/
#define QUIESNT 000
#define REC 001
#define INTRPT1 002
#define SEND 003
#define ERROR 004
#define RECHLD 005
#define SENDREQ 006
#define SENDSTP 007
#define INACT 010
#define INTRPT2 011
#define SENDHLD 012
#define RETURN 013
/*
* Matrix defines new state given current state and state of CD,CTS, and SR.
*/
char action[11][6]{
/* 0,0,0 0,0,1 0,1,0 0,1,1 1,0,0 1,0,1 (CD,CTS,SR) */
INTRPT2,RETURN ,ERROR ,ERROR ,REC ,INTRPT1, /*QUIESNT*/
INTRPT1,QUIESNT,ERROR ,ERROR ,RETURN ,INTRPT1, /*REC*/
RETURN ,QUIESNT,ERROR ,ERROR ,REC ,RETURN , /*INTPRT1*/
ERROR ,ERROR ,SENDHLD,RETURN ,ERROR ,ERROR , /*SEND*/
ERROR ,ERROR ,ERROR ,ERROR ,ERROR ,ERROR , /*ERROR*/
INTRPT1,QUIESNT,ERROR ,ERROR ,RETURN ,INTRPT1, /*RECHLD*/
INTRPT2,RETURN ,INTRPT2,SEND ,REC ,INTRPT1, /*SENDREQ*/
INTRPT2,QUIESNT,INTRPT2,RETURN ,REC ,INTRPT1, /*SENDSTP*/
RETURN ,QUIESNT,ERROR ,ERROR ,REC ,INTRPT1, /*INACT*/
RETURN ,QUIESNT,RETURN ,RETURN ,REC ,INTRPT1, /*INTRPT2*/
ERROR ,ERROR ,RETURN ,RETURN ,ERROR ,ERROR , /*SENDHLD*/
};
/*
* Routine to open a half-duplex line. Initialize and wait
* for carrier to come on. Establish a process
* group for the distribution of quits and interrupts
* from the terminal.
*/
hdopen(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_linest = INACT;
tp->t_lword = QDELIM;
tp->t_erase = CERASE;
tp->t_kill = CKILL;
tp->t_speeds = HSPEED | (HSPEED<<8);
tp->t_flags = HDUP|EVENP;
(*ctlsw[tp->t_dtype].d_param)(dev, tp);
} else if (tp->t_state&XCLUDE && u.u_uid!=0) {
u.u_error = EBUSY;
return;
}
hdmopen(dev, tp);
pp = u.u_procp;
if(pp->p_pgrp == 0) {
if(tp->t_pgrp)
pp->p_pgrp = tp->t_pgrp;
else {
pp->p_pgrp = pp->p_pid;
tp->t_pgrp = pp->p_pid;
}
u.u_ttyp = tp;
u.u_ttyd = dev;
}
tp->t_state =& ~WOPEN;
tp->t_state =| ISOPEN;
}
/*
* Modem open for half-duplex lines. Set up modem to answer phone.
* Test state of CD,CTS, and SR and set protocol state accordingly.
* Sleep until CD or SR goes high.
*/
hdmopen(dev, atp)
struct tty *atp;
{
register struct tty *tp;
register dtype;
tp = atp;
dtype = tp->t_dtype;
if(tp->t_linest == INACT)
(*ctlsw[dtype].d_mctl)(dev, TURNON);
spl5();
hdmt(tp,(*ctlsw[dtype].d_mctl)(dev,FSTATUS));
while((tp->t_state&CARR_ON)==0)
sleep(&tp->t_rawq, TTIPRI);
spl0();
}
/*
* Close a tty line.
*/
hdclose(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;
wflushtty(tp);
}
}
/*
* Handles interrupts caused by transitions of CD,CTS, and SR. The
* current protocol state and the new state of CD,CTS and SR determine
* the new protocol state and thus the new state of RQS and ST.
*/
hdmt(atp,status)
{
register struct tty *tp;
int act;
int intbits;
int modstat;
extern timer();
extern sendstp();
extern sendhld();
tp = atp;
intbits = status;
modstat = 0;
/*
* Determine new protocol state.
*/
if(intbits > 5)
act = ERROR;
else
act = action[tp->t_linest][intbits];
/*
* Set new protocol state.
*/
switch(act){
case QUIESNT :
modstat = TURNON;
if(tp->t_linest == INACT) {
tp->t_state =| CARR_ON;
wakeup(&tp->t_rawq);
}
if(tp->t_linest == INTRPT1)
if((tp->t_lword&QDELIM) == 0 && (tp->t_flags&RAW) == 0)
ttyinput('\n',tp);
if(tp->t_linest == INTRPT2){
tp->t_lword =| WRTON;
tp->t_state =& ~XMTSTOP;
ttstart(tp);
modstat = 0;
}
tp->t_linest = QUIESNT;
if(tp->t_lword&SOLICIT){
solicit(tp);
modstat = 0;
}
if(tp->t_lword&SNDRFLG){
hdmset(tp,SENDREQ);
modstat = 0;
}
break;
case REC :
tp->t_lword =| SOLICIT;
modstat = TURNON|ST;
tp->t_lword =& ~CURSET;
if(tp->t_linest == INACT) {
tp->t_state =| CARR_ON;
wakeup(&tp->t_rawq);
}
tp->t_linest = REC;
break;
case INTRPT1 :
tp->t_linest = INTRPT1 ;
modstat = TURNON;
if(tp->t_lword&LOCAL)
break;
if(tp->t_lword&TIMER2){
tp->t_lword =| EXTND;
}
else{
tp->t_lword =| TIMER2;
timeout(timer,tp,DISCON);
}
break;
case SEND :
tp->t_linest = SEND;
ttstart(tp);
break;
case SENDHLD :
tp->t_state =| XMTSTOP;
timeout(sendhld,tp,DLAY1);
modstat = 0;
tp->t_linest = SENDHLD;
tp->t_lword =& ~(SNDRFLG|SOLICIT|WRTON);
break;
case INTRPT2 :
tp->t_linest = INTRPT2 ;
modstat = TURNON;
if(tp->t_lword&LOCAL)
break;
if(tp->t_lword&TIMER2){
tp->t_lword =| EXTND;
}
else{
tp->t_lword =| TIMER2;
timeout(timer,tp,DISCON);
}
break;
case ERROR :
tp->t_linest = ERROR;
/*printf("er %o %o %o\n",tp->t_linest,intbits,modstat)*/
hdmset(tp,INACT);
}
return(modstat);
}
/*
* Set state of modem interface.
*/
hdmset(atp,state)
{
register struct tty *tp ;
register int st ;
int sps,modstat;
sps = PS->integ;
spl5();
tp = atp;
st = state;
switch(st){
case REC :
tp->t_linest = REC;
modstat = TURNON|ST;
break;
case RECHLD :
modstat = TURNON;
tp->t_linest = RECHLD;
break;
case SENDREQ :
tp->t_lword =& ~SNDRFLG;
modstat = TURNON|RQS;
tp->t_linest = SENDREQ;
break;
case SENDSTP :
tp->t_linest = SENDSTP;
modstat = TURNON;
break;
case INACT :
signal(tp->t_pgrp,SIGHUP);
flushtty(tp);
modstat = HUP;
tp->t_linest = INACT;
tp->t_state =& ~CARR_ON;
break;
}
(*ctlsw[tp->t_dtype].d_mctl)(tp->t_dev,modstat);
PS->integ = sps;
}
/*
* Position cursur to a new line; get characters from user and call hdoutput.
*/
hdwrite(atp)
struct tty *atp;
{
register struct tty *tp;
register int c;
tp = atp;
if(tp->t_lword&SOLICIT)
solicit(tp);
if(tp->t_linest != SENDHLD)
tp->t_lword =| WRTON;
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();
hdoutput(c, tp);
}
ttstart(tp);
tp->t_lword =& ~CURSET;
}
/*
* Call ttyoutput; provide delays for TTY Model 40.
*/
hdoutput(ac, tp)
struct tty *tp;
{
register int c;
register struct tty *rtp;
register char *colp;
register int dlay;
rtp = tp;
colp = &rtp->t_col;
c = ac&0177;
dlay = 0;
ttyoutput(c,rtp);
if(rtp->t_lword&ESCHA){
switch(c) {
case '2':
dlay = 10;
break;
case 'J':
dlay = 14;
break;
case 'R':
dlay = 7;
*colp = 0;
break;
case '\010':
(*colp)--;
break;
case 'C':
(*colp)++;
break;
case '@':
*colp =| 07;
(*colp)++;
break;
case 'L': case 'M':
dlay = 20;
break;
case 'G': case 'H':
*colp = 0;
break;
}
rtp->t_lword =& ~ESCHA;
}
if(c == '\033')
rtp->t_lword =| ESCHA;
if(dlay)
putc(dlay|0200,&rtp->t_outq);
}
/*
* Position cursur to a new line if no characters are availble. Move
* characters from rawq to canq to user to stasfy read. Account for QESC
* as result of RECHLD state.
*/
hdread(atp)
struct tty *atp;
{
struct tty *tp;
register int c,c1;
int n;
tp = atp;
spl5();
if(tp->t_delct == 0 && tp->t_rawq.c_cc == 0 && tp->t_canq.c_cc == 0)
if((tp->t_lword&CURSET) == 0){
if(tp->t_linest == QUIESNT)
solicit(tp);
else
tp->t_lword =| SOLICIT;
}
spl0();
while(tp->t_canq.c_cc || hdcanon(tp)){
c1 = -1;
n = 0;
if((c = getc(&tp->t_canq)) == QESC){
if((c1 = getc(&tp->t_canq)) == QESC)
n = passc(c1);
}else{
n = passc(c);
}
if(n<0 || (tp->t_canq.c_cc == 0 && c1 != QBRK ))
return;
}
}
/*
* Put characters on rawq. Wakeup on additional message ending characters.
* Set queue delimiter (QDELIM) flag. Set RECHLD state if rawq gets full.
*/
hdrcvd(ac,atp)
struct tty *atp;
{
register int t_flags,c;
register struct tty *tp;
tp = atp;
c = ac;
t_flags = tp->t_flags;
if((c&0177)=='\r')
c = (c&0177600)|'\n';
ttyinput(c,tp);
if((c =& 0177) == QESC)
ttyinput(QESC,tp);
if(tp->t_state&XMTSTOP){
tp->t_state =& ~XMTSTOP;
tp->t_lword =| WRTON;
ttstart;
}
if(c==035 || c==024){
wakeup(&tp->t_rawq);
if((t_flags&RAW)==0 && putc(0377,&tp->t_rawq)==0)
tp->t_delct++;
}
if((t_flags&RAW) || (c=='\n' || c==CEOT || c==035 || c==024))
tp->t_lword =| QDELIM;
else
tp->t_lword =& ~QDELIM;
if(tp->t_rawq.c_cc >= M40STP && tp->t_linest == REC){
hdmset(tp,RECHLD);
putc(QESC,&tp->t_rawq);
putc(QBRK,&tp->t_rawq);
if(putc(0377,&tp->t_rawq) == 0)
tp->t_delct++;
wakeup(&tp->t_rawq);
}
}
/*
* Sets the SENDREQ state when there are characters to pass to the calling
* routine. Calls timeout with sendstp when the output queue count is zero.
* Returns a character when in the SEND state and characters are available.
*/
hdxmtd(atp)
struct tty *atp;
{
register struct tty *tp;
register c;
extern sendstp();
tp = atp;
c = 0;
if(tp->t_state&XMTSTOP)
return(0);
if(tp->t_outq.c_cc)
if(tp->t_linest == SEND){
c = getc(&tp->t_outq);
if(c<=0177)
c =| CPRES|(partab[c]&0200);
else
c =| CTOUT;
}
else
if(tp->t_linest == QUIESNT)
hdmset(tp,SENDREQ);
else
tp->t_lword =| SNDRFLG;
else
if(tp->t_linest == SEND)
tp->t_lword =& ~WRTON;
if((tp->t_lword&TIMER1) == 0){
tp->t_lword =| TIMER1;
timeout(sendstp,tp,DLAY);
}
if(tp->t_outq.c_cc <= TTLOWAT && tp->t_state&ASLEEP){
tp->t_state =& ~ASLEEP;
wakeup(&tp->t_outq);
}
return(c);
}
/*
* Common code for tty line disciplines
*/
hdstty(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:
hdclose(dev, tp);
break;
case SET:
hdopen(dev, tp);
break;
case CHNGD:
case STTY:
case STTYNF:
case GTTY:
/*
* Prevent opens on channel.
*/
case XCLD:
case FEK:
case SEK:
ttstty(dev,atp,cmd);
break;
default:
u.u_error = EBIOCTL;
}
}
/*
* Call canon. Allow input when queue reaches low point.
*/
hdcanon(atp)
struct tty *atp;
{
register struct tty *tp;
int n;
tp = atp;
n = canon(tp);
spl5();
if(tp->t_linest == RECHLD && tp->t_rawq.c_cc < M40GO)
hdmset(tp,REC);
spl0();
if(tp->t_canq.c_cc)
return(n);
else
return(0);
}
/*
* Put bell, backspace, and newline on outq.
*/
solicit(atp)
struct tty *atp;
{
register struct tty *tp;
tp = atp;
tp->t_lword =| CURSET;
tp->t_lword =& ~SOLICIT;
tp->t_lword =| WRTON;
ttyoutput('\007',tp);
ttyoutput('\010',tp);
ttyoutput('\n',tp);
ttstart(tp);
}
/*
*Set SENDSTP after timeout.
*/
sendstp(atp)
{
register struct tty *tp;
tp=atp;
if((tp->t_state&(TIMEOUT|BUSY)) == 0 && (tp->t_lword&WRTON) == 0
&& (tp->t_linest == SEND))
if(tp->t_lword&SOLICIT)
solicit(tp);
else
hdmset(tp,SENDSTP);
tp->t_lword =& ~TIMER1;
}
/*
* Unconditionally set SENDSTP state.
*/
sendhld(atp)
{
register struct tty *tp;
tp = atp;
if(tp->t_linest == SENDHLD)
hdmset(tp,SENDSTP);
}
/*
* Set INACT if no carrier for timeout period.
*/
timer(atp)
{
register struct tty *tp;
tp=atp;
if((tp->t_linest != INTRPT1) && (tp->t_linest != INTRPT2)){
tp->t_lword =& ~(EXTND|TIMER2);
return;
}
if(tp->t_lword&EXTND){
timeout(timer,tp,DISCON);
tp->t_lword =& ~EXTND;
}
else{
hdmset(tp,INACT);
tp->t_lword =& ~TIMER2;
}
}