📄 serial.c
字号:
save_flags_cli(flags); /* Disable interrupts */ /* * clear delta_msr_wait queue to avoid mem leaks: we may free the irq * here so the queue might never be waken up */ wake_up_interruptible(&info->delta_msr_wait); /* * First unlink the serial port from the IRQ chain... */ if (info->next_port) info->next_port->prev_port = info->prev_port; if (info->prev_port) info->prev_port->next_port = info->next_port; else IRQ_ports[info->irq] = info->next_port; figure_IRQ_timeout(info->irq); /* * Free the IRQ, if necessary */ if (info->irq && (!IRQ_ports[info->irq] || !IRQ_ports[info->irq]->next_port)) { if (IRQ_ports[info->irq]) { free_irq(info->irq, NULL); retval = request_irq(info->irq, rs_interrupt_single, IRQ_T(info), "serial", NULL); if (retval) printk("serial shutdown: request_irq: error %d" " Couldn't reacquire IRQ.\n", retval); } else free_irq(info->irq, NULL); } if (info->xmit_buf) { free_page((unsigned long) info->xmit_buf); info->xmit_buf = 0; } info->IER = 0; serial_outp(info, UART_IER, 0x00); /* disable all intrs */ if (info->flags & ASYNC_FOURPORT) { /* reset interrupts on the AST Fourport board */ (void) inb((info->port & 0xFE0) | 0x01F); } if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) { info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS); info->MCR_noint &= ~(UART_MCR_DTR|UART_MCR_RTS); } serial_outp(info, UART_MCR, info->MCR_noint); /* disable FIFO's */ serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT)); (void)serial_in(info, UART_RX); /* read data port to reset things */ if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); info->flags &= ~ASYNC_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 async_struct *info){ unsigned short port; int quot = 0; unsigned cflag,cval,fcr; int i; if (!info->tty || !info->tty->termios) return; cflag = info->tty->termios->c_cflag; if (!(port = info->port)) return; i = cflag & CBAUD; if (i & CBAUDEX) { i &= ~CBAUDEX; if (i < 1 || i > 2) info->tty->termios->c_cflag &= ~CBAUDEX; else i += 15; } if (i == 15) { if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) i += 1; if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) i += 2; if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) quot = info->custom_divisor; } if (quot) { info->timeout = ((info->xmit_fifo_size*HZ*15*quot) / info->baud_base) + 2; } else if (baud_table[i] == 134) { quot = (2*info->baud_base / 269); info->timeout = (info->xmit_fifo_size*HZ*30/269) + 2; } else if (baud_table[i]) { quot = info->baud_base / baud_table[i]; info->timeout = (info->xmit_fifo_size*HZ*15/baud_table[i]) + 2; } else { quot = 0; info->timeout = 0; } if (quot) { info->MCR |= UART_MCR_DTR; info->MCR_noint |= UART_MCR_DTR; cli(); serial_out(info, UART_MCR, info->MCR); sti(); } else { info->MCR &= ~UART_MCR_DTR; info->MCR_noint &= ~UART_MCR_DTR; cli(); serial_out(info, UART_MCR, info->MCR); sti(); return; } /* byte size and parity */ switch (cflag & CSIZE) { case CS5: cval = 0x00; break; case CS6: cval = 0x01; break; case CS7: cval = 0x02; break; case CS8: cval = 0x03; break; default: cval = 0x00; break; /* too keep GCC shut... */ } if (cflag & CSTOPB) { cval |= 0x04; } if (cflag & PARENB) cval |= UART_LCR_PARITY; if (!(cflag & PARODD)) cval |= UART_LCR_EPAR; if (info->type == PORT_16550A) { if ((info->baud_base / quot) < 2400) fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1; else fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8; } else if (info->type == PORT_16650) { /* * On the 16650, we disable the FIFOs altogether * because of a design bug in how the implement * things. We could support it by completely changing * how we handle the interrupt driver, but not today.... * * N.B. Because there's no way to set a FIFO trigger * at 1 char, we'd probably disable at speed below * 2400 baud anyway... */ fcr = 0; } else fcr = 0; /* CTS flow control flag and modem status interrupts */ info->IER &= ~UART_IER_MSI; if (cflag & CRTSCTS) { info->flags |= ASYNC_CTS_FLOW; info->IER |= UART_IER_MSI; } else info->flags &= ~ASYNC_CTS_FLOW; if (cflag & CLOCAL) info->flags &= ~ASYNC_CHECK_CD; else { info->flags |= ASYNC_CHECK_CD; info->IER |= UART_IER_MSI; } serial_out(info, UART_IER, info->IER); /* * Set up parity check flag */#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK)) info->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR; if (I_INPCK(info->tty)) info->read_status_mask |= UART_LSR_FE | UART_LSR_PE; if (I_BRKINT(info->tty) || I_PARMRK(info->tty)) info->read_status_mask |= UART_LSR_BI; info->ignore_status_mask = 0;#if 0 /* This should be safe, but for some broken bits of hardware... */ if (I_IGNPAR(info->tty)) { info->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; info->read_status_mask |= UART_LSR_PE | UART_LSR_FE; }#endif if (I_IGNBRK(info->tty)) { info->ignore_status_mask |= UART_LSR_BI; info->read_status_mask |= UART_LSR_BI; /* * If we're ignore parity and break indicators, ignore * overruns too. (For real raw support). */ if (I_IGNPAR(info->tty)) { info->ignore_status_mask |= UART_LSR_OE | UART_LSR_PE | UART_LSR_FE; info->read_status_mask |= UART_LSR_OE | UART_LSR_PE | UART_LSR_FE; } } cli(); serial_outp(info, UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */ serial_outp(info, UART_DLL, quot & 0xff); /* LS of divisor */ serial_outp(info, UART_DLM, quot >> 8); /* MS of divisor */ serial_outp(info, UART_LCR, cval); /* reset DLAB */ serial_outp(info, UART_FCR, fcr); /* set fcr */ sti();}static void rs_put_char(struct tty_struct *tty, unsigned char ch){ struct async_struct *info = (struct async_struct *)tty->driver_data; unsigned long flags; if (serial_paranoia_check(info, tty->device, "rs_put_char")) return; if (!tty || !info->xmit_buf) return; save_flags_cli (flags); if (info->xmit_cnt >= SERIAL_XMIT_SIZE - 1) { restore_flags(flags); return; } info->xmit_buf[info->xmit_head++] = ch; info->xmit_head &= SERIAL_XMIT_SIZE-1; info->xmit_cnt++; restore_flags(flags);}static void rs_flush_chars(struct tty_struct *tty){ struct async_struct *info = (struct async_struct *)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; save_flags_cli (flags); info->IER |= UART_IER_THRI; serial_out(info, UART_IER, info->IER); 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 async_struct *info = (struct async_struct *)tty->driver_data; unsigned long flags; if (serial_paranoia_check(info, tty->device, "rs_write")) return 0; if (!tty || !info->xmit_buf || !tmp_buf) return 0; if (from_user) down(&tmp_buf_sem); 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) { memcpy_fromfs(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); } 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 (from_user) up(&tmp_buf_sem); if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped && !(info->IER & UART_IER_THRI)) { info->IER |= UART_IER_THRI; serial_out(info, UART_IER, info->IER); } restore_flags(flags); return total;}static int rs_write_room(struct tty_struct *tty){ struct async_struct *info = (struct async_struct *)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 async_struct *info = (struct async_struct *)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 async_struct *info = (struct async_struct *)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 async_struct *info = (struct async_struct *)tty->driver_data;#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)) info->x_char = STOP_CHAR(tty); info->MCR &= ~UART_MCR_RTS; info->MCR_noint &= ~UART_MCR_RTS; cli(); serial_out(info, UART_MCR, info->MCR); sti();}static void rs_unthrottle(struct tty_struct * tty){ struct async_struct *info = (struct async_struct *)tty->driver_data;#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)) { if (info->x_char) info->x_char = 0; else info->x_char = START_CHAR(tty); } info->MCR |= UART_MCR_RTS; info->MCR_noint |= UART_MCR_RTS; cli(); serial_out(info, UART_MCR, info->MCR); sti();}/* * ------------------------------------------------------------ * rs_ioctl() and friends * ------------------------------------------------------------ */static int get_serial_info(struct async_struct * 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; tmp.hub6 = info->hub6; memcpy_tofs(retinfo,&tmp,sizeof(*retinfo)); return 0;}static int set_serial_info(struct async_struct * info, struct serial_struct * new_info){ struct serial_struct new_serial; struct async_struct old_info; unsigned int i,change_irq,change_port; int retval = 0; if (!new_info) return -EFAULT; memcpy_fromfs(&new_serial,new_info,sizeof(new_serial)); old_info = *info; change_irq = new_serial.irq != info->irq; change_port = (new_serial.port != info->port) || (new_serial.hub6 != info->hub6); if (!suser()) { if (change_irq || change_port || (new_serial.baud_base != info->baud_base) || (new_serial.type != info->type) || (new_serial.close_delay != info->close_delay) || ((new_serial.flags & ~ASYNC_USR_MASK) != (info->flags & ~ASYNC_USR_MASK))) return -EPERM; info->flags = ((info->flags & ~ASYNC_USR_MASK) | (new_serial.flags & ASYNC_USR_MASK)); info->custom_divisor = new_serial.custom_divisor; goto check_and_exit; }#if 0 if (new_serial.irq == 2) new_serial.irq = 9; if ((new_serial.irq > 15) || (new_serial.port > 0xffff) || (new_serial.type < PORT_UNKNOWN) || (new_serial.type > PORT_MAX)) { return -EINVAL; }#else if ((new_serial.irq > 31) || (new_serial.type < PORT_UNKNOWN) || (new_serial.type > PORT_MAX)) { return -EINVAL; }#endif /* Make sure address is not already in use */ if (new_serial.type) { for (i = 0 ; i < NR_PORTS; i++) if ((info != &rs_table[i]) && (rs_table[i].port == new_serial.port) && rs_table[i].type) return -EADDRINUSE; } if ((change_port || change_irq) && (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 & ~ASYNC_FLAGS) | (new_serial.flags & ASYNC_FLAGS)); info->custom_divisor = new_serial.custom_divisor; info->type = new_serial.type; info->close_delay = new_serial.close_delay * HZ/100; info->closing_wait = new_serial.closing_wait * HZ/100; release_region(info->port,8); if (change_port || change_irq) { /* * We need to shutdown the serial port at the old * port/irq combination. */ shutdown(info); info->irq = new_serial.irq; info->port = new_serial.port;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -