colonel.exe 🤑, refactor all include directives, AHCI is now interrupt driven, upgrades to IDE, implemented I/O Request Packets, fix numerous keyboard issues, change all for (;;) and while (1) to while (true), DbgPrint guarded by spinlock, fix KeSignalEvent, major KernelMain refactor, added SystemInitThread because of new blocking I/O, replace memcpy manouvers with strncpy and strncat, add: run for ide, runa for ahci, rund for debug and ide, runad for debug and ahci.

This commit is contained in:
maksw committed 2026-05-03 19:35:48 +02:00
1 parent 7f4052f41c
commit 9fa2a7e0db
61 files changed
+1311 -456

No files matched your search

+7 -3
View File
@@ -3,7 +3,7 @@
#include <stdint.h>
#include <stdbool.h>
#include <stddef.h>
#include "ke_status.h"
#include <ke_status.h>
typedef struct BOOT_INFO BOOT_INFO;
@@ -21,6 +21,7 @@ typedef struct HAL_KEYBOARD {
bool (*Poll)(void);
uint8_t (*ReadScancode)(void);
char (*ReadChar)(void);
uint32_t (*GetModifiers)(void);
void (*SetCallback)(void (*cb)(uint8_t scancode));
void (*Isr)(void);
uint8_t IrqNumber;
@@ -43,14 +44,17 @@ typedef struct HAL_TIMER {
} HAL_TIMER;
typedef struct HAL_PCI {
uint32_t (*ConfigRead32)(uint8_t bus, uint8_t dev,
uint32_t (*Read32)(uint8_t bus, uint8_t dev,
uint8_t fn, uint8_t reg);
void (*ConfigWrite32)(uint8_t bus, uint8_t dev,
void (*Write32)(uint8_t bus, uint8_t dev,
uint8_t fn, uint8_t reg, uint32_t val);
bool (*FindDevice)(uint16_t vendor, uint16_t device,
uint8_t* bus, uint8_t* dev, uint8_t* fn);
bool (*FindClass)(uint8_t classCode, uint8_t subclass,
uint8_t* bus, uint8_t* dev, uint8_t* fn);
// Returns the IRQ line from PCI config space (register 0x3C, byte 0).
// Returns 0xFF if no interrupt is wired.
uint8_t (*GetIrqLine)(uint8_t bus, uint8_t dev, uint8_t fn);
} HAL_PCI;
typedef struct HAL_INTC {
+2 -2
View File
@@ -1,6 +1,6 @@
// hal_apic.c - Local APIC + I/O APIC
#include "hal_apic.h"
#include "hal_api.h"
#include <hal_apic.h>
#include <hal_api.h>
#include <stdint.h>
extern HAL_API gApi;
+1 -1
View File
@@ -1,4 +1,4 @@
#pragma once
// hal_debug.h - HAL debug output function
// Do not include outside HAL.
void HalDbgPrint(const char* fmt, ...);
+62 -12
View File
@@ -1,15 +1,26 @@
// hal_disk.c - drive registry; drivers register themselves via IoRegisterDisk
#include "hal_api.h"
#include "io_driver.h"
// hal_disk.c - drive registry; drivers register themselves via IoRegisterDisk.
//
// This file lives in the HAL layer and must NOT reference any kernel sync
// objects (KeEventCreate, KeSignalEvent, KeWaitForEvent, ObDereferenceObject).
// All IRP event signalling is done in kernel/ke_irp.c, which sits above us
// in the dependency graph.
//
// HalDiskDispatch is the one bridge: it calls the driver's SubmitIrp (async)
// or runs the sync callbacks inline and writes irp->Status. It does not
// touch irp->CompletionEvent — that is ke_irp.c's responsibility.
#include <stdint.h>
#include <stdbool.h>
#include <hal_api.h>
#include <io_driver.h>
#include <ke_irp.h>
#define MAX_DRIVES 8
static DISK_DRIVER gDrives[MAX_DRIVES];
static bool gPresent[MAX_DRIVES];
KSTATUS IoRegisterDisk(uint8_t index, const DISK_DRIVER* driver) {
KSTATUS IoRegisterDisk(uint8_t index, const DISK_DRIVER *driver) {
if (index >= MAX_DRIVES || !driver) return STATUS_INVALID_PARAMETER;
if (gPresent[index]) return STATUS_ALREADY_EXISTS;
gDrives[index] = *driver;
@@ -17,27 +28,66 @@ KSTATUS IoRegisterDisk(uint8_t index, const DISK_DRIVER* driver) {
return STATUS_SUCCESS;
}
// -------------------------------------------------------------------------
// HalDiskDispatch — called exclusively by ke_irp.c::IoSubmitIrp()
// -------------------------------------------------------------------------
// Separating dispatch here keeps hal_disk.c free of event primitives while
// still allowing ke_irp.c (kernel layer) to fire completion hooks after we
// return.
//
// Returns STATUS_PENDING — driver accepted the IRP asynchronously.
// ke_irp.c must NOT touch irp->CompletionEvent
// yet; the driver will fire it.
// Returns STATUS_SUCCESS — sync fallback ran; irp->Status is already
// written. ke_irp.c should fire completion hooks.
// Returns STATUS_NOT_FOUND — drive not registered.
KSTATUS HalDiskDispatch(uint8_t drive, IRP *irp) {
if (drive >= MAX_DRIVES || !gPresent[drive]) return STATUS_NOT_FOUND;
DISK_DRIVER *drv = &gDrives[drive];
if (drv->SubmitIrp)
return drv->SubmitIrp(drv->Ctx, irp); // STATUS_PENDING
// Sync fallback: execute inline, write result, let caller fire hooks.
bool write = !!(irp->Flags & IRP_FLAG_WRITE);
if (write)
irp->Status = drv->WriteSectors(drv->Ctx, irp->Lba, irp->SectorCount, irp->Buffer);
else
irp->Status = drv->ReadSectors (drv->Ctx, irp->Lba, irp->SectorCount, irp->Buffer);
return STATUS_SUCCESS; // ran synchronously
}
// -------------------------------------------------------------------------
// Legacy HAL_DISK vtable — direct sync calls, no IRP involvement.
// Existing callers (FAT32, shell) use this path unchanged.
// -------------------------------------------------------------------------
static KSTATUS DiskRead(uint8_t drive, uint64_t lba,
uint32_t count, void* buf) {
uint32_t count, void *buf) {
if (drive >= MAX_DRIVES || !gPresent[drive]) return STATUS_NOT_FOUND;
return gDrives[drive].ReadSectors(lba, count, buf);
return gDrives[drive].ReadSectors(gDrives[drive].Ctx, lba, count, buf);
}
static KSTATUS DiskWrite(uint8_t drive, uint64_t lba,
uint32_t count, const void* buf) {
uint32_t count, const void *buf) {
if (drive >= MAX_DRIVES || !gPresent[drive]) return STATUS_NOT_FOUND;
return gDrives[drive].WriteSectors(lba, count, buf);
return gDrives[drive].WriteSectors(gDrives[drive].Ctx, lba, count, buf);
}
static bool DiskPresent(uint8_t drive) {
return drive < MAX_DRIVES && gPresent[drive];
}
static uint64_t DiskSectorCount(uint8_t drive) {
if (drive >= MAX_DRIVES || !gPresent[drive]) return 0;
return gDrives[drive].SectorCount;
}
void HalDiskInitialize(HAL_DISK* out) {
out->ReadSectors = DiskRead;
out->WriteSectors = DiskWrite;
out->DrivePresent = DiskPresent;
void HalDiskInitialize(HAL_DISK *out) {
out->ReadSectors = DiskRead;
out->WriteSectors = DiskWrite;
out->DrivePresent = DiskPresent;
out->DriveSectorCount = DiskSectorCount;
}
+260 -33
View File
@@ -1,71 +1,298 @@
// hal_keyboard.c - PS/2 keyboard, interrupt-driven, 256-entry ring buffer
#include "hal_api.h"
#include "hal_apic.h"
#include "hal_debug.h"
// hal_keyboard.c – PS/2 keyboard, interrupt-driven, 256-entry ring buffer
//
// Scan-code set 1 (XT-compatible), as output by the 8042's built-in
// translation layer. Supports make/break for all standard keys, Shift /
// Ctrl / Alt / CapsLock / NumLock modifiers, and the E0-prefixed extended
// key set (arrows, Home/End/PgUp/PgDn/Ins/Del, RCtrl, RAlt, numpad-Enter,
// numpad-divide).
//
// Threading model
// ---------------
// Single-producer (ISR on IRQ 1) / single-consumer ring buffer. All
// ISR-written state is declared volatile. On a uniprocessor, volatile
// plus the natural atomicity of aligned int reads/writes is sufficient.
// On SMP, add acquire/release memory barriers around gHead and gTail.
//
// Caller responsibilities
// -----------------------
// 1. Register KbdIsr with your interrupt dispatcher for IRQ 1.
// 2. Send the APIC/PIC EOI *after* KbdIsr returns, in the surrounding
// wrapper – not inside KbdIsr itself.
// 3. Unmask IRQ 1 in the APIC or 8259 before expecting any interrupts.
//
#include <stdint.h>
#include <stdbool.h>
#include <intrin.h>
#include <hal_api.h>
#include <hal_apic.h>
#include <hal_debug.h>
#define PS2_DATA 0x60
#define PS2_STAT 0x64
#define PS2_CMD 0x64
// -- Port addresses ------------------------------------------------------------
#define PS2_DATA 0x60u
#define PS2_STAT 0x64u
#define PS2_CMD 0x64u
#define KBD_BUF_SIZE 256
// -- Ring buffer ---------------------------------------------------------------
// KBD_BUF_SIZE must be a power of two; KBD_BUF_MASK replaces % with &.
#define KBD_BUF_SIZE 256u
#define KBD_BUF_MASK (KBD_BUF_SIZE - 1u)
static uint8_t gBuf[KBD_BUF_SIZE];
static volatile int gHead = 0, gTail = 0;
static void (*gCallback)(uint8_t) = (void *)0;
static uint8_t gBuf[KBD_BUF_SIZE];
static volatile int gHead = 0; // consumer read pointer (updated by reader)
static volatile int gTail = 0; // producer write pointer (updated by ISR)
// Minimal Set-2 to ASCII translation (unshifted only).
static const char gScanToAscii[128] = {
0,0,'1','2','3','4','5','6','7','8','9','0','-','=','\b',
'\t','q','w','e','r','t','y','u','i','o','p','[',']','\n',
0,'a','s','d','f','g','h','j','k','l',';','\'','`',
0,'\\','z','x','c','v','b','n','m',',','.','/',0,'*',
0,' ',0,0,0,0,0,0,0,0,0,0,0,0,0,
'7','8','9','-','4','5','6','+','1','2','3','0','.',0,0,0,
// -- Scan-code set 1 constants -------------------------------------------------
#define SC1_BREAK_BIT 0x80u // OR'd into every break (key-up) scan code
#define SC1_MAKE_MASK 0x7Fu // complement of SC1_BREAK_BIT; avoids ~uint cast
#define SC1_EXT_PREFIX 0xE0u // one-byte prefix for extended keys
// -- Modifier flags ------------------------------------------------------------
#define MOD_LSHIFT (1u << 0)
#define MOD_RSHIFT (1u << 1)
#define MOD_LCTRL (1u << 2)
#define MOD_RCTRL (1u << 3)
#define MOD_LALT (1u << 4)
#define MOD_RALT (1u << 5)
#define MOD_CAPS (1u << 6) // toggle, not a held key
#define MOD_NUMLOCK (1u << 7) // toggle, not a held key
#define MOD_SHIFT (MOD_LSHIFT | MOD_RSHIFT)
#define MOD_CTRL (MOD_LCTRL | MOD_RCTRL)
#define MOD_ALT (MOD_LALT | MOD_RALT)
static volatile uint32_t gModifiers = 0;
static volatile bool gNextExtended = false; // previous byte was 0xE0
// -- Raw-scancode callback (invoked from ISR context) --------------------------
// The pointer must be volatile so the compiler re-reads it on every ISR entry
// rather than caching the value seen at startup.
static void (* volatile gCallback)(uint8_t) = NULL;
// -- ASCII translation tables – scan-code set 1, non-extended keys only --------
// Index = 7-bit make code (SC1_BREAK_BIT cleared). 0 = no printable glyph.
// Numpad digits yield their digit characters; a NumLock-aware wrapper can
// substitute cursor-movement codes (e.g. HAL_KEY_HOME) when NumLock is clear.
//
// Layout verified against the IBM PC/AT scan-code set 1 reference.
static const char gUnshifted[128] = {
/*00*/ 0,
/*01*/ 0, // ESC
/*02*/ '1','2','3','4','5','6','7','8','9','0','-','=', // 02–0D
/*0E*/ '\b',
/*0F*/ '\t',
/*10*/ 'q','w','e','r','t','y','u','i','o','p','[',']', // 10–1B
/*1C*/ '\n',
/*1D*/ 0, // Left Ctrl
/*1E*/ 'a','s','d','f','g','h','j','k','l',';','\'','`', // 1E–29
/*2A*/ 0, // Left Shift
/*2B*/ '\\',
/*2C*/ 'z','x','c','v','b','n','m',',','.','/', // 2C–35
/*36*/ 0, // Right Shift
/*37*/ '*', // Numpad * (also PrtSc on some keyboards)
/*38*/ 0, // Left Alt
/*39*/ ' ',
/*3A*/ 0, // Caps Lock
/*3B*/ 0,0,0,0,0,0,0,0,0,0, // F1–F10 (3B–44)
/*45*/ 0, // Num Lock
/*46*/ 0, // Scroll Lock
/*47*/ '7','8','9', // Numpad 7–9
/*4A*/ '-', // Numpad –
/*4B*/ '4','5','6', // Numpad 4–6
/*4E*/ '+', // Numpad +
/*4F*/ '1','2','3', // Numpad 1–3
/*52*/ '0', // Numpad 0
/*53*/ '.', // Numpad .
/*54*/ 0,0,0,
/*57*/ 0, // F11
/*58*/ 0, // F12
/*59*/ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 59–68
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 69–78
0,0,0,0,0,0,0, // 79–7F
};
static const char gShifted[128] = {
/*00*/ 0,
/*01*/ 0, // ESC
/*02*/ '!','@','#','$','%','^','&','*','(',')','_','+', // 02–0D
/*0E*/ '\b',
/*0F*/ '\t',
/*10*/ 'Q','W','E','R','T','Y','U','I','O','P','{','}', // 10–1B
/*1C*/ '\n',
/*1D*/ 0, // Left Ctrl
/*1E*/ 'A','S','D','F','G','H','J','K','L',':','"','~', // 1E–29
/*2A*/ 0, // Left Shift
/*2B*/ '|',
/*2C*/ 'Z','X','C','V','B','N','M','<','>','?', // 2C–35
/*36*/ 0, // Right Shift
/*37*/ '*',
/*38*/ 0, // Left Alt
/*39*/ ' ',
/*3A*/ 0, // Caps Lock
/*3B*/ 0,0,0,0,0,0,0,0,0,0, // F1–F10
/*45*/ 0,
/*46*/ 0,
/*47*/ '7','8','9',
/*4A*/ '-',
/*4B*/ '4','5','6',
/*4E*/ '+',
/*4F*/ '1','2','3',
/*52*/ '0',
/*53*/ '.',
/*54*/ 0,0,0,
/*57*/ 0, // F11
/*58*/ 0, // F12
/*59*/ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,
};
// -- Interrupt service routine --------------------------------------------------
// Called with interrupts disabled (inside the hardware IRQ handler).
// The caller's wrapper must send the APIC/PIC EOI after this returns.
static void KbdIsr(void) {
uint8_t sc = __inbyte(PS2_DATA);
if (sc & 0x80) return; // key-up, ignore
int next = (gTail + 1) % KBD_BUF_SIZE;
if (next != gHead) {
gBuf[gTail] = sc;
gTail = next;
// E0 prefix: the following byte is an extended make/break code.
if (sc == SC1_EXT_PREFIX) {
gNextExtended = true;
return;
}
if (gCallback) gCallback(sc);
bool extended = gNextExtended;
bool is_break = (sc & SC1_BREAK_BIT) != 0;
uint8_t make = sc & SC1_MAKE_MASK;
gNextExtended = false;
// -- Update modifier state -------------------------------------------------
if (!extended) {
switch (make) {
case 0x2A: // Left Shift
gModifiers = is_break ? (gModifiers & ~MOD_LSHIFT)
: (gModifiers | MOD_LSHIFT);
return;
case 0x36: // Right Shift
gModifiers = is_break ? (gModifiers & ~MOD_RSHIFT)
: (gModifiers | MOD_RSHIFT);
return;
case 0x1D: // Left Ctrl
gModifiers = is_break ? (gModifiers & ~MOD_LCTRL)
: (gModifiers | MOD_LCTRL);
return;
case 0x38: // Left Alt
gModifiers = is_break ? (gModifiers & ~MOD_LALT)
: (gModifiers | MOD_LALT);
return;
case 0x3A: // Caps Lock – toggle on make only
if (!is_break) gModifiers ^= MOD_CAPS;
return;
case 0x45: // Num Lock – toggle on make only
if (!is_break) gModifiers ^= MOD_NUMLOCK;
return;
default:
break;
}
} else {
// Extended make codes that map to modifier keys.
switch (make) {
case 0x1D: // Right Ctrl (E0 1D)
gModifiers = is_break ? (gModifiers & ~MOD_RCTRL)
: (gModifiers | MOD_RCTRL);
return;
case 0x38: // Right Alt / AltGr (E0 38)
gModifiers = is_break ? (gModifiers & ~MOD_RALT)
: (gModifiers | MOD_RALT);
return;
default:
break;
}
// All other extended keys (arrows, Home/End, etc.) fall through
// and are enqueued as make codes for consumer-side interpretation.
}
// -- Discard break codes for non-modifier keys -----------------------------
if (is_break) return;
// -- Enqueue make code; silently drop if buffer is full --------------------
int next = (gTail + 1) & (int)KBD_BUF_MASK;
if (next != gHead) {
gBuf[gTail] = make;
gTail = next;
}
// -- Notify optional callback with the raw make code -----------------------
// Take a local snapshot so the compiler cannot reorder the NULL check and
// the call across a volatile barrier, and to avoid double-fetching.
void (*cb)(uint8_t) = (void (*)(uint8_t))gCallback;
if (cb) cb(make);
}
// -- Consumer API --------------------------------------------------------------
static bool KbdPoll(void) {
return gHead != gTail;
}
// Blocking read. Spins with PAUSE until a make code is available.
// On a preemptive kernel, replace the _mm_pause() loop with a
// condition variable or sti/hlt to avoid wasting CPU cycles.
static uint8_t KbdReadScancode(void) {
while (gHead == gTail) { _mm_pause(); }
while (gHead == gTail) {
_mm_pause();
}
uint8_t sc = gBuf[gHead];
gHead = (gHead + 1) % KBD_BUF_SIZE;
gHead = (gHead + 1) & (int)KBD_BUF_MASK;
return sc;
}
// Returns the ASCII character for the next keypress, applying the current
// Shift and CapsLock state. Returns 0 for keys with no ASCII mapping
// (function keys, arrows, unmodified Ctrl/Alt keys, etc.).
//
// CapsLock inverts shift only for alphabetic keys (a–z); digit and symbol
// keys are always governed solely by the physical Shift state.
static char KbdReadChar(void) {
uint8_t sc = KbdReadScancode();
if (sc < 128) return gScanToAscii[sc];
return 0;
uint8_t make = KbdReadScancode();
if (make >= 128) return 0;
uint32_t mods = gModifiers; // snapshot; safe outside ISR
bool shifted = (mods & MOD_SHIFT) != 0;
// CapsLock toggles the effective shift for alphabetic keys only.
if (mods & MOD_CAPS) {
char base = gUnshifted[make];
if (base >= 'a' && base <= 'z') {
shifted = !shifted;
}
}
return shifted ? gShifted[make] : gUnshifted[make];
}
static void KbdSetCallback(void (*cb)(uint8_t sc)) {
static void KbdSetCallback(void (*cb)(uint8_t)) {
gCallback = cb;
}
// Returns the current modifier bitmask (OR of MOD_* flags above).
// Snapshot is taken outside ISR and may lag by at most one interrupt.
static uint32_t KbdGetModifiers(void) {
return gModifiers;
}
// -- Initializer ---------------------------------------------------------------
void HalKeyboardInitialize(HAL_KEYBOARD *out) {
// Flush PS/2 output buffer
while (__inbyte(PS2_STAT) & 1) __inbyte(PS2_DATA);
// Drain any stale bytes sitting in the PS/2 output buffer before
// enabling interrupts, so the first ISR call sees a clean state.
while (__inbyte(PS2_STAT) & 1) {
__inbyte(PS2_DATA);
}
out->Poll = KbdPoll;
out->ReadScancode = KbdReadScancode;
out->ReadChar = KbdReadChar;
out->SetCallback = KbdSetCallback;
out->GetModifiers = KbdGetModifiers;
out->Isr = KbdIsr;
out->IrqNumber = 1;
}
+4 -4
View File
@@ -1,10 +1,10 @@
// hal_main.c - HAL initialisation and HalGetApi
#include "hal_api.h"
#include "hal_apic.h"
#include <hal_api.h>
#include <hal_apic.h>
#include <string.h>
#include <stdint.h>
#include <stdarg.h>
#include "printf.h"
#include <printf.h>
// Declared in other hal_*.c files:
void HalScreenInitialize(uintptr_t fb, int w, int h, int stride, HAL_SCREEN* out);
@@ -310,7 +310,7 @@ static void HalShutdown(void) {
if (gPm1bCntBlk) __outword(gPm1bCntBlk, valB);
// Firmware should have powered off by now. If it didn't, halt.
for (;;) __halt();
while (true) __halt();
}
// ---------------------------------------------------------------------------
+10 -3
View File
@@ -1,6 +1,6 @@
// hal_pci.c - PCI configuration space via Mechanism 1 port I/O
#include "hal_api.h"
#include <stdint.h>
#include <hal_api.h>
#define PCI_CONFIG_ADDR 0xCF8
#define PCI_CONFIG_DATA 0xCFC
@@ -55,9 +55,16 @@ static bool PciFindClass(uint8_t classCode, uint8_t subclass,
return false;
}
static uint8_t PciGetIrqLine(uint8_t bus, uint8_t dev, uint8_t fn) {
// PCI config register 0x3C: byte 0 = IRQ line, byte 1 = IRQ pin
uint32_t val = PciRead32(bus, dev, fn, 0x3C);
return (uint8_t)(val & 0xFF); // 0xFF = not connected
}
void HalPciInitialize(HAL_PCI* out) {
out->ConfigRead32 = PciRead32;
out->ConfigWrite32 = PciWrite32;
out->Read32 = PciRead32;
out->Write32 = PciWrite32;
out->FindDevice = PciFindDevice;
out->FindClass = PciFindClass;
out->GetIrqLine = PciGetIrqLine;
}
+1 -1
View File
@@ -3,10 +3,10 @@
// The 8x16 bitmap font is a standard VGA/PC font. We embed just the
// printable ASCII range (0x20–0x7E). Each glyph is 16 bytes; bit 7 of
// each byte is the leftmost pixel.
#include "hal_api.h"
#include <stdint.h>
#include <string.h>
#include <intrin.h>
#include <hal_api.h>
static uint32_t* gFb = (uint32_t *)0;
static int gWidth = 0;
+4 -4
View File
@@ -1,9 +1,9 @@
// hal_timer.c - Local APIC timer, calibrated against PIT channel 2
#include "hal_api.h"
#include "hal_apic.h"
#include "hal_debug.h"
// hal_timer.c - Local APIC timer
#include <stdint.h>
#include <intrin.h>
#include <hal_api.h>
#include <hal_apic.h>
#include <hal_debug.h>
#define LAPIC_TIMER_LVT 0x320
#define LAPIC_TIMER_INIT 0x380
+30 -7
View File
@@ -1,17 +1,40 @@
#pragma once
// io_driver.h - driver-side interface for registering disk controllers
#include "hal_api.h"
#include "ke_status.h"
#include "../kernel/io_file.h"
// io_driver.h - driver-side interface for registering disk controllers
//
// DISK_DRIVER.SubmitIrp is the async path. IRP is forward-declared here so
// this header (and hal_disk.c) remain free of kernel sync-object dependencies.
// Drivers that implement SubmitIrp include ke_irp.h themselves.
#include <stdint.h>
#include <hal_api.h>
#include <ke_status.h>
#include <io_file.h>
typedef KSTATUS (*DRIVER_ENTRY_FN)(const HAL_API* hal);
typedef void (*DRIVER_UNLOAD_FN)(void);
// Forward declaration only — avoids dragging ke_event.h into the HAL layer.
struct IRP;
typedef struct DISK_DRIVER {
KSTATUS (*ReadSectors)(uint64_t lba, uint32_t count, void* buf);
KSTATUS (*WriteSectors)(uint64_t lba, uint32_t count, const void* buf);
void *Ctx;
// --- Synchronous path (always present) ---------------------------------
KSTATUS (*ReadSectors) (void *ctx, uint64_t lba, uint32_t count, void *buf);
KSTATUS (*WriteSectors)(void *ctx, uint64_t lba, uint32_t count, const void *buf);
// --- Asynchronous path (optional) --------------------------------------
// NULL = driver has not implemented async; IoSubmitIrp() falls back to
// the sync path. Returns STATUS_PENDING when the IRP is accepted.
KSTATUS (*SubmitIrp)(void *ctx, struct IRP *irp);
uint64_t SectorCount;
} DISK_DRIVER;
KSTATUS IoRegisterDisk(uint8_t driveIndex, const DISK_DRIVER* driver);
KSTATUS IoRegisterDisk (uint8_t driveIndex, const DISK_DRIVER *driver);
// Internal dispatch used by ke_irp.c. Calls SubmitIrp if present, otherwise
// runs the sync callbacks inline and writes irp->Status; does NOT touch any
// kernel sync objects so it is safe to call from the HAL layer.
// Returns STATUS_PENDING (async path taken) or STATUS_SUCCESS (ran inline).
KSTATUS HalDiskDispatch (uint8_t drive, struct IRP *irp);