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-2021, 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/pic.h>
  14 #include <fiwix/devices.h>
  15 #include <fiwix/fs.h>
  16 #include <fiwix/errno.h>
  17 #include <fiwix/sched.h>
  18 #include <fiwix/timer.h>
  19 #include <fiwix/sleep.h>
  20 #include <fiwix/process.h>
  21 #include <fiwix/fcntl.h>
  22 #include <fiwix/kd.h>
  23 #include <fiwix/stdio.h>
  24 #include <fiwix/string.h>
  25 
  26 struct tty tty_table[NR_TTYS];
  27 extern short int current_cons;
  28 
  29 static void wait_vtime_off(unsigned int arg)
  30 {
  31         unsigned int *fn = (unsigned int *)arg;
  32 
  33         wakeup(fn);
  34 }
  35 
  36 static void termios2termio(struct termios *termios, struct termio *termio)
  37 {
  38         int n;
  39 
  40         termio->c_iflag = termios->c_iflag;
  41         termio->c_oflag = termios->c_oflag;
  42         termio->c_cflag = termios->c_cflag;
  43         termio->c_lflag = termios->c_lflag;
  44         termio->c_line = termios->c_line;
  45         for(n = 0; n < NCC; n++) {
  46                 termio->c_cc[n] = termios->c_cc[n];
  47         }
  48 }
  49 
  50 static void termio2termios(struct termio *termio, struct termios *termios)
  51 {
  52         int n;
  53 
  54         termios->c_iflag = termio->c_iflag;
  55         termios->c_oflag = termio->c_oflag;
  56         termios->c_cflag = termio->c_cflag;
  57         termios->c_lflag = termio->c_lflag;
  58         termios->c_line = termio->c_line;
  59         for(n = 0; n < NCC; n++) {
  60                 termios->c_cc[n] = termio->c_cc[n];
  61         }
  62 }
  63 
  64 static int opost(struct tty *tty, unsigned char ch)
  65 {
  66         int status;
  67 
  68         status = 0;
  69 
  70         if(tty->termios.c_oflag & OPOST) {
  71                 switch(ch) {
  72                         case '\n':
  73                                 if(tty->termios.c_oflag & ONLCR) {
  74                                         if(tty_queue_room(&tty->write_q) >= 2) {
  75                                                 tty_queue_putchar(tty, &tty->write_q, '\r');
  76                                                 tty->column = 0;
  77                                         } else {
  78                                                 return -1;
  79                                         }
  80                                 }
  81                                 break;
  82                         case '\t':
  83                                 while(tty->column < (tty->winsize.ws_col - 1)) {
  84                                         if(tty->tab_stop[++tty->column]) {
  85                                                 break;
  86                                         }
  87                                 }
  88                                 break;
  89                         case '\b':
  90                                 if(tty->column > 0) {
  91                                         tty->column--;
  92                                 }
  93                         default:
  94                                 if(tty->termios.c_oflag & OLCUC) {
  95                                         ch = TOUPPER(ch);
  96                                 }
  97                                 if(!ISCNTRL(ch)) {
  98                                         tty->column++;
  99                                 }
 100                                 break;
 101                 }
 102         }
 103         if(tty_queue_putchar(tty, &tty->write_q, ch) < 0) {
 104                 status = -1;
 105         }
 106         return status;
 107 }
 108 
 109 static void out_char(struct tty *tty, unsigned char ch)
 110 {
 111         if(ISCNTRL(ch) && !ISSPACE(ch) && (tty->termios.c_lflag & ECHOCTL)) {
 112                 if(tty->lnext || (!tty->lnext && ch != tty->termios.c_cc[VEOF])) {
 113                         tty_queue_putchar(tty, &tty->write_q, '^');
 114                         tty_queue_putchar(tty, &tty->write_q, ch + 64);
 115                         tty->column += 2;
 116                 }
 117         } else {
 118                 opost(tty, ch);
 119         }
 120 }
 121 
 122 static void erase_char(struct tty *tty, unsigned char erasechar)
 123 {
 124         unsigned char ch;
 125 
 126         if(erasechar == tty->termios.c_cc[VERASE]) {
 127                 if((ch = tty_queue_unputchar(&tty->cooked_q))) {
 128                         if(tty->termios.c_lflag & ECHO) {
 129                                 tty_queue_putchar(tty, &tty->write_q, '\b');
 130                                 tty_queue_putchar(tty, &tty->write_q, ' ');
 131                                 tty_queue_putchar(tty, &tty->write_q, '\b');
 132                                 if(ch == '\t') {
 133                                         tty->deltab(tty);
 134                                 }
 135                                 if(ISCNTRL(ch) && !ISSPACE(ch) && tty->termios.c_lflag & ECHOCTL) {
 136                                         tty_queue_putchar(tty, &tty->write_q, '\b');
 137                                         tty_queue_putchar(tty, &tty->write_q, ' ');
 138                                         tty_queue_putchar(tty, &tty->write_q, '\b');
 139                                 }
 140                         }
 141                 }
 142         }
 143         if(erasechar == tty->termios.c_cc[VWERASE]) {
 144                 unsigned char word_seen = 0;
 145 
 146                 while(tty->cooked_q.count > 0) {
 147                         ch = LAST_CHAR(&tty->cooked_q);
 148                         if((ch == ' ' || ch == '\t') && word_seen) {
 149                                 break;
 150                         }
 151                         if(ch != ' ' && ch != '\t') {
 152                                 word_seen = 1;
 153                         }
 154                         erase_char(tty, tty->termios.c_cc[VERASE]);
 155                 }
 156         }
 157         if(erasechar == tty->termios.c_cc[VKILL]) {
 158                 while(tty->cooked_q.count > 0) {
 159                         erase_char(tty, tty->termios.c_cc[VERASE]);
 160                 }
 161                 if(tty->termios.c_lflag & ECHOK && !(tty->termios.c_lflag & ECHOE)) {
 162                         tty_queue_putchar(tty, &tty->write_q, '\n');
 163                 }
 164         }
 165 }
 166 
 167 static void set_termios(struct tty *tty, struct termios *new_termios)
 168 {
 169         memcpy_b(&tty->termios, new_termios, sizeof(struct termios));
 170         if(tty->set_termios) {
 171                 tty->set_termios(tty);
 172         }
 173 }
 174 
 175 static void set_termio(struct tty *tty, struct termio *new_termio)
 176 {
 177         struct termios new_termios;
 178 
 179         termio2termios(new_termio, &new_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);
 254         kill_pgrp(current->pgid, SIGCONT);
 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->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);
 301                                 }
 302                                 break;
 303                         }
 304                         if(ch == tty->termios.c_cc[VQUIT]) {
 305                                 if(tty->pgid > 0) {
 306                                         kill_pgrp(tty->pgid, SIGQUIT);
 307                                 }
 308                                 break;
 309                         }
 310                         if(ch == tty->termios.c_cc[VSUSP]) {
 311                                 if(tty->pgid > 0) {
 312                                         kill_pgrp(tty->pgid, SIGTSTP);
 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->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->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->lnext = 1;
 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->lnext = 0;
 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->lnext = 0;
 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);
 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);
 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                         set_termios(tty, (struct termios *)arg);
 745                         break;
 746 
 747                 /*
 748                  * Same as TCSETSW except that all characters queued for
 749                  * input are discarded.
 750                  */
 751                 case TCSETSF:
 752                         if((errno = check_user_area(VERIFY_READ, (void *)arg, sizeof(struct termios)))) {
 753                                 return errno;
 754                         }
 755                         set_termios(tty, (struct termios *)arg);
 756                         tty_queue_flush(&tty->read_q);
 757                         break;
 758 
 759                 /*
 760                  * Fetch and store the current terminal parameters to a termio
 761                  * structure pointed to by the argument.
 762                  */
 763                 case TCGETA:
 764                         if((errno = check_user_area(VERIFY_WRITE, (void *)arg, sizeof(struct termio)))) {
 765                                 return errno;
 766                         }
 767                         termios2termio(&tty->termios, (struct termio *)arg);
 768                         break;
 769 
 770                 /*
 771                  * Set the current terminal parameters according to the
 772                  * values in the termio structure pointed to by the argument.
 773                  */
 774                 case TCSETA:
 775                         if((errno = check_user_area(VERIFY_READ, (void *)arg, sizeof(struct termio)))) {
 776                                 return errno;
 777                         }
 778                         set_termio(tty, (struct termio *)arg);
 779                         break;
 780 
 781                 /*
 782                  * Same as TCSET except it doesn't take effect until all
 783                  * the characters queued for output have been transmitted.
 784                  */
 785                 case TCSETAW:
 786                         if((errno = check_user_area(VERIFY_READ, (void *)arg, sizeof(struct termio)))) {
 787                                 return errno;
 788                         }
 789                         set_termio(tty, (struct termio *)arg);
 790                         break;
 791 
 792                 /*
 793                  * Same as TCSETAW except that all characters queued for
 794                  * input are discarded.
 795                  */
 796                 case TCSETAF:
 797                         if((errno = check_user_area(VERIFY_READ, (void *)arg, sizeof(struct termio)))) {
 798                                 return errno;
 799                         }
 800                         set_termio(tty, (struct termio *)arg);
 801                         tty_queue_flush(&tty->read_q);
 802                         break;
 803 
 804                 /* Perform start/stop control */
 805                 case TCXONC:
 806                         switch(arg) {
 807                                 case TCOOFF:
 808                                         tty->stop(tty);
 809                                         break;
 810                                 case TCOON:
 811                                         tty->start(tty);
 812                                         break;
 813                                 default:
 814                                         return -EINVAL;
 815                         }
 816                         break;
 817                 case TCFLSH:
 818                         switch(arg) {
 819                                 case TCIFLUSH:
 820                                         tty_queue_flush(&tty->read_q);
 821                                         tty_queue_flush(&tty->cooked_q);
 822                                         break;
 823                                 case TCOFLUSH:
 824                                         tty_queue_flush(&tty->write_q);
 825                                         break;
 826                                 case TCIOFLUSH:
 827                                         tty_queue_flush(&tty->read_q);
 828                                         tty_queue_flush(&tty->cooked_q);
 829                                         tty_queue_flush(&tty->write_q);
 830                                         break;
 831                                 default:
 832                                         return -EINVAL;
 833                         }
 834                         break;
 835                 case TIOCSCTTY:
 836                         if(SESS_LEADER(current) && (current->sid == tty->sid)) {
 837                                 return 0;
 838                         }
 839                         if(!SESS_LEADER(current) || current->ctty) {
 840                                 return -EPERM;
 841                         }
 842                         if(tty->sid) {
 843                                 if((arg == 1) && IS_SUPERUSER) {
 844                                         FOR_EACH_PROCESS(p) {
 845                                                 if(p->ctty == tty) {
 846                                                         p->ctty = NULL;
 847                                                 }
 848                                                 p = p->next;
 849                                         }
 850                                 } else {
 851                                         return -EPERM;
 852                                 }
 853                         }
 854                         current->ctty = tty;
 855                         tty->sid = current->sid;
 856                         tty->pgid = current->pgid;
 857                         break;
 858 
 859                 /*
 860                  * Get the process group ID of the '__pid_t' pointed to by
 861                  * the arg to the foreground processes group ID.
 862                  */
 863                 case TIOCGPGRP:
 864                         if((errno = check_user_area(VERIFY_WRITE, (void *)arg, sizeof(__pid_t)))) {
 865                                 return errno;
 866                         }
 867                         memcpy_b((void *)arg, &tty->pgid, sizeof(__pid_t));
 868                         break;
 869 
 870                 /*
 871                  * Associate the process pointed to by '__pid_t' in the arg to
 872                  * the value of the terminal.
 873                  */
 874                 case TIOCSPGRP:
 875                         if(arg < 1) {
 876                                 return -EINVAL;
 877                         }
 878                         if((errno = check_user_area(VERIFY_READ, (void *)arg, sizeof(__pid_t)))) {
 879                                 return errno;
 880                         }
 881                         memcpy_b(&tty->pgid, (void *)arg, sizeof(__pid_t));
 882                         break;
 883 
 884                 /*
 885                  * The session ID of the terminal is fetched and stored in
 886                  * the '__pid_t' pointed to by the arg.
 887                 case TIOCSID:   FIXME
 888                  */
 889 
 890                 /*
 891                  * The terminal drivers notion of terminal size is stored in
 892                  * the 'winsize' structure pointed to by the arg.
 893                  */
 894                 case TIOCGWINSZ:
 895                         if((errno = check_user_area(VERIFY_WRITE, (void *)arg, sizeof(struct winsize)))) {
 896                                 return errno;
 897                         }
 898                         memcpy_b((void *)arg, &tty->winsize, sizeof(struct winsize));
 899                         break;
 900 
 901                 /*
 902                  * The terminal drivers notion of the terminal size is set
 903                  * to value in the 'winsize' structure pointed to by the arg.
 904                  */
 905                 case TIOCSWINSZ:
 906                 {
 907                         struct winsize *ws = (struct winsize *)arg;
 908                         short int changed;
 909 
 910                         if((errno = check_user_area(VERIFY_READ, (void *)arg, sizeof(struct winsize)))) {
 911                                 return errno;
 912                         }
 913                         changed = 0;
 914                         if(tty->winsize.ws_row != ws->ws_row) {
 915                                 changed = 1;
 916                         }
 917                         if(tty->winsize.ws_col != ws->ws_col) {
 918                                 changed = 1;
 919                         }
 920                         if(tty->winsize.ws_xpixel != ws->ws_xpixel) {
 921                                 changed = 1;
 922                         }
 923                         if(tty->winsize.ws_ypixel != ws->ws_ypixel) {
 924                                 changed = 1;
 925                         }
 926                         tty->winsize.ws_row = ws->ws_row;
 927                         tty->winsize.ws_col = ws->ws_col;
 928                         tty->winsize.ws_xpixel = ws->ws_xpixel;
 929                         tty->winsize.ws_ypixel = ws->ws_ypixel;
 930                         if(changed) {
 931                                 kill_pgrp(tty->pgid, SIGWINCH);
 932                         }
 933                 }
 934                         break;
 935                 case TIOCNOTTY:
 936                         if(current->ctty != tty) {
 937                                 return -ENOTTY;
 938                         }
 939                         if(SESS_LEADER(current)) {
 940                                 disassociate_ctty(tty);
 941                         }
 942                         break;
 943                 case TIOCLINUX:
 944                 {
 945                         int val = *(unsigned char *)arg;
 946                         if((errno = check_user_area(VERIFY_READ, (void *)arg, sizeof(unsigned char)))) {
 947                                 return errno;
 948                         }
 949                         switch(val) {
 950                                 case 12:        /* get current console */
 951                                         return current_cons;
 952                                         break;
 953                                 default:
 954                                         return -EINVAL;
 955                                         break;
 956                         }
 957                         break;
 958                 }
 959 
 960                 default:
 961                         return vt_ioctl(tty, cmd, arg);
 962         }
 963         return 0;
 964 }
 965 
 966 int tty_lseek(struct inode *i, __off_t offset)
 967 {
 968         return -ESPIPE;
 969 }
 970 
 971 int tty_select(struct inode *i, int flag)
 972 {
 973         struct tty *tty;
 974 
 975         if(!(tty = get_tty(i->rdev))) {
 976                 printk("%s(): oops! (%x)\n", __FUNCTION__, i->rdev);
 977                 return 0;
 978         }
 979 
 980         switch(flag) {
 981                 case SEL_R:
 982                         if(tty->cooked_q.count > 0) {
 983                                 if(!(tty->termios.c_lflag & ICANON) || ((tty->termios.c_lflag & ICANON) && tty->canon_data)) {
 984                                         return 1;
 985                                 }
 986                         }
 987                         break;
 988                 case SEL_W:
 989                         if(!tty->write_q.count) {
 990                                 return 1;
 991                         }
 992                         break;
 993         }
 994         return 0;
 995 }
 996 
 997 void tty_init(void)
 998 {
 999         tty_queue_init();
1000         memset_b(tty_table, NULL, sizeof(tty_table));
1001 }

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