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

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