tty: The big operations rework

- Operations are now a shared const function block as with most other Linux
  objects

- Introduce wrappers for some optional functions to get consistent behaviour

- Wrap put_char which used to be patched by the tty layer

- Document which functions are needed/optional

- Make put_char report success/fail

- Cache the driver->ops pointer in the tty as tty->ops

- Remove various surplus lock calls we no longer need

- Remove proc_write method as noted by Alexey Dobriyan

- Introduce some missing sanity checks where certain driver/ldisc
  combinations would oops as they didn't check needed methods were present

[akpm@linux-foundation.org: fix fs/compat_ioctl.c build]
[akpm@linux-foundation.org: fix isicom]
[akpm@linux-foundation.org: fix arch/ia64/hp/sim/simserial.c build]
[akpm@linux-foundation.org: fix kgdb]
Signed-off-by: Alan Cox <alan@redhat.com>
Acked-by: Greg Kroah-Hartman <gregkh@suse.de>
Cc: Jason Wessel <jason.wessel@windriver.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Alan Cox
2008-04-30 00:54:13 -07:00
committed by Linus Torvalds
parent 251b8dd7ee
commit f34d7a5b70
30 changed files with 539 additions and 666 deletions

View File

@ -1108,8 +1108,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 +1181,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;
@ -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
@ -2740,8 +2743,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;
}
@ -2840,7 +2843,7 @@ static int ptmx_open(struct inode *inode, struct file *filp)
goto out1;
check_tty_count(tty, "tty_open");
retval = ptm_driver->open(tty, filp);
retval = ptm_driver->ops->open(tty, filp);
if (!retval)
return 0;
out1:
@ -3336,25 +3339,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;
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 +3367,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 +3377,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 +3394,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 +3418,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 +3449,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 +3487,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 +3525,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 +3536,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 +3571,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 +3599,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 +3650,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 +3861,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 +3964,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 +4028,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);