io: Complete serial support. Closes #155
diff --git a/common/drivers/gop.c b/common/drivers/gop.c
index f31fc088..222f0e7d 100644
--- a/common/drivers/gop.c
+++ b/common/drivers/gop.c
@@ -152,8 +152,10 @@ bool init_gop(struct fb_info *ret,
panic(false, "gop: Initialisation failed");
}
- if (preset_mode == INVALID_PRESET_MODE)
+ if (preset_mode == INVALID_PRESET_MODE) {
preset_mode = gop->Mode->Mode;
+ current_video_mode = preset_mode;
+ }
struct resolution fallback_resolutions[] = {
{ 0, 0, 0 }, // Overridden by preset mode
diff --git a/common/drivers/serial.h b/common/drivers/serial.h
new file mode 100644
index 00000000..c716054a
--- /dev/null
+++ b/common/drivers/serial.h
@@ -0,0 +1,12 @@
+#ifndef __DRIVERS__SERIAL_H__
+#define __DRIVERS__SERIAL_H__
+
+#include <stdint.h>
+
+void serial_out(uint8_t b);
+
+#if bios == 1
+int serial_in(void);
+#endif
+
+#endif
diff --git a/common/drivers/serial.s2.c b/common/drivers/serial.s2.c
new file mode 100644
index 00000000..20cfa6a2
--- /dev/null
+++ b/common/drivers/serial.s2.c
@@ -0,0 +1,76 @@
+#include <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
+#include <drivers/serial.h>
+#include <sys/cpu.h>
+#include <lib/blib.h>
+#if uefi == 1
+# include <efi.h>
+#endif
+
+static bool serial_initialised = false;
+
+#if uefi == 1
+static EFI_SERIAL_IO_PROTOCOL *serial_protocol;
+#endif
+
+static void serial_initialise(void) {
+ if (serial_initialised) {
+ return;
+ }
+
+#if uefi == 1
+ EFI_STATUS status;
+
+ EFI_GUID serial_guid = EFI_SERIAL_IO_PROTOCOL_GUID;
+
+ status = gBS->LocateProtocol(&serial_guid, NULL, (void **)&serial_protocol);
+ if (status) {
+ return;
+ }
+
+ serial_protocol->Reset(serial_protocol);
+#endif
+
+#if bios == 1
+ // Init com1
+ outb(0x3f8 + 1, 0x00);
+ outb(0x3f8 + 3, 0x80);
+ outb(0x3f8 + 0, 0x0c); // 9600 baud
+ outb(0x3f8 + 1, 0x00);
+ outb(0x3f8 + 3, 0x03);
+ outb(0x3f8 + 2, 0xc7);
+ outb(0x3f8 + 4, 0x0b);
+#endif
+
+ serial_initialised = true;
+}
+
+void serial_out(uint8_t b) {
+#if uefi == 1
+ if (efi_boot_services_exited) {
+ return;
+ }
+#endif
+
+ serial_initialise();
+
+#if uefi == 1
+ UINTN bsize = 1;
+ serial_protocol->Write(serial_protocol, &bsize, &b);
+#elif bios == 1
+ while ((inb(0x3f8 + 5) & 0x20) == 0);
+ outb(0x3f8, b);
+#endif
+}
+
+#if bios == 1
+int serial_in(void) {
+ serial_initialise();
+
+ if ((inb(0x3f8 + 5) & 0x01) == 0) {
+ return -1;
+ }
+ return inb(0x3f8);
+}
+#endif
diff --git a/common/lib/print.s2.c b/common/lib/print.s2.c
index fc685f29..06f3ac0e 100644
--- a/common/lib/print.s2.c
+++ b/common/lib/print.s2.c
@@ -9,6 +9,7 @@
#include <lib/real.h>
#endif
#include <sys/cpu.h>
+#include <drivers/serial.h>
#if bios == 1
static void s2_print(const char *s, size_t len) {
@@ -140,27 +141,7 @@ void print(const char *fmt, ...) {
static char print_buf[PRINT_BUF_MAX];
-static void serial_out(uint8_t b) {
- while ((inb(0x3f8 + 5) & 0x20) == 0);
- outb(0x3f8, b);
-}
-
void vprint(const char *fmt, va_list args) {
- static bool com_initialised = false;
-
- if (COM_OUTPUT && !com_initialised) {
- // Init com1
- outb(0x3F8 + 1, 0x00);
- outb(0x3F8 + 3, 0x80);
- outb(0x3F8 + 0, 0x0c); // 9600 baud
- outb(0x3F8 + 1, 0x00);
- outb(0x3F8 + 3, 0x03);
- outb(0x3F8 + 2, 0xc7);
- outb(0x3F8 + 4, 0x0b);
-
- com_initialised = true;
- }
-
size_t print_buf_i = 0;
for (;;) {
diff --git a/common/lib/readline.c b/common/lib/readline.c
index 1f1cfe2c..a94a6898 100644
--- a/common/lib/readline.c
+++ b/common/lib/readline.c
@@ -10,6 +10,8 @@
#elif uefi == 1
# include <efi.h>
#endif
+#include <drivers/serial.h>
+#include <sys/cpu.h>
int getchar_internal(uint8_t scancode, uint8_t ascii, uint32_t shift_state) {
switch (scancode) {
@@ -89,89 +91,171 @@ int getchar_internal(uint8_t scancode, uint8_t ascii, uint32_t shift_state) {
#if bios == 1
int getchar(void) {
- uint8_t scancode = 0;
- uint8_t ascii = 0;
- uint32_t mods = 0;
-again:;
- struct rm_regs r = {0};
- rm_int(0x16, &r, &r);
- scancode = (r.eax >> 8) & 0xff;
- ascii = r.eax & 0xff;
-
- r = (struct rm_regs){ 0 };
- r.eax = 0x0200; // GET SHIFT FLAGS
- rm_int(0x16, &r, &r);
-
- if (r.eax & GETCHAR_LCTRL) {
- /* the bios subtracts 0x60 from ascii if ctrl is pressed */
- mods = GETCHAR_LCTRL;
- ascii += 0x60;
+ for (;;) {
+ int ret = pit_sleep_and_quit_on_keypress(65535);
+ if (ret != 0) {
+ return ret;
+ }
}
-
- int ret = getchar_internal(scancode, ascii, mods);
- if (ret == -1)
- goto again;
- return ret;
}
int _pit_sleep_and_quit_on_keypress(uint32_t ticks);
-int pit_sleep_and_quit_on_keypress(int seconds) {
- return _pit_sleep_and_quit_on_keypress(seconds * 18);
-}
-#endif
-
-#if uefi == 1
-int getchar(void) {
- EFI_KEY_DATA kd;
+static int input_sequence(void) {
+ int val = 0;
- UINTN which;
+ for (;;) {
+ int ret = -1;
+ size_t retries = 0;
- EFI_EVENT events[1];
+ while (ret == -1 && retries < 1000000) {
+ ret = serial_in();
+ retries++;
+ }
+ if (ret == -1) {
+ return 0;
+ }
- EFI_GUID exproto_guid = EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL_GUID;
- EFI_GUID sproto_guid = EFI_SIMPLE_TEXT_INPUT_PROTOCOL_GUID;
- EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *exproto = NULL;
- EFI_SIMPLE_TEXT_IN_PROTOCOL *sproto = NULL;
+ switch (ret) {
+ case 'A':
+ return GETCHAR_CURSOR_UP;
+ case 'B':
+ return GETCHAR_CURSOR_DOWN;
+ case 'C':
+ return GETCHAR_CURSOR_RIGHT;
+ case 'D':
+ return GETCHAR_CURSOR_LEFT;
+ case 'F':
+ return GETCHAR_END;
+ case 'H':
+ return GETCHAR_HOME;
+ }
- if (gBS->HandleProtocol(gST->ConsoleInHandle, &exproto_guid, (void **)&exproto) != EFI_SUCCESS) {
- if (gBS->HandleProtocol(gST->ConsoleInHandle, &sproto_guid, (void **)&sproto) != EFI_SUCCESS) {
- panic(false, "Your input device doesn't have an input protocol!");
+ if (ret > '9' || ret < '0') {
+ break;
}
- events[0] = sproto->WaitForKey;
- } else {
- events[0] = exproto->WaitForKeyEx;
+ val *= 10;
+ val += ret - '0';
+ }
+
+ switch (val) {
+ case 3:
+ return GETCHAR_DELETE;
+ case 5:
+ return GETCHAR_PGUP;
+ case 6:
+ return GETCHAR_PGDOWN;
+ case 21:
+ return GETCHAR_F10;
}
+ return 0;
+}
+
+int pit_sleep_and_quit_on_keypress(int seconds) {
+ for (int i = 0; i < seconds * 18; i++) {
+ int ret = _pit_sleep_and_quit_on_keypress(1);
+
+ if (ret != 0) {
+ return ret;
+ }
+
+ if (!serial) {
+ continue;
+ }
+
+ ret = serial_in();
+
+ if (ret != -1) {
again:
- memset(&kd, 0, sizeof(EFI_KEY_DATA));
+ switch (ret) {
+ case '\r':
+ return '\n';
+ case 0x1b:
+ delay(10000);
+ ret = serial_in();
+ if (ret == -1) {
+ return GETCHAR_ESCAPE;
+ }
+ if (ret == '[') {
+ return input_sequence();
+ }
+ goto again;
+ }
- gBS->WaitForEvent(1, events, &which);
+ return ret;
+ }
+ }
+ return 0;
+}
+#endif
+
+#if uefi == 1
+static int input_sequence(bool ext,
+ EFI_SIMPLE_TEXT_INPUT_EX_PROTOCOL *exproto,
+ EFI_SIMPLE_TEXT_IN_PROTOCOL *sproto) {
EFI_STATUS status;
- if (events[0] == sproto->WaitForKey) {
- status = sproto->ReadKeyStroke(sproto, &kd.Key);
- } else {
- status = exproto->ReadKeyStrokeEx(exproto, &kd);
- }
+ EFI_KEY_DATA kd;
- if (status != EFI_SUCCESS) {
- goto again;
+ int val = 0;
+
+ for (;;) {
+ if (ext == false) {
+ status = sproto->ReadKeyStroke(sproto, &kd.Key);
+ } else {
+ status = exproto->ReadKeyStrokeEx(exproto, &kd);
+ }
+
+ if (status != EFI_SUCCESS) {
+ return 0;
+ }
+
+ switch (kd.Key.UnicodeChar) {
+ case 'A':
+ return GETCHAR_CURSOR_UP;
+ case 'B':
+ return GETCHAR_CURSOR_DOWN;
+ case 'C':
+ return GETCHAR_CURSOR_RIGHT;
+ case 'D':
+ return GETCHAR_CURSOR_LEFT;
+ case 'F':
+ return GETCHAR_END;
+ case 'H':
+ return GETCHAR_HOME;
+ }
+
+ if (kd.Key.UnicodeChar > '9' || kd.Key.UnicodeChar < '0') {
+ break;
+ }
+
+ val *= 10;
+ val += kd.Key.UnicodeChar - '0';
}
- if ((kd.KeyState.KeyShiftState & EFI_SHIFT_STATE_VALID) == 0) {
- kd.KeyState.KeyShiftState = 0;
+ switch (val) {
+ case 3:
+ return GETCHAR_DELETE;
+ case 5:
+ return GETCHAR_PGUP;
+ case 6:
+ return GETCHAR_PGDOWN;
+ case 21:
+ return GETCHAR_F10;
}
- int ret = getchar_internal(kd.Key.ScanCode, kd.Key.UnicodeChar,
- kd.KeyState.KeyShiftState);
+ return 0;
+}
- if (ret == -1) {
- goto again;
+int getchar(void) {
+ for (;;) {
+ int ret = pit_sleep_and_quit_on_keypress(65535);
+ if (ret != 0) {
+ return ret;
+ }
}
-
- return ret;
}
int pit_sleep_and_quit_on_keypress(int seconds) {
@@ -224,6 +308,32 @@ again:
kd.KeyState.KeyShiftState = 0;
}
+ if (kd.Key.ScanCode == SCAN_ESC) {
+ gBS->CreateEvent(EVT_TIMER, TPL_CALLBACK, NULL, NULL, &events[1]);
+
+ gBS->SetTimer(events[1], TimerRelative, 100000);
+
+ gBS->WaitForEvent(2, events, &which);
+
+ if (which == 1) {
+ return GETCHAR_ESCAPE;
+ }
+
+ if (events[0] == sproto->WaitForKey) {
+ status = sproto->ReadKeyStroke(sproto, &kd.Key);
+ } else {
+ status = exproto->ReadKeyStrokeEx(exproto, &kd);
+ }
+
+ if (status != EFI_SUCCESS) {
+ goto again;
+ }
+
+ if (kd.Key.UnicodeChar == '[') {
+ return input_sequence(events[0] == exproto->WaitForKeyEx, exproto, sproto);
+ }
+ }
+
int ret = getchar_internal(kd.Key.ScanCode, kd.Key.UnicodeChar,
kd.KeyState.KeyShiftState);
diff --git a/common/lib/term.c b/common/lib/term.c
index 871bc294..e3cdb326 100644
--- a/common/lib/term.c
+++ b/common/lib/term.c
@@ -40,8 +40,8 @@ void term_vbe(size_t width, size_t height) {
}
if (serial) {
- term_cols = 80;
- term_rows = 24;
+ term_cols = term_cols > 80 ? 80 : term_cols;
+ term_rows = term_rows > 24 ? 24 : term_rows;
}
term_reinit();
@@ -358,8 +358,8 @@ void term_textmode(void) {
init_vga_textmode(&term_rows, &term_cols, true);
if (serial) {
- term_cols = 80;
- term_rows = 24;
+ term_cols = term_cols > 80 ? 80 : term_cols;
+ term_rows = term_rows > 24 ? 24 : term_rows;
}
term_reinit();
