Exemple #1
0
static int set_termios(struct tty_struct * tty, struct termios * termios,
			int channel)
{
	int i;
	unsigned short old_cflag = tty->termios->c_cflag;

	/* If we try to set the state of terminal and we're not in the
	   foreground, send a SIGTTOU.  If the signal is blocked or
	   ignored, go ahead and perform the operation.  POSIX 7.2) */
	if ((current->tty == channel) &&
	     (tty->pgrp != current->pgrp)) {
		if (is_orphaned_pgrp(current->pgrp))
			return -EIO;
		if (!is_ignored(SIGTTOU)) {
			(void) kill_pg(current->pgrp,SIGTTOU,1);
			return -ERESTARTSYS;
		}
	}
	for (i=0 ; i< (sizeof (*termios)) ; i++)
		((char *)tty->termios)[i]=get_fs_byte(i+(char *)termios);
	if (IS_A_SERIAL(channel) && tty->termios->c_cflag != old_cflag)
		change_speed(channel-64);

	/* puting mpty's into echo mode is very bad, and I think under
	   some situations can cause the kernel to do nothing but
	   copy characters back and forth. -RAB */
	if (IS_A_PTY_MASTER(channel)) tty->termios->c_lflag &= ~ECHO;

	return 0;
}
Exemple #2
0
static int check_char_dev(struct inode * inode, struct file * filp)
{
	struct tty_struct *tty;
	int min, dev;

	dev = inode->i_rdev;
	if (MAJOR(dev) == 4 || MAJOR(dev) == 5) {
		if (MAJOR(dev) == 5)
			min = current->tty;
		else
			min = MINOR(dev);
		if (min < 0)
			return -1;
		if ((IS_A_PTY_MASTER(min)) && (inode->i_count>1))
			return -1;
		tty = TTY_TABLE(min);
		if (!(filp->f_flags & O_NOCTTY) &&
		    current->leader &&
		    current->tty<0 &&
		    tty->session==0) {
			current->tty = min;
			tty->session= current->session;
			tty->pgrp = current->pgrp;
		}
	}
	return 0;
}
Exemple #3
0
static void pty_close(struct tty_struct * tty, struct file * filp)
{
	wake_up(&tty->read_q.proc_list);
	wake_up(&tty->link->write_q.proc_list);
	if (IS_A_PTY_MASTER(tty->line)) {
		if (tty->link->pgrp > 0)
			kill_pg(tty->link->pgrp,SIGHUP,1);
	}
}
Exemple #4
0
static void pty_close(struct tty_struct * tty, struct file * filp)
{
	if (!tty || (tty->count > 1))
		return;
	wake_up_interruptible(&tty->read_q.proc_list);
	if (!tty->link)
		return;
	wake_up_interruptible(&tty->link->write_q.proc_list);
	if (IS_A_PTY_MASTER(tty->line)) {
		tty_hangup(tty->link);
		flush_input(tty);
		flush_output(tty);
	}
}
Exemple #5
0
int pty_open(struct tty_struct *tty, struct file * filp)
{
	if (!tty || !tty->link)
		return -ENODEV;
	if (IS_A_PTY_MASTER(tty->line))
		tty->write = mpty_write;
	else
		tty->write = spty_write;
	tty->close = pty_close;
	wake_up(&tty->read_q.proc_list);
	if (filp->f_flags & O_NDELAY)
		return 0;
	while (!tty->link->count && !(current->signal & ~current->blocked))
		interruptible_sleep_on(&tty->link->read_q.proc_list);
	if (!tty->link->count)
		return -ERESTARTSYS;
	return 0;
}
static int set_termios(struct tty_struct * tty, struct termios * termios,
			int channel)
{
	int i;
	struct termios old_termios = *tty->termios;

	i = check_change(tty, channel);
	if (i)
		return i;
	for (i=0 ; i< (sizeof (*termios)) ; i++)
		((char *)tty->termios)[i]=get_fs_byte(i+(char *)termios);

	/* puting mpty's into echo mode is very bad, and I think under
	   some situations can cause the kernel to do nothing but
	   copy characters back and forth. -RAB */
	if (IS_A_PTY_MASTER(channel)) tty->termios->c_lflag &= ~ECHO;

	if (tty->set_termios)
		(*tty->set_termios)(tty, &old_termios);

	return 0;
}
Exemple #7
0
int tty_ioctl(struct inode * inode, struct file * file,
	unsigned int cmd, unsigned int arg)
{
	struct tty_struct * tty;
	struct tty_struct * other_tty;
	int pgrp;
	int dev;

	if (MAJOR(file->f_rdev) != 4) {
		printk("tty_ioctl: tty pseudo-major != 4\n");
		return -EINVAL;
	}
	dev = MINOR(file->f_rdev);
	tty = TTY_TABLE(dev);
	if (!tty)
		return -EINVAL;

	if (IS_A_PTY(dev))
		other_tty = tty_table[PTY_OTHER(dev)];
	else
		other_tty = NULL;
		
	switch (cmd) {
		case TCGETS:
			return get_termios(tty,(struct termios *) arg);
		case TCSETSF:
			flush_input(tty);
		/* fallthrough */
		case TCSETSW:
			wait_until_sent(tty);
		/* fallthrough */
		case TCSETS:
			return set_termios(tty,(struct termios *) arg, dev);
		case TCGETA:
			return get_termio(tty,(struct termio *) arg);
		case TCSETAF:
			flush_input(tty);
		/* fallthrough */
		case TCSETAW:
			wait_until_sent(tty); /* fallthrough */
		case TCSETA:
			return set_termio(tty,(struct termio *) arg, dev);
		case TCXONC:
			switch (arg) {
			case TCOOFF:
				tty->stopped = 1;
				TTY_WRITE_FLUSH(tty);
				return 0;
			case TCOON:
				tty->stopped = 0;
				TTY_WRITE_FLUSH(tty);
				return 0;
			case TCIOFF:
				if (STOP_CHAR(tty))
					put_tty_queue(STOP_CHAR(tty),
						      &tty->write_q);
				return 0;
			case TCION:
				if (START_CHAR(tty))
					put_tty_queue(START_CHAR(tty),
						      &tty->write_q);
				return 0;
			}
			return -EINVAL; /* not implemented */
		case TCFLSH:
			if (arg==0)
				flush_input(tty);
			else if (arg==1)
				flush_output(tty);
			else if (arg==2) {
				flush_input(tty);
				flush_output(tty);
			} else
				return -EINVAL;
			return 0;
		case TIOCEXCL:
			return -EINVAL; /* not implemented */
		case TIOCNXCL:
			return -EINVAL; /* not implemented */
		case TIOCSCTTY:
			return -EINVAL; /* set controlling term NI */
		case TIOCGPGRP:
			verify_area((void *) arg,4);
			put_fs_long(tty->pgrp,(unsigned long *) arg);
			return 0;
		case TIOCSPGRP:
			if ((current->tty < 0) ||
			    (current->tty != dev) ||
			    (tty->session != current->session))
				return -ENOTTY;
			pgrp=get_fs_long((unsigned long *) arg);
			if (pgrp < 0)
				return -EINVAL;
			if (session_of_pgrp(pgrp) != current->session)
				return -EPERM;
			tty->pgrp = pgrp;			
			return 0;
		case TIOCOUTQ:
			verify_area((void *) arg,4);
			put_fs_long(CHARS(&tty->write_q),
				    (unsigned long *) arg);
			return 0;
		case TIOCINQ:
			verify_area((void *) arg,4);
			if (L_CANON(tty) && !tty->secondary.data)
				put_fs_long(0, (unsigned long *) arg);
			else
				put_fs_long(CHARS(&tty->secondary),
					(unsigned long *) arg);
			return 0;
		case TIOCSTI:
			return -EINVAL; /* not implemented */
		case TIOCGWINSZ:
			return get_window_size(tty,(struct winsize *) arg);
		case TIOCSWINSZ:
			if (IS_A_PTY_MASTER(dev))
				set_window_size(other_tty,(struct winsize *) arg);
			return set_window_size(tty,(struct winsize *) arg);
		case TIOCGSOFTCAR:
			return -EINVAL; /* not implemented */
		case TIOCSSOFTCAR:
			return -EINVAL; /* not implemented */
		case TIOCLINUX:
			switch (get_fs_byte((char *)arg))
			{
				case 0: 
					return do_screendump(arg);
				case 1: 
					return do_get_ps_info(arg);
				default: 
					return -EINVAL;
			}
		case TIOCCONS:
			if (!IS_A_PTY(dev))
				return -EINVAL;
			if (redirect)
				return -EBUSY;
			if (!suser())
				return -EPERM;
			if (IS_A_PTY_MASTER(dev))
				redirect = other_tty;
			else
				redirect = tty;
			return 0;
		case FIONBIO:
			if (arg)
				file->f_flags |= O_NONBLOCK;
			else
				file->f_flags &= ~O_NONBLOCK;
			return 0;
		case TIOCNOTTY:
			if (MINOR(file->f_rdev) != current->tty)
				return -EINVAL;
			current->tty = -1;
			if (current->leader) {
				if (tty->pgrp > 0)
					kill_pg(tty->pgrp, SIGHUP, 0);
				tty->pgrp = -1;
				tty->session = 0;
			}
			return 0;

	       case TIOCPKT:
			{
			   int on;
			   if (!IS_A_PTY_MASTER(dev))
			     return (-EINVAL);
			   verify_area ((unsigned long *)arg, sizeof (int));
			   on=get_fs_long ((unsigned long *)arg);
			   if (on )
			     tty->packet = 1;
			   else
			     tty->packet = 0;
			   return (0);
			}

		default:
			if (tty->ioctl)
				return (tty->ioctl)(tty, file, cmd, arg);
			else
				return -EINVAL;
	}
}
/*
 * tty_open and tty_release keep up the tty count that contains the
 * number of opens done on a tty. We cannot use the inode-count, as
 * different inodes might point to the same tty.
 *
 * Open-counting is needed for pty masters, as well as for keeping
 * track of serial lines: DTR is dropped when the last close happens.
 * (This is not done solely through tty->count, now.  - Ted 1/27/92)
 *
 * The termios state of a pty is reset on first open so that
 * settings don't persist across reuse.
 */
static int tty_open(struct inode * inode, struct file * filp)
{
	struct tty_struct *tty;
	int major, minor;
	int noctty, retval;

retry_open:
	minor = MINOR(inode->i_rdev);
	major = MAJOR(inode->i_rdev);
	noctty = filp->f_flags & O_NOCTTY;
	if (major == TTYAUX_MAJOR) {
		if (!minor) {
			major = TTY_MAJOR;
			minor = current->tty;
		}
		/* noctty = 1; */
	} else if (major == TTY_MAJOR) {
		if (!minor) {
			minor = fg_console + 1;
			noctty = 1;
		}
	} else {
		printk("Bad major #%d in tty_open\n", MAJOR(inode->i_rdev));
		return -ENODEV;
	}
	if (minor <= 0)
		return -ENXIO;
	if (IS_A_PTY_MASTER(minor))
		noctty = 1;
	filp->f_rdev = (major << 8) | minor;
	retval = init_dev(minor);
	if (retval)
		return retval;
	tty = tty_table[minor];
#ifdef TTY_DEBUG_HANGUP
	printk("opening tty%d...", tty->line);
#endif
	if (test_bit(TTY_EXCLUSIVE, &tty->flags) && !suser())
		return -EBUSY;

#if 0
	/* clean up the packet stuff. */
	/*
	 *  Why is this not done in init_dev?  Right here, if another 
	 * process opens up a tty in packet mode, all the packet 
	 * variables get cleared.  Come to think of it, is anything 
	 * using the packet mode at all???  - Ted, 1/27/93
	 *
	 * Not to worry, a pty master can only be opened once.
	 * And rlogind and telnetd both use packet mode.  -- jrs
	 *
	 * Not needed.  These are cleared in initialize_tty_struct. -- jlc
	 */
	tty->ctrl_status = 0;
	tty->packet = 0;
#endif

	if (tty->open) {
		retval = tty->open(tty, filp);
	} else {
		retval = -ENODEV;
	}
	if (retval) {
#ifdef TTY_DEBUG_HANGUP
		printk("error %d in opening tty%d...", retval, tty->line);
#endif

		release_dev(minor, filp);
		if (retval != -ERESTARTSYS)
			return retval;
		if (current->signal & ~current->blocked)
			return retval;
		schedule();
		goto retry_open;
	}
	if (!noctty &&
	    current->leader &&
	    current->tty<0 &&
	    tty->session==0) {
		current->tty = minor;
		tty->session = current->session;
		tty->pgrp = current->pgrp;
	}
	filp->f_rdev = MKDEV(TTY_MAJOR,minor); /* Set it to something normal */
	return 0;
}
/*
 * Even releasing the tty structures is a tricky business.. We have
 * to be very careful that the structures are all released at the
 * same time, as interrupts might otherwise get the wrong pointers.
 */
static void release_dev(int dev, struct file * filp)
{
	struct tty_struct *tty, *o_tty;
	struct termios *tp, *o_tp;
	struct task_struct **p;

	tty = tty_table[dev];
	tp = tty_termios[dev];
	o_tty = NULL;
	o_tp = NULL;
	if (!tty) {
		printk("release_dev: tty_table[%d] was NULL\n", dev);
		return;
	}
	if (!tp) {
		printk("release_dev: tty_termios[%d] was NULL\n", dev);
		return;
	}
#ifdef TTY_DEBUG_HANGUP
	printk("release_dev of tty%d (tty count=%d)...", dev, tty->count);
#endif
	if (IS_A_PTY(dev)) {
		o_tty = tty_table[PTY_OTHER(dev)];
		o_tp = tty_termios[PTY_OTHER(dev)];
		if (!o_tty) {
			printk("release_dev: pty pair(%d) was NULL\n", dev);
			return;
		}
		if (!o_tp) {
			printk("release_dev: pty pair(%d) termios was NULL\n", dev);
			return;
		}
		if (tty->link != o_tty || o_tty->link != tty) {
			printk("release_dev: bad pty pointers\n");
			return;
		}
	}
	tty->write_data_cnt = 0; /* Clear out pending trash */
	if (tty->close)
		tty->close(tty, filp);
	if (IS_A_PTY_MASTER(dev)) {
		if (--tty->link->count < 0) {
			printk("release_dev: bad tty slave count (dev = %d): %d\n",
			       dev, tty->count);
			tty->link->count = 0;
		}
	}
	if (--tty->count < 0) {
		printk("release_dev: bad tty_table[%d]->count: %d\n",
		       dev, tty->count);
		tty->count = 0;
	}
	if (tty->count)
		return;
	
#ifdef TTY_DEBUG_HANGUP
	printk("freeing tty structure...");
#endif

	/*
	 * Make sure there aren't any processes that still think this
	 * tty is their controlling tty.
	 */
	for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
		if ((*p) && (*p)->tty == tty->line)
		(*p)->tty = -1;
	}

	/*
	 * Shutdown the current line discipline, and reset it to
	 * N_TTY.
	 */
	if (ldiscs[tty->disc].close != NULL)
		ldiscs[tty->disc].close(tty);
	tty->disc = N_TTY;
	tty->termios->c_line = N_TTY;
	
	if (o_tty) {
		if (o_tty->count)
			return;
		else {
			tty_table[PTY_OTHER(dev)] = NULL;
			tty_termios[PTY_OTHER(dev)] = NULL;
		}
	}
	tty_table[dev] = NULL;
	if (IS_A_PTY(dev)) {
		tty_termios[dev] = NULL;
		kfree_s(tp, sizeof(struct termios));
	}
	if (tty == redirect || o_tty == redirect)
		redirect = NULL;
	free_page((unsigned long) tty);
	if (o_tty)
		free_page((unsigned long) o_tty);
	if (o_tp)
		kfree_s(o_tp, sizeof(struct termios));
}
Exemple #10
0
/*
 * This is so ripe with races that you should *really* not touch this
 * unless you know exactly what you are doing. All the changes have to be
 * made atomically, or there may be incorrect pointers all over the place.
 */
static int init_dev(int dev)
{
	struct tty_struct *tty, *o_tty;
	struct termios *tp, *o_tp, *ltp, *o_ltp;
	int retval;
	int o_dev;

	o_dev = PTY_OTHER(dev);
	tty = o_tty = NULL;
	tp = o_tp = NULL;
	ltp = o_ltp = NULL;
repeat:
	retval = -EAGAIN;
	if (IS_A_PTY_MASTER(dev) && tty_table[dev] && tty_table[dev]->count)
		goto end_init;
	retval = -ENOMEM;
	if (!tty_table[dev] && !tty) {
		if (!(tty = (struct tty_struct*) get_free_page(GFP_KERNEL)))
			goto end_init;
		initialize_tty_struct(dev, tty);
		goto repeat;
	}
	if (!tty_termios[dev] && !tp) {
		tp = (struct termios *) kmalloc(sizeof(struct termios),
						GFP_KERNEL);
		if (!tp)
			goto end_init;
		initialize_termios(dev, tp);
		goto repeat;
	}
	if (!termios_locked[dev] && !ltp) {
		ltp = (struct termios *) kmalloc(sizeof(struct termios),
						 GFP_KERNEL);
		if (!ltp)
			goto end_init;
		memset(ltp, 0, sizeof(struct termios));
		goto repeat;
	}
	if (IS_A_PTY(dev)) {
		if (!tty_table[o_dev] && !o_tty) {
			o_tty = (struct tty_struct *)
				get_free_page(GFP_KERNEL);
			if (!o_tty)
				goto end_init;
			initialize_tty_struct(o_dev, o_tty);
			goto repeat;
		}
		if (!tty_termios[o_dev] && !o_tp) {
			o_tp = (struct termios *)
				kmalloc(sizeof(struct termios), GFP_KERNEL);
			if (!o_tp)
				goto end_init;
			initialize_termios(o_dev, o_tp);
			goto repeat;
		}
		if (!termios_locked[o_dev] && !o_ltp) {
			o_ltp = (struct termios *)
				kmalloc(sizeof(struct termios), GFP_KERNEL);
			if (!o_ltp)
				goto end_init;
			memset(o_ltp, 0, sizeof(struct termios));
			goto repeat;
		}
		
	}
	/* Now we have allocated all the structures: update all the pointers.. */
	if (!tty_termios[dev]) {
		tty_termios[dev] = tp;
		tp = NULL;
	}
	if (!tty_table[dev]) {
		tty->termios = tty_termios[dev];
		tty_table[dev] = tty;
		tty = NULL;
	}
	if (!termios_locked[dev]) {
		termios_locked[dev] = ltp;
		ltp = NULL;
	}
	if (IS_A_PTY(dev)) {
		if (!tty_termios[o_dev]) {
			tty_termios[o_dev] = o_tp;
			o_tp = NULL;
		}
		if (!termios_locked[o_dev]) {
			termios_locked[o_dev] = o_ltp;
			o_ltp = NULL;
		}
		if (!tty_table[o_dev]) {
			o_tty->termios = tty_termios[o_dev];
			tty_table[o_dev] = o_tty;
			o_tty = NULL;
		}
		tty_table[dev]->link = tty_table[o_dev];
		tty_table[o_dev]->link = tty_table[dev];
	}
	tty_table[dev]->count++;
	if (IS_A_PTY_MASTER(dev))
		tty_table[o_dev]->count++;
	retval = 0;
end_init:
	if (tty)
		free_page((unsigned long) tty);
	if (o_tty)
		free_page((unsigned long) o_tty);
	if (tp)
		kfree_s(tp, sizeof(struct termios));
	if (o_tp)
		kfree_s(o_tp, sizeof(struct termios));
	if (ltp)
		kfree_s(ltp, sizeof(struct termios));
	if (o_ltp)
		kfree_s(o_ltp, sizeof(struct termios));
	return retval;
}
int tty_ioctl(struct inode * inode, struct file * file,
	unsigned int cmd, unsigned long arg)
{
	struct tty_struct * tty;
	struct tty_struct * other_tty;
	struct tty_struct * termios_tty;
	pid_t pgrp;
	int dev;
	int termios_dev;
	int retval;

	if (MAJOR(file->f_rdev) != TTY_MAJOR) {
		printk("tty_ioctl: tty pseudo-major != TTY_MAJOR\n");
		return -EINVAL;
	}
	dev = MINOR(file->f_rdev);
	tty = TTY_TABLE(dev);
	if (!tty)
		return -EINVAL;
	if (IS_A_PTY(dev))
		other_tty = tty_table[PTY_OTHER(dev)];
	else
		other_tty = NULL;
	if (IS_A_PTY_MASTER(dev)) {
		termios_tty = other_tty;
		termios_dev = PTY_OTHER(dev);
	} else {
		termios_tty = tty;
		termios_dev = dev;
	}
	switch (cmd) {
		case TCGETS:
			retval = verify_area(VERIFY_WRITE, (void *) arg,
					     sizeof (struct termios));
			if (retval)
				return retval;
			memcpy_tofs((struct termios *) arg,
				    termios_tty->termios,
				    sizeof (struct termios));
			return 0;
		case TCSETSF:
		case TCSETSW:
		case TCSETS:
			retval = check_change(termios_tty, termios_dev);
			if (retval)
				return retval;
			if (cmd == TCSETSF || cmd == TCSETSW) {
				if (cmd == TCSETSF)
					flush_input(termios_tty);
				wait_until_sent(termios_tty, 0);
			}
			return set_termios(termios_tty, (struct termios *) arg,
					   termios_dev);
		case TCGETA:
			return get_termio(termios_tty,(struct termio *) arg);
		case TCSETAF:
		case TCSETAW:
		case TCSETA:
			retval = check_change(termios_tty, termios_dev);
			if (retval)
				return retval;
			if (cmd == TCSETAF || cmd == TCSETAW) {
				if (cmd == TCSETAF)
					flush_input(termios_tty);
				wait_until_sent(termios_tty, 0);
			}
			return set_termio(termios_tty, (struct termio *) arg,
					  termios_dev);
		case TCXONC:
			retval = check_change(tty, dev);
			if (retval)
				return retval;
			switch (arg) {
			case TCOOFF:
				stop_tty(tty);
				break;
			case TCOON:
				start_tty(tty);
				break;
			case TCIOFF:
				if (STOP_CHAR(tty) != __DISABLED_CHAR)
					put_tty_queue(STOP_CHAR(tty),
						      &tty->write_q);
				break;
			case TCION:
				if (START_CHAR(tty) != __DISABLED_CHAR)
					put_tty_queue(START_CHAR(tty),
						      &tty->write_q);
				break;
			default:
				return -EINVAL;
			}
			return 0;
		case TCFLSH:
			retval = check_change(tty, dev);
			if (retval)
				return retval;
			switch (arg) {
			case TCIFLUSH:
				flush_input(tty);
				break;
			case TCIOFLUSH:
				flush_input(tty);
				/* fall through */
			case TCOFLUSH:
				flush_output(tty);
				break;
			default:
				return -EINVAL;
			}
			return 0;
		case TIOCEXCL:
			set_bit(TTY_EXCLUSIVE, &tty->flags);
			return 0;
		case TIOCNXCL:
			clear_bit(TTY_EXCLUSIVE, &tty->flags);
			return 0;
		case TIOCSCTTY:
			if (current->leader &&
			    (current->session == tty->session))
				return 0;
			/*
			 * The process must be a session leader and
			 * not have a controlling tty already.
			 */
			if (!current->leader || (current->tty >= 0))
				return -EPERM;
			if (tty->session > 0) {
				/*
				 * This tty is already the controlling
				 * tty for another session group!
				 */
				if ((arg == 1) && suser()) {
					/*
					 * Steal it away
					 */
					struct task_struct *p;

					for_each_task(p)
						if (p->tty == dev)
							p->tty = -1;
				} else
					return -EPERM;
			}
			current->tty = dev;
			tty->session = current->session;
			tty->pgrp = current->pgrp;
			return 0;
		case TIOCGPGRP:
			retval = verify_area(VERIFY_WRITE, (void *) arg,
					     sizeof (pid_t));
			if (retval)
				return retval;
			if (current->tty != termios_dev)
				return -ENOTTY;
			put_fs_long(termios_tty->pgrp, (pid_t *) arg);
			return 0;
		case TIOCSPGRP:
			retval = check_change(termios_tty, termios_dev);
			if (retval)
				return retval;
			if ((current->tty < 0) ||
			    (current->tty != termios_dev) ||
			    (termios_tty->session != current->session))
				return -ENOTTY;
			pgrp = get_fs_long((pid_t *) arg);
			if (pgrp < 0)
				return -EINVAL;
			if (session_of_pgrp(pgrp) != current->session)
				return -EPERM;
			termios_tty->pgrp = pgrp;
			return 0;
		case TIOCOUTQ:
			retval = verify_area(VERIFY_WRITE, (void *) arg,
					     sizeof (unsigned long));
			if (retval)
				return retval;
			put_fs_long(CHARS(&tty->write_q),
				    (unsigned long *) arg);
			return 0;
		case TIOCINQ:
			retval = verify_area(VERIFY_WRITE, (void *) arg,
					     sizeof (unsigned long));
			if (retval)
				return retval;
			if (L_ICANON(tty))
				put_fs_long(inq_canon(tty),
					(unsigned long *) arg);
			else
				put_fs_long(CHARS(&tty->secondary),
					(unsigned long *) arg);
			return 0;
		case TIOCSTI:
			if ((current->tty != dev) && !suser())
				return -EPERM;
			retval = verify_area(VERIFY_READ, (void *) arg, 1);
			if (retval)
				return retval;
			put_tty_queue(get_fs_byte((char *) arg), &tty->read_q);
			TTY_READ_FLUSH(tty);
			return 0;
		case TIOCGWINSZ:
			retval = verify_area(VERIFY_WRITE, (void *) arg,
					     sizeof (struct winsize));
			if (retval)
				return retval;
			memcpy_tofs((struct winsize *) arg, &tty->winsize,
				    sizeof (struct winsize));
			return 0;
		case TIOCSWINSZ:
			if (IS_A_PTY_MASTER(dev))
				set_window_size(other_tty,(struct winsize *) arg);
			return set_window_size(tty,(struct winsize *) arg);
		case TIOCLINUX:
			switch (get_fs_byte((char *)arg))
			{
				case 0: 
					return do_screendump(arg);
				case 1: 
					return do_get_ps_info(arg);
#ifdef CONFIG_SELECTION
				case 2:
					return set_selection(arg);
				case 3:
					return paste_selection(tty);
				case 4:
					unblank_screen();
					return 0;
#endif /* CONFIG_SELECTION */
				default: 
					return -EINVAL;
			}
		case TIOCCONS:
			if (IS_A_CONSOLE(dev)) {
				if (!suser())
					return -EPERM;
				redirect = NULL;
				return 0;
			}
			if (redirect)
				return -EBUSY;
			if (!suser())
				return -EPERM;
			if (IS_A_PTY_MASTER(dev))
				redirect = other_tty;
			else if (IS_A_PTY_SLAVE(dev))
				redirect = tty;
			else
				return -ENOTTY;
			return 0;
		case FIONBIO:
			arg = get_fs_long((unsigned long *) arg);
			if (arg)
				file->f_flags |= O_NONBLOCK;
			else
				file->f_flags &= ~O_NONBLOCK;
			return 0;
		case TIOCNOTTY:
			if (current->tty != dev)
				return -ENOTTY;
			if (current->leader)
				disassociate_ctty(0);
			current->tty = -1;
			return 0;
		case TIOCGETD:
			retval = verify_area(VERIFY_WRITE, (void *) arg,
					     sizeof (unsigned long));
			if (retval)
				return retval;
			put_fs_long(tty->disc, (unsigned long *) arg);
			return 0;
		case TIOCSETD:
			retval = check_change(tty, dev);
			if (retval)
				return retval;
			arg = get_fs_long((unsigned long *) arg);
			return tty_set_ldisc(tty, arg);
		case TIOCGLCKTRMIOS:
			arg = get_fs_long((unsigned long *) arg);
			retval = verify_area(VERIFY_WRITE, (void *) arg,
					     sizeof (struct termios));
			if (retval)
				return retval;
			memcpy_tofs((struct termios *) arg,
				    &termios_locked[termios_dev],
				    sizeof (struct termios));
			return 0;
		case TIOCSLCKTRMIOS:
			if (!suser())
				return -EPERM;
			arg = get_fs_long((unsigned long *) arg);
			memcpy_fromfs(&termios_locked[termios_dev],
				      (struct termios *) arg,
				      sizeof (struct termios));
			return 0;
		case TIOCPKT:
			if (!IS_A_PTY_MASTER(dev))
				return -ENOTTY;
			retval = verify_area(VERIFY_READ, (void *) arg,
					     sizeof (unsigned long));
			if (retval)
				return retval;
			if (get_fs_long(arg)) {
				if (!tty->packet) {
					tty->packet = 1;
					tty->link->ctrl_status = 0;
				}
			} else
				tty->packet = 0;
			return 0;
		case TCSBRK: case TCSBRKP:
			retval = check_change(tty, dev);
			if (retval)
				return retval;
			wait_until_sent(tty, 0);
			if (!tty->ioctl)
				return 0;
			tty->ioctl(tty, file, cmd, arg);
			return 0;
		default:
			if (tty->ioctl) {
				retval = (tty->ioctl)(tty, file, cmd, arg);
				if (retval != -EINVAL)
					return retval;
			}
			if (ldiscs[tty->disc].ioctl) {
				retval = (ldiscs[tty->disc].ioctl)
					(tty, file, cmd, arg);
				return retval;
			}
			return -EINVAL;
	}