/* * Lisa INS8250A device driver * * Copyright 1984 UniSoft Corporation */ #include "sys/param.h" #include "sys/config.h" #include "sys/types.h" #include "sys/systm.h" #include "sys/dir.h" #include "sys/signal.h" #include "sys/user.h" #include "sys/errno.h" #include "sys/file.h" #include "sys/tty.h" #include "sys/termio.h" #include "sys/conf.h" #include "sys/sysinfo.h" #include "sys/var.h" #include "setjmp.h" #include "sys/reg.h" #include "sys/mmu.h" #include "sys/proc.h" int teproc(); /* * structure to access Tecmar device registers */ struct tedevice { char fill1; char te_rbr; /* +1 data register */ #define te_dvlsb te_rbr /* lsb of divisor latch */ char fill2; char te_ier; /* +3 interrupt enable register */ #define te_dvmsb te_ier /* msb of divisor latch */ char fill3; char te_iir; /* +5 interrupt id register */ char fill4; char te_lcr; /* +7 line control register */ char fill5; char te_mcr; /* +9 modem control register */ char fill6; char te_lsr; /* +11 line status register */ char fill7; char te_msr; /* +13 modem status register */ char fill8; char te_scrat; /* +15 scratch register (8250-A only) */ char fill[0x200-16]; /* sized to make tedevice be 0x200 long */ }; /* * structure to access the interrupt reset bit */ struct teidevice { struct tedevice fill[7];/* filler */ char skip; char te_intr; /* interrupt reset location */ }; /* * array used to remap ivec interrupt board slot number to tty slot */ int te_remap[3]; /* * Slot id to interrupt reset address */ struct teidevice *te_idevice[3] = { (struct teidevice *)(STDIO), (struct teidevice *)(STDIO+0x4000), (struct teidevice *)(STDIO+0x8000) }; extern struct tty te_tty[1]; extern struct ttyptr te_ttptr[1]; extern char te_dparam[1]; extern char te_modem[1]; extern int te_cnt; #define MODEM 0x80 /* modem control on bit */ #define tedev(d) ((d)&0x7f) /* from unix device number to device */ #define BAUD50 2304 #define BAUD75 1356 #define BAUD110 1047 #define BAUD134 857 #define BAUD150 768 #define BAUD300 384 #define BAUD600 192 #define BAUD1200 96 #define BAUD1800 64 #define BAUD2000 58 #define BAUD2400 48 #define BAUD3600 32 #define BAUD4800 24 #define BAUD7200 16 #define BAUD9600 12 #define BAUD19200 6 #define BAUD38400 3 #define BAUD56000 2 /* -1 means hangup, -2 means invalid */ int tebaudmap[] = { -1, BAUD50, BAUD75, BAUD110, BAUD134, BAUD150, BAUD134, BAUD300, BAUD600, BAUD1200, BAUD1800, BAUD2400, BAUD4800, BAUD9600, BAUD19200, BAUD38400 }; /* Interrupt enable register bits */ #define ERBFI 0x01 /* Enable received data available interrupt */ #define ETBEI 0x02 /* Enable transmitter enable holding register empty interrupt */ #define ELSI 0x04 /* Enable receiver line status interrupt */ #define EDSSI 0x08 /* Enable modem status interrupt */ /* Interrupt ident register bits */ #define IRQ 0x01 /* Interrupt request, 0 if interrupt pending */ #define THE 0x02 /* Transmitter holding register empty */ #define IID 0x06 /* Interrupt ID bit mask */ #define RID 0x04 /* Interrupt receive bit mask */ /* Line control register bits */ #define BITS5 0x00 /* 5 bits */ #define BITS6 0x01 /* 6 bits */ #define BITS7 0x02 /* 7 bits */ #define BITS8 0x03 /* 8 bits */ #define STOP1 0x00 /* One stop bit */ #define STOP2 0x04 /* Two stop bit */ #define PEN 0x08 /* Parity enable */ #define EPS 0x10 /* Even parity select */ #define SPS 0x20 /* Stick parity */ #define SBRK 0x40 /* Set break */ #define DLAB 0x80 /* Divisor latch access. i/o direction bit */ /* Modem control register bits */ #define DTR 0x01 /* Data terminal ready */ #define RTS 0x02 /* Request to send */ /* Line status register bits */ #define DATARDY 0x01 /* Data ready */ #define OV_ERR 0x02 /* Overrun error on receiver */ #define PE_ERR 0x04 /* Parity error */ #define FR_ERR 0x08 /* Framing error */ #define BR_INT 0x10 /* Break interrupt */ #define THRE 0x20 /* Transmitter holding register */ #define TEMT 0x40 /* Transmitter empty */ /* Modem status register bits */ #define DCTS 0x01 /* Delta clear to send */ #define DDSR 0x02 /* Delta data set ready */ #define TERI 0x04 /* Trailing edge ring indicator */ #define DDCD 0x08 /* Delta data carrier detect */ #define CTS 0x10 /* Clear to send */ #define DSR 0x20 /* Data set ready */ #define RI 0x40 /* Ring indicator */ #define DCD 0x80 /* Data carrier detect */ int teslotsused = 0; /* * Initialize the baud rate * slot = 0, 1, or 2 */ teinit(slot) { register i, val; if (teslotsused+4 > te_cnt) { printf("\n\nSystem only configured for %d tecmar ports\n\n", te_cnt); return(1); } te_remap[slot] = teslotsused; val = STDIO + slot*0x4000 + 0x200; for (i = teslotsused; i < teslotsused+4; i++) { te_ttptr[i].tt_addr = val; te_ttptr[i].tt_tty = &te_tty[i]; val += 0x200; } teslotsused += 4; return(0); } /* ARGSUSED */ teopen(dev, flag) dev_t dev; { register struct tedevice *addr; register struct tty *tp; register d; #ifdef SINGLEUSER register struct proc *p; #endif SINGLEUSER d = tedev(dev); if (d >= te_cnt) { u.u_error = ENXIO; return; } tp = te_ttptr[d].tt_tty; #ifdef SINGLEUSER p = u.u_procp; if ((p->p_pid == p->p_pgrp) && (u.u_ttyp == NULL) && (tp->t_pgrp == 0)) { u.u_error = ENOTTY; return; } #endif SINGLEUSER addr = (struct tedevice *)te_ttptr[d].tt_addr; if (tp == 0 || addr == 0) { u.u_error = ENXIO; return; } tp->t_index = d; SPL5(); if ((tp->t_state&(ISOPEN|WOPEN)) == 0) { tp->t_proc = teproc; ttinit(tp); tp->t_iflag = ICRNL | ISTRIP; tp->t_oflag = OPOST | ONLCR | TAB3; tp->t_lflag = ISIG | ICANON | ECHO | ECHOK; tp->t_cflag = sspeed | CS8 | CREAD | HUPCL; teparam(dev); } te_modem[d] = dev & MODEM; if ((dev&MODEM)==0 || addr->te_msr&DSR) tp->t_state |= CARR_ON; else tp->t_state &= ~CARR_ON; if (!(flag & FNDELAY)) while ((tp->t_state&CARR_ON) == 0) { tp->t_state |= WOPEN; (void) sleep((caddr_t)&tp->t_rawq, TTOPRI); } SPL0(); (*linesw[tp->t_line].l_open)(tp); } /* ARGSUSED */ teclose(dev, flag) dev_t dev; int flag; { register struct tedevice *addr; register struct tty *tp; register d; d = tedev(dev); tp = te_ttptr[d].tt_tty; (*linesw[tp->t_line].l_close)(tp); if (tp->t_cflag&HUPCL) { addr = (struct tedevice *)te_ttptr[d].tt_addr; d = 0; addr->te_mcr = d; } } teread(dev) dev_t dev; { register struct tty *tp; tp = te_ttptr[tedev(dev)].tt_tty; (*linesw[tp->t_line].l_read)(tp); } tewrite(dev) dev_t dev; { register struct tty *tp; tp = te_ttptr[tedev(dev)].tt_tty; (*linesw[tp->t_line].l_write)(tp); } teproc(tp, cmd) register struct tty *tp; { register struct ccblock *tbuf; register struct tedevice *addr; register dev_t dev; int s; extern ttrstrt(); s = spltty(); dev = tp->t_index; addr = (struct tedevice *)te_ttptr[dev].tt_addr; switch (cmd) { case T_TIME: tp->t_state &= ~TIMEOUT; goto start; case T_WFLUSH: tbuf = &tp->t_tbuf; tbuf->c_size -= tbuf->c_count; tbuf->c_count = 0; /* fall through */ case T_RESUME: tp->t_state &= ~TTSTOP; goto start; case T_OUTPUT: start: if (tp->t_state & (TTSTOP|TIMEOUT|BUSY)) { /* if ((tp->t_state & BUSY) == 0) { addr = (struct tedevice *)((int)addr & 0xFCC000); ((struct teidevice *)addr)->te_intr = dev; } */ break; } if (tp->t_state & TTXOFF) { tp->t_state &= ~TTXOFF; tp->t_state |= BUSY; addr->te_rbr = CSTOP; break; } if (tp->t_state & TTXON) { tp->t_state &= ~TTXON; tp->t_state |= BUSY; addr->te_rbr = CSTART; break; } tbuf = &tp->t_tbuf; if ((tbuf->c_ptr == 0) || (tbuf->c_count == 0)) { if (tbuf->c_ptr) tbuf->c_ptr -= tbuf->c_size - tbuf->c_count; if (!(CPRES & (*linesw[tp->t_line].l_output)(tp))) { break; } } tp->t_state |= BUSY; addr->te_ier = ERBFI | ETBEI | ELSI | EDSSI; addr->te_rbr = *tbuf->c_ptr++; tbuf->c_count--; break; case T_SUSPEND: tp->t_state |= TTSTOP; break; case T_BLOCK: tp->t_state &= ~TTXON; tp->t_state |= TBLOCK; tp->t_state |= TTXOFF; goto start; case T_RFLUSH: if (!(tp->t_state&TBLOCK)) break; /* fall through */ case T_UNBLOCK: tp->t_state &= ~(TTXOFF|TBLOCK); tp->t_state |= TTXON; goto start; case T_BREAK: tp->t_state |= TIMEOUT; timeout(ttrstrt, (caddr_t)tp, v.v_hz>>2); break; } splx(s); } teioctl(dev, cmd, arg, mode) dev_t dev; { if (ttiocom(te_ttptr[tedev(dev)].tt_tty, cmd, arg, mode)) teparam(dev); } teparam(dev) register dev_t dev; { register struct tty *tp; register struct tedevice *addr; register int s, speed, oldpri; char c; tp = te_ttptr[tedev(dev)].tt_tty; addr = (struct tedevice *)te_ttptr[tedev(dev)].tt_addr; /* check for invalid speed */ if ((speed = tebaudmap[tp->t_cflag & CBAUD]) == -2) { u.u_error = EINVAL; return; } s = 0; /* * hangup the line */ if (speed == -1) { addr->te_mcr = s; return; } if (tp->t_state & BUSY) { te_dparam[tedev(dev)] = 1; return; } /* * set new speed */ oldpri = spltty(); addr->te_lcr = DLAB; addr->te_dvlsb = speed; addr->te_dvmsb = speed >> 8; addr->te_lcr = s; /* * set line control information */ if ((tp->t_cflag & CSIZE) == CS8) s |= BITS8; else if ((tp->t_cflag & CSIZE) == CS7) s |= BITS7; else if ((tp->t_cflag & CSIZE) == CS6) s |= BITS6; else if ((tp->t_cflag & CSIZE) == CS5) s |= BITS5; if (tp->t_cflag & CSTOPB) s |= STOP2; if (tp->t_cflag & PARENB) if ((tp->t_cflag & PARODD) == 0) s |= PEN|EPS; addr->te_lcr = s; /* * set modem control information */ addr->te_mcr = DTR | RTS; /* * enable interrupts */ addr->te_ier = ERBFI | ETBEI | ELSI | EDSSI; /* * reset pending interrupts */ c = addr->te_rbr; c = addr->te_lsr; c = addr->te_msr; c = addr->te_iir; splx(oldpri); } /* VARARGS */ teintr(ap) struct args *ap; { register struct tedevice *addr; register struct ccblock *cbp; register struct tty *tp; register int c, lcnt, flg, iir, lsr; register char ctmp; int i, any; struct teidevice *iaddr; int index, s; char lbuf[3]; s = spl5(); index = te_remap[ap->a_dev]; iaddr = te_idevice[ap->a_dev]; c = 0; iaddr->te_intr = c; /* reset master interrupt */ again: any = 0; for (i = index; i < index+4; i++) { addr = (struct tedevice *)te_ttptr[i].tt_addr; restart: iir = addr->te_iir; if (iir & IRQ) continue; tp = te_ttptr[i].tt_tty; lsr = addr->te_lsr; if (iir & RID) { sysinfo.rcvint++; c = addr->te_rbr & 0xFF; if (tp->t_rbuf.c_ptr == NULL) { any++; goto restart; } if ((lsr & DATARDY) == 0) c = 0; if (tp->t_iflag & IXON) { ctmp = c & 0177; if (tp->t_state & TTSTOP) { if (ctmp == CSTART || tp->t_iflag & IXANY) (*tp->t_proc)(tp, T_RESUME); } else { if (ctmp == CSTOP) (*tp->t_proc)(tp, T_SUSPEND); } if (ctmp == CSTART || ctmp == CSTOP) { any++; goto restart; } } /* * Check for errors */ lcnt = 1; flg = tp->t_iflag; if (lsr & (PE_ERR|FR_ERR|OV_ERR|BR_INT)) { if ((lsr & PE_ERR) && (flg & INPCK)) c |= PERROR; if (lsr & OV_ERR) c |= OVERRUN; if (lsr & FR_ERR) c |= FRERROR; if (lsr & BR_INT) c = OVERRUN; /* reset char on a break */ } if (c&(FRERROR|PERROR|OVERRUN)) { if ((c&0377) == 0) { if (flg&IGNBRK) { any++; goto restart; } if (flg&BRKINT) { signal(tp->t_pgrp, SIGINT); ttyflush(tp, (FREAD|FWRITE)); any++; goto restart; } } else { if (flg&IGNPAR) { any++; goto restart; } } if (flg&PARMRK) { lbuf[2] = 0377; lbuf[1] = 0; lcnt = 3; sysinfo.rawch += 2; } else c = 0; } else { if (flg&ISTRIP) c &= 0177; else { if (c == 0377 && flg&PARMRK) { lbuf[1] = 0377; lcnt = 2; } } } /* * Stash character in r_buf */ cbp = &tp->t_rbuf; if (cbp->c_ptr == NULL) { any++; goto restart; } if (lcnt != 1) { lbuf[0] = c; while (lcnt) { *cbp->c_ptr++ = lbuf[--lcnt]; if (--cbp->c_count == 0) { cbp->c_ptr -= cbp->c_size; (*linesw[tp->t_line].l_input)(tp); } } if (cbp->c_size != cbp->c_count) { cbp->c_ptr -= cbp->c_size - cbp->c_count; (*linesw[tp->t_line].l_input)(tp); } } else { *cbp->c_ptr = c; cbp->c_count--; (*linesw[tp->t_line].l_input)(tp); } any++; goto restart; } if (iir&THE) { sysinfo.xmtint++; tp->t_state &= ~BUSY; if (te_dparam[i]) { te_dparam[i] = 0; teparam(i); } teproc(tp, T_OUTPUT); } else { /* * must be a modem transition interrupt */ temodem(tp, addr->te_msr); } any++; goto restart; } if (any != 0) goto again; splx(s); } temodem(tp, msr) register struct tty *tp; { if ((tp->t_state&(ISOPEN|WOPEN))==0) return; if (msr & DSR || te_modem[tp->t_index] == 0) { if ((tp->t_state&CARR_ON) == 0) { tp->t_state |= CARR_ON; if (tp->t_state&WOPEN) wakeup((caddr_t)&tp->t_rawq); } } else { if (tp->t_state&CARR_ON) { tp->t_state &= ~CARR_ON; if (tp->t_rbuf.c_ptr != NULL) { ttyflush(tp, FREAD|FWRITE); signal(tp->t_pgrp, SIGHUP); } } } }