mirror of
https://github.com/memtest86plus/memtest86plus.git
synced 2024-11-27 01:50:20 -06:00
9e3958714b
8/16/32-bit MMIO supported, with configuration options as kernel parameters.
240 lines
6.5 KiB
C
240 lines
6.5 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
// Copyright (C) 2004-2023 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) {
|
|
/* Send the character out. */
|
|
serial_wait_for_xmit(port);
|
|
serial_write_reg(port, UART_TX, *p++);
|
|
}
|
|
}
|
|
|
|
void tty_goto(int y, int x)
|
|
{
|
|
static char s[3];
|
|
|
|
serial_echo_print("\x1b[");
|
|
serial_echo_print(itoa(y + 1, s));
|
|
serial_echo_print(";");
|
|
serial_echo_print(itoa(x + 1, s));
|
|
serial_echo_print("H");
|
|
}
|
|
|
|
//------------------------------------------------------------------------------
|
|
// Public Functions
|
|
//------------------------------------------------------------------------------
|
|
|
|
void tty_init(void)
|
|
{
|
|
if (!enable_tty) {
|
|
return;
|
|
}
|
|
|
|
int uart_status, serial_div;
|
|
unsigned char lcr;
|
|
|
|
console_serial.enable = true;
|
|
console_serial.base_addr = tty_address;
|
|
console_serial.baudrate = tty_baud_rate;
|
|
console_serial.parity = SERIAL_DEFAULT_PARITY;
|
|
console_serial.bits = SERIAL_DEFAULT_BITS;
|
|
|
|
// UART MMIO Address is usually above TOLUD and never < 1MB
|
|
if (console_serial.base_addr > 0xFFFF) {
|
|
console_serial.is_mmio = true;
|
|
console_serial.reg_width = tty_mmio_stride;
|
|
console_serial.refclk = tty_mmio_ref_clk;
|
|
} else {
|
|
console_serial.is_mmio = false;
|
|
console_serial.reg_width = 1;
|
|
console_serial.refclk = UART_REF_CLK_IO;
|
|
}
|
|
|
|
/* 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 */
|
|
|
|
/* In case of MMIO UART, set up FIFO Reg */
|
|
if (console_serial.is_mmio) {
|
|
serial_write_reg(&console_serial, UART_FCR, 0x00);
|
|
serial_write_reg(&console_serial, UART_FCR, (0xFF) & (UART_FCR_ENA | UART_FCR_THR));
|
|
}
|
|
|
|
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];
|
|
uint8_t ch;
|
|
int pos = 0;
|
|
int cur_inverse = -1, inverse = false;
|
|
|
|
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++) {
|
|
|
|
// Always use absolute positioning instead of relying on CR-LF to avoid issues
|
|
// when a CR-LF is lost (especially with Industrial RS232/Ethernet converters).
|
|
tty_goto(row, start_col);
|
|
|
|
// Copy Shadow buffer to TTY buffer
|
|
pos = 0;
|
|
for (int col = start_col; col <= end_col; col++) {
|
|
|
|
inverse = ((shadow_buffer[row][col].attr & 0x70) >> 4 != BLUE);
|
|
|
|
if (cur_inverse != inverse) {
|
|
|
|
if (pos) {
|
|
p[pos] = '\0';
|
|
serial_echo_print(p);
|
|
pos = 0;
|
|
}
|
|
|
|
if (inverse) {
|
|
tty_inverse();
|
|
} else {
|
|
tty_normal();
|
|
}
|
|
|
|
cur_inverse = inverse;
|
|
}
|
|
|
|
/* Make sure only VT100 characters are sent. */
|
|
ch = shadow_buffer[row][col].ch;
|
|
|
|
switch (ch) {
|
|
case 32 ... 127:
|
|
break;
|
|
|
|
case 0xB3:
|
|
ch = '|';
|
|
break;
|
|
|
|
case 0xC1:
|
|
case 0xC2:
|
|
case 0xC4:
|
|
ch = '-';
|
|
break;
|
|
|
|
case 0xF8:
|
|
ch = '*';
|
|
break;
|
|
|
|
default:
|
|
ch = '?';
|
|
}
|
|
|
|
p[pos++] = ch;
|
|
}
|
|
|
|
if (pos) {
|
|
p[pos] = '\0';
|
|
serial_echo_print(p);
|
|
}
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|