Skip to content

Driver API

NtinosTheGamer2324 edited this page Feb 11, 2026 · 3 revisions

Driver API

API for developing hardware drivers in ModuOS.

SQRM (SQRELFM) Third‑Party Driver SDK (Loadable Kernel Modules)

ModuOS supports third‑party loadable kernel modules called SQRM modules. These are ELF64 ET_DYN shared-object style binaries (file extension .sqrm) that are loaded by the kernel module loader at boot.

Where modules live

Modules are searched under:

  • /ModuOS/System64/md/ (on the selected boot filesystem)

On the ISO this corresponds to:

  • targets/AMD64/iso/ModuOS/System64/md/

Required symbols / entry points

A valid module must export:

  1. A module descriptor symbol named sqrm_module_desc
#include "sdk/sqrm_sdk.h"  // or "moduos/kernel/sqrm.h" in-tree

// ABI v1 descriptor (no dependencies)
SQRM_DEFINE_MODULE(SQRM_TYPE_USB, "hello");

// ABI v2 descriptor (dependencies + class/subclass)
static const char * const deps[] = { "uhci" };
SQRM_DEFINE_MODULE_V2(SQRM_TYPE_USB, "usb", 1, 0, 1, deps);
  1. An entry point:
int sqrm_module_init(const sqrm_kernel_api_t *api);

The kernel passes a pointer to a kernel API table (sqrm_kernel_api_t) containing function pointers you can call (logging, driver registration, etc.). Always check ABI compatibility:

if (!api || api->abi_version != SQRM_ABI_VERSION) return -1;

Module types

Common module types:

  • SQRM_TYPE_FS – filesystem drivers (example: modules/ext2_sqrm.c)
  • SQRM_TYPE_AUDIO – audio drivers (example: modules/ac97_audio_sqrm.c)
  • SQRM_TYPE_USB – USB drivers (core, controllers, classes)
  • SQRM_TYPE_GPU – framebuffer GPU/display drivers (examples: QXL, VMSVGA, MD VGA Compatible Display Driver)
  • SQRM_TYPE_NET – network/NIC drivers (skeleton template: sdk/template_net_skeleton/)
  • SQRM_TYPE_HID – Human Interface Device drivers (keyboard/mouse/gamepads; often depends on USB)
  • SQRM_TYPE_GENERIC – generic modules (not bound to a specific subsystem)

Exporting APIs (SQRM Services)

Some modules (notably NET/USB/HID) are most useful when they export a service API that the kernel or other modules can use.

The kernel provides a small named service registry:

  • api->sqrm_service_register("name", api_ptr, sizeof(*api_ptr))
  • api->sqrm_service_get("name", &size)

Conventions (current):

  • "net" exports sqrm_net_api_v1_t
  • "usb" exports sqrm_usb_api_v1_t
  • "hid" exports sqrm_hid_api_v1_t

A skeleton module should export a stub API that returns -ENOSYS for unimplemented operations, so third-party drivers have a stable surface to build against.

Building a .sqrm

You can build third‑party modules out-of-tree using the SDK in sdk/.

Minimal build flags (from sdk/template_hello/build.sh):

x86_64-elf-gcc -I sdk -ffreestanding -fPIC -mno-red-zone -nostdlib \
  -Wl,-shared -Wl,-e,sqrm_module_init \
  hello_sqrm.c -o hello.sqrm

Notes:

  • -fPIC is required.
  • -mno-red-zone is required for interrupt-safe kernel code.
  • The link must produce ET_DYN (-shared).
  • The entry point must be sqrm_module_init.

Installing / testing

  1. Copy your module into the ISO tree:
  • targets/AMD64/iso/ModuOS/System64/md/<name>.sqrm
  1. Rebuild the ISO.

  2. Boot and check COM logs for module loader output:

  • [SQRM] Loading module: ...

Examples

  • In-tree minimal module: modules/hello_sqrm.c
  • SDK template: sdk/template_hello/hello_sqrm.c
  • Filesystem module: modules/ext2_sqrm.c

Driver Structure

typedef struct {
    const char *name;
    int (*init)(void);
    int (*probe)(void);
    void (*remove)(void);
} driver_t;

PCI Driver

PCI Device Structure

typedef struct {
    uint8_t bus;
    uint8_t device;
    uint8_t function;
    uint16_t vendor_id;
    uint16_t device_id;
    uint8_t class_code;
    uint8_t subclass;
    uint32_t bar[6];
} pci_device_t;

PCI Functions

// Find device by vendor/device ID
pci_device_t* pci_find_device(uint16_t vendor_id, uint16_t device_id);

// Find device by class
pci_device_t* pci_find_class(uint8_t class_code, uint8_t subclass);

// Enable bus mastering
void pci_enable_bus_mastering(pci_device_t *dev);

// Read BAR
uint32_t pci_read_bar(pci_device_t *dev, int bar_num);

Example PCI Driver

int mydriver_init(void) {
    // Find device
    pci_device_t *dev = pci_find_device(VENDOR_ID, DEVICE_ID);
    if (!dev) {
        return -1;
    }
    
    // Enable device
    pci_enable_bus_mastering(dev);
    pci_enable_memory_space(dev);
    
    // Map BARs
    uint32_t bar0 = pci_read_bar(dev, 0);
    
    // Initialize hardware
    // ...
    
    return 0;
}

IRQ Handling

Register IRQ Handler

void irq_register_handler(int irq, irq_handler_t handler);

void my_irq_handler(registers_t *regs) {
    // Handle interrupt
    // ...
}

// In driver init:
irq_register_handler(IRQ_NUM, my_irq_handler);

I/O Operations

Port I/O

// Read from I/O port
uint8_t inb(uint16_t port);
uint16_t inw(uint16_t port);
uint32_t inl(uint16_t port);

// Write to I/O port
void outb(uint16_t port, uint8_t value);
void outw(uint16_t port, uint16_t value);
void outl(uint16_t port, uint32_t value);

Memory-Mapped I/O

// Map physical address to virtual
void *ioremap(uint64_t phys_addr, size_t size);

// Access memory-mapped registers
volatile uint32_t *regs = (volatile uint32_t*)ioremap(bar0, 0x1000);
uint32_t status = regs[0];
regs[1] = command;

Next Steps

Clone this wiki locally