This patch extracts generic logic of serial console into a base class
isa-serial-base that abstracts reading and writing a byte. The specializations
like isa-serial (add mmio-isa-serial in later patch) provide specific way of
reading and writing a byte in form of lambda functions.
Signed-off-by: Waldemar Kozaczuk <
jwkoz...@gmail.com>
---
Makefile | 1 +
drivers/isa-serial-base.cc | 85 ++++++++++++++++++++++++++++
drivers/isa-serial-base.hh | 76 +++++++++++++++++++++++++
drivers/isa-serial.cc | 110 +++----------------------------------
drivers/isa-serial.hh | 17 +-----
5 files changed, 174 insertions(+), 115 deletions(-)
create mode 100644 drivers/isa-serial-base.cc
create mode 100644 drivers/isa-serial-base.hh
diff --git a/Makefile b/Makefile
index 20ddf3b1..99923a34 100644
--- a/Makefile
+++ b/Makefile
@@ -776,6 +776,7 @@ drivers += drivers/line-discipline.o
drivers += drivers/clock.o
drivers += drivers/clock-common.o
drivers += drivers/clockevent.o
+drivers += drivers/isa-serial-base.o
drivers += core/elf.o
drivers += drivers/random.o
drivers += drivers/zfs.o
diff --git a/drivers/isa-serial-base.cc b/drivers/isa-serial-base.cc
new file mode 100644
index 00000000..19358f0b
--- /dev/null
+++ b/drivers/isa-serial-base.cc
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2020 Waldemar Kozaczuk.
+ *
+ * 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 "isa-serial-base.hh"
+
+namespace console {
+
+void isa_serial_console_base::common_early_init(
+ const std::function<u8 (const int&)> _read_byte,
+ const std::function<void (const u8&, const int&)> _write_byte)
+{
+ read_byte = _read_byte;
+ write_byte = _write_byte;
+
+ // Set the UART speed to to 115,200 bps, This is done by writing 1,0 to
+ // Divisor Latch registers, but to access these we need to temporarily
+ // set the Divisor Latch Access Bit (DLAB) on the LSR register, because
+ // the UART has fewer ports than registers...
+ write_byte(lcr::LEN_8BIT | lcr::DLAB, regs::LCR);
+ write_byte(1, regs::DLL);
+ write_byte(0, regs::DLM);
+ write_byte(lcr::LEN_8BIT, regs::LCR);
+
+ // interrupt threshold
+ write_byte(0, regs::FCR);
+
+ // disable interrupts
+ write_byte(0, regs::IER);
+
+ // Most physical UARTs need the MCR AUX_OUTPUT_2 bit set to 1 for
+ // interrupts to be generated. QEMU doesn't bother checking this
+ // bit, but interestingly VMWare does, so we must set it.
+ write_byte(mcr::AUX_OUTPUT_2, regs::MCR);
+}
+
+void isa_serial_console_base::write(const char *str, size_t len)
+{
+ while (len-- > 0)
+ putchar(*str++);
+}
+
+bool isa_serial_console_base::input_ready()
+{
+ u8 val = read_byte(regs::LSR);
+ // On VMWare hosts without a serial port, this register always
+ // returns 0xff. Just ignore it instead of spinning incessantly.
+ return (val != 0xff && (val & lsr::RECEIVE_DATA_READY));
+}
+
+char isa_serial_console_base::readch()
+{
+ u8 val;
+ char letter;
+
+ do {
+ val = read_byte(regs::LSR);
+ } while (!(val & (lsr::RECEIVE_DATA_READY | lsr::OVERRUN | lsr::PARITY_ERROR | lsr::FRAME_ERROR)));
+
+ letter = read_byte(0);
+
+ return letter;
+}
+
+void isa_serial_console_base::putchar(const char ch)
+{
+ u8 val;
+
+ do {
+ val = read_byte(regs::LSR);
+ } while (!(val & lsr::TRANSMIT_HOLD_EMPTY));
+
+ write_byte(ch, 0);
+}
+
+void isa_serial_console_base::enable_interrupt()
+{
+ // enable interrupts
+ write_byte(1, regs::IER);
+}
+
+}
diff --git a/drivers/isa-serial-base.hh b/drivers/isa-serial-base.hh
new file mode 100644
index 00000000..f518b366
--- /dev/null
+++ b/drivers/isa-serial-base.hh
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2020 Waldemar Kozaczuk.
+ *
+ * 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_ISA_SERIAL_BASE_HH
+#define DRIVERS_ISA_SERIAL_BASE_HH
+
+#include "console-driver.hh"
+#include <osv/pci.hh>
+#include <osv/sched.hh>
+#include <osv/interrupt.hh>
+
+namespace console {
+
+// UART registers, offsets to ioport:
+enum regs {
+ IER = 1, // Interrupt Enable Register
+ FCR = 2, // FIFO Control Register
+ LCR = 3, // Line Control Register
+ MCR = 4, // Modem Control Register
+ LSR = 5, // Line Control Register
+ MSR = 6, // Modem Status Register
+ SCR = 7, // Scratch Register
+ DLL = 0, // Divisor Latch LSB Register
+ DLM = 1, // Divisor Latch MSB Register
+};
+
+enum lcr {
+ // When bit 7 (DLAB) of LCR is set to 1, the two registers 0 and 1
+ // change their meaning and become two bytes controlling the baud rate
+ DLAB = 0x80, // Divisor Latch Access Bit in LCR register
+ LEN_8BIT = 3,
+};
+
+// Various bits of the Line Status Register
+enum lsr {
+ RECEIVE_DATA_READY = 0x1,
+ OVERRUN = 0x2,
+ PARITY_ERROR = 0x4,
+ FRAME_ERROR = 0x8,
+ BREAK_INTERRUPT = 0x10,
+ TRANSMIT_HOLD_EMPTY = 0x20,
+ TRANSMIT_EMPTY = 0x40,
+ FIFO_ERROR = 0x80,
+};
+
+// Various bits of the Modem Control Register
+enum mcr {
+ DTR = 0x1,
+ RTS = 0x2,
+ AUX_OUTPUT_1 = 0x4,
+ AUX_OUTPUT_2 = 0x8,
+ LOOPBACK_MODE = 0x16,
+};
+
+class isa_serial_console_base : public console_driver {
+public:
+ virtual void write(const char *str, size_t len);
+ virtual void flush() {}
+ virtual bool input_ready() override;
+ virtual char readch();
+protected:
+ static void common_early_init(const std::function<u8 (const int&)> _read_byte,
+ const std::function<void (const u8&, const int&)> _write_byte);
+ void enable_interrupt();
+private:
+ static std::function<u8 (const int&)> read_byte;
+ static std::function<void (const u8&, const int&)> write_byte;
+ void putchar(const char ch);
+};
+}
+
+#endif
diff --git a/drivers/isa-serial.cc b/drivers/isa-serial.cc
index b20b30f3..16da3736 100644
--- a/drivers/isa-serial.cc
+++ b/drivers/isa-serial.cc
@@ -9,113 +9,21 @@
namespace console {
-// UART registers, offsets to ioport:
-enum regs {
- IER = 1, // Interrupt Enable Register
- FCR = 2, // FIFO Control Register
- LCR = 3, // Line Control Register
- MCR = 4, // Modem Control Register
- LSR = 5, // Line Control Register
- MSR = 6, // Modem Status Register
- SCR = 7, // Scratch Register
- DLL = 0, // Divisor Latch LSB Register
- DLM = 1, // Divisor Latch MSB Register
+std::function<u8 (const int&)> isa_serial_console_base::read_byte = [](const int& reg) {
+ return pci::inb(isa_serial_console::ioport + reg);
};
-enum lcr {
- // When bit 7 (DLAB) of LCR is set to 1, the two registers 0 and 1
- // change their meaning and become two bytes controlling the baud rate
- DLAB = 0x80, // Divisor Latch Access Bit in LCR register
- LEN_8BIT = 3,
-};
-
-// Various bits of the Line Status Register
-enum lsr {
- RECEIVE_DATA_READY = 0x1,
- OVERRUN = 0x2,
- PARITY_ERROR = 0x4,
- FRAME_ERROR = 0x8,
- BREAK_INTERRUPT = 0x10,
- TRANSMIT_HOLD_EMPTY = 0x20,
- TRANSMIT_EMPTY = 0x40,
- FIFO_ERROR = 0x80,
-};
-
-// Various bits of the Modem Control Register
-enum mcr {
- DTR = 0x1,
- RTS = 0x2,
- AUX_OUTPUT_1 = 0x4,
- AUX_OUTPUT_2 = 0x8,
- LOOPBACK_MODE = 0x16,
+std::function<void (const u8&, const int&)> isa_serial_console_base::write_byte = [](const u8& val, const int& reg) {
+ pci::outb(val, isa_serial_console::ioport + reg);
};
void isa_serial_console::early_init()
{
- // Set the UART speed to to 115,200 bps, This is done by writing 1,0 to
- // Divisor Latch registers, but to access these we need to temporarily
- // set the Divisor Latch Access Bit (DLAB) on the LSR register, because
- // the UART has fewer ports than registers...
- pci::outb(lcr::LEN_8BIT | lcr::DLAB, ioport + regs::LCR);
- pci::outb(1, ioport + regs::DLL);
- pci::outb(0, ioport + regs::DLM);
- pci::outb(lcr::LEN_8BIT, ioport + regs::LCR);
-
- // interrupt threshold
- pci::outb(0, ioport + regs::FCR);
-
- // disable interrupts
- pci::outb(0, ioport + regs::IER);
-
- // Most physical UARTs need the MCR AUX_OUTPUT_2 bit set to 1 for
- // interrupts to be generated. QEMU doesn't bother checking this
- // bit, but interestingly VMWare does, so we must set it.
- pci::outb(mcr::AUX_OUTPUT_2, ioport + regs::MCR);
-}
-
-void isa_serial_console::write(const char *str, size_t len)
-{
- while (len-- > 0)
- putchar(*str++);
-}
-
-bool isa_serial_console::input_ready()
-{
- u8 val = pci::inb(ioport + regs::LSR);
- // On VMWare hosts without a serial port, this register always
- // returns 0xff. Just ignore it instead of spinning incessantly.
- return (val != 0xff && (val & lsr::RECEIVE_DATA_READY));
-}
-
-char isa_serial_console::readch()
-{
- u8 val;
- char letter;
-
- do {
- val = pci::inb(ioport + regs::LSR);
- } while (!(val & (lsr::RECEIVE_DATA_READY | lsr::OVERRUN | lsr::PARITY_ERROR | lsr::FRAME_ERROR)));
-
- letter = pci::inb(ioport);
-
- return letter;
-}
-
-void isa_serial_console::putchar(const char ch)
-{
- u8 val;
-
- do {
- val = pci::inb(ioport + regs::LSR);
- } while (!(val & lsr::TRANSMIT_HOLD_EMPTY));
-
- pci::outb(ch, ioport);
-}
-
-void isa_serial_console::enable_interrupt()
-{
- // enable interrupts
- pci::outb(1, ioport + regs::IER);
+ common_early_init( [](const int& reg) {
+ return pci::inb(ioport + reg);
+ }, [](const u8& val, const int& reg) {
+ pci::outb(val, ioport + reg);
+ });
}
void isa_serial_console::dev_start() {
diff --git a/drivers/isa-serial.hh b/drivers/isa-serial.hh
index e31d125a..d7908be2 100644
--- a/drivers/isa-serial.hh
+++ b/drivers/isa-serial.hh
@@ -8,30 +8,19 @@
#ifndef DRIVERS_ISA_SERIAL_HH
#define DRIVERS_ISA_SERIAL_HH
-#include "console-driver.hh"
-#include <osv/pci.hh>
-#include <osv/sched.hh>
-#include <osv/interrupt.hh>
+#include "isa-serial-base.hh"
namespace console {
-class isa_serial_console : public console_driver {
+class isa_serial_console : public isa_serial_console_base {
public:
static void early_init();
- virtual void write(const char *str, size_t len);
- virtual void flush() {}
- virtual bool input_ready() override;
- virtual char readch();
+ static const u16 ioport = 0x3f8;
private:
std::unique_ptr<gsi_edge_interrupt> _irq;
- static const u16 ioport = 0x3f8;
-
virtual void dev_start();
- void enable_interrupt();
- static void putchar(const char ch);
virtual const char *thread_name() { return "isa-serial-input"; }
};
-
}
#endif
--
2.25.1