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. set_termios
  8. get_termio
  9. set_termio
  10. set_window_size
  11. get_window_size
  12. tty_ioctl

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

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