root/kernel/chr_drv/tty_io.c

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

DEFINITIONS

This source file includes following definitions.
  1. change_console
  2. sleep_if_empty
  3. sleep_if_full
  4. wait_for_keypress
  5. copy_to_cooked
  6. tty_signal
  7. tty_read
  8. tty_write
  9. chr_dev_init
  10. tty_init

   1 /*
   2  *  linux/kernel/tty_io.c
   3  *
   4  *  (C) 1991  Linus Torvalds
   5  */
   6 
   7 /*
   8  * 'tty_io.c' gives an orthogonal feeling to tty's, be they consoles
   9  * or rs-channels. It also implements echoing, cooked mode etc.
  10  *
  11  * Kill-line thanks to John T Kohl, who also corrected VMIN = VTIME = 0.
  12  */
  13 
  14 #include <ctype.h>
  15 #include <errno.h>
  16 #include <signal.h>
  17 #include <unistd.h>
  18 #include <fcntl.h>
  19 
  20 #define ALRMMASK (1<<(SIGALRM-1))
  21 
  22 #include <linux/sched.h>
  23 #include <linux/tty.h>
  24 #include <asm/segment.h>
  25 #include <asm/system.h>
  26 
  27 int kill_pg(int pgrp, int sig, int priv);
  28 int is_orphaned_pgrp(int pgrp);
  29 
  30 #define _L_FLAG(tty,f)  ((tty)->termios.c_lflag & f)
  31 #define _I_FLAG(tty,f)  ((tty)->termios.c_iflag & f)
  32 #define _O_FLAG(tty,f)  ((tty)->termios.c_oflag & f)
  33 
  34 #define L_CANON(tty)    _L_FLAG((tty),ICANON)
  35 #define L_ISIG(tty)     _L_FLAG((tty),ISIG)
  36 #define L_ECHO(tty)     _L_FLAG((tty),ECHO)
  37 #define L_ECHOE(tty)    _L_FLAG((tty),ECHOE)
  38 #define L_ECHOK(tty)    _L_FLAG((tty),ECHOK)
  39 #define L_ECHONL(tty)   _L_FLAG((tty),ECHONL)
  40 #define L_ECHOCTL(tty)  _L_FLAG((tty),ECHOCTL)
  41 #define L_ECHOKE(tty)   _L_FLAG((tty),ECHOKE)
  42 #define L_TOSTOP(tty)   _L_FLAG((tty),TOSTOP)
  43 
  44 #define I_UCLC(tty)     _I_FLAG((tty),IUCLC)
  45 #define I_NLCR(tty)     _I_FLAG((tty),INLCR)
  46 #define I_CRNL(tty)     _I_FLAG((tty),ICRNL)
  47 #define I_NOCR(tty)     _I_FLAG((tty),IGNCR)
  48 #define I_IXON(tty)     _I_FLAG((tty),IXON)
  49 
  50 #define O_POST(tty)     _O_FLAG((tty),OPOST)
  51 #define O_NLCR(tty)     _O_FLAG((tty),ONLCR)
  52 #define O_CRNL(tty)     _O_FLAG((tty),OCRNL)
  53 #define O_NLRET(tty)    _O_FLAG((tty),ONLRET)
  54 #define O_LCUC(tty)     _O_FLAG((tty),OLCUC)
  55 
  56 #define C_SPEED(tty)    ((tty)->termios.c_cflag & CBAUD)
  57 #define C_HUP(tty)      (C_SPEED((tty)) == B0)
  58 
  59 #ifndef MIN
  60 #define MIN(a,b) ((a) < (b) ? (a) : (b))
  61 #endif
  62 
  63 #define QUEUES  (3*(MAX_CONSOLES+NR_SERIALS+2*NR_PTYS))
  64 static struct tty_queue tty_queues[QUEUES];
  65 struct tty_struct tty_table[256];
  66 
  67 #define con_queues tty_queues
  68 #define rs_queues ((3*MAX_CONSOLES) + tty_queues)
  69 #define mpty_queues ((3*(MAX_CONSOLES+NR_SERIALS)) + tty_queues)
  70 #define spty_queues ((3*(MAX_CONSOLES+NR_SERIALS+NR_PTYS)) + tty_queues)
  71 
  72 #define con_table tty_table
  73 #define rs_table (64+tty_table)
  74 #define mpty_table (128+tty_table)
  75 #define spty_table (192+tty_table)
  76 
  77 int fg_console = 0;
  78 
  79 /*
  80  * these are the tables used by the machine code handlers.
  81  * you can implement virtual consoles.
  82  */
  83 struct tty_queue * table_list[]={
  84         con_queues + 0, con_queues + 1,
  85         rs_queues + 0, rs_queues + 1,
  86         rs_queues + 3, rs_queues + 4,
  87         rs_queues + 6, rs_queues + 7,
  88         rs_queues + 9, rs_queues + 10
  89         };
  90 
  91 void change_console(unsigned int new_console)
     /* [previous][next][first][last][top][bottom][index][help] */
  92 {
  93         if (new_console == fg_console || new_console >= NR_CONSOLES)
  94                 return;
  95         table_list[0] = con_queues + 0 + new_console*3;
  96         table_list[1] = con_queues + 1 + new_console*3;
  97         update_screen(new_console);
  98 }
  99 
 100 static void sleep_if_empty(struct tty_queue * queue)
     /* [previous][next][first][last][top][bottom][index][help] */
 101 {
 102         cli();
 103         while (!(current->signal & ~current->blocked) && EMPTY(queue))
 104                 interruptible_sleep_on(&queue->proc_list);
 105         sti();
 106 }
 107 
 108 static void sleep_if_full(struct tty_queue * queue)
     /* [previous][next][first][last][top][bottom][index][help] */
 109 {
 110         if (!FULL(queue))
 111                 return;
 112         cli();
 113         while (!(current->signal & ~current->blocked) && LEFT(queue)<128)
 114                 interruptible_sleep_on(&queue->proc_list);
 115         sti();
 116 }
 117 
 118 void wait_for_keypress(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 119 {
 120         sleep_if_empty(tty_table[fg_console].secondary);
 121 }
 122 
 123 void copy_to_cooked(struct tty_struct * tty)
     /* [previous][next][first][last][top][bottom][index][help] */
 124 {
 125         unsigned char c;
 126 
 127         if (!(tty && tty->write && tty->read_q &&
 128             tty->write_q && tty->secondary)) {
 129                 printk("copy_to_cooked: missing queues\n\r");
 130                 return;
 131         }
 132         cli();
 133         if (tty->busy) {
 134                 sti();
 135                 return;
 136         }
 137         tty->busy = 1;
 138         sti();
 139         while (1) {
 140                 if (EMPTY(tty->read_q))
 141                         break;
 142                 if (FULL(tty->secondary)) {
 143                         if (tty->secondary->proc_list)
 144                                 if (tty->secondary->proc_list != current)
 145                                         current->counter = 0;
 146                         break;
 147                 }
 148                 GETCH(tty->read_q,c);
 149                 if (c==13) {
 150                         if (I_CRNL(tty))
 151                                 c=10;
 152                         else if (I_NOCR(tty))
 153                                 continue;
 154                 } else if (c==10 && I_NLCR(tty))
 155                         c=13;
 156                 if (I_UCLC(tty))
 157                         c=tolower(c);
 158                 if (L_CANON(tty)) {
 159                         if ((KILL_CHAR(tty) != _POSIX_VDISABLE) &&
 160                             (c==KILL_CHAR(tty))) {
 161                                 /* deal with killing the input line */
 162                                 while(!(EMPTY(tty->secondary) ||
 163                                         (c=LAST(tty->secondary))==10 ||
 164                                         ((EOF_CHAR(tty) != _POSIX_VDISABLE) &&
 165                                          (c==EOF_CHAR(tty))))) {
 166                                         if (L_ECHO(tty)) {
 167                                                 if (c<32)
 168                                                         PUTCH(127,tty->write_q);
 169                                                 PUTCH(127,tty->write_q);
 170                                         }
 171                                         DEC(tty->secondary->head);
 172                                 }
 173                                 continue;
 174                         }
 175                         if ((ERASE_CHAR(tty) != _POSIX_VDISABLE) &&
 176                             (c==ERASE_CHAR(tty))) {
 177                                 if (EMPTY(tty->secondary) ||
 178                                    (c=LAST(tty->secondary))==10 ||
 179                                    ((EOF_CHAR(tty) != _POSIX_VDISABLE) &&
 180                                     (c==EOF_CHAR(tty))))
 181                                         continue;
 182                                 if (L_ECHO(tty)) {
 183                                         if (c<32)
 184                                                 PUTCH(127,tty->write_q);
 185                                         PUTCH(127,tty->write_q);
 186                                 }
 187                                 DEC(tty->secondary->head);
 188                                 continue;
 189                         }
 190                 }
 191                 if (I_IXON(tty)) {
 192                         if ((STOP_CHAR(tty) != _POSIX_VDISABLE) &&
 193                             (c==STOP_CHAR(tty))) {
 194                                 tty->stopped=1;
 195                                 continue;
 196                         }
 197                         if ((START_CHAR(tty) != _POSIX_VDISABLE) &&
 198                             (c==START_CHAR(tty))) {
 199                                 tty->stopped=0;
 200                                 continue;
 201                         }
 202                 }
 203                 if (L_ISIG(tty)) {
 204                         if ((INTR_CHAR(tty) != _POSIX_VDISABLE) &&
 205                             (c==INTR_CHAR(tty))) {
 206                                 kill_pg(tty->pgrp, SIGINT, 1);
 207                                 continue;
 208                         }
 209                         if ((QUIT_CHAR(tty) != _POSIX_VDISABLE) &&
 210                             (c==QUIT_CHAR(tty))) {
 211                                 kill_pg(tty->pgrp, SIGQUIT, 1);
 212                                 continue;
 213                         }
 214                         if ((SUSPEND_CHAR(tty) != _POSIX_VDISABLE) &&
 215                             (c==SUSPEND_CHAR(tty))) {
 216                                 if (!is_orphaned_pgrp(tty->pgrp))
 217                                         kill_pg(tty->pgrp, SIGTSTP, 1);
 218                                 continue;
 219                         }
 220                 }
 221                 if (c==10 || (EOF_CHAR(tty) != _POSIX_VDISABLE &&
 222                               c==EOF_CHAR(tty)))
 223                         tty->secondary->data++;
 224                 if ((L_ECHO(tty) || L_ECHONL(tty)) && (c==10)) {
 225                         PUTCH(10,tty->write_q);
 226                         PUTCH(13,tty->write_q);
 227                 } else if (L_ECHO(tty)) {
 228                         if (c<32 && L_ECHOCTL(tty)) {
 229                                 PUTCH('^',tty->write_q);
 230                                 PUTCH(c+64,tty->write_q);
 231                         } else
 232                                 PUTCH(c,tty->write_q);
 233                 }
 234                 PUTCH(c,tty->secondary);
 235         }
 236         tty->write(tty);
 237         tty->busy = 0;
 238         if (!EMPTY(tty->secondary))
 239                 wake_up(&tty->secondary->proc_list);
 240 }
 241 
 242 /*
 243  * Called when we need to send a SIGTTIN or SIGTTOU to our process
 244  * group
 245  * 
 246  * We only request that a system call be restarted if there was if the 
 247  * default signal handler is being used.  The reason for this is that if
 248  * a job is catching SIGTTIN or SIGTTOU, the signal handler may not want 
 249  * the system call to be restarted blindly.  If there is no way to reset the
 250  * terminal pgrp back to the current pgrp (perhaps because the controlling
 251  * tty has been released on logout), we don't want to be in an infinite loop
 252  * while restarting the system call, and have it always generate a SIGTTIN
 253  * or SIGTTOU.  The default signal handler will cause the process to stop
 254  * thus avoiding the infinite loop problem.  Presumably the job-control
 255  * cognizant parent will fix things up before continuging its child process.
 256  */
 257 int tty_signal(int sig, struct tty_struct *tty)
     /* [previous][next][first][last][top][bottom][index][help] */
 258 {
 259         if (is_orphaned_pgrp(current->pgrp))
 260                 return -EIO;            /* don't stop an orphaned pgrp */
 261         (void) kill_pg(current->pgrp,sig,1);
 262         if ((current->blocked & (1<<(sig-1))) ||
 263             ((int) current->sigaction[sig-1].sa_handler == 1)) 
 264                 return -EIO;            /* Our signal will be ignored */
 265         else if (current->sigaction[sig-1].sa_handler)
 266                 return -EINTR;          /* We _will_ be interrupted :-) */
 267         else
 268                 return -ERESTARTSYS;    /* We _will_ be interrupted :-) */
 269                                         /* (but restart after we continue) */
 270 }
 271 
 272 int tty_read(unsigned channel, char * buf, int nr, unsigned short flags)
     /* [previous][next][first][last][top][bottom][index][help] */
 273 {
 274         struct tty_struct * tty;
 275         struct tty_struct * other_tty = NULL;
 276         unsigned char c;
 277         char * b=buf;
 278         int minimum,time;
 279 
 280         if (channel > 255)
 281                 return -EIO;
 282         tty = TTY_TABLE(channel);
 283         if (!(tty->read_q && tty->secondary))
 284                 return -EIO;
 285         if ((tty->pgrp > 0) && (current->tty == channel) &&
 286             (tty->pgrp != current->pgrp))
 287                 return(tty_signal(SIGTTIN, tty));
 288         if (channel & 0x80)
 289                 other_tty = tty_table + (channel ^ 0x40);
 290         time = 10L*tty->termios.c_cc[VTIME];
 291         minimum = tty->termios.c_cc[VMIN];
 292         if (L_CANON(tty)) {
 293                 minimum = nr;
 294                 current->timeout = 0xffffffff;
 295                 time = 0;
 296         } else if (minimum)
 297                 current->timeout = 0xffffffff;
 298         else {
 299                 minimum = nr;
 300                 if (time)
 301                         current->timeout = time + jiffies;
 302                 time = 0;
 303         }
 304         if (flags & O_NONBLOCK)
 305                 time = current->timeout = 0;
 306         if (minimum>nr)
 307                 minimum = nr;
 308         copy_to_cooked(tty);
 309         while (nr>0) {
 310                 if (other_tty && other_tty->write)
 311                         TTY_WRITE(other_tty);
 312                 cli();
 313                 if (EMPTY(tty->secondary) || (L_CANON(tty) &&
 314                     !FULL(tty->read_q) && !tty->secondary->data)) {
 315                         if (!current->timeout)
 316                                 break;
 317                         if (current->signal & ~current->blocked) 
 318                                 break;
 319                         if (IS_A_PTY_SLAVE(channel) && C_HUP(other_tty))
 320                                 break;
 321                         interruptible_sleep_on(&tty->secondary->proc_list);
 322                         sti();
 323                         copy_to_cooked(tty);
 324                         continue;
 325                 }
 326                 sti();
 327                 do {
 328                         GETCH(tty->secondary,c);
 329                         if ((EOF_CHAR(tty) != _POSIX_VDISABLE &&
 330                              c==EOF_CHAR(tty)) || c==10)
 331                                 tty->secondary->data--;
 332                         if ((EOF_CHAR(tty) != _POSIX_VDISABLE &&
 333                              c==EOF_CHAR(tty)) && L_CANON(tty))
 334                                 break;
 335                         else {
 336                                 put_fs_byte(c,b++);
 337                                 if (!--nr)
 338                                         break;
 339                         }
 340                         if (c==10 && L_CANON(tty))
 341                                 break;
 342                 } while (nr>0 && !EMPTY(tty->secondary));
 343                 wake_up(&tty->read_q->proc_list);
 344                 if (L_CANON(tty) || b-buf >= minimum)
 345                         break;
 346                 if (time)
 347                         current->timeout = time+jiffies;
 348         }
 349         sti();
 350         current->timeout = 0;
 351         if (b-buf)
 352                 return b-buf;
 353         if (current->signal & ~current->blocked)
 354                 return -ERESTARTSYS;
 355         if (flags & O_NONBLOCK)
 356                 return -EAGAIN;
 357         return 0;
 358 }
 359 
 360 int tty_write(unsigned channel, char * buf, int nr)
     /* [previous][next][first][last][top][bottom][index][help] */
 361 {
 362         static cr_flag=0;
 363         struct tty_struct * tty;
 364         char c, *b=buf;
 365 
 366         if (channel > 255)
 367                 return -EIO;
 368         tty = TTY_TABLE(channel);
 369         if (!(tty->write_q && tty->write))
 370                 return -EIO;
 371         if (L_TOSTOP(tty) &&  (tty->pgrp > 0) &&
 372             (current->tty == channel) && (tty->pgrp != current->pgrp)) 
 373                 return(tty_signal(SIGTTOU, tty));
 374         if (nr < 0)
 375                 return -EINVAL;
 376         if (!nr)
 377                 return 0;
 378         while (nr>0) {
 379                 sleep_if_full(tty->write_q);
 380                 if (current->signal & ~current->blocked)
 381                         break;
 382                 while (nr>0 && !FULL(tty->write_q)) {
 383                         c=get_fs_byte(b);
 384                         if (O_POST(tty)) {
 385                                 if (c=='\r' && O_CRNL(tty))
 386                                         c='\n';
 387                                 else if (c=='\n' && O_NLRET(tty))
 388                                         c='\r';
 389                                 if (c=='\n' && !cr_flag && O_NLCR(tty)) {
 390                                         cr_flag = 1;
 391                                         PUTCH(13,tty->write_q);
 392                                         continue;
 393                                 }
 394                                 if (O_LCUC(tty))
 395                                         c=toupper(c);
 396                         }
 397                         b++; nr--;
 398                         cr_flag = 0;
 399                         PUTCH(c,tty->write_q);
 400                 }
 401                 TTY_WRITE(tty);
 402                 if (nr>0)
 403                         schedule();
 404         }
 405         if (b-buf)
 406                 return b-buf;
 407         if (current->signal & ~current->blocked)
 408                 return -ERESTARTSYS;
 409         return 0;
 410 }
 411 
 412 void chr_dev_init(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 413 {
 414 }
 415 
 416 void tty_init(void)
     /* [previous][next][first][last][top][bottom][index][help] */
 417 {
 418         int i;
 419 
 420         for (i=0 ; i < QUEUES ; i++)
 421                 tty_queues[i] = (struct tty_queue) {0,0,0,0,""};
 422         rs_queues[0] = (struct tty_queue) {0x3f8,0,0,0,""};
 423         rs_queues[1] = (struct tty_queue) {0x3f8,0,0,0,""};
 424         rs_queues[3] = (struct tty_queue) {0x2f8,0,0,0,""};
 425         rs_queues[4] = (struct tty_queue) {0x2f8,0,0,0,""};
 426         rs_queues[6] = (struct tty_queue) {0x3e8,0,0,0,""};
 427         rs_queues[7] = (struct tty_queue) {0x3e8,0,0,0,""};
 428         rs_queues[9] = (struct tty_queue) {0x2e8,0,0,0,""};
 429         rs_queues[10] = (struct tty_queue) {0x2e8,0,0,0,""};
 430         for (i=0 ; i<256 ; i++) {
 431                 tty_table[i] =  (struct tty_struct) {
 432                         {0, 0, 0, 0, 0, INIT_C_CC},
 433                         -1, 0, 0, 0, {0,0,0,0},
 434                         NULL, NULL, NULL, NULL
 435                 };
 436         }
 437         con_init();
 438         for (i = 0 ; i<NR_CONSOLES ; i++) {
 439                 con_table[i] = (struct tty_struct) {
 440                         {ICRNL,         /* change incoming CR to NL */
 441                         OPOST|ONLCR,    /* change outgoing NL to CRNL */
 442                         B38400 | CS8,
 443                         IXON | ISIG | ICANON | ECHO | ECHOCTL | ECHOKE,
 444                         0,              /* console termio */
 445                         INIT_C_CC},
 446                         -1,             /* initial pgrp */
 447                         0,                      /* initial session */
 448                         0,                      /* initial stopped */
 449                         0,                      /* initial busy */
 450                         {video_num_lines,video_num_columns,0,0},
 451                         con_write,
 452                         con_queues+0+i*3,con_queues+1+i*3,con_queues+2+i*3
 453                 };
 454         }
 455         for (i = 0 ; i<NR_SERIALS ; i++) {
 456                 rs_table[i] = (struct tty_struct) {
 457                         {0, /* no translation */
 458                         0,  /* no translation */
 459                         B2400 | CS8,
 460                         0,
 461                         0,
 462                         INIT_C_CC},
 463                         -1,
 464                         0,
 465                         0,
 466                         0,
 467                         {25,80,0,0},
 468                         rs_write,
 469                         rs_queues+0+i*3,rs_queues+1+i*3,rs_queues+2+i*3
 470                 };
 471         }
 472         for (i = 0 ; i<NR_PTYS ; i++) {
 473                 mpty_table[i] = (struct tty_struct) {
 474                         {0, /* no translation */
 475                         0,  /* no translation */
 476                         B9600 | CS8,
 477                         0,
 478                         0,
 479                         INIT_C_CC},
 480                         -1,
 481                         0,
 482                         0,
 483                         0,
 484                         {25,80,0,0},
 485                         mpty_write,
 486                         mpty_queues+0+i*3,mpty_queues+1+i*3,mpty_queues+2+i*3
 487                 };
 488                 spty_table[i] = (struct tty_struct) {
 489                         {0, /* no translation */
 490                         0,  /* no translation */
 491                         B9600 | CS8,
 492                         IXON | ISIG | ICANON,
 493                         0,
 494                         INIT_C_CC},
 495                         -1,
 496                         0,
 497                         0,
 498                         0,
 499                         {25,80,0,0},
 500                         spty_write,
 501                         spty_queues+0+i*3,spty_queues+1+i*3,spty_queues+2+i*3
 502                 };
 503         }
 504         rs_init();
 505         printk("%d virtual consoles\n\r",NR_CONSOLES);
 506         printk("%d pty's\n\r",NR_PTYS);
 507 }

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