?? serial_core.c
字號:
/* * linux/drivers/char/core.c * * Driver core for serial ports * * Based on drivers/char/serial.c, by Linus Torvalds, Theodore Ts'o. * * Copyright 1999 ARM Limited * Copyright (C) 2000-2001 Deep Blue Solutions Ltd. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */#include <linux/config.h>#include <linux/module.h>#include <linux/tty.h>#include <linux/slab.h>#include <linux/init.h>#include <linux/console.h>#include <linux/serial_core.h>#include <linux/smp_lock.h>#include <linux/device.h>#include <linux/serial.h> /* for serial_state and serial_icounter_struct */#include <linux/delay.h>#include <linux/mutex.h>#include <asm/irq.h>#include <asm/uaccess.h>#undef DEBUG#ifdef DEBUG#define DPRINTK(x...) printk(x)#else#define DPRINTK(x...) do { } while (0)#endif/* * This is used to lock changes in serial line configuration. */static DEFINE_MUTEX(port_mutex);#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8)#define uart_users(state) ((state)->count + ((state)->info ? (state)->info->blocked_open : 0))#ifdef CONFIG_SERIAL_CORE_CONSOLE#define uart_console(port) ((port)->cons && (port)->cons->index == (port)->line)#else#define uart_console(port) (0)#endifstatic void uart_change_speed(struct uart_state *state, struct termios *old_termios);static void uart_wait_until_sent(struct tty_struct *tty, int timeout);static void uart_change_pm(struct uart_state *state, int pm_state);/* * This routine is used by the interrupt handler to schedule processing in * the software interrupt portion of the driver. */void uart_write_wakeup(struct uart_port *port){ struct uart_info *info = port->info; /* * This means you called this function _after_ the port was * closed. No cookie for you. */ BUG_ON(!info); tasklet_schedule(&info->tlet);}static void uart_stop(struct tty_struct *tty){ struct uart_state *state = tty->driver_data; struct uart_port *port = state->port; unsigned long flags; spin_lock_irqsave(&port->lock, flags); port->ops->stop_tx(port); spin_unlock_irqrestore(&port->lock, flags);}static void __uart_start(struct tty_struct *tty){ struct uart_state *state = tty->driver_data; struct uart_port *port = state->port; if (!uart_circ_empty(&state->info->xmit) && state->info->xmit.buf && !tty->stopped && !tty->hw_stopped) port->ops->start_tx(port);}static void uart_start(struct tty_struct *tty){ struct uart_state *state = tty->driver_data; struct uart_port *port = state->port; unsigned long flags; spin_lock_irqsave(&port->lock, flags); __uart_start(tty); spin_unlock_irqrestore(&port->lock, flags);}static void uart_tasklet_action(unsigned long data){ struct uart_state *state = (struct uart_state *)data; tty_wakeup(state->info->tty);}static inline voiduart_update_mctrl(struct uart_port *port, unsigned int set, unsigned int clear){ unsigned long flags; unsigned int old; spin_lock_irqsave(&port->lock, flags); old = port->mctrl; port->mctrl = (old & ~clear) | set; if (old != port->mctrl) port->ops->set_mctrl(port, port->mctrl); spin_unlock_irqrestore(&port->lock, flags);}#define uart_set_mctrl(port,set) uart_update_mctrl(port,set,0)#define uart_clear_mctrl(port,clear) uart_update_mctrl(port,0,clear)/* * Startup the port. This will be called once per open. All calls * will be serialised by the per-port semaphore. */static int uart_startup(struct uart_state *state, int init_hw){ struct uart_info *info = state->info; struct uart_port *port = state->port; unsigned long page; int retval = 0; if (info->flags & UIF_INITIALIZED) return 0; /* * Set the TTY IO error marker - we will only clear this * once we have successfully opened the port. Also set * up the tty->alt_speed kludge */ set_bit(TTY_IO_ERROR, &info->tty->flags); if (port->type == PORT_UNKNOWN) return 0; /* * Initialise and allocate the transmit and temporary * buffer. */ if (!info->xmit.buf) { page = get_zeroed_page(GFP_KERNEL); if (!page) return -ENOMEM; info->xmit.buf = (unsigned char *) page; uart_circ_clear(&info->xmit); } retval = port->ops->startup(port); if (retval == 0) { if (init_hw) { /* * Initialise the hardware port settings. */ uart_change_speed(state, NULL); /* * Setup the RTS and DTR signals once the * port is open and ready to respond. */ if (info->tty->termios->c_cflag & CBAUD) uart_set_mctrl(port, TIOCM_RTS | TIOCM_DTR); } if (info->flags & UIF_CTS_FLOW) { spin_lock_irq(&port->lock); if (!(port->ops->get_mctrl(port) & TIOCM_CTS)) info->tty->hw_stopped = 1; spin_unlock_irq(&port->lock); } info->flags |= UIF_INITIALIZED; clear_bit(TTY_IO_ERROR, &info->tty->flags); } if (retval && capable(CAP_SYS_ADMIN)) retval = 0; return retval;}/* * This routine will shutdown a serial port; interrupts are disabled, and * DTR is dropped if the hangup on close termio flag is on. Calls to * uart_shutdown are serialised by the per-port semaphore. */static void uart_shutdown(struct uart_state *state){ struct uart_info *info = state->info; struct uart_port *port = state->port; /* * Set the TTY IO error marker */ if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); if (info->flags & UIF_INITIALIZED) { info->flags &= ~UIF_INITIALIZED; /* * Turn off DTR and RTS early. */ if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) uart_clear_mctrl(port, TIOCM_DTR | TIOCM_RTS); /* * clear delta_msr_wait queue to avoid mem leaks: we may free * the irq here so the queue might never be woken up. Note * that we won't end up waiting on delta_msr_wait again since * any outstanding file descriptors should be pointing at * hung_up_tty_fops now. */ wake_up_interruptible(&info->delta_msr_wait); /* * Free the IRQ and disable the port. */ port->ops->shutdown(port); /* * Ensure that the IRQ handler isn't running on another CPU. */ synchronize_irq(port->irq); } /* * kill off our tasklet */ tasklet_kill(&info->tlet); /* * Free the transmit buffer page. */ if (info->xmit.buf) { free_page((unsigned long)info->xmit.buf); info->xmit.buf = NULL; }}/** * uart_update_timeout - update per-port FIFO timeout. * @port: uart_port structure describing the port * @cflag: termios cflag value * @baud: speed of the port * * Set the port FIFO timeout value. The @cflag value should * reflect the actual hardware settings. */voiduart_update_timeout(struct uart_port *port, unsigned int cflag, unsigned int baud){ unsigned int bits; /* byte size and parity */ switch (cflag & CSIZE) { case CS5: bits = 7; break; case CS6: bits = 8; break; case CS7: bits = 9; break; default: bits = 10; break; // CS8 } if (cflag & CSTOPB) bits++; if (cflag & PARENB) bits++; /* * The total number of bits to be transmitted in the fifo. */ bits = bits * port->fifosize; /* * Figure the timeout to send the above number of bits. * Add .02 seconds of slop */ port->timeout = (HZ * bits) / baud + HZ/50;}EXPORT_SYMBOL(uart_update_timeout);/** * uart_get_baud_rate - return baud rate for a particular port * @port: uart_port structure describing the port in question. * @termios: desired termios settings. * @old: old termios (or NULL) * @min: minimum acceptable baud rate * @max: maximum acceptable baud rate * * Decode the termios structure into a numeric baud rate, * taking account of the magic 38400 baud rate (with spd_* * flags), and mapping the %B0 rate to 9600 baud. * * If the new baud rate is invalid, try the old termios setting. * If it's still invalid, we try 9600 baud. * * Update the @termios structure to reflect the baud rate * we're actually going to be using. */unsigned intuart_get_baud_rate(struct uart_port *port, struct termios *termios, struct termios *old, unsigned int min, unsigned int max){ unsigned int try, baud, altbaud = 38400; upf_t flags = port->flags & UPF_SPD_MASK; if (flags == UPF_SPD_HI) altbaud = 57600; if (flags == UPF_SPD_VHI) altbaud = 115200; if (flags == UPF_SPD_SHI) altbaud = 230400; if (flags == UPF_SPD_WARP) altbaud = 460800; for (try = 0; try < 2; try++) { baud = tty_termios_baud_rate(termios); /* * The spd_hi, spd_vhi, spd_shi, spd_warp kludge... * Die! Die! Die! */ if (baud == 38400) baud = altbaud; /* * Special case: B0 rate. */ if (baud == 0) baud = 9600; if (baud >= min && baud <= max) return baud; /* * Oops, the quotient was zero. Try again with * the old baud rate if possible. */ termios->c_cflag &= ~CBAUD; if (old) { termios->c_cflag |= old->c_cflag & CBAUD; old = NULL; continue; } /* * As a last resort, if the quotient is zero, * default to 9600 bps */ termios->c_cflag |= B9600; } return 0;}EXPORT_SYMBOL(uart_get_baud_rate);/** * uart_get_divisor - return uart clock divisor * @port: uart_port structure describing the port. * @baud: desired baud rate * * Calculate the uart clock divisor for the port. */unsigned intuart_get_divisor(struct uart_port *port, unsigned int baud){ unsigned int quot; /* * Old custom speed handling. */ if (baud == 38400 && (port->flags & UPF_SPD_MASK) == UPF_SPD_CUST) quot = port->custom_divisor; else quot = (port->uartclk + (8 * baud)) / (16 * baud); return quot;}EXPORT_SYMBOL(uart_get_divisor);static voiduart_change_speed(struct uart_state *state, struct termios *old_termios){ struct tty_struct *tty = state->info->tty; struct uart_port *port = state->port; struct termios *termios; /* * If we have no tty, termios, or the port does not exist, * then we can't set the parameters for this port. */ if (!tty || !tty->termios || port->type == PORT_UNKNOWN) return; termios = tty->termios; /* * Set flags based on termios cflag */ if (termios->c_cflag & CRTSCTS) state->info->flags |= UIF_CTS_FLOW; else state->info->flags &= ~UIF_CTS_FLOW; if (termios->c_cflag & CLOCAL) state->info->flags &= ~UIF_CHECK_CD; else state->info->flags |= UIF_CHECK_CD; port->ops->set_termios(port, termios, old_termios);}static inline void__uart_put_char(struct uart_port *port, struct circ_buf *circ, unsigned char c){ unsigned long flags; if (!circ->buf) return; spin_lock_irqsave(&port->lock, flags); if (uart_circ_chars_free(circ) != 0) { circ->buf[circ->head] = c; circ->head = (circ->head + 1) & (UART_XMIT_SIZE - 1); } spin_unlock_irqrestore(&port->lock, flags);}static void uart_put_char(struct tty_struct *tty, unsigned char ch){ struct uart_state *state = tty->driver_data; __uart_put_char(state->port, &state->info->xmit, ch);}static void uart_flush_chars(struct tty_struct *tty){ uart_start(tty);}static intuart_write(struct tty_struct *tty, const unsigned char *buf, int count){ struct uart_state *state = tty->driver_data; struct uart_port *port; struct circ_buf *circ; unsigned long flags; int c, ret = 0; /* * This means you called this function _after_ the port was * closed. No cookie for you. */ if (!state || !state->info) { WARN_ON(1); return -EL3HLT; } port = state->port; circ = &state->info->xmit; if (!circ->buf) return 0; spin_lock_irqsave(&port->lock, flags); while (1) { c = CIRC_SPACE_TO_END(circ->head, circ->tail, UART_XMIT_SIZE); if (count < c) c = count; if (c <= 0) break; memcpy(circ->buf + circ->head, buf, c); circ->head = (circ->head + c) & (UART_XMIT_SIZE - 1); buf += c; count -= c; ret += c; } spin_unlock_irqrestore(&port->lock, flags); uart_start(tty); return ret;}static int uart_write_room(struct tty_struct *tty){ struct uart_state *state = tty->driver_data; return uart_circ_chars_free(&state->info->xmit);}static int uart_chars_in_buffer(struct tty_struct *tty){ struct uart_state *state = tty->driver_data; return uart_circ_chars_pending(&state->info->xmit);}static void uart_flush_buffer(struct tty_struct *tty){ struct uart_state *state = tty->driver_data; struct uart_port *port = state->port; unsigned long flags; /* * This means you called this function _after_ the port was * closed. No cookie for you. */ if (!state || !state->info) { WARN_ON(1); return; } DPRINTK("uart_flush_buffer(%d) called\n", tty->index); spin_lock_irqsave(&port->lock, flags); uart_circ_clear(&state->info->xmit); spin_unlock_irqrestore(&port->lock, flags); tty_wakeup(tty);}/* * This function is used to send a high-priority XON/XOFF character to * the device */static void uart_send_xchar(struct tty_struct *tty, char ch){ struct uart_state *state = tty->driver_data; struct uart_port *port = state->port; unsigned long flags; if (port->ops->send_xchar) port->ops->send_xchar(port, ch); else { port->x_char = ch; if (ch) { spin_lock_irqsave(&port->lock, flags); port->ops->start_tx(port); spin_unlock_irqrestore(&port->lock, flags); } }}static void uart_throttle(struct tty_struct *tty){ struct uart_state *state = tty->driver_data; if (I_IXOFF(tty)) uart_send_xchar(tty, STOP_CHAR(tty)); if (tty->termios->c_cflag & CRTSCTS) uart_clear_mctrl(state->port, TIOCM_RTS);}
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -