/* * Remove the last character in a queue and return it. */ static int tty_unputc(struct tty_queue *tq) { if (ttyq_empty(tq)) return -1; int s = splhigh(); tq->tq_tail = ttyq_prev(tq->tq_tail); int c = tq->tq_buf[tq->tq_tail]; tq->tq_count--; splx(s); return c & 0xFF; }
/* * Process a read call on a tty device. */ int tty_read(struct tty *tp, struct uio *uio, size_t *size) { unsigned char *cc; struct tty_queue *qp; int rc, tmp; u_char c; tcflag_t lflag; DPRINTF(TTYDB_CORE, ("tty_read\n")); lflag = tp->t_lflag; cc = tp->t_cc; qp = (lflag & ICANON) ? &tp->t_canq : &tp->t_rawq; size_t total = 0; for (int i = 0; i < uio->iovcnt; ++i) { /* If there is no input, wait it */ while (ttyq_empty(qp)) { rc = sem_wait(&tp->t_input); if ((rc != 0 && errno == EINTR) || tp->t_state & TS_ISIG) { tp->t_state &= ~TS_ISIG; return -EINTR; } } size_t count = 0; char *buf = uio->iov[i].iov_base; size_t nbyte = uio->iov[i].iov_len; while (count < nbyte) { if ((tmp = tty_getc(qp)) == -1) break; c = (u_char)tmp; if (c == cc[VEOF] && (lflag & ICANON)) break; ++count; if (copyout(&c, buf, 1)) return -EFAULT; if ((lflag & ICANON) && (c == '\n' || c == cc[VEOL])) break; ++buf; } total += count; } *size = total; return 0; }
/* * Process a read call on a tty device. */ static int tty_read(file_t file, char *buf, size_t *nbyte, int blkno) { struct tty *tp = file->priv; unsigned char *cc; struct tty_queue *qp; int rc, c; unsigned char byte; size_t count = 0; tcflag_t lflag; lflag = tp->t_lflag; cc = tp->t_cc; qp = (lflag & ICANON) ? &tp->t_canq : &tp->t_rawq; if ((file->f_flags & O_NONBLOCK) && ttyq_empty(qp)) return EAGAIN; /* If there is no input, wait it */ while (ttyq_empty(qp)) { rc = sched_sleep(&tp->t_input); if (rc == SLP_INTR) return EINTR; } while (count < *nbyte) { if ((c = ttyq_getc(qp)) == -1) break; if (c == cc[VEOF] && (lflag & ICANON)) break; count++; byte = c; /* for BIG_ENDIAN */ if (umem_copyout(&byte, buf, 1)) return EFAULT; if ((lflag & ICANON) && (c == '\n' || c == cc[VEOL])) break; buf++; } *nbyte = count; return 0; }
/* * Get a character from a queue. */ int tty_getc(struct tty_queue *tq) { int s = splhigh(); if (ttyq_empty(tq)) { splx(s); return -1; } int c = tq->tq_buf[tq->tq_head]; tq->tq_head = ttyq_next(tq->tq_head); tq->tq_count--; splx(s); return c & 0xFF; }
/* * Remove the last character in a queue and return it. */ int ttyq_unputc(struct tty_queue *tq) { int c; if (ttyq_empty(tq)) return -1; irq_lock(); tq->tq_tail = ttyq_prev(tq->tq_tail); c = tq->tq_buf[tq->tq_tail]; tq->tq_count--; irq_unlock(); return c; }
/* * Get a character from a queue. */ int ttyq_getc(struct tty_queue *tq) { int c; irq_lock(); if (ttyq_empty(tq)) { irq_unlock(); return -1; } c = tq->tq_buf[tq->tq_head]; tq->tq_head = ttyq_next(tq->tq_head); tq->tq_count--; irq_unlock(); return c; }
/* * Process input of a single character received on a tty. * echo if required. * This may be called with interrupt level. */ void tty_input(int c, struct tty *tp) { unsigned char *cc; tcflag_t iflag, lflag; int sig = -1; DPRINTF(TTYDB_CORE, ("tty_input: %d\n", c)); // Reload power management timer */ pm_notify(PME_USER_ACTIVITY); lflag = tp->t_lflag; iflag = tp->t_iflag; cc = tp->t_cc; #if defined(DEBUG) && defined(CONFIG_KD) if (c == cc[VDDB]) { kd_enter(); tty_flush(tp, FREAD | FWRITE); goto endcase; } #endif /* !CONFIG_KD*/ /* IGNCR, ICRNL, INLCR */ if (c == '\r') { if (iflag & IGNCR) goto endcase; else if (iflag & ICRNL) c = '\n'; } else if (c == '\n' && (iflag & INLCR)) { c = '\r'; } if (iflag & IXON) { /* stop (^S) */ if (c == cc[VSTOP]) { if (!(tp->t_state & TS_TTSTOP)) { tp->t_state |= TS_TTSTOP; return; } if (c != cc[VSTART]) return; /* if VSTART == VSTOP then toggle */ goto endcase; } /* start (^Q) */ if (c == cc[VSTART]) goto restartoutput; } if (lflag & ICANON) { /* erase (^H / ^?) or backspace */ if (c == cc[VERASE] || c == '\b') { if (!ttyq_empty(&tp->t_rawq)) tty_rubout(tty_unputc(&tp->t_rawq), tp); goto endcase; } /* kill (^U) */ if (c == cc[VKILL]) { while (!ttyq_empty(&tp->t_rawq)) tty_rubout(tty_unputc(&tp->t_rawq), tp); goto endcase; } } if (lflag & ISIG) { /* quit (^C) */ if (c == cc[VINTR] || c == cc[VQUIT]) { if (!(lflag & NOFLSH)) { tp->t_state |= TS_ISIG; tty_flush(tp, FREAD | FWRITE); } tty_echo(c, tp); sig = (c == cc[VINTR]) ? SIGINT : SIGQUIT; goto endcase; } /* suspend (^Z) */ if (c == cc[VSUSP]) { if (!(lflag & NOFLSH)) { tp->t_state |= TS_ISIG; tty_flush(tp, FREAD | FWRITE); } tty_echo(c, tp); sig = SIGTSTP; goto endcase; } } /* * Check for input buffer overflow */ if (ttyq_full(&tp->t_rawq)) { tty_flush(tp, FREAD | FWRITE); goto endcase; } tty_putc(c, &tp->t_rawq); if (lflag & ICANON) { if (c == '\n' || c == cc[VEOF] || c == cc[VEOL]) { tty_catq(&tp->t_rawq, &tp->t_canq); sem_post(&tp->t_input); } } else { sem_post(&tp->t_input); } if (lflag & ECHO) tty_echo(c, tp); endcase: /* * IXANY means allow any character to restart output. */ if ((tp->t_state & TS_TTSTOP) && (iflag & IXANY) == 0 && cc[VSTART] != cc[VSTOP]) return; restartoutput: tp->t_state &= ~TS_TTSTOP; if (sig != -1) { if (tp->t_pid) { tp->t_signo = sig; sched_dpc(&tp->t_dpc, &tty_signal, tp); } } tty_start(tp); }
/* * Process input of a single character received on a tty. * echo if required. * This may be called with interrupt level. */ void tty_input(int c, struct tty *tp) { unsigned char *cc; tcflag_t iflag, lflag; int sig = -1; #ifdef CONFIG_CPUFREQ /* Reload power management timer */ pm_active(); #endif lflag = tp->t_lflag; iflag = tp->t_iflag; cc = tp->t_cc; /* IGNCR, ICRNL, INLCR */ if (c == '\r') { if (iflag & IGNCR) goto endcase; else if (iflag & ICRNL) c = '\n'; } else if (c == '\n' && (iflag & INLCR)) c = '\r'; if (iflag & IXON) { /* stop (^S) */ if (c == cc[VSTOP]) { if (!(tp->t_state & TS_TTSTOP)) { tp->t_state |= TS_TTSTOP; return; } if (c != cc[VSTART]) return; /* if VSTART == VSTOP then toggle */ goto endcase; } /* start (^Q) */ if (c == cc[VSTART]) goto restartoutput; } if (lflag & ICANON) { /* erase (^H / ^?) or backspace */ if (c == cc[VERASE] || c == '\b') { if (!ttyq_empty(&tp->t_rawq)) { ttyq_unputc(&tp->t_rawq); tty_rubout(tp); } goto endcase; } /* kill (^U) */ if (c == cc[VKILL]) { while (!ttyq_empty(&tp->t_rawq)) { ttyq_unputc(&tp->t_rawq); tty_rubout(tp); } goto endcase; } } if (lflag & ISIG) { /* quit (^C) */ if (c == cc[VINTR] || c == cc[VQUIT]) { if (!(lflag & NOFLSH)) tty_flush(tp, FREAD | FWRITE); tty_echo(c, tp); sig = (c == cc[VINTR]) ? SIGINT : SIGQUIT; goto endcase; } /* suspend (^Z) */ if (c == cc[VSUSP]) { if (!(lflag & NOFLSH)) tty_flush(tp, FREAD | FWRITE); tty_echo(c, tp); sig = SIGTSTP; goto endcase; } } /* * Check for input buffer overflow */ if (ttyq_full(&tp->t_rawq)) { tty_flush(tp, FREAD | FWRITE); goto endcase; } ttyq_putc(c, &tp->t_rawq); if (lflag & ICANON) { if (c == '\n' || c == cc[VEOF] || c == cc[VEOL]) { tty_catq(&tp->t_rawq, &tp->t_canq); sched_wakeup(&tp->t_input); } } else sched_wakeup(&tp->t_input); if (lflag & ECHO) tty_echo(c, tp); endcase: /* * IXANY means allow any character to restart output. */ if ((tp->t_state & TS_TTSTOP) && (iflag & IXANY) == 0 && cc[VSTART] != cc[VSTOP]) return; restartoutput: tp->t_state &= ~TS_TTSTOP; if (sig != -1) { if (sig_task) exception_post(sig_task, sig); } tty_start(tp); }