#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>
return kzalloc(sizeof(struct tty_struct), GFP_KERNEL);
}
-static void tty_buffer_free_all(struct tty_struct *);
-
/**
* free_tty_struct - free a disused tty
* @tty: tty struct to free
imajor(inode), iminor(inode), routine);
return 1;
}
-#endif
- return 0;
-}
-
-static int check_tty_count(struct tty_struct *tty, const char *routine)
-{
-#ifdef CHECK_TTY_COUNT
- struct list_head *p;
- int count = 0;
-
- file_list_lock();
- list_for_each(p, &tty->tty_files) {
- count++;
- }
- file_list_unlock();
- if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
- tty->driver->subtype == PTY_TYPE_SLAVE &&
- tty->link && tty->link->count)
- count++;
- if (tty->count != count) {
- printk(KERN_WARNING "Warning: dev (%s) tty->count(%d) "
- "!= #fd's(%d) in %s\n",
- tty->name, tty->count, count, routine);
- return count;
- }
-#endif
- return 0;
-}
-
-/*
- * Tty buffer allocation management
- */
-
-/**
- * tty_buffer_free_all - free buffers used by a tty
- * @tty: tty to free from
- *
- * Remove all the buffers pending on a tty whether queued with data
- * or in the free ring. Must be called when the tty is no longer in use
- *
- * Locking: none
- */
-
-static void tty_buffer_free_all(struct tty_struct *tty)
-{
- struct tty_buffer *thead;
- while ((thead = tty->buf.head) != NULL) {
- tty->buf.head = thead->next;
- kfree(thead);
- }
- while ((thead = tty->buf.free) != NULL) {
- tty->buf.free = thead->next;
- kfree(thead);
- }
- tty->buf.tail = NULL;
- tty->buf.memory_used = 0;
-}
-
-/**
- * tty_buffer_init - prepare a tty buffer structure
- * @tty: tty to initialise
- *
- * Set up the initial state of the buffer management for a tty device.
- * Must be called before the other tty buffer functions are used.
- *
- * Locking: none
- */
-
-static void tty_buffer_init(struct tty_struct *tty)
-{
- spin_lock_init(&tty->buf.lock);
- tty->buf.head = NULL;
- tty->buf.tail = NULL;
- tty->buf.free = NULL;
- tty->buf.memory_used = 0;
-}
-
-/**
- * tty_buffer_alloc - allocate a tty buffer
- * @tty: tty device
- * @size: desired size (characters)
- *
- * Allocate a new tty buffer to hold the desired number of characters.
- * Return NULL if out of memory or the allocation would exceed the
- * per device queue
- *
- * Locking: Caller must hold tty->buf.lock
- */
-
-static struct tty_buffer *tty_buffer_alloc(struct tty_struct *tty, size_t size)
-{
- struct tty_buffer *p;
-
- if (tty->buf.memory_used + size > 65536)
- return NULL;
- p = kmalloc(sizeof(struct tty_buffer) + 2 * size, GFP_ATOMIC);
- if (p == NULL)
- return NULL;
- p->used = 0;
- p->size = size;
- p->next = NULL;
- p->commit = 0;
- p->read = 0;
- p->char_buf_ptr = (char *)(p->data);
- p->flag_buf_ptr = (unsigned char *)p->char_buf_ptr + size;
- tty->buf.memory_used += size;
- return p;
-}
-
-/**
- * tty_buffer_free - free a tty buffer
- * @tty: tty owning the buffer
- * @b: the buffer to free
- *
- * Free a tty buffer, or add it to the free list according to our
- * internal strategy
- *
- * Locking: Caller must hold tty->buf.lock
- */
-
-static void tty_buffer_free(struct tty_struct *tty, struct tty_buffer *b)
-{
- /* Dumb strategy for now - should keep some stats */
- tty->buf.memory_used -= b->size;
- WARN_ON(tty->buf.memory_used < 0);
-
- if (b->size >= 512)
- kfree(b);
- else {
- b->next = tty->buf.free;
- tty->buf.free = b;
- }
-}
-
-/**
- * __tty_buffer_flush - flush full tty buffers
- * @tty: tty to flush
- *
- * flush all the buffers containing receive data. Caller must
- * hold the buffer lock and must have ensured no parallel flush to
- * ldisc is running.
- *
- * Locking: Caller must hold tty->buf.lock
- */
-
-static void __tty_buffer_flush(struct tty_struct *tty)
-{
- struct tty_buffer *thead;
-
- while ((thead = tty->buf.head) != NULL) {
- tty->buf.head = thead->next;
- tty_buffer_free(tty, thead);
- }
- tty->buf.tail = NULL;
-}
-
-/**
- * tty_buffer_flush - flush full tty buffers
- * @tty: tty to flush
- *
- * flush all the buffers containing receive data. If the buffer is
- * being processed by flush_to_ldisc then we defer the processing
- * to that function
- *
- * Locking: none
- */
-
-static void tty_buffer_flush(struct tty_struct *tty)
-{
- unsigned long flags;
- spin_lock_irqsave(&tty->buf.lock, flags);
-
- /* If the data is being pushed to the tty layer then we can't
- process it here. Instead set a flag and the flush_to_ldisc
- path will process the flush request before it exits */
- if (test_bit(TTY_FLUSHING, &tty->flags)) {
- set_bit(TTY_FLUSHPENDING, &tty->flags);
- spin_unlock_irqrestore(&tty->buf.lock, flags);
- wait_event(tty->read_wait,
- test_bit(TTY_FLUSHPENDING, &tty->flags) == 0);
- return;
- } else
- __tty_buffer_flush(tty);
- spin_unlock_irqrestore(&tty->buf.lock, flags);
-}
-
-/**
- * tty_buffer_find - find a free tty buffer
- * @tty: tty owning the buffer
- * @size: characters wanted
- *
- * Locate an existing suitable tty buffer or if we are lacking one then
- * allocate a new one. We round our buffers off in 256 character chunks
- * to get better allocation behaviour.
- *
- * Locking: Caller must hold tty->buf.lock
- */
-
-static struct tty_buffer *tty_buffer_find(struct tty_struct *tty, size_t size)
-{
- struct tty_buffer **tbh = &tty->buf.free;
- while ((*tbh) != NULL) {
- struct tty_buffer *t = *tbh;
- if (t->size >= size) {
- *tbh = t->next;
- t->next = NULL;
- t->used = 0;
- t->commit = 0;
- t->read = 0;
- tty->buf.memory_used += t->size;
- return t;
- }
- tbh = &((*tbh)->next);
- }
- /* Round the buffer size out */
- size = (size + 0xFF) & ~0xFF;
- return tty_buffer_alloc(tty, size);
- /* Should possibly check if this fails for the largest buffer we
- have queued and recycle that ? */
-}
-
-/**
- * tty_buffer_request_room - grow tty buffer if needed
- * @tty: tty structure
- * @size: size desired
- *
- * Make at least size bytes of linear space available for the tty
- * buffer. If we fail return the size we managed to find.
- *
- * Locking: Takes tty->buf.lock
- */
-int tty_buffer_request_room(struct tty_struct *tty, size_t size)
-{
- struct tty_buffer *b, *n;
- int left;
- unsigned long flags;
-
- spin_lock_irqsave(&tty->buf.lock, flags);
-
- /* OPTIMISATION: We could keep a per tty "zero" sized buffer to
- remove this conditional if its worth it. This would be invisible
- to the callers */
- if ((b = tty->buf.tail) != NULL)
- left = b->size - b->used;
- else
- left = 0;
-
- if (left < size) {
- /* This is the slow path - looking for new buffers to use */
- if ((n = tty_buffer_find(tty, size)) != NULL) {
- if (b != NULL) {
- b->next = n;
- b->commit = b->used;
- } else
- tty->buf.head = n;
- tty->buf.tail = n;
- } else
- size = left;
- }
-
- spin_unlock_irqrestore(&tty->buf.lock, flags);
- return size;
-}
-EXPORT_SYMBOL_GPL(tty_buffer_request_room);
-
-/**
- * tty_insert_flip_string - Add characters to the tty buffer
- * @tty: tty structure
- * @chars: characters
- * @size: size
- *
- * Queue a series of bytes to the tty buffering. All the characters
- * passed are marked as without error. Returns the number added.
- *
- * Locking: Called functions may take tty->buf.lock
- */
-
-int tty_insert_flip_string(struct tty_struct *tty, const unsigned char *chars,
- size_t size)
-{
- int copied = 0;
- do {
- int space = tty_buffer_request_room(tty, size - copied);
- struct tty_buffer *tb = tty->buf.tail;
- /* If there is no space then tb may be NULL */
- if (unlikely(space == 0))
- break;
- memcpy(tb->char_buf_ptr + tb->used, chars, space);
- memset(tb->flag_buf_ptr + tb->used, TTY_NORMAL, space);
- tb->used += space;
- copied += space;
- chars += space;
- /* There is a small chance that we need to split the data over
- several buffers. If this is the case we must loop */
- } while (unlikely(size > copied));
- return copied;
-}
-EXPORT_SYMBOL(tty_insert_flip_string);
-
-/**
- * tty_insert_flip_string_flags - Add characters to the tty buffer
- * @tty: tty structure
- * @chars: characters
- * @flags: flag bytes
- * @size: size
- *
- * Queue a series of bytes to the tty buffering. For each character
- * the flags array indicates the status of the character. Returns the
- * number added.
- *
- * Locking: Called functions may take tty->buf.lock
- */
-
-int tty_insert_flip_string_flags(struct tty_struct *tty,
- const unsigned char *chars, const char *flags, size_t size)
-{
- int copied = 0;
- do {
- int space = tty_buffer_request_room(tty, size - copied);
- struct tty_buffer *tb = tty->buf.tail;
- /* If there is no space then tb may be NULL */
- if (unlikely(space == 0))
- break;
- memcpy(tb->char_buf_ptr + tb->used, chars, space);
- memcpy(tb->flag_buf_ptr + tb->used, flags, space);
- tb->used += space;
- copied += space;
- chars += space;
- flags += space;
- /* There is a small chance that we need to split the data over
- several buffers. If this is the case we must loop */
- } while (unlikely(size > copied));
- return copied;
-}
-EXPORT_SYMBOL(tty_insert_flip_string_flags);
-
-/**
- * tty_schedule_flip - push characters to ldisc
- * @tty: tty to push from
- *
- * Takes any pending buffers and transfers their ownership to the
- * ldisc side of the queue. It then schedules those characters for
- * processing by the line discipline.
- *
- * Locking: Takes tty->buf.lock
- */
-
-void tty_schedule_flip(struct tty_struct *tty)
-{
- unsigned long flags;
- spin_lock_irqsave(&tty->buf.lock, flags);
- if (tty->buf.tail != NULL)
- tty->buf.tail->commit = tty->buf.tail->used;
- spin_unlock_irqrestore(&tty->buf.lock, flags);
- schedule_delayed_work(&tty->buf.work, 1);
-}
-EXPORT_SYMBOL(tty_schedule_flip);
-
-/**
- * tty_prepare_flip_string - make room for characters
- * @tty: tty
- * @chars: return pointer for character write area
- * @size: desired size
- *
- * Prepare a block of space in the buffer for data. Returns the length
- * available and buffer pointer to the space which is now allocated and
- * accounted for as ready for normal characters. This is used for drivers
- * that need their own block copy routines into the buffer. There is no
- * guarantee the buffer is a DMA target!
- *
- * Locking: May call functions taking tty->buf.lock
- */
-
-int tty_prepare_flip_string(struct tty_struct *tty, unsigned char **chars,
- size_t size)
-{
- int space = tty_buffer_request_room(tty, size);
- if (likely(space)) {
- struct tty_buffer *tb = tty->buf.tail;
- *chars = tb->char_buf_ptr + tb->used;
- memset(tb->flag_buf_ptr + tb->used, TTY_NORMAL, space);
- tb->used += space;
- }
- return space;
-}
-
-EXPORT_SYMBOL_GPL(tty_prepare_flip_string);
-
-/**
- * tty_prepare_flip_string_flags - make room for characters
- * @tty: tty
- * @chars: return pointer for character write area
- * @flags: return pointer for status flag write area
- * @size: desired size
- *
- * Prepare a block of space in the buffer for data. Returns the length
- * available and buffer pointer to the space which is now allocated and
- * accounted for as ready for characters. This is used for drivers
- * that need their own block copy routines into the buffer. There is no
- * guarantee the buffer is a DMA target!
- *
- * Locking: May call functions taking tty->buf.lock
- */
-
-int tty_prepare_flip_string_flags(struct tty_struct *tty,
- unsigned char **chars, char **flags, size_t size)
-{
- int space = tty_buffer_request_room(tty, size);
- if (likely(space)) {
- struct tty_buffer *tb = tty->buf.tail;
- *chars = tb->char_buf_ptr + tb->used;
- *flags = tb->flag_buf_ptr + tb->used;
- tb->used += space;
- }
- return space;
-}
-
-EXPORT_SYMBOL_GPL(tty_prepare_flip_string_flags);
-
-
-
-/**
- * tty_set_termios_ldisc - set ldisc field
- * @tty: tty structure
- * @num: line discipline number
- *
- * This is probably overkill for real world processors but
- * they are not on hot paths so a little discipline won't do
- * any harm.
- *
- * Locking: takes termios_mutex
- */
-
-static void tty_set_termios_ldisc(struct tty_struct *tty, int num)
-{
- mutex_lock(&tty->termios_mutex);
- tty->termios->c_line = num;
- mutex_unlock(&tty->termios_mutex);
-}
-
-/*
- * This guards the refcounted line discipline lists. The lock
- * must be taken with irqs off because there are hangup path
- * callers who will do ldisc lookups and cannot sleep.
- */
-
-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];
-
-/**
- * tty_register_ldisc - install a line discipline
- * @disc: ldisc number
- * @new_ldisc: pointer to the ldisc object
- *
- * Installs a new line discipline into the kernel. The discipline
- * is set up as unreferenced and then made available to the kernel
- * from this point onwards.
- *
- * Locking:
- * takes tty_ldisc_lock to guard against ldisc races
- */
-
-int tty_register_ldisc(int disc, struct tty_ldisc *new_ldisc)
-{
- unsigned long flags;
- int ret = 0;
-
- if (disc < N_TTY || disc >= NR_LDISCS)
- 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;
- spin_unlock_irqrestore(&tty_ldisc_lock, flags);
-
- return ret;
-}
-EXPORT_SYMBOL(tty_register_ldisc);
-
-/**
- * tty_unregister_ldisc - unload a line discipline
- * @disc: ldisc number
- * @new_ldisc: pointer to the ldisc object
- *
- * Remove a line discipline from the kernel providing it is not
- * currently in use.
- *
- * Locking:
- * takes tty_ldisc_lock to guard against ldisc races
- */
-
-int tty_unregister_ldisc(int disc)
-{
- unsigned long flags;
- int ret = 0;
-
- if (disc < N_TTY || disc >= NR_LDISCS)
- return -EINVAL;
-
- spin_lock_irqsave(&tty_ldisc_lock, flags);
- if (tty_ldiscs[disc].refcount)
- ret = -EBUSY;
- else
- tty_ldiscs[disc].flags &= ~LDISC_FLAG_DEFINED;
- spin_unlock_irqrestore(&tty_ldisc_lock, flags);
-
- return ret;
-}
-EXPORT_SYMBOL(tty_unregister_ldisc);
-
-/**
- * tty_ldisc_get - take a reference to an ldisc
- * @disc: ldisc number
- *
- * Takes a reference to a line discipline. Deals with refcounts and
- * module locking counts. Returns NULL if the discipline is not available.
- * Returns a pointer to the discipline and bumps the ref count if it is
- * available
- *
- * Locking:
- * takes tty_ldisc_lock to guard against ldisc races
- */
-
-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);
-
-/**
- * tty_ldisc_put - drop ldisc reference
- * @disc: ldisc number
- *
- * Drop a reference to a line discipline. Manage refcounts and
- * module usage counts
- *
- * Locking:
- * takes tty_ldisc_lock to guard against ldisc races
- */
-
-void tty_ldisc_put(int disc)
-{
- struct tty_ldisc *ld;
- unsigned long flags;
-
- BUG_ON(disc < N_TTY || disc >= NR_LDISCS);
-
- spin_lock_irqsave(&tty_ldisc_lock, flags);
- 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);
-
-/**
- * tty_ldisc_assign - set ldisc on a tty
- * @tty: tty to assign
- * @ld: line discipline
- *
- * Install an instance of a line discipline into a tty structure. The
- * ldisc must have a reference count above zero to ensure it remains/
- * The tty instance refcount starts at zero.
- *
- * Locking:
- * Caller must hold references
- */
-
-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
- *
- * Locking: takes tty_ldisc_lock
- */
-
-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)
- *
- * Locking: call functions take tty_ldisc_lock
- */
-
-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.
- *
- * Locking: called functions take tty_ldisc_lock
- */
-
-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.
- *
- * Locking: takes tty_ldisc_lock
- */
-
-void tty_ldisc_deref(struct tty_ldisc *ld)
-{
- unsigned long flags;
-
- BUG_ON(ld == NULL);
-
- 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 necessary 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.
- *
- * Locking: takes tty_ldisc_lock.
- * called functions take termios_mutex
- */
-
-static int tty_set_ldisc(struct tty_struct *tty, int ldisc)
-{
- int retval = 0;
- struct tty_ldisc o_ldisc;
- char buf[64];
- 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;
-
- /*
- * Problem: What do we do if this blocks ?
- */
-
- tty_wait_until_sent(tty, 0);
-
- if (tty->ldisc.num == ldisc) {
- tty_ldisc_put(ldisc);
- return 0;
- }
-
- /*
- * No more input please, we are switching. The new ldisc
- * will update this value in the ldisc open function
- */
-
- tty->receive_room = 0;
-
- o_ldisc = tty->ldisc;
- o_tty = tty->link;
-
- /*
- * Make sure we don't change while someone holds a
- * reference to the line discipline. The TTY_LDISC bit
- * prevents anyone taking a reference once it is clear.
- * We need the lock to avoid racing reference takers.
- */
-
- spin_lock_irqsave(&tty_ldisc_lock, flags);
- if (tty->ldisc.refcount || (o_tty && o_tty->ldisc.refcount)) {
- if (tty->ldisc.refcount) {
- /* Free the new ldisc we grabbed. Must drop the lock
- first. */
- spin_unlock_irqrestore(&tty_ldisc_lock, flags);
- tty_ldisc_put(ldisc);
- /*
- * There are several reasons we may be busy, including
- * random momentary I/O traffic. We must therefore
- * retry. We could distinguish between blocking ops
- * and retries if we made tty_ldisc_wait() smarter.
- * That is up for discussion.
- */
- if (wait_event_interruptible(tty_ldisc_wait, tty->ldisc.refcount == 0) < 0)
- return -ERESTARTSYS;
- goto restart;
- }
- if (o_tty && o_tty->ldisc.refcount) {
- spin_unlock_irqrestore(&tty_ldisc_lock, flags);
- tty_ldisc_put(ldisc);
- if (wait_event_interruptible(tty_ldisc_wait, o_tty->ldisc.refcount == 0) < 0)
- return -ERESTARTSYS;
- goto restart;
- }
- }
- /*
- * If the TTY_LDISC bit is set, then we are racing against
- * another ldisc change
- */
- if (!test_bit(TTY_LDISC, &tty->flags)) {
- spin_unlock_irqrestore(&tty_ldisc_lock, flags);
- tty_ldisc_put(ldisc);
- ld = tty_ldisc_ref_wait(tty);
- tty_ldisc_deref(ld);
- goto restart;
- }
-
- clear_bit(TTY_LDISC, &tty->flags);
- if (o_tty)
- clear_bit(TTY_LDISC, &o_tty->flags);
- spin_unlock_irqrestore(&tty_ldisc_lock, flags);
+#endif
+ return 0;
+}
- /*
- * From this point on we know nobody has an ldisc
- * usage reference, nor can they obtain one until
- * we say so later on.
- */
+static int check_tty_count(struct tty_struct *tty, const char *routine)
+{
+#ifdef CHECK_TTY_COUNT
+ struct list_head *p;
+ int count = 0;
- work = cancel_delayed_work(&tty->buf.work);
- /*
- * Wait for ->hangup_work and ->buf.work handlers to terminate
- */
- flush_scheduled_work();
- /* Shutdown the current discipline. */
- if (tty->ldisc.close)
- (tty->ldisc.close)(tty);
-
- /* Now set up the new line discipline. */
- tty_ldisc_assign(tty, ld);
- tty_set_termios_ldisc(tty, ldisc);
- if (tty->ldisc.open)
- retval = (tty->ldisc.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);
- }
- }
+ file_list_lock();
+ list_for_each(p, &tty->tty_files) {
+ count++;
}
- /* 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)
- tty->ops->set_ldisc(tty);
-
- tty_ldisc_put(o_ldisc.num);
-
- /*
- * Allow ldisc referencing to occur as soon as the driver
- * ldisc callback completes.
- */
-
- tty_ldisc_enable(tty);
- if (o_tty)
- tty_ldisc_enable(o_tty);
-
- /* Restart it in case no characters kick it off. Safe if
- already running */
- if (work)
- schedule_delayed_work(&tty->buf.work, 1);
- return retval;
+ file_list_unlock();
+ if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
+ tty->driver->subtype == PTY_TYPE_SLAVE &&
+ tty->link && tty->link->count)
+ count++;
+ if (tty->count != count) {
+ printk(KERN_WARNING "Warning: dev (%s) tty->count(%d) "
+ "!= #fd's(%d) in %s\n",
+ tty->name, tty->count, count, routine);
+ return count;
+ }
+#endif
+ return 0;
}
/**
{
struct tty_driver *p, *res = NULL;
int tty_line = 0;
+ int len;
char *str;
+ for (str = name; *str; str++)
+ if ((*str >= '0' && *str <= '9') || *str == ',')
+ break;
+ if (!*str)
+ return NULL;
+
+ len = str - name;
+ tty_line = simple_strtoul(str, &str, 10);
+
mutex_lock(&tty_mutex);
/* Search through the tty devices to look for a match */
list_for_each_entry(p, &tty_drivers, tty_drivers) {
- str = name + strlen(p->name);
- tty_line = simple_strtoul(str, &str, 10);
+ if (strncmp(name, p->name, len) != 0)
+ continue;
if (*str == ',')
str++;
if (*str == '\0')
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);
}
}
{
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);
struct tty_ldisc *ld;
int closecount = 0, n;
unsigned long flags;
+ int refs = 0;
if (!tty)
return;
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
if (tty->session) {
do_each_pid_task(tty->session, PIDTYPE_SID, p) {
spin_lock_irq(&p->sighand->siglock);
- if (p->signal->tty == tty)
+ if (p->signal->tty == tty) {
p->signal->tty = NULL;
+ /* We defer the dereferences outside fo
+ the tasklist lock */
+ refs++;
+ }
if (!p->signal->leader) {
spin_unlock_irq(&p->sighand->siglock);
continue;
tty->ctrl_status = 0;
spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+ /* Account for the p->signal references we killed */
+ while (refs--)
+ tty_kref_put(tty);
+
/*
* If one of the devices matches a console pointer, we
* cannot just call hangup() because that will cause
EXPORT_SYMBOL(tty_vhangup);
/**
+ * tty_vhangup_self - process vhangup for own ctty
+ *
+ * Perform a vhangup on the current controlling tty
+ */
+
+void tty_vhangup_self(void)
+{
+ struct tty_struct *tty;
+
+ tty = get_current_tty();
+ if (tty) {
+ tty_vhangup(tty);
+ tty_kref_put(tty);
+ }
+}
+
+/**
* tty_hung_up_p - was tty hung up
* @filp: file pointer of tty
*
EXPORT_SYMBOL(tty_hung_up_p);
-/**
- * is_tty - checker whether file is a TTY
- * @filp: file handle that may be a tty
- *
- * Check if the file handle is a tty handle.
- */
-
-int is_tty(struct file *filp)
-{
- return filp->f_op->read == tty_read
- || filp->f_op->read == hung_up_tty_read;
-}
-
static void session_clear_tty(struct pid *session)
{
struct task_struct *p;
struct pid *tty_pgrp = NULL;
- mutex_lock(&tty_mutex);
tty = get_current_tty();
if (tty) {
tty_pgrp = get_pid(tty->pgrp);
- mutex_unlock(&tty_mutex);
lock_kernel();
- /* XXX: here we race, there is nothing protecting tty */
if (on_exit && tty->driver->type != TTY_DRIVER_TYPE_PTY)
tty_vhangup(tty);
unlock_kernel();
+ tty_kref_put(tty);
} else if (on_exit) {
struct pid *old_pgrp;
spin_lock_irq(¤t->sighand->siglock);
kill_pgrp(old_pgrp, SIGCONT, on_exit);
put_pid(old_pgrp);
}
- mutex_unlock(&tty_mutex);
return;
}
if (tty_pgrp) {
current->signal->tty_old_pgrp = NULL;
spin_unlock_irq(¤t->sighand->siglock);
- mutex_lock(&tty_mutex);
- /* It is possible that do_tty_hangup has free'd this tty */
tty = get_current_tty();
if (tty) {
unsigned long flags;
tty->session = NULL;
tty->pgrp = NULL;
spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+ tty_kref_put(tty);
} else {
#ifdef TTY_DEBUG_HANGUP
printk(KERN_DEBUG "error attempted to write to tty [0x%p]"
" = NULL", tty);
#endif
}
- mutex_unlock(&tty_mutex);
/* Now clear signal->tty under the lock */
read_lock(&tasklist_lock);
/* 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);
return ret;
}
+/**
+ * tty_write_message - write a message to a certain tty, not just the console.
+ * @tty: the destination tty_struct
+ * @msg: the message to write
+ *
+ * This is used for messages that need to be redirected to a specific tty.
+ * We don't put it into the syslog queue right now maybe in the future if
+ * really needed.
+ *
+ * We must still hold the BKL and test the CLOSING flag for the moment.
+ */
+
+void tty_write_message(struct tty_struct *tty, char *msg)
+{
+ lock_kernel();
+ if (tty) {
+ mutex_lock(&tty->atomic_write_lock);
+ if (tty->ops->write && !test_bit(TTY_CLOSING, &tty->flags))
+ tty->ops->write(tty, msg, strlen(msg));
+ tty_write_unlock(tty);
+ }
+ unlock_kernel();
+ return;
+}
+
/**
* tty_write - write method for tty device file
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;
}
o_tty = alloc_tty_struct();
if (!o_tty)
goto free_mem_out;
+ if (!try_module_get(driver->other->owner)) {
+ /* This cannot in fact currently happen */
+ free_tty_struct(o_tty);
+ o_tty = NULL;
+ goto free_mem_out;
+ }
initialize_tty_struct(o_tty);
o_tty->driver = driver->other;
o_tty->ops = driver->ops;
* to decrement the use counts, as release_tty doesn't care.
*/
- if (tty->ldisc.open) {
- retval = (tty->ldisc.open)(tty);
- if (retval)
- goto release_mem_out;
- }
- if (o_tty && o_tty->ldisc.open) {
- retval = (o_tty->ldisc.open)(o_tty);
- if (retval) {
- if (tty->ldisc.close)
- (tty->ldisc.close)(tty);
- goto release_mem_out;
- }
- tty_ldisc_enable(o_tty);
- }
- tty_ldisc_enable(tty);
- goto success;
+ retval = tty_ldisc_setup(tty, o_tty);
+
+ if (retval)
+ goto release_mem_out;
+ goto success;
/*
* This fast open can be used if the tty is already open.
/* Release locally allocated memory ... nothing placed in slots */
free_mem_out:
kfree(o_tp);
- if (o_tty)
+ if (o_tty) {
+ module_put(o_tty->driver->owner);
free_tty_struct(o_tty);
+ }
kfree(ltp);
kfree(tp);
free_tty_struct(tty);
/**
* release_one_tty - release tty structure memory
+ * @kref: kref of tty we are obliterating
*
* Releases memory associated with a tty structure, and clears out the
* driver table slots. This function is called when a device is no longer
* tty_mutex - sometimes only
* takes the file list lock internally when working on the list
* of ttys that the driver keeps.
- * FIXME: should we require tty_mutex is held here ??
*/
-static void release_one_tty(struct tty_struct *tty, int idx)
+static void release_one_tty(struct kref *kref)
{
+ struct tty_struct *tty = container_of(kref, struct tty_struct, kref);
+ struct tty_driver *driver = tty->driver;
int devpts = tty->driver->flags & TTY_DRIVER_DEVPTS_MEM;
struct ktermios *tp;
+ int idx = tty->index;
if (!devpts)
tty->driver->ttys[idx] = NULL;
if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) {
+ /* FIXME: Locking on ->termios array */
tp = tty->termios;
if (!devpts)
tty->driver->termios[idx] = NULL;
tty->magic = 0;
+ /* FIXME: locking on tty->driver->refcount */
tty->driver->refcount--;
+ module_put(driver->owner);
file_list_lock();
list_del_init(&tty->tty_files);
}
/**
+ * tty_kref_put - release a tty kref
+ * @tty: tty device
+ *
+ * Release a reference to a tty device and if need be let the kref
+ * layer destruct the object for us
+ */
+
+void tty_kref_put(struct tty_struct *tty)
+{
+ if (tty)
+ kref_put(&tty->kref, release_one_tty);
+}
+EXPORT_SYMBOL(tty_kref_put);
+
+/**
* release_tty - release tty structure memory
*
* Release both @tty and a possible linked partner (think pty pair),
* takes the file list lock internally when working on the list
* of ttys that the driver keeps.
* FIXME: should we require tty_mutex is held here ??
+ *
*/
static void release_tty(struct tty_struct *tty, int idx)
{
- struct tty_driver *driver = tty->driver;
+ /* This should always be true but check for the moment */
+ WARN_ON(tty->index != idx);
if (tty->link)
- release_one_tty(tty->link, idx);
- release_one_tty(tty, idx);
- module_put(driver->owner);
+ tty_kref_put(tty->link);
+ tty_kref_put(tty);
}
/*
int devpts;
int idx;
char buf[64];
- unsigned long flags;
tty = (struct tty_struct *)filp->private_data;
if (tty_paranoia_check(tty, filp->f_path.dentry->d_inode,
printk(KERN_DEBUG "freeing tty structure...");
#endif
/*
- * Prevent flush_to_ldisc() from rescheduling the work for later. Then
- * kill any delayed work. As this is the final close it does not
- * race with the set_ldisc code path.
- */
- clear_bit(TTY_LDISC, &tty->flags);
- cancel_delayed_work(&tty->buf.work);
-
- /*
- * Wait for ->hangup_work and ->buf.work handlers to terminate
- */
-
- flush_scheduled_work();
-
- /*
- * Wait for any short term users (we know they are just driver
- * side waiters as the file is closing so user count on the file
- * side is zero.
- */
- spin_lock_irqsave(&tty_ldisc_lock, flags);
- while (tty->ldisc.refcount) {
- spin_unlock_irqrestore(&tty_ldisc_lock, flags);
- wait_event(tty_ldisc_wait, tty->ldisc.refcount == 0);
- spin_lock_irqsave(&tty_ldisc_lock, flags);
- }
- 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);
-
- /*
- * Switch the line discipline back
+ * Ask the line discipline code to release its structures
*/
- tty_ldisc_assign(tty, tty_ldisc_get(N_TTY));
- 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));
- tty_set_termios_ldisc(o_tty, N_TTY);
- }
+ tty_ldisc_release(tty, o_tty);
/*
* The release_tty function takes care of the details of clearing
* the slots and preserving the termios structure.
index = tty->index;
filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */
/* noctty = 1; */
+ /* FIXME: Should we take a driver reference ? */
+ tty_kref_put(tty);
goto got_driver;
}
#ifdef CONFIG_VT
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;
}
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;
}
}
/**
- * tiocswinsz - implement window size set ioctl
- * @tty; tty
- * @arg: user buffer for result
- *
- * Copies the user idea of the window size to the kernel. Traditionally
- * this is just advisory information but for the Linux console it
- * actually has driver level meaning and triggers a VC resize.
+ * tty_do_resize - resize event
+ * @tty: tty being resized
+ * @real_tty: real tty (not the same as tty if using a pty/tty pair)
+ * @rows: rows (character)
+ * @cols: cols (character)
*
- * Locking:
- * Called function use the console_sem is used to ensure we do
- * not try and resize the console twice at once.
- * The tty->termios_mutex is used to ensure we don't double
- * resize and get confused. Lock order - tty->termios_mutex before
- * console sem
+ * Update the termios variables and send the neccessary signals to
+ * peform a terminal resize correctly
*/
-static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty,
- struct winsize __user *arg)
+int tty_do_resize(struct tty_struct *tty, struct tty_struct *real_tty,
+ struct winsize *ws)
{
- struct winsize tmp_ws;
struct pid *pgrp, *rpgrp;
unsigned long flags;
- if (copy_from_user(&tmp_ws, arg, sizeof(*arg)))
- return -EFAULT;
-
- mutex_lock(&tty->termios_mutex);
- if (!memcmp(&tmp_ws, &tty->winsize, sizeof(*arg)))
+ /* For a PTY we need to lock the tty side */
+ mutex_lock(&real_tty->termios_mutex);
+ if (!memcmp(ws, &real_tty->winsize, sizeof(*ws)))
goto done;
-
-#ifdef CONFIG_VT
- if (tty->driver->type == TTY_DRIVER_TYPE_CONSOLE) {
- if (vc_lock_resize(tty->driver_data, tmp_ws.ws_col,
- tmp_ws.ws_row)) {
- mutex_unlock(&tty->termios_mutex);
- return -ENXIO;
- }
- }
-#endif
/* Get the PID values and reference them so we can
avoid holding the tty ctrl lock while sending signals */
spin_lock_irqsave(&tty->ctrl_lock, flags);
put_pid(pgrp);
put_pid(rpgrp);
- tty->winsize = tmp_ws;
- real_tty->winsize = tmp_ws;
+ tty->winsize = *ws;
+ real_tty->winsize = *ws;
done:
- mutex_unlock(&tty->termios_mutex);
+ mutex_unlock(&real_tty->termios_mutex);
return 0;
}
/**
+ * tiocswinsz - implement window size set ioctl
+ * @tty; tty
+ * @arg: user buffer for result
+ *
+ * Copies the user idea of the window size to the kernel. Traditionally
+ * this is just advisory information but for the Linux console it
+ * actually has driver level meaning and triggers a VC resize.
+ *
+ * Locking:
+ * Driver dependant. The default do_resize method takes the
+ * tty termios mutex and ctrl_lock. The console takes its own lock
+ * then calls into the default method.
+ */
+
+static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty,
+ struct winsize __user *arg)
+{
+ struct winsize tmp_ws;
+ if (copy_from_user(&tmp_ws, arg, sizeof(*arg)))
+ return -EFAULT;
+
+ if (tty->ops->resize)
+ return tty->ops->resize(tty, real_tty, &tmp_ws);
+ else
+ return tty_do_resize(tty, real_tty, &tmp_ws);
+}
+
+/**
* tioccons - allow admin to move logical console
* @file: the file to become console
*
static int send_break(struct tty_struct *tty, unsigned int duration)
{
- if (tty_write_lock(tty, 0) < 0)
- return -EINTR;
- tty->ops->break_ctl(tty, -1);
- if (!signal_pending(current))
- msleep_interruptible(duration);
- tty->ops->break_ctl(tty, 0);
- tty_write_unlock(tty);
- if (!signal_pending(current))
- return -EINTR;
- return 0;
+ int retval;
+
+ if (tty->ops->break_ctl == NULL)
+ return 0;
+
+ if (tty->driver->flags & TTY_DRIVER_HARDWARE_BREAK)
+ retval = tty->ops->break_ctl(tty, duration);
+ else {
+ /* Do the work ourselves */
+ if (tty_write_lock(tty, 0) < 0)
+ return -EINTR;
+ retval = tty->ops->break_ctl(tty, -1);
+ if (retval)
+ goto out;
+ if (!signal_pending(current))
+ msleep_interruptible(duration);
+ retval = tty->ops->break_ctl(tty, 0);
+out:
+ tty_write_unlock(tty);
+ if (signal_pending(current))
+ retval = -EINTR;
+ }
+ return retval;
}
/**
static int tty_tiocmset(struct tty_struct *tty, struct file *file, unsigned int cmd,
unsigned __user *p)
{
- int retval = -EINVAL;
-
- if (tty->ops->tiocmset) {
- unsigned int set, clear, val;
-
- retval = get_user(val, p);
- if (retval)
- return retval;
-
- set = clear = 0;
- switch (cmd) {
- case TIOCMBIS:
- set = val;
- break;
- case TIOCMBIC:
- clear = val;
- break;
- case TIOCMSET:
- set = val;
- clear = ~val;
- break;
- }
+ int retval;
+ unsigned int set, clear, val;
- set &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP;
- clear &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP;
+ if (tty->ops->tiocmset == NULL)
+ return -EINVAL;
- retval = tty->ops->tiocmset(tty, file, set, clear);
+ retval = get_user(val, p);
+ if (retval)
+ return retval;
+ set = clear = 0;
+ switch (cmd) {
+ case TIOCMBIS:
+ set = val;
+ break;
+ case TIOCMBIC:
+ clear = val;
+ break;
+ case TIOCMSET:
+ set = val;
+ clear = ~val;
+ break;
}
- return retval;
+ set &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP;
+ clear &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP;
+ return tty->ops->tiocmset(tty, file, set, clear);
}
/*
tty->driver->subtype == PTY_TYPE_MASTER)
real_tty = tty->link;
- /*
- * Break handling by driver
- */
-
- retval = -EINVAL;
-
- if (!tty->ops->break_ctl) {
- switch (cmd) {
- case TIOCSBRK:
- case TIOCCBRK:
- 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->ops->ioctl)
- return 0;
- 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;
- }
- }
/*
* Factor out some common prep work
break;
}
+ /*
+ * Now do the stuff.
+ */
switch (cmd) {
case TIOCSTI:
return tiocsti(tty, p);
case TIOCGWINSZ:
- return tiocgwinsz(tty, p);
+ return tiocgwinsz(real_tty, p);
case TIOCSWINSZ:
return tiocswinsz(tty, real_tty, p);
case TIOCCONS:
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
- case TIOCLINUX:
- return tioclinux(tty, arg);
-#endif
/*
* Break handling
*/
case TIOCSBRK: /* Turn break on, unconditionally */
if (tty->ops->break_ctl)
- tty->ops->break_ctl(tty, -1);
+ return tty->ops->break_ctl(tty, -1);
return 0;
-
case TIOCCBRK: /* Turn break off, unconditionally */
if (tty->ops->break_ctl)
- tty->ops->break_ctl(tty, 0);
+ return 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
}
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;
}
}
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;
EXPORT_SYMBOL(do_SAK);
/**
- * flush_to_ldisc
- * @work: tty structure passed from work queue.
- *
- * This routine is called out of the software interrupt to flush data
- * from the buffer chain to the line discipline.
- *
- * Locking: holds tty->buf.lock to guard buffer list. Drops the lock
- * while invoking the line discipline receive_buf method. The
- * receive_buf method is single threaded for each tty instance.
- */
-
-static void flush_to_ldisc(struct work_struct *work)
-{
- struct tty_struct *tty =
- container_of(work, struct tty_struct, buf.work.work);
- unsigned long flags;
- struct tty_ldisc *disc;
- struct tty_buffer *tbuf, *head;
- char *char_buf;
- unsigned char *flag_buf;
-
- disc = tty_ldisc_ref(tty);
- if (disc == NULL) /* !TTY_LDISC */
- return;
-
- spin_lock_irqsave(&tty->buf.lock, flags);
- /* So we know a flush is running */
- set_bit(TTY_FLUSHING, &tty->flags);
- head = tty->buf.head;
- if (head != NULL) {
- tty->buf.head = NULL;
- for (;;) {
- int count = head->commit - head->read;
- if (!count) {
- if (head->next == NULL)
- break;
- tbuf = head;
- head = head->next;
- tty_buffer_free(tty, tbuf);
- continue;
- }
- /* Ldisc or user is trying to flush the buffers
- we are feeding to the ldisc, stop feeding the
- line discipline as we want to empty the queue */
- if (test_bit(TTY_FLUSHPENDING, &tty->flags))
- break;
- if (!tty->receive_room) {
- schedule_delayed_work(&tty->buf.work, 1);
- break;
- }
- if (count > tty->receive_room)
- count = tty->receive_room;
- char_buf = head->char_buf_ptr + head->read;
- 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);
- spin_lock_irqsave(&tty->buf.lock, flags);
- }
- /* Restore the queue head */
- tty->buf.head = head;
- }
- /* We may have a deferred request to flush the input buffer,
- if so pull the chain under the lock and empty the queue */
- if (test_bit(TTY_FLUSHPENDING, &tty->flags)) {
- __tty_buffer_flush(tty);
- clear_bit(TTY_FLUSHPENDING, &tty->flags);
- wake_up(&tty->read_wait);
- }
- clear_bit(TTY_FLUSHING, &tty->flags);
- spin_unlock_irqrestore(&tty->buf.lock, flags);
-
- tty_ldisc_deref(disc);
-}
-
-/**
- * tty_flip_buffer_push - terminal
- * @tty: tty to push
- *
- * Queue a push of the terminal flip buffers to the line discipline. This
- * function must not be called from IRQ context if tty->low_latency is set.
- *
- * In the event of the queue being busy for flipping the work will be
- * held off and retried later.
- *
- * Locking: tty buffer lock. Driver locks in low latency mode.
- */
-
-void tty_flip_buffer_push(struct tty_struct *tty)
-{
- unsigned long flags;
- spin_lock_irqsave(&tty->buf.lock, flags);
- if (tty->buf.tail != NULL)
- tty->buf.tail->commit = tty->buf.tail->used;
- spin_unlock_irqrestore(&tty->buf.lock, flags);
-
- if (tty->low_latency)
- flush_to_ldisc(&tty->buf.work.work);
- else
- schedule_delayed_work(&tty->buf.work, 1);
-}
-
-EXPORT_SYMBOL(tty_flip_buffer_push);
-
-
-/**
* initialize_tty_struct
* @tty: tty to initialize
*
static void initialize_tty_struct(struct tty_struct *tty)
{
memset(tty, 0, sizeof(struct tty_struct));
+ kref_init(&tty->kref);
tty->magic = TTY_MAGIC;
- tty_ldisc_assign(tty, tty_ldisc_get(N_TTY));
+ tty_ldisc_init(tty);
tty->session = NULL;
tty->pgrp = NULL;
tty->overrun_time = jiffies;
tty->buf.head = tty->buf.tail = NULL;
tty_buffer_init(tty);
- INIT_DELAYED_WORK(&tty->buf.work, flush_to_ldisc);
mutex_init(&tty->termios_mutex);
init_waitqueue_head(&tty->write_wait);
init_waitqueue_head(&tty->read_wait);
else
tty_line_name(driver, index, name);
- return device_create(tty_class, device, dev, name);
+ return device_create_drvdata(tty_class, device, dev, NULL, name);
}
/**
void proc_clear_tty(struct task_struct *p)
{
+ struct tty_struct *tty;
spin_lock_irq(&p->sighand->siglock);
+ tty = p->signal->tty;
p->signal->tty = NULL;
spin_unlock_irq(&p->sighand->siglock);
+ tty_kref_put(tty);
}
-EXPORT_SYMBOL(proc_clear_tty);
/* Called under the sighand lock */
tty->pgrp = get_pid(task_pgrp(tsk));
spin_unlock_irqrestore(&tty->ctrl_lock, flags);
tty->session = get_pid(task_session(tsk));
+ if (tsk->signal->tty) {
+ printk(KERN_DEBUG "tty not NULL!!\n");
+ tty_kref_put(tsk->signal->tty);
+ }
}
put_pid(tsk->signal->tty_old_pgrp);
- tsk->signal->tty = tty;
+ tsk->signal->tty = tty_kref_get(tty);
tsk->signal->tty_old_pgrp = NULL;
}
struct tty_struct *get_current_tty(void)
{
struct tty_struct *tty;
- WARN_ON_ONCE(!mutex_is_locked(&tty_mutex));
- tty = current->signal->tty;
- /*
- * session->tty can be changed/cleared from under us, make sure we
- * issue the load. The obtained pointer, when not NULL, is valid as
- * long as we hold tty_mutex.
- */
- barrier();
+ unsigned long flags;
+
+ spin_lock_irqsave(¤t->sighand->siglock, flags);
+ tty = tty_kref_get(current->signal->tty);
+ spin_unlock_irqrestore(¤t->sighand->siglock, flags);
return tty;
}
EXPORT_SYMBOL_GPL(get_current_tty);
initcall_t *call;
/* Setup the default TTY line discipline. */
- (void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);
+ tty_ldisc_begin();
/*
* set up the console device so that later boot sequences can
if (cdev_add(&tty_cdev, MKDEV(TTYAUX_MAJOR, 0), 1) ||
register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty") < 0)
panic("Couldn't register /dev/tty driver\n");
- device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), "tty");
+ device_create_drvdata(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 0), NULL,
+ "tty");
cdev_init(&console_cdev, &console_fops);
if (cdev_add(&console_cdev, MKDEV(TTYAUX_MAJOR, 1), 1) ||
register_chrdev_region(MKDEV(TTYAUX_MAJOR, 1), 1, "/dev/console") < 0)
panic("Couldn't register /dev/console driver\n");
- device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 1), "console");
+ device_create_drvdata(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 1), NULL,
+ "console");
#ifdef CONFIG_UNIX98_PTYS
cdev_init(&ptmx_cdev, &ptmx_fops);
if (cdev_add(&ptmx_cdev, MKDEV(TTYAUX_MAJOR, 2), 1) ||
register_chrdev_region(MKDEV(TTYAUX_MAJOR, 2), 1, "/dev/ptmx") < 0)
panic("Couldn't register /dev/ptmx driver\n");
- device_create(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 2), "ptmx");
+ device_create_drvdata(tty_class, NULL, MKDEV(TTYAUX_MAJOR, 2), NULL, "ptmx");
#endif
#ifdef CONFIG_VT
if (cdev_add(&vc0_cdev, MKDEV(TTY_MAJOR, 0), 1) ||
register_chrdev_region(MKDEV(TTY_MAJOR, 0), 1, "/dev/vc/0") < 0)
panic("Couldn't register /dev/tty0 driver\n");
- device_create(tty_class, NULL, MKDEV(TTY_MAJOR, 0), "tty0");
+ device_create_drvdata(tty_class, NULL, MKDEV(TTY_MAJOR, 0), NULL, "tty0");
vty_init();
#endif