From 0105aa80020297ff4227d2f2dee05f3e2ad861df Mon Sep 17 00:00:00 2001 From: Mark Date: Tue, 21 Jan 2020 17:08:31 +0200 Subject: [PATCH] Proper line discipline for TTYs and simple line discipline for chardevs --- etc/make/conf.mk | 3 +- include/sys/amd64/hw/con.h | 3 + include/sys/amd64/hw/ps2.h | 2 + include/sys/chr.h | 12 ++- include/sys/line.h | 12 +++ include/sys/ring.h | 27 +++--- include/sys/termios.h | 34 +++++++ include/sys/tty.h | 12 ++- sys/amd64/hw/con.c | 5 ++ sys/amd64/hw/ps2.c | 94 ++++++++++--------- sys/amd64/hw/rs232.c | 2 +- sys/amd64/sys_file.c | 2 +- sys/chr.c | 37 -------- sys/line.c | 106 ++++++++++++++++++++++ sys/ring.c | 180 ++++++++++++++++--------------------- sys/tty.c | 89 ++++++++++++------ 16 files changed, 383 insertions(+), 237 deletions(-) create mode 100644 include/sys/line.h create mode 100644 sys/line.c diff --git a/etc/make/conf.mk b/etc/make/conf.mk index 1c7b151..1023877 100644 --- a/etc/make/conf.mk +++ b/etc/make/conf.mk @@ -81,7 +81,8 @@ OBJS+=$(O)/sys/debug.o \ $(O)/sys/vfs/vfs_access.o \ $(O)/sys/time.o \ $(O)/sys/thread.o \ - $(O)/sys/ctype.o + $(O)/sys/ctype.o \ + $(O)/sys/line.o # \ $(O)/sys/vfs/pty.o \ diff --git a/include/sys/amd64/hw/con.h b/include/sys/amd64/hw/con.h index 9e6551a..14fd7c1 100644 --- a/include/sys/amd64/hw/con.h +++ b/include/sys/amd64/hw/con.h @@ -4,6 +4,9 @@ struct winsize; void amd64_con_get_size(struct winsize *ws); +// For tty +int pc_con_putc(void *dev, char c); + void con_blink(void); void amd64_con_putc(int c); void amd64_con_init(void); diff --git a/include/sys/amd64/hw/ps2.h b/include/sys/amd64/hw/ps2.h index 15dfa83..3ce364c 100644 --- a/include/sys/amd64/hw/ps2.h +++ b/include/sys/amd64/hw/ps2.h @@ -1,4 +1,6 @@ #pragma once +struct chrdev; void ps2_init(void); void ps2_register_device(void); +void ps2_kbd_set_tty(struct chrdev *dev); diff --git a/include/sys/chr.h b/include/sys/chr.h index 2b6be2d..0beb724 100644 --- a/include/sys/chr.h +++ b/include/sys/chr.h @@ -1,19 +1,25 @@ #pragma once +#include "sys/termios.h" #include "sys/types.h" #include "sys/ring.h" struct chrdev { void *dev_data; + struct ring buffer; + enum { + CHRDEV_GENERIC = 0, + CHRDEV_TTY + } type; + + // TODO: move tty info somewhere else? + struct termios tc; int (*ioctl) (struct chrdev *chr, unsigned int cmd, void *arg); ssize_t (*write) (struct chrdev *chr, const void *buf, size_t pos, size_t lim); ssize_t (*read) (struct chrdev *chr, void *buf, size_t pos, size_t lim); }; -// Generic ring-buffer reader -ssize_t chr_read_ring(struct chrdev *chr, void *buf, size_t pos, size_t lim); - int chr_ioctl(struct chrdev *chr, unsigned int cmd, void *arg); ssize_t chr_write(struct chrdev *chr, const void *buf, size_t pos, size_t lim); ssize_t chr_read(struct chrdev *chr, void *buf, size_t pos, size_t lim); diff --git a/include/sys/line.h b/include/sys/line.h new file mode 100644 index 0000000..a95c7b8 --- /dev/null +++ b/include/sys/line.h @@ -0,0 +1,12 @@ +#pragma once +struct chrdev; + +// Common TTY line discipline +ssize_t line_read(struct chrdev *chr, void *buf, size_t pos, size_t lim); + +// Simple line discipline - no control, disregard termios, spit out data right after +// a single byte is available +ssize_t simple_line_read(struct chrdev *chr, void *buf, size_t pos, size_t lim); + +// TODO: invent some magic for devices like /dev/urandom and /dev/zero etc. +// they're implemented as block devices right now diff --git a/include/sys/ring.h b/include/sys/ring.h index 22ef187..df95bb4 100644 --- a/include/sys/ring.h +++ b/include/sys/ring.h @@ -2,19 +2,24 @@ #include "sys/types.h" #include "sys/spin.h" +struct thread; + +#define RING_SIGNAL_BRK (1 << 0) +#define RING_SIGNAL_EOF (1 << 1) + // Ring buffer struct ring { - char *data; - size_t wrptr; - size_t rdptr; + size_t rd, wr; size_t cap; - spin_t lock; - int post; - struct thread *listener; + char *base; + int flags; }; -void ring_post(struct ring *b); -size_t ring_avail(struct ring *b); -ssize_t ring_read(struct thread *ctx, struct ring *b, void *buf, size_t lim); -int ring_putc(struct thread *ctx, struct ring *b, char c); -void ring_init(struct ring *b, size_t cap); +int ring_readable(struct ring *b); +int ring_getc(struct thread *ctx, struct ring *b, char *c, int err); + +void ring_signal(struct ring *b, int type); +int ring_putc(struct thread *ctx, struct ring *b, char c, int wait); +int ring_write(struct thread *ctx, struct ring *b, const void *data, size_t count, int wait); + +int ring_init(struct ring *b, size_t cap); diff --git a/include/sys/termios.h b/include/sys/termios.h index 0d8dfba..2b3572c 100644 --- a/include/sys/termios.h +++ b/include/sys/termios.h @@ -1,11 +1,45 @@ #pragma once +#define TCGETS 0x5401 +#define TCSETS 0x5402 #define TIOCSPGRP 0x5410 #define TIOCGWINSZ 0x5413 +// Output flags +#define OPOST (1 << 0) +#define ONLCR (1 << 2) +#define OCRNL (1 << 4) +#define ONLRET (1 << 6) + +// Control flags +#define ECHO (1 << 4) // Echo input characters +#define ECHOE (1 << 5) // if ICANON, ERASE erases prec. character, + // WERASE erases prec. word +#define ECHOK (1 << 6) // if ICANON, KILL character erases the line +#define ECHONL (1 << 8) // if ICANON, echo newline (even if no ECHO) + +#define TERMIOS_DEFAULT \ + { \ + .c_iflag = 0, \ + .c_oflag = OPOST, \ + .c_cflag = 0, \ + .c_lflag = ECHO | ECHONL | ECHOE | ECHOK, \ + } + +typedef unsigned int tcflag_t; + struct winsize { unsigned short ws_row; unsigned short ws_col; unsigned short ws_xpixel; unsigned short ws_ypixel; }; + +struct termios { + tcflag_t c_iflag; + tcflag_t c_oflag; + tcflag_t c_cflag; + tcflag_t c_lflag; +}; + +#define TCSANOW 0 diff --git a/include/sys/tty.h b/include/sys/tty.h index 5d83a8a..d2d36ab 100644 --- a/include/sys/tty.h +++ b/include/sys/tty.h @@ -1,6 +1,12 @@ #pragma once -#include "sys/fs/node.h" +struct chrdev; + +// From keyboard handlers and stuff +void tty_control_write(struct chrdev *n, char c); +void tty_data_write(struct chrdev *n, char c); + +// From code directly to the screen +void tty_puts(struct chrdev *n, const char *s); +void tty_putc(struct chrdev *n, char c); -void tty_control_write(int n, char c); -void tty_buffer_write(int n, char c); void tty_init(void); diff --git a/sys/amd64/hw/con.c b/sys/amd64/hw/con.c index e42f0eb..53e8578 100644 --- a/sys/amd64/hw/con.c +++ b/sys/amd64/hw/con.c @@ -361,6 +361,11 @@ void amd64_con_putc(int c) { } } +int pc_con_putc(void *dev, char c) { + amd64_con_putc(c); + return 0; +} + void amd64_con_get_size(struct winsize *ws) { ws->ws_row = con_height; ws->ws_col = con_width; diff --git a/sys/amd64/hw/ps2.c b/sys/amd64/hw/ps2.c index f9e9c85..fe2611a 100644 --- a/sys/amd64/hw/ps2.c +++ b/sys/amd64/hw/ps2.c @@ -5,6 +5,7 @@ #include "sys/ctype.h" #include "sys/signum.h" #include "sys/debug.h" +#include "sys/line.h" #include "sys/tty.h" #include "sys/chr.h" #include "sys/dev.h" @@ -31,13 +32,15 @@ // Controller stuff +// TTY owning the keyboard +static struct chrdev *_keyboard_tty = NULL; static uint32_t ps2_kbd_mods = 0; static uint32_t ps2_aux_state = 0; static struct chrdev _ps2_mouse = { .dev_data = NULL, .write = NULL, - .read = chr_read_ring, + .read = simple_line_read, .ioctl = NULL }; @@ -73,6 +76,10 @@ static const char ps2_key_table_1[128] = { [0x7F] = 0 }; +void ps2_kbd_set_tty(struct chrdev *dev) { + _keyboard_tty = dev; +} + uint32_t ps2_irq_keyboard(void *ctx) { uint8_t st = inb(0x64); @@ -99,43 +106,48 @@ uint32_t ps2_irq_keyboard(void *ctx) { ps2_kbd_mods ^= PS2_MOD_CAPS; } + if (!_keyboard_tty) { + // No TTY to send strokes to + return IRQ_HANDLED; + } + if (!(key & 0x80) && !(ps2_kbd_mods & PS2_MOD_CTRL)) { // Special keys switch (key) { case 0x01: - tty_buffer_write(0, '\033'); + tty_data_write(_keyboard_tty, '\033'); return IRQ_HANDLED; case 0x47: - tty_buffer_write(0, '\033'); - tty_buffer_write(0, '['); - tty_buffer_write(0, '['); - tty_buffer_write(0, 'H'); + tty_data_write(_keyboard_tty, '\033'); + tty_data_write(_keyboard_tty, '['); + tty_data_write(_keyboard_tty, '['); + tty_data_write(_keyboard_tty, 'H'); return IRQ_HANDLED; case 0x48: - tty_buffer_write(0, '\033'); - tty_buffer_write(0, '['); - tty_buffer_write(0, 'A'); + tty_data_write(_keyboard_tty, '\033'); + tty_data_write(_keyboard_tty, '['); + tty_data_write(_keyboard_tty, 'A'); return IRQ_HANDLED; case 0x50: - tty_buffer_write(0, '\033'); - tty_buffer_write(0, '['); - tty_buffer_write(0, 'B'); + tty_data_write(_keyboard_tty, '\033'); + tty_data_write(_keyboard_tty, '['); + tty_data_write(_keyboard_tty, 'B'); return IRQ_HANDLED; case 0x4D: - tty_buffer_write(0, '\033'); - tty_buffer_write(0, '['); - tty_buffer_write(0, 'C'); + tty_data_write(_keyboard_tty, '\033'); + tty_data_write(_keyboard_tty, '['); + tty_data_write(_keyboard_tty, 'C'); return IRQ_HANDLED; case 0x4F: - tty_buffer_write(0, '\033'); - tty_buffer_write(0, '['); - tty_buffer_write(0, '['); - tty_buffer_write(0, 'F'); + tty_data_write(_keyboard_tty, '\033'); + tty_data_write(_keyboard_tty, '['); + tty_data_write(_keyboard_tty, '['); + tty_data_write(_keyboard_tty, 'F'); return IRQ_HANDLED; case 0x4B: - tty_buffer_write(0, '\033'); - tty_buffer_write(0, '['); - tty_buffer_write(0, 'D'); + tty_data_write(_keyboard_tty, '\033'); + tty_data_write(_keyboard_tty, '['); + tty_data_write(_keyboard_tty, 'D'); return IRQ_HANDLED; default: break; @@ -152,15 +164,15 @@ uint32_t ps2_irq_keyboard(void *ctx) { } if (key_char != 0) { - tty_buffer_write(0, key_char); + tty_data_write(_keyboard_tty, key_char); } } else { switch (key) { case 0x20: // ^D - tty_control_write(0, 'd'); + tty_control_write(_keyboard_tty, 'd'); break; case 0x2E: // ^C - tty_control_write(0, 'c'); + tty_control_write(_keyboard_tty, 'c'); break; } } @@ -179,6 +191,9 @@ uint32_t ps2_irq_mouse(void *arg) { packet[i] = inb(0x60); } + // TODO: ability to "hold" and "release" the buffer + // so objects are written at least in one piece + int16_t dx, dy; dx = packet[1]; dx -= (packet[0] << 4) & 0x100; @@ -186,30 +201,13 @@ uint32_t ps2_irq_mouse(void *arg) { dy = packet[2]; dy -= (packet[0] << 3) & 0x100; - if ((packet[0] & PS2_AUX_BM) != (ps2_aux_state & PS2_AUX_BM)) { - if (packet[0] & PS2_AUX_BM) { - ring_putc(NULL, &_ps2_mouse.buffer, 'M'); - } else { - ring_putc(NULL, &_ps2_mouse.buffer, 'm'); - } - } - if ((packet[0] & PS2_AUX_BR) != (ps2_aux_state & PS2_AUX_BR)) { - if (packet[0] & PS2_AUX_BR) { - ring_putc(NULL, &_ps2_mouse.buffer, 'R'); - } else { - ring_putc(NULL, &_ps2_mouse.buffer, 'r'); - } - } - if ((packet[0] & PS2_AUX_BL) != (ps2_aux_state & PS2_AUX_BL)) { - if (packet[0] & PS2_AUX_BL) { - ring_putc(NULL, &_ps2_mouse.buffer, 'L'); - } else { - ring_putc(NULL, &_ps2_mouse.buffer, 'l'); - } - } - if ((packet[0] & 7) != ps2_aux_state) { - ring_post(&_ps2_mouse.buffer); + if (dx || dy) { + // Write "delta" event + ring_putc(NULL, &_ps2_mouse.buffer, 'd', 0); + ring_write(NULL, &_ps2_mouse.buffer, &dx, 2, 0); + ring_write(NULL, &_ps2_mouse.buffer, &dy, 2, 0); } + ps2_aux_state = packet[0] & 7; return IRQ_HANDLED; diff --git a/sys/amd64/hw/rs232.c b/sys/amd64/hw/rs232.c index 7b19080..04e4efb 100644 --- a/sys/amd64/hw/rs232.c +++ b/sys/amd64/hw/rs232.c @@ -48,7 +48,7 @@ static uint32_t rs232_irq(void *ctx) { c = '\n'; } - tty_buffer_write(0, c); + // TODO: send data to bound TTY } return has_data ? IRQ_HANDLED : IRQ_UNHANDLED; diff --git a/sys/amd64/sys_file.c b/sys/amd64/sys_file.c index 60581a4..5d93f37 100644 --- a/sys/amd64/sys_file.c +++ b/sys/amd64/sys_file.c @@ -269,7 +269,7 @@ int sys_select(int nfds, fd_set *rd, fd_set *wr, fd_set *exc, struct timeval *tv struct chrdev *chr = fd->vnode->dev; _assert(chr); - if (ring_avail(&chr->buffer) > 0) { + if (ring_readable(&chr->buffer) > 0) { FD_SET(i, rd); res = 0; goto done; diff --git a/sys/chr.c b/sys/chr.c index 4686989..6506761 100644 --- a/sys/chr.c +++ b/sys/chr.c @@ -34,40 +34,3 @@ ssize_t chr_read(struct chrdev *chr, void *buf, size_t off, size_t count) { } return chr->read(chr, buf, off, count); } - -ssize_t chr_read_ring(struct chrdev *chr, void *buf, size_t off, size_t lim) { - struct thread *thr = get_cpu()->thread; - _assert(thr); - char ibuf[16]; - - if (lim == 0) { - return 0; - } - - size_t rem = lim; - size_t p = 0; - - while (rem) { - if (chr->buffer.post) { - break; - } - ssize_t rd = ring_read(thr, &chr->buffer, ibuf, MIN(16, rem)); - if (rd <= 0) { - // Interrupt or end of stream - break; - } - memcpy((char *) buf + p, ibuf, rd); - - rem -= rd; - p += rd; - - if (thr->flags & THREAD_INTERRUPTED) { - thr->flags &= ~THREAD_INTERRUPTED; - return p; - } - } - // Reset post bit - chr->buffer.post = 0; - - return p; -} diff --git a/sys/line.c b/sys/line.c new file mode 100644 index 0000000..0b0db52 --- /dev/null +++ b/sys/line.c @@ -0,0 +1,106 @@ +#include "sys/ring.h" +#include "sys/debug.h" +#include "sys/assert.h" +#include "sys/chr.h" +#include "sys/tty.h" +#include "sys/amd64/cpu.h" + +ssize_t line_read(struct chrdev *chr, void *buf, size_t pos, size_t lim) { + struct thread *thr = get_cpu()->thread; + _assert(chr->type == CHRDEV_TTY); + struct ring *r = &chr->buffer; + + size_t rd = 0; + size_t rem = lim; + char *wr = buf; + char c; + + if (r->flags & RING_SIGNAL_EOF) { + return -1; + } + + // NO ICANON YET + // TODO: pass this through for non-canonical mode + while (rem) { + if (ring_getc(thr, r, &c, 0) < 0) { + // TODO: make this optional via termios + if (r->flags & RING_SIGNAL_EOF) { + // ^D + return rd; + } else { + // ^C + // Reset the state + rem = lim; + wr = buf; + rd = 0; + continue; + } + } + + // TODO: ICANON + if (c == '\033') { + if (rem >= 2) { + *wr++ = '^'; + *wr++ = '['; + } + rem -= 2; + rd += 2; + continue; + } + + if (c == '\b' && (chr->tc.c_lflag & ECHOE)) { + if (rd) { + tty_puts(chr, "\033[D \033[D"); + + --wr; + ++rem; + --rd; + } + continue; + } + + *wr++ = c; + ++rd; + --rem; + + if (c == '\n') { + break; + } + } + + return rd; +} + +ssize_t simple_line_read(struct chrdev *chr, void *buf, size_t pos, size_t lim) { + struct thread *thr = get_cpu()->thread; + struct ring *r = &chr->buffer; + + size_t rd = 0; + size_t rem = lim; + char *wr = buf; + + // Non-TTYs don't support breaks/signals + do { + if (!ring_readable(r)) { + asm volatile ("sti; hlt; cli"); + } else { + break; + } + } while (1); + + // TODO: properly handle interrupts + while (ring_readable(r)) { + if (ring_getc(thr, r, wr, 1) < 0) { + break; + } + --rem; + ++wr; + ++rd; + } + + if (rd == 0) { + return -1; + } + + return rd; +} diff --git a/sys/ring.c b/sys/ring.c index 5da874e..695b6c2 100644 --- a/sys/ring.c +++ b/sys/ring.c @@ -4,136 +4,108 @@ #include "sys/ring.h" #include "sys/heap.h" -size_t ring_avail(struct ring *ring) { - if (ring->rdptr == ring->wrptr) { +int ring_readable(struct ring *ring) { + // | . . . . R X X X W . . . | + if (ring->rd <= ring->wr) { + return ring->wr - ring->rd; + // | X X W . . . . . . R X X | + } else { + return ring->wr + (ring->cap - ring->rd); + } +} + +static int ring_writable(struct ring *ring) { + if (ring->wr == ring->cap - 1 && ring->rd == 0) { return 0; } - if (ring->rdptr > ring->wrptr) { - return (ring->cap - ring->rdptr) + ring->wrptr; + // | X X X X R . . . W X X X | + if (ring->rd <= ring->wr) { + return ring->wr + (ring->cap - ring->rd); + // | . . W X X X X R . . . . | } else { - return (ring->wrptr - ring->rdptr); + if (ring->rd == ring->wr + 1) { + return 0; + } + return ring->rd - ring->wr; } } -size_t ring_writable(struct ring *ring) { - if (ring->rdptr == ring->wrptr) { - return ring->cap - 1; - } - - if (ring->rdptr > ring->wrptr) { - return ring->rdptr - ring->wrptr - 1; +static void ring_advance_read(struct ring *ring) { + if (ring->rd == ring->cap - 1) { + ring->rd = 0; } else { - return (ring->cap - ring->wrptr) + ring->rdptr - 1; + ++ring->rd; } } -void ring_post(struct ring *ring) { - ring->post = 1; -} - -static inline void ring_advance_read(struct ring *ring) { - ring->rdptr++; - if (ring->rdptr == ring->cap) { - ring->rdptr = 0; +static void ring_advance_write(struct ring *ring) { + if (ring->wr == ring->cap - 1) { + ring->wr = 0; + } else { + ++ring->wr; } } -static inline void ring_advance_write(struct ring *ring) { - ring->wrptr++; - if (ring->wrptr == ring->cap) { - ring->wrptr = 0; - } -} - -int ring_getc(struct thread *ctx, struct ring *ring, char *out) { - uintptr_t flags; - - //spin_lock_irqsave(&ring->lock, &flags); - - do { - if (ring_avail(ring)) { - break; - } - //spin_release(&ring->lock); - ctx->flags |= THREAD_WAITING; - ctx->flags &= ~THREAD_INTERRUPTED; - asm volatile ("sti; hlt"); - if (ring->post) { - break; - } - if (ctx->flags & (THREAD_INTERRUPTED | THREAD_EOF)) { +int ring_getc(struct thread *ctx, struct ring *ring, char *c, int err) { + if (err) { + if (!ring_readable(ring)) { return -1; } - ctx->flags &= ~THREAD_WAITING; - //spin_lock(&ring->lock); - } while (1); + } else { + do { + if (ring->flags & (RING_SIGNAL_BRK | RING_SIGNAL_EOF)) { + ring->flags &= ~RING_SIGNAL_BRK; + // TODO: send SIGINT on break + return -1; + } + if (!ring_readable(ring)) { + asm volatile ("sti; hlt; cli"); + } else { + break; + } + } while (1); + } - *out = ring->data[ring->rdptr]; + *c = ring->base[ring->rd]; ring_advance_read(ring); - - //spin_release_irqrestore(&ring->lock, &flags); - return 0; } -ssize_t ring_read(struct thread *ctx, struct ring *ring, void *buf, size_t count) { - size_t rem = count; - size_t pos = 0; - - while (rem) { - if (ring->post) { - break; +int ring_putc(struct thread *ctx, struct ring *ring, char c, int wait) { + if (wait) { + while (!ring_writable(ring)) { + asm volatile ("sti; hlt; cli"); } - - if (ring_getc(ctx, ring, (char *) buf + pos) != 0) { - break; - } - - --rem; - ++pos; } - if (pos == 0) { + ring->base[ring->wr] = c; + ring_advance_write(ring); + return 0; +} + +int ring_write(struct thread *ctx, struct ring *ring, const void *buf, size_t len, int wait) { + for (size_t i = 0; i < len; ++i) { + if (ring_putc(ctx, ring, ((const char *) buf)[i], wait) != 0) { + return -1; + } + } + return 0; +} + +void ring_signal(struct ring *r, int s) { + r->flags |= s; +} + +int ring_init(struct ring *r, size_t cap) { + r->cap = cap; + r->rd = 0; + r->wr = 0; + r->flags = 0; + if (!(r->base = kmalloc(cap))) { return -1; } - - return pos; + return 0; } -int ring_putc(struct thread *ctx, struct ring *ring, char c) { - uintptr_t flags; - - //spin_lock_irqsave(&ring->lock, &flags); - - do { - if (ring_writable(ring)) { - break; - } - //spin_release(&ring->lock); - if (ctx) { - asm volatile ("sti; hlt"); - } else { - asm volatile ("sti; hlt"); - } - //spin_lock(&ring->lock); - } while (1); - - - ring->data[ring->wrptr] = c; - ring_advance_write(ring); - - //spin_release_irqrestore(&ring->lock, &flags); - - return 0; -} - -void ring_init(struct ring *ring, size_t cap) { - ring->lock = 0; - ring->wrptr = 0; - ring->rdptr = 0; - ring->data = kmalloc(cap); - _assert(ring->data); - ring->cap = cap; -} diff --git a/sys/tty.c b/sys/tty.c index a07fc87..10d0c84 100644 --- a/sys/tty.c +++ b/sys/tty.c @@ -1,5 +1,7 @@ #include "sys/tty.h" #include "sys/chr.h" +#include "sys/ctype.h" +#include "sys/line.h" #include "sys/errno.h" #include "sys/debug.h" #include "sys/sched.h" @@ -12,43 +14,47 @@ #include "sys/mm.h" #include "sys/amd64/cpu.h" #include "sys/amd64/hw/con.h" +#include "sys/amd64/hw/ps2.h" #include "sys/dev.h" -#define DEV_TTY(n) (n ## ULL) - static ssize_t tty_write(struct chrdev *tty, const void *buf, size_t pos, size_t lim); static int tty_ioctl(struct chrdev *tty, unsigned int cmd, void *arg); struct tty_data { // Process group ID of the foreground group (session leader) pid_t fg_pgid; - // Number - int tty_n; - // TODO: make console screen and keyboard character devices + // Output device info + void *out_dev; + int (*out_putc) (void *dev, char c); }; +// Keyboard + display static struct tty_data _dev_tty0_data = { .fg_pgid = 1, - .tty_n = DEV_TTY(0) + .out_dev = 0, + .out_putc = pc_con_putc }; static struct chrdev _dev_tty0 = { + .type = CHRDEV_TTY, .dev_data = &_dev_tty0_data, + .tc = TERMIOS_DEFAULT, .write = tty_write, - .read = chr_read_ring, + // Line discipline + .read = line_read, .ioctl = tty_ioctl }; -static void tty_control(struct chrdev *tty, char c) { +void tty_control_write(struct chrdev *tty, char c) { struct tty_data *data = tty->dev_data; _assert(data); switch (c) { case 'd': - // Close stdin for process group - sched_close_stdin_group(data->fg_pgid); + ring_signal(&tty->buffer, RING_SIGNAL_EOF); break; case 'c': + ring_signal(&tty->buffer, RING_SIGNAL_BRK); sched_signal_group(data->fg_pgid, SIGINT); break; default: @@ -56,41 +62,60 @@ static void tty_control(struct chrdev *tty, char c) { } } -void tty_control_write(int tty_no, char c) { - kdebug("^%c on tty%d\n", c, tty_no); - switch (tty_no) { - case 0: - tty_control(&_dev_tty0, c); +void tty_data_write(struct chrdev *tty, char c) { + _assert(tty && tty->type == CHRDEV_TTY); + ring_putc(NULL, &tty->buffer, c, 0); + + switch (c) { + case '\n': + // TODO: this should also check ICANON + if (tty->tc.c_lflag & ECHONL) { + tty_putc(tty, c); + } break; + case '\b': + break; + case '\033': + // TODO: ICANON + tty_puts(tty, "^["); + break; + default: + // This ignores ICANON + if (tty->tc.c_lflag & ECHO) { + tty_putc(tty, c); + } } } -// This function receives keystrokes from keyboard drivers -void tty_buffer_write(int tty_no, char c) { - switch (tty_no) { - case 0: - ring_putc(NULL, &_dev_tty0.buffer, c); - break; +void tty_puts(struct chrdev *tty, const char *s) { + for (; *s; ++s) { + tty_putc(tty, *s); } } +void tty_putc(struct chrdev *tty, char c) { + struct tty_data *data = tty->dev_data; + _assert(data); + _assert(data->out_putc); + data->out_putc(data->out_dev, c); +} + void tty_init(void) { ring_init(&_dev_tty0.buffer, 16); + // Bind tty0 to keyboard + ps2_kbd_set_tty(&_dev_tty0); + dev_add(DEV_CLASS_CHAR, DEV_CHAR_TTY, &_dev_tty0, "tty0"); } -// TODO: multiple ttys static ssize_t tty_write(struct chrdev *tty, const void *buf, size_t pos, size_t lim) { struct tty_data *data = tty->dev_data; _assert(data); - if (data->tty_n != DEV_TTY(0)) { - return -EINVAL; - } - - // TODO: buffer sanity checks + _assert(data->out_putc); for (size_t i = 0; i < lim; ++i) { - amd64_con_putc(((const char *) buf)[i]); + // This is printed as-is without tty_putc wrapper + data->out_putc(data->out_dev, ((const char *) buf)[i]); } return lim; } @@ -100,11 +125,19 @@ static int tty_ioctl(struct chrdev *tty, unsigned int cmd, void *arg) { _assert(data); switch (cmd) { + case TCGETS: + memcpy(arg, &tty->tc, sizeof(struct termios)); + return 0; + case TCSETS: + memcpy(&tty->tc, arg, sizeof(struct termios)); + return 0; case TIOCGWINSZ: // TODO: See comment on tty data struct amd64_con_get_size(arg); return 0; case TIOCSPGRP: + // Clear interrupts and stuff + tty->buffer.flags = 0; data->fg_pgid = *(pid_t *) arg; return 0; default: