/* * Sin driver -- Simplex Input on PALS * Line Mode Input Driver * - autodriver channel hopefully thru dios * - based on vdu version 1.4 * * (C) 1981, Dan Ladermann & Gary Gorgen * The Wollongong Group Inc. * Palo Alto, California * */ #include "../h/local.h" #ifdef SCCS_ID static char SCCS_ID [] = "@(#)sin.c 1.19 19:59:16 - 81/11/28 "; #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/systm.h" #include "../h/ccb.h" #include "../h/mx.h" #include "../h/buf.h" #ifdef DIOS #include "../h/dios.h" #endif DIOS #ifndef YES #define YES 1 #define NO 0 #endif YES /*#undef trace /* TRACING ON */ #define TRACE1 0x01000000 #define TRACE2 0x02000000 extern int trmask; extern int nsin; /* number of ports */ /* the following arrays have nsin elements */ extern char sinaddr[]; /* port addresses */ extern char sinrate[][4]; /* Baud rate table */ extern struct tty sin[]; /* common tty structure */ extern struct ccb *sinccb; /* channel control blocks */ #ifdef DIOS extern int sindios[]; /* dioses for sin drivers */ #endif DIOS extern char conscmd2; /* PALS command 2 for 'console' */ extern short isp[]; /* interrupt service ptr table */ extern char devmap[]; extern char devint[]; extern int (*handler[]) (); int sinrint (), sintint (); int sinint (); /* * PALS commands & status bits */ /* command 1 */ #define DIS 0200 #define EN 0100 #define DTR 0040 #define WRT 0002 #define CMD1 0001 /* command 2 */ #define CLKSHIFT 6 /* offset of CLOCK bits in command 2 */ #define DATA8 0060 /* 8 data bits */ #define DATA7 0040 /* 7 data bits */ #define STOP 0010 /* 2 stop bits */ #define PODD 0004 /* odd parity */ #define PARE 0006 /* even parity */ /* Status */ #define PF 0100 #define FRERR 0040 #define BSY 0010 #define EXA 0004 #define CARR_OFF 0002 /* Status as returned by AutoDriver Channel interrupt */ #define ADCBADSTATUS 0x100 #define ADCBUFLIMIT 0x200 /* ADC mask used in ccb command */ #define CCBSEX 0x400 /* Translate table defines */ #define XLATESUB 0x8000 /* substitute on translate */ #define NXLATE 256 /* size of translate table */ short sinxlate[NXLATE]; /* one translate table for all sins */ short sinrawxlate[NXLATE]; /* one translate table for all sins */ /* Buffer management defines */ #define SIZEBUF BSIZE #define ENDBUF (SIZEBUF-1) #define STRTBUF0 0 #define STRTBUF1 0 /* Structure needed for each sin device * should be interfaced with mkconf, so you can change number * of buffers allocated at sysgen time. For now you need to * recompile the dirver if you want a larger number of sins * - could do without this struct but it makes things easier */ #define MAXSINS 6 /* max number of sins, used in local buff */ #define SINWAIT 0x01 /* set by read if waiting for input */ struct lsin { struct buf *sin_0cbuf; /*save area for cache buffer pointer*/ struct buf *sin_1cbuf; /*save area for cache buffer pointer*/ char *sin_buf[2]; /* address of buffer 0 and 1 */ short *sin_cnt[2]; /* address of ccbcount 0 and 1 */ int sin_off[2]; /* offset used for reading */ int sin_curb; /* current buffer being read from */ int sin_stat; /* status not in tty struct */ int sin_minbrk; /* minimum chars to break on */ /* without at least one timeout */ }; struct lsin lsin[MAXSINS]; /* this should be done by mkconf */ #define MINBREAK 128 /* default sin_minbrk */ int sinerr[MAXSINS]; /* count of chars thrown out cause of ADCBADSTATUS */ int xlateset = NO; /* flag used for setting up translate table */ int sint_in_p = NO; /* timeout in progress */ int sin_time = HZ; /* size of time out in ticks, can be set by adb */ /* * open routine: * called each time a process opens sin as a character file * * - if the state was previously inactive, set up the initial status * and arm interrupts * * - if it is the first open, allocate a buffer from the buffer cache * to be used for the auto driver channel imput buffers * */ sinopen (dev) { register struct tty *tp; register struct ccb *ccbp; /* pointer to channel control block */ register struct buf *dbuf0; /* pointer to buffer structure 0 */ register struct buf *dbuf1; /* pointer to buffer structure 1 */ register struct lsin *lsinp; /* pointer to local sin structure */ register int minordev; if ((minordev = minor (dev)) >= nsin) { u.u_error = ENXIO; return; } /* trmask |= TRACE1 | TRACE2; /* TURN ON TRACING */ trace (TRACE1, "open", dev ); tp = &sin[minordev]; if ((tp->t_state & ISOPEN) == 0) { tp->t_dev = dev; tp->t_state = ISOPEN; if (tp->t_flags == 0) tp->t_flags = EVENP | ODDP; ttychars (tp); ccbp = &sinccb[minordev]; lsinp = &lsin[minordev]; isp[sinaddr[minordev] ] = (char *)ccbp + 1; /* set pointer to translate table */ if ( !xlateset) { /* if first open of any sin, */ xlateinit (); /* initialize translate table */ xlateset = YES; } if ((tp->t_flags & RAW)==0) ccbp->cc_tab = sinxlate; else ccbp->cc_tab = sinrawxlate; /* get buffer from buffer cache and set up auto driver */ /* ccb to use the buffer which is split in half */ trace (TRACE1, "bgbl", dev ); dbuf0 = geteblk (); dbuf1 = geteblk (); trace (TRACE1, "agbl", dev ); lsinp->sin_0cbuf = dbuf0;/* save address of buffer for brelse */ lsinp->sin_1cbuf = dbuf1;/* on close */ ccbp->cc_buf0 = & (dbuf0->b_un.b_addr[ENDBUF]); ccbp->cc_buf1 = & (dbuf1->b_un.b_addr[ENDBUF]); ccbp->cc_cnt0 = -ENDBUF; ccbp->cc_cnt1 = -ENDBUF; ccbp->cc_ccw = CCBSEX | CCBEXECUTE | CCBREAD | CCBXLATE | CCBLRC; lsinp->sin_buf[0] = & (dbuf0->b_un.b_addr[STRTBUF0]); lsinp->sin_buf[1] = & (dbuf1->b_un.b_addr[STRTBUF1]); lsinp->sin_cnt[0] = & (ccbp->cc_cnt0); lsinp->sin_cnt[1] = & (ccbp->cc_cnt1); lsinp->sin_off[0] = lsinp->sin_off[1] = 0; lsinp->sin_curb = 0; lsinp->sin_minbrk = MINBREAK; sinerr[minordev] = 0; trace (TRACE1, "benb", dev ); sinenab (tp, minordev); trace (TRACE1, "aenb", dev ); } #ifdef WAIT_FOR_CARRIER spl4 (); while ( !(tp->t_state&CARR_ON)) sleep (&tp->t_rawq, TTIPRI); spl0 (); #endif WAIT_FOR_CARRIER if ((tp->t_state&XCLUDE) && u.u_uid != 0) { u.u_error = EBUSY; return; } } /* * close routine: * - called only when last process using terminal releases it */ sinclose (dev) { register struct tty *tp; register int minordev; tp = &sin[minordev = minor (dev)]; trace (TRACE1, "bclo", dev ); tp->t_state = 0; brelse (lsin[minordev].sin_0cbuf); /*give buffer back to buffer cache*/ brelse (lsin[minordev].sin_1cbuf); /*give buffer back to buffer cache*/ sindisab (dev); if (sinerr[minordev]) printf ("sin%d lost %d character%s\n", minordev, sinerr[minordev], sinerr[minordev] == 1 ? "" : "s"); trace (TRACE1, "aclo", dev ); } /* * read, ioctl routines: */ sinread (dev) { register struct lsin *lsinp; /* pointer to local sin structure */ register int cb; /* current buffer */ register int nready; /* num bytes ready to copy to user */ register int numdone; /* number transferred so far */ register int havetimed; /* we have timed out at least once */ register int minordev; lsinp = &lsin[minordev = minor(dev)]; cb = lsinp->sin_curb; #ifdef DIOS /* testing */ if (sindios[minordev]) { diosint (0, 0); goto out; } #endif DIOS trace (TRACE1, "bred", dev ); for (havetimed = NO, numdone = 0; ; ) { spl5 (); /* lock out auto driver */ #ifdef DIOS if (sindios[minordev]) { } #endif DIOS trace (TRACE1, "rdloop", dev ); while (0 == (nready = ENDBUF + *lsinp->sin_cnt[cb] - lsinp->sin_off[cb])) { /* if sizes the same set them back to start */ if ( numdone && ( havetimed || numdone > lsinp->sin_minbrk ) ) { spl0 (); goto out; } /* wait until there is data to read */ if ( !sint_in_p) { /* if timeout not in progress */ sint_in_p = YES; timeout (sintint, 0, sin_time); } trace (TRACE1, "brdsleep", dev ); sleep ((caddr_t)lsin, TTIPRI); trace (TRACE1, "ardsleep", dev ); havetimed = YES; } spl0 (); /* enable interrupts */ trace (TRACE1, "rdmidloop", dev ); /* now copy data to user */ if (nready > 0) { if (nready > u.u_count) nready = u.u_count; iomove(&lsinp->sin_buf[cb][lsinp->sin_off[cb]], nready, B_READ); lsinp->sin_off[cb] += nready; if (lsinp->sin_off[cb] == SIZEBUF) { /* if we are at the end of the current * buffer, switch to the other one. */ lsinp->sin_off[cb] = 0; *lsinp->sin_cnt[cb] = -ENDBUF; cb = lsinp->sin_curb = lsinp->sin_curb ? 0 :1; } if (u.u_count == 0) goto out; numdone += nready; } } out: trace (TRACE1, "ared", dev ); return; } /* ARGSUSED */ sinioctl (dev, cmd, addr, flag) caddr_t addr; { register struct tty *tp; tp = &sin[minor (dev)]; if (ttioccom (cmd, tp, addr, dev) == 0) { u.u_error = ENOTTY; return; } /* Stty - reissue CMD 2 to change baud rate */ if (cmd == TIOCSETP || cmd == TIOCSETN) sinenab (tp, minor (dev)); } sinrint (minordev, stat) register stat; { register int raddr; register struct tty *tp; register struct ccb *ccbp; /* pointer to channel control block */ register struct lsin *lsinp; /* pointer to local sin structure */ tp = &sin[minordev]; raddr = sinaddr[minordev]; if ( !(tp->t_state & ISOPEN)) { printf ("sinrint while closed\n"); return; } trace (TRACE1, "rint", raddr); trace (TRACE1, "stat", stat); #ifdef DIOS if (sindios[minordev]) { stat = ss (raddr); } #endif DIOS if (stat & ADCBADSTATUS ) { register char c; /* * EXA (bad satsus means error condition, read from device * to reset the mode, and just throw any data away for now */ c = rd (raddr); /* ++sinerr[minordev]; /* we should really find out what */ /* types of errors are occurring */ goto out; } if (stat & ADCBUFLIMIT) { /* buffer overflow just wakeup reader if sleeping */ wakeup ((caddr_t)lsin); goto out; } /* must be translate interrupt */ trace (TRACE1, "xlat", minordev); goto out; /* should not happen in this version */ #ifdef TRANSLATE ccbp = &sinccb[minordev]; lsinp = &lsin[minordev]; /* set donebuf to current buffer */ donebuf = (ccbp->cc_ccw & CCBBUFSW) ? 1 : 0; /* was translate interrupt, put break character in buffer */ /* and increment the count */ lsinp->sin_buf[donebuf][ENDBUF + *lsinp->sin_cnt[donebuf]++] = (char) (stat&0xFF); /* if we are not over TTYHOG move the data to the */ /* raw queue, note could use TTYHOG + SIZEBUF */ if ( tp->t_rawq.c_cc < TTYHOG ) { b_to_q ( lsinp->sin_buf[donebuf], ENDBUF + *lsinp->sin_cnt[donebuf], &tp->t_rawq ); } #endif TRANSLATE out: return; } /* * Handle time out interrupts, set by sinread */ sintint (arg) { sint_in_p = NO; wakeup ((caddr_t)lsin); } /* * Arm interrupts from PALS and set baud rate */ sinenab (atp, minordev) struct tty *atp; int minordev; { register struct tty *tp; register radd; register char *rate; register int clk; register cmd2, stat; tp = atp; if (tp->t_ospeed == 0) cmd2 = conscmd2; else { cmd2 = conscmd2 & (03<t_dev)][0]; for (clk = 0; clk < 4; clk++) if (*rate++ == tp->t_ospeed) { cmd2 = clk << CLKSHIFT; break; } if ((tp->t_flags&RAW) == 0) { if ( tp->t_flags & ODDP ) cmd2 |= DATA7 | STOP | PODD; if ( tp->t_flags & EVENP ) cmd2 |= DATA7 | STOP | PARE; } else cmd2 |= DATA8 | STOP; } #ifdef DIOS /* kickdios (minordev); /* testing */ /* initialize the dios if applicable */ if (sindios[minordev]) diosinit (sindios[minordev]); #endif DIOS radd = sinaddr[minor (tp->t_dev)]; oc (radd, cmd2); oc (radd, EN | DTR | CMD1); /*** oc (radd + 1, EN | DTR | WRT | CMD1); /* oc (wadd ... */ stat = ss (radd); if ((stat&CARR_OFF) == 0) tp->t_state |= CARR_ON; trace (TRACE1, "sinenab", stat); rd (radd); } sinint (dev, stat) register stat; { printf ("begin sin interrupt"); sindisab (dev); printf ("end sin interrupt"); return; } /* * Disarm interrupts from PALS */ sindisab (dev) { register radd; radd = sinaddr[minor (dev)]; oc (radd, DIS | CMD1); /* * oc (radd + 1, DIS | WRT | CMD1); */ trace (TRACE1, "sindisab", ss (radd)); } /* * xlateinit routine * - called to initialize the sinxlate table - it could be initialize * - at define time. */ xlateinit () { register i; trace (TRACE1, "bxin", 0); for (i = 0; i < NXLATE; i++) { sinxlate[i] = (i & 0x7F) | XLATESUB; sinrawxlate[i] = i | XLATESUB; } trace (TRACE1, "axin", 0); return; } #ifndef DIOS diosint (minordev, stat) { printf ("dios %d interrupted. status=0x%x\n", minordev, stat); } #else DIOS diosint (minordev, stat) { extern short diosaddr[]; int piq; int piqdev; /* device addr of device to be serviced */ int piqstatus; /* reason field from piq */ /* printf ("dios %d interrupted. status=0x%x\n", minordev, stat);/**/ while ( !(ss (diosaddr) & PIQEMPTY)) { piq = rh (diosaddr); piqstatus = (piq & PIQ_REASON) >> 8; printf ("dios piq: (in)valid=%d, reason=0x%x, devaddr=0x%x\n", piq & PIQ_VALID ? 1 : 0, piqstatus, piq & PIQ_DEVICE); if (piq & PIQ_VALID) continue; piqdev = (piq & PIQ_DEVICE) | diosaddr[minordev]; switch (piqstatus) { case 0: /* execute reset */ case 1: /* bad status */ case 2: /* buffer limit */ case 8: /* immmediate interrupt */ case 9: /* special character */ case 0xa: /* normal termination */ case 0xb: /* transfer process */ case 0xc: /* response too slow */ break; case 3: /* impossible */ case 4: /* false sync */ case 5: /* spurious interrupt */ case 6: /* main memory malfunction */ case 7: /* impossible */ case 0xd: /* piq overflow */ case 0xe: /* impossible */ case 0xf: /* dios hardware problem */ printf ("I/O error on dios 0x%x: reason=0x%x device=0x%x\n", diosaddr[minordev], piqstatus, piqdev); return; } (*handler[devint[piqdev]]) (devmap[piqdev], piqstatus); } return; } kickdios (minordev) int minordev; { if ( !sindios[minordev]) return; /* set DIOS to KILL mode so auto driver channel should work */ trace (TRACE2, "dios status is", ss (sindios[minordev])); trace (TRACE1, "kill dios", 0); oc (sindios[minordev], KILL_CMD); trace (TRACE2, "result of dios kill", ss (sindios[minordev])); oc (sindios[minordev], 4 | DISABLE_DIOS | KILL_CMD); wh (sindios[minordev], 0x0210); oc (sindios[minordev], ACTIVE_CMD); trace (TRACE2, "dios status after loading piq", ss (sindios[minordev])); oc (sindios[minordev], ACTIVE_CMD | ENABLE_DIOS | READPIQ); trace (TRACE2, "dios enabled", ss (sindios[minordev])); } diosinit (dios) int dios; { static first[MAXDIOS]; int diosminor; if ( !first[diosminor = devmap[dios]]) { first[diosminor] = 1; trace (TRACE2, "dios status is", ss (dios)); oc (dios, DISARM_DIOS | KILL_CMD); /* oc (dios, ENABLE_DIOS | ACTIVE_CMD | READPIQ); /**/ oc (dios, DISABLE_DIOS | ACTIVE_CMD | READPIQ); trace (TRACE2, "dios enabled", ss (dios)); } } #endif DIOS