📄 serial.c
字号:
save_flags(flags); cli(); 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(flags); cli(); 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; }#ifndef CONFIG_COLDFIRE 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; }#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; info->hub6 = new_serial.hub6; } if(info->type != PORT_UNKNOWN) request_region(info->port,8,"serial(set)"); check_and_exit: if (!info->port || !info->type) return 0; if (info->flags & ASYNC_INITIALIZED) { if (((old_info.flags & ASYNC_SPD_MASK) != (info->flags & ASYNC_SPD_MASK)) || (old_info.custom_divisor != info->custom_divisor)) change_speed(info); } else 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 async_struct * info, unsigned int *value){ unsigned char status; unsigned int result; cli(); status = serial_in(info, UART_LSR); sti(); result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0); put_user(result,value); return 0;}static int get_modem_info(struct async_struct * info, unsigned int *value){ unsigned char control, status; unsigned int result; control = info->MCR; cli(); status = serial_in(info, UART_MSR); sti(); result = ((control & UART_MCR_RTS) ? TIOCM_RTS : 0) | ((control & UART_MCR_DTR) ? TIOCM_DTR : 0) | ((status & UART_MSR_DCD) ? TIOCM_CAR : 0) | ((status & UART_MSR_RI) ? TIOCM_RNG : 0) | ((status & UART_MSR_DSR) ? TIOCM_DSR : 0) | ((status & UART_MSR_CTS) ? TIOCM_CTS : 0); put_user(result,value); return 0;}static int set_modem_info(struct async_struct * info, unsigned int cmd, unsigned int *value){ int error; unsigned int arg; error = verify_area(VERIFY_READ, value, sizeof(int)); if (error) return error; arg = get_user(value); switch (cmd) { case TIOCMBIS: if (arg & TIOCM_RTS) { info->MCR |= UART_MCR_RTS; info->MCR_noint |= UART_MCR_RTS; } if (arg & TIOCM_DTR) { info->MCR |= UART_MCR_DTR; info->MCR_noint |= UART_MCR_DTR; } break; case TIOCMBIC: if (arg & TIOCM_RTS) { info->MCR &= ~UART_MCR_RTS; info->MCR_noint &= ~UART_MCR_RTS; } if (arg & TIOCM_DTR) { info->MCR &= ~UART_MCR_DTR; info->MCR_noint &= ~UART_MCR_DTR; } break; case TIOCMSET: info->MCR = ((info->MCR & ~(UART_MCR_RTS | UART_MCR_DTR)) | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0) | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0)); info->MCR_noint = ((info->MCR_noint & ~(UART_MCR_RTS | UART_MCR_DTR)) | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0) | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0)); break; default: return -EINVAL; } cli(); serial_out(info, UART_MCR, info->MCR); sti(); return 0;}static int do_autoconfig(struct async_struct * info){ int retval; if (!suser()) return -EPERM; if (info->count > 1) return -EBUSY; shutdown(info); cli(); autoconfig(info); sti(); retval = startup(info); if (retval) return retval; return 0;}/* * rs_break() --- routine which turns the break handling on or off * adapted from 2.1.124 */static void rs_break(struct async_struct * info, int break_state){ unsigned long flags; if (!info->port) return; save_flags(flags);cli(); if (break_state == -1) serial_out(info, UART_LCR, serial_inp(info, UART_LCR) | UART_LCR_SBC); else serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC); restore_flags(flags);}/* * This routine sends a break character out the serial port. */static void send_break( struct async_struct * info, int duration){ if (!info->port) return; current->state = TASK_INTERRUPTIBLE; current->timeout = jiffies + duration; cli(); serial_out(info, UART_LCR, serial_inp(info, UART_LCR) | UART_LCR_SBC); schedule(); serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC); sti();}#ifndef CONFIG_COLDFIRE/* * This routine returns a bitfield of "wild interrupts". Basically, * any unclaimed interrupts which is flapping around. */static int check_wild_interrupts(int doprint){ int i, mask; int wild_interrupts = 0; int irq_lines; unsigned long timeout; unsigned long flags; /* Turn on interrupts (they may be off) */ save_flags(flags); sti(); irq_lines = grab_all_interrupts(0); /* * Delay for 0.1 seconds -- we use a busy loop since this may * occur during the bootup sequence */ timeout = jiffies+HZ/10; while (timeout >= jiffies) ; rs_triggered = 0; /* Reset after letting things settle */ timeout = jiffies+HZ/10; while (timeout >= jiffies) ; for (i = 0, mask = 1; i < 16; i++, mask <<= 1) { if ((rs_triggered & (1 << i)) && (irq_lines & (1 << i))) { wild_interrupts |= mask; if (doprint) printk("Wild interrupt? (IRQ %d)\n", i); } } free_all_interrupts(irq_lines);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -