/* * Synchronous Data Set Adapter driver * * - uses Autodriver Channel for input & output * * - for QSA (Quad Synchronous Adapter) #define QSA * */ #include "../h/local.h" #ifdef SCCS_ID static char SCCS_ID [] = "@(#)dsa.c 4.2 13:48:38 - 82/01/16 "; #endif SCCS_ID #include "../h/param.h" #include "../h/conf.h" #include "../h/tty.h" #include "../h/dir.h" #include "../h/user.h" #include "../h/proc.h" #include "../h/ccb.h" #define QSA #define ISIZE 256 #define OSIZE 256 extern int ndsa; /* number of DSA'S */ extern char dsaaddr[]; /* DSA addresses */ extern struct tty dsa[]; /* common tty structure */ #define q1 tp->t_rawq #define q2 tp->t_outq extern struct ccb *dsaccb; /* channel control blocks */ extern short isp[]; /* interrupt service ptr table */ char dsaibuf[ISIZE]; char dsaobuf[OSIZE]; int dsastin(), dsastout(); /* * DSA commands & status bits */ #define DIS 0xc0 #define ENB 0x40 #define PAR 0x20 #define SYNSCH 0x10 #define RSDTA 0x10 #define DTR 0x08 #define WR 0x02 #define RQ2S 0x02 #define RD 0x01 #define CMD 0x01 #define BITS7 0x20 #define BITS8 0x30 #define PODD 0x04 #define PEVEN 0x06 #define OV 0x80 #define PARF 0x40 #define SYNC 0x20 #define BSY 0x08 #define EX 0x04 #define CARR_OFF 0x02 #define NOT_CL2S 0x02 #define DU 0x01 #define RDERR (OV|PARF|CARR_OFF|DU) #define WRERR (OV|BSY|NOT_CL2S|DU) #define SYN 0x16 #define ETX 0x03 #define PAD 0xff /* * open routine: * called each time a process opens a DSA as a character file * * - set up the initial status * and arm interrupts */ dsaopen(dev) { register struct tty *tp; register struct ccb *ccb; if (minor(dev) >= ndsa) { u.u_error = ENXIO; return; } tp = &dsa[minor(dev)]; if (tp->t_state & ISOPEN) { u.u_error = ENXIO; return; } tp->t_state = ISOPEN; tp->t_dev = dev; tp->t_flags = RAW; tp->t_oproc = dsastout; tp->t_iproc = dsastin; q1.c_cf = q1.c_cl = dsaibuf; q1.c_cc = - (ISIZE); ccb = &dsaccb[minor(dev)<<1]; isp[dsaaddr[minor(dev)]] = (short)ccb + 1; isp[dsaaddr[minor(dev)] + 1] = (short)(ccb + 1) + 1; tp->t_addr = (caddr_t)ccb; dsaenab(tp); spl4(); while (!(tp->t_state&CARR_ON)) sleep((caddr_t)&q1, TTIPRI); spl0(); (*linesw[tp->t_line].l_open)(dev, tp); } /* * close routine: */ dsaclose(dev) { register struct tty *tp; tp = &dsa[minor(dev)]; (*linesw[tp->t_line].l_close)(tp); ttyclose(tp); dsadisab(dev); } /* * read, write, ioctl routines: */ dsaread(dev) { register struct tty *tp; register c; tp = &dsa[minor(dev)]; if (tp->t_line) { (*linesw[tp->t_line].l_read)(tp); return; } spl4(); while (q1.c_cl == dsaibuf) { dsastin(tp); sleep((caddr_t)&q1, TTIPRI); } spl0(); while (q1.c_cf < q1.c_cl) if (passc(*q1.c_cf++) < 0) return; q1.c_cf = q1.c_cl = dsaibuf; } dsawrite(dev) { register struct tty *tp; register char *p; register c; static char syns[] = SYN, SYN, SYN, SYN, 0; static char pads[] = PAD, PAD, 0; tp = &dsa[minor(dev)]; if (tp->t_line) { (*linesw[tp->t_line].l_write)(tp); return; } if ((tp->t_state & CARR_ON) == 0) return(0); spl4(); while (q2.c_cc) { tp->t_state |= ASLEEP; sleep((caddr_t)&q2, TTOPRI); } spl0(); q2.c_cf = q2.c_cl = dsaobuf; for (p = syns; (c = *p); p++) *q2.c_cl++ = c; while (q2.c_cl < dsaobuf + OSIZE - sizeof pads) { if ((c = cpass()) < 0) break; *q2.c_cl++ = c; } for (p = pads; (c = *p); p++) *q2.c_cl++ = c; q2.c_cc = q2.c_cf - q2.c_cl; spl4(); dsastout(tp); spl0(); } dsaioctl(dev, cmd, addr, flag) caddr_t addr; { register struct tty *tp; tp = &dsa[minor(dev)]; if (ttioccom(cmd, tp, addr, dev) == 0) u.u_error = ENOTTY; } /* * start output to DSA */ dsastout(tp) register struct tty *tp; { register c, waddr; register struct ccb *ccb; register char *s; waddr = dsaaddr[minor(tp->t_dev)] + 1; trace(8<<16, "dsastart", waddr); if ((tp->t_state&(BUSY|CARR_ON)) != CARR_ON || q2.c_cc == 0) return; tp->t_state |= BUSY; ccb = (struct ccb *)tp->t_addr + 1; if (q2.c_cc > 0) { s = dsaobuf; s[0] = SYN; s[1] = SYN; s[2] = SYN; s[3] = SYN; s += 4; s += q_to_b(&q2, s, OSIZE - 6); s[0] = PAD; s[1] = PAD; s += 1; ccb->cc_buf0 = s; ccb->cc_cnt0 = dsaobuf - s; } else { ccb->cc_buf0 = q2.c_cl - 1; ccb->cc_cnt0 = q2.c_cf - q2.c_cl + 1; q2.c_cc = 0; } ccb->cc_ccw = (WRERR<<8) | CCBEXECUTE | CCBWRITE | CCBFAST; #ifdef QSA oc(waddr, ENB | RSDTA | DTR | RQ2S | CMD); if ((ss(waddr)&BSY) == 0) wd(waddr, PAD); #else wd(waddr, -1); oc(waddr, ENB | PAR | DTR | WR); #endif trace(8<<16, "wrt", ss(waddr)); if ((tp->t_state&ASLEEP) && q2.c_cc == 0) wakeup((caddr_t)&q2); } dsastop(tp) register struct tty *tp; { register struct ccb *ccb; register n; trace(8<<16, "dsstop", tp->t_dev); ccb = (struct ccb *)tp->t_addr + 1; ccb->cc_cnt1 = 0; ccb->cc_ccw = 0; } /* * start input from DSA */ dsastin(tp) register struct tty *tp; { register struct ccb *ccb; register int raddr; raddr = dsaaddr[minor(tp->t_dev)]; ccb = (struct ccb *)tp->t_addr; ccb->cc_buf0 = q1.c_cf + (-q1.c_cc) - 1; ccb->cc_cnt0 = q1.c_cc + 1; ccb->cc_ccw = (RDERR<<8) | CCBEXECUTE | CCBREAD | CCBFAST; #ifdef QSA oc(raddr, ENB | SYNSCH | DTR | CMD); #else oc(raddr, ENB | PAR | SYNSCH | DTR); #endif rd(raddr); } dsarint(dev, stat) { register int raddr; register struct tty *tp; register struct ccb *ccb; register char c; tp = &dsa[dev]; raddr = dsaaddr[dev]; if (!(tp->t_state & ISOPEN)) { dsadisab(dev); return; } trace(8<<16, "rint", raddr); trace(8<<16, "stat", stat); /* * Loss of DSR - hangup */ if (stat & DU) { if (tp->t_state & CARR_ON) { signal(tp->t_pgrp, SIGHUP); flushtty(tp); } tp->t_state &= ~CARR_ON; return; } if (!(tp->t_state & CARR_ON)) { tp->t_state |= CARR_ON; wakeup((caddr_t)&q1); } /* * overrun - throw away block and resynchronise */ if (stat & OV) { dsastin(tp); return; } /* * if BSY still set - no char to read */ if ((stat & (BSY|CARR_OFF)) == BSY) return; /* * buffer full or carrier off -- give data to reader */ #ifdef QSA oc(raddr, DIS | DTR | CMD); #else oc(raddr, DIS | DTR | RD); #endif ccb = (struct ccb *)tp->t_addr; q1.c_cl = ccb->cc_buf0 + ccb->cc_cnt0; if (tp->t_line) (*linesw[tp->t_line].l_rend)(tp, q1.c_cf, q1.c_cl); else wakeup((caddr_t)&q1); } dsaxint(dev, stat) { register int waddr; register struct tty *tp; register c; tp = &dsa[dev]; waddr = dsaaddr[dev] + 1; if (!(tp->t_state & ISOPEN)) { dsadisab(dev); return; } trace(8<<16, "wint", waddr); trace(8<<16, "stat", stat); if (stat&BSY) return; tp->t_state &= ~BUSY; #ifdef QSA oc(waddr, DIS | RSDTA | DTR | CMD); #else oc(waddr, DIS | DTR | WR); #endif (*linesw[tp->t_line].l_start)(tp); } /* * Arm interrupts from DSA and set reading */ dsaenab(atp) struct tp *atp; { register struct tp *tp; register radd; register stat; tp = atp; radd = dsaaddr[minor(tp->t_dev)]; #ifdef QSA oc(radd, BITS7 | PODD); oc(radd+1, BITS7 | PODD); wd(radd, SYN); oc(radd, ENB | SYNSCH | DTR | CMD); #else oc(radd, ENB | PAR | SYNSCH | DTR); #endif stat = ss(radd); if ((stat&DU) == 0) tp->t_state |= CARR_ON; trace(8<<16, "dsaenab", stat); rd(radd); } /* * Disarm interrupts from DSA */ dsadisab(dev) { register radd, wadd; radd = dsaaddr[minor(dev)]; wadd = radd + 1; #ifdef QSA oc(radd, DIS | CMD); oc(wadd, DIS | CMD); #else oc(radd, DIS); oc(wadd, DIS); #endif trace(8<<16, "dsadisab", ss(radd)); }