From ed31f8283ca62f550da28456bc190250d6495815 Mon Sep 17 00:00:00 2001 From: Ian Moffett Date: Sat, 18 May 2024 22:54:14 -0400 Subject: kernel: tty: Handle input processing better - Fix copying logic in tty_read() - Handle ICANON correctly - Add ECHO c_lflag bit - Add TTY_SOURCE_DEV and TTY_SOURCE_RAW defines Signed-off-by: Ian Moffett --- sys/arch/amd64/isa/i8042.c | 2 +- sys/include/sys/termios.h | 1 + sys/include/sys/tty.h | 6 ++- sys/kern/kern_syslog.c | 2 +- sys/kern/kern_tty.c | 109 +++++++++++++++++++++++++++++++-------------- 5 files changed, 84 insertions(+), 36 deletions(-) (limited to 'sys') diff --git a/sys/arch/amd64/isa/i8042.c b/sys/arch/amd64/isa/i8042.c index 0f160d3..41b74ef 100644 --- a/sys/arch/amd64/isa/i8042.c +++ b/sys/arch/amd64/isa/i8042.c @@ -234,7 +234,7 @@ kb_isr(void *sf) while (__TEST(inb(I8042_STATUS), I8042_OBUF_FULL)) { data = inb(I8042_DATA); if (scancode_to_chr(data, &c) == 0) { - tty_putc(&g_root_tty, c); + tty_putc(&g_root_tty, c, TTY_SOURCE_DEV); } } spinlock_release(&data_lock); diff --git a/sys/include/sys/termios.h b/sys/include/sys/termios.h index 963c885..1fda67e 100644 --- a/sys/include/sys/termios.h +++ b/sys/include/sys/termios.h @@ -50,6 +50,7 @@ * Local flags */ #define ICANON 0x00000001U +#define ECHO 0x00000002U typedef uint32_t tcflag_t; typedef uint32_t speed_t; diff --git a/sys/include/sys/tty.h b/sys/include/sys/tty.h index c53ea73..342a1a7 100644 --- a/sys/include/sys/tty.h +++ b/sys/include/sys/tty.h @@ -38,6 +38,9 @@ #define TTY_RING_SIZE 32 +#define TTY_SOURCE_RAW 0x0001U /* Raw text */ +#define TTY_SOURCE_DEV 0x0002U /* Input from device (e.g keyboard) */ + struct tty_ring { char data[TTY_RING_SIZE]; /* Ring data */ off_t enq_index; /* Enqueue index */ @@ -48,6 +51,7 @@ struct tty { dev_t id; struct vcons_screen *scr; /* Console screen */ struct tty_ring ring; /* Input ring */ + struct tty_ring outring; /* Output ring */ struct spinlock rlock; /* Ring lock */ struct termios termios; /* Termios structure */ struct device *dev; /* Device pointer */ @@ -56,7 +60,7 @@ struct tty { extern struct tty g_root_tty; dev_t tty_attach(struct tty *tty); -int tty_putc(struct tty *tty, int c); +int tty_putc(struct tty *tty, int c, int flags); int tty_putstr(struct tty *tty, const char *s, size_t count); ssize_t tty_flush(struct tty *tty); diff --git a/sys/kern/kern_syslog.c b/sys/kern/kern_syslog.c index 3ff4e8c..88ae8bc 100644 --- a/sys/kern/kern_syslog.c +++ b/sys/kern/kern_syslog.c @@ -45,7 +45,7 @@ syslog_write(const char *s, size_t len) #if defined(__SERIAL_DEBUG) serial_dbgch(*tmp_s); #endif /* defined(__SERIAL_DEBUG) */ - tty_putc(&g_root_tty, *tmp_s++); + tty_putc(&g_root_tty, *tmp_s++, TTY_SOURCE_RAW); } tty_flush(&g_root_tty); diff --git a/sys/kern/kern_tty.c b/sys/kern/kern_tty.c index 8446e2b..cb3e30a 100644 --- a/sys/kern/kern_tty.c +++ b/sys/kern/kern_tty.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include @@ -41,6 +42,9 @@ struct tty g_root_tty = { .ring = { .enq_index = 0, .deq_index = 0, + }, + .termios = { + .c_lflag = ICANON | ECHO } }; @@ -52,6 +56,35 @@ tty_alloc_id(void) return id++; } +static inline bool +tty_is_special(char c) +{ + return c < 0x1F; +} + +static inline void +tty_reset_ring(struct tty_ring *ring) +{ + ring->enq_index = 0; + ring->deq_index = 0; +} + +static void +tty_process(struct tty *tty, char c, bool echo) +{ + const struct termios *termios; + bool canon, special; + + termios = &tty->termios; + canon = __TEST(termios->c_lflag, ICANON); + special = tty_is_special(c); + + if (canon && special) + vcons_process_output(tty->scr, c); + if (echo && !special) + vcons_putch(tty->scr, c); +} + /* * Flushes the TTY ring buffer. * @@ -63,31 +96,30 @@ static ssize_t __tty_flush(struct tty *tty) { struct tty_ring *ring = &tty->ring; - const struct termios *termios; + struct tty_ring *outring = &tty->outring; size_t count = 0; char tmp; - termios = &tty->termios; - /* Do we have any data left? */ if (ring->deq_index >= ring->enq_index) return -EAGAIN; /* - * Flush the ring to the console if we are in - * canonical mode. Otherwise, just throw away - * the bytes. + * Flush the input ring to the output ring + * to allow user programs to fetch from it + * with /dev/ttyN. */ - if (__TEST(termios->c_lflag, ICANON)) { - while (ring->deq_index < ring->enq_index) { - tmp = ring->data[ring->deq_index++]; - vcons_putch(tty->scr, tmp); - ++count; - } + while (ring->deq_index < ring->enq_index) { + tmp = ring->data[ring->deq_index++]; + outring->data[outring->enq_index++] = tmp; + + if (outring->enq_index > TTY_RING_SIZE) + tty_reset_ring(outring); + + ++count; } - ring->enq_index = 0; - ring->deq_index = 0; + tty_reset_ring(ring); return count; } @@ -98,11 +130,15 @@ __tty_flush(struct tty *tty) static int tty_dev_read(struct device *dev, struct sio_txn *sio) { - struct tty_ring *ring = &g_root_tty.ring; - size_t len; + struct tty_ring *ring = &g_root_tty.outring; + size_t len, max_len; spinlock_acquire(&g_root_tty.rlock); - len = (ring->enq_index - ring->deq_index); + max_len = (ring->enq_index - ring->deq_index); + len = sio->len; + + if (len > max_len) + len = max_len; /* * Transfer data from the TTY ring with SIO and @@ -112,8 +148,7 @@ tty_dev_read(struct device *dev, struct sio_txn *sio) * TTY, add support for multiple TTYs. */ memcpy(sio->buf, ring->data, len); - __tty_flush(&g_root_tty); - + tty_reset_ring(ring); spinlock_release(&g_root_tty.rlock); return len; } @@ -139,30 +174,30 @@ tty_flush(struct tty *tty) * @c: Character to write. */ int -tty_putc(struct tty *tty, int c) +tty_putc(struct tty *tty, int c, int flags) { struct tty_ring *ring; const struct termios *termios; - bool canon; + bool canon, echo; ring = &tty->ring; termios = &tty->termios; + canon = __TEST(termios->c_lflag, ICANON); + echo = __TEST(termios->c_lflag, ECHO); spinlock_acquire(&tty->rlock); ring->data[ring->enq_index++] = c; /* - * If we aren't in canonical mode, just write directly to - * the console. - * - * XXX: Won't need to worry about extra bytes being printed - * as __tty_flush() won't flush them to the console - * when we aren't in canonical mode. + * Process the characters for both device input + * and raw input. Device input will only be echoed + * if the ECHO bit is set within c_lflag */ - if (!canon) { - vcons_putch(tty->scr, c); - } + if (__TEST(flags, TTY_SOURCE_DEV) && echo) + tty_process(tty, c, echo); + if (__TEST(flags, TTY_SOURCE_RAW)) + tty_process(tty, c, true); /* * If we are in canonical mode and we have a linefeed ('\n') @@ -172,11 +207,19 @@ tty_putc(struct tty *tty, int c) __tty_flush(tty); } - /* Flush if the buffer is full */ - if (ring->enq_index >= TTY_RING_SIZE) { + /* + * Just flush the ring if we aren't in canonical + * mode. + */ + if (!canon) { __tty_flush(tty); } + /* Reset the ring if it is full */ + if (ring->enq_index >= TTY_RING_SIZE) { + tty_reset_ring(ring); + } + spinlock_release(&tty->rlock); return 0; } @@ -192,7 +235,7 @@ int tty_putstr(struct tty *tty, const char *s, size_t count) { for (size_t i = 0; i < count; ++i) { - tty_putc(tty, *s++); + tty_putc(tty, *s++, TTY_SOURCE_RAW); } return 0; -- cgit v1.2.3