/* * Copyright (c) 2011, 2016 Jonas 'Sortie' Termansen. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * * uart.cpp * A simple serial terminal driver. */ #include #include #include #include #include #include #include "vga.h" #include "uart.h" namespace Sortix { namespace UART { const unsigned TXR = 0; // Transmit register const unsigned RXR = 0; // Receive register const unsigned IER = 1; // Interrupt Enable const unsigned IIR = 2; // Interrupt ID const unsigned FCR = 2; // FIFO control const unsigned LCR = 3; // Line control const unsigned MCR = 4; // Modem control const unsigned LSR = 5; // Line Status const unsigned MSR = 6; // Modem Status const unsigned DLL = 0; // divisor Latch Low const unsigned DLM = 1; // divisor latch High const unsigned LCR_DLAB = 0x80; // divisor latch access bit const unsigned LCR_SBC = 0x40; // Set break control const unsigned LCR_SPAR = 0x20; // Stick parity (?) const unsigned LCR_EPAR = 0x10; // Even parity select const unsigned LCR_PARITY = 0x08; // Parity Enable const unsigned LCR_STOP = 0x04; // Stop bits: 0=1 bit, 1=2 bits const unsigned LCR_WLEN5 = 0x00; // Wordlength: 5 bits const unsigned LCR_WLEN6 = 0x01; // Wordlength: 6 bits const unsigned LCR_WLEN7 = 0x02; // Wordlength: 7 bits const unsigned LCR_WLEN8 = 0x03; // Wordlength: 8 bits const unsigned LSR_TEMT = 0x40; // Transmitter empty const unsigned LSR_THRE = 0x20; // Transmit-hold-register empty const unsigned LSR_READY = 0x1; const unsigned BASE_BAUD = 1843200 / 16; const unsigned BOTH_EMPTY = LSR_TEMT | LSR_THRE; unsigned ProbeBaud(unsigned port) { uint8_t lcr = inport8(port + LCR); outport8(port + LCR, lcr | LCR_DLAB); uint8_t dll = inport8(port + DLL); uint8_t dlm = inport8(port + DLM); outport8(port + LCR, lcr); unsigned quot = dlm << 8 | dll; return BASE_BAUD / quot; } void WaitForEmptyBuffers(unsigned port) { while ( (inport8(port + LSR) & BOTH_EMPTY) != BOTH_EMPTY ); } const unsigned PORT = 0x3F8; void Init() { unsigned baud = ProbeBaud(PORT); outport8(PORT + LCR, 0x3); // 8n1 outport8(PORT + IER, 0); // No interrupt outport8(PORT + FCR, 0); // No FIFO outport8(PORT + MCR, 0x3); // DTR + RTS unsigned divisor = 115200 / baud; uint8_t c = inport8(PORT + LCR); outport8(PORT + LCR, c | LCR_DLAB); outport8(PORT + DLL, divisor >> 0 & 0xFF); outport8(PORT + DLM, divisor >> 8 & 0xFF); outport8(PORT + LCR, c & ~LCR_DLAB); } void Read(uint8_t* buffer, size_t size) { // Save the IER and disable interrupts. unsigned ier = inport8(PORT + IER); outport8(PORT + IER, 0); for ( size_t i = 0; i < size; i++ ) { while ( !(inport8(PORT + LSR) & LSR_READY) ); buffer[i] = inport8(PORT); } // Wait for transmitter to become empty and restore the IER. WaitForEmptyBuffers(PORT); outport8(PORT + IER, ier); } void Write(const void* b, size_t size) { const uint8_t* buffer = (const uint8_t*) b; // Save the IER and disable interrupts. unsigned ier = inport8(PORT + IER); outport8(PORT + IER, 0); for ( size_t i = 0; i < size; i++ ) { WaitForEmptyBuffers(PORT); outport8(PORT, buffer[i]); } // Wait for transmitter to become empty and restore the IER. WaitForEmptyBuffers(PORT); outport8(PORT + IER, ier); } void WriteChar(char c) { // Save the IER and disable interrupts. unsigned ier = inport8(PORT + IER); outport8(PORT + IER, 0); WaitForEmptyBuffers(PORT); outport8(PORT, c); // Wait for transmitter to become empty and restore the IER. WaitForEmptyBuffers(PORT); outport8(PORT + IER, ier); } void WriteString(const char* str) { Write(str, strlen(str)); } static size_t WriteCallback(void*, const char* chars, size_t size) { Write(chars, size); return size; } void WriteF(const char* format, ...) { va_list list; va_start(list, format); vcbprintf(NULL, WriteCallback, format, list); va_end(list); } int TryPopChar() { // Save the IER and disable interrupts. unsigned ier = inport8(PORT + IER); outport8(PORT + IER, 0); int result = -1; if ( inport8(PORT + LSR) & LSR_READY ) result = inport8(PORT); // Wait for transmitter to become empty and restore the IER. WaitForEmptyBuffers(PORT); outport8(PORT + IER, ier); return result; } } // namespace UART } // namespace Sortix