|
@@ -95,8 +95,9 @@
|
|
|
#include <linux/wait.h>
|
|
|
#include <linux/bitops.h>
|
|
|
#include <linux/delay.h>
|
|
|
+#include <linux/seq_file.h>
|
|
|
|
|
|
-#include <asm/uaccess.h>
|
|
|
+#include <linux/uaccess.h>
|
|
|
#include <asm/system.h>
|
|
|
|
|
|
#include <linux/kbd_kern.h>
|
|
@@ -682,7 +683,7 @@ static void tty_set_termios_ldisc(struct tty_struct *tty, int num)
|
|
|
static DEFINE_SPINLOCK(tty_ldisc_lock);
|
|
|
static DECLARE_WAIT_QUEUE_HEAD(tty_ldisc_wait);
|
|
|
/* Line disc dispatch table */
|
|
|
-static struct tty_ldisc tty_ldiscs[NR_LDISCS];
|
|
|
+static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS];
|
|
|
|
|
|
/**
|
|
|
* tty_register_ldisc - install a line discipline
|
|
@@ -697,7 +698,7 @@ static struct tty_ldisc tty_ldiscs[NR_LDISCS];
|
|
|
* takes tty_ldisc_lock to guard against ldisc races
|
|
|
*/
|
|
|
|
|
|
-int tty_register_ldisc(int disc, struct tty_ldisc *new_ldisc)
|
|
|
+int tty_register_ldisc(int disc, struct tty_ldisc_ops *new_ldisc)
|
|
|
{
|
|
|
unsigned long flags;
|
|
|
int ret = 0;
|
|
@@ -706,10 +707,9 @@ int tty_register_ldisc(int disc, struct tty_ldisc *new_ldisc)
|
|
|
return -EINVAL;
|
|
|
|
|
|
spin_lock_irqsave(&tty_ldisc_lock, flags);
|
|
|
- tty_ldiscs[disc] = *new_ldisc;
|
|
|
- tty_ldiscs[disc].num = disc;
|
|
|
- tty_ldiscs[disc].flags |= LDISC_FLAG_DEFINED;
|
|
|
- tty_ldiscs[disc].refcount = 0;
|
|
|
+ tty_ldiscs[disc] = new_ldisc;
|
|
|
+ new_ldisc->num = disc;
|
|
|
+ new_ldisc->refcount = 0;
|
|
|
spin_unlock_irqrestore(&tty_ldisc_lock, flags);
|
|
|
|
|
|
return ret;
|
|
@@ -737,19 +737,56 @@ int tty_unregister_ldisc(int disc)
|
|
|
return -EINVAL;
|
|
|
|
|
|
spin_lock_irqsave(&tty_ldisc_lock, flags);
|
|
|
- if (tty_ldiscs[disc].refcount)
|
|
|
+ if (tty_ldiscs[disc]->refcount)
|
|
|
ret = -EBUSY;
|
|
|
else
|
|
|
- tty_ldiscs[disc].flags &= ~LDISC_FLAG_DEFINED;
|
|
|
+ tty_ldiscs[disc] = NULL;
|
|
|
spin_unlock_irqrestore(&tty_ldisc_lock, flags);
|
|
|
|
|
|
return ret;
|
|
|
}
|
|
|
EXPORT_SYMBOL(tty_unregister_ldisc);
|
|
|
|
|
|
+
|
|
|
+/**
|
|
|
+ * tty_ldisc_try_get - try and reference an ldisc
|
|
|
+ * @disc: ldisc number
|
|
|
+ * @ld: tty ldisc structure to complete
|
|
|
+ *
|
|
|
+ * Attempt to open and lock a line discipline into place. Return
|
|
|
+ * the line discipline refcounted and assigned in ld. On an error
|
|
|
+ * report the error code back
|
|
|
+ */
|
|
|
+
|
|
|
+static int tty_ldisc_try_get(int disc, struct tty_ldisc *ld)
|
|
|
+{
|
|
|
+ unsigned long flags;
|
|
|
+ struct tty_ldisc_ops *ldops;
|
|
|
+ int err = -EINVAL;
|
|
|
+
|
|
|
+ spin_lock_irqsave(&tty_ldisc_lock, flags);
|
|
|
+ ld->ops = NULL;
|
|
|
+ ldops = tty_ldiscs[disc];
|
|
|
+ /* Check the entry is defined */
|
|
|
+ if (ldops) {
|
|
|
+ /* If the module is being unloaded we can't use it */
|
|
|
+ if (!try_module_get(ldops->owner))
|
|
|
+ err = -EAGAIN;
|
|
|
+ else {
|
|
|
+ /* lock it */
|
|
|
+ ldops->refcount++;
|
|
|
+ ld->ops = ldops;
|
|
|
+ err = 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ spin_unlock_irqrestore(&tty_ldisc_lock, flags);
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* tty_ldisc_get - take a reference to an ldisc
|
|
|
* @disc: ldisc number
|
|
|
+ * @ld: tty line discipline structure to use
|
|
|
*
|
|
|
* Takes a reference to a line discipline. Deals with refcounts and
|
|
|
* module locking counts. Returns NULL if the discipline is not available.
|
|
@@ -760,32 +797,20 @@ EXPORT_SYMBOL(tty_unregister_ldisc);
|
|
|
* takes tty_ldisc_lock to guard against ldisc races
|
|
|
*/
|
|
|
|
|
|
-struct tty_ldisc *tty_ldisc_get(int disc)
|
|
|
+static int tty_ldisc_get(int disc, struct tty_ldisc *ld)
|
|
|
{
|
|
|
- unsigned long flags;
|
|
|
- struct tty_ldisc *ld;
|
|
|
+ int err;
|
|
|
|
|
|
if (disc < N_TTY || disc >= NR_LDISCS)
|
|
|
- return NULL;
|
|
|
-
|
|
|
- spin_lock_irqsave(&tty_ldisc_lock, flags);
|
|
|
-
|
|
|
- ld = &tty_ldiscs[disc];
|
|
|
- /* Check the entry is defined */
|
|
|
- if (ld->flags & LDISC_FLAG_DEFINED) {
|
|
|
- /* If the module is being unloaded we can't use it */
|
|
|
- if (!try_module_get(ld->owner))
|
|
|
- ld = NULL;
|
|
|
- else /* lock it */
|
|
|
- ld->refcount++;
|
|
|
- } else
|
|
|
- ld = NULL;
|
|
|
- spin_unlock_irqrestore(&tty_ldisc_lock, flags);
|
|
|
- return ld;
|
|
|
+ return -EINVAL;
|
|
|
+ err = tty_ldisc_try_get(disc, ld);
|
|
|
+ if (err == -EAGAIN) {
|
|
|
+ request_module("tty-ldisc-%d", disc);
|
|
|
+ err = tty_ldisc_try_get(disc, ld);
|
|
|
+ }
|
|
|
+ return err;
|
|
|
}
|
|
|
|
|
|
-EXPORT_SYMBOL_GPL(tty_ldisc_get);
|
|
|
-
|
|
|
/**
|
|
|
* tty_ldisc_put - drop ldisc reference
|
|
|
* @disc: ldisc number
|
|
@@ -797,22 +822,67 @@ EXPORT_SYMBOL_GPL(tty_ldisc_get);
|
|
|
* takes tty_ldisc_lock to guard against ldisc races
|
|
|
*/
|
|
|
|
|
|
-void tty_ldisc_put(int disc)
|
|
|
+static void tty_ldisc_put(struct tty_ldisc_ops *ld)
|
|
|
{
|
|
|
- struct tty_ldisc *ld;
|
|
|
unsigned long flags;
|
|
|
+ int disc = ld->num;
|
|
|
|
|
|
BUG_ON(disc < N_TTY || disc >= NR_LDISCS);
|
|
|
|
|
|
spin_lock_irqsave(&tty_ldisc_lock, flags);
|
|
|
- ld = &tty_ldiscs[disc];
|
|
|
+ ld = tty_ldiscs[disc];
|
|
|
BUG_ON(ld->refcount == 0);
|
|
|
ld->refcount--;
|
|
|
module_put(ld->owner);
|
|
|
spin_unlock_irqrestore(&tty_ldisc_lock, flags);
|
|
|
}
|
|
|
|
|
|
-EXPORT_SYMBOL_GPL(tty_ldisc_put);
|
|
|
+static void * tty_ldiscs_seq_start(struct seq_file *m, loff_t *pos)
|
|
|
+{
|
|
|
+ return (*pos < NR_LDISCS) ? pos : NULL;
|
|
|
+}
|
|
|
+
|
|
|
+static void * tty_ldiscs_seq_next(struct seq_file *m, void *v, loff_t *pos)
|
|
|
+{
|
|
|
+ (*pos)++;
|
|
|
+ return (*pos < NR_LDISCS) ? pos : NULL;
|
|
|
+}
|
|
|
+
|
|
|
+static void tty_ldiscs_seq_stop(struct seq_file *m, void *v)
|
|
|
+{
|
|
|
+}
|
|
|
+
|
|
|
+static int tty_ldiscs_seq_show(struct seq_file *m, void *v)
|
|
|
+{
|
|
|
+ int i = *(loff_t *)v;
|
|
|
+ struct tty_ldisc ld;
|
|
|
+
|
|
|
+ if (tty_ldisc_get(i, &ld) < 0)
|
|
|
+ return 0;
|
|
|
+ seq_printf(m, "%-10s %2d\n", ld.ops->name ? ld.ops->name : "???", i);
|
|
|
+ tty_ldisc_put(ld.ops);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static const struct seq_operations tty_ldiscs_seq_ops = {
|
|
|
+ .start = tty_ldiscs_seq_start,
|
|
|
+ .next = tty_ldiscs_seq_next,
|
|
|
+ .stop = tty_ldiscs_seq_stop,
|
|
|
+ .show = tty_ldiscs_seq_show,
|
|
|
+};
|
|
|
+
|
|
|
+static int proc_tty_ldiscs_open(struct inode *inode, struct file *file)
|
|
|
+{
|
|
|
+ return seq_open(file, &tty_ldiscs_seq_ops);
|
|
|
+}
|
|
|
+
|
|
|
+const struct file_operations tty_ldiscs_proc_fops = {
|
|
|
+ .owner = THIS_MODULE,
|
|
|
+ .open = proc_tty_ldiscs_open,
|
|
|
+ .read = seq_read,
|
|
|
+ .llseek = seq_lseek,
|
|
|
+ .release = seq_release,
|
|
|
+};
|
|
|
|
|
|
/**
|
|
|
* tty_ldisc_assign - set ldisc on a tty
|
|
@@ -829,8 +899,8 @@ EXPORT_SYMBOL_GPL(tty_ldisc_put);
|
|
|
|
|
|
static void tty_ldisc_assign(struct tty_struct *tty, struct tty_ldisc *ld)
|
|
|
{
|
|
|
+ ld->refcount = 0;
|
|
|
tty->ldisc = *ld;
|
|
|
- tty->ldisc.refcount = 0;
|
|
|
}
|
|
|
|
|
|
/**
|
|
@@ -953,6 +1023,41 @@ static void tty_ldisc_enable(struct tty_struct *tty)
|
|
|
wake_up(&tty_ldisc_wait);
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * tty_ldisc_restore - helper for tty ldisc change
|
|
|
+ * @tty: tty to recover
|
|
|
+ * @old: previous ldisc
|
|
|
+ *
|
|
|
+ * Restore the previous line discipline or N_TTY when a line discipline
|
|
|
+ * change fails due to an open error
|
|
|
+ */
|
|
|
+
|
|
|
+static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old)
|
|
|
+{
|
|
|
+ char buf[64];
|
|
|
+ struct tty_ldisc new_ldisc;
|
|
|
+
|
|
|
+ /* There is an outstanding reference here so this is safe */
|
|
|
+ tty_ldisc_get(old->ops->num, old);
|
|
|
+ tty_ldisc_assign(tty, old);
|
|
|
+ tty_set_termios_ldisc(tty, old->ops->num);
|
|
|
+ if (old->ops->open && (old->ops->open(tty) < 0)) {
|
|
|
+ tty_ldisc_put(old->ops);
|
|
|
+ /* This driver is always present */
|
|
|
+ if (tty_ldisc_get(N_TTY, &new_ldisc) < 0)
|
|
|
+ panic("n_tty: get");
|
|
|
+ tty_ldisc_assign(tty, &new_ldisc);
|
|
|
+ tty_set_termios_ldisc(tty, N_TTY);
|
|
|
+ if (new_ldisc.ops->open) {
|
|
|
+ int r = new_ldisc.ops->open(tty);
|
|
|
+ if (r < 0)
|
|
|
+ panic("Couldn't open N_TTY ldisc for "
|
|
|
+ "%s --- error %d.",
|
|
|
+ tty_name(tty, buf), r);
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
/**
|
|
|
* tty_set_ldisc - set line discipline
|
|
|
* @tty: the terminal to set
|
|
@@ -967,28 +1072,18 @@ static void tty_ldisc_enable(struct tty_struct *tty)
|
|
|
|
|
|
static int tty_set_ldisc(struct tty_struct *tty, int ldisc)
|
|
|
{
|
|
|
- int retval = 0;
|
|
|
- struct tty_ldisc o_ldisc;
|
|
|
- char buf[64];
|
|
|
+ int retval;
|
|
|
+ struct tty_ldisc o_ldisc, new_ldisc;
|
|
|
int work;
|
|
|
unsigned long flags;
|
|
|
- struct tty_ldisc *ld;
|
|
|
struct tty_struct *o_tty;
|
|
|
|
|
|
- if ((ldisc < N_TTY) || (ldisc >= NR_LDISCS))
|
|
|
- return -EINVAL;
|
|
|
-
|
|
|
restart:
|
|
|
-
|
|
|
- ld = tty_ldisc_get(ldisc);
|
|
|
- /* Eduardo Blanco <ejbs@cs.cs.com.uy> */
|
|
|
- /* Cyrus Durgin <cider@speakeasy.org> */
|
|
|
- if (ld == NULL) {
|
|
|
- request_module("tty-ldisc-%d", ldisc);
|
|
|
- ld = tty_ldisc_get(ldisc);
|
|
|
- }
|
|
|
- if (ld == NULL)
|
|
|
- return -EINVAL;
|
|
|
+ /* This is a bit ugly for now but means we can break the 'ldisc
|
|
|
+ is part of the tty struct' assumption later */
|
|
|
+ retval = tty_ldisc_get(ldisc, &new_ldisc);
|
|
|
+ if (retval)
|
|
|
+ return retval;
|
|
|
|
|
|
/*
|
|
|
* Problem: What do we do if this blocks ?
|
|
@@ -996,8 +1091,8 @@ restart:
|
|
|
|
|
|
tty_wait_until_sent(tty, 0);
|
|
|
|
|
|
- if (tty->ldisc.num == ldisc) {
|
|
|
- tty_ldisc_put(ldisc);
|
|
|
+ if (tty->ldisc.ops->num == ldisc) {
|
|
|
+ tty_ldisc_put(new_ldisc.ops);
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -1024,7 +1119,7 @@ restart:
|
|
|
/* Free the new ldisc we grabbed. Must drop the lock
|
|
|
first. */
|
|
|
spin_unlock_irqrestore(&tty_ldisc_lock, flags);
|
|
|
- tty_ldisc_put(ldisc);
|
|
|
+ tty_ldisc_put(o_ldisc.ops);
|
|
|
/*
|
|
|
* There are several reasons we may be busy, including
|
|
|
* random momentary I/O traffic. We must therefore
|
|
@@ -1038,7 +1133,7 @@ restart:
|
|
|
}
|
|
|
if (o_tty && o_tty->ldisc.refcount) {
|
|
|
spin_unlock_irqrestore(&tty_ldisc_lock, flags);
|
|
|
- tty_ldisc_put(ldisc);
|
|
|
+ tty_ldisc_put(o_tty->ldisc.ops);
|
|
|
if (wait_event_interruptible(tty_ldisc_wait, o_tty->ldisc.refcount == 0) < 0)
|
|
|
return -ERESTARTSYS;
|
|
|
goto restart;
|
|
@@ -1049,8 +1144,9 @@ restart:
|
|
|
* another ldisc change
|
|
|
*/
|
|
|
if (!test_bit(TTY_LDISC, &tty->flags)) {
|
|
|
+ struct tty_ldisc *ld;
|
|
|
spin_unlock_irqrestore(&tty_ldisc_lock, flags);
|
|
|
- tty_ldisc_put(ldisc);
|
|
|
+ tty_ldisc_put(new_ldisc.ops);
|
|
|
ld = tty_ldisc_ref_wait(tty);
|
|
|
tty_ldisc_deref(ld);
|
|
|
goto restart;
|
|
@@ -1060,7 +1156,7 @@ restart:
|
|
|
if (o_tty)
|
|
|
clear_bit(TTY_LDISC, &o_tty->flags);
|
|
|
spin_unlock_irqrestore(&tty_ldisc_lock, flags);
|
|
|
-
|
|
|
+
|
|
|
/*
|
|
|
* From this point on we know nobody has an ldisc
|
|
|
* usage reference, nor can they obtain one until
|
|
@@ -1070,45 +1166,30 @@ restart:
|
|
|
work = cancel_delayed_work(&tty->buf.work);
|
|
|
/*
|
|
|
* Wait for ->hangup_work and ->buf.work handlers to terminate
|
|
|
+ * MUST NOT hold locks here.
|
|
|
*/
|
|
|
flush_scheduled_work();
|
|
|
/* Shutdown the current discipline. */
|
|
|
- if (tty->ldisc.close)
|
|
|
- (tty->ldisc.close)(tty);
|
|
|
+ if (o_ldisc.ops->close)
|
|
|
+ (o_ldisc.ops->close)(tty);
|
|
|
|
|
|
/* Now set up the new line discipline. */
|
|
|
- tty_ldisc_assign(tty, ld);
|
|
|
+ tty_ldisc_assign(tty, &new_ldisc);
|
|
|
tty_set_termios_ldisc(tty, ldisc);
|
|
|
- if (tty->ldisc.open)
|
|
|
- retval = (tty->ldisc.open)(tty);
|
|
|
+ if (new_ldisc.ops->open)
|
|
|
+ retval = (new_ldisc.ops->open)(tty);
|
|
|
if (retval < 0) {
|
|
|
- tty_ldisc_put(ldisc);
|
|
|
- /* There is an outstanding reference here so this is safe */
|
|
|
- tty_ldisc_assign(tty, tty_ldisc_get(o_ldisc.num));
|
|
|
- tty_set_termios_ldisc(tty, tty->ldisc.num);
|
|
|
- if (tty->ldisc.open && (tty->ldisc.open(tty) < 0)) {
|
|
|
- tty_ldisc_put(o_ldisc.num);
|
|
|
- /* This driver is always present */
|
|
|
- tty_ldisc_assign(tty, tty_ldisc_get(N_TTY));
|
|
|
- tty_set_termios_ldisc(tty, N_TTY);
|
|
|
- if (tty->ldisc.open) {
|
|
|
- int r = tty->ldisc.open(tty);
|
|
|
-
|
|
|
- if (r < 0)
|
|
|
- panic("Couldn't open N_TTY ldisc for "
|
|
|
- "%s --- error %d.",
|
|
|
- tty_name(tty, buf), r);
|
|
|
- }
|
|
|
- }
|
|
|
+ tty_ldisc_put(new_ldisc.ops);
|
|
|
+ tty_ldisc_restore(tty, &o_ldisc);
|
|
|
}
|
|
|
/* At this point we hold a reference to the new ldisc and a
|
|
|
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->ops->set_ldisc)
|
|
|
+ if (tty->ldisc.ops->num != o_ldisc.ops->num && tty->ops->set_ldisc)
|
|
|
tty->ops->set_ldisc(tty);
|
|
|
|
|
|
- tty_ldisc_put(o_ldisc.num);
|
|
|
+ tty_ldisc_put(o_ldisc.ops);
|
|
|
|
|
|
/*
|
|
|
* Allow ldisc referencing to occur as soon as the driver
|
|
@@ -1335,8 +1416,8 @@ void tty_wakeup(struct tty_struct *tty)
|
|
|
if (test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) {
|
|
|
ld = tty_ldisc_ref(tty);
|
|
|
if (ld) {
|
|
|
- if (ld->write_wakeup)
|
|
|
- ld->write_wakeup(tty);
|
|
|
+ if (ld->ops->write_wakeup)
|
|
|
+ ld->ops->write_wakeup(tty);
|
|
|
tty_ldisc_deref(ld);
|
|
|
}
|
|
|
}
|
|
@@ -1357,8 +1438,8 @@ void tty_ldisc_flush(struct tty_struct *tty)
|
|
|
{
|
|
|
struct tty_ldisc *ld = tty_ldisc_ref(tty);
|
|
|
if (ld) {
|
|
|
- if (ld->flush_buffer)
|
|
|
- ld->flush_buffer(tty);
|
|
|
+ if (ld->ops->flush_buffer)
|
|
|
+ ld->ops->flush_buffer(tty);
|
|
|
tty_ldisc_deref(ld);
|
|
|
}
|
|
|
tty_buffer_flush(tty);
|
|
@@ -1386,7 +1467,7 @@ static void tty_reset_termios(struct tty_struct *tty)
|
|
|
* do_tty_hangup - actual handler for hangup events
|
|
|
* @work: tty device
|
|
|
*
|
|
|
- * This can be called by the "eventd" kernel thread. That is process
|
|
|
+k * This can be called by the "eventd" kernel thread. That is process
|
|
|
* synchronous but doesn't hold any locks, so we need to make sure we
|
|
|
* have the appropriate locks for what we're doing.
|
|
|
*
|
|
@@ -1449,14 +1530,14 @@ static void do_tty_hangup(struct work_struct *work)
|
|
|
ld = tty_ldisc_ref(tty);
|
|
|
if (ld != NULL) {
|
|
|
/* We may have no line discipline at this point */
|
|
|
- if (ld->flush_buffer)
|
|
|
- ld->flush_buffer(tty);
|
|
|
+ if (ld->ops->flush_buffer)
|
|
|
+ ld->ops->flush_buffer(tty);
|
|
|
tty_driver_flush_buffer(tty);
|
|
|
if ((test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) &&
|
|
|
- ld->write_wakeup)
|
|
|
- ld->write_wakeup(tty);
|
|
|
- if (ld->hangup)
|
|
|
- ld->hangup(tty);
|
|
|
+ ld->ops->write_wakeup)
|
|
|
+ ld->ops->write_wakeup(tty);
|
|
|
+ if (ld->ops->hangup)
|
|
|
+ ld->ops->hangup(tty);
|
|
|
}
|
|
|
/*
|
|
|
* FIXME: Once we trust the LDISC code better we can wait here for
|
|
@@ -1825,8 +1906,8 @@ static ssize_t tty_read(struct file *file, char __user *buf, size_t count,
|
|
|
/* We want to wait for the line discipline to sort out in this
|
|
|
situation */
|
|
|
ld = tty_ldisc_ref_wait(tty);
|
|
|
- if (ld->read)
|
|
|
- i = (ld->read)(tty, file, buf, count);
|
|
|
+ if (ld->ops->read)
|
|
|
+ i = (ld->ops->read)(tty, file, buf, count);
|
|
|
else
|
|
|
i = -EIO;
|
|
|
tty_ldisc_deref(ld);
|
|
@@ -1978,10 +2059,10 @@ static ssize_t tty_write(struct file *file, const char __user *buf,
|
|
|
printk(KERN_ERR "tty driver %s lacks a write_room method.\n",
|
|
|
tty->driver->name);
|
|
|
ld = tty_ldisc_ref_wait(tty);
|
|
|
- if (!ld->write)
|
|
|
+ if (!ld->ops->write)
|
|
|
ret = -EIO;
|
|
|
else
|
|
|
- ret = do_tty_write(ld->write, tty, file, buf, count);
|
|
|
+ ret = do_tty_write(ld->ops->write, tty, file, buf, count);
|
|
|
tty_ldisc_deref(ld);
|
|
|
return ret;
|
|
|
}
|
|
@@ -2076,6 +2157,7 @@ static int init_dev(struct tty_driver *driver, int idx,
|
|
|
struct ktermios *tp, **tp_loc, *o_tp, **o_tp_loc;
|
|
|
struct ktermios *ltp, **ltp_loc, *o_ltp, **o_ltp_loc;
|
|
|
int retval = 0;
|
|
|
+ struct tty_ldisc *ld;
|
|
|
|
|
|
/* check whether we're reopening an existing tty */
|
|
|
if (driver->flags & TTY_DRIVER_DEVPTS_MEM) {
|
|
@@ -2224,17 +2306,19 @@ static int init_dev(struct tty_driver *driver, int idx,
|
|
|
* If we fail here just call release_tty to clean up. No need
|
|
|
* to decrement the use counts, as release_tty doesn't care.
|
|
|
*/
|
|
|
+
|
|
|
+ ld = &tty->ldisc;
|
|
|
|
|
|
- if (tty->ldisc.open) {
|
|
|
- retval = (tty->ldisc.open)(tty);
|
|
|
+ if (ld->ops->open) {
|
|
|
+ retval = (ld->ops->open)(tty);
|
|
|
if (retval)
|
|
|
goto release_mem_out;
|
|
|
}
|
|
|
- if (o_tty && o_tty->ldisc.open) {
|
|
|
- retval = (o_tty->ldisc.open)(o_tty);
|
|
|
+ if (o_tty && o_tty->ldisc.ops->open) {
|
|
|
+ retval = (o_tty->ldisc.ops->open)(o_tty);
|
|
|
if (retval) {
|
|
|
- if (tty->ldisc.close)
|
|
|
- (tty->ldisc.close)(tty);
|
|
|
+ if (ld->ops->close)
|
|
|
+ (ld->ops->close)(tty);
|
|
|
goto release_mem_out;
|
|
|
}
|
|
|
tty_ldisc_enable(o_tty);
|
|
@@ -2378,6 +2462,7 @@ static void release_tty(struct tty_struct *tty, int idx)
|
|
|
static void release_dev(struct file *filp)
|
|
|
{
|
|
|
struct tty_struct *tty, *o_tty;
|
|
|
+ struct tty_ldisc ld;
|
|
|
int pty_master, tty_closing, o_tty_closing, do_sleep;
|
|
|
int devpts;
|
|
|
int idx;
|
|
@@ -2611,26 +2696,27 @@ static void release_dev(struct file *filp)
|
|
|
spin_unlock_irqrestore(&tty_ldisc_lock, flags);
|
|
|
/*
|
|
|
* Shutdown the current line discipline, and reset it to N_TTY.
|
|
|
- * N.B. why reset ldisc when we're releasing the memory??
|
|
|
*
|
|
|
* FIXME: this MUST get fixed for the new reflocking
|
|
|
*/
|
|
|
- if (tty->ldisc.close)
|
|
|
- (tty->ldisc.close)(tty);
|
|
|
- tty_ldisc_put(tty->ldisc.num);
|
|
|
+ if (tty->ldisc.ops->close)
|
|
|
+ (tty->ldisc.ops->close)(tty);
|
|
|
+ tty_ldisc_put(tty->ldisc.ops);
|
|
|
|
|
|
/*
|
|
|
* Switch the line discipline back
|
|
|
*/
|
|
|
- tty_ldisc_assign(tty, tty_ldisc_get(N_TTY));
|
|
|
+ WARN_ON(tty_ldisc_get(N_TTY, &ld));
|
|
|
+ tty_ldisc_assign(tty, &ld);
|
|
|
tty_set_termios_ldisc(tty, N_TTY);
|
|
|
if (o_tty) {
|
|
|
/* FIXME: could o_tty be in setldisc here ? */
|
|
|
clear_bit(TTY_LDISC, &o_tty->flags);
|
|
|
- if (o_tty->ldisc.close)
|
|
|
- (o_tty->ldisc.close)(o_tty);
|
|
|
- tty_ldisc_put(o_tty->ldisc.num);
|
|
|
- tty_ldisc_assign(o_tty, tty_ldisc_get(N_TTY));
|
|
|
+ if (o_tty->ldisc.ops->close)
|
|
|
+ (o_tty->ldisc.ops->close)(o_tty);
|
|
|
+ tty_ldisc_put(o_tty->ldisc.ops);
|
|
|
+ WARN_ON(tty_ldisc_get(N_TTY, &ld));
|
|
|
+ tty_ldisc_assign(o_tty, &ld);
|
|
|
tty_set_termios_ldisc(o_tty, N_TTY);
|
|
|
}
|
|
|
/*
|
|
@@ -2899,8 +2985,8 @@ static unsigned int tty_poll(struct file *filp, poll_table *wait)
|
|
|
return 0;
|
|
|
|
|
|
ld = tty_ldisc_ref_wait(tty);
|
|
|
- if (ld->poll)
|
|
|
- ret = (ld->poll)(tty, filp, wait);
|
|
|
+ if (ld->ops->poll)
|
|
|
+ ret = (ld->ops->poll)(tty, filp, wait);
|
|
|
tty_ldisc_deref(ld);
|
|
|
return ret;
|
|
|
}
|
|
@@ -2974,7 +3060,7 @@ static int tiocsti(struct tty_struct *tty, char __user *p)
|
|
|
if (get_user(ch, p))
|
|
|
return -EFAULT;
|
|
|
ld = tty_ldisc_ref_wait(tty);
|
|
|
- ld->receive_buf(tty, &ch, &mbz, 1);
|
|
|
+ ld->ops->receive_buf(tty, &ch, &mbz, 1);
|
|
|
tty_ldisc_deref(ld);
|
|
|
return 0;
|
|
|
}
|
|
@@ -3528,7 +3614,7 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|
|
case TIOCGSID:
|
|
|
return tiocgsid(tty, real_tty, p);
|
|
|
case TIOCGETD:
|
|
|
- return put_user(tty->ldisc.num, (int __user *)p);
|
|
|
+ return put_user(tty->ldisc.ops->num, (int __user *)p);
|
|
|
case TIOCSETD:
|
|
|
return tiocsetd(tty, p);
|
|
|
#ifdef CONFIG_VT
|
|
@@ -3581,8 +3667,8 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
|
|
|
}
|
|
|
ld = tty_ldisc_ref_wait(tty);
|
|
|
retval = -EINVAL;
|
|
|
- if (ld->ioctl) {
|
|
|
- retval = ld->ioctl(tty, file, cmd, arg);
|
|
|
+ if (ld->ops->ioctl) {
|
|
|
+ retval = ld->ops->ioctl(tty, file, cmd, arg);
|
|
|
if (retval == -ENOIOCTLCMD)
|
|
|
retval = -EINVAL;
|
|
|
}
|
|
@@ -3609,8 +3695,8 @@ static long tty_compat_ioctl(struct file *file, unsigned int cmd,
|
|
|
}
|
|
|
|
|
|
ld = tty_ldisc_ref_wait(tty);
|
|
|
- if (ld->compat_ioctl)
|
|
|
- retval = ld->compat_ioctl(tty, file, cmd, arg);
|
|
|
+ if (ld->ops->compat_ioctl)
|
|
|
+ retval = ld->ops->compat_ioctl(tty, file, cmd, arg);
|
|
|
tty_ldisc_deref(ld);
|
|
|
|
|
|
return retval;
|
|
@@ -3782,7 +3868,8 @@ static void flush_to_ldisc(struct work_struct *work)
|
|
|
flag_buf = head->flag_buf_ptr + head->read;
|
|
|
head->read += count;
|
|
|
spin_unlock_irqrestore(&tty->buf.lock, flags);
|
|
|
- disc->receive_buf(tty, char_buf, flag_buf, count);
|
|
|
+ disc->ops->receive_buf(tty, char_buf,
|
|
|
+ flag_buf, count);
|
|
|
spin_lock_irqsave(&tty->buf.lock, flags);
|
|
|
}
|
|
|
/* Restore the queue head */
|
|
@@ -3843,9 +3930,12 @@ EXPORT_SYMBOL(tty_flip_buffer_push);
|
|
|
|
|
|
static void initialize_tty_struct(struct tty_struct *tty)
|
|
|
{
|
|
|
+ struct tty_ldisc ld;
|
|
|
memset(tty, 0, sizeof(struct tty_struct));
|
|
|
tty->magic = TTY_MAGIC;
|
|
|
- tty_ldisc_assign(tty, tty_ldisc_get(N_TTY));
|
|
|
+ if (tty_ldisc_get(N_TTY, &ld) < 0)
|
|
|
+ panic("n_tty: init_tty");
|
|
|
+ tty_ldisc_assign(tty, &ld);
|
|
|
tty->session = NULL;
|
|
|
tty->pgrp = NULL;
|
|
|
tty->overrun_time = jiffies;
|