memtest86plus/system/serial.c

226 lines
6.0 KiB
C

// 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) {
/* 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.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];
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;
}
}