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         verify_area((void *)arg, sizeof(struct tstruct));
 108                 
 109         for (p = &FIRST_TASK ; p <= &LAST_TASK ; p++, n++)
 110                 if (*p)
 111                 {
 112                         c = (char *)(*p);
 113                         d = (char *)(ts->tasks+n);
 114                         for (i=0 ; i<sizeof(struct task_struct) ; i++)
 115                                 put_fs_byte(*c++, d++);
 116                         put_fs_long(1, (unsigned long *)(ts->present+n));
 117                 }
 118                 else    
 119                         put_fs_long(0, (unsigned long *)(ts->present+n));
 120         return(0);                      
 121 }
 122 
 123 static int get_termios(struct tty_struct * tty, struct termios * termios)
     /* [previous][next][first][last][top][bottom][index][help] */
 124 {
 125         int i;
 126 
 127         verify_area(termios, sizeof (*termios));
 128         for (i=0 ; i< (sizeof (*termios)) ; i++)
 129                 put_fs_byte( ((char *)tty->termios)[i] , i+(char *)termios );
 130         return 0;
 131 }
 132 
 133 static int check_change(struct tty_struct * tty, int channel)
     /* [previous][next][first][last][top][bottom][index][help] */
 134 {
 135         /* If we try to set the state of terminal and we're not in the
 136            foreground, send a SIGTTOU.  If the signal is blocked or
 137            ignored, go ahead and perform the operation.  POSIX 7.2) */
 138         if (current->tty != channel)
 139                 return 0;
 140         if (tty->pgrp <= 0 || tty->pgrp == current->pgrp)
 141                 return 0;
 142         if (is_orphaned_pgrp(current->pgrp))
 143                 return -EIO;
 144         if (is_ignored(SIGTTOU))
 145                 return 0;
 146         (void) kill_pg(current->pgrp,SIGTTOU,1);
 147         return -ERESTARTSYS;
 148 }
 149 
 150 static int set_termios(struct tty_struct * tty, struct termios * termios,
     /* [previous][next][first][last][top][bottom][index][help] */
 151                         int channel)
 152 {
 153         int i;
 154         struct termios old_termios = *tty->termios;
 155 
 156         i = check_change(tty, channel);
 157         if (i)
 158                 return i;
 159         for (i=0 ; i< (sizeof (*termios)) ; i++)
 160                 ((char *)tty->termios)[i]=get_fs_byte(i+(char *)termios);
 161 
 162         /* puting mpty's into echo mode is very bad, and I think under
 163            some situations can cause the kernel to do nothing but
 164            copy characters back and forth. -RAB */
 165         if (IS_A_PTY_MASTER(channel)) tty->termios->c_lflag &= ~ECHO;
 166 
 167         if (tty->set_termios)
 168                 (*tty->set_termios)(tty, &old_termios);
 169 
 170         return 0;
 171 }
 172 
 173 static int get_termio(struct tty_struct * tty, struct termio * termio)
     /* [previous][next][first][last][top][bottom][index][help] */
 174 {
 175         int i;
 176         struct termio tmp_termio;
 177 
 178         verify_area(termio, sizeof (*termio));
 179         tmp_termio.c_iflag = tty->termios->c_iflag;
 180         tmp_termio.c_oflag = tty->termios->c_oflag;
 181         tmp_termio.c_cflag = tty->termios->c_cflag;
 182         tmp_termio.c_lflag = tty->termios->c_lflag;
 183         tmp_termio.c_line = tty->termios->c_line;
 184         for(i=0 ; i < NCC ; i++)
 185                 tmp_termio.c_cc[i] = tty->termios->c_cc[i];
 186         for (i=0 ; i< (sizeof (*termio)) ; i++)
 187                 put_fs_byte( ((char *)&tmp_termio)[i] , i+(char *)termio );
 188         return 0;
 189 }
 190 
 191 /*
 192  * This only works as the 386 is low-byte-first
 193  */
 194 static int set_termio(struct tty_struct * tty, struct termio * termio,
     /* [previous][next][first][last][top][bottom][index][help] */
 195                         int channel)
 196 {
 197         int i;
 198         struct termio tmp_termio;
 199         struct termios old_termios = *tty->termios;
 200 
 201         i = check_change(tty, channel);
 202         if (i)
 203                 return i;
 204         for (i=0 ; i< (sizeof (*termio)) ; i++)
 205                 ((char *)&tmp_termio)[i]=get_fs_byte(i+(char *)termio);
 206 
 207         /* take care of the packet stuff. */
 208         if ((tmp_termio.c_iflag & IXON) &&
 209             ~(tty->termios->c_iflag & IXON))
 210           {
 211              tty->status_changed = 1;
 212              tty->ctrl_status |= TIOCPKT_DOSTOP;
 213           }
 214 
 215         if (~(tmp_termio.c_iflag & IXON) &&
 216             (tty->termios->c_iflag & IXON))
 217           {
 218              tty->status_changed = 1;
 219              tty->ctrl_status |= TIOCPKT_NOSTOP;
 220           }
 221 
 222         *(unsigned short *)&tty->termios->c_iflag = tmp_termio.c_iflag;
 223         *(unsigned short *)&tty->termios->c_oflag = tmp_termio.c_oflag;
 224         *(unsigned short *)&tty->termios->c_cflag = tmp_termio.c_cflag;
 225         *(unsigned short *)&tty->termios->c_lflag = tmp_termio.c_lflag;
 226         tty->termios->c_line = tmp_termio.c_line;
 227         for(i=0 ; i < NCC ; i++)
 228                 tty->termios->c_cc[i] = tmp_termio.c_cc[i];
 229 
 230         if (tty->set_termios)
 231                 (*tty->set_termios)(tty, &old_termios);
 232 
 233         return 0;
 234 }
 235 
 236 static int set_window_size(struct tty_struct * tty, struct winsize * ws)
     /* [previous][next][first][last][top][bottom][index][help] */
 237 {
 238         int i,changed;
 239         char c, * tmp;
 240 
 241         if (!ws)
 242                 return -EINVAL;
 243         tmp = (char *) &tty->winsize;
 244         changed = 0;
 245         for (i = 0; i < sizeof (*ws) ; i++,tmp++) {
 246                 c = get_fs_byte(i + (char *) ws);
 247                 if (c == *tmp)
 248                         continue;
 249                 changed = 1;
 250                 *tmp = c;
 251         }
 252         if (changed)
 253                 kill_pg(tty->pgrp, SIGWINCH, 1);
 254         return 0;
 255 }
 256 
 257 static int get_window_size(struct tty_struct * tty, struct winsize * ws)
     /* [previous][next][first][last][top][bottom][index][help] */
 258 {
 259         int i;
 260         char * tmp;
 261 
 262         if (!ws)
 263                 return -EINVAL;
 264         verify_area(ws, sizeof (*ws));
 265         tmp = (char *) ws;
 266         for (i = 0; i < sizeof (struct winsize) ; i++,tmp++)
 267                 put_fs_byte(((char *) &tty->winsize)[i], tmp);
 268         return 0;
 269 }
 270 
 271 /* Set the discipline of a tty line. */
 272 static int tty_set_ldisc(struct tty_struct *tty, int ldisc)
     /* [previous][next][first][last][top][bottom][index][help] */
 273 {
 274         if ((ldisc < N_TTY) || (ldisc >= NR_LDISCS) ||
 275             !(ldiscs[ldisc].flags & LDISC_FLAG_DEFINED))
 276                 return -EINVAL;
 277 
 278         if (tty->disc == ldisc)
 279                 return 0;       /* We are already in the desired discipline */
 280 
 281         /* Shutdown the current discipline. */
 282         wait_until_sent(tty);
 283         flush_input(tty);
 284         if (ldiscs[tty->disc].close)
 285                 ldiscs[tty->disc].close(tty);
 286 
 287         /* Now set up the new line discipline. */
 288         tty->disc = ldisc;
 289         if (ldiscs[tty->disc].open)
 290                 return(ldiscs[tty->disc].open(tty));
 291         else
 292                 return 0;
 293 }
 294 
 295 
 296 int tty_ioctl(struct inode * inode, struct file * file,
     /* [previous][next][first][last][top][bottom][index][help] */
 297         unsigned int cmd, unsigned int arg)
 298 {
 299         struct tty_struct * tty;
 300         struct tty_struct * other_tty;
 301         struct tty_struct * termios_tty;
 302         int pgrp;
 303         int dev;
 304         int termios_dev;
 305         int retval;
 306 
 307         if (MAJOR(file->f_rdev) != 4) {
 308                 printk("tty_ioctl: tty pseudo-major != 4\n");
 309                 return -EINVAL;
 310         }
 311         dev = MINOR(file->f_rdev);
 312         tty = TTY_TABLE(dev);
 313         if (!tty)
 314                 return -EINVAL;
 315         if (IS_A_PTY(dev))
 316                 other_tty = tty_table[PTY_OTHER(dev)];
 317         else
 318                 other_tty = NULL;
 319         termios_tty = tty;
 320         termios_dev = dev;
 321         if (IS_A_PTY_MASTER(dev)) {
 322                 termios_tty = other_tty;
 323                 termios_dev = PTY_OTHER(dev);
 324         }
 325         switch (cmd) {
 326                 case TCGETS:
 327                         return get_termios(termios_tty,(struct termios *) arg);
 328                 case TCSETSF:
 329                         flush_input(tty);
 330                 /* fallthrough */
 331                 case TCSETSW:
 332                         wait_until_sent(tty);
 333                 /* fallthrough */
 334                 case TCSETS:
 335                         return set_termios(termios_tty,(struct termios *) arg, termios_dev);
 336                 case TCGETA:
 337                         return get_termio(termios_tty,(struct termio *) arg);
 338                 case TCSETAF:
 339                         flush_input(tty);
 340                 /* fallthrough */
 341                 case TCSETAW:
 342                         wait_until_sent(tty); /* fallthrough */
 343                 case TCSETA:
 344                         return set_termio(termios_tty,(struct termio *) arg, termios_dev);
 345                 case TCXONC:
 346                         switch (arg) {
 347                         case TCOOFF:
 348                                 tty->stopped = 1;
 349                                 TTY_WRITE_FLUSH(tty);
 350                                 return 0;
 351                         case TCOON:
 352                                 tty->stopped = 0;
 353                                 TTY_WRITE_FLUSH(tty);
 354                                 return 0;
 355                         case TCIOFF:
 356                                 if (STOP_CHAR(tty))
 357                                         put_tty_queue(STOP_CHAR(tty),
 358                                                       &tty->write_q);
 359                                 return 0;
 360                         case TCION:
 361                                 if (START_CHAR(tty))
 362                                         put_tty_queue(START_CHAR(tty),
 363                                                       &tty->write_q);
 364                                 return 0;
 365                         }
 366                         return -EINVAL; /* not implemented */
 367                 case TCFLSH:
 368                         if (arg==0)
 369                                 flush_input(tty);
 370                         else if (arg==1)
 371                                 flush_output(tty);
 372                         else if (arg==2) {
 373                                 flush_input(tty);
 374                                 flush_output(tty);
 375                         } else
 376                                 return -EINVAL;
 377                         return 0;
 378                 case TIOCEXCL:
 379                         return -EINVAL; /* not implemented */
 380                 case TIOCNXCL:
 381                         return -EINVAL; /* not implemented */
 382                 case TIOCSCTTY:
 383                         if (current->leader && current->tty < 0
 384                             && tty->session == 0) {
 385                                 current->tty = dev;
 386                                 tty->session = current->session;
 387                                 tty->pgrp = current->pgrp;
 388                                 return 0;
 389                         }
 390                         return -EPERM;
 391                 case TIOCGPGRP:
 392                         verify_area((void *) arg,4);
 393                         put_fs_long(termios_tty->pgrp,(unsigned long *) arg);
 394                         return 0;
 395                 case TIOCSPGRP:
 396                         if ((current->tty < 0) ||
 397                             (current->tty != termios_dev) ||
 398                             (termios_tty->session != current->session))
 399                                 return -ENOTTY;
 400                         pgrp=get_fs_long((unsigned long *) arg);
 401                         if (pgrp < 0)
 402                                 return -EINVAL;
 403                         if (session_of_pgrp(pgrp) != current->session)
 404                                 return -EPERM;
 405                         termios_tty->pgrp = pgrp;                       
 406                         return 0;
 407                 case TIOCOUTQ:
 408                         verify_area((void *) arg,4);
 409                         put_fs_long(CHARS(&tty->write_q),
 410                                     (unsigned long *) arg);
 411                         return 0;
 412                 case TIOCINQ:
 413                         verify_area((void *) arg,4);
 414                         if (L_CANON(tty) && !tty->secondary.data)
 415                                 put_fs_long(0, (unsigned long *) arg);
 416                         else
 417                                 put_fs_long(CHARS(&tty->secondary),
 418                                         (unsigned long *) arg);
 419                         return 0;
 420                 case TIOCSTI:
 421                         return -EINVAL; /* not implemented */
 422                 case TIOCGWINSZ:
 423                         return get_window_size(tty,(struct winsize *) arg);
 424                 case TIOCSWINSZ:
 425                         if (IS_A_PTY_MASTER(dev))
 426                                 set_window_size(other_tty,(struct winsize *) arg);
 427                         return set_window_size(tty,(struct winsize *) arg);
 428                 case TIOCGSOFTCAR:
 429                         return -EINVAL; /* not implemented */
 430                 case TIOCSSOFTCAR:
 431                         return -EINVAL; /* not implemented */
 432                 case TIOCLINUX:
 433                         switch (get_fs_byte((char *)arg))
 434                         {
 435                                 case 0: 
 436                                         return do_screendump(arg);
 437                                 case 1: 
 438                                         return do_get_ps_info(arg);
 439                                 default: 
 440                                         return -EINVAL;
 441                         }
 442                 case TIOCCONS:
 443                         if (IS_A_CONSOLE(dev)) {
 444                                 if (!suser())
 445                                         return -EPERM;
 446                                 redirect = NULL;
 447                                 return 0;
 448                         }
 449                         if (redirect)
 450                                 return -EBUSY;
 451                         if (!suser())
 452                                 return -EPERM;
 453                         if (IS_A_PTY_MASTER(dev))
 454                                 redirect = other_tty;
 455                         else if (IS_A_PTY_SLAVE(dev))
 456                                 redirect = tty;
 457                         else
 458                                 return -EINVAL;
 459                         return 0;
 460                 case FIONBIO:
 461                         arg = get_fs_long((unsigned long *) arg);
 462                         if (arg)
 463                                 file->f_flags |= O_NONBLOCK;
 464                         else
 465                                 file->f_flags &= ~O_NONBLOCK;
 466                         return 0;
 467                 case TIOCNOTTY:
 468                         if (MINOR(file->f_rdev) != current->tty)
 469                                 return -EINVAL;
 470                         current->tty = -1;
 471                         if (current->leader) {
 472                                 if (tty->pgrp > 0)
 473                                         kill_pg(tty->pgrp, SIGHUP, 0);
 474                                 tty->pgrp = -1;
 475                                 tty->session = 0;
 476                         }
 477                         return 0;
 478                 case TIOCGETD:
 479                         verify_area((void *) arg,4);
 480                         put_fs_long(tty->disc, (unsigned long *) arg);
 481                         return 0;
 482                 case TIOCSETD:
 483                         arg = get_fs_long((unsigned long *) arg);
 484                         return tty_set_ldisc(tty, arg);
 485                case TIOCPKT:
 486                         {
 487                            int on;
 488                            if (!IS_A_PTY_MASTER(dev))
 489                              return (-EINVAL);
 490                            verify_area ((unsigned long *)arg, sizeof (int));
 491                            on=get_fs_long ((unsigned long *)arg);
 492                            if (on )
 493                              tty->packet = 1;
 494                            else
 495                              tty->packet = 0;
 496                            return (0);
 497                         }
 498 
 499                 default:
 500                         if (tty->ioctl) {
 501                                 retval = (tty->ioctl)(tty, file, cmd, arg);
 502                                 if (retval != -EINVAL)
 503                                         return retval;
 504                         }
 505                         if (ldiscs[tty->disc].ioctl) {
 506                                 retval = (ldiscs[tty->disc].ioctl)
 507                                         (tty, file, cmd, arg);
 508                                 return retval;
 509                         }
 510                         return -EINVAL;
 511         }
 512 }

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