📄 sunserial.c
字号:
return; zs_intreg = read_zsreg(info->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 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); } info=info->zs_next; /* 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; zs_intreg = read_zsreg(info->zs_channel, 3); /* *** Chip 2 *** */ /* Channel A -- /dev/kbd, pass communication to keyboard driver */ 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); } info=info->zs_next; /* Channel B -- /dev/mouse, pass communication to mouse driver */ 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); } return;}/* * ------------------------------------------------------------------- * 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 sun_serial *info = (struct sun_serial *) private_; struct tty_struct *tty; tty = info->tty; if (!tty) return; if (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 sun_serial *info = (struct sun_serial *) private_; struct tty_struct *tty; tty = info->tty; if (!tty) return; tty_hangup(tty);}/* * This subroutine is called when the RS_TIMER goes off. It is used * by the serial driver to handle ports that do not have an interrupt * (irq=0). This doesn't work at all for 16450's, as a sun has a Z8530. */ static void rs_timer(void){ printk("rs_timer called\n"); prom_halt(); return;}static int startup(struct sun_serial * info){ 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)...", 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. */ info->zs_channel->control = ERR_RES; udelay(5); info->zs_channel->control = RES_H_IUS; udelay(5); /* * 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. */ info->zs_channel->control = ERR_RES; udelay(5); info->zs_channel->control = RES_H_IUS; udelay(5); if (info->tty) clear_bit(TTY_IO_ERROR, &info->tty->flags); info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; /* * Set up serial timers... */#if 0 /* Works well and stops the machine. */ timer_table[RS_TIMER].expires = jiffies + 2; timer_active |= 1 << RS_TIMER;#endif /* * 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 sun_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 sun_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; } 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] = BRSRC | 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 mouse/keyboard output. * XXX mouse output??? can we send it commands??? XXX */void kbd_put_char(unsigned char ch){ struct sun_zschannel *chan = zs_kbdchan; int flags, loops = 0; if(!chan) return; save_flags(flags); cli(); while((chan->control & Tx_BUF_EMP)==0 && loops < 10000) { loops++; udelay(5); } chan->data = ch; udelay(5); restore_flags(flags);}void mouse_put_char(char ch){ struct sun_zschannel *chan = zs_mousechan; int flags, loops = 0; if(!chan) return; save_flags(flags); cli(); while((chan->control & Tx_BUF_EMP)==0 && loops < 10000) { loops++; udelay(5); } chan->data = ch; udelay(5); restore_flags(flags);}/* This is for console output over ttya/ttyb */static void rs_put_char(char ch){ struct sun_zschannel *chan = zs_conschan; int flags, loops = 0; if(!chan) return; save_flags(flags); cli(); while((chan->control & Tx_BUF_EMP)==0 && loops < 10000) { loops++; udelay(5); } chan->data = ch; udelay(5); restore_flags(flags);}/* These are for receiving and sending characters under the kgdb * source level kernel debugger. */void putDebugChar(char kgdb_char){ struct sun_zschannel *chan = zs_kgdbchan; while((chan->control & Tx_BUF_EMP)==0) udelay(5); chan->data = kgdb_char;}char getDebugChar(void){ struct sun_zschannel *chan = zs_kgdbchan; while((chan->control & Rx_CH_AV)==0) barrier(); 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 sun_serial *info = zs_consinfo; 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); rs_put_char(c); save_flags(flags); cli(); left = MIN(info->xmit_cnt, left-1); } /* Last character is being transmitted now (hopefully). */ zs_conschan->control = RES_Tx_P; udelay(5); restore_flags(flags); return;}/* * zs_console_print is registered for printk. */static void zs_console_print(const char *p){ char c; while((c=*(p++)) != 0) { if(c == '\n') rs_put_char('\r'); rs_put_char(c); } /* Comment this if you want to have a strict interrupt-driven output */ rs_fair_output(); return;}static void rs_flush_chars(struct tty_struct *tty){ struct sun_serial *info = (struct sun_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) { /* Send char */ info->zs_channel->data = info->xmit_buf[info->xmit_tail++]; udelay(5); info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1); info->xmit_cnt--; } restore_flags(flags);}static int rs_write(struct tty_struct * tty, int from_user, const unsigned char *buf, int count){ int c, total = 0; struct sun_serial *info = (struct sun_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); memcpy_fromfs(tmp_buf, buf, c);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -