mirror of
https://github.com/memtest86plus/memtest86plus.git
synced 2024-11-27 01:50:20 -06:00
Add support for Serial/TTY (#32)
* Add preliminary support for TTY Serial/UART (#15) * Use shadow_buffer instead of VGA buffer to get a framebuffer-agnostic TTY supprot * Added menu browsing & inputs from Serial TTY (#15) * Add fix for degree symbol on TTY. Correct serial.c & serial.h file created with CRLF (#15) * Move tty_error_redraw() to insure correct redraw when a error occurs * Many reindent / cleanup * Various optimization from @martinwhitaker comments
This commit is contained in:
parent
5cc72a6bed
commit
2e048a7c61
116
app/config.c
116
app/config.c
@ -23,6 +23,7 @@
|
||||
#include "keyboard.h"
|
||||
#include "memsize.h"
|
||||
#include "pmem.h"
|
||||
#include "serial.h"
|
||||
#include "screen.h"
|
||||
#include "smp.h"
|
||||
#include "usbhcd.h"
|
||||
@ -98,12 +99,65 @@ bool enable_bench = true;
|
||||
|
||||
bool pause_at_start = false;
|
||||
|
||||
power_save_t power_save = POWER_SAVE_HIGH;
|
||||
power_save_t power_save = POWER_SAVE_HIGH;
|
||||
|
||||
bool enable_tty = false;
|
||||
int tty_params_port = SERIAL_PORT_0x3F8;
|
||||
int tty_params_baud = SERIAL_DEFAULT_BAUDRATE;
|
||||
int tty_update_period = 2; // Update TTY every 2 seconds (default)
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Private Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static void parse_serial_params(const char *params)
|
||||
{
|
||||
if (strncmp(params, "ttyS", 5) == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (params[4] >= '0' && params[4] <= '3') {
|
||||
tty_params_port = params[4] - '0';
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
|
||||
enable_tty = true;
|
||||
|
||||
if (params[5] != ',' && params[5] != ' ') {
|
||||
return;
|
||||
}
|
||||
|
||||
if (params[6] >= '0' && params[6] <= '9') {
|
||||
|
||||
switch (params[6])
|
||||
{
|
||||
default:
|
||||
return;
|
||||
case '0':
|
||||
tty_params_baud = 9600;
|
||||
tty_update_period = 5;
|
||||
break;
|
||||
case '1':
|
||||
tty_params_baud = 19200;
|
||||
tty_update_period = 4;
|
||||
break;
|
||||
case '2':
|
||||
tty_params_baud = 38400;
|
||||
tty_update_period = 3;
|
||||
break;
|
||||
case '3':
|
||||
tty_params_baud = 57600;
|
||||
tty_update_period = 3;
|
||||
break;
|
||||
case '4':
|
||||
tty_params_baud = 115200;
|
||||
tty_update_period = 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void parse_option(const char *option, const char *params)
|
||||
{
|
||||
if (option[0] == '\0') return;
|
||||
@ -117,10 +171,6 @@ static void parse_option(const char *option, const char *params)
|
||||
keyboard_types = KT_USB;
|
||||
usb_init_options = USB_EXTRA_RESET;
|
||||
}
|
||||
} else if (strncmp(option, "nopause", 8) == 0) {
|
||||
pause_at_start = false;
|
||||
} else if (strncmp(option, "nobench", 8) == 0) {
|
||||
enable_bench = false;
|
||||
} else if (strncmp(option, "powersave", 10) == 0) {
|
||||
if (strncmp(params, "off", 4) == 0) {
|
||||
power_save = POWER_SAVE_OFF;
|
||||
@ -129,6 +179,14 @@ static void parse_option(const char *option, const char *params)
|
||||
} else if (strncmp(params, "high", 5) == 0) {
|
||||
power_save = POWER_SAVE_HIGH;
|
||||
}
|
||||
} else if (strncmp(option, "console", 8) == 0) {
|
||||
if (params != NULL) {
|
||||
parse_serial_params(params);
|
||||
}
|
||||
} else if (strncmp(option, "nobench", 8) == 0) {
|
||||
enable_bench = false;
|
||||
} else if (strncmp(option, "nopause", 8) == 0) {
|
||||
pause_at_start = false;
|
||||
} else if (strncmp(option, "smp", 4) == 0) {
|
||||
smp_enabled = true;
|
||||
} else if (strncmp(option, "trace", 6) == 0) {
|
||||
@ -209,6 +267,7 @@ static void display_input_message(int row, const char *message)
|
||||
{
|
||||
clear_popup_row(row);
|
||||
prints(row, POP_LM, message);
|
||||
if (enable_tty) tty_send_region(POP_REGION);
|
||||
}
|
||||
|
||||
static void display_error_message(int row, const char *message)
|
||||
@ -217,6 +276,7 @@ static void display_error_message(int row, const char *message)
|
||||
set_foreground_colour(YELLOW);
|
||||
prints(row, POP_LM, message);
|
||||
set_foreground_colour(WHITE);
|
||||
if (enable_tty) tty_send_region(POP_REGION);
|
||||
}
|
||||
|
||||
static void display_selection_header(int row, int max_num, int offset)
|
||||
@ -321,9 +381,16 @@ static void test_selection_menu(void)
|
||||
display_enabled(POP_R+12, i, test_list[i].enabled);
|
||||
}
|
||||
|
||||
bool tty_update = enable_tty;
|
||||
bool exit_menu = false;
|
||||
while (!exit_menu) {
|
||||
bool changed = false;
|
||||
|
||||
if (tty_update) {
|
||||
tty_send_region(POP_REGION);
|
||||
}
|
||||
tty_update = enable_tty;
|
||||
|
||||
switch (get_key()) {
|
||||
case '1':
|
||||
changed = set_all_tests(false);
|
||||
@ -356,6 +423,7 @@ static void test_selection_menu(void)
|
||||
} break;
|
||||
default:
|
||||
usleep(1000);
|
||||
tty_update = false;
|
||||
break;
|
||||
}
|
||||
if (changed) {
|
||||
@ -363,6 +431,7 @@ static void test_selection_menu(void)
|
||||
changed = false;
|
||||
}
|
||||
}
|
||||
|
||||
clear_screen_region(POP_REGION);
|
||||
}
|
||||
|
||||
@ -376,9 +445,16 @@ static void address_range_menu(void)
|
||||
prints(POP_R+6, POP_LI, "<F10> Exit menu");
|
||||
printf(POP_R+8, POP_LM, "Current range: %kB - %kB", pm_limit_lower << 2, pm_limit_upper << 2);
|
||||
|
||||
bool tty_update = enable_tty;
|
||||
bool exit_menu = false;
|
||||
while (!exit_menu) {
|
||||
bool changed = false;
|
||||
|
||||
if (tty_update) {
|
||||
tty_send_region(POP_REGION);
|
||||
}
|
||||
tty_update = enable_tty;
|
||||
|
||||
switch (get_key()) {
|
||||
case '1': {
|
||||
display_input_message(POP_R+10, "Enter lower limit: ");
|
||||
@ -413,6 +489,7 @@ static void address_range_menu(void)
|
||||
break;
|
||||
default:
|
||||
usleep(1000);
|
||||
tty_update = false;
|
||||
break;
|
||||
}
|
||||
if (changed) {
|
||||
@ -444,9 +521,16 @@ static void cpu_mode_menu(void)
|
||||
prints(POP_R+6, POP_LI, "<F10> Exit menu");
|
||||
printc(POP_R+3+cpu_mode, POP_LM, '*');
|
||||
|
||||
bool tty_update = enable_tty;
|
||||
bool exit_menu = false;
|
||||
while (!exit_menu) {
|
||||
int ch = get_key();
|
||||
|
||||
if (tty_update) {
|
||||
tty_send_region(POP_REGION);
|
||||
}
|
||||
tty_update = enable_tty;
|
||||
|
||||
switch (ch) {
|
||||
case '1':
|
||||
case '2':
|
||||
@ -468,6 +552,7 @@ static void cpu_mode_menu(void)
|
||||
break;
|
||||
default:
|
||||
usleep(1000);
|
||||
tty_update = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -493,9 +578,17 @@ static void error_mode_menu(void)
|
||||
prints(POP_R+7, POP_LI, "<F10> Exit menu");
|
||||
printc(POP_R+3+error_mode, POP_LM, '*');
|
||||
|
||||
bool tty_update = enable_tty;
|
||||
bool exit_menu = false;
|
||||
while (!exit_menu) {
|
||||
int ch = get_key();
|
||||
|
||||
if (tty_update) {
|
||||
tty_send_region(POP_REGION);
|
||||
}
|
||||
|
||||
tty_update = enable_tty;
|
||||
|
||||
switch (ch) {
|
||||
case '1':
|
||||
case '2':
|
||||
@ -518,6 +611,7 @@ static void error_mode_menu(void)
|
||||
break;
|
||||
default:
|
||||
usleep(1000);
|
||||
tty_update = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -690,6 +784,7 @@ void config_menu(bool initial)
|
||||
|
||||
cpu_mode_t old_cpu_mode = cpu_mode;
|
||||
|
||||
bool tty_update = enable_tty;
|
||||
bool exit_menu = false;
|
||||
while (!exit_menu) {
|
||||
prints(POP_R+1, POP_LM, "Settings:");
|
||||
@ -711,6 +806,12 @@ void config_menu(bool initial)
|
||||
prints(POP_R+8 , POP_LI, "<F10> Exit menu");
|
||||
}
|
||||
|
||||
if (tty_update) {
|
||||
tty_send_region(POP_REGION);
|
||||
}
|
||||
|
||||
tty_update = enable_tty;
|
||||
|
||||
switch (get_key()) {
|
||||
case '1':
|
||||
test_selection_menu();
|
||||
@ -751,6 +852,7 @@ void config_menu(bool initial)
|
||||
break;
|
||||
default:
|
||||
usleep(1000);
|
||||
tty_update = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -758,6 +860,10 @@ void config_menu(bool initial)
|
||||
restore_screen_region(POP_REGION, popup_save_buffer);
|
||||
set_background_colour(BLUE);
|
||||
|
||||
if (enable_tty) {
|
||||
tty_send_region(POP_REGION);
|
||||
}
|
||||
|
||||
if (cpu_mode != old_cpu_mode) {
|
||||
display_cpu_mode(cpu_mode_str[cpu_mode]);
|
||||
restart = true;
|
||||
|
@ -49,12 +49,17 @@ extern bool enable_temperature;
|
||||
extern bool enable_trace;
|
||||
|
||||
extern bool enable_sm;
|
||||
extern bool enable_tty;
|
||||
extern bool enable_bench;
|
||||
|
||||
extern bool pause_at_start;
|
||||
|
||||
extern power_save_t power_save;
|
||||
|
||||
extern int tty_params_port;
|
||||
extern int tty_params_baud;
|
||||
extern int tty_update_period;
|
||||
|
||||
void config_init(void);
|
||||
|
||||
void config_menu(bool initial);
|
||||
|
@ -9,6 +9,7 @@
|
||||
#include "hwctrl.h"
|
||||
#include "io.h"
|
||||
#include "keyboard.h"
|
||||
#include "serial.h"
|
||||
#include "pmem.h"
|
||||
#include "smbios.h"
|
||||
#include "smbus.h"
|
||||
@ -53,6 +54,9 @@ static int test_bar_length = 0; // currently displayed length
|
||||
static uint64_t run_start_time = 0; // TSC time stamp
|
||||
static uint64_t next_spin_time = 0; // TSC time stamp
|
||||
|
||||
static int prev_sec = -1; // previous second
|
||||
static bool timed_update_done = false; // update cycle status
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Variables
|
||||
//------------------------------------------------------------------------------
|
||||
@ -91,19 +95,22 @@ void display_init(void)
|
||||
prints(9, 0, "-------------------------------------------------------------------------------");
|
||||
|
||||
// Redraw lines using box drawing characters.
|
||||
for (int i = 0;i < 80; i++) {
|
||||
print_char(6, i, 0xc4);
|
||||
print_char(9, i, 0xc4);
|
||||
// Disable if TTY is enabled to avoid VT100 char replacements
|
||||
if (!enable_tty) {
|
||||
for (int i = 0;i < 80; i++) {
|
||||
print_char(6, i, 0xc4);
|
||||
print_char(9, i, 0xc4);
|
||||
}
|
||||
for (int i = 0; i < 6; i++) {
|
||||
print_char(i, 28, 0xb3);
|
||||
}
|
||||
for (int i = 7; i < 10; i++) {
|
||||
print_char(i, 39, 0xb3);
|
||||
}
|
||||
print_char(6, 28, 0xc1);
|
||||
print_char(6, 39, 0xc2);
|
||||
print_char(9, 39, 0xc1);
|
||||
}
|
||||
for (int i = 0; i < 6; i++) {
|
||||
print_char(i, 28, 0xb3);
|
||||
}
|
||||
for (int i = 7; i < 10; i++) {
|
||||
print_char(i, 39, 0xb3);
|
||||
}
|
||||
print_char(6, 28, 0xc1);
|
||||
print_char(6, 39, 0xc2);
|
||||
print_char(9, 39, 0xc1);
|
||||
|
||||
set_foreground_colour(BLUE);
|
||||
set_background_colour(WHITE);
|
||||
@ -175,6 +182,10 @@ void display_start_run(void)
|
||||
next_spin_time = run_start_time + SPINNER_PERIOD * clks_per_msec;
|
||||
}
|
||||
display_spinner('-');
|
||||
|
||||
if (enable_tty){
|
||||
tty_full_redraw();
|
||||
}
|
||||
}
|
||||
|
||||
void display_start_pass(void)
|
||||
@ -252,6 +263,7 @@ void scroll(void)
|
||||
|
||||
void do_tick(int my_cpu)
|
||||
{
|
||||
int act_sec = 0;
|
||||
bool use_spin_wait = (power_save < POWER_SAVE_HIGH);
|
||||
if (use_spin_wait) {
|
||||
barrier_spin_wait(run_barrier);
|
||||
@ -303,7 +315,7 @@ void do_tick(int my_cpu)
|
||||
uint64_t current_time = get_tsc();
|
||||
|
||||
int secs = (current_time - run_start_time) / (1000 * clks_per_msec);
|
||||
int mins = secs / 60; secs %= 60;
|
||||
int mins = secs / 60; secs %= 60; act_sec = secs;
|
||||
int hours = mins / 60; mins %= 60;
|
||||
display_run_time(hours, mins, secs);
|
||||
|
||||
@ -313,14 +325,38 @@ void do_tick(int my_cpu)
|
||||
update_spinner = false;
|
||||
}
|
||||
}
|
||||
|
||||
/* ---------------
|
||||
* Timed functions
|
||||
* --------------- */
|
||||
|
||||
// update spinner every SPINNER_PERIOD ms
|
||||
if (update_spinner) {
|
||||
spin_idx = (spin_idx + 1) % NUM_SPIN_STATES;
|
||||
display_spinner(spin_state[spin_idx]);
|
||||
}
|
||||
|
||||
// This only tick one time per second
|
||||
if (!timed_update_done) {
|
||||
|
||||
if (enable_temperature) {
|
||||
display_temperature(get_cpu_temperature());
|
||||
// Update temperature one time per second
|
||||
if (enable_temperature) {
|
||||
display_temperature(get_cpu_temperature());
|
||||
}
|
||||
|
||||
// Update TTY one time every TTY_UPDATE_PERIOD second(s)
|
||||
if (enable_tty) {
|
||||
|
||||
if (act_sec % tty_update_period == 0) {
|
||||
tty_partial_redraw();
|
||||
}
|
||||
}
|
||||
timed_update_done = true;
|
||||
}
|
||||
|
||||
if (act_sec != prev_sec) {
|
||||
prev_sec = act_sec;
|
||||
timed_update_done = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
10
app/error.c
10
app/error.c
@ -23,6 +23,8 @@
|
||||
|
||||
#include "tests.h"
|
||||
|
||||
#include "serial.h"
|
||||
|
||||
#include "error.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
@ -213,7 +215,7 @@ static void common_err(error_type_t type, uintptr_t addr, testword_t good, testw
|
||||
bits++;
|
||||
}
|
||||
}
|
||||
display_pinned_message(0, 25, "%09x%03x (%kB)",
|
||||
display_pinned_message(0, 25, "%09x%03x (%kB)",
|
||||
error_info.min_addr.page,
|
||||
error_info.min_addr.offset,
|
||||
error_info.min_addr.page << 2);
|
||||
@ -255,7 +257,7 @@ static void common_err(error_type_t type, uintptr_t addr, testword_t good, testw
|
||||
#else
|
||||
// columns: 0---------1---------2---------3---------4---------5---------6---------7---------
|
||||
display_pinned_message(0, 0, "pCPU Pass Test Failing Address Expected Found Err Bits");
|
||||
display_pinned_message(1, 0, "---- ---- ---- --------------------- -------- -------- --------");
|
||||
display_pinned_message(1, 0, "---- ---- ---- --------------------- -------- -------- --------");
|
||||
// fields: NN NNNN NN PPPPPPPPPOOO (N.NN?B) XXXXXXXX XXXXXXXX XXXXXXXX
|
||||
#endif
|
||||
}
|
||||
@ -365,5 +367,9 @@ void error_update(void)
|
||||
test_list[test_num].errors);
|
||||
}
|
||||
display_error_count(error_count);
|
||||
|
||||
if (enable_tty) {
|
||||
tty_error_redraw();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -28,6 +28,7 @@
|
||||
#include "memsize.h"
|
||||
#include "pci.h"
|
||||
#include "screen.h"
|
||||
#include "serial.h"
|
||||
#include "smbios.h"
|
||||
#include "smp.h"
|
||||
#include "temperature.h"
|
||||
@ -145,7 +146,6 @@ static void run_at(uintptr_t addr, int my_cpu)
|
||||
{
|
||||
uintptr_t *new_start_addr = (uintptr_t *)(addr + startup - _start);
|
||||
|
||||
|
||||
if (my_cpu == 0) {
|
||||
// Copy the program code and all data except the stacks.
|
||||
memcpy((void *)addr, (void *)_start, _stacks - _start);
|
||||
@ -212,6 +212,8 @@ static void global_init(void)
|
||||
|
||||
membw_init();
|
||||
|
||||
tty_init();
|
||||
|
||||
smbios_init();
|
||||
|
||||
badram_init();
|
||||
|
@ -18,6 +18,7 @@ SYS_OBJS = system/cpuid.o \
|
||||
system/pmem.o \
|
||||
system/reloc.o \
|
||||
system/screen.o \
|
||||
system/serial.o \
|
||||
system/smbios.o \
|
||||
system/smbus.o \
|
||||
system/smp.o \
|
||||
|
@ -18,6 +18,7 @@ SYS_OBJS = system/cpuid.o \
|
||||
system/pmem.o \
|
||||
system/reloc.o \
|
||||
system/screen.o \
|
||||
system/serial.o \
|
||||
system/smbios.o \
|
||||
system/smbus.o \
|
||||
system/smp.o \
|
||||
|
11
lib/read.c
11
lib/read.c
@ -11,9 +11,11 @@
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "config.h"
|
||||
#include "ctype.h"
|
||||
#include "keyboard.h"
|
||||
#include "print.h"
|
||||
#include "serial.h"
|
||||
#include "unistd.h"
|
||||
|
||||
#include "read.h"
|
||||
@ -34,9 +36,17 @@ uintptr_t read_value(int row, int col, int field_width, int shift)
|
||||
int n = 0;
|
||||
int base = 10;
|
||||
bool done = false;
|
||||
bool tty_update = enable_tty;
|
||||
bool got_suffix = false;
|
||||
while (!done) {
|
||||
char c = get_key();
|
||||
|
||||
if (tty_update) {
|
||||
tty_send_region(row, col, row, col+10);
|
||||
}
|
||||
|
||||
tty_update = enable_tty;
|
||||
|
||||
switch (c) {
|
||||
case '\n':
|
||||
if (n > 0) {
|
||||
@ -91,6 +101,7 @@ uintptr_t read_value(int row, int col, int field_width, int shift)
|
||||
break;
|
||||
default:
|
||||
usleep(1000);
|
||||
tty_update = false;
|
||||
break;
|
||||
}
|
||||
if (n < field_width && buffer[n] != ' ') {
|
||||
|
41
lib/string.c
41
lib/string.c
@ -12,6 +12,21 @@
|
||||
|
||||
#include "string.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Private Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void reverse(char s[])
|
||||
{
|
||||
int i, j;
|
||||
char c;
|
||||
|
||||
for (i = 0, j = strlen(s)-1; i<j; i++, j--) {
|
||||
c = s[i];
|
||||
s[i] = s[j];
|
||||
s[j] = c;
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Functions
|
||||
//------------------------------------------------------------------------------
|
||||
@ -105,3 +120,29 @@ char *strstr(const char *haystack, const char *needle)
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
char *itoa(int num, char *str)
|
||||
{
|
||||
int i = 0;
|
||||
|
||||
/* Special case for 0 */
|
||||
if (num == 0) {
|
||||
str[i++] = '0';
|
||||
str[i] = '\0';
|
||||
return str;
|
||||
}
|
||||
|
||||
// Parse digits
|
||||
while (num != 0) {
|
||||
int rem = num % 10;
|
||||
str[i++] = (rem > 9) ? (rem-10) + 'a' : rem + '0';
|
||||
num /= 10;
|
||||
}
|
||||
|
||||
// Last is string terminator
|
||||
str[i] = '\0';
|
||||
|
||||
reverse(str);
|
||||
|
||||
return str;
|
||||
}
|
||||
|
@ -60,4 +60,10 @@ int strncmp(const char *s1, const char *s2, size_t n);
|
||||
*/
|
||||
char *strstr(const char *haystack, const char *needle);
|
||||
|
||||
/**
|
||||
* Convert n to characters in s
|
||||
*/
|
||||
|
||||
char *itoa(int num, char *str);
|
||||
|
||||
#endif // STRING_H
|
||||
|
@ -6,7 +6,10 @@
|
||||
#include "io.h"
|
||||
#include "usbhcd.h"
|
||||
|
||||
#include "serial.h"
|
||||
|
||||
#include "keyboard.h"
|
||||
#include "config.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Private Variables
|
||||
@ -255,5 +258,13 @@ char get_key(void)
|
||||
}
|
||||
}
|
||||
|
||||
if (enable_tty) {
|
||||
uint8_t c = tty_get_key();
|
||||
if (c != 0xFF) {
|
||||
if (c == 0x0D) c = '\n'; // Enter
|
||||
return c;
|
||||
}
|
||||
}
|
||||
|
||||
return '\0';
|
||||
}
|
||||
|
@ -13,7 +13,7 @@
|
||||
#include "screen.h"
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Types
|
||||
// Private Variables
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
typedef struct {
|
||||
@ -22,22 +22,6 @@ typedef struct {
|
||||
uint8_t b;
|
||||
} __attribute__((packed)) rgb_value_t;
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
uint8_t ch;
|
||||
uint8_t attr;
|
||||
};
|
||||
struct {
|
||||
uint16_t value;
|
||||
};
|
||||
} vga_char_t;
|
||||
|
||||
typedef vga_char_t vga_buffer_t[SCREEN_HEIGHT][SCREEN_WIDTH];
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Private Variables
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static const rgb_value_t vga_pallete[16] = {
|
||||
// R G B
|
||||
{ 0, 0, 0 }, // BLACK
|
||||
@ -60,7 +44,7 @@ static const rgb_value_t vga_pallete[16] = {
|
||||
|
||||
static vga_buffer_t *vga_buffer = (vga_buffer_t *)(0xb8000);
|
||||
|
||||
static vga_buffer_t shadow_buffer;
|
||||
vga_buffer_t shadow_buffer;
|
||||
|
||||
static int lfb_bytes_per_pixel = 0;
|
||||
|
||||
|
@ -19,6 +19,18 @@
|
||||
#define SCREEN_WIDTH 80
|
||||
#define SCREEN_HEIGHT 25
|
||||
|
||||
typedef union {
|
||||
struct {
|
||||
uint8_t ch;
|
||||
uint8_t attr;
|
||||
};
|
||||
struct {
|
||||
uint16_t value;
|
||||
};
|
||||
} vga_char_t;
|
||||
|
||||
typedef vga_char_t vga_buffer_t[SCREEN_HEIGHT][SCREEN_WIDTH];
|
||||
|
||||
/**
|
||||
* Colours that can be used for the foreground or background.
|
||||
*/
|
||||
@ -33,6 +45,12 @@ typedef enum {
|
||||
WHITE = 7
|
||||
} screen_colour_t;
|
||||
|
||||
/**
|
||||
* BIOS/UEFI(GOP) agnostic framebuffer copy
|
||||
*/
|
||||
|
||||
extern vga_buffer_t shadow_buffer;
|
||||
|
||||
/**
|
||||
* Modifier that can be added to any foreground colour.
|
||||
* Has no effect on background colours.
|
||||
|
210
system/serial.c
Normal file
210
system/serial.c
Normal file
@ -0,0 +1,210 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (C) 2004-2022 Sam Demeulemeester
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "io.h"
|
||||
#include "string.h"
|
||||
#include "serial.h"
|
||||
#include "unistd.h"
|
||||
|
||||
#include "config.h"
|
||||
#include "display.h"
|
||||
|
||||
static struct serial_port console_serial;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Private Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
static void serial_write_reg(struct serial_port *port, uint16_t reg, uint8_t val)
|
||||
{
|
||||
union {
|
||||
uintptr_t addr;
|
||||
uint8_t *ptr;
|
||||
} reg_walker;
|
||||
|
||||
reg_walker.addr = port->base_addr + reg * port->reg_width;
|
||||
|
||||
if (port->is_mmio) {
|
||||
*reg_walker.ptr = val;
|
||||
} else {
|
||||
__outb(val, reg_walker.addr);
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t serial_read_reg(struct serial_port *port, uint16_t reg)
|
||||
{
|
||||
union {
|
||||
uintptr_t addr;
|
||||
uint8_t *ptr;
|
||||
} reg_walker;
|
||||
|
||||
reg_walker.addr = port->base_addr + reg * port->reg_width;
|
||||
|
||||
if (port->is_mmio) {
|
||||
return *reg_walker.ptr;
|
||||
} else {
|
||||
return __inb(reg_walker.addr);
|
||||
}
|
||||
}
|
||||
|
||||
static void serial_wait_for_xmit(struct serial_port *port)
|
||||
{
|
||||
uint8_t lsr;
|
||||
|
||||
do {
|
||||
lsr = serial_read_reg(port, UART_LSR);
|
||||
} while ((lsr & BOTH_EMPTY) != BOTH_EMPTY);
|
||||
}
|
||||
|
||||
void serial_echo_print(const char *p)
|
||||
{
|
||||
struct serial_port *port = &console_serial;
|
||||
|
||||
if (!port->enable) {
|
||||
return;
|
||||
}
|
||||
/* Now, do each character */
|
||||
while (*p) {
|
||||
serial_wait_for_xmit(port);
|
||||
|
||||
/* Send the character out. */
|
||||
serial_write_reg(port, UART_TX, *p);
|
||||
if (*p==10) {
|
||||
serial_wait_for_xmit(port);
|
||||
serial_write_reg(port, UART_TX, 13);
|
||||
}
|
||||
p++;
|
||||
}
|
||||
}
|
||||
|
||||
void tty_print(int y, int x, const char *p)
|
||||
{
|
||||
static char sx[3], sy[3];
|
||||
|
||||
itoa(++x,sx);
|
||||
itoa(++y,sy);
|
||||
|
||||
serial_echo_print("\x1b[");
|
||||
serial_echo_print(sy);
|
||||
serial_echo_print(";");
|
||||
serial_echo_print(sx);
|
||||
serial_echo_print("H");
|
||||
serial_echo_print(p);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Public Functions
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
void tty_init(void)
|
||||
{
|
||||
if (!enable_tty) {
|
||||
return;
|
||||
}
|
||||
|
||||
int uart_status, serial_div;
|
||||
unsigned char lcr;
|
||||
|
||||
console_serial.enable = true;
|
||||
console_serial.is_mmio = false;
|
||||
console_serial.parity = SERIAL_DEFAULT_PARITY;
|
||||
console_serial.bits = SERIAL_DEFAULT_BITS;
|
||||
console_serial.baudrate = tty_params_baud;
|
||||
console_serial.reg_width = 1;
|
||||
console_serial.refclk = UART_REF_CLK;
|
||||
console_serial.base_addr = serial_base_ports[tty_params_port];
|
||||
|
||||
/* read the Divisor Latch */
|
||||
uart_status = serial_read_reg(&console_serial, UART_LCR);
|
||||
serial_write_reg(&console_serial, UART_LCR, uart_status | UART_LCR_DLAB);
|
||||
serial_read_reg(&console_serial, UART_DLM);
|
||||
serial_read_reg(&console_serial, UART_DLL);
|
||||
serial_write_reg(&console_serial, UART_LCR, uart_status);
|
||||
|
||||
/* now do hardwired init */
|
||||
lcr = console_serial.parity | (console_serial.bits - 5);
|
||||
serial_write_reg(&console_serial, UART_LCR, lcr); /* No parity, 8 data bits, 1 stop */
|
||||
serial_div = (console_serial.refclk / console_serial.baudrate) / 16;
|
||||
serial_write_reg(&console_serial, UART_LCR, 0x80|lcr); /* Access divisor latch */
|
||||
serial_write_reg(&console_serial, UART_DLL, serial_div & 0xff); /* baud rate divisor */
|
||||
serial_write_reg(&console_serial, UART_DLM, (serial_div >> 8) & 0xff);
|
||||
serial_write_reg(&console_serial, UART_LCR, lcr); /* Done with divisor */
|
||||
|
||||
/* Prior to disabling interrupts, read the LSR and RBR registers */
|
||||
uart_status = serial_read_reg(&console_serial, UART_LSR); /* COM? LSR */
|
||||
uart_status = serial_read_reg(&console_serial, UART_RX); /* COM? RBR */
|
||||
serial_write_reg(&console_serial, UART_IER, 0x00); /* Disable all interrupts */
|
||||
|
||||
tty_clear_screen();
|
||||
tty_disable_cursor();
|
||||
}
|
||||
|
||||
void tty_send_region(int start_row, int start_col, int end_row, int end_col)
|
||||
{
|
||||
char p[SCREEN_WIDTH+1];
|
||||
int col_len = end_col - start_col;
|
||||
|
||||
if (start_col > (SCREEN_WIDTH - 1) || end_col > (SCREEN_WIDTH - 1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (start_row > (SCREEN_HEIGHT - 1) || end_row > (SCREEN_HEIGHT - 1)) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (int row = start_row; row <= end_row; row++) {
|
||||
|
||||
// Last line is inverted (Black on white)
|
||||
if (row == SCREEN_HEIGHT-1) {
|
||||
tty_inverse();
|
||||
}
|
||||
|
||||
// Copy Shadow buffer to TTY buffer
|
||||
for (int col = start_col; col <= end_col; col++) {
|
||||
p[col] = shadow_buffer[row][col].value & 0x7F;
|
||||
}
|
||||
|
||||
// Add string terminator
|
||||
p[end_col+1] = '\0';
|
||||
|
||||
// For first line, title on top-left must be inverted
|
||||
// Do the switch, send to TTY then continue to next line.
|
||||
if (row == 0 && start_col == 0 && col_len > 28) {
|
||||
tty_inverse();
|
||||
p[28] = '\0';
|
||||
tty_print(row,0,p);
|
||||
tty_normal();
|
||||
p[28] = '|';
|
||||
tty_print(row, 28, p + 28);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Replace degree symbol with '*' for tty to avoid a C437/VT100 translation table.
|
||||
if (row == 7 && col_len > 77 && (shadow_buffer[7][73].value & 0xFF) == 0xF8) {
|
||||
p[73] = 0x2A;
|
||||
}
|
||||
|
||||
// Send row to TTY
|
||||
tty_print(row, start_col, p + start_col);
|
||||
|
||||
// Revert to normal if last line.
|
||||
if (row == SCREEN_HEIGHT-1) {
|
||||
tty_normal();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char tty_get_key(void)
|
||||
{
|
||||
int uart_status = serial_read_reg(&console_serial, UART_LSR);
|
||||
|
||||
if (uart_status & UART_LSR_DR) {
|
||||
return serial_read_reg(&console_serial, UART_RX);
|
||||
} else {
|
||||
return 0xFF;
|
||||
}
|
||||
}
|
177
system/serial.h
Normal file
177
system/serial.h
Normal file
@ -0,0 +1,177 @@
|
||||
#ifndef _SERIAL_REG_H
|
||||
#define _SERIAL_REG_H
|
||||
/**
|
||||
* \file
|
||||
*
|
||||
* Provides the TTY interface. It provides an 80x25 VT100 compatible
|
||||
* display via Serial/UART.
|
||||
*
|
||||
*//*
|
||||
* Copyright (C) 2004-2022 Sam Demeulemeester.
|
||||
*/
|
||||
|
||||
#define SERIAL_DEFAULT_BAUDRATE 115200
|
||||
#define SERIAL_DEFAULT_BITS 8
|
||||
#define SERIAL_DEFAULT_PARITY 0
|
||||
|
||||
#define SERIAL_PORT_0x3F8 0
|
||||
#define SERIAL_PORT_0x2F8 1
|
||||
#define SERIAL_PORT_0x3E8 2
|
||||
#define SERIAL_PORT_0x2E8 3
|
||||
|
||||
static const uint16_t serial_base_ports[] = { 0x3f8, 0x2f8, 0x3e8, 0x2e8 };
|
||||
|
||||
struct serial_port {
|
||||
bool enable;
|
||||
bool is_mmio;
|
||||
int parity;
|
||||
int bits;
|
||||
int baudrate;
|
||||
int reg_width;
|
||||
int refclk;
|
||||
uintptr_t base_addr;
|
||||
};
|
||||
|
||||
/*
|
||||
* Definitions for VT100 commands
|
||||
*/
|
||||
|
||||
#define TTY_CLEAR_SCREEN "\x1b[2J"
|
||||
|
||||
#define TTY_DISABLE_CURSOR "\x1b[?25l"
|
||||
|
||||
#define TTY_NORMAL "\x1b[0m"
|
||||
#define TTY_BOLD "\x1b[1m"
|
||||
#define TTY_UNDERLINE "\x1b[4m"
|
||||
#define TTY_INVERSE "\x1b[7m"
|
||||
|
||||
/*
|
||||
* Definitions for the Base UART Registers
|
||||
*/
|
||||
|
||||
#define UART_REF_CLK 1843200
|
||||
|
||||
#define UART_RX 0 /* In: Receive buffer (DLAB=0) */
|
||||
#define UART_TX 0 /* Out: Transmit buffer (DLAB=0) */
|
||||
#define UART_DLL 0 /* Out: Divisor Latch Low (DLAB=1) */
|
||||
#define UART_DLM 1 /* Out: Divisor Latch High (DLAB=1) */
|
||||
#define UART_IER 1 /* Out: Interrupt Enable Register */
|
||||
#define UART_IIR 2 /* In: Interrupt ID Register */
|
||||
#define UART_FCR 2 /* Out: FIFO Control Register */
|
||||
#define UART_EFR 2 /* I/O: Extended Features Register */
|
||||
/* (DLAB=1, 16C660 only) */
|
||||
#define UART_LCR 3 /* Out: Line Control Register */
|
||||
#define UART_MCR 4 /* Out: Modem Control Register */
|
||||
#define UART_LSR 5 /* In: Line Status Register */
|
||||
#define UART_MSR 6 /* In: Modem Status Register */
|
||||
#define UART_SCR 7 /* I/O: Scratch Register */
|
||||
|
||||
/*
|
||||
* Definitions for the Line Control Register
|
||||
*/
|
||||
|
||||
#define UART_LCR_DLAB 0x80 /* Divisor latch access bit */
|
||||
#define UART_LCR_SBC 0x40 /* Set break control */
|
||||
#define UART_LCR_SPAR 0x20 /* Stick parity (?) */
|
||||
#define UART_LCR_EPAR 0x10 /* Even parity select */
|
||||
#define UART_LCR_PARITY 0x08 /* Parity Enable */
|
||||
#define UART_LCR_STOP 0x04 /* Stop bits: 0=1 stop bit, 1= 2 stop bits */
|
||||
#define UART_LCR_WLEN5 0x00 /* Wordlength: 5 bits */
|
||||
#define UART_LCR_WLEN6 0x01 /* Wordlength: 6 bits */
|
||||
#define UART_LCR_WLEN7 0x02 /* Wordlength: 7 bits */
|
||||
#define UART_LCR_WLEN8 0x03 /* Wordlength: 8 bits */
|
||||
|
||||
/*
|
||||
* Definitions for the Line Status Register
|
||||
*/
|
||||
#define UART_LSR_TEMT 0x40 /* Transmitter empty */
|
||||
#define UART_LSR_THRE 0x20 /* Transmit-hold-register empty */
|
||||
#define UART_LSR_BI 0x10 /* Break interrupt indicator */
|
||||
#define UART_LSR_FE 0x08 /* Frame error indicator */
|
||||
#define UART_LSR_PE 0x04 /* Parity error indicator */
|
||||
#define UART_LSR_OE 0x02 /* Overrun error indicator */
|
||||
#define UART_LSR_DR 0x01 /* Receiver data ready */
|
||||
|
||||
/*
|
||||
* Definitions for the Interrupt Identification Register
|
||||
*/
|
||||
#define UART_IIR_NO_INT 0x01 /* No interrupts pending */
|
||||
#define UART_IIR_ID 0x06 /* Mask for the interrupt ID */
|
||||
|
||||
#define UART_IIR_MSI 0x00 /* Modem status interrupt */
|
||||
#define UART_IIR_THRI 0x02 /* Transmitter holding register empty */
|
||||
#define UART_IIR_RDI 0x04 /* Receiver data interrupt */
|
||||
#define UART_IIR_RLSI 0x06 /* Receiver line status interrupt */
|
||||
|
||||
/*
|
||||
* Definitions for the Interrupt Enable Register
|
||||
*/
|
||||
#define UART_IER_MS 0x08 /* Enable Modem status interrupt */
|
||||
#define UART_IER_RLSI 0x04 /* Enable receiver line status interrupt */
|
||||
#define UART_IER_THRI 0x02 /* Enable Transmitter holding register int. */
|
||||
#define UART_IER_RDI 0x01 /* Enable receiver data interrupt */
|
||||
|
||||
/*
|
||||
* Definitions for the Modem Control Register
|
||||
*/
|
||||
#define UART_MCR_LOOP 0x10 /* Enable loopback test mode */
|
||||
#define UART_MCR_OUT2 0x08 /* Out2 complement */
|
||||
#define UART_MCR_OUT1 0x04 /* Out1 complement */
|
||||
#define UART_MCR_RTS 0x02 /* RTS complement */
|
||||
#define UART_MCR_DTR 0x01 /* DTR complement */
|
||||
|
||||
/*
|
||||
* Definitions for the Modem Status Register
|
||||
*/
|
||||
#define UART_MSR_DCD 0x80 /* Data Carrier Detect */
|
||||
#define UART_MSR_RI 0x40 /* Ring Indicator */
|
||||
#define UART_MSR_DSR 0x20 /* Data Set Ready */
|
||||
#define UART_MSR_CTS 0x10 /* Clear to Send */
|
||||
#define UART_MSR_DDCD 0x08 /* Delta DCD */
|
||||
#define UART_MSR_TERI 0x04 /* Trailing edge ring indicator */
|
||||
#define UART_MSR_DDSR 0x02 /* Delta DSR */
|
||||
#define UART_MSR_DCTS 0x01 /* Delta CTS */
|
||||
#define UART_MSR_ANY_DELTA 0x0F /* Any of the delta bits! */
|
||||
|
||||
/*
|
||||
* Definitions for the Extended Features Register
|
||||
* (StarTech 16C660 only, when DLAB=1)
|
||||
*/
|
||||
#define UART_EFR_CTS 0x80 /* CTS flow control */
|
||||
#define UART_EFR_RTS 0x40 /* RTS flow control */
|
||||
#define UART_EFR_SCD 0x20 /* Special character detect */
|
||||
#define UART_EFR_ENI 0x10 /* Enhanced Interrupt */
|
||||
|
||||
#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
|
||||
|
||||
#define tty_full_redraw() \
|
||||
tty_send_region(0, 0, 24, 79);
|
||||
|
||||
#define tty_partial_redraw() \
|
||||
tty_send_region(1, 34, 5, 79); \
|
||||
tty_send_region(7, 0, 8, 79);
|
||||
|
||||
#define tty_error_redraw() \
|
||||
tty_send_region(10, 0, 23, 79);
|
||||
|
||||
#define tty_normal() \
|
||||
serial_echo_print(TTY_NORMAL);
|
||||
|
||||
#define tty_inverse() \
|
||||
serial_echo_print(TTY_INVERSE);
|
||||
|
||||
#define tty_disable_cursor() \
|
||||
serial_echo_print(TTY_DISABLE_CURSOR);
|
||||
|
||||
#define tty_clear_screen() \
|
||||
serial_echo_print(TTY_CLEAR_SCREEN);
|
||||
|
||||
void tty_init(void);
|
||||
|
||||
void tty_print(int y, int x, const char *p);
|
||||
|
||||
void tty_send_region(int start_row, int start_col, int end_row, int end_col);
|
||||
|
||||
char tty_get_key(void);
|
||||
|
||||
#endif /* _SERIAL_REG_H */
|
Loading…
Reference in New Issue
Block a user