Prex Home / Browse Source - Prex Version: 0.9.0

root/bsp/drv/dev/base/tty.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. tty_getc
  2. tty_putc
  3. tty_unputc
  4. tty_catq
  5. tty_rubout
  6. tty_echo
  7. tty_start
  8. tty_flush
  9. tty_done
  10. tty_wait
  11. tty_signal
  12. tty_input
  13. tty_output
  14. tty_read
  15. tty_write
  16. tty_ioctl
  17. tty_attach

   1 /*-
   2  * Copyright (c) 1982, 1986, 1990, 1991, 1993
   3  *      The Regents of the University of California.  All rights reserved.
   4  * (c) UNIX System Laboratories, Inc.
   5  * All or some portions of this file are derived from material licensed
   6  * to the University of California by American Telephone and Telegraph
   7  * Co. or Unix System Laboratories, Inc. and are reproduced herein with
   8  * the permission of UNIX System Laboratories, Inc.
   9  *
  10  * Redistribution and use in source and binary forms, with or without
  11  * modification, are permitted provided that the following conditions
  12  * are met:
  13  * 1. Redistributions of source code must retain the above copyright
  14  *    notice, this list of conditions and the following disclaimer.
  15  * 2. Redistributions in binary form must reproduce the above copyright
  16  *    notice, this list of conditions and the following disclaimer in the
  17  *    documentation and/or other materials provided with the distribution.
  18  * 3. Neither the name of the University nor the names of its contributors
  19  *    may be used to endorse or promote products derived from this software
  20  *    without specific prior written permission.
  21  *
  22  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  32  * SUCH DAMAGE.
  33  *
  34  *      @(#)tty.c       8.13 (Berkeley) 1/9/95
  35  */
  36 
  37 /* Modified for Prex by Kohsuke Ohtani. */
  38 
  39 /*
  40  * tty.c - TTY device helper
  41  */
  42 
  43 #include <driver.h>
  44 #include <sys/signal.h>
  45 #include <sys/termios.h>
  46 #include <tty.h>
  47 #include <pm.h>
  48 #include <kd.h>
  49 
  50 /* #define DEBUG_TTY 1 */
  51 
  52 #ifdef DEBUG_TTY
  53 #define DPRINTF(a) printf a
  54 #else
  55 #define DPRINTF(a)
  56 #endif
  57 
  58 #define FREAD           0x0001
  59 #define FWRITE          0x0002
  60 
  61 static void tty_output(int c, struct tty *tp);
  62 
  63 /* default control characters */
  64 static const cc_t       ttydefchars[NCCS] = TTYDEFCHARS;
  65 
  66 #define is_ctrl(c)   ((c) < 32 || (c) == 0x7f)
  67 
  68 /*
  69  * TTY queue operations
  70  */
  71 #define ttyq_next(i)    (((i) + 1) & (TTYQ_SIZE - 1))
  72 #define ttyq_prev(i)    (((i) - 1) & (TTYQ_SIZE - 1))
  73 #define ttyq_full(q)    ((q)->tq_count >= TTYQ_SIZE)
  74 #define ttyq_empty(q)   ((q)->tq_count == 0)
  75 
  76 /*
  77  * Get a character from a queue.
  78  */
  79 int
  80 tty_getc(struct tty_queue *tq)
  81 {
  82         char c;
  83         int s;
  84 
  85         s = splhigh();
  86         if (ttyq_empty(tq)) {
  87                 splx(s);
  88                 return -1;
  89         }
  90         c = tq->tq_buf[tq->tq_head];
  91         tq->tq_head = ttyq_next(tq->tq_head);
  92         tq->tq_count--;
  93         splx(s);
  94         return (int)c;
  95 }
  96 
  97 /*
  98  * Put a character into a queue.
  99  */
 100 static void
 101 tty_putc(int c, struct tty_queue *tq)
 102 {
 103         int s;
 104 
 105         s = splhigh();
 106         if (ttyq_full(tq)) {
 107                 splx(s);
 108                 return;
 109         }
 110         tq->tq_buf[tq->tq_tail] = (char)(c & 0xff);
 111         tq->tq_tail = ttyq_next(tq->tq_tail);
 112         tq->tq_count++;
 113         splx(s);
 114 }
 115 
 116 /*
 117  * Remove the last character in a queue and return it.
 118  */
 119 static int
 120 tty_unputc(struct tty_queue *tq)
 121 {
 122         char c;
 123         int s;
 124 
 125         if (ttyq_empty(tq))
 126                 return -1;
 127         s = splhigh();
 128         tq->tq_tail = ttyq_prev(tq->tq_tail);
 129         c = tq->tq_buf[tq->tq_tail];
 130         tq->tq_count--;
 131         splx(s);
 132         return (int)c;
 133 }
 134 
 135 /*
 136  * Put the chars in the from queue on the end of the to queue.
 137  */
 138 static void
 139 tty_catq(struct tty_queue *from, struct tty_queue *to)
 140 {
 141         int c;
 142 
 143         while ((c = tty_getc(from)) != -1)
 144                 tty_putc(c, to);
 145 }
 146 
 147 /*
 148  * Rubout one character from the rawq of tp
 149  */
 150 static void
 151 tty_rubout(int c, struct tty *tp)
 152 {
 153 
 154         if (!(tp->t_lflag & ECHO))
 155                 return;
 156         if (tp->t_lflag & ECHOE) {
 157                 tty_output('\b', tp);
 158                 tty_output(' ', tp);
 159                 tty_output('\b', tp);
 160         } else
 161                 tty_output(tp->t_cc[VERASE], tp);
 162 }
 163 
 164 /*
 165  * Echo char
 166  */
 167 static void
 168 tty_echo(int c, struct tty *tp)
 169 {
 170 
 171         if (!(tp->t_lflag & ECHO)) {
 172                 if (c == '\n' && (tp->t_lflag & ECHONL))
 173                         tty_output('\n', tp);
 174                 return;
 175         }
 176         if (is_ctrl(c) && c != '\n' && c != '\t' && c != '\b') {
 177                 tty_output('^', tp);
 178                 tty_output(c + 'A' - 1, tp);
 179         } else
 180                 tty_output(c, tp);
 181 }
 182 
 183 /*
 184  * Start output.
 185  */
 186 static void
 187 tty_start(struct tty *tp)
 188 {
 189 
 190         DPRINTF(("tty_start\n"));
 191 
 192         if (tp->t_state & (TS_TTSTOP|TS_BUSY))
 193                 return;
 194         if (tp->t_oproc)
 195                 (*tp->t_oproc)(tp);
 196 }
 197 
 198 /*
 199  * Flush tty read and/or write queues, notifying anyone waiting.
 200  */
 201 static void
 202 tty_flush(struct tty *tp, int rw)
 203 {
 204 
 205         DPRINTF(("tty_flush rw=%d\n", rw));
 206 
 207         if (rw & FREAD) {
 208                 while (tty_getc(&tp->t_canq) != -1)
 209                         ;
 210                 while (tty_getc(&tp->t_rawq) != -1)
 211                         ;
 212                 sched_wakeup(&tp->t_input);
 213         }
 214         if (rw & FWRITE) {
 215                 tp->t_state &= ~TS_TTSTOP;
 216                 tty_start(tp);
 217         }
 218 }
 219 
 220 /*
 221  * Output is completed.
 222  */
 223 void
 224 tty_done(struct tty *tp)
 225 {
 226 
 227         if (tp->t_outq.tq_count == 0)
 228                 tp->t_state &= ~TS_BUSY;
 229         if (tp->t_state & TS_ASLEEP) {
 230                 tp->t_state &= ~TS_ASLEEP;
 231                 sched_wakeup(&tp->t_output);
 232         }
 233 }
 234 
 235 /*
 236  * Wait for output to drain.
 237  */
 238 static void
 239 tty_wait(struct tty *tp)
 240 {
 241 
 242         /*      DPRINTF(("tty_wait\n")); */
 243 
 244         if ((tp->t_outq.tq_count > 0) && tp->t_oproc) {
 245                 tp->t_state |= TS_BUSY;
 246                 while (1) {
 247                         (*tp->t_oproc)(tp);
 248                         if ((tp->t_state & TS_BUSY) == 0)
 249                                 break;
 250                         tp->t_state |= TS_ASLEEP;
 251                         sched_sleep(&tp->t_output);
 252                 }
 253         }
 254 }
 255 
 256 /*
 257  * Send TTY signal at DPC level.
 258  */
 259 static void
 260 tty_signal(void *arg)
 261 {
 262         struct tty *tp = arg;
 263 
 264         DPRINTF(("tty_signal sig=%d\n", tp->t_signo));
 265         exception_post(tp->t_sigtask, tp->t_signo);
 266 }
 267 
 268 /*
 269  * Process input of a single character received on a tty.
 270  * echo if required.
 271  * This may be called with interrupt level.
 272  */
 273 void
 274 tty_input(int c, struct tty *tp)
 275 {
 276         unsigned char *cc;
 277         tcflag_t iflag, lflag;
 278         int sig = -1;
 279 
 280         DPRINTF(("tty_input: %d\n", c));
 281 
 282         /* Reload power management timer */
 283         pm_notify(PME_USER_ACTIVITY);
 284 
 285         lflag = tp->t_lflag;
 286         iflag = tp->t_iflag;
 287         cc = tp->t_cc;
 288 
 289 #if defined(DEBUG) && defined(CONFIG_KD)
 290         if (c == cc[VDDB]) {
 291                 kd_enter();
 292                 tty_flush(tp, FREAD | FWRITE);
 293                 goto endcase;
 294         }
 295 #endif /* !CONFIG_KD*/
 296 
 297         /* IGNCR, ICRNL, INLCR */
 298         if (c == '\r') {
 299                 if (iflag & IGNCR)
 300                         goto endcase;
 301                 else if (iflag & ICRNL)
 302                         c = '\n';
 303         } else if (c == '\n' && (iflag & INLCR))
 304                 c = '\r';
 305 
 306         if (iflag & IXON) {
 307                 /* stop (^S) */
 308                 if (c == cc[VSTOP]) {
 309                         if (!(tp->t_state & TS_TTSTOP)) {
 310                                 tp->t_state |= TS_TTSTOP;
 311                                 return;
 312                         }
 313                         if (c != cc[VSTART])
 314                                 return;
 315                         /* if VSTART == VSTOP then toggle */
 316                         goto endcase;
 317                 }
 318                 /* start (^Q) */
 319                 if (c == cc[VSTART])
 320                         goto restartoutput;
 321         }
 322         if (lflag & ICANON) {
 323                 /* erase (^H / ^?) or backspace */
 324                 if (c == cc[VERASE] || c == '\b') {
 325                         if (!ttyq_empty(&tp->t_rawq))
 326                                 tty_rubout(tty_unputc(&tp->t_rawq), tp);
 327                         goto endcase;
 328                 }
 329                 /* kill (^U) */
 330                 if (c == cc[VKILL]) {
 331                         while (!ttyq_empty(&tp->t_rawq))
 332                                 tty_rubout(tty_unputc(&tp->t_rawq), tp);
 333                         goto endcase;
 334                 }
 335         }
 336         if (lflag & ISIG) {
 337                 /* quit (^C) */
 338                 if (c == cc[VINTR] || c == cc[VQUIT]) {
 339                         if (!(lflag & NOFLSH)) {
 340                                 tp->t_state |= TS_ISIG;
 341                                 tty_flush(tp, FREAD | FWRITE);
 342                         }
 343                         tty_echo(c, tp);
 344                         sig = (c == cc[VINTR]) ? SIGINT : SIGQUIT;
 345                         goto endcase;
 346                 }
 347                 /* suspend (^Z) */
 348                 if (c == cc[VSUSP]) {
 349                         if (!(lflag & NOFLSH)) {
 350                                 tp->t_state |= TS_ISIG;
 351                                 tty_flush(tp, FREAD | FWRITE);
 352                         }
 353                         tty_echo(c, tp);
 354                         sig = SIGTSTP;
 355                         goto endcase;
 356                 }
 357         }
 358 
 359         /*
 360          * Check for input buffer overflow
 361          */
 362         if (ttyq_full(&tp->t_rawq)) {
 363                 tty_flush(tp, FREAD | FWRITE);
 364                 goto endcase;
 365         }
 366         tty_putc(c, &tp->t_rawq);
 367 
 368         if (lflag & ICANON) {
 369                 if (c == '\n' || c == cc[VEOF] || c == cc[VEOL]) {
 370                         tty_catq(&tp->t_rawq, &tp->t_canq);
 371                         sched_wakeup(&tp->t_input);
 372                 }
 373         } else
 374                 sched_wakeup(&tp->t_input);
 375 
 376         if (lflag & ECHO)
 377                 tty_echo(c, tp);
 378  endcase:
 379         /*
 380          * IXANY means allow any character to restart output.
 381          */
 382         if ((tp->t_state & TS_TTSTOP) && (iflag & IXANY) == 0 &&
 383             cc[VSTART] != cc[VSTOP])
 384                 return;
 385  restartoutput:
 386         tp->t_state &= ~TS_TTSTOP;
 387 
 388         if (sig != -1) {
 389                 if (tp->t_sigtask) {
 390                         tp->t_signo = sig;
 391                         sched_dpc(&tp->t_dpc, &tty_signal, tp);
 392                 }
 393         }
 394         tty_start(tp);
 395 }
 396 
 397 /*
 398  * Output a single character on a tty, doing output processing
 399  * as needed (expanding tabs, newline processing, etc.).
 400  */
 401 static void
 402 tty_output(int c, struct tty *tp)
 403 {
 404         int i, col;
 405 
 406         if ((tp->t_lflag & ICANON) == 0) {
 407                 tty_putc(c, &tp->t_outq);
 408                 return;
 409         }
 410         /* Expand tab */
 411         if (c == '\t' && (tp->t_oflag & OXTABS)) {
 412                 i = 8 - (tp->t_column & 7);
 413                 tp->t_column += i;
 414                 do
 415                         tty_putc(' ', &tp->t_outq);
 416                 while (--i > 0);
 417                 return;
 418         }
 419         /* Translate newline into "\r\n" */
 420         if (c == '\n' && (tp->t_oflag & ONLCR))
 421                 tty_putc('\r', &tp->t_outq);
 422 
 423         tty_putc(c, &tp->t_outq);
 424 
 425         col = tp->t_column;
 426         switch (c) {
 427         case '\b':      /* back space */
 428                 if (col > 0)
 429                         --col;
 430                 break;
 431         case '\t':      /* tab */
 432                 col = (col + 8) & ~7;
 433                 break;
 434         case '\n':      /* newline */
 435                 col = 0;
 436                 break;
 437         case '\r':      /* return */
 438                 col = 0;
 439                 break;
 440         default:
 441                 if (!is_ctrl(c))
 442                     ++col;
 443                 break;
 444         }
 445         tp->t_column = col;
 446         return;
 447 }
 448 
 449 /*
 450  * Process a read call on a tty device.
 451  */
 452 int
 453 tty_read(struct tty *tp, char *buf, size_t *nbyte)
 454 {
 455         unsigned char *cc;
 456         struct tty_queue *qp;
 457         int rc, tmp;
 458         u_char c;
 459         size_t count = 0;
 460         tcflag_t lflag;
 461 
 462         DPRINTF(("tty_read\n"));
 463 
 464         lflag = tp->t_lflag;
 465         cc = tp->t_cc;
 466         qp = (lflag & ICANON) ? &tp->t_canq : &tp->t_rawq;
 467 
 468         /* If there is no input, wait it */
 469         while (ttyq_empty(qp)) {
 470                 rc = sched_sleep(&tp->t_input);
 471                 if (rc == SLP_INTR || tp->t_state & TS_ISIG) {
 472                         tp->t_state &= ~TS_ISIG;
 473                         return EINTR;
 474                 }
 475         }
 476         while (count < *nbyte) {
 477                 if ((tmp = tty_getc(qp)) == -1)
 478                         break;
 479                 c = (u_char)tmp;
 480                 if (c == cc[VEOF] && (lflag & ICANON))
 481                         break;
 482                 count++;
 483                 if (copyout(&c, buf, 1))
 484                         return EFAULT;
 485                 if ((lflag & ICANON) && (c == '\n' || c == cc[VEOL]))
 486                         break;
 487                 buf++;
 488         }
 489         *nbyte = count;
 490         return 0;
 491 }
 492 
 493 /*
 494  * Process a write call on a tty device.
 495  */
 496 int
 497 tty_write(struct tty *tp, char *buf, size_t *nbyte)
 498 {
 499         size_t remain, count = 0;
 500         u_char c;
 501 
 502         DPRINTF(("tty_write\n"));
 503 
 504         remain = *nbyte;
 505         while (remain > 0) {
 506                 if (tp->t_outq.tq_count > TTYQ_HIWAT) {
 507                         tty_start(tp);
 508                         if (tp->t_outq.tq_count <= TTYQ_HIWAT)
 509                                 continue;
 510                         tp->t_state |= TS_ASLEEP;
 511                         sched_sleep(&tp->t_output);
 512                         continue;
 513                 }
 514                 if (copyin(buf, &c, 1))
 515                         return EFAULT;
 516                 tty_output((int)c, tp);
 517                 buf++;
 518                 remain--;
 519                 count++;
 520         }
 521         tty_start(tp);
 522         *nbyte = count;
 523         return 0;
 524 }
 525 
 526 /*
 527  * Ioctls for all tty devices.
 528  */
 529 int
 530 tty_ioctl(struct tty *tp, u_long cmd, void *data)
 531 {
 532         int flags;
 533         struct tty_queue *qp;
 534 
 535         switch (cmd) {
 536         case TIOCGETA:
 537                 if (copyout(&tp->t_termios, data,
 538                                  sizeof(struct termios)))
 539                         return EFAULT;
 540                 break;
 541         case TIOCSETAW:
 542         case TIOCSETAF:
 543                 tty_wait(tp);
 544                 if (cmd == TIOCSETAF)
 545                         tty_flush(tp, FREAD);
 546                 /* FALLTHROUGH */
 547         case TIOCSETA:
 548                 if (copyin(data, &tp->t_termios,
 549                            sizeof(struct termios)))
 550                         return EFAULT;
 551                 break;
 552         case TIOCSPGRP:                 /* set pgrp of tty */
 553                 if (copyin(data, &tp->t_pgid, sizeof(pid_t)))
 554                         return EFAULT;
 555                 break;
 556         case TIOCGPGRP:
 557                 if (copyout(&tp->t_pgid, data, sizeof(pid_t)))
 558                         return EFAULT;
 559                 break;
 560         case TIOCFLUSH:
 561                 if (copyin(data, &flags, sizeof(flags)))
 562                         return EFAULT;
 563                 if (flags == 0)
 564                         flags = FREAD | FWRITE;
 565                 else
 566                         flags &= FREAD | FWRITE;
 567                 tty_flush(tp, flags);
 568                 break;
 569         case TIOCSTART:
 570                 if (tp->t_state & TS_TTSTOP) {
 571                         tp->t_state &= ~TS_TTSTOP;
 572                         tty_start(tp);
 573                 }
 574                 break;
 575         case TIOCSTOP:
 576                 if (!(tp->t_state & TS_TTSTOP)) {
 577                         tp->t_state |= TS_TTSTOP;
 578                 }
 579                 break;
 580         case TIOCGWINSZ:
 581                 if (copyout(&tp->t_winsize, data,
 582                             sizeof(struct winsize)))
 583                         return EFAULT;
 584                 break;
 585         case TIOCSWINSZ:
 586                 if (copyin(data, &tp->t_winsize,
 587                            sizeof(struct winsize)))
 588                         return EFAULT;
 589                 break;
 590         case TIOCSETSIGT:       /* Prex */
 591                 if (copyin(data, &tp->t_sigtask, sizeof(task_t)))
 592                         return EFAULT;
 593                 break;
 594         case TIOCINQ:
 595                 qp = (tp->t_lflag & ICANON) ? &tp->t_canq : &tp->t_rawq;
 596                 if (copyout(&qp->tq_count, data, sizeof(int)))
 597                         return EFAULT;
 598                 break;
 599         case TIOCOUTQ:
 600                 if (copyout(&tp->t_outq.tq_count, data, sizeof(int)))
 601                         return EFAULT;
 602                 break;
 603         }
 604         return 0;
 605 }
 606 
 607 /*
 608  * Attach a tty to the tty list.
 609  */
 610 void
 611 tty_attach(struct tty *tp)
 612 {
 613         struct bootinfo *bi;
 614 
 615         /* Initialize tty */
 616         memset(tp, 0, sizeof(struct tty));
 617         memcpy(&tp->t_termios.c_cc, ttydefchars, sizeof(ttydefchars));
 618 
 619         event_init(&tp->t_input, "TTY input");
 620         event_init(&tp->t_output, "TTY output");
 621 
 622         tp->t_iflag = TTYDEF_IFLAG;
 623         tp->t_oflag = TTYDEF_OFLAG;
 624         tp->t_cflag = TTYDEF_CFLAG;
 625         tp->t_lflag = TTYDEF_LFLAG;
 626         tp->t_ispeed = TTYDEF_SPEED;
 627         tp->t_ospeed = TTYDEF_SPEED;
 628 
 629         machine_bootinfo(&bi);
 630         tp->t_winsize.ws_row = (u_short)bi->video.text_y;
 631         tp->t_winsize.ws_col = (u_short)bi->video.text_x;
 632 }

/* [<][>][^][v][top][bottom][index][help] */