diff options
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/ata.c | 253 | ||||
-rw-r--r-- | drivers/ata.h | 16 | ||||
-rw-r--r-- | drivers/keyboard.c | 188 | ||||
-rw-r--r-- | drivers/keyboard.h | 11 | ||||
-rw-r--r-- | drivers/mouse.c | 144 | ||||
-rw-r--r-- | drivers/mouse.h | 5 | ||||
-rw-r--r-- | drivers/pit.c | 55 | ||||
-rw-r--r-- | drivers/pit.h | 12 | ||||
-rw-r--r-- | drivers/pst.c | 17 | ||||
-rw-r--r-- | drivers/pst.h | 7 | ||||
-rw-r--r-- | drivers/serial.c | 35 | ||||
-rw-r--r-- | drivers/serial.h | 2 | ||||
-rw-r--r-- | drivers/vbe.c | 50 | ||||
-rw-r--r-- | drivers/vbe.h | 7 |
14 files changed, 802 insertions, 0 deletions
diff --git a/drivers/ata.c b/drivers/ata.c new file mode 100644 index 0000000..fd9b504 --- /dev/null +++ b/drivers/ata.c @@ -0,0 +1,253 @@ +#include <assert.h> +#include <cpu/io.h> +#include <drivers/ata.h> +#include <drivers/pit.h> + +#define PRIMARY_BUS_BASEPORT 0x1F0 +#define SECONDAY_BUS_BASEPORT 0x170 + +#define PRIMARY_BUS_IRQ 14 +#define SECONDAY_BUS_IRQ 15 + +#define STATUS_PORT 7 +#define COMMAND_PORT 7 +#define DRIVE_SELECT 6 +#define LBAhi 5 +#define LBAmid 4 +#define LBAlo 3 +#define SECTOR_COUNT 2 +#define DATA_PORT 0 + +#define IDENTIFY 0xEC +#define READ_SECTORS 0x20 +#define WRITE_SECTORS 0x30 +#define CACHE_FLUSH 0xE7 + +#define STATUS_BSY ((1 << 7)) +#define STATUS_DF ((1 << 5)) +#define STATUS_DRQ ((1 << 3)) +#define STATUS_ERR ((1 << 0)) + +uint32_t io_base; + +unsigned char read_buffer[SECTOR_SIZE]; + +void select_drive(uint8_t master_slave) { + outb(io_base + DRIVE_SELECT, (master_slave) ? 0xA0 : 0xB0); +} + +int identify(int master_slave) { + select_drive(master_slave); + outb(io_base + SECTOR_COUNT, 0); + outb(io_base + LBAlo, 0); + outb(io_base + LBAmid, 0); + outb(io_base + LBAhi, 0); + outb(io_base + COMMAND_PORT, IDENTIFY); + if (0 == inb(io_base + STATUS_PORT)) + return 0; // Drive does not exist + + for (; 0 != (inb(io_base + STATUS_PORT) & STATUS_BSY);) + ; + + // Because of some ATAPI drives that do not + // follow spec, at this point we need to check + // the LBAmid and LBAhi ports to see if they are + // non-zero. If so, the drive is not ATA, and we + // should stop polling. + if (0 != inb(io_base + LBAmid) || 0 != inb(io_base + LBAhi)) { + klog("Drive is not ATA.", LOG_ERROR); + return -1; + } + + for (uint16_t status;;) { + status = inb(io_base + STATUS_PORT); + + if (1 == (status & STATUS_ERR)) { + klog("Drive ERR set.", LOG_ERROR); + return -2; + } + + if ((status & STATUS_DRQ)) + break; + } + + // The data is ready to read from the Data + // port (0x1F0). Read 256 16-bit values, + // and store them. + // TODO: This returns some intreasting information. + // https://wiki.osdev.org/ATA_PIO_Mode#Interesting_information_returned_by_IDENTIFY + uint16_t array[256]; + rep_insw(1 * SECTOR_SIZE / 16, io_base + DATA_PORT, array); + return 1; +} + +int poll_status(void) { + for (uint16_t status;;) { + // Read the Regular Status port until... + // We read this 15 times to give some + // time for the drive to catch up. + for (int n = 0; n < 15; n++) + status = inb(io_base + STATUS_PORT); + + // ERR or + // DF sets + if ((status & STATUS_ERR) || (status & STATUS_DF)) { + klog("Drive error set.", LOG_ERROR); + return 0; + } + + // BSY clears + // DRQ sets + if (0 == (status & STATUS_BSY) && 0 != (status & STATUS_DRQ)) + break; + } + return 1; +} + +// Instructions from: https://wiki.osdev.org/ATA_PIO_Mode#28_bit_PIO +void __attribute__((optimize("O0"))) +setup_drive_for_command(uint32_t lba, uint32_t sector_count) { + // 1. Send 0xE0 for the "master" or 0xF0 for + // the "slave", ORed with the highest 4 bits + // of the LBA to port 0x1F6 + outb(io_base + DRIVE_SELECT, 0xE0 | (0 << 4) | ((lba >> 24) & 0x0F)); + + // 2. Send a NULL byte to port 0x1F1, if you + // like (it is ignored and wastes + // lots of CPU time) + + // NOP + + // 3. Send the sectorcount to port 0x1F2 + outb(io_base + SECTOR_COUNT, sector_count); + + // 4. Send the low 8 bits of the LBA to port 0x1F3 + outb(io_base + LBAlo, (lba >> 0) & 0xFF); + + // 5. Send the next 8 bits of the LBA to port 0x1F4 + outb(io_base + LBAmid, (lba >> 8) & 0xFF); + + // 6. Send the next 8 bits of the LBA to port 0x1F5 + outb(io_base + LBAhi, (lba >> 16) & 0xFF); +} + +void delayed_rep_outsw(size_t n, uint16_t port, volatile uint8_t *buffer) { + for (volatile size_t i = 0; i < n; i++) { + outsw(port, (uint32_t)buffer); + buffer += 2; + // outsw(port, buffer); + } +} + +void __attribute__((optimize("O0"))) +ata_write_lba28(uint32_t lba, uint32_t sector_count, + volatile const uint8_t *buffer) { + setup_drive_for_command(lba, sector_count); + + outb(io_base + COMMAND_PORT, WRITE_SECTORS); + + for (volatile uint32_t i = 0; i < sector_count; i++) { + if (!poll_status()) { + // FIXME: Fail properly + for (;;) + ; + } + + delayed_rep_outsw(256, io_base + DATA_PORT, + (void *)((uint32_t)buffer + i * 256)); + } + + // Cache flush + outb(io_base + COMMAND_PORT, CACHE_FLUSH); + + // Wait for BSY to clear + for (;;) { + uint16_t status = inb(io_base + STATUS_PORT); + if (!(status & STATUS_BSY)) + break; + } +} + +// Instructions from: https://wiki.osdev.org/ATA_PIO_Mode#28_bit_PIO +void __attribute__((optimize("O0"))) +ata_read_lba28(uint32_t lba, uint32_t sector_count, volatile void *address) { + // Step 1-6 is done in this function. + setup_drive_for_command(lba, sector_count); + + // 7. Send the "READ SECTORS" command to port 0x17F + outb(io_base + COMMAND_PORT, READ_SECTORS); + + // 8. Wait for an IRQ or poll. + + // This step can be found in the for loop + + // 9. Transfer 256 16-bit values, a uint16_t at a time, + // into your buffer from I/O port 0x1F0 + for (volatile uint32_t i = 0; i < sector_count; i++) { + // 10. Then loop back to waiting for the next IRQ + // or poll again for each successive sector. + // 8. Wait for an IRQ or poll. + if (!poll_status()) { + // FIXME: Fail properly + for (;;) + ; + } + rep_insw(256, io_base + DATA_PORT, (void *)((uint32_t)address + i * 256)); + } +} + +void ata_init(void) { + io_base = PRIMARY_BUS_BASEPORT; + + // Before sending any data to the IO ports, + // read the Regular Status byte. The value + // 0xFF is an illegal status value, and + // indicates that the bus has no drives + if (0xFF == inb(io_base + STATUS_PORT)) { + klog("Bus has no drives", LOG_ERROR); + } + + // Issue IDENTIFY command + select_drive(1); +} + +void __attribute__((optimize("O0"))) +read_lba(uint32_t lba, void *address, size_t size, size_t offset) { + uintptr_t ptr = (uintptr_t)address; + lba += offset / SECTOR_SIZE; + offset = offset % SECTOR_SIZE; + asm("cli"); + size_t total_read = 0; + for (int i = 0; size > 0; i++) { + uint32_t read_len = + (SECTOR_SIZE < (size + offset)) ? (SECTOR_SIZE - offset) : size; + ata_read_lba28(lba + i, 1, read_buffer); + memcpy((void *)ptr, read_buffer + offset, read_len); + size -= read_len; + total_read += read_len; + ptr += read_len; + offset = 0; + } +} + +void write_lba(uint32_t lba, volatile void *address, size_t size, + size_t offset) { + uintptr_t ptr = (uintptr_t)address; + lba += offset / SECTOR_SIZE; + offset = offset % SECTOR_SIZE; + size_t total_write = 0; + uint8_t sector_buffer[512]; + for (int i = 0; size > 0; i++) { + ata_read_lba28(lba + i, 1, sector_buffer); + uint32_t write_len = + (SECTOR_SIZE < (size + offset)) ? (SECTOR_SIZE - offset) : size; + + memcpy(sector_buffer + offset, (void *)ptr, write_len); + ata_write_lba28(lba + i, 1, sector_buffer); + + size -= write_len; + total_write += write_len; + ptr += write_len; + offset = 0; + } +} diff --git a/drivers/ata.h b/drivers/ata.h new file mode 100644 index 0000000..3e88d7f --- /dev/null +++ b/drivers/ata.h @@ -0,0 +1,16 @@ +#include "../cpu/idt.h" +#include <stddef.h> + +#define SECTOR_SIZE 512 + +void ata_init(void); + +void read_drive_chs(uint8_t head_index, uint8_t sector_count, + uint8_t sector_index, uint8_t cylinder_low_value, + uint8_t cylinder_high_value, void *address); +void read_drive_lba(uint32_t lba, uint8_t sector_count, void *address); +void read_lba(uint32_t lba, void *address, size_t size, size_t offset); +void write_lba(uint32_t lba, volatile void *address, size_t size, + size_t offset); +void ata_write_lba28(uint32_t lba, uint32_t sector_count, + volatile const uint8_t *buffer); diff --git a/drivers/keyboard.c b/drivers/keyboard.c new file mode 100644 index 0000000..8c96606 --- /dev/null +++ b/drivers/keyboard.c @@ -0,0 +1,188 @@ +#include <assert.h> +#include <drivers/keyboard.h> +#include <errno.h> +#include <fs/devfs.h> +#include <fs/fifo.h> +#include <fs/vfs.h> +#include <sched/scheduler.h> +#include <stdint.h> + +#define PS2_REG_DATA 0x60 +#define PS2_REG_STATUS 0x64 +#define PS2_REG_COMMAND 0x64 + +#define PS2_CMD_ENABLE_FIRST_PORT 0xAE // no rsp + +#define PS2_CMD_TEST_CONTROLLER 0xAA // has rsp + +#define PS2_RSP_TEST_PASSED 0x55 +#define PS2_RSP_TEST_FAILED 0xFC + +#define PS2_CMD_SET_SCANCODE 0xF0 // has rsp +#define PS2_KB_ACK 0xFA +#define PS2_KB_RESEND 0xFE + +#define PS2_CMD_SET_MAKE_RELEASE 0xF8 // has rsp + +uint8_t kb_scancodes[3] = {0x43, 0x41, 0x3f}; + +FIFO_FILE *keyboard_fifo; + +uint8_t ascii_table[] = { + 'e', '\x1B', '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', 8, + '\t', + + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', + // 0, // [ + // 0, // ] + // 0, + // 0, // ? + '[', ']', + '\n', // ENTER + 'C', + + 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', + ';', // ; + '\'', // ; + '`', // ; + 'D', // LEFT SHIFT + '\\', // ; + 'z', 'x', 'c', 'v', 'b', 'n', 'm', + ',', // ; + '.', // ; + '/', // ; + 'U', // ; + 'U', // ; + 'U', // ; + ' ', // ; +}; + +uint8_t capital_ascii_table[] = { + 'e', '\x1B', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', 8, + '\t', + + 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', + // 0, // [ + // 0, // ] + // 0, + // 0, // ? + '{', '}', + '\n', // ENTER + 'C', + + 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', + ':', // ; + '\"', // ; + '~', // ; + 'D', // LEFT SHIFT + '\\', // ; + 'Z', 'X', 'C', 'V', 'B', 'N', 'M', + '<', // ; + '>', // ; + '?', // ; + 'U', // ; + 'U', // ; + 'U', // ; + ' ', // ; +}; + +vfs_inode_t *kb_inode; + +uint8_t keyboard_to_ascii(uint16_t key, uint8_t capital) { + if ((key & 0xFF) > sizeof(ascii_table)) + return 'U'; + if (capital) + return capital_ascii_table[key & 0xFF]; + else + return ascii_table[key & 0xFF]; +} + +uint8_t is_shift_down = 0; +uint8_t is_alt_down = 0; + +struct KEY_EVENT { + char c; + uint8_t mode; // (shift (0 bit)) (alt (1 bit)) + uint8_t release; // 0 pressed, 1 released +}; + +extern process_t *ready_queue; +__attribute__((interrupt)) void +int_keyboard(__attribute__((unused)) struct interrupt_frame *frame) { + outb(0x20, 0x20); + uint16_t c; + c = inb(PS2_REG_DATA); + int released = 0; + if (c & 0x80) { + switch ((c & ~(0x80)) & 0xFF) { + case 0x2A: // Left shift + case 0x36: // Right shift + is_shift_down = 0; + return; + case 0x38: + is_alt_down = 0; + return; + } + released = 1; + } else { + switch (c & 0xFF) { + case 0x2A: // Left shift + case 0x36: // Right shift + is_shift_down = 1; + return; + case 0x38: + is_alt_down = 1; + return; + } + released = 0; + } + unsigned char a = keyboard_to_ascii((c & ~(0x80)) & 0xFF, is_shift_down); + struct KEY_EVENT ev; + ev.c = a; + ev.release = released; + ev.mode = 0; + ev.mode |= is_shift_down << 0; + ev.mode |= is_alt_down << 1; + fifo_object_write((uint8_t *)&ev, 0, sizeof(ev), keyboard_fifo); + kb_inode->has_data = keyboard_fifo->has_data; +} + +#define PS2_WAIT_RECV \ + { \ + for (;;) { \ + uint8_t status = inb(PS2_REG_STATUS); \ + if (status & 0x1) \ + break; \ + } \ + } + +#define PS2_WAIT_SEND \ + { \ + for (;;) { \ + uint8_t status = inb(PS2_REG_STATUS); \ + if (!(status & (0x1 << 1))) \ + break; \ + } \ + } + +void install_keyboard(void) { + keyboard_fifo = create_fifo_object(); + install_handler(int_keyboard, INT_32_INTERRUPT_GATE(0x3), 0x21); +} + +int keyboard_read(uint8_t *buffer, uint64_t offset, uint64_t len, + vfs_fd_t *fd) { + (void)offset; + + if (0 == fd->inode->has_data) { + return -EAGAIN; + } + int rc = fifo_object_read(buffer, 0, len, keyboard_fifo); + fd->inode->has_data = keyboard_fifo->has_data; + return rc; +} + +void add_keyboard(void) { + kb_inode = devfs_add_file("/keyboard", keyboard_read, NULL, NULL, 0, 0, + FS_TYPE_CHAR_DEVICE); +} diff --git a/drivers/keyboard.h b/drivers/keyboard.h new file mode 100644 index 0000000..4b76f22 --- /dev/null +++ b/drivers/keyboard.h @@ -0,0 +1,11 @@ +#ifndef KEYBOARD_H +#define KEYBOARD_H + +#include <stdint.h> +#include <cpu/io.h> +#include <cpu/idt.h> + +void install_keyboard(void); +void add_keyboard(void); + +#endif diff --git a/drivers/mouse.c b/drivers/mouse.c new file mode 100644 index 0000000..23619a0 --- /dev/null +++ b/drivers/mouse.c @@ -0,0 +1,144 @@ +#include <cpu/idt.h> +#include <drivers/mouse.h> +#include <fs/devfs.h> +#include <fs/fifo.h> +#include <fs/vfs.h> +#include <stdint.h> + +uint8_t mouse_cycle = 0; // unsigned char +uint8_t mouse_uint8_t[3]; // signed char +uint8_t mouse_x = 0; // signed char +uint8_t mouse_y = 0; // signed char +vfs_inode_t *mouse_inode; +vfs_fd_t *mouse_fd; + +struct mouse_event { + uint8_t buttons; + uint8_t x; + uint8_t y; +}; + +int fs_mouse_write(uint8_t *buffer, uint64_t offset, uint64_t len, + vfs_fd_t *fd) { + int rc = fifo_object_write(buffer, offset, len, fd->inode->internal_object); + FIFO_FILE *f = fd->inode->internal_object; + mouse_inode->has_data = f->has_data; + return rc; +} + +int fs_mouse_read(uint8_t *buffer, uint64_t offset, uint64_t len, + vfs_fd_t *fd) { + FIFO_FILE *f = fd->inode->internal_object; + if (!mouse_inode->has_data) + return 0; + int rc = fifo_object_read(buffer, offset, len, f); + mouse_inode->has_data = f->has_data; + return rc; +} + +void add_mouse(void) { + mouse_inode = devfs_add_file("/mouse", fs_mouse_read, fs_mouse_write, NULL, 0, + 0, FS_TYPE_CHAR_DEVICE); + mouse_inode->internal_object = create_fifo_object(); + // Don't look at this + int fd = vfs_open("/dev/mouse", O_RDWR, 0); + mouse_fd = get_vfs_fd(fd); + get_current_task()->file_descriptors[fd] = NULL; +} + +__attribute__((interrupt)) void what(registers_t *r) { EOI(0xe); } + +__attribute__((interrupt)) void int_mouse(registers_t *r) { + (void)r; + EOI(12); + switch (mouse_cycle) { + case 0: + mouse_uint8_t[0] = inb(0x60); + if(!(mouse_uint8_t[0] & (1 << 3))) { + mouse_cycle = 0; + return; + } + mouse_cycle++; + break; + case 1: + mouse_uint8_t[1] = inb(0x60); + mouse_cycle++; + break; + case 2: + mouse_uint8_t[2] = inb(0x60); + mouse_x = mouse_uint8_t[1]; + mouse_y = mouse_uint8_t[2]; + mouse_cycle = 0; + struct mouse_event e; + e.buttons = mouse_uint8_t[0]; + e.x = mouse_x; + e.y = mouse_y; + raw_vfs_pwrite(mouse_fd, &e, sizeof(e), 0); + break; + } +} + +void mouse_wait(uint8_t a_type) { + uint32_t _time_out = 100000; + if (a_type == 0) { + while (_time_out--) { + if ((inb(0x64) & 1) == 1) { + return; + } + } + return; + } else { + while (_time_out--) { + if ((inb(0x64) & 2) == 0) { + return; + } + } + return; + } +} + +void mouse_write(uint8_t a_write) { + // Wait to be able to send a command + mouse_wait(1); + // Tell the mouse we are sending a command + outb(0x64, 0xD4); + // Wait for the final part + mouse_wait(1); + // Finally write + outb(0x60, a_write); +} + +uint8_t mouse_read() { + // Get's response from mouse + mouse_wait(0); + return inb(0x60); +} + +void install_mouse(void) { + uint8_t _status; // unsigned char + asm("cli"); + // Enable the auxiliary mouse device + mouse_wait(1); + outb(0x64, 0xA8); + + // Enable the interrupts + mouse_wait(1); + outb(0x64, 0x20); + mouse_wait(0); + _status = (inb(0x60) | 2); + mouse_wait(1); + outb(0x64, 0x60); + mouse_wait(1); + outb(0x60, _status); + + // Tell the mouse to use default settings + mouse_write(0xF6); + mouse_read(); // Acknowledge + + // Enable the mouse + mouse_write(0xF4); + mouse_read(); // Acknowledge + + install_handler(int_mouse, INT_32_INTERRUPT_GATE(0x3), 12 + 0x20); + install_handler(what, INT_32_INTERRUPT_GATE(0x3), 0xe + 0x20); +} diff --git a/drivers/mouse.h b/drivers/mouse.h new file mode 100644 index 0000000..5248143 --- /dev/null +++ b/drivers/mouse.h @@ -0,0 +1,5 @@ +#ifndef MOUSE_H +#define MOUSE_H +void install_mouse(void); +void add_mouse(void); +#endif diff --git a/drivers/pit.c b/drivers/pit.c new file mode 100644 index 0000000..711883c --- /dev/null +++ b/drivers/pit.c @@ -0,0 +1,55 @@ +#include "pit.h" + +#define PIT_IO_CHANNEL_0 0x40 +#define PIT_IO_MODE_COMMAND 0x43 + +uint64_t clock_num_ms_ticks = 0; +uint64_t pit_counter = 0; +uint16_t hertz; + +uint64_t pit_num_ms(void) { return clock_num_ms_ticks; } + +uint16_t read_pit_count(void) { + uint16_t count = 0; + + outb(PIT_IO_MODE_COMMAND, 0x0 /*0b00000000*/); + + count = inb(PIT_IO_CHANNEL_0); + count |= inb(PIT_IO_CHANNEL_0) << 8; + + return count; +} + +void set_pit_count(uint16_t hertz) { + uint16_t divisor = 1193180 / hertz; + + /* + * 0b00110110 + * ^^ + * channel - 0 + * ^^ + * r/w mode - LSB then MSB + * ^^^ + * mode - 3 Square Wave Mode + * ^ + * BCD - no + */ + outb(PIT_IO_MODE_COMMAND, 0x36 /*0b00110110*/); + outb(PIT_IO_CHANNEL_0, divisor & 0xFF); + outb(PIT_IO_CHANNEL_0, (divisor & 0xFF00) >> 8); +} + +__attribute__((interrupt)) void +int_clock(__attribute__((unused)) struct interrupt_frame *frame) { + outb(0x20, 0x20); + pit_counter++; + if (pit_counter >= hertz / 1000) { + pit_counter = 0; + clock_num_ms_ticks++; + } + switch_task(); +} + +void pit_install(void) { + install_handler(int_clock, INT_32_INTERRUPT_GATE(0x0), 0x20); +} diff --git a/drivers/pit.h b/drivers/pit.h new file mode 100644 index 0000000..8d9ce98 --- /dev/null +++ b/drivers/pit.h @@ -0,0 +1,12 @@ +#ifndef PIT_H +#define PIT_H +#include <cpu/idt.h> +#include <cpu/io.h> +#include <sched/scheduler.h> +#include <stdint.h> +#include <stdio.h> + +void pit_install(void); +void set_pit_count(uint16_t hertz); +uint64_t pit_num_ms(void); +#endif diff --git a/drivers/pst.c b/drivers/pst.c new file mode 100644 index 0000000..d063ec9 --- /dev/null +++ b/drivers/pst.c @@ -0,0 +1,17 @@ +#include <drivers/pst.h> +#include <fs/tmpfs.h> + +int openpty(int *amaster, int *aslave, char *name, + /*const struct termios*/ void *termp, + /*const struct winsize*/ void *winp) { + (void)name; + (void)termp; + (void) winp; + int fd[2]; + pipe(fd); // This depends upon that pipe will support read and write + // through the same fd. In reality this should not be the + // case. + *amaster = fd[0]; + *aslave = fd[1]; + return 0; +} diff --git a/drivers/pst.h b/drivers/pst.h new file mode 100644 index 0000000..e8fdfaa --- /dev/null +++ b/drivers/pst.h @@ -0,0 +1,7 @@ +#ifndef PST_H +#define PST_H +#include "../fs/vfs.h" + +int openpty(int *amaster, int *aslave, char *name, /*const struct termios*/ void *termp, + /*const struct winsize*/ void *winp); +#endif diff --git a/drivers/serial.c b/drivers/serial.c new file mode 100644 index 0000000..549d852 --- /dev/null +++ b/drivers/serial.c @@ -0,0 +1,35 @@ +#include "cpu/io.h" + +#define PORT 0x3f8 // COM1 + +int serial_init(void) { + outb(PORT + 1, 0x00); // Disable all interrupts + outb(PORT + 3, 0x80); // Enable DLAB (set baud rate divisor) + outb(PORT + 0, 0x03); // Set divisor to 3 (lo byte) 38400 baud + outb(PORT + 1, 0x00); // (hi byte) + outb(PORT + 3, 0x03); // 8 bits, no parity, one stop bit + outb(PORT + 2, 0xC7); // Enable FIFO, clear them, with 14-byte threshold + outb(PORT + 4, 0x0B); // IRQs enabled, RTS/DSR set + outb(PORT + 4, 0x1E); // Set in loopback mode, test the serial chip + outb(PORT + 0, 0xAE); // Test serial chip (send byte 0xAE and check if serial + // returns same byte) + + // Check if serial is faulty (i.e: not same byte as sent) + if (inb(PORT + 0) != 0xAE) { + return 1; + } + + // If serial is not faulty set it in normal operation mode + // (not-loopback with IRQs enabled and OUT#1 and OUT#2 bits enabled) + outb(PORT + 4, 0x0F); + return 0; +} + +int is_transmit_empty() { return inb(PORT + 5) & 0x20; } + +void write_serial(char a) { + while (is_transmit_empty() == 0) + ; + + outb(PORT, a); +} diff --git a/drivers/serial.h b/drivers/serial.h new file mode 100644 index 0000000..327765b --- /dev/null +++ b/drivers/serial.h @@ -0,0 +1,2 @@ +int serial_init(void); +void write_serial(char a); diff --git a/drivers/vbe.c b/drivers/vbe.c new file mode 100644 index 0000000..97f5507 --- /dev/null +++ b/drivers/vbe.c @@ -0,0 +1,50 @@ +#include <assert.h> +#include <drivers/vbe.h> +#include <fs/devfs.h> +#include <fs/vfs.h> +#include <mmu.h> +#include <stdio.h> + +uint8_t *framebuffer; +uint32_t framebuffer_physical; +uint32_t framebuffer_width; +uint32_t framebuffer_height; +uint64_t framebuffer_size; + +vfs_vm_object_t vbe_vm_object; + +#define CHECK_FLAG(flags, bit) ((flags) & (1 << (bit))) + +void display_driver_init(multiboot_info_t *mbi) { + assert(CHECK_FLAG(mbi->flags, 12)); + framebuffer_width = mbi->framebuffer_width; + framebuffer_height = mbi->framebuffer_height; + + uint32_t bits_pp = mbi->framebuffer_bpp; + uint32_t bytes_pp = (bits_pp / 8) + (8 - (bits_pp % 8)); + + framebuffer_size = bytes_pp * framebuffer_width * framebuffer_height; + + framebuffer_physical = mbi->framebuffer_addr; + framebuffer = + mmu_map_frames((void *)(uint32_t)mbi->framebuffer_addr, framebuffer_size); +} + +vfs_vm_object_t *vbe_get_vm_object(uint64_t length, uint64_t offset, + vfs_fd_t *fd) { + (void)fd; + (void)length; + (void)offset; + vbe_vm_object.size = framebuffer_size; + int n = (uintptr_t)align_page((void *)(uint32_t)framebuffer_size) / 0x1000; + vbe_vm_object.object = kmalloc(sizeof(void *) * n); + for (int i = 0; i < n; i++) { + vbe_vm_object.object[i] = (void *)framebuffer_physical + (i * 0x1000); + } + return &vbe_vm_object; +} + +void add_vbe_device(void) { + devfs_add_file("/vbe", NULL, NULL, vbe_get_vm_object, 1, 1, + FS_TYPE_BLOCK_DEVICE); +} diff --git a/drivers/vbe.h b/drivers/vbe.h new file mode 100644 index 0000000..4df2221 --- /dev/null +++ b/drivers/vbe.h @@ -0,0 +1,7 @@ +#ifndef VBE_H +#define VBE_H +#include <multiboot.h> +void display_driver_init(multiboot_info_t *mb); +void display_driver_cross(multiboot_info_t *mbi); +void add_vbe_device(void); +#endif // VBE_H |