📄 dmb.c
字号:
if (lpr&DMB_DTR) b |= TIOCM_DTR; if (lstat&DMB_DCD) b |= TIOCM_CD; if (lstat&DMB_CTS) b |= TIOCM_CTS; if (lstat&DMB_RI) b |= TIOCM_RI; if (lstat&DMB_DSR) b |= TIOCM_DSR; return(b);}dmtodmb(bits) register int bits;{ register long lpr = 0; if (bits&TIOCM_RTS) lpr |= DMB_RTS; if (bits&TIOCM_DTR) lpr |= DMB_DTR; return(lpr);}/* * Set parameters from open or stty into the DMB hardware * registers. */dmbparam(unit) register int unit;{ register struct tty *tp; register struct dmb_device *addr; register long lpar; tp = &dmb_tty[unit]; if (tp->t_state & TS_BUSY) { tp->t_state |= TS_NEED_PARAM; return; } addr = (struct dmb_device *)tp->t_addr; addr->dmb_acsr = (unit & LINEMASK) | DMB_IE; /* line select */ /* * Disconnect modem line if baudrate is zero. POSIX checks the output * baud rate, while non-POSIX checks the input baud rate. */ if ((((tp->t_cflag&CBAUD)==B0) && (u.u_procp->p_progenv != A_POSIX)) || (((tp->t_cflag_ext & CBAUD)==B0) && (u.u_procp->p_progenv == A_POSIX))) { tp->t_cflag |= HUPCL; addr->dmb_lpr &= ~(DMB_DTR | DMB_RTS); /* turn off DTR & RTS but leave enabled */ return; } /* else - set line parameters */ lpar = (dmb_speeds[tp->t_cflag_ext&CBAUD]<<28) | /* ospeed */ (dmb_speeds[tp->t_cflag&CBAUD]<<24); /* ispeed */ /* * Berkeley-only dinosaurs */ if (tp->t_line != TERMIODISC) { if ((tp->t_cflag & CBAUD) == B134){ lpar |= DMB_BITS6|DMB_PARITYENAB; tp->t_cflag |= CS6|PARENB; } else if ((tp->t_cflag_ext & CBAUD) == B110){ lpar |= DMB_TWOSTOPB; tp->t_cflag |= CSTOPB; } } /* * System V termio functionality. * Set device registers according to the specifications of the termio * structure. */ if (tp->t_cflag & CREAD) lpar |= DMB_RXENA; else lpar &= ~DMB_RXENA; if (tp->t_cflag & CSTOPB) lpar |= DMB_TWOSTOPB; else lpar &= ~DMB_TWOSTOPB; /* parity is enable */ if (tp->t_cflag & PARENB) { if ((tp->t_cflag & PARODD) == 0) /* set even */ lpar |= DMB_PARITYENAB|DMB_EVENPARITY; else /* else set odd */ lpar = (lpar | DMB_PARITYENAB)&~DMB_EVENPARITY; } /* * character size. * clear bits and check for 6,7,and 8, else its 5 bits. */ lpar &= ~DMB_BITS8; switch(tp->t_cflag&CSIZE) { case CS6: lpar |= DMB_BITS6; break; case CS7: lpar |= DMB_BITS7; break; case CS8: lpar |= DMB_BITS8; break; } /* * Outgoing Auto flow control. * No auto flow control allowed if startc != ^q and startc != * ^s. Most drivers do not allow this to be changed. */ if ((tp->t_cflag_ext & PAUTOFLOW) && (tp->t_cc[VSTOP] == CTRL('s')) && (tp->t_cc[VSTART] == CTRL('q'))) { /* * OAUTO doesn't work on the DHB. * Take this out if it ever does work. */ if (dmb_lines[unit >> LINEBITS] != DMB_16_LINES) lpar |= DMB_OAUTOFLOW; else tp->t_cflag_ext &= ~PAUTOFLOW; } # ifdef DEBUG if (dmbdebug) mprintf("dmbparam: tp = %x, lpar = %x\n",tp, lpar);# endif DEBUG /* * Set "Report Modem" bit to get interrupts on modem status changes. * Enable receiver, and set Transmission interrupt delay so that * xmit interrupt does not actually happen until all characters * in current DMA have been transmitted. * Make all lines modem lines, in case a line is * changed from local to remote (modem). */ lpar |= (DMB_REPORT | DMB_TXINTDELAY | DMB_DTR | DMB_RTS); /* * If outgoing auto flow control is enabled, the hardware will * control the transmit enable bit. */ if ((tp->t_cflag_ext & PAUTOFLOW) == 0) addr->dmb_lstathigh |= DMB_TXENA; addr->dmb_lpr = lpar;}/* * DMB32 transmitter interrupt. * Restart each line which used to be active but has * terminated transmission since the last interrupt. */dmbxint(dmb) int dmb;{ int unit = dmb << LINEBITS; struct tty *tp0 = &dmb_tty[unit]; register struct tty *tp; register struct dmb_device *addr; register struct uba_device *ui; register long tbuf; u_short cntr; register temp_reg; int totaldelay; ui = dmbinfo[dmb]; addr = (struct dmb_device *)ui->ui_addr; while ((tbuf = addr->dmb_tbuf) < 0) { /* xmitter action is set if "< 0" */ tbuf = tbuf & LINEMASK; tp = tp0 + tbuf; smp_lock(&tp->t_lk_tty,LK_RETRY); DMB_LOCK(dmb); /* * Make sure another processor hasn't serviced the interrupt. */ if ((tbuf = addr->dmb_tbuf) >= 0){ DMB_UNLOCK(dmb); smp_unlock(&tp->t_lk_tty); break; } if (tp != (tp0 + (tbuf & LINEMASK))) { DMB_UNLOCK(dmb); smp_unlock(&tp->t_lk_tty); continue; } addr->dmb_tbuf = 0; /* must write to clear xmitter act bit */ smp_unlock(&lk_dmb[dmb]); /* * Check Error byte and if any error bits set, * decode the error. */ switch (tbuf & DMB_ERMASK) { case DMB_TXDMAERROR: printf("dmb%d: DMA Error. tbuf = 0x%x\n", dmb, tbuf); break; case DMB_MSGERR: printf("dmb%d: Message Error. tbuf = 0x%x\n", dmb, tbuf); break; case DMB_LASTCHERR: printf("dmb%d: Last character Incomplete. tbuf = 0x%x\n", dmb, tbuf); break; case DMB_BUFERR: printf("dmb%d: Buffer Error. tbuf = 0x%x\n", dmb, tbuf); break; case DMB_MODEMERR: printf("dmb%d: Modem Error. tbuf = 0x%x\n", dmb, tbuf); break; case DMB_INTERNALERR: printf("dmb%d: Internal Error. tbuf = 0x%x\n", dmb, tbuf); break; }# ifdef DEBUG printd10("dmbxint: unit=0x%x, line=%d, tp=0x%x, c_cc=%d\n", unit, tbuf, tp, tp->t_outq.c_cc);# endif tbuf = tbuf & LINEMASK; tp->t_state &= ~(TS_BUSY|TS_OABORT); totaldelay = 0; DMB_LOCK(dmb); addr->dmb_acsr = DMB_IE | tbuf; while ((addr->dmb_startdma&DMB_TXDMASTART)&&(totaldelay <= 100)){ totaldelay++; DELAY(10000); } if (addr->dmb_startdma&DMB_TXDMASTART) { printf("dmbxint: Resetting DMA START bit on line %d\n",tbuf); addr->dmb_startdma &= ~DMB_TXDMASTART; } if (tp->t_state&TS_FLUSH) tp->t_state &= ~TS_FLUSH; /* was a non ^S stop */ else { /* * Determine number of characters transmitted so far * and flush these from the tty output queue. * (unit is the dmb unit number so add in the line #.) * unit is already left shifted by the number of lines */ /* The original code below is replaced by functionally equivalent code. We do this to work around a bug in the C optimizer which optimizes the original code which was an ASHL instruction followed by BICL to be an EXTZV instruction. It is not permitted to have an EXTZV instruction read from I/O space. By using a register variable as is done below, the optimization is avoided. This could be reconsidered once the C optimizer is corrected. */#ifdef ORIGINAL_CODE cntr = dmb_numchars[unit+tbuf] - ((addr->dmb_tbuffct >> DMB_TXCHARCT) & 0xffff);#else temp_reg = (addr->dmb_tbuffct >> DMB_TXCHARCT); temp_reg &= 0xffff; cntr = dmb_numchars[unit+tbuf] - temp_reg;#endif ndflush(&tp->t_outq, (int)cntr); } if (tp->t_state & TS_NEED_PARAM) { tp->t_state &= ~TS_NEED_PARAM; /* * (unit is the dmb unit number so add in the line #.) * unit is already left shifted by the number of lines */ dmbparam(unit+tbuf); } DMB_UNLOCK(dmb); if (tp->t_line) (*linesw[tp->t_line].l_start)(tp); else dmbstart(tp); smp_unlock(&tp->t_lk_tty); }}/* * Start (restart) transmission on the given DMB32 line. */dmbstart(tp) register struct tty *tp;{ register struct dmb_device *addr; register int unit, nch; register int totaldelay; register int dmb; int post_wakeup = 0; TTY_ASSERT(tp); unit = minor(tp->t_dev); dmb = unit >> LINEBITS; addr = (struct dmb_device *)tp->t_addr; /* * If it's currently active, or delaying, no need to do anything. * Also do not transmit if not CTS. */ if ((tp->t_state&(TS_TIMEOUT|TS_BUSY|TS_TTSTOP)) || ((tp->t_state & TS_CARR_ON) && (dmbmodem[unit]&MODEM_CTS)==0)) return; /* * If there are sleepers, and output has drained below low * water mark, wake up the sleepers. */ if (tp->t_outq.c_cc<=TTLOWAT(tp)) { if (tp->t_state&TS_ASLEEP) { tp->t_state &= ~TS_ASLEEP; post_wakeup = WAKEUP_OUTQ; } if (tp->t_wsel) { /* for select system call */ post_wakeup |= WAKEUP_SELECT; tp->t_wsel = 0; tp->t_state &= ~TS_WCOLL; } } /* * Output had been aborted by a stop character. Resume output by * turning on transmit enable. */ if ((tp->t_state & TS_OABORT) && ((tp->t_cflag_ext & PAUTOFLOW) == 0)) { DMB_LOCK(dmb); addr->dmb_acsr = DMB_IE | (unit & LINEMASK); addr->dmb_lstathigh |= DMB_TXENA; DMB_UNLOCK(dmb); tp->t_state |= TS_BUSY; tp->t_state &= ~TS_OABORT; goto dmbendstart; } /* * Now restart transmission unless the output queue is empty. */ if (tp->t_outq.c_cc == 0) goto dmbendstart; if ((tp->t_lflag_ext & PRAW) || (tp->t_oflag_ext & PLITOUT) || ((tp->t_oflag & OPOST) == 0)) nch = ndqb(&tp->t_outq, 0); /* # of consecutive chars */ else { nch = ndqb(&tp->t_outq, DELAY_FLAG); /* * If first thing on queue is a delay process it. */ if (nch == 0) { nch = getc(&tp->t_outq); timeout(ttrstrt, (caddr_t)tp, (nch&0x7f)+6); tp->t_state |= TS_TIMEOUT; goto dmbendstart; } } /* * If characters to transmit, restart transmission. */ if (nch) { DMB_LOCK(dmb); addr->dmb_acsr = DMB_IE | (unit & LINEMASK); /* line select */ /* * Wait for dma start to clear to a maximum of 1 second to * prevent a system hang on hardware failure. After the bit * has stuck once, do not repeat this check. */ totaldelay = 0; while ((addr->dmb_startdma&DMB_TXDMASTART)&&(totaldelay <= 100)){ if ((dmb_softc[unit/16].sc_flags[unit&LINEMASK]) == 0){ totaldelay++; DELAY(10000); } else{ totaldelay = 90000; } } if ((addr->dmb_startdma & DMB_TXDMASTART) && ((dmb_softc[unit/16].sc_flags[unit&LINEMASK]) == 0)) { printf("dmb%d,line%d DMB HARDWARE ERROR. TX.DMA.START failed\n",unit/16,unit&LINEMASK); /* * Prevent further checks on this line by * setting state flag to be nonzero. */ dmb_softc[unit/16].sc_flags[unit&LINEMASK]++; DMB_UNLOCK(dmb); goto dmbendstart; } if (addr->dmb_abort & DMB_TXOUTABORT) { addr->dmb_abort &= ~(DMB_TXOUTABORT); } /* * If outgoing auto flow control is enabled, the hardware will * control the transmit enable bit. */ if ((tp->t_cflag_ext & PAUTOFLOW) == 0) addr->dmb_lstathigh |= DMB_TXENA; /* * TODO: if (nch == 1) { use programmed (preempt) transfer } * This will save on DMA setup overhead. */ /* hand the board the physical address of the buffer */ addr->dmb_tbuffadd = (long)svtophy(tp->t_outq.c_cf); addr->dmb_tbuffct = (nch << DMB_TXCHARCT); /* * Save the number of characters that we are DMA'ing, * for use in the transmit interrupt routine. * (unit is the whole minor device, unit # & line #.) */ dmb_numchars[unit] = nch; addr->dmb_startdma |= (DMB_TXDMASTART | DMB_TXDMAPHYS); tp->t_state |= TS_BUSY; DMB_UNLOCK(dmb); } /* * Unfortunately these wakeups will be done while the tty lock * is still held. The penalty is not so bad because most of * the callers of this routine will release locks very shortly * after calling this routine. */dmbendstart: if (post_wakeup & WAKEUP_OUTQ) wakeup((caddr_t)&tp->t_outq); if (post_wakeup & WAKEUP_SELECT) selwakeup(tp->t_wsel, tp->t_state & TS_WCOLL);}/* * Stop output on a line, e.g. for ^S/^Q or output flush. *//*ARGSUSED*/dmbstop(tp, flag) register struct tty *tp;{ register struct dmb_device *addr; register int unit, s, dmb; TTY_ASSERT(tp); dmb = (minor(tp->t_dev)) >> LINEBITS; addr = (struct dmb_device *)tp->t_addr; /* * Block input/output interrupts while messing with state. * (tty lock must be taken out prior to calling this routine) */ if (tp->t_state & TS_BUSY) { /* * Device is transmitting; stop output. * We can continue later by examining the character count. */ unit = minor(tp->t_dev) & LINEMASK; DMB_LOCK(dmb); addr->dmb_acsr = unit | DMB_IE; if ((tp->t_state&TS_TTSTOP)==0) { if (addr->dmb_startdma & DMB_TXDMASTART) { addr->dmb_abort |= DMB_TXOUTABORT; /* abort DMA transmission */ tp->t_state |= TS_FLUSH; /* NOT a ctl-S */ } } /* * Suspend output by turning off transmit enable. Output will * resume when a start char is received. */ else if ((tp->t_cflag_ext & PAUTOFLOW) == 0) { addr->dmb_lstathigh &= ~DMB_TXENA; tp->t_state &= ~TS_BUSY; tp->t_state |= TS_OABORT; } DMB_UNLOCK(dmb); }}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -