]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/serial/serial_core.c
tty: The big operations rework
[linux-2.6-omap-h63xx.git] / drivers / serial / serial_core.c
index 276da148c57e015a6a148ec60a028d3d2e840ba6..1e2b9d826f69a5db2756e8eb477f2249587122e4 100644 (file)
@@ -58,7 +58,8 @@ static struct lock_class_key port_lock_key;
 #define uart_console(port)     (0)
 #endif
 
-static void uart_change_speed(struct uart_state *state, struct ktermios *old_termios);
+static void uart_change_speed(struct uart_state *state,
+                                       struct ktermios *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);
 
@@ -129,8 +130,8 @@ uart_update_mctrl(struct uart_port *port, unsigned int set, unsigned int clear)
        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)
+#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
@@ -290,7 +291,7 @@ uart_update_timeout(struct uart_port *port, unsigned int cflag,
                break;
        default:
                bits = 10;
-               break; // CS8
+               break; /* CS8 */
        }
 
        if (cflag & CSTOPB)
@@ -328,13 +329,15 @@ EXPORT_SYMBOL(uart_update_timeout);
  *     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.
+ *     we're actually going to be using. Don't do this for the case
+ *     where B0 is requested ("hang up").
  */
 unsigned int
 uart_get_baud_rate(struct uart_port *port, struct ktermios *termios,
                   struct ktermios *old, unsigned int min, unsigned int max)
 {
        unsigned int try, baud, altbaud = 38400;
+       int hung_up = 0;
        upf_t flags = port->flags & UPF_SPD_MASK;
 
        if (flags == UPF_SPD_HI)
@@ -359,8 +362,10 @@ uart_get_baud_rate(struct uart_port *port, struct ktermios *termios,
                /*
                 * Special case: B0 rate.
                 */
-               if (baud == 0)
+               if (baud == 0) {
+                       hung_up = 1;
                        baud = 9600;
+               }
 
                if (baud >= min && baud <= max)
                        return baud;
@@ -372,7 +377,9 @@ uart_get_baud_rate(struct uart_port *port, struct ktermios *termios,
                termios->c_cflag &= ~CBAUD;
                if (old) {
                        baud = tty_termios_baud_rate(old);
-                       tty_termios_encode_baud_rate(termios, baud, baud);
+                       if (!hung_up)
+                               tty_termios_encode_baud_rate(termios,
+                                                               baud, baud);
                        old = NULL;
                        continue;
                }
@@ -381,7 +388,8 @@ uart_get_baud_rate(struct uart_port *port, struct ktermios *termios,
                 * As a last resort, if the quotient is zero,
                 * default to 9600 bps
                 */
-               tty_termios_encode_baud_rate(termios, 9600, 9600);
+               if (!hung_up)
+                       tty_termios_encode_baud_rate(termios, 9600, 9600);
        }
 
        return 0;
@@ -414,6 +422,7 @@ uart_get_divisor(struct uart_port *port, unsigned int baud)
 
 EXPORT_SYMBOL(uart_get_divisor);
 
+/* FIXME: Consistent locking policy */
 static void
 uart_change_speed(struct uart_state *state, struct ktermios *old_termios)
 {
@@ -446,27 +455,30 @@ uart_change_speed(struct uart_state *state, struct ktermios *old_termios)
        port->ops->set_termios(port, termios, old_termios);
 }
 
-static inline void
+static inline int
 __uart_put_char(struct uart_port *port, struct circ_buf *circ, unsigned char c)
 {
        unsigned long flags;
+       int ret = 0;
 
        if (!circ->buf)
-               return;
+               return 0;
 
        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);
+               ret = 1;
        }
        spin_unlock_irqrestore(&port->lock, flags);
+       return ret;
 }
 
-static void uart_put_char(struct tty_struct *tty, unsigned char ch)
+static int 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);
+       return __uart_put_char(state->port, &state->info->xmit, ch);
 }
 
 static void uart_flush_chars(struct tty_struct *tty)
@@ -520,15 +532,25 @@ uart_write(struct tty_struct *tty, const unsigned char *buf, int count)
 static int uart_write_room(struct tty_struct *tty)
 {
        struct uart_state *state = tty->driver_data;
+       unsigned long flags;
+       int ret;
 
-       return uart_circ_chars_free(&state->info->xmit);
+       spin_lock_irqsave(&state->port->lock, flags);
+       ret = uart_circ_chars_free(&state->info->xmit);
+       spin_unlock_irqrestore(&state->port->lock, flags);
+       return ret;
 }
 
 static int uart_chars_in_buffer(struct tty_struct *tty)
 {
        struct uart_state *state = tty->driver_data;
+       unsigned long flags;
+       int ret;
 
-       return uart_circ_chars_pending(&state->info->xmit);
+       spin_lock_irqsave(&state->port->lock, flags);
+       ret = uart_circ_chars_pending(&state->info->xmit);
+       spin_unlock_irqrestore(&state->port->lock, flags);
+       return ret;
 }
 
 static void uart_flush_buffer(struct tty_struct *tty)
@@ -610,6 +632,11 @@ static int uart_get_info(struct uart_state *state,
        struct serial_struct tmp;
 
        memset(&tmp, 0, sizeof(tmp));
+
+       /* Ensure the state we copy is consistent and no hardware changes
+          occur as we go */
+       mutex_lock(&state->mutex);
+
        tmp.type            = port->type;
        tmp.line            = port->line;
        tmp.port            = port->iobase;
@@ -622,13 +649,15 @@ static int uart_get_info(struct uart_state *state,
        tmp.close_delay     = state->close_delay / 10;
        tmp.closing_wait    = state->closing_wait == USF_CLOSING_WAIT_NONE ?
                                ASYNC_CLOSING_WAIT_NONE :
-                               state->closing_wait / 10;
+                               state->closing_wait / 10;
        tmp.custom_divisor  = port->custom_divisor;
        tmp.hub6            = port->hub6;
        tmp.io_type         = port->iotype;
        tmp.iomem_reg_shift = port->regshift;
        tmp.iomem_base      = (void *)(unsigned long)port->mapbase;
 
+       mutex_unlock(&state->mutex);
+
        if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
                return -EFAULT;
        return 0;
@@ -788,7 +817,8 @@ static int uart_set_info(struct uart_state *state,
                         * We failed anyway.
                         */
                        retval = -EBUSY;
-                       goto exit;  // Added to return the correct error -Ram Gupta
+                       /* Added to return the correct error -Ram Gupta */
+                       goto exit;
                }
        }
 
@@ -858,7 +888,7 @@ static int uart_get_lsr_info(struct uart_state *state,
            ((uart_circ_chars_pending(&state->info->xmit) > 0) &&
             !state->info->tty->stopped && !state->info->tty->hw_stopped))
                result &= ~TIOCSER_TEMT;
-       
+
        return put_user(result, value);
 }
 
@@ -905,8 +935,6 @@ static void uart_break_ctl(struct tty_struct *tty, int break_state)
        struct uart_state *state = tty->driver_data;
        struct uart_port *port = state->port;
 
-       BUG_ON(!kernel_locked());
-
        mutex_lock(&state->mutex);
 
        if (port->type != PORT_UNKNOWN)
@@ -996,8 +1024,8 @@ uart_wait_modem_status(struct uart_state *state, unsigned long arg)
                    ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
                    ((arg & TIOCM_CD)  && (cnow.dcd != cprev.dcd)) ||
                    ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts))) {
-                       ret = 0;
-                       break;
+                       ret = 0;
+                       break;
                }
 
                schedule();
@@ -1050,7 +1078,7 @@ static int uart_get_count(struct uart_state *state,
 }
 
 /*
- * Called via sys_ioctl under the BKL.  We can use spin_lock_irq() here.
+ * Called via sys_ioctl.  We can use spin_lock_irq() here.
  */
 static int
 uart_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd,
@@ -1060,7 +1088,6 @@ uart_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd,
        void __user *uarg = (void __user *)arg;
        int ret = -ENOIOCTLCMD;
 
-       BUG_ON(!kernel_locked());
 
        /*
         * These ioctls don't rely on the hardware to be present.
@@ -1131,19 +1158,19 @@ uart_ioctl(struct tty_struct *tty, struct file *filp, unsigned int cmd,
                break;
        }
        }
- out_up:
+out_up:
        mutex_unlock(&state->mutex);
- out:
+out:
        return ret;
 }
 
-static void uart_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
+static void uart_set_termios(struct tty_struct *tty,
+                                               struct ktermios *old_termios)
 {
        struct uart_state *state = tty->driver_data;
        unsigned long flags;
        unsigned int cflag = tty->termios->c_cflag;
 
-       BUG_ON(!kernel_locked());
 
        /*
         * These are the bits that are used to setup various
@@ -1155,8 +1182,9 @@ static void uart_set_termios(struct tty_struct *tty, struct ktermios *old_termio
        if ((cflag ^ old_termios->c_cflag) == 0 &&
            tty->termios->c_ospeed == old_termios->c_ospeed &&
            tty->termios->c_ispeed == old_termios->c_ispeed &&
-           RELEVANT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0)
+           RELEVANT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0) {
                return;
+       }
 
        uart_change_speed(state, old_termios);
 
@@ -1190,7 +1218,6 @@ static void uart_set_termios(struct tty_struct *tty, struct ktermios *old_termio
                }
                spin_unlock_irqrestore(&state->port->lock, flags);
        }
-
 #if 0
        /*
         * No need to wake up processes in open wait, since they
@@ -1213,7 +1240,7 @@ static void uart_close(struct tty_struct *tty, struct file *filp)
 {
        struct uart_state *state = tty->driver_data;
        struct uart_port *port;
-       
+
        BUG_ON(!kernel_locked());
 
        if (!state || !state->port)
@@ -1278,8 +1305,8 @@ static void uart_close(struct tty_struct *tty, struct file *filp)
        uart_shutdown(state);
        uart_flush_buffer(tty);
 
-       tty_ldisc_flush(tty);   
-       
+       tty_ldisc_flush(tty);
+
        tty->closing = 0;
        state->info->tty = NULL;
 
@@ -1306,11 +1333,11 @@ static void uart_wait_until_sent(struct tty_struct *tty, int timeout)
        struct uart_port *port = state->port;
        unsigned long char_time, expire;
 
-       BUG_ON(!kernel_locked());
-
        if (port->type == PORT_UNKNOWN || port->fifosize == 0)
                return;
 
+       lock_kernel();
+
        /*
         * 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
@@ -1341,7 +1368,7 @@ static void uart_wait_until_sent(struct tty_struct *tty, int timeout)
        expire = jiffies + timeout;
 
        pr_debug("uart_wait_until_sent(%d), jiffies=%lu, expire=%lu...\n",
-               port->line, jiffies, expire);
+               port->line, jiffies, expire);
 
        /*
         * Check whether the transmitter is empty every 'char_time'.
@@ -1356,6 +1383,7 @@ static void uart_wait_until_sent(struct tty_struct *tty, int timeout)
                        break;
        }
        set_current_state(TASK_RUNNING); /* might not be needed */
+       unlock_kernel();
 }
 
 /*
@@ -1460,10 +1488,9 @@ uart_block_til_ready(struct file *filp, struct uart_state *state)
                 * 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))) {
+                   (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
@@ -1551,8 +1578,8 @@ static struct uart_state *uart_get(struct uart_driver *drv, int line)
 }
 
 /*
- * In 2.4.5, calls to uart_open are serialised by the BKL in
- *   linux/fs/devices.c:chrdev_open()
+ * calls to uart_open are serialised by the BKL in
+ *   fs/char_dev.c:chrdev_open()
  * Note that if this fails, then uart_close() _will_ be called.
  *
  * In time, we want to scrap the "opening nonpresent ports"
@@ -1674,7 +1701,7 @@ static int uart_line_info(char *buf, struct uart_driver *drv, int i)
                        port->line, uart_type(port),
                        mmio ? "mmio:0x" : "port:",
                        mmio ? (unsigned long long)port->mapbase
-                            : (unsigned long long) port->iobase,
+                            : (unsigned long long) port->iobase,
                        port->irq);
 
        if (port->type == PORT_UNKNOWN) {
@@ -1682,8 +1709,7 @@ static int uart_line_info(char *buf, struct uart_driver *drv, int i)
                return ret + 1;
        }
 
-       if(capable(CAP_SYS_ADMIN))
-       {
+       if (capable(CAP_SYS_ADMIN)) {
                mutex_lock(&state->mutex);
                pm_state = state->pm_state;
                if (pm_state)
@@ -1709,12 +1735,12 @@ static int uart_line_info(char *buf, struct uart_driver *drv, int i)
                if (port->icount.overrun)
                        ret += sprintf(buf + ret, " oe:%d",
                                port->icount.overrun);
-       
-#define INFOBIT(bit,str) \
+
+#define INFOBIT(bit, str) \
        if (port->mctrl & (bit)) \
                strncat(stat_buf, (str), sizeof(stat_buf) - \
                        strlen(stat_buf) - 2)
-#define STATBIT(bit,str) \
+#define STATBIT(bit, str) \
        if (status & (bit)) \
                strncat(stat_buf, (str), sizeof(stat_buf) - \
                       strlen(stat_buf) - 2)
@@ -1730,7 +1756,7 @@ static int uart_line_info(char *buf, struct uart_driver *drv, int i)
                if (stat_buf[0])
                        stat_buf[0] = ' ';
                strcat(stat_buf, "\n");
-       
+
                ret += sprintf(buf + ret, stat_buf);
        } else {
                strcat(buf, "\n");
@@ -1770,7 +1796,7 @@ static int uart_read_proc(char *page, char **start, off_t off,
 }
 #endif
 
-#ifdef CONFIG_SERIAL_CORE_CONSOLE
+#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(CONFIG_CONSOLE_POLL)
 /*
  *     uart_console_write - write a console message to a serial port
  *     @port: the port to write the message
@@ -1826,7 +1852,7 @@ uart_get_console(struct uart_port *ports, int nr, struct console *co)
  *     options.  The format of the string is <baud><parity><bits><flow>,
  *     eg: 115200n8r
  */
-void __init
+void
 uart_parse_options(char *options, int *baud, int *parity, int *bits, int *flow)
 {
        char *s = options;
@@ -1841,6 +1867,7 @@ uart_parse_options(char *options, int *baud, int *parity, int *bits, int *flow)
        if (*s)
                *flow = *s;
 }
+EXPORT_SYMBOL_GPL(uart_parse_options);
 
 struct baud_rates {
        unsigned int rate;
@@ -1871,7 +1898,7 @@ static const struct baud_rates baud_rates[] = {
  *     @bits: number of data bits
  *     @flow: flow control character - 'r' (rts)
  */
-int __init
+int
 uart_set_options(struct uart_port *port, struct console *co,
                 int baud, int parity, int bits, int flow)
 {
@@ -1923,10 +1950,16 @@ uart_set_options(struct uart_port *port, struct console *co,
        port->mctrl |= TIOCM_DTR;
 
        port->ops->set_termios(port, &termios, &dummy);
-       co->cflag = termios.c_cflag;
+       /*
+        * Allow the setting of the UART parameters with a NULL console
+        * too:
+        */
+       if (co)
+               co->cflag = termios.c_cflag;
 
        return 0;
 }
+EXPORT_SYMBOL_GPL(uart_set_options);
 #endif /* CONFIG_SERIAL_CORE_CONSOLE */
 
 static void uart_change_pm(struct uart_state *state, int pm_state)
@@ -1992,11 +2025,11 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *port)
                /*
                 * Wait for the transmitter to empty.
                 */
-               for (tries = 3; !ops->tx_empty(port) && tries; tries--) {
+               for (tries = 3; !ops->tx_empty(port) && tries; tries--)
                        msleep(10);
-               }
                if (!tries)
-                       printk(KERN_ERR "%s%s%s%d: Unable to drain transmitter\n",
+                       printk(KERN_ERR "%s%s%s%d: Unable to drain "
+                                       "transmitter\n",
                               port->dev ? port->dev->bus_id : "",
                               port->dev ? ": " : "",
                               drv->dev_name, port->line);
@@ -2064,7 +2097,9 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *port)
                int ret;
 
                uart_change_pm(state, 0);
+               spin_lock_irq(&port->lock);
                ops->set_mctrl(port, 0);
+               spin_unlock_irq(&port->lock);
                ret = ops->startup(port);
                if (ret == 0) {
                        uart_change_speed(state, NULL);
@@ -2181,6 +2216,60 @@ uart_configure_port(struct uart_driver *drv, struct uart_state *state,
        }
 }
 
+#ifdef CONFIG_CONSOLE_POLL
+
+static int uart_poll_init(struct tty_driver *driver, int line, char *options)
+{
+       struct uart_driver *drv = driver->driver_state;
+       struct uart_state *state = drv->state + line;
+       struct uart_port *port;
+       int baud = 9600;
+       int bits = 8;
+       int parity = 'n';
+       int flow = 'n';
+
+       if (!state || !state->port)
+               return -1;
+
+       port = state->port;
+       if (!(port->ops->poll_get_char && port->ops->poll_put_char))
+               return -1;
+
+       if (options) {
+               uart_parse_options(options, &baud, &parity, &bits, &flow);
+               return uart_set_options(port, NULL, baud, parity, bits, flow);
+       }
+
+       return 0;
+}
+
+static int uart_poll_get_char(struct tty_driver *driver, int line)
+{
+       struct uart_driver *drv = driver->driver_state;
+       struct uart_state *state = drv->state + line;
+       struct uart_port *port;
+
+       if (!state || !state->port)
+               return -1;
+
+       port = state->port;
+       return port->ops->poll_get_char(port);
+}
+
+static void uart_poll_put_char(struct tty_driver *driver, int line, char ch)
+{
+       struct uart_driver *drv = driver->driver_state;
+       struct uart_state *state = drv->state + line;
+       struct uart_port *port;
+
+       if (!state || !state->port)
+               return;
+
+       port = state->port;
+       port->ops->poll_put_char(port, ch);
+}
+#endif
+
 static const struct tty_operations uart_ops = {
        .open           = uart_open,
        .close          = uart_close,
@@ -2205,6 +2294,11 @@ static const struct tty_operations uart_ops = {
 #endif
        .tiocmget       = uart_tiocmget,
        .tiocmset       = uart_tiocmset,
+#ifdef CONFIG_CONSOLE_POLL
+       .poll_init      = uart_poll_init,
+       .poll_get_char  = uart_poll_get_char,
+       .poll_put_char  = uart_poll_put_char,
+#endif
 };
 
 /**
@@ -2355,7 +2449,7 @@ int uart_add_one_port(struct uart_driver *drv, struct uart_port *port)
         */
        tty_dev = tty_register_device(drv->tty_driver, port->line, port->dev);
        if (likely(!IS_ERR(tty_dev))) {
-               device_can_wakeup(tty_dev) = 1;
+               device_init_wakeup(tty_dev, 1);
                device_set_wakeup_enable(tty_dev, 0);
        } else
                printk(KERN_ERR "Cannot register tty device on line %d\n",