root/kernel/chr_drv/tty_ioctl.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. flush
  2. flush_input
  3. flush_output
  4. wait_until_sent
  5. do_get_ps_info
  6. get_termios
  7. check_change
  8. set_termios
  9. get_termio
  10. set_termio
  11. set_window_size
  12. get_window_size
  13. tty_set_ldisc
  14. tty_ioctl

   1 /*
   2  *  linux/kernel/chr_drv/tty_ioctl.c
   3  *
   4  *  Copyright (C) 1991, 1992  Linus Torvalds
   5  *
   6  * Modified by Fred N. van Kempen, 01/29/93, to add line disciplines
   7  * which can be dynamically activated and de-activated by the line
   8  * discipline handling modules (like SLIP).
   9  */
  10 
  11 #include <linux/types.h>
  12 #include <linux/termios.h>
  13 #include <linux/errno.h>
  14 #include <linux/sched.h>
  15 #include <linux/kernel.h>
  16 #include <linux/tty.h>
  17 #include <linux/fcntl.h>
  18 
  19 #include <asm/io.h>
  20 #include <asm/segment.h>
  21 #include <asm/system.h>
  22 
  23 #undef  DEBUG
  24 #ifdef DEBUG
  25 # define        PRINTK(x)       printk (x)
  26 #else
  27 # define        PRINTK(x)       /**/
  28 #endif
  29 
  30 extern int session_of_pgrp(int pgrp);
  31 extern int do_screendump(int arg);
  32 extern int kill_pg(int pgrp, int sig, int priv);
  33 
  34 static void flush(struct tty_queue * queue)
     /* [previous][next][first][last][top][bottom][index][help] */
  35 {
  36         if (queue) {
  37                 cli();
  38                 queue->head = queue->tail;
  39                 sti();
  40                 wake_up_interruptible(&queue->proc_list);
  41         }
  42 }
  43 
  44 void flush_input(struct tty_struct * tty)
     /* [previous][next][first][last][top][bottom][index][help] */
  45 {
  46         tty->status_changed = 1;
  47         tty->ctrl_status |= TIOCPKT_FLUSHREAD;
  48         flush(&tty->read_q);
  49         wake_up_interruptible(&tty->read_q.proc_list);
  50         flush(&tty->secondary);
  51         tty->secondary.data = 0;
  52 
  53         if ((tty = tty->link) != NULL) {
  54                 flush(&tty->write_q);
  55                 wake_up_interruptible(&tty->write_q.proc_list);
  56         }
  57 }
  58 
  59 void flush_output(struct tty_struct * tty)
     /* [previous][next][first][last][top][bottom][index][help] */
  60 {
  61         tty->status_changed = 1;
  62         tty->ctrl_status |= TIOCPKT_FLUSHWRITE;
  63         flush(&tty->write_q);
  64         wake_up_interruptible(&tty->write_q.proc_list);
  65         if ((tty = tty->link) != NULL) {
  66                 flush(&tty->read_q);
  67                 wake_up_interruptible(&tty->read_q.proc_list);
  68                 flush(&tty->secondary);
  69                 tty->secondary.data = 0;
  70         }
  71 }
  72 
  73 void wait_until_sent(struct tty_struct * tty)
     /* [previous][next][first][last][top][bottom][index][help] */
  74 {
  75         struct wait_queue wait = { current, NULL };
  76 
  77         TTY_WRITE_FLUSH(tty);
  78         if (EMPTY(&tty->write_q))
  79                 return;
  80         add_wait_queue(&tty->write_q.proc_list, &wait);
  81         current->counter = 0;   /* make us low-priority */
  82         while (1) {
  83                 current->state = TASK_INTERRUPTIBLE;
  84                 if (current->signal & ~current->blocked)
  85                         break;
  86                 TTY_WRITE_FLUSH(tty);
  87                 if (EMPTY(&tty->write_q))
  88                         break;
  89                 schedule();
  90         }
  91         current->state = TASK_RUNNING;
  92         remove_wait_queue(&tty->write_q.proc_list, &wait);
  93 }
  94 
  95 static int do_get_ps_info(int arg)
     /* [previous][next][first][last][top][bottom][index][help] */
  96 {
  97         struct tstruct {
  98                 int flag;
  99                 int present[NR_TASKS];
 100                 struct task_struct tasks[NR_TASKS];
 101         };
 102         struct tstruct *ts = (struct tstruct *)arg;
 103         struct task_struct **p;
 104         char *c, *d;
 105         int i, n = 0;
 106         
 107         i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct tstruct));
 108         if (i)
 109                 return i;
 110         for (p = &FIRST_TASK ; p <= &LAST_TASK ; p++, n++)
 111                 if (*p)
 112                 {
 113                         c = (char *)(*p);
 114                         d = (char *)(ts->tasks+n);
 115                         for (i=0 ; i<sizeof(struct task_struct) ; i++)
 116                                 put_fs_byte(*c++, d++);
 117                         put_fs_long(1, (unsigned long *)(ts->present+n));
 118                 }
 119                 else    
 120                         put_fs_long(0, (unsigned long *)(ts->present+n));
 121         return(0);                      
 122 }
 123 
 124 static int get_termios(struct tty_struct * tty, struct termios * termios)
     /* [previous][next][first][last][top][bottom][index][help] */
 125 {
 126         int i;
 127 
 128         i = verify_area(VERIFY_WRITE, termios, sizeof (*termios));
 129         if (i)
 130                 return i;
 131         for (i=0 ; i< (sizeof (*termios)) ; i++)
 132                 put_fs_byte( ((char *)tty->termios)[i] , i+(char *)termios );
 133         return 0;
 134 }
 135 
 136 static int check_change(struct tty_struct * tty, int channel)
     /* [previous][next][first][last][top][bottom][index][help] */
 137 {
 138         /* If we try to set the state of terminal and we're not in the
 139            foreground, send a SIGTTOU.  If the signal is blocked or
 140            ignored, go ahead and perform the operation.  POSIX 7.2) */
 141         if (current->tty != channel)
 142                 return 0;
 143         if (tty->pgrp <= 0 || tty->pgrp == current->pgrp)
 144                 return 0;
 145         if (is_orphaned_pgrp(current->pgrp))
 146                 return -EIO;
 147         if (is_ignored(SIGTTOU))
 148                 return 0;
 149         (void) kill_pg(current->pgrp,SIGTTOU,1);
 150         return -ERESTARTSYS;
 151 }
 152 
 153 static int set_termios(struct tty_struct * tty, struct termios * termios,
     /* [previous][next][first][last][top][bottom][index][help] */
 154                         int channel)
 155 {
 156         int i;
 157         struct termios old_termios = *tty->termios;
 158 
 159         i = check_change(tty, channel);
 160         if (i)
 161                 return i;
 162         for (i=0 ; i< (sizeof (*termios)) ; i++)
 163                 ((char *)tty->termios)[i]=get_fs_byte(i+(char *)termios);
 164 
 165         /* puting mpty's into echo mode is very bad, and I think under
 166            some situations can cause the kernel to do nothing but
 167            copy characters back and forth. -RAB */
 168         if (IS_A_PTY_MASTER(channel)) tty->termios->c_lflag &= ~ECHO;
 169 
 170         if (tty->set_termios)
 171                 (*tty->set_termios)(tty, &old_termios);
 172 
 173         return 0;
 174 }
 175 
 176 static int get_termio(struct tty_struct * tty, struct termio * termio)
     /* [previous][next][first][last][top][bottom][index][help] */
 177 {
 178         int i;
 179         struct termio tmp_termio;
 180 
 181         i = verify_area(VERIFY_WRITE, termio, sizeof (*termio));
 182         if (i)
 183                 return i;
 184         tmp_termio.c_iflag = tty->termios->c_iflag;
 185         tmp_termio.c_oflag = tty->termios->c_oflag;
 186         tmp_termio.c_cflag = tty->termios->c_cflag;
 187         tmp_termio.c_lflag = tty->termios->c_lflag;
 188         tmp_termio.c_line = tty->termios->c_line;
 189         for(i=0 ; i < NCC ; i++)
 190                 tmp_termio.c_cc[i] = tty->termios->c_cc[i];
 191         for (i=0 ; i< (sizeof (*termio)) ; i++)
 192                 put_fs_byte( ((char *)&tmp_termio)[i] , i+(char *)termio );
 193         return 0;
 194 }
 195 
 196 /*
 197  * This only works as the 386 is low-byte-first
 198  */
 199 static int set_termio(struct tty_struct * tty, struct termio * termio,
     /* [previous][next][first][last][top][bottom][index][help] */
 200                         int channel)
 201 {
 202         int i;
 203         struct termio tmp_termio;
 204         struct termios old_termios = *tty->termios;
 205 
 206         i = check_change(tty, channel);
 207         if (i)
 208                 return i;
 209         for (i=0 ; i< (sizeof (*termio)) ; i++)
 210                 ((char *)&tmp_termio)[i]=get_fs_byte(i+(char *)termio);
 211 
 212         /* take care of the packet stuff. */
 213         if ((tmp_termio.c_iflag & IXON) &&
 214             ~(tty->termios->c_iflag & IXON))
 215           {
 216              tty->status_changed = 1;
 217              tty->ctrl_status |= TIOCPKT_DOSTOP;
 218           }
 219 
 220         if (~(tmp_termio.c_iflag & IXON) &&
 221             (tty->termios->c_iflag & IXON))
 222           {
 223              tty->status_changed = 1;
 224              tty->ctrl_status |= TIOCPKT_NOSTOP;
 225           }
 226 
 227         *(unsigned short *)&tty->termios->c_iflag = tmp_termio.c_iflag;
 228         *(unsigned short *)&tty->termios->c_oflag = tmp_termio.c_oflag;
 229         *(unsigned short *)&tty->termios->c_cflag = tmp_termio.c_cflag;
 230         *(unsigned short *)&tty->termios->c_lflag = tmp_termio.c_lflag;
 231         tty->termios->c_line = tmp_termio.c_line;
 232         for(i=0 ; i < NCC ; i++)
 233                 tty->termios->c_cc[i] = tmp_termio.c_cc[i];
 234 
 235         if (tty->set_termios)
 236                 (*tty->set_termios)(tty, &old_termios);
 237 
 238         return 0;
 239 }
 240 
 241 static int set_window_size(struct tty_struct * tty, struct winsize * ws)
     /* [previous][next][first][last][top][bottom][index][help] */
 242 {
 243         int i,changed;
 244         char c, * tmp;
 245 
 246         if (!ws)
 247                 return -EINVAL;
 248         tmp = (char *) &tty->winsize;
 249         changed = 0;
 250         for (i = 0; i < sizeof (*ws) ; i++,tmp++) {
 251                 c = get_fs_byte(i + (char *) ws);
 252                 if (c == *tmp)
 253                         continue;
 254                 changed = 1;
 255                 *tmp = c;
 256         }
 257         if (changed)
 258                 kill_pg(tty->pgrp, SIGWINCH, 1);
 259         return 0;
 260 }
 261 
 262 static int get_window_size(struct tty_struct * tty, struct winsize * ws)
     /* [previous][next][first][last][top][bottom][index][help] */
 263 {
 264         int i;
 265         char * tmp;
 266 
 267         if (!ws)
 268                 return -EINVAL;
 269         i = verify_area(VERIFY_WRITE, ws, sizeof (*ws));
 270         if (i)
 271                 return i;
 272         tmp = (char *) ws;
 273         for (i = 0; i < sizeof (struct winsize) ; i++,tmp++)
 274                 put_fs_byte(((char *) &tty->winsize)[i], tmp);
 275         return 0;
 276 }
 277 
 278 /* Set the discipline of a tty line. */
 279 static int tty_set_ldisc(struct tty_struct *tty, int ldisc)
     /* [previous][next][first][last][top][bottom][index][help] */
 280 {
 281         if ((ldisc < N_TTY) || (ldisc >= NR_LDISCS) ||
 282             !(ldiscs[ldisc].flags & LDISC_FLAG_DEFINED))
 283                 return -EINVAL;
 284 
 285         if (tty->disc == ldisc)
 286                 return 0;       /* We are already in the desired discipline */
 287 
 288         /* Shutdown the current discipline. */
 289         wait_until_sent(tty);
 290         flush_input(tty);
 291         if (ldiscs[tty->disc].close)
 292                 ldiscs[tty->disc].close(tty);
 293 
 294         /* Now set up the new line discipline. */
 295         tty->disc = ldisc;
 296         if (ldiscs[tty->disc].open)
 297                 return(ldiscs[tty->disc].open(tty));
 298         else
 299                 return 0;
 300 }
 301 
 302 
 303 int tty_ioctl(struct inode * inode, struct file * file,
     /* [previous][next][first][last][top][bottom][index][help] */
 304         unsigned int cmd, unsigned long arg)
 305 {
 306         struct tty_struct * tty;
 307         struct tty_struct * other_tty;
 308         struct tty_struct * termios_tty;
 309         int pgrp;
 310         int dev;
 311         int termios_dev;
 312         int retval;
 313 
 314         if (MAJOR(file->f_rdev) != 4) {
 315                 printk("tty_ioctl: tty pseudo-major != 4\n");
 316                 return -EINVAL;
 317         }
 318         dev = MINOR(file->f_rdev);
 319         tty = TTY_TABLE(dev);
 320         if (!tty)
 321                 return -EINVAL;
 322         if (IS_A_PTY(dev))
 323                 other_tty = tty_table[PTY_OTHER(dev)];
 324         else
 325                 other_tty = NULL;
 326         termios_tty = tty;
 327         termios_dev = dev;
 328         if (IS_A_PTY_MASTER(dev)) {
 329                 termios_tty = other_tty;
 330                 termios_dev = PTY_OTHER(dev);
 331         }
 332         switch (cmd) {
 333                 case TCGETS:
 334                         return get_termios(termios_tty,(struct termios *) arg);
 335                 case TCSETSF:
 336                         flush_input(tty);
 337                 /* fallthrough */
 338                 case TCSETSW:
 339                         wait_until_sent(tty);
 340                 /* fallthrough */
 341                 case TCSETS:
 342                         return set_termios(termios_tty,(struct termios *) arg, termios_dev);
 343                 case TCGETA:
 344                         return get_termio(termios_tty,(struct termio *) arg);
 345                 case TCSETAF:
 346                         flush_input(tty);
 347                 /* fallthrough */
 348                 case TCSETAW:
 349                         wait_until_sent(tty); /* fallthrough */
 350                 case TCSETA:
 351                         return set_termio(termios_tty,(struct termio *) arg, termios_dev);
 352                 case TCXONC:
 353                         switch (arg) {
 354                         case TCOOFF:
 355                                 tty->stopped = 1;
 356                                 TTY_WRITE_FLUSH(tty);
 357                                 return 0;
 358                         case TCOON:
 359                                 tty->stopped = 0;
 360                                 TTY_WRITE_FLUSH(tty);
 361                                 return 0;
 362                         case TCIOFF:
 363                                 if (STOP_CHAR(tty))
 364                                         put_tty_queue(STOP_CHAR(tty),
 365                                                       &tty->write_q);
 366                                 return 0;
 367                         case TCION:
 368                                 if (START_CHAR(tty))
 369                                         put_tty_queue(START_CHAR(tty),
 370                                                       &tty->write_q);
 371                                 return 0;
 372                         }
 373                         return -EINVAL; /* not implemented */
 374                 case TCFLSH:
 375                         if (arg==0)
 376                                 flush_input(tty);
 377                         else if (arg==1)
 378                                 flush_output(tty);
 379                         else if (arg==2) {
 380                                 flush_input(tty);
 381                                 flush_output(tty);
 382                         } else
 383                                 return -EINVAL;
 384                         return 0;
 385                 case TIOCEXCL:
 386                         return -EINVAL; /* not implemented */
 387                 case TIOCNXCL:
 388                         return -EINVAL; /* not implemented */
 389                 case TIOCSCTTY:
 390                         if ((current->leader && current->tty < 0 &&
 391                              tty->session == 0) ||
 392                             (arg == 1 && suser())) {
 393                                 current->tty = dev;
 394                                 tty->session = current->session;
 395                                 tty->pgrp = current->pgrp;
 396                                 return 0;
 397                         }
 398                         return -EPERM;
 399                 case TIOCGPGRP:
 400                         retval = verify_area(VERIFY_WRITE, (void *) arg,4);
 401                         if (!retval)
 402                                 put_fs_long(termios_tty->pgrp,(unsigned long *) arg);
 403                         return retval;
 404                 case TIOCSPGRP:
 405                         if ((current->tty < 0) ||
 406                             (current->tty != termios_dev) ||
 407                             (termios_tty->session != current->session))
 408                                 return -ENOTTY;
 409                         pgrp=get_fs_long((unsigned long *) arg);
 410                         if (pgrp < 0)
 411                                 return -EINVAL;
 412                         if (session_of_pgrp(pgrp) != current->session)
 413                                 return -EPERM;
 414                         termios_tty->pgrp = pgrp;                       
 415                         return 0;
 416                 case TIOCOUTQ:
 417                         retval = verify_area(VERIFY_WRITE, (void *) arg,4);
 418                         if (!retval)
 419                                 put_fs_long(CHARS(&tty->write_q),
 420                                     (unsigned long *) arg);
 421                         return retval;
 422                 case TIOCINQ:
 423                         retval = verify_area(VERIFY_WRITE, (void *) arg,4);
 424                         if (retval)
 425                                 return retval;
 426                         if (L_CANON(tty) && !tty->secondary.data)
 427                                 put_fs_long(0, (unsigned long *) arg);
 428                         else
 429                                 put_fs_long(CHARS(&tty->secondary),
 430                                         (unsigned long *) arg);
 431                         return 0;
 432                 case TIOCSTI:
 433                         return -EINVAL; /* not implemented */
 434                 case TIOCGWINSZ:
 435                         return get_window_size(tty,(struct winsize *) arg);
 436                 case TIOCSWINSZ:
 437                         if (IS_A_PTY_MASTER(dev))
 438                                 set_window_size(other_tty,(struct winsize *) arg);
 439                         return set_window_size(tty,(struct winsize *) arg);
 440                 case TIOCGSOFTCAR:
 441                         return -EINVAL; /* not implemented */
 442                 case TIOCSSOFTCAR:
 443                         return -EINVAL; /* not implemented */
 444                 case TIOCLINUX:
 445                         switch (get_fs_byte((char *)arg))
 446                         {
 447                                 case 0: 
 448                                         return do_screendump(arg);
 449                                 case 1: 
 450                                         return do_get_ps_info(arg);
 451                                 default: 
 452                                         return -EINVAL;
 453                         }
 454                 case TIOCCONS:
 455                         if (IS_A_CONSOLE(dev)) {
 456                                 if (!suser())
 457                                         return -EPERM;
 458                                 redirect = NULL;
 459                                 return 0;
 460                         }
 461                         if (redirect)
 462                                 return -EBUSY;
 463                         if (!suser())
 464                                 return -EPERM;
 465                         if (IS_A_PTY_MASTER(dev))
 466                                 redirect = other_tty;
 467                         else if (IS_A_PTY_SLAVE(dev))
 468                                 redirect = tty;
 469                         else
 470                                 return -EINVAL;
 471                         return 0;
 472                 case FIONBIO:
 473                         arg = get_fs_long((unsigned long *) arg);
 474                         if (arg)
 475                                 file->f_flags |= O_NONBLOCK;
 476                         else
 477                                 file->f_flags &= ~O_NONBLOCK;
 478                         return 0;
 479                 case TIOCNOTTY:
 480                         if (MINOR(file->f_rdev) != current->tty)
 481                                 return -EINVAL;
 482                         current->tty = -1;
 483                         if (current->leader) {
 484                                 if (tty->pgrp > 0)
 485                                         kill_pg(tty->pgrp, SIGHUP, 0);
 486                                 tty->pgrp = -1;
 487                                 tty->session = 0;
 488                         }
 489                         return 0;
 490                 case TIOCGETD:
 491                         retval = verify_area(VERIFY_WRITE, (void *) arg,4);
 492                         if (!retval)
 493                                 put_fs_long(tty->disc, (unsigned long *) arg);
 494                         return retval;
 495                 case TIOCSETD:
 496                         arg = get_fs_long((unsigned long *) arg);
 497                         return tty_set_ldisc(tty, arg);
 498                case TIOCPKT:
 499                         {
 500                            int on;
 501                            if (!IS_A_PTY_MASTER(dev))
 502                              return -EINVAL;
 503                            retval = verify_area(VERIFY_READ, (unsigned long *)arg, sizeof (int));
 504                            if (retval)
 505                                 return retval;
 506                            on=get_fs_long ((unsigned long *)arg);
 507                            if (on )
 508                              tty->packet = 1;
 509                            else
 510                              tty->packet = 0;
 511                            return (0);
 512                         }
 513 
 514                 default:
 515                         if (tty->ioctl) {
 516                                 retval = (tty->ioctl)(tty, file, cmd, arg);
 517                                 if (retval != -EINVAL)
 518                                         return retval;
 519                         }
 520                         if (ldiscs[tty->disc].ioctl) {
 521                                 retval = (ldiscs[tty->disc].ioctl)
 522                                         (tty, file, cmd, arg);
 523                                 return retval;
 524                         }
 525                         return -EINVAL;
 526         }
 527 }

/* [previous][next][first][last][top][bottom][index][help] */