Sync/kernel/serial.c

195 lines
5.4 KiB
C
Raw Normal View History

/************************
*** Team Kitty, 2019 ***
*** Sync ***
***********************/
/* This file provides an interface to the serial port.
* Most emulators allow the serial port to be saved into
* a text buffer, or a file, but this code should work on
* hardware with an actual serial port and monitor. */
#include <kernel/utils.h>
#include <stdarg.h>
#include <stdint.h>
#include <stddef.h>
#define SERIAL_DATA_PORT(base) (base)
#define SERIAL_FIFO_COMMAND_PORT(base) (base + 2)
#define SERIAL_LINE_COMMAND_PORT(base) (base + 3)
#define SERIAL_MODEM_COMMAND_PORT(base) (base + 4)
#define SERIAL_LINE_STATUS_PORT(base) (base + 5)
#define SERIAL_COM1_BASE 0x3F8
#define SERIAL_LINE_ENABLE_DLAB 0x80
/** serial_set_baud_rate:
* Sets the speed that data is sent via the serial port.
* The default speed is 115200 bits/s.
* The argument given is the divisor of the number.
* Hence, the new speed becomes (115200/divisor) bits/s.
*
* @param com The COM port to configure.
* @param divisor The new divisor for the baud rate.
*/
void serial_set_baud_rate(uint16_t com, uint16_t divisor) {
outb(SERIAL_LINE_COMMAND_PORT(com),
SERIAL_LINE_ENABLE_DLAB);
outb(SERIAL_DATA_PORT(com),
(divisor >> 8) & 0x00FF);
outb(SERIAL_DATA_PORT(com),
divisor & 0x00FF);
}
/** serial_configure_line:
* Sets a specific data byteset in the Serial hardware.
* Required for expected operation.
*
* @param com The COM port to configure.
*/
void serial_configure_line(uint16_t com) {
/* Bit: | 7 | 6 | 5 4 3 | 2 | 1 0 |
* Content: | d | b | parity| s | dl |
* Value: | 0 | 0 | 0 0 0 | 0 | 1 1 | = 0x03
*/
outb(SERIAL_LINE_COMMAND_PORT(com), 0x0B);
}
/** serial_configure_buffers:
* Enables FIFO (First In First Out) on the Serial line.
* It clears both the receive and transmit queues.
* It uses a 14 byte wide queue.
*
* @param com The COM port to configure.
*/
void serial_configure_buffers(uint16_t com) {
/* Bit: | 7 6 | 5 | 4 | 3 | 2 | 1 | 0 |
* Content: | 1v1 | bs | r | dma | clt | clr | e |
* Value: | 1 1 | 0 | 0 | 0 | 1 | 1 | 1 | = 0xC7
*/
outb(SERIAL_FIFO_COMMAND_PORT(com), 0xC7);
}
/** serial_configure_modem
* @param com The COM port to configure.
*/
void serial_configure_modem(uint16_t com) {
/* Bit: | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
* Content: | r | r | af | lb | ao2 | ao1 | rts | dtr |
* Value: | 0 | 0 | 0 | 0 | 0 | 0 | 1 | 1 | = 0x03
*/
outb(SERIAL_MODEM_COMMAND_PORT(com), 0x3);
}
/** serial_check_tqueue:
* Checks whether the Transmit FIFO Queue is empty on the given port.
*
* @param com The COM port to check.
* @return 0 If the queue is not empty.
* 1 If the queue is empty.
*/
int serial_check_tqueue(uint16_t com) {
return inb(SERIAL_LINE_STATUS_PORT(com)) & 0x20;
}
/** serial_write:
* Prints a single character to the serial port.
* Does nothing fancy.
* @param com The COM port to write to.
* @param data The character to write.
*/
void serial_write(uint16_t com, const char chr) {
//Hang until we have access to the COM port.
while(serial_check_tqueue(com) == 0);
outb(com, chr);
}
/** serial_print:
* Prints a string to the serial port.
* Does not support ANSI codes, or color.
*
* @param com The COM port to write to.
* @param data The string to write.
*/
void serial_print(uint16_t com, const char* data) {
for(size_t i = 0; i < strlen(data); i++) {
serial_write(com, data[i]);
}
}
/** serial_printf:
* A printf-style function for the serial port.
*
* @param format The base string - used to substitute the succeding values.
* @param ... The substitutions.
*/
void serial_printf(uint16_t com, const char* format, ...) {
uint32_t storage; //To hold temporary variables
char stringstore[10] = {0}; //To convert ints to strings.
va_list list;
va_start(list, format);
for(size_t i = 0; i < strlen(format); i++) {
//Check for the format specifier
if(format[i] == '%') {
//Check for the other specs
if(format[i+1] == 'd') {
storage = va_arg(list, int);
int_to_ascii(storage, stringstore);
serial_print(com, stringstore);
empty_string(stringstore);
i += 2;
} else if(format[i+1] == 'x') {
storage = va_arg(list, int);
int_to_hex(storage, stringstore);
serial_print(com, stringstore);
empty_string(stringstore);
i += 2;
} else if(format[i+1] == 's') {
serial_print(com, va_arg(list, char*));
i += 2;
} else {
serial_print(com, "ERROR: Attempting to parse unknown format string.");
return;
}
} else {
serial_write(com, format[i]);
i++;
}
}
va_end(list); //Free the list
}
void init_serial() {
// Disable interrupts
outb(SERIAL_COM1_BASE + 1, 0x00);
// Set baud rate divisor.
serial_set_baud_rate(SERIAL_COM1_BASE, 3);
// 8 bits, no parity, one stop bit.
serial_configure_line(SERIAL_COM1_BASE);
// Enable FIFO and clear buffers.
serial_configure_buffers(SERIAL_COM1_BASE);
// Enable IRQs, RTS/DSR set.
serial_configure_modem(SERIAL_COM1_BASE);
}