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:
Sam Demeulemeester 2022-04-04 18:31:54 +02:00 committed by GitHub
parent 5cc72a6bed
commit 2e048a7c61
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 656 additions and 41 deletions

View File

@ -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;

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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();
}
}
}

View File

@ -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();

View File

@ -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 \

View File

@ -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 \

View File

@ -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] != ' ') {

View File

@ -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;
}

View File

@ -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

View File

@ -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';
}

View File

@ -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;

View File

@ -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
View 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
View 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 */