📄 serial_core.c
字号:
spin_lock_irqsave(&port->lock, flags); port->ops->stop_rx(port); spin_unlock_irqrestore(&port->lock, flags); /* * Before we drop DTR, make sure the UART transmitter * has completely drained; this is especially * important if there is a transmit FIFO! */ uart_wait_until_sent(tty, port->timeout); } uart_shutdown(state); uart_flush_buffer(tty); tty_ldisc_flush(tty); tty->closing = 0; state->info->tty = NULL; if (state->info->blocked_open) { if (state->close_delay) msleep_interruptible(state->close_delay); } else if (!uart_console(port)) { uart_change_pm(state, 3); } /* * Wake up anyone trying to open this port. */ state->info->flags &= ~UIF_NORMAL_ACTIVE; wake_up_interruptible(&state->info->open_wait); done: up(&state->sem);}static void uart_wait_until_sent(struct tty_struct *tty, int timeout){ struct uart_state *state = tty->driver_data; struct uart_port *port = state->port; unsigned long char_time, expire; BUG_ON(!kernel_locked()); if (port->type == PORT_UNKNOWN || port->fifosize == 0) return; /* * 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 = (port->timeout - HZ/50) / port->fifosize; char_time = char_time / 5; if (char_time == 0) char_time = 1; if (timeout && timeout < char_time) 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 port->timeout, this is probably due to a * UART bug of some kind. So, we clamp the timeout parameter at * 2*port->timeout. */ if (timeout == 0 || timeout > 2 * port->timeout) timeout = 2 * port->timeout; expire = jiffies + timeout; DPRINTK("uart_wait_until_sent(%d), jiffies=%lu, expire=%lu...\n", port->line, jiffies, expire); /* * Check whether the transmitter is empty every 'char_time'. * 'timeout' / 'expire' give us the maximum amount of time * we wait. */ while (!port->ops->tx_empty(port)) { msleep_interruptible(jiffies_to_msecs(char_time)); if (signal_pending(current)) break; if (time_after(jiffies, expire)) break; } set_current_state(TASK_RUNNING); /* might not be needed */}/* * This is called with the BKL held in * linux/drivers/char/tty_io.c:do_tty_hangup() * We're called from the eventd thread, so we can sleep for * a _short_ time only. */static void uart_hangup(struct tty_struct *tty){ struct uart_state *state = tty->driver_data; BUG_ON(!kernel_locked()); DPRINTK("uart_hangup(%d)\n", state->port->line); down(&state->sem); if (state->info && state->info->flags & UIF_NORMAL_ACTIVE) { uart_flush_buffer(tty); uart_shutdown(state); state->count = 0; state->info->flags &= ~UIF_NORMAL_ACTIVE; state->info->tty = NULL; wake_up_interruptible(&state->info->open_wait); wake_up_interruptible(&state->info->delta_msr_wait); } up(&state->sem);}/* * Copy across the serial console cflag setting into the termios settings * for the initial open of the port. This allows continuity between the * kernel settings, and the settings init adopts when it opens the port * for the first time. */static void uart_update_termios(struct uart_state *state){ struct tty_struct *tty = state->info->tty; struct uart_port *port = state->port; if (uart_console(port) && port->cons->cflag) { tty->termios->c_cflag = port->cons->cflag; port->cons->cflag = 0; } /* * If the device failed to grab its irq resources, * or some other error occurred, don't try to talk * to the port hardware. */ if (!(tty->flags & (1 << TTY_IO_ERROR))) { /* * Make termios settings take effect. */ uart_change_speed(state, NULL); /* * And finally enable the RTS and DTR signals. */ if (tty->termios->c_cflag & CBAUD) uart_set_mctrl(port, TIOCM_DTR | TIOCM_RTS); }}/* * Block the open until the port is ready. We must be called with * the per-port semaphore held. */static intuart_block_til_ready(struct file *filp, struct uart_state *state){ DECLARE_WAITQUEUE(wait, current); struct uart_info *info = state->info; struct uart_port *port = state->port; info->blocked_open++; state->count--; add_wait_queue(&info->open_wait, &wait); while (1) { set_current_state(TASK_INTERRUPTIBLE); /* * If we have been hung up, tell userspace/restart open. */ if (tty_hung_up_p(filp) || info->tty == NULL) break; /* * If the port has been closed, tell userspace/restart open. */ if (!(info->flags & UIF_INITIALIZED)) break; /* * If non-blocking mode is set, or CLOCAL mode is set, * we don't want to wait for the modem status lines to * indicate that the port is ready. * * Also, if the port is not enabled/configured, we want * to allow the open to succeed here. Note that we will * have set TTY_IO_ERROR for a non-existant port. */ if ((filp->f_flags & O_NONBLOCK) || (info->tty->termios->c_cflag & CLOCAL) || (info->tty->flags & (1 << TTY_IO_ERROR))) { break; } /* * Set DTR to allow modem to know we're waiting. Do * not set RTS here - we want to make sure we catch * the data from the modem. */ if (info->tty->termios->c_cflag & CBAUD) uart_set_mctrl(port, TIOCM_DTR); /* * and wait for the carrier to indicate that the * modem is ready for us. */ if (port->ops->get_mctrl(port) & TIOCM_CAR) break; up(&state->sem); schedule(); down(&state->sem); if (signal_pending(current)) break; } set_current_state(TASK_RUNNING); remove_wait_queue(&info->open_wait, &wait); state->count++; info->blocked_open--; if (signal_pending(current)) return -ERESTARTSYS; if (!info->tty || tty_hung_up_p(filp)) return -EAGAIN; return 0;}static struct uart_state *uart_get(struct uart_driver *drv, int line){ struct uart_state *state; down(&port_sem); state = drv->state + line; if (down_interruptible(&state->sem)) { state = ERR_PTR(-ERESTARTSYS); goto out; } state->count++; if (!state->port) { state->count--; up(&state->sem); state = ERR_PTR(-ENXIO); goto out; } if (!state->info) { state->info = kmalloc(sizeof(struct uart_info), GFP_KERNEL); if (state->info) { memset(state->info, 0, sizeof(struct uart_info)); init_waitqueue_head(&state->info->open_wait); init_waitqueue_head(&state->info->delta_msr_wait); /* * Link the info into the other structures. */ state->port->info = state->info; tasklet_init(&state->info->tlet, uart_tasklet_action, (unsigned long)state); } else { state->count--; up(&state->sem); state = ERR_PTR(-ENOMEM); } } out: up(&port_sem); return state;}/* * In 2.4.5, calls to uart_open are serialised by the BKL in * linux/fs/devices.c:chrdev_open() * Note that if this fails, then uart_close() _will_ be called. * * In time, we want to scrap the "opening nonpresent ports" * behaviour and implement an alternative way for setserial * to set base addresses/ports/types. This will allow us to * get rid of a certain amount of extra tests. */static int uart_open(struct tty_struct *tty, struct file *filp){ struct uart_driver *drv = (struct uart_driver *)tty->driver->driver_state; struct uart_state *state; int retval, line = tty->index; BUG_ON(!kernel_locked()); DPRINTK("uart_open(%d) called\n", line); /* * tty->driver->num won't change, so we won't fail here with * tty->driver_data set to something non-NULL (and therefore * we won't get caught by uart_close()). */ retval = -ENODEV; if (line >= tty->driver->num) goto fail; /* * We take the semaphore inside uart_get to guarantee that we won't * be re-entered while allocating the info structure, or while we * request any IRQs that the driver may need. This also has the nice * side-effect that it delays the action of uart_hangup, so we can * guarantee that info->tty will always contain something reasonable. */ state = uart_get(drv, line); if (IS_ERR(state)) { retval = PTR_ERR(state); goto fail; } /* * Once we set tty->driver_data here, we are guaranteed that * uart_close() will decrement the driver module use count. * Any failures from here onwards should not touch the count. */ tty->driver_data = state; tty->low_latency = (state->port->flags & UPF_LOW_LATENCY) ? 1 : 0; tty->alt_speed = 0; state->info->tty = tty; /* * If the port is in the middle of closing, bail out now. */ if (tty_hung_up_p(filp)) { retval = -EAGAIN; state->count--; up(&state->sem); goto fail; } /* * Make sure the device is in D0 state. */ if (state->count == 1) uart_change_pm(state, 0); /* * Start up the serial port. */ retval = uart_startup(state, 0); /* * If we succeeded, wait until the port is ready. */ if (retval == 0) retval = uart_block_til_ready(filp, state); up(&state->sem); /* * If this is the first open to succeed, adjust things to suit. */ if (retval == 0 && !(state->info->flags & UIF_NORMAL_ACTIVE)) { state->info->flags |= UIF_NORMAL_ACTIVE; uart_update_termios(state); } fail: return retval;}static const char *uart_type(struct uart_port *port){ const char *str = NULL; if (port->ops->type) str = port->ops->type(port); if (!str) str = "unknown"; return str;}#ifdef CONFIG_PROC_FSstatic int uart_line_info(char *buf, struct uart_driver *drv, int i){ struct uart_state *state = drv->state + i; struct uart_port *port = state->port; char stat_buf[32]; unsigned int status; int ret; if (!port) return 0; ret = sprintf(buf, "%d: uart:%s %s%08lX irq:%d", port->line, uart_type(port), port->iotype == UPIO_MEM ? "mmio:0x" : "port:", port->iotype == UPIO_MEM ? port->mapbase : (unsigned long) port->iobase, port->irq); if (port->type == PORT_UNKNOWN) { strcat(buf, "\n"); return ret + 1; } if(capable(CAP_SYS_ADMIN)) { status = port->ops->get_mctrl(port); ret += sprintf(buf + ret, " tx:%d rx:%d", port->icount.tx, port->icount.rx); if (port->icount.frame) ret += sprintf(buf + ret, " fe:%d", port->icount.frame); if (port->icount.parity) ret += sprintf(buf + ret, " pe:%d", port->icount.parity); if (port->icount.brk) ret += sprintf(buf + ret, " brk:%d", port->icount.brk); if (port->icount.overrun) ret += sprintf(buf + ret, " oe:%d", port->icount.overrun); #define INFOBIT(bit,str) \ if (port->mctrl & (bit)) \ strncat(stat_buf, (str), sizeof(stat_buf) - \ strlen(stat_buf) - 2)#define STATBIT(bit,str) \ if (status & (bit)) \ strncat(stat_buf, (str), sizeof(stat_buf) - \ strlen(stat_buf) - 2) stat_buf[0] = '\0'; stat_buf[1] = '\0'; INFOBIT(TIOCM_RTS, "|RTS"); STATBIT(TIOCM_CTS, "|CTS"); INFOBIT(TIOCM_DTR, "|DTR"); STATBIT(TIOCM_DSR, "|DSR"); STATBIT(TIOCM_CAR, "|CD"); STATBIT(TIOCM_RNG, "|RI"); if (stat_buf[0]) stat_buf[0] = ' '; strcat(stat_buf, "\n"); ret += sprintf(buf + ret, stat_buf); } else { strcat(buf, "\n"); ret++; }#undef STATBIT#undef INFOBIT return ret;}static int uart_read_proc(char *page, char **start, off_t off, int count, int *eof, void *data){ struct tty_driver *ttydrv = data; struct uart_driver *drv = ttydrv->driver_state; int i, len = 0, l; off_t begin = 0; len += sprintf(page, "serinfo:1.0 driver%s%s revision:%s\n", "", "", ""); for (i = 0; i < drv->nr && len < PAGE_SIZE - 96; i++) { l = uart_line_info(page + len, drv, i); len += l; if (len + begin > off + count) goto done; if (len + begin < off) { begin += len; len = 0; } } *eof = 1; done: if (off >= len + begin) return 0; *start = page + (off - begin); return (count < begin + len - off) ? count : (begin + len - off);}#endif#ifdef CONFIG_SERIAL_CORE_CONSOLE/* * Check whether an invalid uart number has been specified, and * if so, search for the first available port that does have * console support. */struct uart_port * __inituart_get_console(struct uart_port *ports, int nr, struct console *co){ int idx = co->index; if (idx < 0 || idx >= nr || (ports[idx].iobase == 0 && ports[idx].membase == NULL)) for (idx = 0; idx < nr; idx++) if (ports[idx].iobase != 0 || ports[idx].membase != NULL) break; co->index = idx; return ports + idx;}/** * uart_parse_options - Parse serial port baud/parity/bits/flow contro. * @options: pointer to option string * @baud: pointer to an 'int' variable for the baud rate. * @parity: pointer to an 'int' variable for the parity. * @bits: pointer to an 'int' variable for the number of data bits. * @flow: pointer to an 'int' variable for the flow control character. * * uart_parse_options decodes a string containing the serial console * options. The format of the string is <baud><parity><bits><flow>, * eg: 115200n8r */void __inituart_parse_options(char *options, int *baud, int *parity, int *bits, int *flow){ char *s = options; *baud = simple_strtoul(s, NULL, 10); while (*s >= '0' && *s <= '9') s++; if (*s) *parity = *s++; if (*s) *bits = *s++ - '0'; if (*s) *flow = *s;}struct baud_rates { unsigned int rate; unsigned int cflag;};static struct baud_rates baud_rates[] = { { 921600, B921600 }, { 460800, B460800 }, { 230400, B230400 }, { 115200, B115200 }, { 57600, B57600 }, { 38400, B38400 }, { 19200, B19200 }, { 9600, B9600 }, { 4800, B4800 }, { 2400, B2400 }, { 1200, B1200 }, { 0, B38400 }};/** * uart_set_options - setup the serial console parameters * @port: pointer to the serial ports uart_port structure * @co: console pointer * @baud: baud rate * @parity: parity character - 'n' (none), 'o' (odd), 'e' (even) * @bits: number of data bits * @flow: flow control character - 'r' (rts) */int __inituart_set_options(struct uart_port *port, struct console *co, int baud, int parity, int bits, int flow){ struct termios termios; int i; memset(&termios, 0, sizeof(struct termios)); termios.c_cflag = CREAD | HUPCL | CLOCAL; /* * Construct a cflag setting. */ for (i = 0; baud_rates[i].rate; i++) if (baud_rates[i].rate <= baud) break; termios.c_cflag |= baud_rates[i].cflag; if (bits == 7) termios.c_cflag |= CS7; else termios.c_cflag |= CS8; switch (parity) { case 'o': case 'O': termios.c_cflag |= PARODD; /*fall through*/ case 'e': case 'E': termios.c_cflag |= PARENB; break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -