]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/serial/serial_core.c
serial: fix enable_irq_wake/disable_irq_wake imbalance in serial_core.c
[linux-2.6-omap-h63xx.git] / drivers / serial / serial_core.c
index 977ce820ce303914dc7e998940cdf524dec52586..53b03c629afffc86812e4fc22ba973d0c757cc5a 100644 (file)
@@ -422,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)
 {
@@ -454,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)
@@ -528,21 +532,31 @@ 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)
 {
        struct uart_state *state = tty->driver_data;
-       struct uart_port *port = state->port;
+       struct uart_port *port;
        unsigned long flags;
 
        /*
@@ -554,6 +568,7 @@ static void uart_flush_buffer(struct tty_struct *tty)
                return;
        }
 
+       port = state->port;
        pr_debug("uart_flush_buffer(%d) called\n", tty->index);
 
        spin_lock_irqsave(&port->lock, flags);
@@ -618,6 +633,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;
@@ -637,6 +657,8 @@ static int uart_get_info(struct uart_state *state,
        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;
@@ -914,8 +936,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)
@@ -1059,7 +1079,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,
@@ -1069,7 +1089,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.
@@ -1140,9 +1159,9 @@ 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;
 }
 
@@ -1153,7 +1172,6 @@ static void uart_set_termios(struct tty_struct *tty,
        unsigned long flags;
        unsigned int cflag = tty->termios->c_cflag;
 
-       BUG_ON(!kernel_locked());
 
        /*
         * These are the bits that are used to setup various
@@ -1165,8 +1183,9 @@ static void uart_set_termios(struct tty_struct *tty,
        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);
 
@@ -1200,7 +1219,6 @@ static void uart_set_termios(struct tty_struct *tty,
                }
                spin_unlock_irqrestore(&state->port->lock, flags);
        }
-
 #if 0
        /*
         * No need to wake up processes in open wait, since they
@@ -1316,11 +1334,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
@@ -1366,6 +1384,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();
 }
 
 /*
@@ -2035,6 +2054,8 @@ int uart_suspend_port(struct uart_driver *drv, struct uart_port *port)
 int uart_resume_port(struct uart_driver *drv, struct uart_port *port)
 {
        struct uart_state *state = drv->state + port->line;
+       struct device *tty_dev;
+       struct uart_match match = {port, drv};
 
        mutex_lock(&state->mutex);
 
@@ -2044,7 +2065,8 @@ int uart_resume_port(struct uart_driver *drv, struct uart_port *port)
                return 0;
        }
 
-       if (!port->suspended) {
+       tty_dev = device_find_child(port->dev, &match, serial_match_port);
+       if (!port->suspended && device_may_wakeup(tty_dev)) {
                disable_irq_wake(port->irq);
                mutex_unlock(&state->mutex);
                return 0;
@@ -2079,7 +2101,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);