From 0e0d79811e2cfe6d703d4a20cea3d5f38cf8e923 Mon Sep 17 00:00:00 2001 From: Arnau478 Date: Sun, 7 Aug 2022 20:08:46 +0200 Subject: [PATCH] Implemented UART and created its printf-like function --- src/kernel/drivers/serial.c | 42 ----------------------------- src/kernel/drivers/serial.h | 16 ----------- src/kernel/drivers/uart.c | 41 ++++++++++++++++++++++++++++ src/kernel/drivers/uart.h | 17 ++++++++++++ src/kernel/lib/stdio.c | 54 +++++++++++++++++++++++++++---------- src/kernel/main.c | 7 +++-- src/kernel/shell.c | 24 ++++++++++++----- 7 files changed, 120 insertions(+), 81 deletions(-) delete mode 100644 src/kernel/drivers/serial.c delete mode 100644 src/kernel/drivers/serial.h create mode 100644 src/kernel/drivers/uart.c create mode 100644 src/kernel/drivers/uart.h diff --git a/src/kernel/drivers/serial.c b/src/kernel/drivers/serial.c deleted file mode 100644 index 12f3099..0000000 --- a/src/kernel/drivers/serial.c +++ /dev/null @@ -1,42 +0,0 @@ -#include "serial.h" -#include "../arch/i686/io.h" - -int serial_initialize(uint8_t port){ - i686_outb(port + 1, 0x00); // Disable all interrupts - i686_outb(port + 3, 0x80); // Enable DLAB - i686_outb(port + 0, 0x03); // Divisor high byte (38400 baud) - i686_outb(port + 1, 0x00); // Divisor low byte - i686_outb(port + 3, 0x03); // 8 bits, no parity, one stop bit - i686_outb(port + 2, 0xC7); // Enable FIFO - i686_outb(port + 4, 0x0B); // IRQs enabled, RTS/DSR set - - i686_outb(port + 4, 0x1E); // Loopback mode, test serial chip - i686_outb(port + 0, 0xAE); // Test serial chip sending 0xAE - - if(i686_inb(port + 0) != 0xAE){ // Not working - return 1; - } - - i686_outb(port + 4, 0x0F); // Normal operation mode - return 0; -} - -static int serial_received(uint8_t port){ - return i686_inb(port + 5) & 0x01; -} - -char serial_read(uint8_t port){ - while(!serial_received(port)); - - return i686_inb(port); -} - -static int serial_tx_empty(uint8_t port){ - return i686_inb(port + 5) & 0x20; -} - -void serial_write(uint8_t port, char c){ - while(!serial_tx_empty(port)); - - return i686_outb(port, c); -} diff --git a/src/kernel/drivers/serial.h b/src/kernel/drivers/serial.h deleted file mode 100644 index 9b93c0e..0000000 --- a/src/kernel/drivers/serial.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include - -#define COM1 (uint8_t)0x3F8 -#define COM2 (uint8_t)0x2F8 -#define COM3 (uint8_t)0x3E8 -#define COM4 (uint8_t)0x2E8 -#define COM5 (uint8_t)0x5F8 -#define COM6 (uint8_t)0x4F8 -#define COM7 (uint8_t)0x5E8 -#define COM8 (uint8_t)0x4E8 - -int serial_initialize(uint8_t port); -char serial_read(uint8_t port); -void serial_write(uint8_t port, char c); diff --git a/src/kernel/drivers/uart.c b/src/kernel/drivers/uart.c new file mode 100644 index 0000000..4b12969 --- /dev/null +++ b/src/kernel/drivers/uart.c @@ -0,0 +1,41 @@ +#include "uart.h" +#include "../arch/i686/io.h" + +void uart_set_baudrate(uint16_t port, uint16_t divisor){ + i686_outb(port + 3, 0x80); // Enable DLAB + i686_outb(port, (divisor >> 8) & 0x00FF); // High part + i686_outb(port, divisor & 0x00FF); // Low part +} + +void uart_initialize(uint16_t port, uint16_t baudrate_div){ + uart_set_baudrate(port, baudrate_div); + + // Configure line + i686_outb(port + 3, 0x03); // 8 bit, no parity, 1 stop bit, no break control + + // Configure buffering + i686_outb(port + 2, 0xC7); // FIFO enabled, clear both FIFO queues, 14 bytes queue(s) + + // Configure modem + i686_outb(port + 4, 0x03); // RTS=1 DTS=1 +} + +static int uart_received(uint16_t port){ + return i686_inb(port + 5) & 0x01; +} + +char uart_read(uint16_t port){ + while(!uart_received(port)); + + return i686_inb(port); +} + +static int uart_tx_empty(uint16_t port){ + return i686_inb(port + 5) & 0x20; +} + +void uart_write(uint16_t port, char c){ + while(!uart_tx_empty(port)); + + return i686_outb(port, c); +} diff --git a/src/kernel/drivers/uart.h b/src/kernel/drivers/uart.h new file mode 100644 index 0000000..e531f41 --- /dev/null +++ b/src/kernel/drivers/uart.h @@ -0,0 +1,17 @@ +#pragma once + +#include + +#define COM1 (uint16_t)0x3F8 +#define COM2 (uint16_t)0x2F8 +#define COM3 (uint16_t)0x3E8 +#define COM4 (uint16_t)0x2E8 +#define COM5 (uint16_t)0x5F8 +#define COM6 (uint16_t)0x4F8 +#define COM7 (uint16_t)0x5E8 +#define COM8 (uint16_t)0x4E8 + +void uart_set_baudrate(uint16_t port, uint16_t divisor); +void uart_initialize(uint16_t port, uint16_t baudrate_div); +char uart_read(uint16_t port); +void uart_write(uint16_t port, char c); diff --git a/src/kernel/lib/stdio.c b/src/kernel/lib/stdio.c index 50a14e5..668b28d 100644 --- a/src/kernel/lib/stdio.c +++ b/src/kernel/lib/stdio.c @@ -2,6 +2,9 @@ #include #include "stdio.h" #include "../drivers/vga.h" +#include "../drivers/uart.h" + +typedef void (*print_callable_fn_t)(char); static char g_HexChars[] = "0123456789abcdef"; @@ -16,7 +19,11 @@ void putc(char c){ vga_print_string(str); } -static void printf_unsigned(unsigned long long num, int radix){ +void serial_putc(char c){ + uart_write(COM1, c); // TODO: Add an option to decide what COM to use +} + +static void printf_unsigned(print_callable_fn_t out_fn, unsigned long long num, int radix){ char buffer[32]; int pos = 0; @@ -27,21 +34,20 @@ static void printf_unsigned(unsigned long long num, int radix){ buffer[pos++] = g_HexChars[rem]; } while(num > 0); - while(--pos >= 0) putc(buffer[pos]); // Note reverse order + while(--pos >= 0) out_fn(buffer[pos]); // Note reverse order } -static void printf_signed(unsigned long long num, int radix){ +static void printf_signed(print_callable_fn_t out_fn, unsigned long long num, int radix){ if(num < 0){ - putc('-'); - printf_unsigned(-num, radix); + out_fn('-'); + printf_unsigned(out_fn, -num, radix); } - else printf_unsigned(num, radix); + else printf_unsigned(out_fn, num, radix); } -void printf(char *fmt, ...){ +// printf() template +static void _vprintf(print_callable_fn_t out_fn, char *fmt, va_list args){ // Variadic function arguments - va_list args; - va_start(args, fmt); while(*fmt){ if(*fmt == '%'){ @@ -52,10 +58,10 @@ void printf(char *fmt, ...){ fmt++; switch(*fmt){ case '%': - putc('%'); + out_fn('%'); break; case 'c': - putc((char)va_arg(args, int)); + out_fn((char)va_arg(args, int)); break; case 's': puts(va_arg(args, char *)); @@ -89,19 +95,39 @@ void printf(char *fmt, ...){ // TODO: Implement length modifiers if(number){ if(sign){ - printf_signed(va_arg(args, int), radix); + printf_signed(out_fn, va_arg(args, int), radix); } else{ - printf_unsigned(va_arg(args, unsigned int), radix); + printf_unsigned(out_fn, va_arg(args, unsigned int), radix); } } fmt++; } - else putc(*(fmt++)); + else out_fn(*(fmt++)); } } +void vprintf(char *fmt, va_list args){ + _vprintf(putc, fmt, args); +} + +void printf(char *fmt, ...){ + va_list args; + va_start(args, fmt); + _vprintf(putc, fmt, args); +} + +void serial_vprintf(char *fmt, va_list args){ + _vprintf(putc, fmt, args); +} + +void serial_printf(char *fmt, ...){ + va_list args; + va_start(args, fmt); + _vprintf(serial_putc, fmt, args); +} + void clear_screen(){ vga_fill_screen(' ', VGA_COLOR_BG_BLACK | VGA_COLOR_FG_WHITE); vga_set_cursor(0); diff --git a/src/kernel/main.c b/src/kernel/main.c index 37044b8..c0a8a2b 100644 --- a/src/kernel/main.c +++ b/src/kernel/main.c @@ -3,7 +3,7 @@ #include "hal/hal.h" #include "drivers/timer.h" #include "drivers/keyboard.h" -#include "drivers/serial.h" +#include "drivers/uart.h" #include "fs/vfs.h" #include "arch/i686/mm/pmm.h" #include "arch/i686/mm/vmm.h" @@ -18,6 +18,9 @@ void __attribute__((cdecl)) kmain(multiboot_info_t *multiboot_info){ // Initialize chip-specific hardware hal_initialize(); + // Initialize serial + uart_initialize(COM1, 2); + // Initialize physical memory manager uint32_t mem_size = 1024 + multiboot_info->mem_lower + multiboot_info->mem_upper*64; @@ -25,7 +28,7 @@ void __attribute__((cdecl)) kmain(multiboot_info_t *multiboot_info){ for(int i = 0; i < multiboot_info->mmap_length; i += sizeof(multiboot_memory_map_t)){ multiboot_memory_map_t *memory_map = (multiboot_memory_map_t *)(multiboot_info->mmap_addr + i); - printf("Start Addr: 0x%x%x | Length: 0x%x%x | Size: 0x%x | Type: %i\n", memory_map->addr_h, memory_map->addr_l, memory_map->len_h, memory_map->len_l, memory_map->size, memory_map->type); + serial_printf("Start Addr: 0x%x%x | Length: 0x%x%x | Size: 0x%x | Type: %i\n", memory_map->addr_h, memory_map->addr_l, memory_map->len_h, memory_map->len_l, memory_map->size, memory_map->type); if(memory_map->type == 1){ i686_pmm_init_region(memory_map->addr_l, memory_map->len_l); diff --git a/src/kernel/shell.c b/src/kernel/shell.c index 0a66579..d4b39dd 100644 --- a/src/kernel/shell.c +++ b/src/kernel/shell.c @@ -2,7 +2,7 @@ #include "lib/stdio.h" #include "lib/string.h" #include "lib/memory.h" -#include "drivers/serial.h" +#include "drivers/uart.h" int shell_run(char *cmd){ int ret = 0; @@ -17,12 +17,22 @@ int shell_run(char *cmd){ else if(!strcmp(cmd, "clear")){ clear_screen(); } - else if(!strcmp(cmd, "serial")){ - serial_write(COM1, 'H'); - serial_write(COM1, 'E'); - serial_write(COM1, 'L'); - serial_write(COM1, 'L'); - serial_write(COM1, 'O'); + else if(!strcmp(cmd, "uart")){ + uart_write(COM1, '\x1b'); + uart_write(COM1, '['); + uart_write(COM1, '3'); + uart_write(COM1, '2'); + uart_write(COM1, 'm'); + uart_write(COM1, 'H'); + uart_write(COM1, 'E'); + uart_write(COM1, 'L'); + uart_write(COM1, 'L'); + uart_write(COM1, 'O'); + uart_write(COM1, '\n'); + uart_write(COM1, '\x1b'); + uart_write(COM1, '['); + uart_write(COM1, '0'); + uart_write(COM1, 'm'); } else{ printf("SHELL: Unknown command \"%s\"\n", cmd);