From c0a1e5552fc31c0d2545388f7bdebee110eafab3 Mon Sep 17 00:00:00 2001 From: Takuya ASADA <syuu@cloudius-systems.com> Date: Thu, 24 Apr 2014 23:41:23 +0900 Subject: [PATCH] Implement console multiplexer Signed-off-by: Takuya ASADA <syuu@cloudius-systems.com> Signed-off-by: Pekka Enberg <penberg@cloudius-systems.com> --- build.mk | 3 + drivers/console-driver.cc | 20 +++ drivers/console-driver.hh | 33 +++++ drivers/console-multiplexer.cc | 93 ++++++++++++ drivers/console-multiplexer.hh | 42 ++++++ drivers/console.cc | 255 +++++---------------------------- drivers/console.hh | 8 -- drivers/isa-serial.cc | 18 +-- drivers/isa-serial.hh | 13 +- drivers/line-discipline.cc | 165 +++++++++++++++++++++ drivers/line-discipline.hh | 41 ++++++ drivers/vga.cc | 32 +++-- drivers/vga.hh | 13 +- 13 files changed, 467 insertions(+), 269 deletions(-) create mode 100644 drivers/console-driver.cc create mode 100644 drivers/console-driver.hh create mode 100644 drivers/console-multiplexer.cc create mode 100644 drivers/console-multiplexer.hh create mode 100644 drivers/line-discipline.cc create mode 100644 drivers/line-discipline.hh diff --git a/build.mk b/build.mk index 52444c766..cc409f84e 100644 --- a/build.mk +++ b/build.mk @@ -664,6 +664,9 @@ libtsm += drivers/libtsm/tsm_vte_charsets.o drivers := $(bsd) $(solaris) drivers += core/mmu.o drivers += drivers/console.o +drivers += drivers/console-multiplexer.o +drivers += drivers/console-driver.o +drivers += drivers/line-discipline.o drivers += drivers/clock.o drivers += drivers/clockevent.o drivers += drivers/ramdisk.o diff --git a/drivers/console-driver.cc b/drivers/console-driver.cc new file mode 100644 index 000000000..f9cd199d4 --- /dev/null +++ b/drivers/console-driver.cc @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2014 Cloudius Systems, Ltd. + * + * This work is open source software, licensed under the terms of the + * BSD license as described in the LICENSE file in the top-level directory. + */ + +#include "console-driver.hh" + +namespace console { + +void ConsoleDriver::start(std::function<void()> read_poll) +{ + _thread = new sched::thread([&] { read_poll(); }, + sched::thread::attr().name(thread_name())); + dev_start(); + _thread->start(); +} + +}; diff --git a/drivers/console-driver.hh b/drivers/console-driver.hh new file mode 100644 index 000000000..348242af4 --- /dev/null +++ b/drivers/console-driver.hh @@ -0,0 +1,33 @@ +/* + * Copyright (C) 2014 Cloudius Systems, Ltd. + * + * This work is open source software, licensed under the terms of the + * BSD license as described in the LICENSE file in the top-level directory. + */ + +#ifndef DRIVERS_CONSOLE_DRIVER_HH +#define DRIVERS_CONSOLE_DRIVER_HH + +#include <functional> +#include <osv/sched.hh> + +namespace console { + +class ConsoleDriver { +public: + virtual ~ConsoleDriver() {} + virtual void write(const char *str, size_t len) = 0; + virtual void flush() = 0; + virtual bool input_ready() = 0; + virtual char readch() = 0; + void start(std::function<void()> read_poll); +protected: + sched::thread *_thread; +private: + virtual void dev_start() = 0; + virtual const char *thread_name() = 0; +}; + +}; + +#endif diff --git a/drivers/console-multiplexer.cc b/drivers/console-multiplexer.cc new file mode 100644 index 000000000..f0c4a1b5e --- /dev/null +++ b/drivers/console-multiplexer.cc @@ -0,0 +1,93 @@ +/* Copyright (C) 2014 Cloudius Systems, Ltd. + * + * This work is open source software, licensed under the terms of the + * BSD license as described in the LICENSE file in the top-level directory. + */ + +#include <osv/device.h> +#include <osv/debug.hh> +#include <signal.h> +#include "console-multiplexer.hh" + +namespace console { + +ConsoleMultiplexer::ConsoleMultiplexer(const termios *tio, ConsoleDriver *early_driver) + : _tio(tio) + , _early_driver(early_driver) +{ +} + +void ConsoleMultiplexer::driver_add(ConsoleDriver *driver) +{ + _drivers.push_back(driver); +} + +void ConsoleMultiplexer::start() +{ + _ldisc = new LineDiscipline(_tio); + for (auto driver : _drivers) + driver->start([&] { _ldisc->read_poll(driver); }); + _started = true; +} + +void ConsoleMultiplexer::read(struct uio *uio, int ioflag) { + if (!_started) + return; + _ldisc->read(uio, ioflag); +} + +void ConsoleMultiplexer::drivers_write(const char *str, size_t len) +{ + for (auto driver : _drivers) + driver->write(str, len); +} + +void ConsoleMultiplexer::drivers_flush() +{ + for (auto driver : _drivers) + driver->flush(); +} + +void ConsoleMultiplexer::write_ll(const char *str, size_t len) +{ + if (!_started) { + if (_early_driver != nullptr) { + _early_driver->write(str, len); + _early_driver->flush(); + } + } else { + _ldisc->write(str, len, + [&] (const char *str, size_t len) { drivers_write(str, len); }); + drivers_flush(); + } +} + +void ConsoleMultiplexer::write(const char *str, size_t len) +{ + if (!_started) { + WITH_LOCK(_early_lock) { + write_ll(str, len); + } + } else { + WITH_LOCK(_mutex) { + write_ll(str, len); + } + } +} + +void ConsoleMultiplexer::write(struct uio *uio, int ioflag) +{ + linearize_uio_write(uio, ioflag, [&] (const char *str, size_t len) { + write(str, len); + }); +} + +int ConsoleMultiplexer::read_queue_size() +{ + if (!_started) + return -1; + + return _ldisc->read_queue_size(); +} + +} diff --git a/drivers/console-multiplexer.hh b/drivers/console-multiplexer.hh new file mode 100644 index 000000000..f72497cfc --- /dev/null +++ b/drivers/console-multiplexer.hh @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2014 Cloudius Systems, Ltd. + * + * This work is open source software, licensed under the terms of the + * BSD license as described in the LICENSE file in the top-level directory. + */ + +#ifndef DRIVERS_CONSOLE_MULTIPLEXER_HH +#define DRIVERS_CONSOLE_MULTIPLEXER_HH +#include <osv/spinlock.h> +#include <termios.h> +#include "console-driver.hh" +#include "line-discipline.hh" + +namespace console { + +class ConsoleMultiplexer { +public: + explicit ConsoleMultiplexer(const termios *tio, ConsoleDriver *early_driver = nullptr); + ~ConsoleMultiplexer() {}; + void driver_add(ConsoleDriver *driver); + void start(); + void read(struct uio *uio, int ioflag); + void write_ll(const char *str, size_t len); + void write(const char *str, size_t len); + void write(struct uio *uio, int ioflag); + int read_queue_size(); +private: + void drivers_write(const char *str, size_t len); + void drivers_flush(); + const termios *_tio; + spinlock _early_lock; + bool _started = false; + ConsoleDriver *_early_driver; + std::list<ConsoleDriver *> _drivers; + mutex _mutex; + LineDiscipline *_ldisc; +}; + +}; + +#endif diff --git a/drivers/console.cc b/drivers/console.cc index 30183b4ee..a0f72a225 100644 --- a/drivers/console.cc +++ b/drivers/console.cc @@ -8,17 +8,19 @@ #include <osv/prex.h> #include <osv/device.h> -#include <osv/sched.hh> -#include <osv/spinlock.h> +#include <osv/debug.hh> +#include <osv/prio.hh> #include <queue> #include <deque> #include <vector> #include <sys/ioctl.h> +#include "console.hh" +#include "console-multiplexer.hh" #ifdef __x86_64__ -#include "isa-serial.hh" -#include "vga.hh" -#endif /* __x86_64__ */ +#include "drivers/vga.hh" +#include "drivers/isa-serial.hh" +#endif #include <termios.h> #include <signal.h> @@ -27,47 +29,6 @@ namespace console { -// should eventually become a list of console device that we chose the best from -static Console* console = nullptr; -static spinlock early_write_lock; - -static void early_write(const char *str, size_t len) -{ - IsaSerialConsole::early_write(str, len); -} - -void write(const char *msg, size_t len) -{ - if (len == 0) - return; - - if (console != nullptr) { - console->write(msg, len); - } else { - WITH_LOCK(early_write_lock) { - early_write(msg, len); - } - } -} - -// lockless version -void write_ll(const char *msg, size_t len) -{ - if (len == 0) - return; - - if (console != nullptr) - console->write(msg, len); - else - early_write(msg, len); -} - -mutex console_mutex; -// characters available to be returned on read() from the console -std::queue<char> console_queue; -// who to wake when characters are added to console_queue -std::list<sched::thread*> readers; - termios tio = { .c_iflag = ICRNL, .c_oflag = OPOST | ONLCR, @@ -80,170 +41,31 @@ termios tio = { /*VREPRINT*/0, /*VDISCARD*/0, /*VWERASE*/0, /*VLNEXT*/0, /*VEOL2*/0}, }; +#ifdef __x86_64__ +IsaSerialConsole early_driver + __attribute__((init_priority((int)init_prio::console))); +ConsoleMultiplexer mux __attribute__((init_priority((int)init_prio::console))) + (&tio, &early_driver); +#else +ConsoleMultiplexer mux __attribute__((init_priority((int)init_prio::console))) + (&tio, nullptr); +#endif -// Console line discipline thread. -// -// The "line discipline" is an intermediate layer between a physical device -// (here a serial port) and a character-device interface (here console_read()) -// implementing features such as input echo, line editing, etc. In OSv, this -// is implemented in a thread, which is also responsible for read-ahead (input -// characters are read, echoed and buffered even if no-one is yet reading). -// -// The code below implements a fixed line discipline (actually two - canonical -// and non-canonical). We resisted the temptation to make the line discipline -// a stand-alone pluggable object: In the early 1980s, 8th Edition Research -// Unix experimented with pluggable line disciplines, providing improved -// editing features such as CRT erase (backspace outputs backspace-space- -// backspace), word erase, etc. These pluggable line-disciplines led to the -// development of Unix "STREAMS". However, today, these concepts are all but -// considered obsolete: In the mid 80s it was realized that these editing -// features can better be implemented in userspace code - Contemporary shells -// introduced sophisticated command-line editing (tcsh and ksh were both -// announced in 1983), and the line-editing libraries appeared (GNU Readline, -// in 1989). Posix's standardization of termios(3) also more-or-less set in -// stone the features that Posix-compliant line discipline should support. -// -// We currently support only a subset of the termios(3) features, which we -// considered most useful. More of the features can be added as needed. - -static inline bool isctrl(char c) { - return ((c<' ' && c!='\t' && c!='\n') || c=='\177'); -} -// inputed characters not yet made available to read() in ICANON mode -std::deque<char> line_buffer; - -void console_poll() -{ - while (true) { - std::lock_guard<mutex> lock(console_mutex); - sched::thread::wait_until(console_mutex, [&] { return console->input_ready(); }); - char c = console->readch(); - if (c == 0) - continue; - - if (c == '\r' && tio.c_iflag & ICRNL) { - c = '\n'; - } - if (tio.c_lflag & ISIG) { - // Currently, INTR and QUIT signal OSv's only process, process 0. - if (c == tio.c_cc[VINTR]) { - kill(0, SIGINT); - continue; - } else if (c == tio.c_cc[VQUIT]) { - kill(0, SIGQUIT); - continue; - } - } - - if (tio.c_lflag & ICANON) { - // canonical ("cooked") mode, where input is only made - // available to the reader after a newline (until then, the - // user can edit it with backspace, etc.). - if (c == '\n') { - if (tio.c_lflag && ECHO) - console->write(&c, 1); - line_buffer.push_back('\n'); - while (!line_buffer.empty()) { - console_queue.push(line_buffer.front()); - line_buffer.pop_front(); - } - for (auto t : readers) { - t->wake(); - } - continue; // already echoed - } else if (c == tio.c_cc[VERASE]) { - if (line_buffer.empty()) { - continue; // do nothing, and echo nothing - } - char e = line_buffer.back(); - line_buffer.pop_back(); - if (tio.c_lflag && ECHO) { - static const char eraser[] = {'\b',' ','\b','\b',' ','\b'}; - if (tio.c_lflag && ECHOE) { - if (isctrl(e)) { // Erase the two characters ^X - console->write(eraser, 6); - } else { - console->write(eraser, 3); - } - } else { - if (isctrl(e)) { - console->write(eraser+2, 2); - } else { - console->write(eraser, 1); - } - } - continue; // already echoed - } - } else { - line_buffer.push_back(c); - } - } else { - // non-canonical ("cbreak") mode, where characters are made - // available for reading as soon as they are typed. - console_queue.push(c); - for (auto t : readers) { - t->wake(); - } - } - if (tio.c_lflag & ECHO) { - if (isctrl(c) && (tio.c_lflag & ECHOCTL)) { - char out[2]; - out[0] = '^'; - out[1] = c^'@'; - console->write(out, 2); - } else { - console->write(&c, 1); - } - } - } -} - -static int -console_read(struct uio *uio, int ioflag) +void write(const char *msg, size_t len) { - WITH_LOCK(console_mutex) { - readers.push_back(sched::thread::current()); - sched::thread::wait_until(console_mutex, [] { return !console_queue.empty(); }); - readers.remove(sched::thread::current()); - while (uio->uio_resid && !console_queue.empty()) { - struct iovec *iov = uio->uio_iov; - auto n = std::min(console_queue.size(), iov->iov_len); - for (size_t i = 0; i < n; ++i) { - static_cast<char*>(iov->iov_base)[i] = console_queue.front(); - console_queue.pop(); - } + if (len == 0) + return; - uio->uio_resid -= n; - uio->uio_offset += n; - if (n == iov->iov_len) { - uio->uio_iov++; - uio->uio_iovcnt--; - } - } - return 0; - } + mux.write(msg, len); } -static int -console_write(struct uio *uio, int ioflag) +// lockless version +void write_ll(const char *msg, size_t len) { - while (uio->uio_resid > 0) { - struct iovec *iov = uio->uio_iov; - - if (iov->iov_len) { - WITH_LOCK(console_mutex) { - console::write(reinterpret_cast<const char *>(iov->iov_base), - iov->iov_len); - } - } - - uio->uio_iov++; - uio->uio_iovcnt--; - uio->uio_resid -= iov->iov_len; - uio->uio_offset += iov->iov_len; - } + if (len == 0) + return; - return 0; + mux.write_ll(msg, len); } static int @@ -261,9 +83,7 @@ console_ioctl(u_long request, void *arg) return 0; case FIONREAD: // used in OpenJDK's os::available (os_linux.cpp) - console_mutex.lock(); - *static_cast<int*>(arg) = console_queue.size(); - console_mutex.unlock(); + *static_cast<int*>(arg) = mux.read_queue_size(); return 0; default: return -ENOTTY; @@ -273,13 +93,15 @@ console_ioctl(u_long request, void *arg) template <class T> static int op_read(T *ignore, struct uio *uio, int ioflag) { - return console_read(uio, ioflag); + mux.read(uio, ioflag); + return 0; } template <class T> static int op_write(T *ignore, struct uio *uio, int ioflag) { - return console_write(uio, ioflag); + mux.write(uio, ioflag); + return 0; } template <class T> static int @@ -304,19 +126,14 @@ struct driver console_driver = { void console_init(bool use_vga) { -#ifdef AARCH64_PORT_STUB - abort(); -#else /* !AARCH64_PORT_STUB */ - auto console_poll_thread = new sched::thread(console_poll, - sched::thread::attr().name("console")); - - if (use_vga) - console = new VGAConsole(console_poll_thread, &tio); +#ifdef __x86_64__ + if (!use_vga) + mux.driver_add(&early_driver); else - console = new IsaSerialConsole(console_poll_thread, &tio); - console_poll_thread->start(); + mux.driver_add(new VGAConsole()); +#endif device_create(&console_driver, "console", D_CHR); -#endif /* !AARCH64_PORT_STUB */ + mux.start(); } class console_file : public special_file { diff --git a/drivers/console.hh b/drivers/console.hh index 32e50be45..ff7e67ffa 100644 --- a/drivers/console.hh +++ b/drivers/console.hh @@ -12,14 +12,6 @@ namespace console { -class Console { -public: - virtual ~Console() {} - virtual void write(const char *str, size_t len) = 0; - virtual bool input_ready() = 0; - virtual char readch() = 0; -}; - void write(const char *msg, size_t len); void write_ll(const char *msg, size_t len); void console_init(bool use_vga); diff --git a/drivers/isa-serial.cc b/drivers/isa-serial.cc index 7266ee337..41c583738 100644 --- a/drivers/isa-serial.cc +++ b/drivers/isa-serial.cc @@ -10,20 +10,10 @@ namespace console { -IsaSerialConsole::IsaSerialConsole(sched::thread* poll_thread, const termios *tio) - : _irq(4, [=] { poll_thread->wake(); }), _tio(tio) -{ - reset(); -} - void IsaSerialConsole::write(const char *str, size_t len) { - while (len > 0) { - if ((*str == '\n') && (_tio->c_oflag & OPOST) && (_tio->c_oflag & ONLCR)) - writeByte('\r'); + while (len-- > 0) writeByte(*str++); - len--; - } } bool IsaSerialConsole::input_ready() @@ -80,9 +70,9 @@ void IsaSerialConsole::reset() { pci::outb(MCR_AUX_OUTPUT_2, ioport + MCR_ADDRESS); } -void IsaSerialConsole::early_write(const char *str, size_t len) { - while (len-- > 0) - writeByte(*str++); +void IsaSerialConsole::dev_start() { + _irq = new gsi_edge_interrupt(4, [&] { _thread->wake(); }); + reset(); } } diff --git a/drivers/isa-serial.hh b/drivers/isa-serial.hh index 38f504b29..43211cf73 100644 --- a/drivers/isa-serial.hh +++ b/drivers/isa-serial.hh @@ -8,26 +8,23 @@ #ifndef DRIVERS_ISA_SERIAL_HH #define DRIVERS_ISA_SERIAL_HH -#include "console.hh" +#include "console-driver.hh" #include "drivers/pci.hh" #include <osv/sched.hh> #include <osv/interrupt.hh> -#include <termios.h> namespace console { -class IsaSerialConsole : public Console { +class IsaSerialConsole : public ConsoleDriver { public: - explicit IsaSerialConsole(sched::thread* consumer, const termios *tio); virtual void write(const char *str, size_t len); + virtual void flush() {} virtual bool input_ready() override; virtual char readch(); - static void early_write(const char *str, size_t len); private: - gsi_edge_interrupt _irq; + gsi_edge_interrupt* _irq; static const u16 ioport = 0x3f8; u8 lcr; - const termios *_tio; enum IsaSerialValues { // UART registers, offsets to ioport: @@ -61,8 +58,10 @@ private: MCR_LOOPBACK_MODE = 0x16, }; + virtual void dev_start(); void reset(); static void writeByte(const char letter); + virtual const char *thread_name() { return "isa-serial-input"; } }; } diff --git a/drivers/line-discipline.cc b/drivers/line-discipline.cc new file mode 100644 index 000000000..59db1f0f5 --- /dev/null +++ b/drivers/line-discipline.cc @@ -0,0 +1,165 @@ +/* + * Copyright (C) 2014 Cloudius Systems, Ltd. + * + * This work is open source software, licensed under the terms of the + * BSD license as described in the LICENSE file in the top-level directory. + */ +#include "line-discipline.hh" +#include <osv/sched.hh> +#include <signal.h> + +namespace console { + +LineDiscipline::LineDiscipline(const termios *tio) + : _tio(tio) +{ +} + +void LineDiscipline::read(struct uio *uio, int ioflag) { + WITH_LOCK(_mutex) { + _readers.push_back(sched::thread::current()); + sched::thread::wait_until(_mutex, [&] { return !_read_queue.empty(); }); + _readers.remove(sched::thread::current()); + while (uio->uio_resid && !_read_queue.empty()) { + struct iovec *iov = uio->uio_iov; + auto n = std::min(_read_queue.size(), iov->iov_len); + for (size_t i = 0; i < n; ++i) { + static_cast<char*>(iov->iov_base)[i] = _read_queue.front(); + _read_queue.pop(); + } + + uio->uio_resid -= n; + uio->uio_offset += n; + if (n == iov->iov_len) { + uio->uio_iov++; + uio->uio_iovcnt--; + } + } + } +} + +// Console line discipline thread. +// +// The "line discipline" is an intermediate layer between a physical device +// (here a serial port) and a character-device interface (here console_read()) +// implementing features such as input echo, line editing, etc. In OSv, this +// is implemented in a thread, which is also responsible for read-ahead (input +// characters are read, echoed and buffered even if no-one is yet reading). +// +// The code below implements a fixed line discipline (actually two - canonical +// and non-canonical). We resisted the temptation to make the line discipline +// a stand-alone pluggable object: In the early 1980s, 8th Edition Research +// Unix experimented with pluggable line disciplines, providing improved +// editing features such as CRT erase (backspace outputs backspace-space- +// backspace), word erase, etc. These pluggable line-disciplines led to the +// development of Unix "STREAMS". However, today, these concepts are all but +// considered obsolete: In the mid 80s it was realized that these editing +// features can better be implemented in userspace code - Contemporary shells +// introduced sophisticated command-line editing (tcsh and ksh were both +// announced in 1983), and the line-editing libraries appeared (GNU Readline, +// in 1989). Posix's standardization of termios(3) also more-or-less set in +// stone the features that Posix-compliant line discipline should support. +// +// We currently support only a subset of the termios(3) features, which we +// considered most useful. More of the features can be added as needed. + +static inline bool isctrl(char c) { + return ((c<' ' && c!='\t' && c!='\n') || c=='\177'); +} + +void LineDiscipline::read_poll(ConsoleDriver *driver) +{ + while (true) { + std::lock_guard<mutex> lock(_mutex); + sched::thread::wait_until(_mutex, [&] { return driver->input_ready(); }); + char c = driver->readch(); + if (c == 0) + continue; + + if (c == '\r' && _tio->c_iflag & ICRNL) { + c = '\n'; + } + if (_tio->c_lflag & ISIG) { + // Currently, INTR and QUIT signal OSv's only process, process 0. + if (c == _tio->c_cc[VINTR]) { + kill(0, SIGINT); + continue; + } else if (c == _tio->c_cc[VQUIT]) { + kill(0, SIGQUIT); + continue; + } + } + + if (_tio->c_lflag & ICANON) { + // canonical ("cooked") mode, where input is only made + // available to the reader after a newline (until then, the + // user can edit it with backspace, etc.). + if (c == '\n') { + if (_tio->c_lflag && ECHO) + driver->write(&c, 1); + _line_buffer.push_back('\n'); + while (!_line_buffer.empty()) { + _read_queue.push(_line_buffer.front()); _line_buffer.pop_front(); + } + for (auto t : _readers) { + t->wake(); + } + continue; // already echoed + } else if (c == _tio->c_cc[VERASE]) { + if (_line_buffer.empty()) { + continue; // do nothing, and echo nothing + } + char e = _line_buffer.back(); + _line_buffer.pop_back(); + if (_tio->c_lflag && ECHO) { + static const char eraser[] = {'\b',' ','\b','\b',' ','\b'}; + if (_tio->c_lflag && ECHOE) { + if (isctrl(e)) { // Erase the two characters ^X + driver->write(eraser, 6); + } else { + driver->write(eraser, 3); + } + } else { + if (isctrl(e)) { + driver->write(eraser+2, 2); + } else { + driver->write(eraser, 1); + } + } + continue; // already echoed + } + } else { + _line_buffer.push_back(c); + } + } else { + // non-canonical ("cbreak") mode, where characters are made + // available for reading as soon as they are typed. + _read_queue.push(c); + for (auto t : _readers) { + t->wake(); + } + } + if (_tio->c_lflag & ECHO) { + if (isctrl(c) && (_tio->c_lflag & ECHOCTL)) { + char out[2]; + out[0] = '^'; + out[1] = c^'@'; + driver->write(out, 2); + } else { + driver->write(&c, 1); + } + } + } +} +void LineDiscipline::write(const char *str, size_t len, + std::function<void(const char *str, size_t len)> writer) +{ + while (len-- > 0) { + if ((*str == '\n') && + (_tio->c_oflag & OPOST) && (_tio->c_oflag & ONLCR)) + writer("\r", 1); + writer(str++, 1); + } +} + +} diff --git a/drivers/line-discipline.hh b/drivers/line-discipline.hh new file mode 100644 index 000000000..84e96f037 --- /dev/null +++ b/drivers/line-discipline.hh @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2014 Cloudius Systems, Ltd. + * + * This work is open source software, licensed under the terms of the + * BSD license as described in the LICENSE file in the top-level directory. + */ + +#ifndef DRIVERS_LINE_DISCIPLINE_HH +#define DRIVERS_LINE_DISCIPLINE_HH +#include <termios.h> +#include <queue> +#include <list> +#include <functional> +#include <osv/uio.h> +#include <osv/mutex.h> +#include "console-driver.hh" + +namespace console { + +class LineDiscipline { +public: + explicit LineDiscipline(const termios *tio); + void read(struct uio *uio, int ioflag); + void read_poll(ConsoleDriver *driver); + int read_queue_size() { return _read_queue.size(); } + void write(const char *str, size_t len, + std::function<void(const char *str, size_t len)> writer); +private: + mutex _mutex; + const termios *_tio; + // characters available to be returned on read() from the console + std::queue<char> _read_queue; + // who to wake when characters are added to _read_queue + std::list<sched::thread*> _readers; + // inputed characters not yet made available to read() in ICANON mode + std::deque<char> _line_buffer; +}; + +}; + +#endif diff --git a/drivers/vga.cc b/drivers/vga.cc index fb2cc6cfb..099da8e13 100644 --- a/drivers/vga.cc +++ b/drivers/vga.cc @@ -59,10 +59,8 @@ static int tsm_scroll_cb(struct tsm_screen *screen, int scroll_count, void *data return 0; } -VGAConsole::VGAConsole(sched::thread* poll_thread, const termios *tio) - : _tio(tio) - , _kbd(poll_thread) - , _offset_dirty(false) +VGAConsole::VGAConsole() + : _offset_dirty(false) { tsm_screen_new(&_tsm_screen, tsm_log_cb, this); tsm_screen_resize(_tsm_screen, NCOLS, NROWS); @@ -123,12 +121,11 @@ void VGAConsole::apply_offset() void VGAConsole::write(const char *str, size_t len) { - while (len > 0) { - if ((*str == '\n') && (_tio->c_oflag & OPOST) && (_tio->c_oflag & ONLCR)) - tsm_vte_input(_tsm_vte, "\r", 1); - tsm_vte_input(_tsm_vte, str++, 1); - len--; - } + tsm_vte_input(_tsm_vte, str, len); +} + +void VGAConsole::flush() +{ tsm_screen_draw(_tsm_screen, tsm_draw_cb, tsm_cursor_cb, tsm_scroll_cb, this); if (_offset_dirty) apply_offset(); @@ -136,7 +133,7 @@ void VGAConsole::write(const char *str, size_t len) bool VGAConsole::input_ready() { - return !_read_queue.empty() || _kbd.input_ready(); + return !_read_queue.empty() || _kbd->input_ready(); } char VGAConsole::readch() @@ -152,18 +149,18 @@ char VGAConsole::readch() return c; } - key = _kbd.readkey(); + key = _kbd->readkey(); if (!key) { if (_read_queue.empty()) return 0; continue; } - if (_kbd.shift & MOD_SHIFT) + if (_kbd->shift & MOD_SHIFT) mods |= TSM_SHIFT_MASK; - if (_kbd.shift & MOD_CTL) + if (_kbd->shift & MOD_CTL) mods |= TSM_CONTROL_MASK; - if (_kbd.shift & MOD_ALT) + if (_kbd->shift & MOD_ALT) mods |= TSM_ALT_MASK; switch (key) { @@ -192,4 +189,9 @@ char VGAConsole::readch() } } +void VGAConsole::dev_start() +{ + _kbd = new Keyboard(_thread); +} + } diff --git a/drivers/vga.hh b/drivers/vga.hh index aba358ae4..f3203731b 100644 --- a/drivers/vga.hh +++ b/drivers/vga.hh @@ -8,19 +8,19 @@ #ifndef DRIVERS_VGA_HH #define DRIVERS_VGA_HH -#include "console.hh" +#include "console-driver.hh" #include <osv/sched.hh> -#include <termios.h> #include "kbd.hh" #include "libtsm/libtsm.hh" #include <queue> namespace console { -class VGAConsole : public Console { +class VGAConsole : public ConsoleDriver { public: - explicit VGAConsole(sched::thread* consumer, const termios *tio); + explicit VGAConsole(); virtual void write(const char *str, size_t len); + virtual void flush(); virtual bool input_ready(); virtual char readch(); void draw(const uint32_t c, const struct tsm_screen_attr *attr, unsigned int x, unsigned int y); @@ -43,14 +43,15 @@ private: }; unsigned _col = 0; static volatile unsigned short * const _buffer; - const termios *_tio; struct tsm_screen *_tsm_screen; struct tsm_vte *_tsm_vte; - Keyboard _kbd; + Keyboard *_kbd; std::queue<char> _read_queue; unsigned short _history[BUFFER_SIZE]; unsigned _offset; bool _offset_dirty; + virtual void dev_start(); + virtual const char *thread_name() { return "kbd-input"; } }; } -- GitLab