287 lines
5.8 KiB
C
287 lines
5.8 KiB
C
// Serial I/O driver
|
|
// Author: xamidev
|
|
// Licensed under the Unlicense. See the repo below.
|
|
// https://github.com/xamidev/blankos
|
|
|
|
#include "../kernel/io.h"
|
|
#include "serial.h"
|
|
#include "../libk/stdio.h"
|
|
|
|
int init_serial()
|
|
{
|
|
outb(PORT+1, 0x00);
|
|
outb(PORT+3, 0x80);
|
|
outb(PORT+0, 0x03);
|
|
outb(PORT+1, 0x00);
|
|
|
|
outb(PORT+3, 0x03);
|
|
outb(PORT+2, 0xC7);
|
|
outb(PORT+4, 0x0B);
|
|
outb(PORT+4, 0x1E);
|
|
outb(PORT+0, 0xAE);
|
|
|
|
if (inb(PORT+0) != 0xAE) {
|
|
return 1;
|
|
}
|
|
|
|
outb(PORT+4, 0x0F);
|
|
puts("[serial] initialized i/o on port COM1\n");
|
|
return 0;
|
|
}
|
|
|
|
int is_transmit_empty()
|
|
{
|
|
return inb(PORT+5) & 0x20;
|
|
}
|
|
|
|
void write_serial(const char a)
|
|
{
|
|
while (is_transmit_empty() == 0);
|
|
outb(PORT, a);
|
|
}
|
|
|
|
void serial_puts(const char* str)
|
|
{
|
|
unsigned int i = 0;
|
|
write_serial(str[0]); // Transmit first byte 2 times
|
|
while (*str++)
|
|
{
|
|
write_serial(str[i]);
|
|
}
|
|
}
|
|
|
|
void log(const char* str, const int errlevel)
|
|
{
|
|
switch (errlevel)
|
|
{
|
|
case 0:
|
|
serial_puts("[ERROR] ");
|
|
break;
|
|
case 1:
|
|
serial_puts("[WARNING] ");
|
|
break;
|
|
case 2:
|
|
serial_puts("[INFO] ");
|
|
break;
|
|
case 3:
|
|
serial_puts("[DEBUG] ");
|
|
break;
|
|
}
|
|
serial_puts(str);
|
|
}
|
|
|
|
const char serial_charset[] = "0123456789abcdef";
|
|
|
|
int* serial_printf_number(int* argp, int length, bool sign, int radix)
|
|
{
|
|
char buffer[32];
|
|
unsigned long long number;
|
|
int number_sign = 1;
|
|
int pos = 0;
|
|
|
|
switch(length)
|
|
{
|
|
case PRINTF_LENGTH_SHORT_SHORT:
|
|
case PRINTF_LENGTH_SHORT:
|
|
case PRINTF_LENGTH_START:
|
|
if (sign)
|
|
{
|
|
int n = *argp;
|
|
if (n < 0)
|
|
{
|
|
n = -n;
|
|
number_sign = -1;
|
|
}
|
|
number = (unsigned long long) n;
|
|
}
|
|
else {
|
|
number = *(unsigned int*) argp;
|
|
}
|
|
argp++;
|
|
break;
|
|
case PRINTF_LENGTH_LONG:
|
|
if (sign)
|
|
{
|
|
long int n = *(long int*)argp;
|
|
if (n < 0)
|
|
{
|
|
n = -n;
|
|
number_sign = -1;
|
|
}
|
|
number = (unsigned long long) n;
|
|
}
|
|
else {
|
|
number = *(unsigned long int*) argp;
|
|
}
|
|
argp += 2;
|
|
break;
|
|
case PRINTF_LENGTH_LONG_LONG:
|
|
if (sign)
|
|
{
|
|
long long int n = *(long long int*)argp;
|
|
if (n < 0)
|
|
{
|
|
n = -n;
|
|
number_sign = -1;
|
|
}
|
|
number = (unsigned long long) n;
|
|
}
|
|
else {
|
|
number = *(unsigned long long int*) argp;
|
|
}
|
|
argp += 4;
|
|
break;
|
|
}
|
|
|
|
do {
|
|
uint32_t rem;
|
|
x86_div64_32(number, radix, &number, &rem);
|
|
buffer[pos++] = serial_charset[rem];
|
|
} while (number > 0);
|
|
|
|
if (sign && number_sign < 0)
|
|
{
|
|
buffer[pos++] = '-';
|
|
}
|
|
|
|
while (--pos >= 0)
|
|
{
|
|
write_serial(buffer[pos]);
|
|
}
|
|
|
|
return argp;
|
|
}
|
|
void serial_printf(int errlevel, const char* fmt, ...)
|
|
{
|
|
switch (errlevel)
|
|
{
|
|
case 0:
|
|
serial_puts("[ERROR] ");
|
|
break;
|
|
case 1:
|
|
serial_puts("[WARNING] ");
|
|
break;
|
|
case 2:
|
|
serial_puts("[INFO] ");
|
|
break;
|
|
case 3:
|
|
serial_puts("[DEBUG] ");
|
|
break;
|
|
}
|
|
|
|
int* argp = (int*) &fmt;
|
|
int state = PRINTF_STATE_START;
|
|
int length = PRINTF_LENGTH_START;
|
|
int radix = 10;
|
|
bool sign = false;
|
|
|
|
argp++;
|
|
while (*fmt)
|
|
{
|
|
switch(state) {
|
|
case PRINTF_STATE_START:
|
|
if (*fmt == '%')
|
|
{
|
|
state = PRINTF_STATE_LENGTH;
|
|
}
|
|
else {
|
|
write_serial(*fmt);
|
|
}
|
|
break;
|
|
case PRINTF_STATE_LENGTH:
|
|
if (*fmt == 'h')
|
|
{
|
|
length = PRINTF_LENGTH_SHORT;
|
|
state = PRINTF_STATE_SHORT;
|
|
}
|
|
else if (*fmt == 'l')
|
|
{
|
|
length = PRINTF_LENGTH_LONG;
|
|
state = PRINTF_STATE_LONG;
|
|
}
|
|
else {
|
|
goto PRINTF_STATE_SPEC_;
|
|
}
|
|
break;
|
|
case PRINTF_STATE_SHORT:
|
|
if (*fmt == 'h')
|
|
{
|
|
length = PRINTF_LENGTH_SHORT_SHORT;
|
|
state = PRINTF_STATE_SPEC;
|
|
}
|
|
else {
|
|
goto PRINTF_STATE_SPEC_;
|
|
}
|
|
break;
|
|
case PRINTF_STATE_LONG:
|
|
if (*fmt == 'l')
|
|
{
|
|
length = PRINTF_LENGTH_LONG_LONG;
|
|
state = PRINTF_STATE_SPEC;
|
|
}
|
|
else {
|
|
goto PRINTF_STATE_SPEC_;
|
|
}
|
|
break;
|
|
case PRINTF_STATE_SPEC:
|
|
PRINTF_STATE_SPEC_:
|
|
switch(*fmt)
|
|
{
|
|
case 'c':
|
|
serial_puts(*(const char **)argp);
|
|
argp++;
|
|
break;
|
|
case 's':
|
|
serial_puts(*(const char **)argp);
|
|
argp++;
|
|
break;
|
|
case '%':
|
|
putc('%');
|
|
break;
|
|
case 'd':
|
|
case 'i':
|
|
radix = 10;
|
|
sign = true;
|
|
argp = serial_printf_number(argp, length, sign, radix);
|
|
break;
|
|
case 'u':
|
|
radix = 10;
|
|
sign = false;
|
|
argp = serial_printf_number(argp, length, sign, radix);
|
|
break;
|
|
case 'X':
|
|
case 'x':
|
|
case 'p':
|
|
radix = 16;
|
|
sign = false;
|
|
argp = serial_printf_number(argp, length, sign, radix);
|
|
break;
|
|
case 'o':
|
|
radix = 8;
|
|
sign = false;
|
|
argp = serial_printf_number(argp, length, sign, radix);
|
|
break;
|
|
case 'f': {
|
|
// Handle floating-point numbers
|
|
double* dargp = (double*)argp;
|
|
double d = *(double*)dargp;
|
|
char buffer[64];
|
|
dtostrf(d, buffer, 6); // Default precision: 6
|
|
serial_puts(buffer);
|
|
argp += 2; // Incrementing by 2 to move past the double argument
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
state = PRINTF_STATE_START;
|
|
length = PRINTF_LENGTH_START;
|
|
radix = 10;
|
|
sign = false;
|
|
break;
|
|
}
|
|
fmt++;
|
|
}
|
|
serial_puts("\r\n");
|
|
}
|