📄 sgiserial.c
字号:
*/void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs){ struct sgi_serial * info = (struct sgi_serial *) dev_id; unsigned char zs_intreg; zs_intreg = read_zsreg(info->zs_next->zs_channel, 3); /* NOTE: The read register 3, which holds the irq status, * does so for both channels on each chip. Although * the status value itself must be read from the A * channel and is only valid when read from channel A. * Yes... broken hardware... */#define CHAN_A_IRQMASK (CHARxIP | CHATxIP | CHAEXT)#define CHAN_B_IRQMASK (CHBRxIP | CHBTxIP | CHBEXT) /* *** Chip 1 *** */ /* Channel B -- /dev/ttyb, could be the console */ if(zs_intreg & CHAN_B_IRQMASK) { if (zs_intreg & CHBRxIP) receive_chars(info, regs); if (zs_intreg & CHBTxIP) transmit_chars(info); if (zs_intreg & CHBEXT) status_handle(info); } info=info->zs_next; /* Channel A -- /dev/ttya, could be the console */ if(zs_intreg & CHAN_A_IRQMASK) { if (zs_intreg & CHARxIP) receive_chars(info, regs); if (zs_intreg & CHATxIP) transmit_chars(info); if (zs_intreg & CHAEXT) status_handle(info); }}/* * ------------------------------------------------------------------- * Here ends the serial interrupt routines. * ------------------------------------------------------------------- *//* * This routine is used to handle the "bottom half" processing for the * serial driver, known also the "software interrupt" processing. * This processing is done at the kernel interrupt level, after the * rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This * is where time-consuming activities which can not be done in the * interrupt driver proper are done; the interrupt driver schedules * them using rs_sched_event(), and they get done here. */static void do_serial_bh(void){ run_task_queue(&tq_serial);}static void do_softint(void *private_){ struct sgi_serial *info = (struct sgi_serial *) private_; struct tty_struct *tty; tty = info->tty; if (!tty) return; if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) { if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup)(tty); wake_up_interruptible(&tty->write_wait); }}/* * This routine is called from the scheduler tqueue when the interrupt * routine has signalled that a hangup has occurred. The path of * hangup processing is: * * serial interrupt routine -> (scheduler tqueue) -> * do_serial_hangup() -> tty->hangup() -> rs_hangup() * */static void do_serial_hangup(void *private_){ struct sgi_serial *info = (struct sgi_serial *) private_; struct tty_struct *tty; tty = info->tty; if (!tty) return; tty_hangup(tty);}static int startup(struct sgi_serial * info){ volatile unsigned char junk; unsigned long flags; if (info->flags & ZILOG_INITIALIZED) return 0; if (!info->xmit_buf) { info->xmit_buf = (unsigned char *) get_free_page(GFP_KERNEL); if (!info->xmit_buf) return -ENOMEM; } save_flags(flags); cli();#ifdef SERIAL_DEBUG_OPEN printk("starting up ttys%d (irq %d)...\n", info->line, info->irq);#endif /* * Clear the FIFO buffers and disable them * (they will be reenabled in change_speed()) */ ZS_CLEARFIFO(info->zs_channel); info->xmit_fifo_size = 1; /* * Clear the interrupt registers. */ udelay(2); info->zs_channel->control = ERR_RES; junk = ioc_icontrol->istat0; udelay(2); info->zs_channel->control = RES_H_IUS; junk = ioc_icontrol->istat0; /* * Now, initialize the Zilog */ zs_rtsdtr(info, 1); /* * Finally, enable sequencing and interrupts */ info->curregs[1] |= (info->curregs[1] & ~0x18) | (EXT_INT_ENAB|INT_ALL_Rx); info->pendregs[1] = info->curregs[1]; info->curregs[3] |= (RxENABLE | Rx8); info->pendregs[3] = info->curregs[3]; /* We enable Tx interrupts as needed. */ info->curregs[5] |= (TxENAB | Tx8); info->pendregs[5] = info->curregs[5]; info->curregs[9] |= (NV | MIE); info->pendregs[9] = info->curregs[9]; write_zsreg(info->zs_channel, 3, info->curregs[3]); write_zsreg(info->zs_channel, 5, info->curregs[5]); write_zsreg(info->zs_channel, 9, info->curregs[9]); /* * And clear the interrupt registers again for luck. */ udelay(2); info->zs_channel->control = ERR_RES; junk = ioc_icontrol->istat0; udelay(2); info->zs_channel->control = RES_H_IUS; junk = ioc_icontrol->istat0; if (info->tty) clear_bit(TTY_IO_ERROR, &info->tty->flags); info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; /* * and set the speed of the serial port */ change_speed(info); info->flags |= ZILOG_INITIALIZED; restore_flags(flags); return 0;}/* * This routine will shutdown a serial port; interrupts are disabled, and * DTR is dropped if the hangup on close termio flag is on. */static void shutdown(struct sgi_serial * info){ unsigned long flags; if (!(info->flags & ZILOG_INITIALIZED)) return;#ifdef SERIAL_DEBUG_OPEN printk("Shutting down serial port %d (irq %d)....", info->line, info->irq);#endif save_flags(flags); cli(); /* Disable interrupts */ if (info->xmit_buf) { free_page((unsigned long) info->xmit_buf); info->xmit_buf = 0; } if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); info->flags &= ~ZILOG_INITIALIZED; restore_flags(flags);}/* * This routine is called to set the UART divisor registers to match * the specified baud rate for a serial port. */static void change_speed(struct sgi_serial *info){ unsigned short port; unsigned cflag; int i; int brg; if (!info->tty || !info->tty->termios) return; cflag = info->tty->termios->c_cflag; if (!(port = info->port)) return; i = cflag & CBAUD; if (i & CBAUDEX) { /* XXX CBAUDEX is not obeyed. * It is impossible at a 32bits SPARC. * But we have to report this to user ... someday. */ i = B9600; } if (i == 0) { /* XXX B0, hangup the line. */ do_serial_hangup(info); } else if (baud_table[i]) { info->zs_baud = baud_table[i]; info->clk_divisor = 16; info->curregs[4] = X16CLK; info->curregs[11] = TCBR | RCBR; brg = BPS_TO_BRG(info->zs_baud, ZS_CLOCK/info->clk_divisor); info->curregs[12] = (brg & 255); info->curregs[13] = ((brg >> 8) & 255); info->curregs[14] = BRENABL; } /* byte size and parity */ switch (cflag & CSIZE) { case CS5: info->curregs[3] &= ~(0xc0); info->curregs[3] |= Rx5; info->pendregs[3] = info->curregs[3]; info->curregs[5] &= ~(0xe0); info->curregs[5] |= Tx5; info->pendregs[5] = info->curregs[5]; break; case CS6: info->curregs[3] &= ~(0xc0); info->curregs[3] |= Rx6; info->pendregs[3] = info->curregs[3]; info->curregs[5] &= ~(0xe0); info->curregs[5] |= Tx6; info->pendregs[5] = info->curregs[5]; break; case CS7: info->curregs[3] &= ~(0xc0); info->curregs[3] |= Rx7; info->pendregs[3] = info->curregs[3]; info->curregs[5] &= ~(0xe0); info->curregs[5] |= Tx7; info->pendregs[5] = info->curregs[5]; break; case CS8: default: /* defaults to 8 bits */ info->curregs[3] &= ~(0xc0); info->curregs[3] |= Rx8; info->pendregs[3] = info->curregs[3]; info->curregs[5] &= ~(0xe0); info->curregs[5] |= Tx8; info->pendregs[5] = info->curregs[5]; break; } info->curregs[4] &= ~(0x0c); if (cflag & CSTOPB) { info->curregs[4] |= SB2; } else { info->curregs[4] |= SB1; } info->pendregs[4] = info->curregs[4]; if (cflag & PARENB) { info->curregs[4] |= PAR_ENA; info->pendregs[4] |= PAR_ENA; } else { info->curregs[4] &= ~PAR_ENA; info->pendregs[4] &= ~PAR_ENA; } if (!(cflag & PARODD)) { info->curregs[4] |= PAR_EVEN; info->pendregs[4] |= PAR_EVEN; } else { info->curregs[4] &= ~PAR_EVEN; info->pendregs[4] &= ~PAR_EVEN; } /* Load up the new values */ load_zsregs(info->zs_channel, info->curregs); return;}/* This is for console output over ttya/ttyb */static void zs_cons_put_char(char ch){ struct sgi_zschannel *chan = zs_conschan; volatile unsigned char junk; int flags, loops = 0; save_flags(flags); cli(); while(((junk = chan->control) & Tx_BUF_EMP)==0 && loops < 10000) { loops++; udelay(2); } udelay(2); chan->data = ch; junk = ioc_icontrol->istat0; restore_flags(flags);}/* * This is the more generic put_char function for the driver. * In earlier versions of this driver, "rs_put_char" was the * name of the console-specific fucntion, now called zs_cons_put_char */static void rs_put_char(struct tty_struct *tty, char ch){ struct sgi_zschannel *chan = ((struct sgi_serial *)tty->driver_data)->zs_channel; volatile unsigned char junk; int flags, loops = 0; save_flags(flags); cli(); while(((junk = chan->control) & Tx_BUF_EMP)==0 && loops < 10000) { loops++; udelay(2); } udelay(2); chan->data = ch; junk = ioc_icontrol->istat0; restore_flags(flags);}/* These are for receiving and sending characters under the kgdb * source level kernel debugger. */int putDebugChar(char kgdb_char){ struct sgi_zschannel *chan = zs_kgdbchan; volatile unsigned char junk; unsigned long flags; save_flags(flags); cli(); udelay(2); while((chan->control & Tx_BUF_EMP)==0) udelay(2); udelay(2); chan->data = kgdb_char; junk = ioc_icontrol->istat0; restore_flags(flags); return 1;}char getDebugChar(void){ struct sgi_zschannel *chan = zs_kgdbchan; unsigned char junk; while((chan->control & Rx_CH_AV)==0) udelay(2); junk = ioc_icontrol->istat0; udelay(2); return chan->data;}/* * Fair output driver allows a process to speak. */static void rs_fair_output(void){ int left; /* Output no more than that */ unsigned long flags; struct sgi_serial *info = zs_consinfo; volatile unsigned char junk; char c; if (info == 0) return; if (info->xmit_buf == 0) return; save_flags(flags); cli(); left = info->xmit_cnt; while (left != 0) { c = info->xmit_buf[info->xmit_tail]; info->xmit_tail = (info->xmit_tail+1) & (SERIAL_XMIT_SIZE-1); info->xmit_cnt--; restore_flags(flags); zs_cons_put_char(c); save_flags(flags); cli(); left = MIN(info->xmit_cnt, left-1); } /* Last character is being transmitted now (hopefully). */ udelay(2); zs_conschan->control = RES_Tx_P; junk = ioc_icontrol->istat0; restore_flags(flags); return;}static int rs_write(struct tty_struct * tty, int from_user, const unsigned char *buf, int count){ int c, total = 0; struct sgi_serial *info = (struct sgi_serial *)tty->driver_data; unsigned long flags; if (serial_paranoia_check(info, tty->device, "rs_write")) return 0; if (!tty || !info->xmit_buf) return 0; save_flags(flags); while (1) { cli(); c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, SERIAL_XMIT_SIZE - info->xmit_head)); if (c <= 0) break; if (from_user) { down(&tmp_buf_sem); copy_from_user(tmp_buf, buf, c); c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1, SERIAL_XMIT_SIZE - info->xmit_head)); memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c); up(&tmp_buf_sem); } else memcpy(info->xmit_buf + info->xmit_head, buf, c); info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1); info->xmit_cnt += c; restore_flags(flags); buf += c; count -= c; total += c; } if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped) { /* * The above test used to include the condition * "&& !(info->curregs[5] & TxENAB)", but there * is reason to suspect that it is never statisfied * when the port is running. The problem may in fact * have been masked by the fact that, if O_POST is set, * there is always a rs_flush_xx operation following the * rs_write, and the flush ignores that condition when * it kicks off the transmit. */ /* Enable transmitter */ info->curregs[1] |= TxINT_ENAB|EXT_INT_ENAB; info->pendregs[1] |= TxINT_ENAB|EXT_INT_ENAB; write_zsreg(info->zs_channel, 1, info->curregs[1]); info->curregs[5] |= TxENAB; info->pendregs[5] |= TxENAB; write_zsreg(info->zs_channel, 5, info->curregs[5]); /* * The following code is imported from the 2.3.6 Sun sbus zs.c * driver, of which an earlier version served as the basis * for sgiserial.c. Perhaps due to changes over time in * the line discipline code, ns_write()s with from_user * set would not otherwise actually kick-off output in * Linux 2.2.x or later. Maybe it never really worked. */ rs_put_char(tty, info->xmit_buf[info->xmit_tail++]); info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1); info->xmit_cnt--; } restore_flags(flags); return total;}static int rs_write_room(struct tty_struct *tty){ struct sgi_serial *info = (struct sgi_serial *)tty->driver_data; int ret; if (serial_paranoia_check(info, tty->device, "rs_write_room")) return 0; ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1; if (ret < 0) ret = 0; return ret;}static int rs_chars_in_buffer(struct tty_struct *tty){ struct sgi_serial *info = (struct sgi_serial *)tty->driver_data; if (serial_paranoia_check(info, tty->device, "rs_chars_in_buffer")) return 0; return info->xmit_cnt;}static void rs_flush_buffer(struct tty_struct *tty){ struct sgi_serial *info = (struct sgi_serial *)tty->driver_data; if (serial_paranoia_check(info, tty->device, "rs_flush_buffer")) return; cli(); info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; sti(); wake_up_interruptible(&tty->write_wait); if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup)(tty);}static void rs_flush_chars(struct tty_struct *tty){ struct sgi_serial *info = (struct sgi_serial *)tty->driver_data; unsigned long flags; if (serial_paranoia_check(info, tty->device, "rs_flush_chars")) return; if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped || !info->xmit_buf) return; /* Enable transmitter */ save_flags(flags); cli(); info->curregs[1] |= TxINT_ENAB|EXT_INT_ENAB; info->pendregs[1] |= TxINT_ENAB|EXT_INT_ENAB; write_zsreg(info->zs_channel, 1, info->curregs[1]); info->curregs[5] |= TxENAB; info->pendregs[5] |= TxENAB; write_zsreg(info->zs_channel, 5, info->curregs[5]); /* * Send a first (bootstrapping) character. A best solution is * to call transmit_chars() here which handles output in a * generic way. Current transmit_chars() not only transmits, * but resets interrupts also what we do not desire here. * XXX Discuss with David. */ if (info->zs_channel->control & Tx_BUF_EMP) {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -