Merge git://git.kernel.org/pub/scm/linux/kernel/git/hskinnemoen/avr32-2.6
[pandora-kernel.git] / drivers / char / tty_io.c
index 35c7d2e..7501310 100644 (file)
@@ -78,6 +78,7 @@
 #include <linux/tty_flip.h>
 #include <linux/devpts_fs.h>
 #include <linux/file.h>
+#include <linux/fdtable.h>
 #include <linux/console.h>
 #include <linux/timer.h>
 #include <linux/ctype.h>
@@ -91,7 +92,6 @@
 #include <linux/module.h>
 #include <linux/smp_lock.h>
 #include <linux/device.h>
-#include <linux/idr.h>
 #include <linux/wait.h>
 #include <linux/bitops.h>
 #include <linux/delay.h>
@@ -137,9 +137,6 @@ EXPORT_SYMBOL(tty_mutex);
 
 #ifdef CONFIG_UNIX98_PTYS
 extern struct tty_driver *ptm_driver;  /* Unix98 pty masters; for /dev/ptmx */
-extern int pty_limit;                  /* Config limit on Unix98 ptys */
-static DEFINE_IDR(allocated_ptys);
-static DEFINE_MUTEX(allocated_ptys_lock);
 static int ptmx_open(struct inode *, struct file *);
 #endif
 
@@ -1108,8 +1105,8 @@ restart:
           a reference to the old ldisc. If we ended up flipping back
           to the existing ldisc we have two references to it */
 
-       if (tty->ldisc.num != o_ldisc.num && tty->driver->set_ldisc)
-               tty->driver->set_ldisc(tty);
+       if (tty->ldisc.num != o_ldisc.num && tty->ops->set_ldisc)
+               tty->ops->set_ldisc(tty);
 
        tty_ldisc_put(o_ldisc.num);
 
@@ -1181,9 +1178,8 @@ struct tty_driver *tty_find_polling_driver(char *name, int *line)
                if (*str == '\0')
                        str = NULL;
 
-               if (tty_line >= 0 && tty_line <= p->num && p->poll_init &&
-                               !p->poll_init(p, tty_line, str)) {
-
+               if (tty_line >= 0 && tty_line <= p->num && p->ops &&
+                   p->ops->poll_init && !p->ops->poll_init(p, tty_line, str)) {
                        res = p;
                        *line = tty_line;
                        break;
@@ -1204,7 +1200,7 @@ EXPORT_SYMBOL_GPL(tty_find_polling_driver);
  *     not in the foreground, send a SIGTTOU.  If the signal is blocked or
  *     ignored, go ahead and perform the operation.  (POSIX 7.2)
  *
- *     Locking: ctrl_lock - FIXME: review this
+ *     Locking: ctrl_lock
  */
 
 int tty_check_change(struct tty_struct *tty)
@@ -1219,10 +1215,11 @@ int tty_check_change(struct tty_struct *tty)
 
        if (!tty->pgrp) {
                printk(KERN_WARNING "tty_check_change: tty->pgrp == NULL!\n");
-               goto out;
+               goto out_unlock;
        }
        if (task_pgrp(current) == tty->pgrp)
-               goto out;
+               goto out_unlock;
+       spin_unlock_irqrestore(&tty->ctrl_lock, flags);
        if (is_ignored(SIGTTOU))
                goto out;
        if (is_current_pgrp_orphaned()) {
@@ -1233,6 +1230,8 @@ int tty_check_change(struct tty_struct *tty)
        set_thread_flag(TIF_SIGPENDING);
        ret = -ERESTARTSYS;
 out:
+       return ret;
+out_unlock:
        spin_unlock_irqrestore(&tty->ctrl_lock, flags);
        return ret;
 }
@@ -1452,8 +1451,7 @@ static void do_tty_hangup(struct work_struct *work)
                /* We may have no line discipline at this point */
                if (ld->flush_buffer)
                        ld->flush_buffer(tty);
-               if (tty->driver->flush_buffer)
-                       tty->driver->flush_buffer(tty);
+               tty_driver_flush_buffer(tty);
                if ((test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) &&
                    ld->write_wakeup)
                        ld->write_wakeup(tty);
@@ -1516,11 +1514,11 @@ static void do_tty_hangup(struct work_struct *work)
         * So we just call close() the right number of times.
         */
        if (cons_filp) {
-               if (tty->driver->close)
+               if (tty->ops->close)
                        for (n = 0; n < closecount; n++)
-                               tty->driver->close(tty, cons_filp);
-       } else if (tty->driver->hangup)
-               (tty->driver->hangup)(tty);
+                               tty->ops->close(tty, cons_filp);
+       } else if (tty->ops->hangup)
+               (tty->ops->hangup)(tty);
        /*
         * We don't want to have driver/ldisc interactions beyond
         * the ones we did here. The driver layer expects no
@@ -1752,8 +1750,8 @@ void stop_tty(struct tty_struct *tty)
                wake_up_interruptible(&tty->link->read_wait);
        }
        spin_unlock_irqrestore(&tty->ctrl_lock, flags);
-       if (tty->driver->stop)
-               (tty->driver->stop)(tty);
+       if (tty->ops->stop)
+               (tty->ops->stop)(tty);
 }
 
 EXPORT_SYMBOL(stop_tty);
@@ -1786,8 +1784,8 @@ void start_tty(struct tty_struct *tty)
                wake_up_interruptible(&tty->link->read_wait);
        }
        spin_unlock_irqrestore(&tty->ctrl_lock, flags);
-       if (tty->driver->start)
-               (tty->driver->start)(tty);
+       if (tty->ops->start)
+               (tty->ops->start)(tty);
        /* If we have a running line discipline it may need kicking */
        tty_wakeup(tty);
 }
@@ -1972,10 +1970,13 @@ static ssize_t tty_write(struct file *file, const char __user *buf,
        tty = (struct tty_struct *)file->private_data;
        if (tty_paranoia_check(tty, inode, "tty_write"))
                return -EIO;
-       if (!tty || !tty->driver->write ||
+       if (!tty || !tty->ops->write ||
                (test_bit(TTY_IO_ERROR, &tty->flags)))
                        return -EIO;
-
+       /* Short term debug to catch buggy drivers */
+       if (tty->ops->write_room == NULL)
+               printk(KERN_ERR "tty driver %s lacks a write_room method.\n",
+                       tty->driver->name);
        ld = tty_ldisc_ref_wait(tty);
        if (!ld->write)
                ret = -EIO;
@@ -2122,6 +2123,7 @@ static int init_dev(struct tty_driver *driver, int idx,
                goto fail_no_mem;
        initialize_tty_struct(tty);
        tty->driver = driver;
+       tty->ops = driver->ops;
        tty->index = idx;
        tty_line_name(driver, idx, tty->name);
 
@@ -2152,6 +2154,7 @@ static int init_dev(struct tty_driver *driver, int idx,
                        goto free_mem_out;
                initialize_tty_struct(o_tty);
                o_tty->driver = driver->other;
+               o_tty->ops = driver->ops;
                o_tty->index = idx;
                tty_line_name(driver->other, idx, o_tty->name);
 
@@ -2456,8 +2459,8 @@ static void release_dev(struct file *filp)
                }
        }
 #endif
-       if (tty->driver->close)
-               tty->driver->close(tty, filp);
+       if (tty->ops->close)
+               tty->ops->close(tty, filp);
 
        /*
         * Sanity check: if tty->count is going to zero, there shouldn't be
@@ -2636,15 +2639,9 @@ static void release_dev(struct file *filp)
         */
        release_tty(tty, idx);
 
-#ifdef CONFIG_UNIX98_PTYS
        /* Make this pty number available for reallocation */
-       if (devpts) {
-               mutex_lock(&allocated_ptys_lock);
-               idr_remove(&allocated_ptys, idx);
-               mutex_unlock(&allocated_ptys_lock);
-       }
-#endif
-
+       if (devpts)
+               devpts_kill_index(idx);
 }
 
 /**
@@ -2740,8 +2737,8 @@ got_driver:
        printk(KERN_DEBUG "opening %s...", tty->name);
 #endif
        if (!retval) {
-               if (tty->driver->open)
-                       retval = tty->driver->open(tty, filp);
+               if (tty->ops->open)
+                       retval = tty->ops->open(tty, filp);
                else
                        retval = -ENODEV;
        }
@@ -2800,29 +2797,13 @@ static int ptmx_open(struct inode *inode, struct file *filp)
        struct tty_struct *tty;
        int retval;
        int index;
-       int idr_ret;
 
        nonseekable_open(inode, filp);
 
        /* find a device that is not in use. */
-       mutex_lock(&allocated_ptys_lock);
-       if (!idr_pre_get(&allocated_ptys, GFP_KERNEL)) {
-               mutex_unlock(&allocated_ptys_lock);
-               return -ENOMEM;
-       }
-       idr_ret = idr_get_new(&allocated_ptys, NULL, &index);
-       if (idr_ret < 0) {
-               mutex_unlock(&allocated_ptys_lock);
-               if (idr_ret == -EAGAIN)
-                       return -ENOMEM;
-               return -EIO;
-       }
-       if (index >= pty_limit) {
-               idr_remove(&allocated_ptys, index);
-               mutex_unlock(&allocated_ptys_lock);
-               return -EIO;
-       }
-       mutex_unlock(&allocated_ptys_lock);
+       index = devpts_new_index();
+       if (index < 0)
+               return index;
 
        mutex_lock(&tty_mutex);
        retval = init_dev(ptm_driver, index, &tty);
@@ -2835,21 +2816,19 @@ static int ptmx_open(struct inode *inode, struct file *filp)
        filp->private_data = tty;
        file_move(filp, &tty->tty_files);
 
-       retval = -ENOMEM;
-       if (devpts_pty_new(tty->link))
+       retval = devpts_pty_new(tty->link);
+       if (retval)
                goto out1;
 
-       check_tty_count(tty, "tty_open");
-       retval = ptm_driver->open(tty, filp);
+       check_tty_count(tty, "ptmx_open");
+       retval = ptm_driver->ops->open(tty, filp);
        if (!retval)
                return 0;
 out1:
        release_dev(filp);
        return retval;
 out:
-       mutex_lock(&allocated_ptys_lock);
-       idr_remove(&allocated_ptys, index);
-       mutex_unlock(&allocated_ptys_lock);
+       devpts_kill_index(index);
        return retval;
 }
 #endif
@@ -3336,25 +3315,20 @@ static int tiocsetd(struct tty_struct *tty, int __user *p)
 
 static int send_break(struct tty_struct *tty, unsigned int duration)
 {
-       int retval = -EINTR;
-
-       lock_kernel();
        if (tty_write_lock(tty, 0) < 0)
-               goto out;
-       tty->driver->break_ctl(tty, -1);
+               return -EINTR;
+       tty->ops->break_ctl(tty, -1);
        if (!signal_pending(current))
                msleep_interruptible(duration);
-       tty->driver->break_ctl(tty, 0);
+       tty->ops->break_ctl(tty, 0);
        tty_write_unlock(tty);
-       if (!signal_pending(current))
-               retval = 0;
-out:
-       unlock_kernel();
-       return retval;
+       if (signal_pending(current))
+               return -EINTR;
+       return 0;
 }
 
 /**
- *     tiocmget                -       get modem status
+ *     tty_tiocmget            -       get modem status
  *     @tty: tty device
  *     @file: user file pointer
  *     @p: pointer to result
@@ -3369,10 +3343,8 @@ static int tty_tiocmget(struct tty_struct *tty, struct file *file, int __user *p
 {
        int retval = -EINVAL;
 
-       if (tty->driver->tiocmget) {
-               lock_kernel();
-               retval = tty->driver->tiocmget(tty, file);
-               unlock_kernel();
+       if (tty->ops->tiocmget) {
+               retval = tty->ops->tiocmget(tty, file);
 
                if (retval >= 0)
                        retval = put_user(retval, p);
@@ -3381,7 +3353,7 @@ static int tty_tiocmget(struct tty_struct *tty, struct file *file, int __user *p
 }
 
 /**
- *     tiocmset                -       set modem status
+ *     tty_tiocmset            -       set modem status
  *     @tty: tty device
  *     @file: user file pointer
  *     @cmd: command - clear bits, set bits or set all
@@ -3398,7 +3370,7 @@ static int tty_tiocmset(struct tty_struct *tty, struct file *file, unsigned int
 {
        int retval = -EINVAL;
 
-       if (tty->driver->tiocmset) {
+       if (tty->ops->tiocmset) {
                unsigned int set, clear, val;
 
                retval = get_user(val, p);
@@ -3422,9 +3394,7 @@ static int tty_tiocmset(struct tty_struct *tty, struct file *file, unsigned int
                set &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP;
                clear &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP;
 
-               lock_kernel();
-               retval = tty->driver->tiocmset(tty, file, set, clear);
-               unlock_kernel();
+               retval = tty->ops->tiocmset(tty, file, set, clear);
        }
        return retval;
 }
@@ -3455,23 +3425,25 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 
        retval = -EINVAL;
 
-       if (!tty->driver->break_ctl) {
+       if (!tty->ops->break_ctl) {
                switch (cmd) {
                case TIOCSBRK:
                case TIOCCBRK:
-                       if (tty->driver->ioctl)
-                               retval = tty->driver->ioctl(tty, file, cmd, arg);
+                       if (tty->ops->ioctl)
+                               retval = tty->ops->ioctl(tty, file, cmd, arg);
+                       if (retval != -EINVAL && retval != -ENOIOCTLCMD)
+                               printk(KERN_WARNING "tty: driver %s needs updating to use break_ctl\n", tty->driver->name);
                        return retval;
 
                /* These two ioctl's always return success; even if */
                /* the driver doesn't support them. */
                case TCSBRK:
                case TCSBRKP:
-                       if (!tty->driver->ioctl)
+                       if (!tty->ops->ioctl)
                                return 0;
-                       lock_kernel();
-                       retval = tty->driver->ioctl(tty, file, cmd, arg);
-                       unlock_kernel();
+                       retval = tty->ops->ioctl(tty, file, cmd, arg);
+                       if (retval != -EINVAL && retval != -ENOIOCTLCMD)
+                               printk(KERN_WARNING "tty: driver %s needs updating to use break_ctl\n", tty->driver->name);
                        if (retval == -ENOIOCTLCMD)
                                retval = 0;
                        return retval;
@@ -3491,9 +3463,7 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
                if (retval)
                        return retval;
                if (cmd != TIOCCBRK) {
-                       lock_kernel();
                        tty_wait_until_sent(tty, 0);
-                       unlock_kernel();
                        if (signal_pending(current))
                                return -EINTR;
                }
@@ -3531,7 +3501,6 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
        case TIOCGSID:
                return tiocgsid(tty, real_tty, p);
        case TIOCGETD:
-               /* FIXME: check this is ok */
                return put_user(tty->ldisc.num, (int __user *)p);
        case TIOCSETD:
                return tiocsetd(tty, p);
@@ -3543,15 +3512,13 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
         * Break handling
         */
        case TIOCSBRK:  /* Turn break on, unconditionally */
-               lock_kernel();
-               tty->driver->break_ctl(tty, -1);
-               unlock_kernel();
+               if (tty->ops->break_ctl)
+                       tty->ops->break_ctl(tty, -1);
                return 0;
 
        case TIOCCBRK:  /* Turn break off, unconditionally */
-               lock_kernel();
-               tty->driver->break_ctl(tty, 0);
-               unlock_kernel();
+               if (tty->ops->break_ctl)
+                       tty->ops->break_ctl(tty, 0);
                return 0;
        case TCSBRK:   /* SVID version: non-zero arg --> no break */
                /* non-zero arg means wait for all output data
@@ -3580,8 +3547,8 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
                }
                break;
        }
-       if (tty->driver->ioctl) {
-               retval = (tty->driver->ioctl)(tty, file, cmd, arg);
+       if (tty->ops->ioctl) {
+               retval = (tty->ops->ioctl)(tty, file, cmd, arg);
                if (retval != -ENOIOCTLCMD)
                        return retval;
        }
@@ -3608,8 +3575,8 @@ static long tty_compat_ioctl(struct file *file, unsigned int cmd,
        if (tty_paranoia_check(tty, inode, "tty_ioctl"))
                return -EINVAL;
 
-       if (tty->driver->compat_ioctl) {
-               retval = (tty->driver->compat_ioctl)(tty, file, cmd, arg);
+       if (tty->ops->compat_ioctl) {
+               retval = (tty->ops->compat_ioctl)(tty, file, cmd, arg);
                if (retval != -ENOIOCTLCMD)
                        return retval;
        }
@@ -3659,8 +3626,7 @@ void __do_SAK(struct tty_struct *tty)
 
        tty_ldisc_flush(tty);
 
-       if (tty->driver->flush_buffer)
-               tty->driver->flush_buffer(tty);
+       tty_driver_flush_buffer(tty);
 
        read_lock(&tasklist_lock);
        /* Kill the entire session */
@@ -3871,15 +3837,27 @@ static void initialize_tty_struct(struct tty_struct *tty)
        INIT_WORK(&tty->SAK_work, do_SAK_work);
 }
 
-/*
- * The default put_char routine if the driver did not define one.
+/**
+ *     tty_put_char    -       write one character to a tty
+ *     @tty: tty
+ *     @ch: character
+ *
+ *     Write one byte to the tty using the provided put_char method
+ *     if present. Returns the number of characters successfully output.
+ *
+ *     Note: the specific put_char operation in the driver layer may go
+ *     away soon. Don't call it directly, use this method
  */
 
-static void tty_default_put_char(struct tty_struct *tty, unsigned char ch)
+int tty_put_char(struct tty_struct *tty, unsigned char ch)
 {
-       tty->driver->write(tty, &ch, 1);
+       if (tty->ops->put_char)
+               return tty->ops->put_char(tty, ch);
+       return tty->ops->write(tty, &ch, 1);
 }
 
+EXPORT_SYMBOL_GPL(tty_put_char);
+
 static struct class *tty_class;
 
 /**
@@ -3962,37 +3940,8 @@ void put_tty_driver(struct tty_driver *driver)
 void tty_set_operations(struct tty_driver *driver,
                        const struct tty_operations *op)
 {
-       driver->open = op->open;
-       driver->close = op->close;
-       driver->write = op->write;
-       driver->put_char = op->put_char;
-       driver->flush_chars = op->flush_chars;
-       driver->write_room = op->write_room;
-       driver->chars_in_buffer = op->chars_in_buffer;
-       driver->ioctl = op->ioctl;
-       driver->compat_ioctl = op->compat_ioctl;
-       driver->set_termios = op->set_termios;
-       driver->throttle = op->throttle;
-       driver->unthrottle = op->unthrottle;
-       driver->stop = op->stop;
-       driver->start = op->start;
-       driver->hangup = op->hangup;
-       driver->break_ctl = op->break_ctl;
-       driver->flush_buffer = op->flush_buffer;
-       driver->set_ldisc = op->set_ldisc;
-       driver->wait_until_sent = op->wait_until_sent;
-       driver->send_xchar = op->send_xchar;
-       driver->read_proc = op->read_proc;
-       driver->write_proc = op->write_proc;
-       driver->tiocmget = op->tiocmget;
-       driver->tiocmset = op->tiocmset;
-#ifdef CONFIG_CONSOLE_POLL
-       driver->poll_init = op->poll_init;
-       driver->poll_get_char = op->poll_get_char;
-       driver->poll_put_char = op->poll_put_char;
-#endif
-}
-
+       driver->ops = op;
+};
 
 EXPORT_SYMBOL(alloc_tty_driver);
 EXPORT_SYMBOL(put_tty_driver);
@@ -4055,9 +4004,6 @@ int tty_register_driver(struct tty_driver *driver)
                return error;
        }
 
-       if (!driver->put_char)
-               driver->put_char = tty_default_put_char;
-
        mutex_lock(&tty_mutex);
        list_add(&driver->tty_drivers, &tty_drivers);
        mutex_unlock(&tty_mutex);