📄 duart_serial.c
字号:
struct serial_multiport_struct *retinfo){ struct serial_multiport_struct ret; struct rs_multiport_struct *multi; multi = &rs_multiport[info->state->irq]; ret.port_monitor = multi->port_monitor; ret.port1 = multi->port1; ret.mask1 = multi->mask1; ret.match1 = multi->match1; ret.port2 = multi->port2; ret.mask2 = multi->mask2; ret.match2 = multi->match2; ret.port3 = multi->port3; ret.mask3 = multi->mask3; ret.match3 = multi->match3; ret.port4 = multi->port4; ret.mask4 = multi->mask4; ret.match4 = multi->match4; ret.irq = info->state->irq; if (copy_to_user(retinfo,&ret,sizeof(*retinfo))) return -EFAULT; return 0;}static int set_multiport_struct(struct async_struct * info, struct serial_multiport_struct *in_multi){ struct serial_multiport_struct new_multi; struct rs_multiport_struct *multi; struct serial_state *state; int was_multi, now_multi; int retval; void (*handler)(int, void *, struct pt_regs *); if (!capable(CAP_SYS_ADMIN)) return -EPERM; state = info->state; if (copy_from_user(&new_multi, in_multi, sizeof(struct serial_multiport_struct))) return -EFAULT; if (new_multi.irq != state->irq || state->irq == 0 || !IRQ_ports[state->irq]) return -EINVAL; multi = &rs_multiport[state->irq]; was_multi = (multi->port1 != 0); multi->port_monitor = new_multi.port_monitor; if (multi->port1) release_region(multi->port1,1); multi->port1 = new_multi.port1; multi->mask1 = new_multi.mask1; multi->match1 = new_multi.match1; if (multi->port1) request_region(multi->port1,1,"serial(multiport1)"); if (multi->port2) release_region(multi->port2,1); multi->port2 = new_multi.port2; multi->mask2 = new_multi.mask2; multi->match2 = new_multi.match2; if (multi->port2) request_region(multi->port2,1,"serial(multiport2)"); if (multi->port3) release_region(multi->port3,1); multi->port3 = new_multi.port3; multi->mask3 = new_multi.mask3; multi->match3 = new_multi.match3; if (multi->port3) request_region(multi->port3,1,"serial(multiport3)"); if (multi->port4) release_region(multi->port4,1); multi->port4 = new_multi.port4; multi->mask4 = new_multi.mask4; multi->match4 = new_multi.match4; if (multi->port4) request_region(multi->port4,1,"serial(multiport4)"); now_multi = (multi->port1 != 0); if (IRQ_ports[state->irq]->next_port && (was_multi != now_multi)) { free_irq(state->irq, NULL); if (now_multi) handler = rs_interrupt_multi; else handler = rs_interrupt; retval = request_irq(state->irq, handler, IRQ_T(info), "serial", NULL); if (retval) { printk("Couldn't reallocate serial interrupt " "driver!!\n"); } } return 0;}#endifstatic int rs_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg){ int error; struct async_struct * info = (struct async_struct *)tty->driver_data; struct async_icount cprev, cnow; /* kernel counter temps */ struct serial_icounter_struct *p_cuser; /* user space */ unsigned long flags; if (serial_paranoia_check(info, tty->device, "rs_ioctl")) return -ENODEV; if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) && (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { if (tty->flags & (1 << TTY_IO_ERROR)) return -EIO; } switch (cmd) { case TIOCMGET: return get_modem_info(info, (unsigned int *) arg); case TIOCMBIS: case TIOCMBIC: case TIOCMSET: return set_modem_info(info, cmd, (unsigned int *) arg); case TIOCGSERIAL: return get_serial_info(info, (struct serial_struct *) arg); case TIOCSSERIAL: return set_serial_info(info, (struct serial_struct *) arg); case TIOCSERCONFIG: return do_autoconfig(info); case TIOCSERGETLSR: /* Get line status register */ return get_lsr_info(info, (unsigned int *) arg); case TIOCSERGSTRUCT: if (copy_to_user((struct async_struct *) arg, info, sizeof(struct async_struct))) return -EFAULT; return 0; #ifdef CONFIG_SERIAL_MULTIPORT case TIOCSERGETMULTI: return get_multiport_struct(info, (struct serial_multiport_struct *) arg); case TIOCSERSETMULTI: return set_multiport_struct(info, (struct serial_multiport_struct *) arg);#endif /* * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change * - mask passed in arg for lines of interest * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) * Caller should use TIOCGICOUNT to see which one it was */ case TIOCMIWAIT: save_flags(flags); cli(); /* note the counters on entry */ cprev = info->state->icount; restore_flags(flags); while (1) { interruptible_sleep_on(&info->delta_msr_wait); /* see if a signal did it */ if (signal_pending(current)) return -ERESTARTSYS; save_flags(flags); cli(); cnow = info->state->icount; /* atomic copy */ restore_flags(flags); if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) return -EIO; /* no change => error */ if ( ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) { return 0; } cprev = cnow; } /* NOTREACHED */ /* * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) * Return: write counters to the user passed counter struct * NB: both 1->0 and 0->1 transitions are counted except for * RI where only 0->1 is counted. */ case TIOCGICOUNT: save_flags(flags); cli(); cnow = info->state->icount; restore_flags(flags); p_cuser = (struct serial_icounter_struct *) arg; error = put_user(cnow.cts, &p_cuser->cts); if (error) return error; error = put_user(cnow.dsr, &p_cuser->dsr); if (error) return error; error = put_user(cnow.rng, &p_cuser->rng); if (error) return error; error = put_user(cnow.dcd, &p_cuser->dcd); if (error) return error; error = put_user(cnow.rx, &p_cuser->rx); if (error) return error; error = put_user(cnow.tx, &p_cuser->tx); if (error) return error; error = put_user(cnow.frame, &p_cuser->frame); if (error) return error; error = put_user(cnow.overrun, &p_cuser->overrun); if (error) return error; error = put_user(cnow.parity, &p_cuser->parity); if (error) return error; error = put_user(cnow.brk, &p_cuser->brk); if (error) return error; error = put_user(cnow.buf_overrun, &p_cuser->buf_overrun); if (error) return error; return 0; case TIOCSERGWILD: case TIOCSERSWILD: /* "setserial -W" is called in Debian boot */ printk ("TIOCSER?WILD ioctl obsolete, ignored.\n"); return 0; default: return -ENOIOCTLCMD; } return 0;}static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios){ struct async_struct *info = (struct async_struct *)tty->driver_data; unsigned long flags; unsigned int cflag = tty->termios->c_cflag; if ( (cflag == old_termios->c_cflag) && ( RELEVANT_IFLAG(tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) return; change_speed(info, old_termios); /* Handle transition to B0 status */ if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD)) { info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS); save_flags(flags); cli(); serial_out(info, UART_MCR, info->MCR); restore_flags(flags); } /* Handle transition away from B0 status */ if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) { info->MCR |= UART_MCR_DTR; if (!(tty->termios->c_cflag & CRTSCTS) || !test_bit(TTY_THROTTLED, &tty->flags)) { info->MCR |= UART_MCR_RTS; } save_flags(flags); cli(); serial_out(info, UART_MCR, info->MCR); restore_flags(flags); } /* Handle turning off CRTSCTS */ if ((old_termios->c_cflag & CRTSCTS) && !(tty->termios->c_cflag & CRTSCTS)) { tty->hw_stopped = 0; rs_start(tty); }#if 0 /* * No need to wake up processes in open wait, since they * sample the CLOCAL flag once, and don't recheck it. * XXX It's not clear whether the current behavior is correct * or not. Hence, this may change..... */ if (!(old_termios->c_cflag & CLOCAL) && (tty->termios->c_cflag & CLOCAL)) wake_up_interruptible(&info->open_wait);#endif}/* * ------------------------------------------------------------ * rs_close() * * This routine is called when the serial port gets closed. First, we * wait for the last remaining data to be sent. Then, we unlink its * async structure from the interrupt chain if necessary, and we free * that IRQ if nothing is left in the chain. * ------------------------------------------------------------ */static void rs_close(struct tty_struct *tty, struct file * filp){ struct async_struct * info = (struct async_struct *)tty->driver_data; struct serial_state *state; unsigned long flags; if (!info || serial_paranoia_check(info, tty->device, "rs_close")) return; state = info->state; save_flags(flags); cli(); if (tty_hung_up_p(filp)) { DBG_CNT("before DEC-hung"); MOD_DEC_USE_COUNT; restore_flags(flags); return; } #ifdef SERIAL_DEBUG_OPEN printk("\nrs_close ttys%d, count = %d\n", info->line, state->count);#endif if ((tty->count == 1) && (state->count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty * structure will be freed. state->count should always * be one in these conditions. If it's greater than * one, we've got real problems, since it means the * serial port won't be shutdown. */ printk("rs_close: bad serial port count; tty->count is 1, " "state->count is %d\n", state->count); state->count = 1; } if (--state->count < 0) { printk("rs_close: bad serial port count for ttys%d: %d\n", info->line, state->count); state->count = 0; } if (state->count) { DBG_CNT("before DEC-2"); MOD_DEC_USE_COUNT; restore_flags(flags); return; } info->flags |= ASYNC_CLOSING; /* * Save the termios structure, since this port may have * separate termios for callout and dialin. */ if (info->flags & ASYNC_NORMAL_ACTIVE) info->state->normal_termios = *tty->termios; if (info->flags & ASYNC_CALLOUT_ACTIVE) info->state->callout_termios = *tty->termios; /* * Now we wait for the transmit buffer to clear; and we notify * the line discipline to only process XON/XOFF characters. */ tty->closing = 1; if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE) tty_wait_until_sent(tty, info->closing_wait); /* * At this point we stop accepting input. To do this, we * disable the receive line status interrupts, and tell the * interrupt driver to stop checking the data ready bit in the * line status register. */ info->IER &= ~UART_IER_RLSI; info->read_status_mask &= ~UART_LSR_DR; if (info->flags & ASYNC_INITIALIZED) { serial_out(info, UART_IER, info->IER); /* * Before we drop DTR, make sure the UART transmitter * has completely drained; this is especially * important if there is a transmit FIFO! */ rs_wait_until_sent(tty, info->timeout); } shutdown(info); if (tty->driver.flush_buffer) tty->driver.flush_buffer(tty); if (tty->ldisc.flush_buffer) tty->ldisc.flush_buffer(tty); tty->closing = 0; info->event = 0; info->tty = 0; if (info->blocked_open) { if (info->close_delay) { current->state = TASK_INTERRUPTIBLE; schedule_timeout(info->close_delay); } wake_up_interruptible(&info->open_wait); } info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE| ASYNC_CLOSING); wake_up_interruptible(&info->close_wait); MOD_DEC_USE_COUNT; restore_flags(flags);}/* * rs_wait_until_sent() --- wait until the transmitter is empty */static void rs_wait_until_sent(struct tty_struct *tty, int timeout){ struct async_struct * info = (struct async_struct *)tty->driver_data; unsigned long orig_jiffies, char_time; int lsr; if (serial_paranoia_check(info, tty->device, "rs_wait_until_sent")) return; if (info->state->type == PORT_UNKNOWN) return; if (info->xmit_fifo_size == 0) return; /* Just in case.... */ orig_jiffies = jiffies; /* * Set the check interval to be 1/5 of the estimated time to * send a single character, and make it at least 1. The check * interval should also be less than the timeout. * * Note: we have to use pretty tight timings here to satisfy * the NIST-PCTS. */ char_time = (info->timeout - HZ/50) / info->xmit_fifo_size; char_time = char_time / 5; if (char_time == 0) char_time = 1; if (timeout) char_time = MIN(char_time, timeout); /* * If the transmitter hasn't cleared in twice the approximate * amount of time to send the entire FIFO, it probably won't * ever clear. This assumes the UART isn't doing flow * control, which is currently the case. Hence, if it ever * takes longer than info->timeout, this is probably due to a * UART bug of some kind. So, we clamp the timeout parameter at * 2*info->timeout. */ if (!timeout || timeout > 2*info->timeout) timeout = 2*info->timeout;#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT printk("In rs_wait_until_sent(%d) check=%lu...", timeout, char_time); printk("jiff=%lu...", jiffies);#endif while (!((lsr = serial_inp(info, UART_LSR)) & UART_LSR_TEMT)) {#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT printk("lsr = %d (jiff=%lu)...", lsr, jiffies);#endif current->state = TASK_INTERRUPTIBLE; current->counter = 0; /* make us low-priority */ schedule_timeout(char_time); if (signal_pending(current)) break; if (timeout && time_after(jiffies, orig_jiffies + timeout)) break; } current->state = TASK_RUNNING;#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies);#endif}/* * rs_hangup() --- called by tty_hangup() when a hangup is signaled. */static void rs_hangup(struct tty_struct *tty){ struct async_struct * info = (struct async_struct *)tty->driver_data; struct serial_state *state = info->state; if (serial_paranoia_check(info, tty->device, "rs_hangup")) return; state = info->state; rs_flush_buffer(tty); shutdown(info); info->event = 0; state->cou
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -