Fork me on GitHub

root/drivers/char/tty.c

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

DEFINITIONS

This source file includes following definitions.
  1. wait_vtime_off
  2. termios2termio
  3. termio2termios
  4. opost
  5. out_char
  6. erase_char
  7. set_termios
  8. set_termio
  9. register_tty
  10. get_tty
  11. disassociate_ctty
  12. termios_reset
  13. do_cook
  14. tty_open
  15. tty_close
  16. tty_read
  17. tty_write
  18. tty_ioctl
  19. tty_lseek
  20. tty_select
  21. tty_init

   1 /*
   2  * fiwix/drivers/char/tty.c
   3  *
   4  * Copyright 2018-2022, Jordi Sanfeliu. All rights reserved.
   5  * Distributed under the terms of the Fiwix License.
   6  */
   7 
   8 #include <fiwix/kernel.h>
   9 #include <fiwix/ioctl.h>
  10 #include <fiwix/tty.h>
  11 #include <fiwix/ctype.h>
  12 #include <fiwix/console.h>
  13 #include <fiwix/devices.h>
  14 #include <fiwix/fs.h>
  15 #include <fiwix/errno.h>
  16 #include <fiwix/sched.h>
  17 #include <fiwix/timer.h>
  18 #include <fiwix/sleep.h>
  19 #include <fiwix/process.h>
  20 #include <fiwix/fcntl.h>
  21 #include <fiwix/kd.h>
  22 #include <fiwix/stdio.h>
  23 #include <fiwix/string.h>
  24 
  25 struct tty tty_table[NR_TTYS];
  26 extern short int current_cons;
  27 
  28 static void wait_vtime_off(unsigned int arg)
  29 {
  30         unsigned int *fn = (unsigned int *)arg;
  31 
  32         wakeup(fn);
  33 }
  34 
  35 static void termios2termio(struct termios *termios, struct termio *termio)
  36 {
  37         int n;
  38 
  39         termio->c_iflag = termios->c_iflag;
  40         termio->c_oflag = termios->c_oflag;
  41         termio->c_cflag = termios->c_cflag;
  42         termio->c_lflag = termios->c_lflag;
  43         termio->c_line = termios->c_line;
  44         for(n = 0; n < NCC; n++) {
  45                 termio->c_cc[n] = termios->c_cc[n];
  46         }
  47 }
  48 
  49 static void termio2termios(struct termio *termio, struct termios *termios)
  50 {
  51         int n;
  52 
  53         termios->c_iflag = termio->c_iflag;
  54         termios->c_oflag = termio->c_oflag;
  55         termios->c_cflag = termio->c_cflag;
  56         termios->c_lflag = termio->c_lflag;
  57         termios->c_line = termio->c_line;
  58         for(n = 0; n < NCC; n++) {
  59                 termios->c_cc[n] = termio->c_cc[n];
  60         }
  61 }
  62 
  63 static int opost(struct tty *tty, unsigned char ch)
  64 {
  65         int status;
  66 
  67         status = 0;
  68 
  69         if(tty->termios.c_oflag & OPOST) {
  70                 switch(ch) {
  71                         case '\n':
  72                                 if(tty->termios.c_oflag & ONLCR) {
  73                                         if(tty_queue_room(&tty->write_q) >= 2) {
  74                                                 tty_queue_putchar(tty, &tty->write_q, '\r');
  75                                                 tty->column = 0;
  76                                         } else {
  77                                                 return -1;
  78                                         }
  79                                 }
  80                                 break;
  81                         case '\t':
  82                                 while(tty->column < (tty->winsize.ws_col - 1)) {
  83                                         if(tty->tab_stop[++tty->column]) {
  84                                                 break;
  85                                         }
  86                                 }
  87                                 break;
  88                         case '\b':
  89                                 if(tty->column > 0) {
  90                                         tty->column--;
  91                                 }
  92                         default:
  93                                 if(tty->termios.c_oflag & OLCUC) {
  94                                         ch = TOUPPER(ch);
  95                                 }
  96                                 if(!ISCNTRL(ch)) {
  97                                         tty->column++;
  98                                 }
  99                                 break;
 100                 }
 101         }
 102         if(tty_queue_putchar(tty, &tty->write_q, ch) < 0) {
 103                 status = -1;
 104         }
 105         return status;
 106 }
 107 
 108 static void out_char(struct tty *tty, unsigned char ch)
 109 {
 110         if(ISCNTRL(ch) && !ISSPACE(ch) && (tty->termios.c_lflag & ECHOCTL)) {
 111                 if(tty->flags & TTY_HAS_LNEXT || (!(tty->flags & TTY_HAS_LNEXT) && ch != tty->termios.c_cc[VEOF])) {
 112                         tty_queue_putchar(tty, &tty->write_q, '^');
 113                         tty_queue_putchar(tty, &tty->write_q, ch + 64);
 114                         tty->column += 2;
 115                 }
 116         } else {
 117                 opost(tty, ch);
 118         }
 119 }
 120 
 121 static void erase_char(struct tty *tty, unsigned char erasechar)
 122 {
 123         unsigned char ch;
 124 
 125         if(erasechar == tty->termios.c_cc[VERASE]) {
 126                 if((ch = tty_queue_unputchar(&tty->cooked_q))) {
 127                         if(tty->termios.c_lflag & ECHO) {
 128                                 tty_queue_putchar(tty, &tty->write_q, '\b');
 129                                 tty_queue_putchar(tty, &tty->write_q, ' ');
 130                                 tty_queue_putchar(tty, &tty->write_q, '\b');
 131                                 if(ch == '\t') {
 132                                         tty->deltab(tty);
 133                                 }
 134                                 if(ISCNTRL(ch) && !ISSPACE(ch) && tty->termios.c_lflag & ECHOCTL) {
 135                                         tty_queue_putchar(tty, &tty->write_q, '\b');
 136                                         tty_queue_putchar(tty, &tty->write_q, ' ');
 137                                         tty_queue_putchar(tty, &tty->write_q, '\b');
 138                                 }
 139                         }
 140                 }
 141         }
 142         if(erasechar == tty->termios.c_cc[VWERASE]) {
 143                 unsigned char word_seen = 0;
 144 
 145                 while(tty->cooked_q.count > 0) {
 146                         ch = LAST_CHAR(&tty->cooked_q);
 147                         if((ch == ' ' || ch == '\t') && word_seen) {
 148                                 break;
 149                         }
 150                         if(ch != ' ' && ch != '\t') {
 151                                 word_seen = 1;
 152                         }
 153                         erase_char(tty, tty->termios.c_cc[VERASE]);
 154                 }
 155         }
 156         if(erasechar == tty->termios.c_cc[VKILL]) {
 157                 while(tty->cooked_q.count > 0) {
 158                         erase_char(tty, tty->termios.c_cc[VERASE]);
 159                 }
 160                 if(tty->termios.c_lflag & ECHOK && !(tty->termios.c_lflag & ECHOE)) {
 161                         tty_queue_putchar(tty, &tty->write_q, '\n');
 162                 }
 163         }
 164 }
 165 
 166 static void set_termios(struct tty *tty, struct termios *new_termios)
 167 {
 168         memcpy_b(&tty->termios, new_termios, sizeof(struct termios));
 169         if(tty->set_termios) {
 170                 tty->set_termios(tty);
 171         }
 172 }
 173 
 174 static void set_termio(struct tty *tty, struct termio *new_termio)
 175 {
 176         struct termios new_termios;
 177 
 178         termio2termios(new_termio, &new_termios);
 179         memcpy_b(&tty->termios, &new_termios, sizeof(struct termios));
 180 }
 181 
 182 int register_tty(__dev_t dev)
 183 {
 184         int n;
 185 
 186         for(n = 0; n < NR_TTYS; n++) {
 187                 if(tty_table[n].dev == dev) {
 188                         printk("ERROR: %s(): tty device %d,%d already registered!\n", __FUNCTION__, MAJOR(dev), MINOR(dev));
 189                         return 1;
 190                 }
 191                 if(!tty_table[n].dev) {
 192                         tty_table[n].dev = dev;
 193                         tty_table[n].count = 0;
 194                         return 0;
 195                 }
 196         }
 197         printk("ERROR: %s(): tty table is full!\n", __FUNCTION__);
 198         return 1;
 199 }
 200 
 201 struct tty *get_tty(__dev_t dev)
 202 {
 203         int n;
 204 
 205         if(!dev) {
 206                 return NULL;
 207         }
 208 
 209         /* /dev/console = system console */
 210         if(dev == MKDEV(SYSCON_MAJOR, 1)) {
 211                 dev = (__dev_t)_syscondev;
 212         }
 213 
 214         /* /dev/tty0 = current virtual console */
 215         if(dev == MKDEV(VCONSOLES_MAJOR, 0)) {
 216                 dev = MKDEV(VCONSOLES_MAJOR, current_cons);
 217         }
 218 
 219         /* /dev/tty = controlling TTY device */
 220         if(dev == MKDEV(SYSCON_MAJOR, 0)) {
 221                 if(!current->ctty) {
 222                         return NULL;
 223                 }
 224                 dev = current->ctty->dev;
 225         }
 226 
 227         for(n = 0; n < NR_TTYS; n++) {
 228                 if(tty_table[n].dev == dev) {
 229                         return &tty_table[n];
 230                 }
 231         }
 232         return NULL;
 233 }
 234 
 235 void disassociate_ctty(struct tty *tty)
 236 {
 237         struct proc *p;
 238 
 239         if(!tty) {
 240                 return;
 241         }
 242 
 243         /* this tty is no longer the controlling tty of any session */
 244         tty->pgid = tty->sid = 0;
 245 
 246         /* clear the controlling tty for all processes in the same SID */
 247         FOR_EACH_PROCESS(p) {
 248                 if(p->sid == current->sid) {
 249                         p->ctty = NULL;
 250                 }
 251                 p = p->next;
 252         }
 253         kill_pgrp(current->pgid, SIGHUP, KERNEL);
 254         kill_pgrp(current->pgid, SIGCONT, KERNEL);
 255 }
 256 
 257 void termios_reset(struct tty *tty)
 258 {
 259         tty->kbd.mode = K_XLATE;
 260         tty->termios.c_iflag = ICRNL | IXON | IXOFF;
 261         tty->termios.c_oflag = OPOST | ONLCR;
 262         tty->termios.c_cflag = B9600 | CS8 | HUPCL | CREAD | CLOCAL;
 263         tty->termios.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE | IEXTEN;
 264         tty->termios.c_line = 0;
 265         tty->termios.c_cc[VINTR]    = 3;        /* ^C */
 266         tty->termios.c_cc[VQUIT]    = 28;       /* ^\ */
 267         tty->termios.c_cc[VERASE]   = BS;       /* ^? (127) not '\b' (^H) */
 268         tty->termios.c_cc[VKILL]    = 21;       /* ^U */
 269         tty->termios.c_cc[VEOF]     = 4;        /* ^D */
 270         tty->termios.c_cc[VTIME]    = 0;
 271         tty->termios.c_cc[VMIN]     = 1;
 272         tty->termios.c_cc[VSWTC]    = 0;
 273         tty->termios.c_cc[VSTART]   = 17;       /* ^Q */
 274         tty->termios.c_cc[VSTOP]    = 19;       /* ^S */
 275         tty->termios.c_cc[VSUSP]    = 26;       /* ^Z */
 276         tty->termios.c_cc[VEOL]     = '\n';     /* ^J */
 277         tty->termios.c_cc[VREPRINT] = 18;       /* ^R */
 278         tty->termios.c_cc[VDISCARD] = 15;       /* ^O */
 279         tty->termios.c_cc[VWERASE]  = 23;       /* ^W */
 280         tty->termios.c_cc[VLNEXT]   = 22;       /* ^V */
 281         tty->termios.c_cc[VEOL2]    = 0;        
 282 }
 283 
 284 void do_cook(struct tty *tty)
 285 {
 286         int n;
 287         unsigned char ch;
 288         struct cblock *cb;
 289 
 290         while(tty->read_q.count > 0) {
 291                 ch = tty_queue_getchar(&tty->read_q);
 292 
 293                 if((tty->termios.c_lflag & ISIG) && !(tty->flags & TTY_HAS_LNEXT)) {
 294                         if(ch == tty->termios.c_cc[VINTR]) {
 295                                 if(!(tty->termios.c_lflag & NOFLSH)) {
 296                                         tty_queue_flush(&tty->read_q);
 297                                         tty_queue_flush(&tty->cooked_q);
 298                                 }
 299                                 if(tty->pgid > 0) {
 300                                         kill_pgrp(tty->pgid, SIGINT, KERNEL);
 301                                 }
 302                                 break;
 303                         }
 304                         if(ch == tty->termios.c_cc[VQUIT]) {
 305                                 if(tty->pgid > 0) {
 306                                         kill_pgrp(tty->pgid, SIGQUIT, KERNEL);
 307                                 }
 308                                 break;
 309                         }
 310                         if(ch == tty->termios.c_cc[VSUSP]) {
 311                                 if(tty->pgid > 0) {
 312                                         kill_pgrp(tty->pgid, SIGTSTP, KERNEL);
 313                                 }
 314                                 break;
 315                         }
 316                 }
 317 
 318                 if(tty->termios.c_iflag & ISTRIP) {
 319                         ch = TOASCII(ch);
 320                 }
 321                 if(tty->termios.c_iflag & IUCLC) {
 322                         if(ISUPPER(ch)) {
 323                                 ch = TOLOWER(ch);
 324                         }
 325                 }
 326 
 327                 if(!(tty->flags & TTY_HAS_LNEXT)) {
 328                         if(ch == '\r') {
 329                                 if(tty->termios.c_iflag & IGNCR) {
 330                                         continue;
 331                                 }
 332                                 if(tty->termios.c_iflag & ICRNL) {
 333                                         ch = '\n';
 334                                 }
 335                         } else {
 336                                 if(ch == '\n') {
 337                                         if(tty->termios.c_iflag & INLCR) {
 338                                                 ch = '\r';
 339                                         }
 340                                 }
 341                         }
 342                 }
 343 
 344                 if(tty->termios.c_lflag & ICANON && !(tty->flags & TTY_HAS_LNEXT)) {
 345                         if(ch == tty->termios.c_cc[VERASE] || ch == tty->termios.c_cc[VWERASE] || ch == tty->termios.c_cc[VKILL]) {
 346                                 erase_char(tty, ch);
 347                                 continue;
 348                         }
 349 
 350                         if(ch == tty->termios.c_cc[VREPRINT]) {
 351                                 out_char(tty, ch);
 352                                 tty_queue_putchar(tty, &tty->write_q, '\n');
 353                                 cb = tty->cooked_q.head;
 354                                 while(cb) {
 355                                         for(n = 0; n < cb->end_off; n++) {
 356                                                 if(n >= cb->start_off) {
 357                                                         out_char(tty, cb->data[n]);
 358                                                 }
 359                                         }
 360                                         cb = cb->next;
 361                                 }
 362                                 continue;
 363                         }
 364 
 365                         if(ch == tty->termios.c_cc[VLNEXT] && tty->termios.c_lflag & IEXTEN) {
 366                                 tty->flags |= TTY_HAS_LNEXT;
 367                                 if(tty->termios.c_lflag & ECHOCTL) {
 368                                         tty_queue_putchar(tty, &tty->write_q, '^');
 369                                         tty_queue_putchar(tty, &tty->write_q, '\b');
 370                                 }
 371                                 break;
 372                         }
 373 
 374                         if(tty->termios.c_iflag & IXON) {
 375                                 if(ch == tty->termios.c_cc[VSTART]) {
 376                                         tty->start(tty);
 377                                         continue;
 378                                 }
 379                                 if(ch == tty->termios.c_cc[VSTOP]) {
 380                                         tty->stop(tty);
 381                                         continue;
 382                                 }
 383                                 if(tty->termios.c_iflag & IXANY) {
 384                                         tty->start(tty);
 385                                 }
 386                         }
 387                 }
 388 
 389                 /* FIXME: using ISSPACE here makes LNEXT working incorrectly */
 390                 if(tty->termios.c_lflag & ICANON) {
 391                         if(ISCNTRL(ch) && !ISSPACE(ch) && (tty->termios.c_lflag & ECHOCTL)) {
 392                                 out_char(tty, ch);
 393                                 tty_queue_putchar(tty, &tty->cooked_q, ch);
 394                                 tty->flags &= ~TTY_HAS_LNEXT;
 395                                 continue;
 396                         }
 397                         if(ch == '\n') {
 398                                 tty->canon_data = 1;
 399                         }
 400                 }
 401 
 402                 if(tty->termios.c_lflag & ECHO) {
 403                         out_char(tty, ch);
 404                 } else {
 405                         if((tty->termios.c_lflag & ECHONL) && (ch == '\n')) {
 406                                 out_char(tty, ch);
 407                         }
 408                 }
 409                 tty_queue_putchar(tty, &tty->cooked_q, ch);
 410                 tty->flags &= ~TTY_HAS_LNEXT;
 411         }
 412         tty->output(tty);
 413         if(!(tty->termios.c_lflag & ICANON) || ((tty->termios.c_lflag & ICANON) && tty->canon_data)) {
 414                 wakeup(&do_select);
 415         }
 416         wakeup(&tty_read);
 417 }
 418 
 419 int tty_open(struct inode *i, struct fd *fd_table)
 420 {
 421         int noctty_flag;
 422         struct tty *tty;
 423         int errno;
 424          
 425         noctty_flag = fd_table->flags & O_NOCTTY;
 426 
 427         if(MAJOR(i->rdev) == SYSCON_MAJOR && MINOR(i->rdev) == 0) {
 428                 if(!current->ctty) {
 429                         return -ENXIO;
 430                 }
 431         }
 432 
 433         if(MAJOR(i->rdev) == VCONSOLES_MAJOR && MINOR(i->rdev) == 0) {
 434                 noctty_flag = 1;
 435         }
 436 
 437         if(!(tty = get_tty(i->rdev))) {
 438                 printk("%s(): oops! (%x)\n", __FUNCTION__, i->rdev);
 439                 printk("_syscondev = %x\n", _syscondev);
 440                 return -ENXIO;
 441         }
 442 
 443         if(tty->open) {
 444                 if((errno = tty->open(tty)) < 0) {
 445                         return errno;
 446                 }
 447         }
 448         tty->count++;
 449         tty->column = 0;
 450 
 451         if(SESS_LEADER(current) && !current->ctty && !noctty_flag && !tty->sid) {
 452                 current->ctty = tty;
 453                 tty->sid = current->sid;
 454                 tty->pgid = current->pgid;
 455         }
 456         return 0;
 457 }
 458 
 459 int tty_close(struct inode *i, struct fd *fd_table)
 460 {
 461         struct proc *p;
 462         struct tty *tty;
 463         int errno;
 464 
 465         if(!(tty = get_tty(i->rdev))) {
 466                 printk("%s(): oops! (%x)\n", __FUNCTION__, i->rdev);
 467                 return -ENXIO;
 468         }
 469 
 470         if(tty->close) {
 471                 if((errno = tty->close(tty)) < 0) {
 472                         return errno;
 473                 }
 474         }
 475         tty->count--;
 476         if(!tty->count) {
 477                 termios_reset(tty);
 478                 tty->pgid = tty->sid = 0;
 479 
 480                 /* this tty is no longer the controlling tty of any process */
 481                 FOR_EACH_PROCESS(p) {
 482                         if(p->ctty == tty) {
 483                                 p->ctty = NULL;
 484                         }
 485                         p = p->next;
 486                 }
 487         }
 488         return 0;
 489 }
 490 
 491 int tty_read(struct inode *i, struct fd *fd_table, char *buffer, __size_t count)
 492 {
 493         unsigned int min;
 494         unsigned char ch;
 495         struct tty *tty;
 496         struct callout_req creq;
 497         int n;
 498 
 499         if(!(tty = get_tty(i->rdev))) {
 500                 printk("%s(): oops! (%x)\n", __FUNCTION__, i->rdev);
 501                 return -ENXIO;
 502         }
 503 
 504         /* only the foreground process group is allowed to read from the tty */
 505         if(current->ctty == tty && current->pgid != tty->pgid) {
 506                 if(current->sigaction[SIGTTIN - 1].sa_handler == SIG_IGN || current->sigblocked & (1 << (SIGTTIN - 1)) || is_orphaned_pgrp(current->pgid)) {
 507                         return -EIO;
 508                 }
 509                 kill_pgrp(current->pgid, SIGTTIN, KERNEL);
 510                 return -ERESTART;
 511         }
 512 
 513         n = min = 0;
 514         while(count > 0) {
 515                 if(tty->kbd.mode == K_RAW || tty->kbd.mode == K_MEDIUMRAW) {
 516                         n = 0;
 517                         while(n < count) {
 518                                 if((ch = tty_queue_getchar(&tty->read_q))) {
 519                                         buffer[n++] = ch;
 520                                 } else {
 521                                         break;
 522                                 }
 523                         }
 524                         if(n) {
 525                                 break;
 526                         }
 527                 }
 528 
 529                 if(tty->termios.c_lflag & ICANON) {
 530                         if((ch = LAST_CHAR(&tty->cooked_q))) {
 531                                 if(ch == '\n' || ch == tty->termios.c_cc[VEOL] || ch == tty->termios.c_cc[VEOF] || (tty->termios.c_lflag & IEXTEN && ch == tty->termios.c_cc[VEOL2] && tty->termios.c_cc[VEOL2] != 0)) {
 532 
 533                                         tty->canon_data = 0;
 534                                         /* EOF is not passed to the reading process */
 535                                         if(ch == tty->termios.c_cc[VEOF]) {
 536                                                 tty_queue_unputchar(&tty->cooked_q);
 537                                         }
 538 
 539                                         while(n < count) {
 540                                                 if((ch = tty_queue_getchar(&tty->cooked_q))) {
 541                                                         buffer[n++] = ch;
 542                                                 } else {
 543                                                         break;
 544                                                 }
 545                                         }
 546                                         break;
 547                                 }
 548                         }
 549                 } else {
 550                         if(tty->termios.c_cc[VTIME] > 0) {
 551                                 unsigned int ini_ticks = kstat.ticks;
 552                                 unsigned int timeout;
 553 
 554                                 if(!tty->termios.c_cc[VMIN]) {
 555                                         /* VTIME is measured in tenths of second */
 556                                         timeout = tty->termios.c_cc[VTIME] * (HZ / 10);
 557 
 558                                         while(kstat.ticks - ini_ticks < timeout && !tty->cooked_q.count) {
 559                                                 creq.fn = wait_vtime_off;
 560                                                 creq.arg = (unsigned int)&tty->cooked_q;
 561                                                 add_callout(&creq, timeout);
 562                                                 if(fd_table->flags & O_NONBLOCK) {
 563                                                         return -EAGAIN;
 564                                                 }
 565                                                 if(sleep(&tty_read, PROC_INTERRUPTIBLE)) {
 566                                                         return -EINTR;
 567                                                 }
 568                                         }
 569                                         while(n < count) {
 570                                                 if((ch = tty_queue_getchar(&tty->cooked_q))) {
 571                                                         buffer[n++] = ch;
 572                                                 } else {
 573                                                         break;
 574                                                 }
 575                                         }
 576                                         break;
 577                                 } else {
 578                                         if(tty->cooked_q.count > 0) {
 579                                                 if(n < MIN(tty->termios.c_cc[VMIN], count)) {
 580                                                         ch = tty_queue_getchar(&tty->cooked_q);
 581                                                         buffer[n++] = ch;
 582                                                 }
 583                                                 if(n >= MIN(tty->termios.c_cc[VMIN], count)) {
 584                                                         del_callout(&creq);
 585                                                         break;
 586                                                 }
 587                                                 timeout = tty->termios.c_cc[VTIME] * (HZ / 10);
 588                                                 creq.fn = wait_vtime_off;
 589                                                 creq.arg = (unsigned int)&tty->cooked_q;
 590                                                 add_callout(&creq, timeout);
 591                                                 if(fd_table->flags & O_NONBLOCK) {
 592                                                         n = -EAGAIN;
 593                                                         break;
 594                                                 }
 595                                                 if(sleep(&tty_read, PROC_INTERRUPTIBLE)) {
 596                                                         n = -EINTR;
 597                                                         break;
 598                                                 }
 599                                                 if(!tty->cooked_q.count) {
 600                                                         break;
 601                                                 }
 602                                                 continue;
 603                                         }
 604                                 }
 605                         } else {
 606                                 if(tty->cooked_q.count > 0) {
 607                                         if(min < tty->termios.c_cc[VMIN] || !tty->termios.c_cc[VMIN]) {
 608                                                 if(n < count) {
 609                                                         ch = tty_queue_getchar(&tty->cooked_q);
 610                                                         buffer[n++] = ch;
 611                                                         if(--tty->canon_data < 0) {
 612                                                                 tty->canon_data = 0;
 613                                                         }
 614                                                 }
 615                                                 min++;
 616                                         }
 617                                 }
 618                                 if(min >= tty->termios.c_cc[VMIN]) {
 619                                         break;
 620                                 }
 621                         }
 622                 }
 623                 if(fd_table->flags & O_NONBLOCK) {
 624                         n = -EAGAIN;
 625                         break;
 626                 }
 627                 if(sleep(&tty_read, PROC_INTERRUPTIBLE)) {
 628                         n = -EINTR;
 629                         break;
 630                 }
 631         }
 632 
 633         if(n) {
 634                 i->i_atime = CURRENT_TIME;
 635         }
 636         return n;
 637 }
 638 
 639 int tty_write(struct inode *i, struct fd *fd_table, const char *buffer, __size_t count)
 640 {
 641         unsigned char ch;
 642         struct tty *tty;
 643         int n;
 644 
 645         if(!(tty = get_tty(i->rdev))) {
 646                 printk("%s(): oops! (%x)\n", __FUNCTION__, i->rdev);
 647                 return -ENXIO;
 648         }
 649 
 650         /* only the foreground process group is allowed to write to the tty */
 651         if(current->ctty == tty && current->pgid != tty->pgid) {
 652                 if(tty->termios.c_lflag & TOSTOP) {
 653                         if(current->sigaction[SIGTTIN - 1].sa_handler != SIG_IGN && !(current->sigblocked & (1 << (SIGTTIN - 1)))) {
 654                                 if(is_orphaned_pgrp(current->pgid)) {
 655                                         return -EIO;
 656                                 }
 657                                 kill_pgrp(current->pgid, SIGTTOU, KERNEL);
 658                                 return -ERESTART;
 659                         }
 660                 }
 661         }
 662 
 663         n = 0;
 664         for(;;) {
 665                 if(current->sigpending & ~current->sigblocked) {
 666                         return -ERESTART;
 667                 }
 668                 while(count && n < count) {
 669                         ch = *(buffer + n);
 670                         /* FIXME: check if *(buffer + n) address is valid */
 671                         if(opost(tty, ch) < 0) {
 672                                 break;
 673                         }
 674                         n++;
 675                 }
 676                 tty->output(tty);
 677                 if(n == count) {
 678                         break;
 679                 }
 680                 if(fd_table->flags & O_NONBLOCK) {
 681                         n = -EAGAIN;
 682                         break;
 683                 }
 684                 if(tty->write_q.count > 0) {
 685                         if(sleep(&tty_write, PROC_INTERRUPTIBLE)) {
 686                                 n = -EINTR;
 687                                 break;
 688                         }
 689                 }
 690                 if(need_resched) {
 691                         do_sched();
 692                 }
 693         }
 694 
 695         if(n) {
 696                 i->i_mtime = CURRENT_TIME;
 697         }
 698         return n;
 699 }
 700 
 701 /* FIXME: http://www.lafn.org/~dave/linux/termios.txt (doc/termios.txt) */
 702 int tty_ioctl(struct inode *i, int cmd, unsigned long int arg)
 703 {
 704         struct proc *p;
 705         struct tty *tty;
 706         int errno;
 707 
 708         if(!(tty = get_tty(i->rdev))) {
 709                 printk("%s(): oops! (%x)\n", __FUNCTION__, i->rdev);
 710                 return -ENXIO;
 711         }
 712 
 713         switch(cmd) {
 714                 /*
 715                  * Fetch and store the current terminal parameters to a termios
 716                  * structure pointed to by the argument.
 717                  */
 718                 case TCGETS:
 719                         if((errno = check_user_area(VERIFY_WRITE, (void *)arg, sizeof(struct termios)))) {
 720                                 return errno;
 721                         }
 722                         memcpy_b((struct termios *)arg, &tty->termios, sizeof(struct termios));
 723                         break;
 724 
 725                 /*
 726                  * Set the current terminal parameters according to the
 727                  * values in the termios structure pointed to by the argument.
 728                  */
 729                 case TCSETS:
 730                         if((errno = check_user_area(VERIFY_READ, (void *)arg, sizeof(struct termios)))) {
 731                                 return errno;
 732                         }
 733                         set_termios(tty, (struct termios *)arg);
 734                         break;
 735 
 736                 /*
 737                  * Same as TCSETS except it doesn't take effect until all
 738                  * the characters queued for output have been transmitted.
 739                  */
 740                 case TCSETSW:
 741                         if((errno = check_user_area(VERIFY_READ, (void *)arg, sizeof(struct termios)))) {
 742                                 return errno;
 743                         }
 744                         /* not tested */
 745                         while(tty->write_q.count) {
 746                                 if(sleep(&tty_write, PROC_INTERRUPTIBLE)) {
 747                                         return -EINTR;
 748                                 }
 749                                 do_sched();
 750                         }
 751                         set_termios(tty, (struct termios *)arg);
 752                         break;
 753 
 754                 /*
 755                  * Same as TCSETSW except that all characters queued for
 756                  * input are discarded.
 757                  */
 758                 case TCSETSF:
 759                         if((errno = check_user_area(VERIFY_READ, (void *)arg, sizeof(struct termios)))) {
 760                                 return errno;
 761                         }
 762                         /* not tested */
 763                         while(tty->write_q.count) {
 764                                 if(sleep(&tty_write, PROC_INTERRUPTIBLE)) {
 765                                         return -EINTR;
 766                                 }
 767                                 do_sched();
 768                         }
 769                         set_termios(tty, (struct termios *)arg);
 770                         tty_queue_flush(&tty->read_q);
 771                         break;
 772 
 773                 /*
 774                  * Fetch and store the current terminal parameters to a termio
 775                  * structure pointed to by the argument.
 776                  */
 777                 case TCGETA:
 778                         if((errno = check_user_area(VERIFY_WRITE, (void *)arg, sizeof(struct termio)))) {
 779                                 return errno;
 780                         }
 781                         termios2termio(&tty->termios, (struct termio *)arg);
 782                         break;
 783 
 784                 /*
 785                  * Set the current terminal parameters according to the
 786                  * values in the termio structure pointed to by the argument.
 787                  */
 788                 case TCSETA:
 789                         if((errno = check_user_area(VERIFY_READ, (void *)arg, sizeof(struct termio)))) {
 790                                 return errno;
 791                         }
 792                         set_termio(tty, (struct termio *)arg);
 793                         break;
 794 
 795                 /*
 796                  * Same as TCSET except it doesn't take effect until all
 797                  * the characters queued for output have been transmitted.
 798                  */
 799                 case TCSETAW:
 800                         if((errno = check_user_area(VERIFY_READ, (void *)arg, sizeof(struct termio)))) {
 801                                 return errno;
 802                         }
 803                         /* not tested */
 804                         while(tty->write_q.count) {
 805                                 if(sleep(&tty_write, PROC_INTERRUPTIBLE)) {
 806                                         return -EINTR;
 807                                 }
 808                                 do_sched();
 809                         }
 810                         set_termio(tty, (struct termio *)arg);
 811                         break;
 812 
 813                 /*
 814                  * Same as TCSETAW except that all characters queued for
 815                  * input are discarded.
 816                  */
 817                 case TCSETAF:
 818                         if((errno = check_user_area(VERIFY_READ, (void *)arg, sizeof(struct termio)))) {
 819                                 return errno;
 820                         }
 821                         /* not tested */
 822                         while(tty->write_q.count) {
 823                                 if(sleep(&tty_write, PROC_INTERRUPTIBLE)) {
 824                                         return -EINTR;
 825                                 }
 826                                 do_sched();
 827                         }
 828                         set_termio(tty, (struct termio *)arg);
 829                         tty_queue_flush(&tty->read_q);
 830                         break;
 831 
 832                 /* Perform start/stop control */
 833                 case TCXONC:
 834                         switch(arg) {
 835                                 case TCOOFF:
 836                                         tty->stop(tty);
 837                                         break;
 838                                 case TCOON:
 839                                         tty->start(tty);
 840                                         break;
 841                                 default:
 842                                         return -EINVAL;
 843                         }
 844                         break;
 845                 case TCFLSH:
 846                         switch(arg) {
 847                                 case TCIFLUSH:
 848                                         tty_queue_flush(&tty->read_q);
 849                                         tty_queue_flush(&tty->cooked_q);
 850                                         break;
 851                                 case TCOFLUSH:
 852                                         tty_queue_flush(&tty->write_q);
 853                                         break;
 854                                 case TCIOFLUSH:
 855                                         tty_queue_flush(&tty->read_q);
 856                                         tty_queue_flush(&tty->cooked_q);
 857                                         tty_queue_flush(&tty->write_q);
 858                                         break;
 859                                 default:
 860                                         return -EINVAL;
 861                         }
 862                         break;
 863                 case TIOCSCTTY:
 864                         if(SESS_LEADER(current) && (current->sid == tty->sid)) {
 865                                 return 0;
 866                         }
 867                         if(!SESS_LEADER(current) || current->ctty) {
 868                                 return -EPERM;
 869                         }
 870                         if(tty->sid) {
 871                                 if((arg == 1) && IS_SUPERUSER) {
 872                                         FOR_EACH_PROCESS(p) {
 873                                                 if(p->ctty == tty) {
 874                                                         p->ctty = NULL;
 875                                                 }
 876                                                 p = p->next;
 877                                         }
 878                                 } else {
 879                                         return -EPERM;
 880                                 }
 881                         }
 882                         current->ctty = tty;
 883                         tty->sid = current->sid;
 884                         tty->pgid = current->pgid;
 885                         break;
 886 
 887                 /*
 888                  * Get the process group ID of the '__pid_t' pointed to by
 889                  * the arg to the foreground processes group ID.
 890                  */
 891                 case TIOCGPGRP:
 892                         if((errno = check_user_area(VERIFY_WRITE, (void *)arg, sizeof(__pid_t)))) {
 893                                 return errno;
 894                         }
 895                         memcpy_b((void *)arg, &tty->pgid, sizeof(__pid_t));
 896                         break;
 897 
 898                 /*
 899                  * Associate the process pointed to by '__pid_t' in the arg to
 900                  * the value of the terminal.
 901                  */
 902                 case TIOCSPGRP:
 903                         if(arg < 1) {
 904                                 return -EINVAL;
 905                         }
 906                         if((errno = check_user_area(VERIFY_READ, (void *)arg, sizeof(__pid_t)))) {
 907                                 return errno;
 908                         }
 909                         memcpy_b(&tty->pgid, (void *)arg, sizeof(__pid_t));
 910                         break;
 911 
 912                 /*
 913                  * The session ID of the terminal is fetched and stored in
 914                  * the '__pid_t' pointed to by the arg.
 915                 case TIOCSID:   FIXME
 916                  */
 917 
 918                 /*
 919                  * The terminal drivers notion of terminal size is stored in
 920                  * the 'winsize' structure pointed to by the arg.
 921                  */
 922                 case TIOCGWINSZ:
 923                         if((errno = check_user_area(VERIFY_WRITE, (void *)arg, sizeof(struct winsize)))) {
 924                                 return errno;
 925                         }
 926                         memcpy_b((void *)arg, &tty->winsize, sizeof(struct winsize));
 927                         break;
 928 
 929                 /*
 930                  * The terminal drivers notion of the terminal size is set
 931                  * to value in the 'winsize' structure pointed to by the arg.
 932                  */
 933                 case TIOCSWINSZ:
 934                 {
 935                         struct winsize *ws = (struct winsize *)arg;
 936                         short int changed;
 937 
 938                         if((errno = check_user_area(VERIFY_READ, (void *)arg, sizeof(struct winsize)))) {
 939                                 return errno;
 940                         }
 941                         changed = 0;
 942                         if(tty->winsize.ws_row != ws->ws_row) {
 943                                 changed = 1;
 944                         }
 945                         if(tty->winsize.ws_col != ws->ws_col) {
 946                                 changed = 1;
 947                         }
 948                         if(tty->winsize.ws_xpixel != ws->ws_xpixel) {
 949                                 changed = 1;
 950                         }
 951                         if(tty->winsize.ws_ypixel != ws->ws_ypixel) {
 952                                 changed = 1;
 953                         }
 954                         tty->winsize.ws_row = ws->ws_row;
 955                         tty->winsize.ws_col = ws->ws_col;
 956                         tty->winsize.ws_xpixel = ws->ws_xpixel;
 957                         tty->winsize.ws_ypixel = ws->ws_ypixel;
 958                         if(changed) {
 959                                 kill_pgrp(tty->pgid, SIGWINCH, KERNEL);
 960                         }
 961                 }
 962                         break;
 963                 case TIOCNOTTY:
 964                         if(current->ctty != tty) {
 965                                 return -ENOTTY;
 966                         }
 967                         if(SESS_LEADER(current)) {
 968                                 disassociate_ctty(tty);
 969                         }
 970                         break;
 971                 case TIOCLINUX:
 972                 {
 973                         int val = *(unsigned char *)arg;
 974                         if((errno = check_user_area(VERIFY_READ, (void *)arg, sizeof(unsigned char)))) {
 975                                 return errno;
 976                         }
 977                         switch(val) {
 978                                 case 12:        /* get current console */
 979                                         return current_cons;
 980                                         break;
 981                                 default:
 982                                         return -EINVAL;
 983                                         break;
 984                         }
 985                         break;
 986                 }
 987 
 988                 default:
 989                         return vt_ioctl(tty, cmd, arg);
 990         }
 991         return 0;
 992 }
 993 
 994 int tty_lseek(struct inode *i, __off_t offset)
 995 {
 996         return -ESPIPE;
 997 }
 998 
 999 int tty_select(struct inode *i, int flag)
1000 {
1001         struct tty *tty;
1002 
1003         if(!(tty = get_tty(i->rdev))) {
1004                 printk("%s(): oops! (%x)\n", __FUNCTION__, i->rdev);
1005                 return 0;
1006         }
1007 
1008         switch(flag) {
1009                 case SEL_R:
1010                         if(tty->cooked_q.count > 0) {
1011                                 if(!(tty->termios.c_lflag & ICANON) || ((tty->termios.c_lflag & ICANON) && tty->canon_data)) {
1012                                         return 1;
1013                                 }
1014                         }
1015                         break;
1016                 case SEL_W:
1017                         if(!tty->write_q.count) {
1018                                 return 1;
1019                         }
1020                         break;
1021         }
1022         return 0;
1023 }
1024 
1025 void tty_init(void)
1026 {
1027         tty_queue_init();
1028         memset_b(tty_table, 0, sizeof(tty_table));
1029 }

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