📄 core.c
字号:
} wake_up_interruptible(&info->open_wait); } else {#ifdef CONFIG_PM /* * Put device into D3 state. */ pm_send(info->state->pm, PM_SUSPEND, (void *)3);#else if (info->ops->pm) info->ops->pm(info->port, 3, 0);#endif } info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE| ASYNC_CLOSING); wake_up_interruptible(&info->close_wait);done: if (drv->owner) __MOD_DEC_USE_COUNT(drv->owner);}static void uart_wait_until_sent(struct tty_struct *tty, int timeout){ struct uart_info *info = (struct uart_info *) tty->driver_data; unsigned long char_time, expire; if (info->port->type == PORT_UNKNOWN || info->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 = (info->timeout - HZ/50) / info->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 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; expire = jiffies + timeout;#ifdef DEBUG printk("uart_wait_until_sent(%d), jiff=%lu, expire=%lu...\n", MINOR(tty->device) - tty->driver.minor_start, jiffies, expire);#endif while (!info->ops->tx_empty(info->port)) { set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(char_time); if (signal_pending(current)) break; if (timeout && time_after(jiffies, expire)) break; } set_current_state(TASK_RUNNING); /* might not be needed */}/* * This is called with the BKL in effect * linux/drivers/char/tty_io.c:do_tty_hangup() */static void uart_hangup(struct tty_struct *tty){ struct uart_info *info = tty->driver_data; struct uart_state *state = info->state; uart_flush_buffer(tty); if (info->flags & ASYNC_CLOSING) return; uart_shutdown(info); info->event = 0; state->count = 0; info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE); info->tty = NULL; wake_up_interruptible(&info->open_wait);}static int uart_block_til_ready(struct tty_struct *tty, struct file *filp, struct uart_info *info){ DECLARE_WAITQUEUE(wait, current); struct uart_state *state = info->state; unsigned long flags; int do_clocal = 0, extra_count = 0, retval; /* * If the device is in the middle of being closed, then block * until it's done, and then try again. */ if (tty_hung_up_p(filp) || (info->flags & ASYNC_CLOSING)) { if (info->flags & ASYNC_CLOSING) interruptible_sleep_on(&info->close_wait); return (info->flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS; } /* * If this is a callout device, then just make sure the normal * device isn't being used. */ if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) { if (info->flags & ASYNC_NORMAL_ACTIVE) return -EBUSY; if ((info->flags & ASYNC_CALLOUT_ACTIVE) && (info->flags & ASYNC_SESSION_LOCKOUT) && (info->session != current->session)) return -EBUSY; if ((info->flags & ASYNC_CALLOUT_ACTIVE) && (info->flags & ASYNC_PGRP_LOCKOUT) && (info->pgrp != current->pgrp)) return -EBUSY; info->flags |= ASYNC_CALLOUT_ACTIVE; return 0; } /* * If non-blocking mode is set, or the port is not enabled, * then make the check up front and then exit. Note that * we have set TTY_IO_ERROR for a non-enabled port. */ if ((filp->f_flags & O_NONBLOCK) || (tty->flags & (1 << TTY_IO_ERROR))) { if (info->flags & ASYNC_CALLOUT_ACTIVE) return -EBUSY; info->flags |= ASYNC_NORMAL_ACTIVE; return 0; } if (info->flags & ASYNC_CALLOUT_ACTIVE) { if (state->normal_termios.c_cflag & CLOCAL) do_clocal = 1; } else { if (tty->termios->c_cflag & CLOCAL) do_clocal = 1; } /* * Block waiting for the carrier detect and the line to become * free (i.e., not in use by the callout). While we are in * this loop, state->count is dropped by one, so that * rs_close() knows when to free things. We restore it upon * exit, either normal or abnormal. */ retval = 0; add_wait_queue(&info->open_wait, &wait); down(&state->count_sem); spin_lock_irqsave(&info->lock, flags); if (!tty_hung_up_p(filp)) { extra_count = 1; state->count--; } spin_unlock_irqrestore(&info->lock, flags); info->blocked_open++; up(&state->count_sem); while (1) { spin_lock_irqsave(&info->lock, flags); if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && (tty->termios->c_cflag & CBAUD)) { info->mctrl |= TIOCM_DTR | TIOCM_RTS; info->ops->set_mctrl(info->port, info->mctrl); } spin_unlock_irqrestore(&info->lock, flags); set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || !(info->flags & ASYNC_INITIALIZED)) { if (info->flags & ASYNC_HUP_NOTIFY) retval = -EAGAIN; else retval = -ERESTARTSYS; break; } if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && !(info->flags & ASYNC_CLOSING) && (do_clocal || (info->ops->get_mctrl(info->port) & TIOCM_CAR))) break; if (signal_pending(current)) { retval = -ERESTARTSYS; break; } schedule(); } set_current_state(TASK_RUNNING); remove_wait_queue(&info->open_wait, &wait); down(&state->count_sem); if (extra_count) state->count++; info->blocked_open--; up(&state->count_sem); if (retval) return retval; info->flags |= ASYNC_NORMAL_ACTIVE; return 0;}static struct uart_info *uart_get(struct uart_driver *drv, int line){ struct uart_state *state = drv->state + line; struct uart_info *info; down(&state->count_sem); state->count++; if (state->info) goto out; info = kmalloc(sizeof(struct uart_info), GFP_KERNEL); if (info) { memset(info, 0, sizeof(struct uart_info)); init_waitqueue_head(&info->open_wait); init_waitqueue_head(&info->close_wait); init_waitqueue_head(&info->delta_msr_wait); info->port = state->port; info->flags = info->port->flags; info->ops = info->port->ops; info->state = state; tasklet_init(&info->tlet, uart_tasklet_action, (unsigned long)info); } if (state->info) kfree(info); else state->info = info;out: up(&state->count_sem); return state->info;}/* * Make sure we have the temporary buffer allocated. Note * that we set retval appropriately above, and we rely on * this. */static inline int uart_alloc_tmpbuf(void){ if (!tmp_buf) { unsigned long buf = get_zeroed_page(GFP_KERNEL); if (!tmp_buf) { if (buf) tmp_buf = (u_char *)buf; else return -ENOMEM; } else free_page(buf); } return 0;}/* * 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. */static int uart_open(struct tty_struct *tty, struct file *filp){ struct uart_driver *drv = (struct uart_driver *)tty->driver.driver_state; struct uart_info *info; int retval, line = MINOR(tty->device) - tty->driver.minor_start;#ifdef DEBUG printk("uart_open(%d) called\n", line);#endif retval = -ENODEV; if (line >= tty->driver.num) goto fail; if (!try_inc_mod_count(drv->owner)) goto fail; info = uart_get(drv, line); retval = -ENOMEM; if (!info) goto out; /* * Set the tty driver_data. If we fail from this point on, * the generic tty layer will cause uart_close(), which will * decrement the module use count. */ tty->driver_data = info; info->tty = tty; info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; if (uart_alloc_tmpbuf()) goto fail; /* * If the port is in the middle of closing, bail out now. */ if (tty_hung_up_p(filp) || (info->flags & ASYNC_CLOSING)) { if (info->flags & ASYNC_CLOSING) interruptible_sleep_on(&info->close_wait); retval = (info->flags & ASYNC_HUP_NOTIFY) ? -EAGAIN : -ERESTARTSYS; goto fail; } /* * Make sure the device is in D0 state. */ if (info->state->count == 1)#ifdef CONFIG_PM pm_send(info->state->pm, PM_RESUME, (void *)0);#else if (info->ops->pm) info->ops->pm(info->port, 0, 3);#endif /* * Start up the serial port */ retval = uart_startup(info); if (retval) goto fail; retval = uart_block_til_ready(tty, filp, info); if (retval) goto fail; if (info->state->count == 1) { int changed_termios = 0; if (info->flags & ASYNC_SPLIT_TERMIOS) { if (tty->driver.subtype == SERIAL_TYPE_NORMAL) *tty->termios = info->state->normal_termios; else *tty->termios = info->state->callout_termios; changed_termios = 1; }#ifdef CONFIG_SERIAL_CORE_CONSOLE /* * Copy across the serial console cflag setting */ { struct console *c = drv->cons; if (c && c->cflag && c->index == line) { tty->termios->c_cflag = c->cflag; c->cflag = 0; changed_termios = 1; } }#endif if (changed_termios) uart_change_speed(info, NULL); } info->session = current->session; info->pgrp = current->pgrp; return 0;out: if (drv->owner) __MOD_DEC_USE_COUNT(drv->owner);fail: return retval;}#ifdef CONFIG_PROC_FSstatic 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;}static 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]; u_int status; int ret; ret = sprintf(buf, "%d: uart:%s port:%08X irq:%d", port->line, uart_type(port), port->iobase, port->irq); if (port->type == PORT_UNKNOWN) { strcat(buf, "\n"); return ret + 1; } 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 (state->info && state->info->mctrl & (bit)) \ strcat(stat_buf, (str))#define STATBIT(bit,str) \ if (status & (bit)) \ strcat(stat_buf, (str)) 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); 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;}/** * 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){ u_int cflag = CREAD | HUPCL | CLOCAL; u_int quot; /* * Construct a cflag setting. */ switch (baud) { case 1200: cflag |= B1200; break; case 2400: cflag |= B2400; break; case 4800: cflag |= B4800; break; case 9600: cflag |= B9600; break; case 19200: cflag |= B19200; break; default: cflag |= B38400; baud = 38400; break; case 57600: cflag |= B57600; break; case 115200: cflag |= B115200; break; case 230400: cflag |= B230400; break; case 460800: cflag |= B460800; break; } if (bits == 7)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -