Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit eeb89d91 authored by Alan Cox's avatar Alan Cox Committed by Greg Kroah-Hartman
Browse files

tty: push the BKL down into the handlers a bit



Start trying to untangle the remaining BKL mess

Updated to fix missing unlock_kernel noted by Dan Carpenter

Signed-off-by: default avatarAlan "I must be out of my tree" Cox <alan@linux.intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent e8c62103
Loading
Loading
Loading
Loading
+1 −1
Original line number Original line Diff line number Diff line
@@ -659,7 +659,7 @@ static int __ptmx_open(struct inode *inode, struct file *filp)
	if (!retval)
	if (!retval)
		return 0;
		return 0;
out1:
out1:
	tty_release_dev(filp);
	tty_release(inode, filp);
	return retval;
	return retval;
out:
out:
	devpts_kill_index(inode, index);
	devpts_kill_index(inode, index);
+80 −61
Original line number Original line Diff line number Diff line
@@ -142,7 +142,6 @@ ssize_t redirected_tty_write(struct file *, const char __user *,
							size_t, loff_t *);
							size_t, loff_t *);
static unsigned int tty_poll(struct file *, poll_table *);
static unsigned int tty_poll(struct file *, poll_table *);
static int tty_open(struct inode *, struct file *);
static int tty_open(struct inode *, struct file *);
static int tty_release(struct inode *, struct file *);
long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
#ifdef CONFIG_COMPAT
#ifdef CONFIG_COMPAT
static long tty_compat_ioctl(struct file *file, unsigned int cmd,
static long tty_compat_ioctl(struct file *file, unsigned int cmd,
@@ -1017,14 +1016,16 @@ static inline ssize_t do_tty_write(


void tty_write_message(struct tty_struct *tty, char *msg)
void tty_write_message(struct tty_struct *tty, char *msg)
{
{
	lock_kernel();
	if (tty) {
	if (tty) {
		mutex_lock(&tty->atomic_write_lock);
		mutex_lock(&tty->atomic_write_lock);
		if (tty->ops->write && !test_bit(TTY_CLOSING, &tty->flags))
		lock_kernel();
		if (tty->ops->write && !test_bit(TTY_CLOSING, &tty->flags)) {
			unlock_kernel();
			tty->ops->write(tty, msg, strlen(msg));
			tty->ops->write(tty, msg, strlen(msg));
		} else
			unlock_kernel();
		tty_write_unlock(tty);
		tty_write_unlock(tty);
	}
	}
	unlock_kernel();
	return;
	return;
}
}


@@ -1202,14 +1203,21 @@ static int tty_driver_install_tty(struct tty_driver *driver,
						struct tty_struct *tty)
						struct tty_struct *tty)
{
{
	int idx = tty->index;
	int idx = tty->index;
	int ret;


	if (driver->ops->install)
	if (driver->ops->install) {
		return driver->ops->install(driver, tty);
		lock_kernel();
		ret = driver->ops->install(driver, tty);
		unlock_kernel();
		return ret;
	}


	if (tty_init_termios(tty) == 0) {
	if (tty_init_termios(tty) == 0) {
		lock_kernel();
		tty_driver_kref_get(driver);
		tty_driver_kref_get(driver);
		tty->count++;
		tty->count++;
		driver->ttys[idx] = tty;
		driver->ttys[idx] = tty;
		unlock_kernel();
		return 0;
		return 0;
	}
	}
	return -ENOMEM;
	return -ENOMEM;
@@ -1302,10 +1310,14 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx,
	struct tty_struct *tty;
	struct tty_struct *tty;
	int retval;
	int retval;


	lock_kernel();
	/* Check if pty master is being opened multiple times */
	/* Check if pty master is being opened multiple times */
	if (driver->subtype == PTY_TYPE_MASTER &&
	if (driver->subtype == PTY_TYPE_MASTER &&
		(driver->flags & TTY_DRIVER_DEVPTS_MEM) && !first_ok)
		(driver->flags & TTY_DRIVER_DEVPTS_MEM) && !first_ok) {
		unlock_kernel();
		return ERR_PTR(-EIO);
		return ERR_PTR(-EIO);
	}
	unlock_kernel();


	/*
	/*
	 * First time open is complex, especially for PTY devices.
	 * First time open is complex, especially for PTY devices.
@@ -1335,8 +1347,9 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx,
	 * If we fail here just call release_tty to clean up.  No need
	 * If we fail here just call release_tty to clean up.  No need
	 * to decrement the use counts, as release_tty doesn't care.
	 * to decrement the use counts, as release_tty doesn't care.
	 */
	 */

	lock_kernel();
	retval = tty_ldisc_setup(tty, tty->link);
	retval = tty_ldisc_setup(tty, tty->link);
	unlock_kernel();
	if (retval)
	if (retval)
		goto release_mem_out;
		goto release_mem_out;
	return tty;
	return tty;
@@ -1350,7 +1363,9 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx,
	if (printk_ratelimit())
	if (printk_ratelimit())
		printk(KERN_INFO "tty_init_dev: ldisc open failed, "
		printk(KERN_INFO "tty_init_dev: ldisc open failed, "
				 "clearing slot %d\n", idx);
				 "clearing slot %d\n", idx);
	lock_kernel();
	release_tty(tty, idx);
	release_tty(tty, idx);
	unlock_kernel();
	return ERR_PTR(retval);
	return ERR_PTR(retval);
}
}


@@ -1464,7 +1479,17 @@ static void release_tty(struct tty_struct *tty, int idx)
	tty_kref_put(tty);
	tty_kref_put(tty);
}
}


/*
/**
 *	tty_release		-	vfs callback for close
 *	@inode: inode of tty
 *	@filp: file pointer for handle to tty
 *
 *	Called the last time each file handle is closed that references
 *	this tty. There may however be several such references.
 *
 *	Locking:
 *		Takes bkl. See tty_release_dev
 *
 * Even releasing the tty structures is a tricky business.. We have
 * Even releasing the tty structures is a tricky business.. We have
 * to be very careful that the structures are all released at the
 * to be very careful that the structures are all released at the
 * same time, as interrupts might otherwise get the wrong pointers.
 * same time, as interrupts might otherwise get the wrong pointers.
@@ -1472,20 +1497,20 @@ static void release_tty(struct tty_struct *tty, int idx)
 * WSH 09/09/97: rewritten to avoid some nasty race conditions that could
 * WSH 09/09/97: rewritten to avoid some nasty race conditions that could
 * lead to double frees or releasing memory still in use.
 * lead to double frees or releasing memory still in use.
 */
 */
void tty_release_dev(struct file *filp)

int tty_release(struct inode *inode, struct file *filp)
{
{
	struct tty_struct *tty, *o_tty;
	struct tty_struct *tty, *o_tty;
	int	pty_master, tty_closing, o_tty_closing, do_sleep;
	int	pty_master, tty_closing, o_tty_closing, do_sleep;
	int	devpts;
	int	devpts;
	int	idx;
	int	idx;
	char	buf[64];
	char	buf[64];
	struct 	inode *inode;


	inode = filp->f_path.dentry->d_inode;
	tty = (struct tty_struct *)filp->private_data;
	tty = (struct tty_struct *)filp->private_data;
	if (tty_paranoia_check(tty, inode, "tty_release_dev"))
	if (tty_paranoia_check(tty, inode, "tty_release_dev"))
		return;
		return 0;


	lock_kernel();
	check_tty_count(tty, "tty_release_dev");
	check_tty_count(tty, "tty_release_dev");


	tty_fasync(-1, filp, 0);
	tty_fasync(-1, filp, 0);
@@ -1500,19 +1525,22 @@ void tty_release_dev(struct file *filp)
	if (idx < 0 || idx >= tty->driver->num) {
	if (idx < 0 || idx >= tty->driver->num) {
		printk(KERN_DEBUG "tty_release_dev: bad idx when trying to "
		printk(KERN_DEBUG "tty_release_dev: bad idx when trying to "
				  "free (%s)\n", tty->name);
				  "free (%s)\n", tty->name);
		return;
		unlock_kernel();
		return 0;
	}
	}
	if (!devpts) {
	if (!devpts) {
		if (tty != tty->driver->ttys[idx]) {
		if (tty != tty->driver->ttys[idx]) {
			unlock_kernel();
			printk(KERN_DEBUG "tty_release_dev: driver.table[%d] not tty "
			printk(KERN_DEBUG "tty_release_dev: driver.table[%d] not tty "
			       "for (%s)\n", idx, tty->name);
			       "for (%s)\n", idx, tty->name);
			return;
			return 0;
		}
		}
		if (tty->termios != tty->driver->termios[idx]) {
		if (tty->termios != tty->driver->termios[idx]) {
			unlock_kernel();
			printk(KERN_DEBUG "tty_release_dev: driver.termios[%d] not termios "
			printk(KERN_DEBUG "tty_release_dev: driver.termios[%d] not termios "
			       "for (%s)\n",
			       "for (%s)\n",
			       idx, tty->name);
			       idx, tty->name);
			return;
			return 0;
		}
		}
	}
	}
#endif
#endif
@@ -1526,26 +1554,30 @@ void tty_release_dev(struct file *filp)
	if (tty->driver->other &&
	if (tty->driver->other &&
	     !(tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)) {
	     !(tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)) {
		if (o_tty != tty->driver->other->ttys[idx]) {
		if (o_tty != tty->driver->other->ttys[idx]) {
			unlock_kernel();
			printk(KERN_DEBUG "tty_release_dev: other->table[%d] "
			printk(KERN_DEBUG "tty_release_dev: other->table[%d] "
					  "not o_tty for (%s)\n",
					  "not o_tty for (%s)\n",
			       idx, tty->name);
			       idx, tty->name);
			return;
			return 0 ;
		}
		}
		if (o_tty->termios != tty->driver->other->termios[idx]) {
		if (o_tty->termios != tty->driver->other->termios[idx]) {
			unlock_kernel();
			printk(KERN_DEBUG "tty_release_dev: other->termios[%d] "
			printk(KERN_DEBUG "tty_release_dev: other->termios[%d] "
					  "not o_termios for (%s)\n",
					  "not o_termios for (%s)\n",
			       idx, tty->name);
			       idx, tty->name);
			return;
			return 0;
		}
		}
		if (o_tty->link != tty) {
		if (o_tty->link != tty) {
			unlock_kernel();
			printk(KERN_DEBUG "tty_release_dev: bad pty pointers\n");
			printk(KERN_DEBUG "tty_release_dev: bad pty pointers\n");
			return;
			return 0;
		}
		}
	}
	}
#endif
#endif
	if (tty->ops->close)
	if (tty->ops->close)
		tty->ops->close(tty, filp);
		tty->ops->close(tty, filp);


	unlock_kernel();
	/*
	/*
	 * Sanity check: if tty->count is going to zero, there shouldn't be
	 * Sanity check: if tty->count is going to zero, there shouldn't be
	 * any waiters on tty->read_wait or tty->write_wait.  We test the
	 * any waiters on tty->read_wait or tty->write_wait.  We test the
@@ -1568,6 +1600,7 @@ void tty_release_dev(struct file *filp)
		   opens on /dev/tty */
		   opens on /dev/tty */


		mutex_lock(&tty_mutex);
		mutex_lock(&tty_mutex);
		lock_kernel();
		tty_closing = tty->count <= 1;
		tty_closing = tty->count <= 1;
		o_tty_closing = o_tty &&
		o_tty_closing = o_tty &&
			(o_tty->count <= (pty_master ? 1 : 0));
			(o_tty->count <= (pty_master ? 1 : 0));
@@ -1598,6 +1631,7 @@ void tty_release_dev(struct file *filp)


		printk(KERN_WARNING "tty_release_dev: %s: read/write wait queue "
		printk(KERN_WARNING "tty_release_dev: %s: read/write wait queue "
				    "active!\n", tty_name(tty, buf));
				    "active!\n", tty_name(tty, buf));
		unlock_kernel();
		mutex_unlock(&tty_mutex);
		mutex_unlock(&tty_mutex);
		schedule();
		schedule();
	}
	}
@@ -1661,8 +1695,10 @@ void tty_release_dev(struct file *filp)
	mutex_unlock(&tty_mutex);
	mutex_unlock(&tty_mutex);


	/* check whether both sides are closing ... */
	/* check whether both sides are closing ... */
	if (!tty_closing || (o_tty && !o_tty_closing))
	if (!tty_closing || (o_tty && !o_tty_closing)) {
		return;
		unlock_kernel();
		return 0;
	}


#ifdef TTY_DEBUG_HANGUP
#ifdef TTY_DEBUG_HANGUP
	printk(KERN_DEBUG "freeing tty structure...");
	printk(KERN_DEBUG "freeing tty structure...");
@@ -1680,10 +1716,12 @@ void tty_release_dev(struct file *filp)
	/* Make this pty number available for reallocation */
	/* Make this pty number available for reallocation */
	if (devpts)
	if (devpts)
		devpts_kill_index(inode, idx);
		devpts_kill_index(inode, idx);
	unlock_kernel();
	return 0;
}
}


/**
/**
 *	__tty_open		-	open a tty device
 *	tty_open		-	open a tty device
 *	@inode: inode of device file
 *	@inode: inode of device file
 *	@filp: file pointer to tty
 *	@filp: file pointer to tty
 *
 *
@@ -1703,7 +1741,7 @@ void tty_release_dev(struct file *filp)
 *		 ->siglock protects ->signal/->sighand
 *		 ->siglock protects ->signal/->sighand
 */
 */


static int __tty_open(struct inode *inode, struct file *filp)
static int tty_open(struct inode *inode, struct file *filp)
{
{
	struct tty_struct *tty = NULL;
	struct tty_struct *tty = NULL;
	int noctty, retval;
	int noctty, retval;
@@ -1720,10 +1758,12 @@ static int __tty_open(struct inode *inode, struct file *filp)
	retval = 0;
	retval = 0;


	mutex_lock(&tty_mutex);
	mutex_lock(&tty_mutex);
	lock_kernel();


	if (device == MKDEV(TTYAUX_MAJOR, 0)) {
	if (device == MKDEV(TTYAUX_MAJOR, 0)) {
		tty = get_current_tty();
		tty = get_current_tty();
		if (!tty) {
		if (!tty) {
			unlock_kernel();
			mutex_unlock(&tty_mutex);
			mutex_unlock(&tty_mutex);
			return -ENXIO;
			return -ENXIO;
		}
		}
@@ -1755,12 +1795,14 @@ static int __tty_open(struct inode *inode, struct file *filp)
				goto got_driver;
				goto got_driver;
			}
			}
		}
		}
		unlock_kernel();
		mutex_unlock(&tty_mutex);
		mutex_unlock(&tty_mutex);
		return -ENODEV;
		return -ENODEV;
	}
	}


	driver = get_tty_driver(device, &index);
	driver = get_tty_driver(device, &index);
	if (!driver) {
	if (!driver) {
		unlock_kernel();
		mutex_unlock(&tty_mutex);
		mutex_unlock(&tty_mutex);
		return -ENODEV;
		return -ENODEV;
	}
	}
@@ -1770,6 +1812,7 @@ static int __tty_open(struct inode *inode, struct file *filp)
		tty = tty_driver_lookup_tty(driver, inode, index);
		tty = tty_driver_lookup_tty(driver, inode, index);


		if (IS_ERR(tty)) {
		if (IS_ERR(tty)) {
			unlock_kernel();
			mutex_unlock(&tty_mutex);
			mutex_unlock(&tty_mutex);
			return PTR_ERR(tty);
			return PTR_ERR(tty);
		}
		}
@@ -1784,8 +1827,10 @@ static int __tty_open(struct inode *inode, struct file *filp)


	mutex_unlock(&tty_mutex);
	mutex_unlock(&tty_mutex);
	tty_driver_kref_put(driver);
	tty_driver_kref_put(driver);
	if (IS_ERR(tty))
	if (IS_ERR(tty)) {
		unlock_kernel();
		return PTR_ERR(tty);
		return PTR_ERR(tty);
	}


	filp->private_data = tty;
	filp->private_data = tty;
	file_move(filp, &tty->tty_files);
	file_move(filp, &tty->tty_files);
@@ -1813,11 +1858,15 @@ static int __tty_open(struct inode *inode, struct file *filp)
		printk(KERN_DEBUG "error %d in opening %s...", retval,
		printk(KERN_DEBUG "error %d in opening %s...", retval,
		       tty->name);
		       tty->name);
#endif
#endif
		tty_release_dev(filp);
		tty_release(inode, filp);
		if (retval != -ERESTARTSYS)
		if (retval != -ERESTARTSYS) {
			unlock_kernel();
			return retval;
			return retval;
		if (signal_pending(current))
		}
		if (signal_pending(current)) {
			unlock_kernel();
			return retval;
			return retval;
		}
		schedule();
		schedule();
		/*
		/*
		 * Need to reset f_op in case a hangup happened.
		 * Need to reset f_op in case a hangup happened.
@@ -1826,8 +1875,11 @@ static int __tty_open(struct inode *inode, struct file *filp)
			filp->f_op = &tty_fops;
			filp->f_op = &tty_fops;
		goto retry_open;
		goto retry_open;
	}
	}
	unlock_kernel();



	mutex_lock(&tty_mutex);
	mutex_lock(&tty_mutex);
	lock_kernel();
	spin_lock_irq(&current->sighand->siglock);
	spin_lock_irq(&current->sighand->siglock);
	if (!noctty &&
	if (!noctty &&
	    current->signal->leader &&
	    current->signal->leader &&
@@ -1835,43 +1887,12 @@ static int __tty_open(struct inode *inode, struct file *filp)
	    tty->session == NULL)
	    tty->session == NULL)
		__proc_set_tty(current, tty);
		__proc_set_tty(current, tty);
	spin_unlock_irq(&current->sighand->siglock);
	spin_unlock_irq(&current->sighand->siglock);
	unlock_kernel();
	mutex_unlock(&tty_mutex);
	mutex_unlock(&tty_mutex);
	return 0;
	return 0;
}
}


/* BKL pushdown: scary code avoidance wrapper */
static int tty_open(struct inode *inode, struct file *filp)
{
	int ret;

	lock_kernel();
	ret = __tty_open(inode, filp);
	unlock_kernel();
	return ret;
}




/**
 *	tty_release		-	vfs callback for close
 *	@inode: inode of tty
 *	@filp: file pointer for handle to tty
 *
 *	Called the last time each file handle is closed that references
 *	this tty. There may however be several such references.
 *
 *	Locking:
 *		Takes bkl. See tty_release_dev
 */


static int tty_release(struct inode *inode, struct file *filp)
{
	lock_kernel();
	tty_release_dev(filp);
	unlock_kernel();
	return 0;
}


/**
/**
 *	tty_poll	-	check tty status
 *	tty_poll	-	check tty status
@@ -2317,9 +2338,7 @@ static int tiocsetd(struct tty_struct *tty, int __user *p)
	if (get_user(ldisc, p))
	if (get_user(ldisc, p))
		return -EFAULT;
		return -EFAULT;


	lock_kernel();
	ret = tty_set_ldisc(tty, ldisc);
	ret = tty_set_ldisc(tty, ldisc);
	unlock_kernel();


	return ret;
	return ret;
}
}
+13 −0
Original line number Original line Diff line number Diff line
@@ -34,6 +34,8 @@
#include <linux/vt_kern.h>
#include <linux/vt_kern.h>
#include <linux/selection.h>
#include <linux/selection.h>


#include <linux/smp_lock.h>	/* For the moment */

#include <linux/kmod.h>
#include <linux/kmod.h>
#include <linux/nsproxy.h>
#include <linux/nsproxy.h>


@@ -545,6 +547,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
	if (IS_ERR(new_ldisc))
	if (IS_ERR(new_ldisc))
		return PTR_ERR(new_ldisc);
		return PTR_ERR(new_ldisc);


	lock_kernel();
	/*
	/*
	 *	We need to look at the tty locking here for pty/tty pairs
	 *	We need to look at the tty locking here for pty/tty pairs
	 *	when both sides try to change in parallel.
	 *	when both sides try to change in parallel.
@@ -558,6 +561,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
	 */
	 */


	if (tty->ldisc->ops->num == ldisc) {
	if (tty->ldisc->ops->num == ldisc) {
		unlock_kernel();
		tty_ldisc_put(new_ldisc);
		tty_ldisc_put(new_ldisc);
		return 0;
		return 0;
	}
	}
@@ -569,6 +573,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)


	tty_wait_until_sent(tty, 0);
	tty_wait_until_sent(tty, 0);


	unlock_kernel();
	mutex_lock(&tty->ldisc_mutex);
	mutex_lock(&tty->ldisc_mutex);


	/*
	/*
@@ -582,6 +587,9 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
			test_bit(TTY_LDISC_CHANGING, &tty->flags) == 0);
			test_bit(TTY_LDISC_CHANGING, &tty->flags) == 0);
		mutex_lock(&tty->ldisc_mutex);
		mutex_lock(&tty->ldisc_mutex);
	}
	}

	lock_kernel();

	set_bit(TTY_LDISC_CHANGING, &tty->flags);
	set_bit(TTY_LDISC_CHANGING, &tty->flags);


	/*
	/*
@@ -592,6 +600,8 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
	tty->receive_room = 0;
	tty->receive_room = 0;


	o_ldisc = tty->ldisc;
	o_ldisc = tty->ldisc;

	unlock_kernel();
	/*
	/*
	 *	Make sure we don't change while someone holds a
	 *	Make sure we don't change while someone holds a
	 *	reference to the line discipline. The TTY_LDISC bit
	 *	reference to the line discipline. The TTY_LDISC bit
@@ -617,12 +627,14 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
	flush_scheduled_work();
	flush_scheduled_work();


	mutex_lock(&tty->ldisc_mutex);
	mutex_lock(&tty->ldisc_mutex);
	lock_kernel();
	if (test_bit(TTY_HUPPED, &tty->flags)) {
	if (test_bit(TTY_HUPPED, &tty->flags)) {
		/* We were raced by the hangup method. It will have stomped
		/* We were raced by the hangup method. It will have stomped
		   the ldisc data and closed the ldisc down */
		   the ldisc data and closed the ldisc down */
		clear_bit(TTY_LDISC_CHANGING, &tty->flags);
		clear_bit(TTY_LDISC_CHANGING, &tty->flags);
		mutex_unlock(&tty->ldisc_mutex);
		mutex_unlock(&tty->ldisc_mutex);
		tty_ldisc_put(new_ldisc);
		tty_ldisc_put(new_ldisc);
		unlock_kernel();
		return -EIO;
		return -EIO;
	}
	}


@@ -664,6 +676,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
	if (o_work)
	if (o_work)
		schedule_delayed_work(&o_tty->buf.work, 1);
		schedule_delayed_work(&o_tty->buf.work, 1);
	mutex_unlock(&tty->ldisc_mutex);
	mutex_unlock(&tty->ldisc_mutex);
	unlock_kernel();
	return retval;
	return retval;
}
}


+1 −1
Original line number Original line Diff line number Diff line
@@ -449,7 +449,7 @@ extern void initialize_tty_struct(struct tty_struct *tty,
		struct tty_driver *driver, int idx);
		struct tty_driver *driver, int idx);
extern struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx,
extern struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx,
								int first_ok);
								int first_ok);
extern void tty_release_dev(struct file *filp);
extern int tty_release(struct inode *inode, struct file *filp);
extern int tty_init_termios(struct tty_struct *tty);
extern int tty_init_termios(struct tty_struct *tty);


extern struct tty_struct *tty_pair_get_tty(struct tty_struct *tty);
extern struct tty_struct *tty_pair_get_tty(struct tty_struct *tty);