Welcome to The Fiwix Project
A UNIX-like kernel for the i386 architecture
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 }