Implement Serial Console messaging.
Intended to be used for debugging, but it's useful.
This commit is contained in:
parent
245d09b056
commit
e4fcbb20b4
19
include/kernel/serial.h
Executable file
19
include/kernel/serial.h
Executable file
|
@ -0,0 +1,19 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
extern void init_serial();
|
||||||
|
|
||||||
|
extern void serial_set_baud_rate(uint16_t, uint16_t);
|
||||||
|
|
||||||
|
extern void serial_configure_line(uint16_t);
|
||||||
|
|
||||||
|
extern void serial_configure_buffers(uint16_t);
|
||||||
|
|
||||||
|
extern void serial_write(uint16_t, const char);
|
||||||
|
|
||||||
|
extern void serial_configure_modem(uint16_t);
|
||||||
|
|
||||||
|
extern int serial_check_tqueue(uint16_t);
|
||||||
|
|
||||||
|
extern void serial_print(uint16_t, const char*);
|
||||||
|
|
||||||
|
extern void serial_printf(const char*, ...);
|
|
@ -25,8 +25,5 @@ void puts(const char*);
|
||||||
void handleControlSequence(struct csi_sequence);
|
void handleControlSequence(struct csi_sequence);
|
||||||
void set_cursor(int, int);
|
void set_cursor(int, int);
|
||||||
void term_scroll(bool);
|
void term_scroll(bool);
|
||||||
void int_to_ascii(int, char*);
|
|
||||||
void int_to_hex(int, char*);
|
|
||||||
void empty_string(char*);
|
|
||||||
|
|
||||||
bool isDigit(char c);
|
bool isDigit(char c);
|
||||||
|
|
|
@ -12,4 +12,10 @@ void outb(unsigned short, unsigned char);
|
||||||
|
|
||||||
void memcpy(void*, void*, size_t);
|
void memcpy(void*, void*, size_t);
|
||||||
|
|
||||||
void memset(void*, int, size_t);
|
void memset(void*, int, size_t);
|
||||||
|
|
||||||
|
void int_to_ascii(int, char*);
|
||||||
|
|
||||||
|
void int_to_hex(int, char*);
|
||||||
|
|
||||||
|
void empty_string(char*);
|
||||||
|
|
|
@ -30,13 +30,13 @@ void gdt_set_gate(int num, unsigned long base,
|
||||||
gdt[num].low_limit = (limit & 0xFFFF);
|
gdt[num].low_limit = (limit & 0xFFFF);
|
||||||
gdt[num].granular = ((limit >> 16) & 0x0F);
|
gdt[num].granular = ((limit >> 16) & 0x0F);
|
||||||
|
|
||||||
gdt[num].granular != (gran & 0xF0);
|
gdt[num].granular = (gran & 0xF0);
|
||||||
gdt[num].access = access;
|
gdt[num].access = access;
|
||||||
}
|
}
|
||||||
|
|
||||||
void gdt_install() {
|
void gdt_install() {
|
||||||
gp.limit = (sizeof(struct gdt_item) * 3) - 1;
|
gp.limit = (sizeof(struct gdt_item) * 3) - 1;
|
||||||
gp.base = &gdt;
|
gp.base = (unsigned int)&gdt;
|
||||||
|
|
||||||
gdt_set_gate(0, 0, 0, 0, 0); //NULL item
|
gdt_set_gate(0, 0, 0, 0, 0); //NULL item
|
||||||
|
|
||||||
|
|
|
@ -2,15 +2,20 @@
|
||||||
//#include <stdio.h>
|
//#include <stdio.h>
|
||||||
|
|
||||||
#include <kernel/tty.h>
|
#include <kernel/tty.h>
|
||||||
#include <kernel/gdt.h>
|
#include <kernel/gdt.h>
|
||||||
|
#include <kernel/serial.h>
|
||||||
|
|
||||||
int kernel_main(void) {
|
int kernel_main(void) {
|
||||||
//Prepare the GDT
|
// Prepare the screen, and blank it out.
|
||||||
gdt_install();
|
screen_initialize();
|
||||||
//Prepare interrupts
|
|
||||||
|
// Prepare the serial console.
|
||||||
|
init_serial();
|
||||||
|
serial_print(0x3F8, "[INFO] Serial ready.");
|
||||||
|
// Prepare the GDT
|
||||||
|
//gdt_install();
|
||||||
|
// Prepare interrupts
|
||||||
//idt_install();
|
//idt_install();
|
||||||
//Prepare the screen, and blank it out.
|
|
||||||
screen_initialize();
|
|
||||||
|
|
||||||
//Print a copyright message.
|
//Print a copyright message.
|
||||||
term_writes("(c)");
|
term_writes("(c)");
|
||||||
|
|
184
kernel/serial.c
Executable file
184
kernel/serial.c
Executable file
|
@ -0,0 +1,184 @@
|
||||||
|
#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);
|
||||||
|
}
|
|
@ -1,4 +1,7 @@
|
||||||
|
#include <kernel/utils.h>
|
||||||
|
#include <stddef.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <stdbool.h>
|
||||||
#include <stddef.h>
|
#include <stddef.h>
|
||||||
|
|
||||||
size_t strlen(const char* string) {
|
size_t strlen(const char* string) {
|
||||||
|
@ -33,3 +36,69 @@ void memset(void* src, int chr, size_t n) {
|
||||||
*ptr++ = (unsigned char) chr;
|
*ptr++ = (unsigned char) chr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void int_to_ascii(int num, char* string) {
|
||||||
|
size_t i = 0; //Counter.
|
||||||
|
//TODO: Convert this to a for-loop?
|
||||||
|
int32_t calc = 0;
|
||||||
|
bool negative = false;
|
||||||
|
|
||||||
|
if(num == 0) {
|
||||||
|
string[0] = '0';
|
||||||
|
string[1] = '\0';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(num < 0) {
|
||||||
|
negative = true;
|
||||||
|
num = -num;
|
||||||
|
}
|
||||||
|
|
||||||
|
while(num != 0) {
|
||||||
|
calc = num % 10;
|
||||||
|
num = num / 10;
|
||||||
|
calc += 48;
|
||||||
|
|
||||||
|
string[i] = calc;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(negative) {
|
||||||
|
string[i] = '-';
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(size_t j = 0; j < i/2; j++) {
|
||||||
|
calc = string[j];
|
||||||
|
string[j] = string[i-j-1];
|
||||||
|
string[j-i-1] = calc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void int_to_hex(int num, char* string) {
|
||||||
|
empty_string(string);
|
||||||
|
|
||||||
|
uint8_t i = 8;
|
||||||
|
uint8_t temp = 0;
|
||||||
|
|
||||||
|
while(i != 0) {
|
||||||
|
i--;
|
||||||
|
temp = num & 0xF;
|
||||||
|
num = num >> 4;
|
||||||
|
|
||||||
|
if(temp >= 10) {
|
||||||
|
temp += 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
temp += 48;
|
||||||
|
string[i] = temp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void empty_string(char* string) {
|
||||||
|
size_t len = strlen(string);
|
||||||
|
|
||||||
|
for(size_t i = 0; i < len + 1; i++) {
|
||||||
|
string[i] = '\0';
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user