MiniUnix/usr/sys/dev/dp.c
#
/*
*/
/*
* DP-11 Synchronous interface driver
* This driver is rather insensitive to the remote
* device it talks to, which is to say most of the protocol
* must be supplied by the calling program.
* Exceptions: parity is even; 7 data bits per character;
* max. of 512 characters per record; 10 second timeout
* on waiting to receive; half-duplex transmission.
*/
#include "../param.h"
#include "../conf.h"
#include "../user.h"
#include "../buf.h"
/* control info */
struct {
char *dp_buf;
char *dp_bufp;
int dp_nxmit;
char dp_state;
char dp_timer;
int dp_proc;
} dp11;
/* device registers */
struct {
int dprcsr;
char dprbuf;
char dpsyn0;
int dptcsr;
char dptbuf;
char dpsyn1;
};
/* bits */
#define ODDPAR 010000
#define IENABLE 0100
#define HDUPLX 02
#define CTRANS 0100000
#define RORUN 040000
#define RING 020000
#define DSRDY 010000
#define CARRIER 04000
#define DONE 0200
#define IENABLE 0100
#define SIENABL 040
#define WRITE 1
#define READ 0
#define DTRDY 01
#define RCVACT 04000
#define DPADDR 0174770
#define DPPRI 5
#define SYN 026 /* (receive) sync character */
/*
* The open fails unless the device is not open or
* the opening process is the one that has it open already.
*/
dpopen(dev, flag)
{
int dptimeout();
if (dp11.dp_proc!=0 && dp11.dp_proc!=u.u_procp) {
u.u_error = ENXIO;
return;
}
dp11.dp_proc = u.u_procp;
dp11.dp_state = READ;
if (dp11.dp_buf==0) {
dp11.dp_buf = getblk(NODEV);
dp11.dp_bufp = dp11.dp_buf->b_addr;
dp11.dp_timer = HZ;
timeout(dptimeout, 0, HZ);
}
DPADDR->dpsyn0 = SYN;
DPADDR->dprcsr = HDUPLX|IENABLE;
DPADDR->dptcsr = IENABLE|SIENABL|DTRDY;
}
dpclose()
{
DPADDR->dprcsr = 0;
DPADDR->dptcsr = 0;
dp11.dp_timer = 0;
dp11.dp_proc = 0;
if (dp11.dp_buf != 0) {
brelse(dp11.dp_buf);
dp11.dp_buf = 0;
}
}
/*
* Read waits until:
* there is loss of "data set ready", or
* a timeout occurs, or
* a full record has been received.
* The former two result in an error.
*/
dpread()
{
register char *bp, **epp;
bp = dp11.dp_buf->b_addr;
epp = &dp11.dp_bufp;
for(;;) {
if(dpwait())
return;
if (*epp > bp)
break;
spl6();
if (dp11.dp_timer <= 1) {
spl0();
return;
}
sleep(&dp11, DPPRI);
spl0();
}
iomove(dp11.dp_buf, 0, min(u.u_count, *epp-bp), B_READ);
}
/*
* write checks to make sure that the data set is not reading,
* and that it is ready. Then the record is copied
* and transmission started.
*/
dpwrite()
{
register char *bp;
if (u.u_count==0 || dpwait())
return;
dp11.dp_state = WRITE;
bp = dp11.dp_buf->b_addr;
dp11.dp_bufp = bp;
if (u.u_count>512)
u.u_count = 512;
dp11.dp_nxmit = u.u_count;
iomove(dp11.dp_buf, 0, u.u_count, B_WRITE);
dpstart();
}
/*
* If "data set ready" is down return an error; otherwise
* wait until the dataset is in read state with no carrier,
* which means a record has just been received.
*/
dpwait()
{
for(;;) {
if ((DPADDR->dptcsr&DSRDY)==0 || dp11.dp_buf==0) {
u.u_error = EIO;
return(1);
}
spl6();
if (dp11.dp_state==READ && (DPADDR->dptcsr&CARRIER)==0) {
spl0();
return(0);
}
sleep(&dp11, DPPRI);
spl0();
}
}
/*
* Start off the next character to be transmitted;
* when the record is done, drop back into read state.
*/
dpstart()
{
register int c;
extern char partab[];
dp11.dp_timer = 10;
if (--dp11.dp_nxmit >= 0) {
c = (*dp11.dp_bufp++) & 0177;
DPADDR->dptbuf = c | ~partab[c]&0200;
} else {
dp11.dp_bufp = dp11.dp_buf->b_addr;
dp11.dp_state = READ;
}
}
/*
* Count down the DP timer (once per second)
* If it runs out, it presumably means the other station
* won't speak.
*/
dptimeout()
{
if (dp11.dp_timer==0)
return;
if (--dp11.dp_timer==0) {
dpturnaround();
dp11.dp_timer = 1;
}
timeout(dptimeout, 0, HZ);
}
/*
* Receiver interrupt: if reading, stash character
* unless there is an overrun.
*/
dprint()
{
register int c;
c = DPADDR->dprbuf & 0177;
if (dp11.dp_state==READ) {
if ((DPADDR->dprcsr&ODDPAR) == 0)
c =| 0200;
if (dp11.dp_bufp < dp11.dp_buf->b_addr+512)
*dp11.dp_bufp++ = c;
}
}
/*
* Transmitter interrupt:
* Knock down hardware bits.
* If carrier has dropped, the record is done, so turn the line around;
* otherwise start another character.
*/
dpxint()
{
register int dpstat;
dpstat = DPADDR->dptcsr;
DPADDR->dptcsr =& ~(CTRANS|RORUN|RING|DONE);
if (dpstat & (CTRANS|RORUN))
dpturnaround();
else if (dpstat&DONE && dp11.dp_state==WRITE)
dpstart();
}
/*
* Change the state from writing to reading at the end of a record.
*/
dpturnaround()
{
DPADDR->dprcsr =& ~RCVACT;
if (dp11.dp_state==WRITE) {
dp11.dp_timer = 10;
dp11.dp_state = READ;
dp11.dp_bufp = dp11.dp_buf->b_addr;
}
wakeup(&dp11);
}