mirror of
https://github.com/JannisHeydemann/BoredOS.git
synced 2026-05-30 02:16:58 +00:00
src/kernel --> src/
This commit is contained in:
42
src/dev/disk.h
Normal file
42
src/dev/disk.h
Normal file
@@ -0,0 +1,42 @@
|
||||
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||
#ifndef DISK_H
|
||||
#define DISK_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
#define SECTOR_SIZE 512
|
||||
|
||||
typedef enum {
|
||||
DISK_TYPE_RAM,
|
||||
DISK_TYPE_IDE,
|
||||
DISK_TYPE_SATA,
|
||||
DISK_TYPE_USB
|
||||
} DiskType;
|
||||
|
||||
typedef struct Disk {
|
||||
char letter;
|
||||
DiskType type;
|
||||
bool is_fat32;
|
||||
char name[32];
|
||||
uint32_t partition_lba_offset; // LBA offset of FAT32 partition (0 for raw)
|
||||
|
||||
// Function pointers for driver operations
|
||||
int (*read_sector)(struct Disk *disk, uint32_t sector, uint8_t *buffer);
|
||||
int (*write_sector)(struct Disk *disk, uint32_t sector, const uint8_t *buffer);
|
||||
|
||||
// Private driver data
|
||||
void *driver_data;
|
||||
} Disk;
|
||||
|
||||
void disk_manager_init(void);
|
||||
void disk_manager_scan(void); // Scans for new disks
|
||||
Disk* disk_get_by_letter(char letter);
|
||||
char disk_get_next_free_letter(void);
|
||||
void disk_register(Disk *disk);
|
||||
int disk_get_count(void);
|
||||
Disk* disk_get_by_index(int index);
|
||||
|
||||
#endif
|
||||
376
src/dev/disk_manager.c
Normal file
376
src/dev/disk_manager.c
Normal file
@@ -0,0 +1,376 @@
|
||||
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||
#include "disk.h"
|
||||
#include "pci.h"
|
||||
#include "memory_manager.h"
|
||||
#include "io.h"
|
||||
#include "wm.h"
|
||||
#include <stddef.h>
|
||||
|
||||
#define MAX_DISKS 26
|
||||
|
||||
static Disk *disks[MAX_DISKS];
|
||||
static int disk_count = 0;
|
||||
|
||||
// === ATA Definitions ===
|
||||
|
||||
#define ATA_PRIMARY_IO 0x1F0
|
||||
#define ATA_PRIMARY_CTRL 0x3F6
|
||||
#define ATA_SECONDARY_IO 0x170
|
||||
#define ATA_SECONDARY_CTRL 0x376
|
||||
|
||||
#define ATA_REG_DATA 0x00
|
||||
#define ATA_REG_ERROR 0x01
|
||||
#define ATA_REG_FEATURES 0x01
|
||||
#define ATA_REG_SEC_COUNT0 0x02
|
||||
#define ATA_REG_LBA0 0x03
|
||||
#define ATA_REG_LBA1 0x04
|
||||
#define ATA_REG_LBA2 0x05
|
||||
#define ATA_REG_HDDEVSEL 0x06
|
||||
#define ATA_REG_COMMAND 0x07
|
||||
#define ATA_REG_STATUS 0x07
|
||||
|
||||
#define ATA_CMD_READ_PIO 0x20
|
||||
#define ATA_CMD_WRITE_PIO 0x30
|
||||
#define ATA_CMD_IDENTIFY 0xEC
|
||||
|
||||
#define ATA_SR_BSY 0x80 // Busy
|
||||
#define ATA_SR_DRDY 0x40 // Drive ready
|
||||
#define ATA_SR_DF 0x20 // Drive write fault
|
||||
#define ATA_SR_DSC 0x10 // Drive seek complete
|
||||
#define ATA_SR_DRQ 0x08 // Data request ready
|
||||
#define ATA_SR_CORR 0x04 // Corrected data
|
||||
#define ATA_SR_IDX 0x02 // Index
|
||||
#define ATA_SR_ERR 0x01 // Error
|
||||
|
||||
typedef struct {
|
||||
uint16_t port_base;
|
||||
bool slave;
|
||||
} ATADriverData;
|
||||
|
||||
// === Helpers ===
|
||||
|
||||
static void dm_strcpy(char *dest, const char *src) {
|
||||
while (*src) *dest++ = *src++;
|
||||
*dest = 0;
|
||||
}
|
||||
|
||||
void disk_register(Disk *disk);
|
||||
|
||||
|
||||
static int ramdisk_read(Disk *disk, uint32_t sector, uint8_t *buffer) {
|
||||
(void)disk; (void)sector; (void)buffer;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ramdisk_write(Disk *disk, uint32_t sector, const uint8_t *buffer) {
|
||||
(void)disk; (void)sector; (void)buffer;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void ata_wait_bsy(uint16_t port_base) {
|
||||
while (inb(port_base + ATA_REG_STATUS) & ATA_SR_BSY);
|
||||
}
|
||||
|
||||
static void ata_wait_drq(uint16_t port_base) {
|
||||
while (!(inb(port_base + ATA_REG_STATUS) & ATA_SR_DRQ));
|
||||
}
|
||||
|
||||
// Returns 1 if drive exists, 0 otherwise
|
||||
static int ata_identify(uint16_t port_base, bool slave) {
|
||||
// Select Drive
|
||||
outb(port_base + ATA_REG_HDDEVSEL, slave ? 0xB0 : 0xA0);
|
||||
// Zero out sector count and LBA registers
|
||||
outb(port_base + ATA_REG_SEC_COUNT0, 0);
|
||||
outb(port_base + ATA_REG_LBA0, 0);
|
||||
outb(port_base + ATA_REG_LBA1, 0);
|
||||
outb(port_base + ATA_REG_LBA2, 0);
|
||||
|
||||
// Send Identify command
|
||||
outb(port_base + ATA_REG_COMMAND, ATA_CMD_IDENTIFY);
|
||||
|
||||
// Check if status is 0 (no drive)
|
||||
uint8_t status = inb(port_base + ATA_REG_STATUS);
|
||||
if (status == 0) return 0;
|
||||
|
||||
// Wait until BSY clears
|
||||
int timeout = 10000;
|
||||
while ((inb(port_base + ATA_REG_STATUS) & ATA_SR_BSY) && --timeout > 0) {
|
||||
status = inb(port_base + ATA_REG_STATUS);
|
||||
if (status == 0) return 0; // Check again
|
||||
}
|
||||
if (timeout <= 0) return 0; // Hardware didn't respond
|
||||
|
||||
// Check for error
|
||||
if (inb(port_base + ATA_REG_STATUS) & ATA_SR_ERR) {
|
||||
return 0; // Error, likely not ATA
|
||||
}
|
||||
|
||||
// Wait for DRQ or ERR
|
||||
while (!(inb(port_base + ATA_REG_STATUS) & (ATA_SR_DRQ | ATA_SR_ERR)));
|
||||
|
||||
if (inb(port_base + ATA_REG_STATUS) & ATA_SR_ERR) return 0;
|
||||
|
||||
// Read 256 words (512 bytes) of identity data
|
||||
for (int i = 0; i < 256; i++) {
|
||||
uint16_t data = inw(port_base + ATA_REG_DATA);
|
||||
(void)data;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int ata_read_sector(Disk *disk, uint32_t lba, uint8_t *buffer) {
|
||||
ATADriverData *data = (ATADriverData*)disk->driver_data;
|
||||
uint16_t port_base = data->port_base;
|
||||
bool slave = data->slave;
|
||||
|
||||
ata_wait_bsy(port_base);
|
||||
|
||||
// Select drive and send highest 4 bits of LBA
|
||||
outb(port_base + ATA_REG_HDDEVSEL, 0xE0 | (slave << 4) | ((lba >> 24) & 0x0F));
|
||||
outb(port_base + ATA_REG_FEATURES, 0x00);
|
||||
outb(port_base + ATA_REG_SEC_COUNT0, 1);
|
||||
outb(port_base + ATA_REG_LBA0, (uint8_t)(lba));
|
||||
outb(port_base + ATA_REG_LBA1, (uint8_t)(lba >> 8));
|
||||
outb(port_base + ATA_REG_LBA2, (uint8_t)(lba >> 16));
|
||||
outb(port_base + ATA_REG_COMMAND, ATA_CMD_READ_PIO);
|
||||
|
||||
ata_wait_bsy(port_base);
|
||||
ata_wait_drq(port_base);
|
||||
|
||||
uint16_t *ptr = (uint16_t*)buffer;
|
||||
for (int i = 0; i < 256; i++) {
|
||||
ptr[i] = inw(port_base + ATA_REG_DATA);
|
||||
}
|
||||
|
||||
return 0; // Success
|
||||
}
|
||||
|
||||
static int ata_write_sector(Disk *disk, uint32_t lba, const uint8_t *buffer) {
|
||||
ATADriverData *data = (ATADriverData*)disk->driver_data;
|
||||
uint16_t port_base = data->port_base;
|
||||
bool slave = data->slave;
|
||||
|
||||
ata_wait_bsy(port_base);
|
||||
|
||||
outb(port_base + ATA_REG_HDDEVSEL, 0xE0 | (slave << 4) | ((lba >> 24) & 0x0F));
|
||||
outb(port_base + ATA_REG_FEATURES, 0x00);
|
||||
outb(port_base + ATA_REG_SEC_COUNT0, 1);
|
||||
outb(port_base + ATA_REG_LBA0, (uint8_t)(lba));
|
||||
outb(port_base + ATA_REG_LBA1, (uint8_t)(lba >> 8));
|
||||
outb(port_base + ATA_REG_LBA2, (uint8_t)(lba >> 16));
|
||||
outb(port_base + ATA_REG_COMMAND, ATA_CMD_WRITE_PIO);
|
||||
|
||||
ata_wait_bsy(port_base);
|
||||
ata_wait_drq(port_base);
|
||||
|
||||
const uint16_t *ptr = (const uint16_t*)buffer;
|
||||
for (int i = 0; i < 256; i++) {
|
||||
outw(port_base + ATA_REG_DATA, ptr[i]);
|
||||
}
|
||||
|
||||
// Flush / Sync
|
||||
outb(port_base + ATA_REG_COMMAND, 0xE7); // Cache Flush
|
||||
ata_wait_bsy(port_base);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
char disk_get_next_free_letter(void) {
|
||||
for (int i = 0; i < MAX_DISKS; i++) {
|
||||
char letter = 'A' + i;
|
||||
bool used = false;
|
||||
for (int j = 0; j < disk_count; j++) {
|
||||
if (disks[j]->letter == letter) {
|
||||
used = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!used) return letter;
|
||||
}
|
||||
return 0; // No free letters
|
||||
}
|
||||
|
||||
void disk_register(Disk *disk) {
|
||||
if (disk_count >= MAX_DISKS) return;
|
||||
|
||||
// Ensure letter is unique
|
||||
if (disk->letter == 0) {
|
||||
disk->letter = disk_get_next_free_letter();
|
||||
}
|
||||
|
||||
disks[disk_count++] = disk;
|
||||
}
|
||||
|
||||
void disk_manager_init(void) {
|
||||
for (int i = 0; i < MAX_DISKS; i++) {
|
||||
disks[i] = NULL;
|
||||
}
|
||||
disk_count = 0;
|
||||
|
||||
// Register A: (Ramdisk)
|
||||
Disk *ramdisk = (Disk*)kmalloc(sizeof(Disk));
|
||||
ramdisk->letter = 'A';
|
||||
ramdisk->type = DISK_TYPE_RAM;
|
||||
ramdisk->is_fat32 = true; // Ramdisk is always formatted
|
||||
dm_strcpy(ramdisk->name, "RAM");
|
||||
ramdisk->read_sector = ramdisk_read;
|
||||
ramdisk->write_sector = ramdisk_write;
|
||||
ramdisk->driver_data = NULL;
|
||||
ramdisk->partition_lba_offset = 0;
|
||||
|
||||
disk_register(ramdisk);
|
||||
}
|
||||
|
||||
Disk* disk_get_by_letter(char letter) {
|
||||
// Uppercase
|
||||
if (letter >= 'a' && letter <= 'z') letter -= 32;
|
||||
|
||||
for (int i = 0; i < disk_count; i++) {
|
||||
if (disks[i]->letter == letter) {
|
||||
return disks[i];
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int disk_get_count(void) {
|
||||
return disk_count;
|
||||
}
|
||||
|
||||
Disk* disk_get_by_index(int index) {
|
||||
if (index < 0 || index >= disk_count) return NULL;
|
||||
return disks[index];
|
||||
}
|
||||
|
||||
|
||||
// === MBR Partition Table Structures ===
|
||||
|
||||
typedef struct {
|
||||
uint8_t status; // 0x80 = bootable, 0x00 = inactive
|
||||
uint8_t chs_first[3]; // CHS of first sector
|
||||
uint8_t type; // Partition type
|
||||
uint8_t chs_last[3]; // CHS of last sector
|
||||
uint32_t lba_start; // LBA of first sector
|
||||
uint32_t sector_count; // Number of sectors
|
||||
} __attribute__((packed)) MBR_PartitionEntry;
|
||||
|
||||
// FAT32 partition type codes
|
||||
#define PART_TYPE_FAT32 0x0B
|
||||
#define PART_TYPE_FAT32_LBA 0x0C
|
||||
|
||||
// Check if sector contains a valid FAT32 BPB (Volume Boot Record)
|
||||
static bool is_fat32_bpb(const uint8_t *sector) {
|
||||
// Must have 0xAA55 boot signature
|
||||
if (sector[510] != 0x55 || sector[511] != 0xAA) return false;
|
||||
|
||||
// Check for FAT32 filesystem string at offset 82
|
||||
// "FAT32 " in the fs_type field of the BPB
|
||||
if (sector[82] == 'F' && sector[83] == 'A' && sector[84] == 'T' &&
|
||||
sector[85] == '3' && sector[86] == '2') {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Also accept if bytes_per_sector is 512 and sectors_per_fat_16 is 0
|
||||
// (FAT32 always has sectors_per_fat_16 == 0)
|
||||
uint16_t bps = *(uint16_t*)§or[11];
|
||||
uint16_t spf16 = *(uint16_t*)§or[22];
|
||||
uint32_t spf32 = *(uint32_t*)§or[36];
|
||||
if (bps == 512 && spf16 == 0 && spf32 > 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Parse MBR partition table and find a FAT32 partition.
|
||||
// Sets disk->partition_lba_offset and returns true if found.
|
||||
static bool detect_fat32_partition(Disk *disk) {
|
||||
uint8_t *buffer = (uint8_t*)kmalloc(512);
|
||||
if (!buffer) return false;
|
||||
|
||||
// Read sector 0 (MBR or raw BPB)
|
||||
if (disk->read_sector(disk, 0, buffer) != 0) {
|
||||
kfree(buffer);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Must have 0xAA55 boot signature
|
||||
if (buffer[510] != 0x55 || buffer[511] != 0xAA) {
|
||||
kfree(buffer);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check MBR partition table entries (4 entries at offset 446)
|
||||
MBR_PartitionEntry *partitions = (MBR_PartitionEntry*)&buffer[446];
|
||||
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (partitions[i].type == PART_TYPE_FAT32 ||
|
||||
partitions[i].type == PART_TYPE_FAT32_LBA) {
|
||||
|
||||
uint32_t part_lba = partitions[i].lba_start;
|
||||
|
||||
// Read the partition's first sector to verify it's a valid FAT32 BPB
|
||||
uint8_t *pbuf = (uint8_t*)kmalloc(512);
|
||||
if (!pbuf) { kfree(buffer); return false; }
|
||||
|
||||
if (disk->read_sector(disk, part_lba, pbuf) == 0 && is_fat32_bpb(pbuf)) {
|
||||
disk->partition_lba_offset = part_lba;
|
||||
kfree(pbuf);
|
||||
kfree(buffer);
|
||||
return true;
|
||||
}
|
||||
kfree(pbuf);
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: check if sector 0 itself is a raw FAT32 BPB (no partition table)
|
||||
if (is_fat32_bpb(buffer)) {
|
||||
disk->partition_lba_offset = 0;
|
||||
kfree(buffer);
|
||||
return true;
|
||||
}
|
||||
|
||||
kfree(buffer);
|
||||
return false;
|
||||
}
|
||||
|
||||
static void try_add_ata_drive(uint16_t port, bool slave, const char *name) {
|
||||
if (ata_identify(port, slave)) {
|
||||
Disk *new_disk = (Disk*)kmalloc(sizeof(Disk));
|
||||
if (!new_disk) return;
|
||||
|
||||
ATADriverData *data = (ATADriverData*)kmalloc(sizeof(ATADriverData));
|
||||
data->port_base = port;
|
||||
data->slave = slave;
|
||||
|
||||
new_disk->letter = 0; // Auto-assign
|
||||
new_disk->type = DISK_TYPE_IDE;
|
||||
dm_strcpy(new_disk->name, name);
|
||||
new_disk->read_sector = ata_read_sector;
|
||||
new_disk->write_sector = ata_write_sector;
|
||||
new_disk->driver_data = data;
|
||||
new_disk->partition_lba_offset = 0;
|
||||
|
||||
// Detect FAT32 (with MBR partition support)
|
||||
if (detect_fat32_partition(new_disk)) {
|
||||
new_disk->is_fat32 = true;
|
||||
disk_register(new_disk);
|
||||
} else {
|
||||
kfree(data);
|
||||
kfree(new_disk);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void disk_manager_scan(void) {
|
||||
// Probe Standard ATA Ports
|
||||
try_add_ata_drive(ATA_PRIMARY_IO, false, "IDE1");
|
||||
try_add_ata_drive(ATA_PRIMARY_IO, true, "IDE2");
|
||||
try_add_ata_drive(ATA_SECONDARY_IO, false, "IDE3");
|
||||
try_add_ata_drive(ATA_SECONDARY_IO, true, "IDE4");
|
||||
}
|
||||
99
src/dev/pci.c
Normal file
99
src/dev/pci.c
Normal file
@@ -0,0 +1,99 @@
|
||||
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||
#include <stdint.h>
|
||||
#include "pci.h"
|
||||
#include "io.h"
|
||||
|
||||
uint32_t pci_read_config(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset) {
|
||||
uint32_t address = (uint32_t)((1u << 31) | (bus << 16) | (device << 11) | (function << 8) | (offset & 0xFC));
|
||||
outl(PCI_CONFIG_ADDRESS, address);
|
||||
return inl(PCI_CONFIG_DATA);
|
||||
}
|
||||
|
||||
void pci_write_config(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset, uint32_t value) {
|
||||
uint32_t address = (uint32_t)((1u << 31) | (bus << 16) | (device << 11) | (function << 8) | (offset & 0xFC));
|
||||
outl(PCI_CONFIG_ADDRESS, address);
|
||||
outl(PCI_CONFIG_DATA, value);
|
||||
}
|
||||
|
||||
int pci_device_exists(uint8_t bus, uint8_t device, uint8_t function) {
|
||||
uint16_t vendor_id = pci_get_vendor_id(bus, device, function);
|
||||
return vendor_id != 0xFFFF;
|
||||
}
|
||||
|
||||
uint16_t pci_get_vendor_id(uint8_t bus, uint8_t device, uint8_t function) {
|
||||
uint32_t config = pci_read_config(bus, device, function, 0x00);
|
||||
return (uint16_t)(config & 0xFFFF);
|
||||
}
|
||||
|
||||
uint16_t pci_get_device_id(uint8_t bus, uint8_t device, uint8_t function) {
|
||||
uint32_t config = pci_read_config(bus, device, function, 0x00);
|
||||
return (uint16_t)((config >> 16) & 0xFFFF);
|
||||
}
|
||||
|
||||
uint8_t pci_get_class_code(uint8_t bus, uint8_t device, uint8_t function) {
|
||||
uint32_t config = pci_read_config(bus, device, function, 0x08);
|
||||
return (uint8_t)((config >> 24) & 0xFF);
|
||||
}
|
||||
|
||||
uint8_t pci_get_subclass(uint8_t bus, uint8_t device, uint8_t function) {
|
||||
uint32_t config = pci_read_config(bus, device, function, 0x08);
|
||||
return (uint8_t)((config >> 16) & 0xFF);
|
||||
}
|
||||
|
||||
uint8_t pci_get_prog_if(uint8_t bus, uint8_t device, uint8_t function) {
|
||||
uint32_t config = pci_read_config(bus, device, function, 0x08);
|
||||
return (uint8_t)((config >> 8) & 0xFF);
|
||||
}
|
||||
|
||||
int pci_enumerate_devices(pci_device_t* devices, int max_devices) {
|
||||
int count = 0;
|
||||
for (uint8_t bus = 0; bus < 256 && count < max_devices; bus++) {
|
||||
for (uint8_t dev = 0; dev < 32 && count < max_devices; dev++) {
|
||||
if (pci_device_exists(bus, dev, 0)) {
|
||||
uint32_t config_val = pci_read_config(bus, dev, 0, 0x0C);
|
||||
uint8_t header_type = (uint8_t)((config_val >> 16) & 0xFF);
|
||||
uint8_t num_functions = (header_type & 0x80) ? 8 : 1;
|
||||
for (uint8_t fn = 0; fn < num_functions && count < max_devices; fn++) {
|
||||
if (pci_device_exists(bus, dev, fn)) {
|
||||
devices[count].bus = bus;
|
||||
devices[count].device = dev;
|
||||
devices[count].function = fn;
|
||||
devices[count].vendor_id = pci_get_vendor_id(bus, dev, fn);
|
||||
devices[count].device_id = pci_get_device_id(bus, dev, fn);
|
||||
devices[count].class_code = pci_get_class_code(bus, dev, fn);
|
||||
devices[count].subclass = pci_get_subclass(bus, dev, fn);
|
||||
devices[count].prog_if = pci_get_prog_if(bus, dev, fn);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
int pci_find_device(uint16_t vendor_id, uint16_t device_id, pci_device_t* device) {
|
||||
pci_device_t devices[32];
|
||||
int count = pci_enumerate_devices(devices, 32);
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (devices[i].vendor_id == vendor_id && devices[i].device_id == device_id) {
|
||||
*device = devices[i];
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int pci_find_device_by_class(uint8_t class_code, uint8_t subclass, pci_device_t* device) {
|
||||
pci_device_t devices[32];
|
||||
int count = pci_enumerate_devices(devices, 32);
|
||||
for (int i = 0; i < count; i++) {
|
||||
if (devices[i].class_code == class_code && devices[i].subclass == subclass) {
|
||||
*device = devices[i];
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
38
src/dev/pci.h
Normal file
38
src/dev/pci.h
Normal file
@@ -0,0 +1,38 @@
|
||||
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||
#ifndef PCI_H
|
||||
#define PCI_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define PCI_CONFIG_ADDRESS 0xCF8
|
||||
#define PCI_CONFIG_DATA 0xCFC
|
||||
|
||||
typedef struct {
|
||||
uint16_t vendor_id;
|
||||
uint16_t device_id;
|
||||
uint8_t bus;
|
||||
uint8_t device;
|
||||
uint8_t function;
|
||||
uint8_t class_code;
|
||||
uint8_t subclass;
|
||||
uint8_t prog_if;
|
||||
} pci_device_t;
|
||||
|
||||
#define PCI_CLASS_NETWORK_CONTROLLER 0x02
|
||||
#define PCI_CLASS_ETHERNET_CONTROLLER 0x00
|
||||
|
||||
uint32_t pci_read_config(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset);
|
||||
void pci_write_config(uint8_t bus, uint8_t device, uint8_t function, uint8_t offset, uint32_t value);
|
||||
int pci_device_exists(uint8_t bus, uint8_t device, uint8_t function);
|
||||
uint16_t pci_get_vendor_id(uint8_t bus, uint8_t device, uint8_t function);
|
||||
uint16_t pci_get_device_id(uint8_t bus, uint8_t device, uint8_t function);
|
||||
uint8_t pci_get_class_code(uint8_t bus, uint8_t device, uint8_t function);
|
||||
uint8_t pci_get_subclass(uint8_t bus, uint8_t device, uint8_t function);
|
||||
uint8_t pci_get_prog_if(uint8_t bus, uint8_t device, uint8_t function);
|
||||
int pci_enumerate_devices(pci_device_t* devices, int max_devices);
|
||||
int pci_find_device(uint16_t vendor_id, uint16_t device_id, pci_device_t* device);
|
||||
int pci_find_device_by_class(uint8_t class_code, uint8_t subclass, pci_device_t* device);
|
||||
|
||||
#endif
|
||||
229
src/dev/ps2.c
Normal file
229
src/dev/ps2.c
Normal file
@@ -0,0 +1,229 @@
|
||||
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||
#include "ps2.h"
|
||||
#include "io.h"
|
||||
#include "wm.h"
|
||||
#include "network.h"
|
||||
#include <stdbool.h>
|
||||
|
||||
extern void serial_print(const char *s);
|
||||
extern void serial_print_hex(uint64_t n);
|
||||
|
||||
// --- Timer Handler ---
|
||||
volatile uint64_t kernel_ticks = 0;
|
||||
|
||||
uint64_t timer_handler(registers_t *regs) {
|
||||
kernel_ticks++;
|
||||
wm_timer_tick();
|
||||
network_process_frames();
|
||||
|
||||
outb(0x20, 0x20); // EOI after processing to prevent nested timer interrupts
|
||||
extern uint64_t process_schedule(uint64_t current_rsp);
|
||||
return process_schedule((uint64_t)regs);
|
||||
}
|
||||
|
||||
// --- Keyboard ---
|
||||
static bool shift_pressed = false;
|
||||
bool ps2_ctrl_pressed = false;
|
||||
static bool extended_scancode = false;
|
||||
|
||||
static char scancode_map[128] = {
|
||||
0, 27, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '-', '=', '\b',
|
||||
'\t', 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n',
|
||||
21, 'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', '\'', '`', 0,
|
||||
'\\', 'z', 'x', 'c', 'v', 'b', 'n', 'm', ',', '.', '/', 0, '*',
|
||||
22, ' ', 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
|
||||
static char scancode_map_shift[128] = {
|
||||
0, 27, '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '_', '+', '\b',
|
||||
'\t', 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', 'O', 'P', '{', '}', '\n',
|
||||
21, 'A', 'S', 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', '"', '~', 0,
|
||||
'|', 'Z', 'X', 'C', 'V', 'B', 'N', 'M', '<', '>', '?', 0, '*',
|
||||
22, ' ', 23, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
};
|
||||
|
||||
uint64_t keyboard_handler(registers_t *regs) {
|
||||
uint8_t scancode = inb(0x60);
|
||||
|
||||
if (scancode == 0xE0) {
|
||||
extended_scancode = true;
|
||||
outb(0x20, 0x20);
|
||||
return (uint64_t)regs;
|
||||
}
|
||||
|
||||
if (scancode == 0x1D) {
|
||||
ps2_ctrl_pressed = true;
|
||||
extended_scancode = false; // Reset if Ctrl is pressed (prevents E0 1D bug)
|
||||
} else if (scancode == 0x9D) {
|
||||
ps2_ctrl_pressed = false;
|
||||
extended_scancode = false;
|
||||
}
|
||||
|
||||
if (ps2_ctrl_pressed && scancode == 0x2E) {
|
||||
extern process_t* process_get_current(void);
|
||||
process_t* proc = process_get_current();
|
||||
if (proc && proc->is_user && proc->is_terminal_proc && proc->ui_window) {
|
||||
// Only kill if the associated terminal window is focused
|
||||
if (((Window*)proc->ui_window)->focused) {
|
||||
extern uint64_t process_terminate_current(void);
|
||||
outb(0x20, 0x20); // EOI before context switch
|
||||
return process_terminate_current();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (scancode == 0x2A || scancode == 0x36) { // Shift Down
|
||||
shift_pressed = true;
|
||||
} else if (scancode == 0xAA || scancode == 0xB6) { // Shift Up
|
||||
shift_pressed = false;
|
||||
} else if (!(scancode & 0x80)) { // Key Press (not release)
|
||||
if (extended_scancode) {
|
||||
extended_scancode = false;
|
||||
switch (scancode) {
|
||||
case 0x48: wm_handle_key(17, true); break; // Up arrow
|
||||
case 0x50: wm_handle_key(18, true); break; // Down arrow
|
||||
case 0x4B: wm_handle_key(19, true); break; // Left arrow
|
||||
case 0x4D: wm_handle_key(20, true); break; // Right arrow
|
||||
}
|
||||
} else {
|
||||
char c = shift_pressed ? scancode_map_shift[scancode] : scancode_map[scancode];
|
||||
if (c) {
|
||||
wm_handle_key(c, true);
|
||||
}
|
||||
}
|
||||
} else if (scancode & 0x80) { // Key release
|
||||
if (extended_scancode) {
|
||||
extended_scancode = false;
|
||||
switch (scancode & 0x7F) { // Strip the release bit
|
||||
case 0x48: wm_handle_key(17, false); break; // Up arrow
|
||||
case 0x50: wm_handle_key(18, false); break; // Down arrow
|
||||
case 0x4B: wm_handle_key(19, false); break; // Left arrow
|
||||
case 0x4D: wm_handle_key(20, false); break; // Right arrow
|
||||
}
|
||||
} else {
|
||||
char c = shift_pressed ? scancode_map_shift[scancode & 0x7F] : scancode_map[scancode & 0x7F];
|
||||
if (c) {
|
||||
wm_handle_key(c, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
outb(0x20, 0x20); // EOI
|
||||
return (uint64_t)regs;
|
||||
}
|
||||
|
||||
// --- Mouse ---
|
||||
static uint8_t mouse_cycle = 0;
|
||||
static int8_t mouse_byte[4];
|
||||
static bool mouse_has_wheel = false;
|
||||
|
||||
void mouse_wait(uint8_t type) {
|
||||
uint32_t timeout = 100000;
|
||||
if (type == 0) { // Write
|
||||
while (timeout--) {
|
||||
if ((inb(0x64) & 2) == 0) return;
|
||||
}
|
||||
} else { // Read
|
||||
while (timeout--) {
|
||||
if ((inb(0x64) & 1) == 1) return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void mouse_write(uint8_t write) {
|
||||
mouse_wait(0);
|
||||
outb(0x64, 0xD4);
|
||||
mouse_wait(0);
|
||||
outb(0x60, write);
|
||||
}
|
||||
|
||||
uint8_t mouse_read(void) {
|
||||
mouse_wait(1);
|
||||
return inb(0x60);
|
||||
}
|
||||
|
||||
void mouse_init(void) {
|
||||
uint8_t status;
|
||||
|
||||
// Enable Aux Device
|
||||
mouse_wait(0);
|
||||
outb(0x64, 0xA8);
|
||||
|
||||
// Enable Interrupts
|
||||
mouse_wait(0);
|
||||
outb(0x64, 0x20);
|
||||
mouse_wait(1);
|
||||
status = inb(0x60) | 2;
|
||||
mouse_wait(0);
|
||||
outb(0x64, 0x60);
|
||||
mouse_wait(0);
|
||||
outb(0x60, status);
|
||||
|
||||
// Set Defaults
|
||||
mouse_write(0xF6);
|
||||
mouse_read();
|
||||
|
||||
// Enable Wheel - Magic Sequence
|
||||
mouse_write(0xF3); mouse_read(); mouse_write(200); mouse_read();
|
||||
mouse_write(0xF3); mouse_read(); mouse_write(100); mouse_read();
|
||||
mouse_write(0xF3); mouse_read(); mouse_write(80); mouse_read();
|
||||
|
||||
mouse_write(0xF2);
|
||||
mouse_read();
|
||||
uint8_t id = mouse_read();
|
||||
if (id == 3) mouse_has_wheel = true;
|
||||
|
||||
// Enable Streaming
|
||||
mouse_write(0xF4);
|
||||
mouse_read();
|
||||
}
|
||||
|
||||
uint64_t mouse_handler(registers_t *regs) {
|
||||
uint8_t status = inb(0x64);
|
||||
if (!(status & 0x20)) {
|
||||
outb(0x20, 0x20);
|
||||
outb(0xA0, 0x20);
|
||||
return (uint64_t)regs;
|
||||
}
|
||||
|
||||
uint8_t b = inb(0x60);
|
||||
|
||||
if (mouse_cycle == 0) {
|
||||
if ((b & 0x08) == 0) {
|
||||
// Out of sync
|
||||
} else {
|
||||
mouse_byte[0] = b;
|
||||
mouse_cycle++;
|
||||
}
|
||||
} else if (mouse_cycle == 1) {
|
||||
mouse_byte[1] = b;
|
||||
mouse_cycle++;
|
||||
} else if (mouse_cycle == 2) {
|
||||
mouse_byte[2] = b;
|
||||
if (mouse_has_wheel) {
|
||||
mouse_cycle++;
|
||||
} else {
|
||||
mouse_cycle = 0;
|
||||
int8_t dx = mouse_byte[1];
|
||||
int8_t dy = mouse_byte[2];
|
||||
wm_handle_mouse(dx, -dy, mouse_byte[0] & 0x07, 0);
|
||||
}
|
||||
} else if (mouse_cycle == 3) {
|
||||
mouse_byte[3] = b;
|
||||
mouse_cycle = 0;
|
||||
int8_t dx = mouse_byte[1];
|
||||
int8_t dy = mouse_byte[2];
|
||||
int8_t dz = mouse_byte[3];
|
||||
wm_handle_mouse(dx, -dy, mouse_byte[0] & 0x07, -dz);
|
||||
}
|
||||
|
||||
outb(0x20, 0x20);
|
||||
outb(0xA0, 0x20);
|
||||
return (uint64_t)regs;
|
||||
}
|
||||
|
||||
void ps2_init(void) {
|
||||
mouse_init();
|
||||
}
|
||||
16
src/dev/ps2.h
Normal file
16
src/dev/ps2.h
Normal file
@@ -0,0 +1,16 @@
|
||||
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||
#ifndef PS2_H
|
||||
#define PS2_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void ps2_init(void);
|
||||
#include "process.h"
|
||||
|
||||
uint64_t timer_handler(registers_t *regs);
|
||||
uint64_t keyboard_handler(registers_t *regs);
|
||||
uint64_t mouse_handler(registers_t *regs);
|
||||
|
||||
#endif
|
||||
113
src/dev/rtc.c
Normal file
113
src/dev/rtc.c
Normal file
@@ -0,0 +1,113 @@
|
||||
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||
#include "rtc.h"
|
||||
#include "io.h"
|
||||
|
||||
#define CMOS_ADDRESS 0x70
|
||||
#define CMOS_DATA 0x71
|
||||
|
||||
static int updating_rtc() {
|
||||
outb(CMOS_ADDRESS, 0x0A);
|
||||
return (inb(CMOS_DATA) & 0x80);
|
||||
}
|
||||
|
||||
static uint8_t get_rtc_register(int reg) {
|
||||
outb(CMOS_ADDRESS, reg);
|
||||
return inb(CMOS_DATA);
|
||||
}
|
||||
|
||||
void rtc_get_datetime(int *year, int *month, int *day, int *hour, int *minute, int *second) {
|
||||
uint8_t century;
|
||||
uint8_t last_second;
|
||||
uint8_t last_minute;
|
||||
uint8_t last_hour;
|
||||
uint8_t last_day;
|
||||
uint8_t last_month;
|
||||
uint8_t last_year;
|
||||
uint8_t last_century;
|
||||
uint8_t registerB;
|
||||
|
||||
while (updating_rtc());
|
||||
*second = get_rtc_register(0x00);
|
||||
*minute = get_rtc_register(0x02);
|
||||
*hour = get_rtc_register(0x04);
|
||||
*day = get_rtc_register(0x07);
|
||||
*month = get_rtc_register(0x08);
|
||||
*year = get_rtc_register(0x09);
|
||||
|
||||
do {
|
||||
last_second = *second;
|
||||
last_minute = *minute;
|
||||
last_hour = *hour;
|
||||
last_day = *day;
|
||||
last_month = *month;
|
||||
last_year = *year;
|
||||
|
||||
while (updating_rtc());
|
||||
*second = get_rtc_register(0x00);
|
||||
*minute = get_rtc_register(0x02);
|
||||
*hour = get_rtc_register(0x04);
|
||||
*day = get_rtc_register(0x07);
|
||||
*month = get_rtc_register(0x08);
|
||||
*year = get_rtc_register(0x09);
|
||||
} while( (last_second != *second) || (last_minute != *minute) || (last_hour != *hour) ||
|
||||
(last_day != *day) || (last_month != *month) || (last_year != *year) );
|
||||
|
||||
registerB = get_rtc_register(0x0B);
|
||||
|
||||
// Convert BCD to binary values if necessary
|
||||
if (!(registerB & 0x04)) {
|
||||
*second = (*second & 0x0F) + ((*second / 16) * 10);
|
||||
*minute = (*minute & 0x0F) + ((*minute / 16) * 10);
|
||||
*hour = ( (*hour & 0x0F) + (((*hour & 0x70) / 16) * 10) ) | (*hour & 0x80);
|
||||
*day = (*day & 0x0F) + ((*day / 16) * 10);
|
||||
*month = (*month & 0x0F) + ((*month / 16) * 10);
|
||||
*year = (*year & 0x0F) + ((*year / 16) * 10);
|
||||
}
|
||||
|
||||
// Convert 12 hour clock to 24 hour clock if necessary
|
||||
if (!(registerB & 0x02) && (*hour & 0x80)) {
|
||||
*hour = ((*hour & 0x7F) + 12) % 24;
|
||||
}
|
||||
|
||||
// Calculate full year
|
||||
*year += 2000;
|
||||
}
|
||||
|
||||
static void set_rtc_register(int reg, uint8_t value) {
|
||||
outb(CMOS_ADDRESS, reg);
|
||||
outb(CMOS_DATA, value);
|
||||
}
|
||||
|
||||
void rtc_set_datetime(int year, int month, int day, int hour, int minute, int second) {
|
||||
uint8_t registerB = get_rtc_register(0x0B);
|
||||
|
||||
// Disable NMI and start update
|
||||
outb(CMOS_ADDRESS, 0x8B);
|
||||
uint8_t prev_b = inb(CMOS_DATA);
|
||||
outb(CMOS_DATA, prev_b | 0x80); // Set SET bit to prevent updates while writing
|
||||
|
||||
if (year >= 2000) year -= 2000;
|
||||
|
||||
if (!(registerB & 0x04)) {
|
||||
// Convert binary to BCD
|
||||
second = ((second / 10) << 4) | (second % 10);
|
||||
minute = ((minute / 10) << 4) | (minute % 10);
|
||||
hour = ((hour / 10) << 4) | (hour % 10);
|
||||
day = ((day / 10) << 4) | (day % 10);
|
||||
month = ((month / 10) << 4) | (month % 10);
|
||||
year = ((year / 10) << 4) | (year % 10);
|
||||
}
|
||||
|
||||
set_rtc_register(0x00, (uint8_t)second);
|
||||
set_rtc_register(0x02, (uint8_t)minute);
|
||||
set_rtc_register(0x04, (uint8_t)hour);
|
||||
set_rtc_register(0x07, (uint8_t)day);
|
||||
set_rtc_register(0x08, (uint8_t)month);
|
||||
set_rtc_register(0x09, (uint8_t)year);
|
||||
|
||||
// Re-enable updates
|
||||
outb(CMOS_ADDRESS, 0x8B);
|
||||
outb(CMOS_DATA, prev_b & ~0x80);
|
||||
}
|
||||
12
src/dev/rtc.h
Normal file
12
src/dev/rtc.h
Normal file
@@ -0,0 +1,12 @@
|
||||
// Copyright (c) 2023-2026 Chris (boreddevnl)
|
||||
// This software is released under the GNU General Public License v3.0. See LICENSE file for details.
|
||||
// This header needs to maintain in any file it is present in, as per the GPL license terms.
|
||||
#ifndef RTC_H
|
||||
#define RTC_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
void rtc_get_datetime(int *year, int *month, int *day, int *hour, int *minute, int *second);
|
||||
void rtc_set_datetime(int year, int month, int day, int hour, int minute, int second);
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user