| /* |
| * linux/kernel/chr_drv/tty_ioctl.c |
| * |
| * (C) 1991 Linus Torvalds |
| */ |
| |
| #include <errno.h> |
| #include <termios.h> |
| #include <sys/types.h> |
| |
| #include <linux/sched.h> |
| #include <linux/kernel.h> |
| #include <linux/tty.h> |
| |
| #include <asm/io.h> |
| #include <asm/segment.h> |
| #include <asm/system.h> |
| |
| extern int session_of_pgrp(int pgrp); |
| extern int do_screendump(int arg); |
| extern int kill_pg(int pgrp, int sig, int priv); |
| extern int vt_ioctl(struct tty_struct *tty, int dev, int cmd, int arg); |
| |
| static void flush(struct tty_queue * queue) |
| { |
| if (queue) { |
| cli(); |
| queue->head = queue->tail; |
| sti(); |
| wake_up(&queue->proc_list); |
| } |
| } |
| |
| void flush_input(struct tty_struct * tty) |
| { |
| if (tty->read_q) { |
| flush(tty->read_q); |
| wake_up(&tty->read_q->proc_list); |
| } |
| if (tty->secondary) { |
| flush(tty->secondary); |
| tty->secondary->data = 0; |
| } |
| if ((tty = tty->link) && tty->write_q) { |
| flush(tty->write_q); |
| wake_up(&tty->write_q->proc_list); |
| } |
| } |
| |
| void flush_output(struct tty_struct * tty) |
| { |
| if (tty->write_q) { |
| flush(tty->write_q); |
| wake_up(&tty->write_q->proc_list); |
| } |
| if (tty = tty->link) { |
| if (tty->read_q) { |
| flush(tty->read_q); |
| wake_up(&tty->read_q->proc_list); |
| } |
| if (tty->secondary) { |
| flush(tty->secondary); |
| tty->secondary->data = 0; |
| } |
| } |
| } |
| |
| static void wait_until_sent(struct tty_struct * tty) |
| { |
| while (!(current->signal & ~current->blocked) && !EMPTY(tty->write_q)) { |
| TTY_WRITE_FLUSH(tty); |
| current->counter = 0; |
| cli(); |
| if (EMPTY(tty->write_q)) |
| break; |
| else |
| interruptible_sleep_on(&tty->write_q->proc_list); |
| sti(); |
| } |
| sti(); |
| } |
| |
| 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; |
| |
| verify_area((void *)arg, sizeof(struct tstruct)); |
| |
| 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 int get_termios(struct tty_struct * tty, struct termios * termios) |
| { |
| int i; |
| |
| verify_area(termios, sizeof (*termios)); |
| for (i=0 ; i< (sizeof (*termios)) ; i++) |
| put_fs_byte( ((char *)&tty->termios)[i] , i+(char *)termios ); |
| return 0; |
| } |
| |
| static int set_termios(struct tty_struct * tty, struct termios * termios, |
| int channel) |
| { |
| int i; |
| |
| /* 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)) |
| return tty_signal(SIGTTOU, tty); |
| } |
| for (i=0 ; i< (sizeof (*termios)) ; i++) |
| ((char *)&tty->termios)[i]=get_fs_byte(i+(char *)termios); |
| if (IS_A_SERIAL(channel)) |
| change_speed(channel-64); |
| return 0; |
| } |
| |
| static int get_termio(struct tty_struct * tty, struct termio * termio) |
| { |
| int i; |
| struct termio tmp_termio; |
| |
| verify_area(termio, sizeof (*termio)); |
| 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]; |
| for (i=0 ; i< (sizeof (*termio)) ; i++) |
| put_fs_byte( ((char *)&tmp_termio)[i] , i+(char *)termio ); |
| return 0; |
| } |
| |
| /* |
| * This only works as the 386 is low-byte-first |
| */ |
| static int set_termio(struct tty_struct * tty, struct termio * termio, |
| int channel) |
| { |
| int i; |
| struct termio tmp_termio; |
| |
| if ((current->tty == channel) && |
| (tty->pgrp > 0) && |
| (tty->pgrp != current->pgrp)) { |
| if (is_orphaned_pgrp(current->pgrp)) |
| return -EIO; |
| if (!is_ignored(SIGTTOU)) |
| return tty_signal(SIGTTOU, tty); |
| } |
| for (i=0 ; i< (sizeof (*termio)) ; i++) |
| ((char *)&tmp_termio)[i]=get_fs_byte(i+(char *)termio); |
| *(unsigned short *)&tty->termios.c_iflag = tmp_termio.c_iflag; |
| *(unsigned short *)&tty->termios.c_oflag = tmp_termio.c_oflag; |
| *(unsigned short *)&tty->termios.c_cflag = tmp_termio.c_cflag; |
| *(unsigned short *)&tty->termios.c_lflag = tmp_termio.c_lflag; |
| tty->termios.c_line = tmp_termio.c_line; |
| for(i=0 ; i < NCC ; i++) |
| tty->termios.c_cc[i] = tmp_termio.c_cc[i]; |
| if (IS_A_SERIAL(channel)) |
| change_speed(channel-64); |
| return 0; |
| } |
| |
| static int set_window_size(struct tty_struct * tty, struct winsize * ws) |
| { |
| int i,changed; |
| char c, * tmp; |
| |
| if (!ws) |
| return -EINVAL; |
| tmp = (char *) &tty->winsize; |
| changed = 0; |
| for (i = 0; i < sizeof (*ws) ; i++,tmp++) { |
| c = get_fs_byte(i + (char *) ws); |
| if (c == *tmp) |
| continue; |
| changed = 1; |
| *tmp = c; |
| } |
| if (changed) |
| kill_pg(tty->pgrp, SIGWINCH, 1); |
| return 0; |
| } |
| |
| static int get_window_size(struct tty_struct * tty, struct winsize * ws) |
| { |
| int i; |
| char * tmp; |
| |
| if (!ws) |
| return -EINVAL; |
| verify_area(ws, sizeof (*ws)); |
| tmp = (char *) ws; |
| for (i = 0; i < sizeof (struct winsize) ; i++,tmp++) |
| put_fs_byte(((char *) &tty->winsize)[i], tmp); |
| return 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(inode->i_rdev) == 5) { |
| dev = current->tty; |
| if (dev<0) |
| return -EINVAL; |
| } else |
| dev = MINOR(inode->i_rdev); |
| tty = tty_table + (dev ? ((dev < 64)? dev-1:dev) : fg_console); |
| |
| if (IS_A_PTY(dev)) |
| other_tty = tty_table + PTY_OTHER(dev); |
| else |
| other_tty = NULL; |
| |
| if (!(tty->write_q && tty->read_q && tty->secondary && tty->write)) |
| return -EINVAL; |
| 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 TCSBRK: |
| if (!IS_A_SERIAL(dev)) |
| return -EINVAL; |
| wait_until_sent(tty); |
| if (!arg) |
| send_break(dev-64); |
| return 0; |
| 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)) |
| PUTCH(STOP_CHAR(tty),tty->write_q); |
| return 0; |
| case TCION: |
| if (START_CHAR(tty)) |
| PUTCH(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 TIOCMGET: |
| return -EINVAL; /* not implemented */ |
| case TIOCMBIS: |
| return -EINVAL; /* not implemented */ |
| case TIOCMBIC: |
| return -EINVAL; /* not implemented */ |
| case TIOCMSET: |
| return -EINVAL; /* not implemented */ |
| 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 TIOCGSERIAL: |
| if (!IS_A_SERIAL(dev)) |
| return -EINVAL; |
| verify_area((void *) arg,sizeof(struct serial_struct)); |
| return get_serial_info(dev-64,(struct serial_struct *) arg); |
| case TIOCSSERIAL: |
| if (!IS_A_SERIAL(dev)) |
| return -EINVAL; |
| return set_serial_info(dev-64,(struct serial_struct *) arg); |
| default: |
| return vt_ioctl(tty, dev, cmd, arg); |
| } |
| } |