/* * Rubout one character from the rawq of tp */ static void tty_rubout(int c, struct tty *tp) { if (!(tp->t_lflag & ECHO)) return; if (tp->t_lflag & ECHOE) { tty_output('\b', tp); tty_output(' ', tp); tty_output('\b', tp); } else { tty_output(tp->t_cc[VERASE], tp); } }
static void tty_echo_erase(struct tty *t) { cc_t *cc = t->termios.c_cc; if (!TC_L(t, ECHO)) return; /* See also http://users.sosdg.org/~qiyong/mxr/source/drivers/tty/tty.c#L1430 * as example of how ECHOE flag is handled in Minix. */ if (!TC_L(t, ECHOE)) tty_output(t, cc[VERASE]); else for (char *ch = "\b \b"; *ch; ++ch) tty_output(t, *ch); }
/* * Echo char */ static void tty_echo(int c, struct tty *tp) { if (!(tp->t_lflag & ECHO)) { if (c == '\n' && (tp->t_lflag & ECHONL)) tty_output('\n', tp); return; } if (is_ctrl(c) && c != '\n' && c != '\t' && c != '\b') { tty_output('^', tp); tty_output(c + 'A' - 1, tp); } else { tty_output(c, tp); } }
static int tty_blockin_output(struct tty *t, char ch) { struct idesc_wait_link iwl; int ret; idesc_wait_init(&iwl, POLLOUT | POLLERR); do { if (tty_output(t, ch)) return 0; if (!t->idesc) return -EBADF; ret = idesc_wait_prepare(t->idesc, &iwl); if (!ret) { mutex_unlock(&t->lock); tty_out_wake(t); ret = sched_wait(); mutex_lock(&t->lock); } idesc_wait_cleanup(t->idesc, &iwl); } while (!ret); return ret; }
/* * Process a write call on a tty device. */ static int tty_write(file_t file, char *buf, size_t *nbyte, int blkno) { struct tty *tp = file->priv; size_t remain, count = 0; unsigned char c; /* must be char (not int) for BIG ENDIAN */ DPRINTF(("tty_write\n")); remain = *nbyte; while (remain > 0) { if (tp->t_outq.tq_count > TTYQ_HIWAT) { tty_start(tp); if (tp->t_outq.tq_count <= TTYQ_HIWAT) continue; tp->t_state |= TS_ASLEEP; sched_sleep(&tp->t_output); continue; } if (umem_copyin(buf, &c, 1)) return EFAULT; tty_output(c, tp); buf++; remain--; count++; } tty_start(tp); *nbyte = count; return 0; }
/* * Process a write call on a tty device. */ int tty_write(struct tty *tp, struct uio *uio, size_t *size) { u_char c; DPRINTF(TTYDB_CORE, ("tty_write\n")); size_t total = 0; for (int i = 0; i < uio->iovcnt; ++i) { size_t count = 0; char *buf = uio->iov[i].iov_base; size_t nbyte = uio->iov[i].iov_len; while (nbyte > 0) { if (tp->t_outq.tq_count > TTYQ_HIWAT) { tty_start(tp); if (tp->t_outq.tq_count <= TTYQ_HIWAT) continue; tp->t_state |= TS_ASLEEP; sem_wait(&tp->t_output); continue; } if (copyin(buf, &c, 1)) return -EFAULT; tty_output((int)c, tp); ++buf; --nbyte; ++count; } total += count; } tty_start(tp); *size = total; return 0; }
/* * Set TTY status. * No locks may be held. * Calls device start or stop routines; must already be on master if * device needs to run on master. */ io_return_t tty_set_status( register struct tty *tp, dev_flavor_t flavor, int * data, natural_t count) { int s; switch (flavor) { case TTY_FLUSH: { register int flags; if (count < TTY_FLUSH_COUNT) return D_INVALID_OPERATION; flags = *data; if (flags == 0) flags = D_READ | D_WRITE; s = spltty(); simple_lock(&tp->t_lock); tty_flush(tp, flags); simple_unlock(&tp->t_lock); splx(s); break; } case TTY_STOP: /* stop output */ s = spltty(); simple_lock(&tp->t_lock); if ((tp->t_state & TS_TTSTOP) == 0) { tp->t_state |= TS_TTSTOP; (*tp->t_stop)(tp, 0); } simple_unlock(&tp->t_lock); splx(s); break; case TTY_START: /* start output */ s = spltty(); simple_lock(&tp->t_lock); if (tp->t_state & TS_TTSTOP) { tp->t_state &= ~TS_TTSTOP; tty_output(tp); } simple_unlock(&tp->t_lock); splx(s); break; case TTY_STATUS: /* set special characters and speed */ { register struct tty_status *tsp; if (count < TTY_STATUS_COUNT) return D_INVALID_OPERATION; tsp = (struct tty_status *)data; if (tsp->tt_ispeed < 0 || tsp->tt_ispeed >= NSPEEDS || tsp->tt_ospeed < 0 || tsp->tt_ospeed >= NSPEEDS) { return D_INVALID_OPERATION; } s = spltty(); simple_lock(&tp->t_lock); tp->t_ispeed = tsp->tt_ispeed; tp->t_ospeed = tsp->tt_ospeed; tp->t_breakc = tsp->tt_breakc; tp->t_flags = tsp->tt_flags & ~TF_HUPCLS; if (tsp->tt_flags & TF_HUPCLS) tp->t_state |= TS_HUPCLS; simple_unlock(&tp->t_lock); splx(s); break; } default: return D_INVALID_OPERATION; } return D_SUCCESS; }
/* * Write to TTY. * No locks may be held. * Calls device start routine; must already be on master if * device needs to run on master. */ io_return_t char_write( register struct tty * tp, register io_req_t ior) { spl_t s; register int count; register char *data; vm_offset_t addr; io_return_t rc = D_SUCCESS; data = ior->io_data; count = ior->io_count; if (count == 0) return rc; if (!(ior->io_op & IO_INBAND)) { /* * Copy out-of-line data into kernel address space. * Since data is copied as page list, it will be * accessible. */ vm_map_copy_t copy = (vm_map_copy_t) data; kern_return_t kr; kr = vm_map_copyout(device_io_map, &addr, copy); if (kr != KERN_SUCCESS) return kr; data = (char *) addr; } /* * Check for tty operating. */ s = spltty(); simple_lock(&tp->t_lock); if ((tp->t_state & TS_CARR_ON) == 0) { if ((tp->t_state & TS_ONDELAY) == 0) { /* * No delayed writes - tell caller that device is down */ rc = D_IO_ERROR; goto out; } if (ior->io_mode & D_NOWAIT) { rc = D_WOULD_BLOCK; goto out; } } /* * Copy data into the output buffer. * Report the amount not copied. */ ior->io_residual = b_to_q(data, count, &tp->t_outq); /* * Start hardware output. */ tp->t_state &= ~TS_TTSTOP; tty_output(tp); if (tp->t_outq.c_cc > TTHIWAT(tp) || (tp->t_state & TS_CARR_ON) == 0) { /* * Do not send reply until some characters have been sent. */ ior->io_dev_ptr = (char *)tp; queue_delayed_reply(&tp->t_delayed_write, ior, char_write_done); rc = D_IO_QUEUED; } out: simple_unlock(&tp->t_lock); splx(s); if (!(ior->io_op & IO_INBAND)) (void) vm_deallocate(device_io_map, addr, ior->io_count); return rc; }