?? zs.c
字號:
ZSLOG(REGDATA, ch, 0); ch &= info->parity_mask; ZSDELAY(); /* If this is the console keyboard, we need to handle * L1-A's here. */ if (info->cons_keyb) { if (ch == SUNKBD_RESET) { l1a_state.kbd_id = 1; l1a_state.l1_down = 0; } else if (l1a_state.kbd_id) { l1a_state.kbd_id = 0; } else if (ch == SUNKBD_L1) { l1a_state.l1_down = 1; } else if (ch == (SUNKBD_L1|SUNKBD_UP)) { l1a_state.l1_down = 0; } else if (ch == SUNKBD_A && l1a_state.l1_down) { /* whee... */ batten_down_hatches(); /* Continue execution... */ l1a_state.l1_down = 0; l1a_state.kbd_id = 0; return; } sunkbd_inchar(ch, regs); goto next_char; } if (info->cons_mouse) { sun_mouse_inbyte(ch, 0); goto next_char; } if (info->is_cons) { if (ch == 0) { /* whee, break received */ batten_down_hatches(); /* Continue execution... */ return; } /* It is a 'keyboard interrupt' ;-) */ wake_up(&keypress_wait); }#ifndef __sparc_v9__ /* Look for kgdb 'stop' character, consult the gdb * documentation for remote target debugging and * arch/sparc/kernel/sparc-stub.c to see how all this works. */ if (info->kgdb_channel && (ch =='\003')) { breakpoint(); return; }#endif if (!tty) return; do_queue_task++; if (tty->flip.count >= TTY_FLIPBUF_SIZE) break; tty->flip.count++; if (r1 & PAR_ERR) *tty->flip.flag_buf_ptr++ = TTY_PARITY; else if (r1 & Rx_OVR) *tty->flip.flag_buf_ptr++ = TTY_OVERRUN; else if (r1 & CRC_ERR) *tty->flip.flag_buf_ptr++ = TTY_FRAME; else *tty->flip.flag_buf_ptr++ = 0; *tty->flip.char_buf_ptr++ = ch; next_char: { unsigned char stat; /* Check if we have another character... */ stat = sbus_readb(&info->zs_channel->control); ZSDELAY(); ZSLOG(REGCTRL, stat, 0); if (!(stat & Rx_CH_AV)) break; } } if (do_queue_task != 0) queue_task(&tty->flip.tqueue, &tq_timer);}static void transmit_chars(struct sun_serial *info){ struct tty_struct *tty = info->tty; if (info->x_char) { /* Send next char */ zs_put_char(info->zs_channel, info->x_char); info->x_char = 0; return; } if ((info->xmit_cnt <= 0) || (tty != 0 && tty->stopped)) { /* That's peculiar... */ sbus_writeb(RES_Tx_P, &info->zs_channel->control); ZSDELAY(); ZS_WSYNC(info->zs_channel); ZSLOG(REGCTRL, RES_Tx_P, 1); return; } /* Send char */ zs_put_char(info->zs_channel, info->xmit_buf[info->xmit_tail++]); info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1); info->xmit_cnt--; if (info->xmit_cnt < WAKEUP_CHARS) zs_sched_event(info, RS_EVENT_WRITE_WAKEUP); if (info->xmit_cnt <= 0) { sbus_writeb(RES_Tx_P, &info->zs_channel->control); ZSDELAY(); ZS_WSYNC(info->zs_channel); ZSLOG(REGCTRL, RES_Tx_P, 1); }}static void status_handle(struct sun_serial *info){ unsigned char status; /* Get status from Read Register 0 */ status = sbus_readb(&info->zs_channel->control); ZSDELAY(); ZSLOG(REGCTRL, status, 0); /* Clear status condition... */ sbus_writeb(RES_EXT_INT, &info->zs_channel->control); ZSDELAY(); ZS_WSYNC(info->zs_channel); ZSLOG(REGCTRL, RES_EXT_INT, 1);#if 0 if (status & DCD) { if ((info->tty->termios->c_cflag & CRTSCTS) && ((info->curregs[3] & AUTO_ENAB)==0)) { info->curregs[3] |= AUTO_ENAB; write_zsreg(info->zs_channel, 3, info->curregs[3]); } } else { if ((info->curregs[3] & AUTO_ENAB)) { info->curregs[3] &= ~AUTO_ENAB; write_zsreg(info->zs_channel, 3, info->curregs[3]); } }#endif /* Whee, if this is console input and this is a * 'break asserted' status change interrupt, call * the boot prom. */ if (status & BRK_ABRT) { if (info->break_abort) batten_down_hatches(); if (info->cons_mouse) sun_mouse_inbyte(0, 1); } /* XXX Whee, put in a buffer somewhere, the status information * XXX whee whee whee... Where does the information go... */ return;}/* * This is the serial driver's generic interrupt routine */void zs_interrupt(int irq, void *dev_id, struct pt_regs * regs){ struct sun_serial *info; int i; info = (struct sun_serial *)dev_id; ZSLOG(REGIRQ, 0, 0); for (i = 0; i < NUM_SERIAL; i++) { unsigned char r3 = read_zsreg(info->zs_channel, 3); /* Channel A -- /dev/ttya or /dev/kbd, could be the console */ if (r3 & (CHAEXT | CHATxIP | CHARxIP)) { sbus_writeb(RES_H_IUS, &info->zs_channel->control); ZSDELAY(); ZS_WSYNC(info->zs_channel); ZSLOG(REGCTRL, RES_H_IUS, 1); if (r3 & CHARxIP) receive_chars(info, regs); if (r3 & CHAEXT) status_handle(info); if (r3 & CHATxIP) transmit_chars(info); } /* Channel B -- /dev/ttyb or /dev/mouse, could be the console */ info = info->zs_next; if (r3 & (CHBEXT | CHBTxIP | CHBRxIP)) { sbus_writeb(RES_H_IUS, &info->zs_channel->control); ZSDELAY(); ZS_WSYNC(info->zs_channel); ZSLOG(REGCTRL, RES_H_IUS, 1); if (r3 & CHBRxIP) receive_chars(info, regs); if (r3 & CHBEXT) status_handle(info); if (r3 & CHBTxIP) transmit_chars(info); } info = info->zs_next; }}/* * ------------------------------------------------------------------- * 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 * zs_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 zs_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 (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() -> zs_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;#ifdef SERIAL_DEBUG_OPEN printk("do_serial_hangup<%p: tty-%d\n", __builtin_return_address(0), info->line);#endif tty_hangup(tty);}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 tty-%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. */ sbus_writeb(ERR_RES, &info->zs_channel->control); ZSDELAY(); ZS_WSYNC(info->zs_channel); ZSLOG(REGCTRL, ERR_RES, 1); sbus_writeb(RES_H_IUS, &info->zs_channel->control); ZSDELAY(); ZS_WSYNC(info->zs_channel); ZSLOG(REGCTRL, RES_H_IUS, 1); /* * Now, initialize the Zilog */ zs_rtsdtr(info, 1); /* * Finally, enable sequencing and interrupts */ info->curregs[1] |= (info->curregs[1] & ~(RxINT_MASK)) | (EXT_INT_ENAB | INT_ALL_Rx); info->curregs[3] |= (RxENAB | Rx8); /* We enable Tx interrupts as needed. */ info->curregs[5] |= (TxENAB | Tx8); info->curregs[9] |= (NV | MIE); 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. */ sbus_writeb(ERR_RES, &info->zs_channel->control); ZSDELAY(); ZS_WSYNC(info->zs_channel); ZSLOG(REGCTRL, ERR_RES, 1); sbus_writeb(RES_H_IUS, &info->zs_channel->control); ZSDELAY(); ZS_WSYNC(info->zs_channel); ZSLOG(REGCTRL, RES_H_IUS, 1); 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 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 cflag; int baud, quot = 0; int brg; if (!info->tty || !info->tty->termios) return; cflag = info->tty->termios->c_cflag; if (!info->port) return; baud = tty_get_baud_rate(info->tty); if ((baud == 38400) && ((info->flags & ZILOG_SPD_MASK) == ZILOG_SPD_CUST)) quot = info->custom_divisor; if (quot) { info->zs_baud = info->baud_base / quot; 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 | BRENAB; zs_rtsdtr(info, 1); } else if (baud) { info->zs_baud = baud; 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 | BRENAB; zs_rtsdtr(info, 1); } else { zs_rtsdtr(info, 0); return; } /* byte size and parity */ switch (cflag & CSIZE) { case CS5: info->curregs[3] &= ~(RxN_MASK); info->curregs[3] |= Rx5; info->curregs[5] &= ~(TxN_MASK); info->curregs[5] |= Tx5; info->parity_mask = 0x1f; break; case CS6: info->curregs[3] &= ~(RxN_MASK); info->curregs[3] |= Rx6; info->curregs[5] &= ~(TxN_MASK); info->curregs[5] |= Tx6; info->parity_mask = 0x3f; break; case CS7: info->curregs[3] &= ~(RxN_MASK); info->curregs[3] |= Rx7; info->curregs[5] &= ~(TxN_MASK); info->curregs[5] |= Tx7; info->parity_mask = 0x7f; break; case CS8: default: /* defaults to 8 bits */ info->curregs[3] &= ~(RxN_MASK); info->curregs[3] |= Rx8; info->curregs[5] &= ~(TxN_MASK); info->curregs[5] |= Tx8; info->parity_mask = 0xff; break; } info->curregs[4] &= ~(0x0c); if (cflag & CSTOPB) { info->curregs[4] |= SB2; } else { info->curregs[4] |= SB1; } if (cflag & PARENB) { info->curregs[4] |= PAR_ENAB; } else { info->curregs[4] &= ~PAR_ENAB; } if (!(cflag & PARODD)) { info->curregs[4] |= PAR_EVEN; } else { info->curregs[4] &= ~PAR_EVEN; } /* Load up the new values */ load_zsregs(info, info->curregs); return;}/* This is for mouse/keyboard output. * XXX mouse output??? can we send it commands??? XXX */static void kbd_put_char(unsigned char ch){ struct sun_zschannel *chan = zs_kbdchan; unsigned long flags; if(!chan) return; save_flags(flags); cli(); zs_put_char(chan, ch);
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -