From dad41847e3c8c2db23cddee50235347a5b58995e Mon Sep 17 00:00:00 2001 From: Mark Date: Wed, 22 Jan 2020 15:01:37 +0200 Subject: [PATCH] Add ttyS0 --- include/sys/amd64/hw/rs232.h | 4 ++++ include/sys/config.h | 1 + include/sys/ring.h | 1 + include/sys/termios.h | 5 ++++- sys/amd64/hw/rs232.c | 43 +++++++++++++++++++++++++++++++++--- sys/amd64/sys_file.c | 16 ++++++++++++-- sys/config.c | 2 ++ sys/init.c | 5 +++-- sys/line.c | 12 +++++++++- sys/tty.c | 31 +++++++++++++++++++++++--- 10 files changed, 108 insertions(+), 12 deletions(-) diff --git a/include/sys/amd64/hw/rs232.h b/include/sys/amd64/hw/rs232.h index 8588ee0..516be82 100644 --- a/include/sys/amd64/hw/rs232.h +++ b/include/sys/amd64/hw/rs232.h @@ -2,6 +2,10 @@ #include #define RS232_COM1 0x3F8 +struct chrdev; + +int rs232_putc(void *dev, char c); +void rs232_set_tty(uint16_t com, struct chrdev *tty); void rs232_init(uint16_t port); void rs232_send(uint16_t port, char c); diff --git a/include/sys/config.h b/include/sys/config.h index 0ccc3da..75398d1 100644 --- a/include/sys/config.h +++ b/include/sys/config.h @@ -5,6 +5,7 @@ enum { CFG_ROOT = 1, CFG_INIT, CFG_RDINIT, + CFG_CONSOLE, __CFG_SIZE }; diff --git a/include/sys/ring.h b/include/sys/ring.h index df95bb4..20403e8 100644 --- a/include/sys/ring.h +++ b/include/sys/ring.h @@ -6,6 +6,7 @@ struct thread; #define RING_SIGNAL_BRK (1 << 0) #define RING_SIGNAL_EOF (1 << 1) +#define RING_SIGNAL_RET (1 << 2) // Ring buffer struct ring { diff --git a/include/sys/termios.h b/include/sys/termios.h index 2b3572c..6f15f3a 100644 --- a/include/sys/termios.h +++ b/include/sys/termios.h @@ -5,6 +5,9 @@ #define TIOCSPGRP 0x5410 #define TIOCGWINSZ 0x5413 +// Input flags +#define ICANON (1 << 1) + // Output flags #define OPOST (1 << 0) #define ONLCR (1 << 2) @@ -20,7 +23,7 @@ #define TERMIOS_DEFAULT \ { \ - .c_iflag = 0, \ + .c_iflag = ICANON, \ .c_oflag = OPOST, \ .c_cflag = 0, \ .c_lflag = ECHO | ECHONL | ECHOE | ECHOK, \ diff --git a/sys/amd64/hw/rs232.c b/sys/amd64/hw/rs232.c index 04e4efb..bcffb4b 100644 --- a/sys/amd64/hw/rs232.c +++ b/sys/amd64/hw/rs232.c @@ -29,12 +29,27 @@ #define RS232_LSR_TEMT 64 #define RS232_LSR_ERR 128 +static struct chrdev *_rs232_tty[4] = {NULL}; + void rs232_send(uint16_t port, char c) { while (!(inb(port + RS232_LSR) & RS232_LSR_THRE)) { } outb(port, c); } +int rs232_putc(void *dev, char c) { + rs232_send(((uintptr_t) dev) & 0xFFFF, c); + return 0; +} + +void rs232_set_tty(uint16_t com, struct chrdev *tty) { + switch (com) { + case RS232_COM1: + _rs232_tty[0] = tty; + break; + } +} + static uint32_t rs232_irq(void *ctx) { uint16_t port = (uint16_t) (uintptr_t) ctx; int has_data = 0; @@ -44,11 +59,33 @@ static uint32_t rs232_irq(void *ctx) { has_data = 1; uint8_t c = inb(port); - if (c == '\r') { - c = '\n'; + if (c < ' ') { + kdebug("Special character in serial input: 0x%02x\n", c); } - // TODO: send data to bound TTY + // Translate serial codes to proper values recognized by kernel + // line discipline. TODO: make c_cc in termios handle special characters + int control = 0; + + switch (c) { + case 0x0D: + c = '\n'; + break; + case 0x7F: + c = '\b'; + break; + case 0x04: + control = 'd'; + break; + } + + if (_rs232_tty[0]) { + if (!control) { + tty_data_write(_rs232_tty[0], c); + } else { + tty_control_write(_rs232_tty[0], control); + } + } } return has_data ? IRQ_HANDLED : IRQ_UNHANDLED; diff --git a/sys/amd64/sys_file.c b/sys/amd64/sys_file.c index 5d93f37..556f0da 100644 --- a/sys/amd64/sys_file.c +++ b/sys/amd64/sys_file.c @@ -256,7 +256,10 @@ int sys_select(int nfds, fd_set *rd, fd_set *wr, fd_set *exc, struct timeval *tv } } - uint64_t deadline = tv->tv_sec * 1000000000ULL + tv->tv_usec * 1000ULL + system_time; + uint64_t deadline = (uint64_t) -1; + if (tv) { + deadline = tv->tv_sec * 1000000000ULL + tv->tv_usec * 1000ULL + system_time; + } int res; while (1) { @@ -269,11 +272,20 @@ 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_readable(&chr->buffer) > 0) { + if (chr->buffer.flags & (RING_SIGNAL_EOF | RING_SIGNAL_BRK | RING_SIGNAL_RET)) { FD_SET(i, rd); res = 0; goto done; } + + // Report data available for non-canonical devices + if (chr->type == CHRDEV_TTY && !(chr->tc.c_iflag & ICANON)) { + if (ring_readable(&chr->buffer) > 0) { + FD_SET(i, rd); + res = 0; + goto done; + } + } } } diff --git a/sys/config.c b/sys/config.c index c68628f..4eb04a6 100644 --- a/sys/config.c +++ b/sys/config.c @@ -16,6 +16,7 @@ static const struct __kernel_cmdline_pair { { "root", CFG_ROOT, VALUE_STRING }, { "init", CFG_INIT, VALUE_STRING }, { "rdinit", CFG_RDINIT, VALUE_STRING }, + { "console", CFG_CONSOLE, VALUE_STRING }, }; // Default config @@ -24,6 +25,7 @@ uintptr_t kernel_config[__CFG_SIZE] = { [CFG_ROOT] = (uintptr_t) "ram0", [CFG_INIT] = (uintptr_t) "/init", [CFG_RDINIT] = (uintptr_t) "/init", + [CFG_CONSOLE] = (uintptr_t) "tty0", }; static int parse_number(const char *s, intptr_t *res) { diff --git a/sys/init.c b/sys/init.c index 8565987..4cb63d6 100644 --- a/sys/init.c +++ b/sys/init.c @@ -22,6 +22,7 @@ extern uintptr_t argp_copy(struct thread *thr, const char *const argv[], size_t *arg_count); static int user_init_start(const char *init_filename) { + const char *init_console = (const char *) kernel_config[CFG_CONSOLE]; struct ofile fd; struct stat st; struct thread *user_init; @@ -100,8 +101,8 @@ static int user_init_start(const char *init_filename) { stderr = stdout; struct vnode *stdout_device; - if ((res = dev_find(DEV_CLASS_CHAR, "tty0", &stdout_device)) != 0) { - panic("%s: %s\n", "tty0", kstrerror(res)); + if ((res = dev_find(DEV_CLASS_CHAR, init_console, &stdout_device)) != 0) { + panic("%s: %s\n", init_console, kstrerror(res)); } if ((res = vfs_open_vnode(&user_init->ioctx, stdin, stdout_device, O_RDONLY)) != 0) { diff --git a/sys/line.c b/sys/line.c index 0b0db52..6974444 100644 --- a/sys/line.c +++ b/sys/line.c @@ -4,6 +4,7 @@ #include "sys/chr.h" #include "sys/tty.h" #include "sys/amd64/cpu.h" +#include "sys/line.h" ssize_t line_read(struct chrdev *chr, void *buf, size_t pos, size_t lim) { struct thread *thr = get_cpu()->thread; @@ -15,8 +16,16 @@ ssize_t line_read(struct chrdev *chr, void *buf, size_t pos, size_t lim) { char *wr = buf; char c; + // Only needed for stuff like select(), which is called before read() + chr->buffer.flags &= ~RING_SIGNAL_RET; + if (r->flags & RING_SIGNAL_EOF) { - return -1; + return 0; + } + + if (!(chr->tc.c_iflag & ICANON)) { + // Just spit out data as soon as it's available + return simple_line_read(chr, buf, pos, lim); } // NO ICANON YET @@ -68,6 +77,7 @@ ssize_t line_read(struct chrdev *chr, void *buf, size_t pos, size_t lim) { } } + chr->buffer.flags &= ~RING_SIGNAL_RET; return rd; } diff --git a/sys/tty.c b/sys/tty.c index 10d0c84..0fe34d5 100644 --- a/sys/tty.c +++ b/sys/tty.c @@ -10,6 +10,7 @@ #include "sys/string.h" #include "sys/assert.h" #include "sys/termios.h" +#include "sys/amd64/hw/rs232.h" #include "sys/ring.h" #include "sys/mm.h" #include "sys/amd64/cpu.h" @@ -45,6 +46,23 @@ static struct chrdev _dev_tty0 = { .ioctl = tty_ioctl }; +// Serial console +static struct tty_data _dev_ttyS0_data = { + .fg_pgid = 1, + .out_dev = (void *) RS232_COM1, + .out_putc = rs232_putc +}; + +static struct chrdev _dev_ttyS0 = { + .type = CHRDEV_TTY, + .dev_data = &_dev_ttyS0_data, + .tc = TERMIOS_DEFAULT, + .write = tty_write, + // Line discipline + .read = line_read, + .ioctl = tty_ioctl +}; + void tty_control_write(struct chrdev *tty, char c) { struct tty_data *data = tty->dev_data; _assert(data); @@ -72,6 +90,10 @@ void tty_data_write(struct chrdev *tty, char c) { if (tty->tc.c_lflag & ECHONL) { tty_putc(tty, c); } + if (tty->tc.c_iflag & ICANON) { + // Trigger line flush + ring_signal(&tty->buffer, RING_SIGNAL_RET); + } break; case '\b': break; @@ -101,12 +123,15 @@ void tty_putc(struct chrdev *tty, char c) { } void tty_init(void) { + // tty0 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"); + + // ttyS0 + ring_init(&_dev_ttyS0.buffer, 16); + rs232_set_tty(RS232_COM1, &_dev_ttyS0); + dev_add(DEV_CLASS_CHAR, DEV_CHAR_TTY, &_dev_ttyS0, "ttyS0"); } static ssize_t tty_write(struct chrdev *tty, const void *buf, size_t pos, size_t lim) {