📄 zs.c
字号:
transmit_chars(info); }#endif restore_flags(flags);}/* * 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_zs_serial);}static void do_softint(void *private_){ struct dec_serial *info = (struct dec_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); }}static int startup(struct dec_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 receive FIFO. */ ZS_CLEARFIFO(info->zs_channel); info->xmit_fifo_size = 1; /* * Clear the interrupt registers. */ write_zsreg(info->zs_channel, 0, ERR_RES); write_zsreg(info->zs_channel, 0, RES_H_IUS); /* * Turn on RTS and DTR. */ zs_rtsdtr(info, 1); /* * Finally, enable sequencing and interrupts */ info->zs_channel->curregs[1] = (info->zs_channel->curregs[1] & ~0x18) | (EXT_INT_ENAB | INT_ALL_Rx | TxINT_ENAB); info->zs_channel->curregs[3] |= (RxENABLE | Rx8); info->zs_channel->curregs[5] |= (TxENAB | Tx8); info->zs_channel->curregs[15] |= (DCDIE | CTSIE | TxUIE | BRKIE); info->zs_channel->curregs[9] |= (VIS | MIE); write_zsreg(info->zs_channel, 1, info->zs_channel->curregs[1]); write_zsreg(info->zs_channel, 3, info->zs_channel->curregs[3]); write_zsreg(info->zs_channel, 5, info->zs_channel->curregs[5]); write_zsreg(info->zs_channel, 15, info->zs_channel->curregs[15]); write_zsreg(info->zs_channel, 9, info->zs_channel->curregs[9]); /* * And clear the interrupt registers again for luck. */ write_zsreg(info->zs_channel, 0, ERR_RES); write_zsreg(info->zs_channel, 0, RES_H_IUS); if (info->tty) clear_bit(TTY_IO_ERROR, &info->tty->flags); info->xmit_cnt = info->xmit_head = info->xmit_tail = 0; /* * Set the speed of the serial port */ change_speed(info); /* Save the current value of RR0 */ info->read_reg_zero = read_zsreg(info->zs_channel, 0); 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 dec_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; } info->zs_channel->curregs[1] = 0; write_zsreg(info->zs_channel, 1, info->zs_channel->curregs[1]); /* no interrupts */ info->zs_channel->curregs[3] &= ~RxENABLE; write_zsreg(info->zs_channel, 3, info->zs_channel->curregs[3]); info->zs_channel->curregs[5] &= ~TxENAB; write_zsreg(info->zs_channel, 5, info->zs_channel->curregs[5]); if (!info->tty || C_HUPCL(info->tty)) { info->zs_chan_a->curregs[5] &= ~(DTR | RTS); write_zsreg(info->zs_chan_a, 5, info->zs_chan_a->curregs[5]); } 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 dec_serial *info){ unsigned short port; unsigned cflag; int i; int brg; unsigned long flags; if (!info->tty || !info->tty->termios) return; cflag = info->tty->termios->c_cflag; if (!(port = info->port)) return; i = cflag & CBAUD; save_flags(flags); cli(); info->zs_baud = baud_table[i]; info->clk_divisor = 16; switch (info->zs_baud) { default: info->zs_channel->curregs[4] = X16CLK; brg = BPS_TO_BRG(info->zs_baud, ZS_CLOCK/info->clk_divisor); info->zs_channel->curregs[12] = (brg & 255); info->zs_channel->curregs[13] = ((brg >> 8) & 255); } /* byte size and parity */ info->zs_channel->curregs[3] &= ~RxNBITS_MASK; info->zs_channel->curregs[5] &= ~TxNBITS_MASK; switch (cflag & CSIZE) { case CS5: info->zs_channel->curregs[3] |= Rx5; info->zs_channel->curregs[5] |= Tx5; break; case CS6: info->zs_channel->curregs[3] |= Rx6; info->zs_channel->curregs[5] |= Tx6; break; case CS7: info->zs_channel->curregs[3] |= Rx7; info->zs_channel->curregs[5] |= Tx7; break; case CS8: default: /* defaults to 8 bits */ info->zs_channel->curregs[3] |= Rx8; info->zs_channel->curregs[5] |= Tx8; break; } info->zs_channel->curregs[4] &= ~(SB_MASK | PAR_ENA | PAR_EVEN); if (cflag & CSTOPB) { info->zs_channel->curregs[4] |= SB2; } else { info->zs_channel->curregs[4] |= SB1; } if (cflag & PARENB) { info->zs_channel->curregs[4] |= PAR_ENA; } if (!(cflag & PARODD)) { info->zs_channel->curregs[4] |= PAR_EVEN; } if (!(cflag & CLOCAL)) { if (!(info->zs_channel->curregs[15] & DCDIE)) info->read_reg_zero = read_zsreg(info->zs_channel, 0); info->zs_channel->curregs[15] |= DCDIE; } else info->zs_channel->curregs[15] &= ~DCDIE; if (cflag & CRTSCTS) { info->zs_channel->curregs[15] |= CTSIE; if ((read_zsreg(info->zs_channel, 0) & CTS) != 0) info->tx_stopped = 1; } else { info->zs_channel->curregs[15] &= ~CTSIE; info->tx_stopped = 0; } /* Load up the new values */ load_zsregs(info->zs_channel, info->zs_channel->curregs); restore_flags(flags);}static void rs_flush_chars(struct tty_struct *tty){ struct dec_serial *info = (struct dec_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 || info->tx_stopped || !info->xmit_buf) return; /* Enable transmitter */ save_flags(flags); cli(); transmit_chars(info); 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 dec_serial *info = (struct dec_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 && !info->tx_stopped && !info->tx_active) transmit_chars(info); restore_flags(flags); return total;}static int rs_write_room(struct tty_struct *tty){ struct dec_serial *info = (struct dec_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 dec_serial *info = (struct dec_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 dec_serial *info = (struct dec_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);}/* * ------------------------------------------------------------ * rs_throttle() * * This routine is called by the upper-layer tty layer to signal that * incoming characters should be throttled. * ------------------------------------------------------------ */static void rs_throttle(struct tty_struct * tty){ struct dec_serial *info = (struct dec_serial *)tty->driver_data; unsigned long flags;#ifdef SERIAL_DEBUG_THROTTLE char buf[64]; printk("throttle %s: %d....\n", _tty_name(tty, buf), tty->ldisc.chars_in_buffer(tty));#endif if (serial_paranoia_check(info, tty->device, "rs_throttle")) return; if (I_IXOFF(tty)) { save_flags(flags); cli(); info->x_char = STOP_CHAR(tty); if (!info->tx_active) transmit_chars(info); restore_flags(flags); } if (C_CRTSCTS(tty)) { /* * Here we want to turn off the RTS line. On Macintoshes, * we only get the DTR line, which goes to both DTR and * RTS on the modem. RTS doesn't go out to the serial * port socket. So you should make sure your modem is * set to ignore DTR if you're using CRTSCTS. */ save_flags(flags); cli(); info->zs_chan_a->curregs[5] &= ~(DTR | RTS); write_zsreg(info->zs_chan_a, 5, info->zs_chan_a->curregs[5]); restore_flags(flags); }}static void rs_unthrottle(struct tty_struct * tty){ struct dec_serial *info = (struct dec_serial *)tty->driver_data; unsigned long flags;#ifdef SERIAL_DEBUG_THROTTLE char buf[64]; printk("unthrottle %s: %d....\n", _tty_name(tty, buf), tty->ldisc.chars_in_buffer(tty));#endif if (serial_paranoia_check(info, tty->device, "rs_unthrottle")) return; if (I_IXOFF(tty)) { save_flags(flags); cli(); if (info->x_char) info->x_char = 0; else { info->x_char = START_CHAR(tty); if (!info->tx_active) transmit_chars(info); } restore_flags(flags); } if (C_CRTSCTS(tty)) { /* Assert RTS and DTR lines */ save_flags(flags); cli(); info->zs_chan_a->curregs[5] |= DTR | RTS; write_zsreg(info->zs_chan_a, 5, info->zs_chan_a->curregs[5]); restore_flags(flags); }}/* * ------------------------------------------------------------ * rs_ioctl() and friends * ------------------------------------------------------------ */static int get_serial_info(struct dec_serial * info, struct serial_struct * retinfo){ struct serial_struct tmp; if (!retinfo) return -EFAULT; memset(&tmp, 0, sizeof(tmp)); tmp.type = info->type; tmp.line = info->line; tmp.port = info->port; tmp.irq = info->irq; tmp.flags = info->flags; tmp.baud_base = info->baud_base; tmp.close_delay = info->close_delay; tmp.closing_wait = info->closing_wait; tmp.custom_divisor = info->custom_divisor; return copy_to_user(retinfo,&tmp,sizeof(*retinfo));}static int set_serial_info(struct dec_serial * info, struct serial_struct * new_info){ struct serial_struct new_serial; struct dec_serial old_info; int retval = 0; if (!new_info) return -EFAULT; copy_from_user(&new_serial,new_info,sizeof(new_serial)); old_info = *info; if (!suser()) { if ((new_serial.baud_base != info->baud_base) || (new_serial.type != info->type) || (new_serial.close_delay != info->close_delay) || ((new_serial.flags & ~ZILOG_USR_MASK) != (info->flags & ~ZILOG_USR_MASK))) return -EPERM; info->flags = ((info->flags & ~ZILOG_USR_MASK) | (new_serial.flags & ZILOG_USR_MASK)); info->custom_divisor = new_serial.custom_divisor; goto check_and_exit; } if (info->count > 1) return -EBUSY; /* * OK, past this point, all the error checking has been done. * At this point, we start making changes..... */ info->baud_base = new_serial.baud_base; info->flags = ((info->flags & ~ZILOG_FLAGS) | (new_serial.flags & ZILOG_FLAGS)); info->type = new_serial.type; info->close_delay = new_serial.close_delay; info->closing_wait = new_serial.closing_wait;check_and_exit: retval = startup(info); return retval;}/* * get_lsr_info - get line status register info * * Purpose: Let user call ioctl() to get info when the UART physically * is emptied. On bus types like RS485, the transmitter must * release the bus after transmitting. This must be done when * the transmit shift register is empty, not be done when the * transmit holding register is empty. This functionality * allows an RS485 driver to be written in user space. */static int get_lsr_info(struct dec_serial * info, unsigned int *value){ unsigned char status; cli(); status = read_zsreg(info->zs_channel, 0); sti(); put_user(status,value); return 0;}static int get_modem_info(struct dec_serial *info, unsigned int *value){ unsigned char control, status; unsigned int result; cli(); control = info->zs_chan_a->curregs[5]; status = read_zsreg(info->zs_channel, 0); sti();
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -