+struct tty_ldisc *tty_ldisc_get(int disc)
+{
+ unsigned long flags;
+ struct tty_ldisc *ld;
+
+ 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;
+}
+
+EXPORT_SYMBOL_GPL(tty_ldisc_get);
+
+void tty_ldisc_put(int disc)
+{
+ struct tty_ldisc *ld;
+ unsigned long flags;
+
+ if (disc < N_TTY || disc >= NR_LDISCS)
+ BUG();
+
+ spin_lock_irqsave(&tty_ldisc_lock, flags);
+ ld = &tty_ldiscs[disc];
+ if(ld->refcount == 0)
+ BUG();
+ ld->refcount --;
+ module_put(ld->owner);
+ spin_unlock_irqrestore(&tty_ldisc_lock, flags);
+}
+
+EXPORT_SYMBOL_GPL(tty_ldisc_put);
+
+static void tty_ldisc_assign(struct tty_struct *tty, struct tty_ldisc *ld)
+{
+ tty->ldisc = *ld;
+ tty->ldisc.refcount = 0;
+}
+
+/**
+ * tty_ldisc_try - internal helper
+ * @tty: the tty
+ *
+ * Make a single attempt to grab and bump the refcount on
+ * the tty ldisc. Return 0 on failure or 1 on success. This is
+ * used to implement both the waiting and non waiting versions
+ * of tty_ldisc_ref
+ */
+
+static int tty_ldisc_try(struct tty_struct *tty)
+{
+ unsigned long flags;
+ struct tty_ldisc *ld;
+ int ret = 0;
+
+ spin_lock_irqsave(&tty_ldisc_lock, flags);
+ ld = &tty->ldisc;
+ if(test_bit(TTY_LDISC, &tty->flags))
+ {
+ ld->refcount++;
+ ret = 1;
+ }
+ spin_unlock_irqrestore(&tty_ldisc_lock, flags);
+ return ret;
+}
+
+/**
+ * tty_ldisc_ref_wait - wait for the tty ldisc
+ * @tty: tty device
+ *
+ * Dereference the line discipline for the terminal and take a
+ * reference to it. If the line discipline is in flux then
+ * wait patiently until it changes.
+ *
+ * Note: Must not be called from an IRQ/timer context. The caller
+ * must also be careful not to hold other locks that will deadlock
+ * against a discipline change, such as an existing ldisc reference
+ * (which we check for)
+ */
+
+struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *tty)
+{
+ /* wait_event is a macro */
+ wait_event(tty_ldisc_wait, tty_ldisc_try(tty));
+ if(tty->ldisc.refcount == 0)
+ printk(KERN_ERR "tty_ldisc_ref_wait\n");
+ return &tty->ldisc;
+}
+
+EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait);
+
+/**
+ * tty_ldisc_ref - get the tty ldisc
+ * @tty: tty device
+ *
+ * Dereference the line discipline for the terminal and take a
+ * reference to it. If the line discipline is in flux then
+ * return NULL. Can be called from IRQ and timer functions.
+ */
+
+struct tty_ldisc *tty_ldisc_ref(struct tty_struct *tty)
+{
+ if(tty_ldisc_try(tty))
+ return &tty->ldisc;
+ return NULL;
+}
+
+EXPORT_SYMBOL_GPL(tty_ldisc_ref);
+
+/**
+ * tty_ldisc_deref - free a tty ldisc reference
+ * @ld: reference to free up
+ *
+ * Undoes the effect of tty_ldisc_ref or tty_ldisc_ref_wait. May
+ * be called in IRQ context.
+ */
+
+void tty_ldisc_deref(struct tty_ldisc *ld)
+{
+ unsigned long flags;
+
+ if(ld == NULL)
+ BUG();
+
+ spin_lock_irqsave(&tty_ldisc_lock, flags);
+ if(ld->refcount == 0)
+ printk(KERN_ERR "tty_ldisc_deref: no references.\n");
+ else
+ ld->refcount--;
+ if(ld->refcount == 0)
+ wake_up(&tty_ldisc_wait);
+ spin_unlock_irqrestore(&tty_ldisc_lock, flags);
+}
+
+EXPORT_SYMBOL_GPL(tty_ldisc_deref);
+
+/**
+ * tty_ldisc_enable - allow ldisc use
+ * @tty: terminal to activate ldisc on
+ *
+ * Set the TTY_LDISC flag when the line discipline can be called
+ * again. Do neccessary wakeups for existing sleepers.
+ *
+ * Note: nobody should set this bit except via this function. Clearing
+ * directly is allowed.
+ */
+
+static void tty_ldisc_enable(struct tty_struct *tty)
+{
+ set_bit(TTY_LDISC, &tty->flags);
+ wake_up(&tty_ldisc_wait);
+}
+
+/**
+ * tty_set_ldisc - set line discipline
+ * @tty: the terminal to set
+ * @ldisc: the line discipline
+ *
+ * Set the discipline of a tty line. Must be called from a process
+ * context.
+ */
+