# /* */ /* * 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); }