658 lines
16 KiB
C
658 lines
16 KiB
C
|
/*
|
||
|
* linux/kernel/drivers/char/tty_ioctl.c
|
||
|
*
|
||
|
* Copyright (C) 1991, 1992, 1993, 1994 Linus Torvalds
|
||
|
*
|
||
|
* Modified by Fred N. van Kempen, 01/29/93, to add line disciplines
|
||
|
* which can be dynamically activated and de-activated by the line
|
||
|
* discipline handling modules (like SLIP).
|
||
|
*/
|
||
|
|
||
|
#include <linux/types.h>
|
||
|
#include <linux/termios.h>
|
||
|
#include <linux/errno.h>
|
||
|
#include <linux/sched.h>
|
||
|
#include <linux/config.h>
|
||
|
#include <linux/kernel.h>
|
||
|
#include <linux/major.h>
|
||
|
#include <linux/tty.h>
|
||
|
#include <linux/fcntl.h>
|
||
|
#include <linux/string.h>
|
||
|
|
||
|
#include <asm/io.h>
|
||
|
#include <asm/bitops.h>
|
||
|
#include <asm/segment.h>
|
||
|
#include <asm/system.h>
|
||
|
|
||
|
#undef DEBUG
|
||
|
#ifdef DEBUG
|
||
|
# define PRINTK(x) printk (x)
|
||
|
#else
|
||
|
# define PRINTK(x) /**/
|
||
|
#endif
|
||
|
|
||
|
extern int session_of_pgrp(int pgrp);
|
||
|
extern int do_screendump(int arg);
|
||
|
extern int kill_pg(int pgrp, int sig, int priv);
|
||
|
|
||
|
#ifdef CONFIG_SELECTION
|
||
|
extern int set_selection(const int arg);
|
||
|
extern int paste_selection(struct tty_struct *tty);
|
||
|
#endif /* CONFIG_SELECTION */
|
||
|
|
||
|
static int tty_set_ldisc(struct tty_struct *tty, int ldisc);
|
||
|
|
||
|
void flush_input(struct tty_struct * tty)
|
||
|
{
|
||
|
cli();
|
||
|
tty->read_q.head = tty->read_q.tail = 0;
|
||
|
tty->secondary.head = tty->secondary.tail = 0;
|
||
|
tty->canon_head = tty->canon_data = tty->erasing = 0;
|
||
|
memset(&tty->readq_flags, 0, sizeof tty->readq_flags);
|
||
|
memset(&tty->secondary_flags, 0, sizeof tty->secondary_flags);
|
||
|
sti();
|
||
|
if (!tty->link)
|
||
|
return;
|
||
|
/* No cli() since ptys don't use interrupts. */
|
||
|
tty->link->write_q.head = tty->link->write_q.tail = 0;
|
||
|
wake_up_interruptible(&tty->link->write_q.proc_list);
|
||
|
if (tty->link->packet) {
|
||
|
tty->ctrl_status |= TIOCPKT_FLUSHREAD;
|
||
|
wake_up_interruptible(&tty->link->secondary.proc_list);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void flush_output(struct tty_struct * tty)
|
||
|
{
|
||
|
cli();
|
||
|
tty->write_q.head = tty->write_q.tail = 0;
|
||
|
sti();
|
||
|
wake_up_interruptible(&tty->write_q.proc_list);
|
||
|
if (!tty->link)
|
||
|
return;
|
||
|
/* No cli() since ptys don't use interrupts. */
|
||
|
tty->link->read_q.head = tty->link->read_q.tail = 0;
|
||
|
tty->link->secondary.head = tty->link->secondary.tail = 0;
|
||
|
tty->link->canon_head = tty->link->canon_data = tty->link->erasing = 0;
|
||
|
memset(&tty->link->readq_flags, 0, sizeof tty->readq_flags);
|
||
|
memset(&tty->link->secondary_flags, 0, sizeof tty->secondary_flags);
|
||
|
if (tty->link->packet) {
|
||
|
tty->ctrl_status |= TIOCPKT_FLUSHWRITE;
|
||
|
wake_up_interruptible(&tty->link->secondary.proc_list);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void wait_until_sent(struct tty_struct * tty)
|
||
|
{
|
||
|
struct wait_queue wait = { current, NULL };
|
||
|
|
||
|
TTY_WRITE_FLUSH(tty);
|
||
|
if (EMPTY(&tty->write_q))
|
||
|
return;
|
||
|
add_wait_queue(&tty->write_q.proc_list, &wait);
|
||
|
current->counter = 0; /* make us low-priority */
|
||
|
while (1) {
|
||
|
current->state = TASK_INTERRUPTIBLE;
|
||
|
if (current->signal & ~current->blocked)
|
||
|
break;
|
||
|
TTY_WRITE_FLUSH(tty);
|
||
|
if (EMPTY(&tty->write_q))
|
||
|
break;
|
||
|
schedule();
|
||
|
}
|
||
|
current->state = TASK_RUNNING;
|
||
|
remove_wait_queue(&tty->write_q.proc_list, &wait);
|
||
|
}
|
||
|
|
||
|
static int do_get_ps_info(int arg)
|
||
|
{
|
||
|
struct tstruct {
|
||
|
int flag;
|
||
|
int present[NR_TASKS];
|
||
|
struct task_struct tasks[NR_TASKS];
|
||
|
};
|
||
|
struct tstruct *ts = (struct tstruct *)arg;
|
||
|
struct task_struct **p;
|
||
|
char *c, *d;
|
||
|
int i, n = 0;
|
||
|
|
||
|
i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct tstruct));
|
||
|
if (i)
|
||
|
return i;
|
||
|
for (p = &FIRST_TASK ; p <= &LAST_TASK ; p++, n++)
|
||
|
if (*p)
|
||
|
{
|
||
|
c = (char *)(*p);
|
||
|
d = (char *)(ts->tasks+n);
|
||
|
for (i=0 ; i<sizeof(struct task_struct) ; i++)
|
||
|
put_fs_byte(*c++, d++);
|
||
|
put_fs_long(1, (unsigned long *)(ts->present+n));
|
||
|
}
|
||
|
else
|
||
|
put_fs_long(0, (unsigned long *)(ts->present+n));
|
||
|
return(0);
|
||
|
}
|
||
|
|
||
|
static void unset_locked_termios(struct termios *termios,
|
||
|
struct termios *old,
|
||
|
struct termios *locked)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
#define NOSET_MASK(x,y,z) (x = ((x) & ~(z)) | ((y) & (z)))
|
||
|
|
||
|
if (!locked) {
|
||
|
printk("Warning?!? termios_locked is NULL.\n");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
NOSET_MASK(termios->c_iflag, old->c_iflag, locked->c_iflag);
|
||
|
NOSET_MASK(termios->c_oflag, old->c_oflag, locked->c_oflag);
|
||
|
NOSET_MASK(termios->c_cflag, old->c_cflag, locked->c_cflag);
|
||
|
NOSET_MASK(termios->c_lflag, old->c_lflag, locked->c_lflag);
|
||
|
termios->c_line = locked->c_line ? old->c_line : termios->c_line;
|
||
|
for (i=0; i < NCCS; i++)
|
||
|
termios->c_cc[i] = locked->c_cc[i] ?
|
||
|
old->c_cc[i] : termios->c_cc[i];
|
||
|
}
|
||
|
|
||
|
int check_change(struct tty_struct * tty, int channel)
|
||
|
{
|
||
|
/* 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)
|
||
|
return 0;
|
||
|
if (tty->pgrp <= 0) {
|
||
|
printk("check_change: tty->pgrp <= 0!\n");
|
||
|
return 0;
|
||
|
}
|
||
|
if (current->pgrp == tty->pgrp)
|
||
|
return 0;
|
||
|
if (is_ignored(SIGTTOU))
|
||
|
return 0;
|
||
|
if (is_orphaned_pgrp(current->pgrp))
|
||
|
return -EIO;
|
||
|
(void) kill_pg(current->pgrp,SIGTTOU,1);
|
||
|
return -ERESTARTSYS;
|
||
|
}
|
||
|
|
||
|
static int set_termios_2(struct tty_struct * tty, struct termios * termios)
|
||
|
{
|
||
|
struct termios old_termios = *tty->termios;
|
||
|
int canon_change;
|
||
|
|
||
|
canon_change = (old_termios.c_lflag ^ termios->c_lflag) & ICANON;
|
||
|
cli();
|
||
|
*tty->termios = *termios;
|
||
|
if (canon_change) {
|
||
|
memset(&tty->secondary_flags, 0, sizeof tty->secondary_flags);
|
||
|
tty->canon_head = tty->secondary.tail;
|
||
|
tty->canon_data = 0;
|
||
|
tty->erasing = 0;
|
||
|
}
|
||
|
sti();
|
||
|
if (canon_change && !L_ICANON(tty) && !EMPTY(&tty->secondary))
|
||
|
/* Get characters left over from canonical mode. */
|
||
|
wake_up_interruptible(&tty->secondary.proc_list);
|
||
|
|
||
|
/* see if packet mode change of state */
|
||
|
|
||
|
if (tty->link && tty->link->packet) {
|
||
|
int old_flow = ((old_termios.c_iflag & IXON) &&
|
||
|
(old_termios.c_cc[VSTOP] == '\023') &&
|
||
|
(old_termios.c_cc[VSTART] == '\021'));
|
||
|
int new_flow = (I_IXON(tty) &&
|
||
|
STOP_CHAR(tty) == '\023' &&
|
||
|
START_CHAR(tty) == '\021');
|
||
|
if (old_flow != new_flow) {
|
||
|
tty->ctrl_status &= ~(TIOCPKT_DOSTOP | TIOCPKT_NOSTOP);
|
||
|
if (new_flow)
|
||
|
tty->ctrl_status |= TIOCPKT_DOSTOP;
|
||
|
else
|
||
|
tty->ctrl_status |= TIOCPKT_NOSTOP;
|
||
|
wake_up_interruptible(&tty->link->secondary.proc_list);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
unset_locked_termios(tty->termios, &old_termios,
|
||
|
termios_locked[tty->line]);
|
||
|
|
||
|
if (tty->set_termios)
|
||
|
(*tty->set_termios)(tty, &old_termios);
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int set_termios(struct tty_struct * tty, struct termios * termios,
|
||
|
int channel)
|
||
|
{
|
||
|
struct termios tmp_termios;
|
||
|
|
||
|
memcpy_fromfs(&tmp_termios, termios, sizeof (struct termios));
|
||
|
return set_termios_2(tty, &tmp_termios);
|
||
|
}
|
||
|
|
||
|
static int get_termio(struct tty_struct * tty, struct termio * termio)
|
||
|
{
|
||
|
int i;
|
||
|
struct termio tmp_termio;
|
||
|
|
||
|
i = verify_area(VERIFY_WRITE, termio, sizeof (struct termio));
|
||
|
if (i)
|
||
|
return i;
|
||
|
tmp_termio.c_iflag = tty->termios->c_iflag;
|
||
|
tmp_termio.c_oflag = tty->termios->c_oflag;
|
||
|
tmp_termio.c_cflag = tty->termios->c_cflag;
|
||
|
tmp_termio.c_lflag = tty->termios->c_lflag;
|
||
|
tmp_termio.c_line = tty->termios->c_line;
|
||
|
for(i=0 ; i < NCC ; i++)
|
||
|
tmp_termio.c_cc[i] = tty->termios->c_cc[i];
|
||
|
memcpy_tofs(termio, &tmp_termio, sizeof (struct termio));
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int set_termio(struct tty_struct * tty, struct termio * termio,
|
||
|
int channel)
|
||
|
{
|
||
|
struct termio tmp_termio;
|
||
|
struct termios tmp_termios;
|
||
|
|
||
|
tmp_termios = *tty->termios;
|
||
|
memcpy_fromfs(&tmp_termio, termio, sizeof (struct termio));
|
||
|
|
||
|
#define SET_LOW_BITS(x,y) ((x) = (0xffff0000 & (x)) | (y))
|
||
|
|
||
|
SET_LOW_BITS(tmp_termios.c_iflag, tmp_termio.c_iflag);
|
||
|
SET_LOW_BITS(tmp_termios.c_oflag, tmp_termio.c_oflag);
|
||
|
SET_LOW_BITS(tmp_termios.c_cflag, tmp_termio.c_cflag);
|
||
|
SET_LOW_BITS(tmp_termios.c_lflag, tmp_termio.c_lflag);
|
||
|
memcpy(&tmp_termios.c_cc, &tmp_termio.c_cc, NCC);
|
||
|
|
||
|
#undef SET_LOW_BITS
|
||
|
|
||
|
return set_termios_2(tty, &tmp_termios);
|
||
|
}
|
||
|
|
||
|
static int set_window_size(struct tty_struct * tty, struct winsize * ws)
|
||
|
{
|
||
|
struct winsize tmp_ws;
|
||
|
|
||
|
memcpy_fromfs(&tmp_ws, ws, sizeof (struct winsize));
|
||
|
if (memcmp(&tmp_ws, &tty->winsize, sizeof (struct winsize)) &&
|
||
|
tty->pgrp > 0)
|
||
|
kill_pg(tty->pgrp, SIGWINCH, 1);
|
||
|
tty->winsize = tmp_ws;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* Set the discipline of a tty line. */
|
||
|
static int tty_set_ldisc(struct tty_struct *tty, int ldisc)
|
||
|
{
|
||
|
if ((ldisc < N_TTY) || (ldisc >= NR_LDISCS) ||
|
||
|
!(ldiscs[ldisc].flags & LDISC_FLAG_DEFINED))
|
||
|
return -EINVAL;
|
||
|
|
||
|
if (tty->disc == ldisc)
|
||
|
return 0; /* We are already in the desired discipline */
|
||
|
|
||
|
/* Shutdown the current discipline. */
|
||
|
wait_until_sent(tty);
|
||
|
flush_input(tty);
|
||
|
if (ldiscs[tty->disc].close)
|
||
|
ldiscs[tty->disc].close(tty);
|
||
|
|
||
|
/* Now set up the new line discipline. */
|
||
|
tty->disc = ldisc;
|
||
|
tty->termios->c_line = ldisc;
|
||
|
if (ldiscs[tty->disc].open)
|
||
|
return(ldiscs[tty->disc].open(tty));
|
||
|
else
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static unsigned long inq_canon(struct tty_struct * tty)
|
||
|
{
|
||
|
int nr, head, tail;
|
||
|
|
||
|
if (!tty->canon_data)
|
||
|
return 0;
|
||
|
head = tty->canon_head;
|
||
|
tail = tty->secondary.tail;
|
||
|
nr = (head - tail) & (TTY_BUF_SIZE-1);
|
||
|
/* Skip EOF-chars.. */
|
||
|
while (head != tail) {
|
||
|
if (test_bit(tail, &tty->secondary_flags) &&
|
||
|
tty->secondary.buf[tail] == __DISABLED_CHAR)
|
||
|
nr--;
|
||
|
INC(tail);
|
||
|
}
|
||
|
return nr;
|
||
|
}
|
||
|
|
||
|
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);
|
||
|
}
|
||
|
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);
|
||
|
}
|
||
|
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;
|
||
|
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 -EACCES;
|
||
|
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);
|
||
|
#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 -EINVAL;
|
||
|
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 (MINOR(file->f_rdev) != current->tty)
|
||
|
return -EINVAL;
|
||
|
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 -EINVAL;
|
||
|
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->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);
|
||
|
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;
|
||
|
}
|
||
|
}
|