Why does PS2 Mouse Interrupt is not working?

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
User avatar
gamingjam60
Member
Member
Posts: 39
Joined: Sat Aug 24, 2024 10:06 pm
Libera.chat IRC: gamingjam60
Location: India
GitHub: https://github.com/baponkar
Contact:

Why does PS2 Mouse Interrupt is not working?

Post by gamingjam60 »

I am trying to start ps2 mouse by ioapic route

mouse.c

Code: Select all

#define MOUSE_IRQ 12       // IRQ number for PS/2 mouse
#define MOUSE_VECTOR 44    // APIC interrupt vector for PS/2 mouse

#define MOUSE_CMD_PORT 0x64
#define MOUSE_DATA_PORT 0x60

extern uint64_t fb_width;
extern uint64_t fb_height;

int mouse_x = 40; // Initial cursor x position
int mouse_y = 12; // Initial cursor y position

static uint8_t mouse_cycle = 0;
static int8_t mouse_bytes[3];
static bool mouse_initialized = false;

// Mouse cursor dimensions and colors
#define CURSOR_WIDTH 8
#define CURSOR_HEIGHT 8
#define CURSOR_COLOR COLOR_RED    // Red
#define CURSOR_BG_COLOR COLOR_BLACK // Black (to erase old position)


void mouse_wait(int type) {
    int timeout = 100000; // Timeout for waiting
    if (type == 0) {
        while (--timeout && (inb(MOUSE_CMD_PORT) & 1) == 0);
    } else {
        while (--timeout && (inb(MOUSE_CMD_PORT) & 2) == 0);
    }
}


void mouse_write(uint8_t data) {
    mouse_wait(1);
    outb(MOUSE_CMD_PORT, 0xD4);
    mouse_wait(1);
    outb(MOUSE_DATA_PORT, data);
}

uint8_t mouse_read() {
    mouse_wait(0);
    return inb(MOUSE_DATA_PORT);
}



// Draws the mouse cursor at its current position.
void draw_mouse_cursor() {
    fill_rectangle(mouse_x, mouse_y, CURSOR_WIDTH, CURSOR_HEIGHT, CURSOR_COLOR);
}

// Erases the mouse cursor at the given old position.
void erase_mouse_cursor(int old_x, int old_y) {
    fill_rectangle(old_x, old_y, CURSOR_WIDTH, CURSOR_HEIGHT, CURSOR_BG_COLOR);
}

// Process incoming mouse data packets.
void mouse_handler() {
    int old_x = 40, old_y = 12;
    mouse_bytes[mouse_cycle++] = mouse_read();
    if (mouse_cycle == 3) {
        mouse_cycle = 0;
        int dx = mouse_bytes[1];
        int dy = -mouse_bytes[2];  // Y-axis is inverted in VGA

        // Update mouse position
        erase_mouse_cursor(old_x, old_y);
        mouse_x += dx;
        mouse_y += dy;

        // Clamp to screen boundaries
        if (mouse_x < 0) mouse_x = 0;
        if (mouse_y < 0) mouse_y = 0;
        if (mouse_x > (fb_width - CURSOR_WIDTH))
            mouse_x = fb_width - CURSOR_WIDTH;
        if (mouse_y > (fb_height - CURSOR_HEIGHT))
            mouse_y = fb_height - CURSOR_HEIGHT;

        draw_mouse_cursor();
        old_x = mouse_x;
        old_y = mouse_y;
    }
}

// This is the interrupt handler called by the APIC.
// It processes the mouse packet and signals the end of interrupt.
void mouseHandler(registers_t *regs) {
    printf("Mouse Interrupt: X=%d, Y=%d\n", mouse_x, mouse_y); // Debug output
    mouse_handler();
    apic_send_eoi();  // Signal end-of-interrupt for APIC
}



// Enable mouse interrupts using APIC (no PIC modifications needed)
void enable_mouse() {
    printf("Enabling mouse interrupts...\n");
    interrupt_install_handler(MOUSE_VECTOR - 32, &mouseHandler);
}

// Disable mouse interrupts
void disable_mouse() {
    interrupt_uninstall_handler(MOUSE_VECTOR);
}


// Install and initialize the PS/2 mouse device.
void mouse_install() {
    mouse_wait(1);
    outb(MOUSE_CMD_PORT, 0xA8);  // Enable auxiliary device (mouse)
    mouse_wait(1);
    outb(MOUSE_CMD_PORT, 0x20);  // Get Compaq status byte
    uint8_t status = mouse_read() | 2;
    mouse_wait(1);
    outb(MOUSE_CMD_PORT, 0x60);  // Set Compaq status byte
    mouse_write(status);
    mouse_write(0xF6);           // Set default settings
    mouse_read();                // Acknowledge
    mouse_write(0xF4);           // Enable packet streaming
    mouse_read();                // Acknowledge
    mouse_initialized = true;
}


void mouse_init() {
    asm volatile("cli");  // Disable interrupts
    configure_mouse_irq();  // Configure mouse IRQ in IOAPIC
    mouse_install();
    enable_mouse();   // Enable mouse interrupts via APIC
    asm volatile("sti");  // Enable interrupts

    clear_screen();   // Clear the screen
    draw_mouse_cursor();  // Draw the initial mouse cursor

    apic_send_eoi();  // Signal end-of-interrupt for APIC

    printf("[Info] Successfully MOUSE initialized.\n");
}
But nothing in debug print....

interrupt.c

Code: Select all

/*
This file will hold general function which will
be use in pic and apic interrupt.

The Interrupt Service Routine (ISR) is the function that is called when an cpu exception interrupt is triggered.
The Interrupt Request (IRQ) is the function that is called when an hardware interrupt is triggered.
*/

#include "../timer/apic_timer.h"
#include "../../driver/io/ports.h" // for outb
#include "../../lib/stdio.h" //for printf
#include "../../memory/paging.h" // for page_fault_handler
#include "../../lib/string.h"
#include "../../cpu/cpu.h"
#include "apic.h"

#include "interrupt.h"

#define PIC_COMMAND_MASTER 0x20
#define PIC_COMMAND_SLAVE 0xA0
#define PIC_DATA_MASTER 0x21
#define PIC_DATA_SLAVE 0xA1
#define PIC_EOI 0x20 // End of Interrupt

#define MAX_CPU_COUNT 256           // Maximum CPU cores supported

#define GET_IRQ(INT_VECTOR) (INT_VECTOR - 32)   // Get IRQ from Interrupt Vector

extern bool has_apic();
extern void idt_flush(uint64_t);
extern void load_tss(uint64_t);

int_entry_t core_int_entries[MAX_CPU_COUNT][256]; // 256 entries for each core

int_ptr_t core_int_ptr[MAX_CPU_COUNT];  // 256 entries for each core


char* exception_messages[] = {
    "Division By Zero", // 0
    "Debug", // 1
    "Non Maskable Interrupt", // 2
    "Breakpoint", // 3
    "Into Detected Overflow", // 4
    "Out of Bounds", // 5
    "Invalid Opcode", // 6
    "No Coprocessor", // 7
    "Double fault (pushes an error code)", // 8
    "Coprocessor Segment Overrun", // 9
    "Bad TSS (pushes an error code)", // 10
    "Segment not present (pushes an error code)", // 11
    "Stack fault (pushes an error code)", // 12
    "General protection fault (pushes an error code)", // 13
    "Page fault (pushes an error code)", // 14
    "Unknown Interrupt", // 15
    "Coprocessor Fault", // 16
    "Alignment Fault", // 17
    "Machine Check",  // 18
    "SIMD (SSE/AVX) error", // 19
    "Reserved", // 20
    "Reserved", // 21
    "Reserved", // 22
    "Reserved", // 23
    "Reserved", // 24
    "Reserved", // 25
    "Reserved", // 26
    "Reserved", // 27
    "Reserved", // 28
    "Reserved", // 29
    "Reserved", // 30
    "Reserved"  // 31
};

void cpu_exception_handler(registers_t *regs){
    printf("Received Interrupt : %d\n%s\nError Code : %d\nSystem Halted!\n", 
        regs->int_no, exception_messages[regs->int_no], regs->err_code);
    // debug_error_code(regs->err_code);
    halt_kernel();
}

// This gets called from our ASM interrupt handler stub.
void isr_handler(registers_t *regs)
{
    if (regs->int_no == 14) {
        page_fault_handler(regs);
        return;
    }else if(regs->int_no == 13){
        // print("General Protection Fault\n");
        // debug_error_code(regs->err_code);
        gpf_handler(regs);
        return;
    }else if(regs->int_no < 32){
        cpu_exception_handler(regs);
        return;
    }else{
        printf("Received Interrupt : %d\n", regs->int_no);
        return;
    }

    // cpu_exception_handler(regs);
}


void irq_handler(registers_t *regs)
{
    /* This is a blank function pointer */
    void (*handler)(registers_t *r);
    
    /* Find out if we have a custom handler to run for this
    *  IRQ, and then finally, run it */
    handler = interrupt_routines[regs->int_no - 32];

    if (handler)
    {
        handler(regs);
    }

    if(has_apic()){
        apic_send_eoi();
    }else{
        /* If the IDT entry that was invoked was greater than 40
        *  (meaning IRQ8 - 15), then we need to send an EOI to
        *  the slave controller */
        if (regs->int_no >= 40)
        {
            outb(PIC_COMMAND_SLAVE, PIC_EOI); /* slave */
        }

        /* In either case, we need to send an EOI to the master
        *  interrupt controller too */
        outb(PIC_COMMAND_MASTER, PIC_EOI); /* master */
    }
}



void (*interrupt_routines[224])(registers_t *) = { 0 };

void interrupt_install_handler(int int_no, void (*handler)(registers_t *r))
{
    interrupt_routines[int_no] = handler;
}

void interrupt_uninstall_handler(int int_no)
{
    interrupt_routines[int_no] = 0;
}




void core_int_set_gate(uint64_t core_id, uint8_t index, uint64_t offset, uint16_t selector, uint8_t attr){
    int_entry_t *entry = (int_entry_t *) &core_int_entries[core_id][index];

    entry->offset_1 = (uint16_t) (offset & 0xFFFF); // set lower 16 bit
    entry->offset_2 = (uint16_t) (offset >> 16) & 0xFFFF; // set 16 bit
    entry->offset_3 = (uint32_t) (offset >> 32) & 0xFFFFFFFF; // set upper 32 bit

    entry->selector = selector;                   // set 16 bit of selector
    //              |P|DPL|R|TYPE|
    // for x86_64 : |1|00 |0|1110| ==> 10001110 ==> 0x8E
    entry->type_attributes = attr;    // set 8 bit  of P(1 bit) + DPL(2 bit) + gate type(4 bit) + 0(1 bit)
    
    entry->ist = 0; // disabled ist i.e clear 3 bit of ist and 5 bit of reserved field 
    entry->zero = 0; // set top 32 bit to zero
}


void set_core_descriptor_table(uint64_t core_id){

    // Setting Interrupts Service Routine Gate(ISR Gate)
    // https://stackoverflow.com/questions/9113310/segment-selector-in-ia-32
    // selector = 0x08 = 0b1000, 64-bit Interrupt Gate => attr = 0x8E = 1 0 00 1110, (p=0b1,0b0, dpl=0b00, gate type=0b1110)
    // selector value is 1000 because GDT code segment index is 1
    // selector = index + table_to_use + privilege
    // selector  = 1<<3(index 1) + 0<<2(TI for GDT 0) + 0<<1(for ring 0) => 1000 + 000 + 00 = 1000 = 0x08

    core_int_set_gate(core_id,  0, (uint64_t)&isr0 ,  0x8, 0x8E);    // Division By Zero
    core_int_set_gate(core_id,  1, (uint64_t)&isr1 ,  0x8, 0x8E);    // Debug
    core_int_set_gate(core_id,  2, (uint64_t)&isr2 ,  0x8, 0x8E);    // Non Maskable Interrupt  
    core_int_set_gate(core_id,  3, (uint64_t)&isr3 ,  0x8, 0x8E);    // Breakpoint 
    core_int_set_gate(core_id,  4, (uint64_t)&isr4 ,  0x8, 0x8E);    // Into Detected Overflow
    core_int_set_gate(core_id,  5, (uint64_t)&isr5 ,  0x8, 0x8E);    // Out of Bounds
    core_int_set_gate(core_id,  6, (uint64_t)&isr6 ,  0x8, 0x8E);    // Invalid Opcode
    core_int_set_gate(core_id,  7, (uint64_t)&isr7 ,  0x8, 0x8E);    // No Coprocessor
    core_int_set_gate(core_id,  8, (uint64_t)&isr8 ,  0x8, 0x8E);    // Double fault (pushes an error code)
    core_int_set_gate(core_id,  9, (uint64_t)&isr9 ,  0x8, 0x8E);    // Coprocessor Segment Overrun
    core_int_set_gate(core_id, 10, (uint64_t)&isr10 , 0x8, 0x8E);    // Bad TSS (pushes an error code)
    core_int_set_gate(core_id, 11, (uint64_t)&isr11 , 0x8, 0x8E);    // Segment not present (pushes an error code)
    core_int_set_gate(core_id, 12, (uint64_t)&isr12 , 0x8, 0x8E);    // Stack fault (pushes an error code)
    core_int_set_gate(core_id, 13, (uint64_t)&isr13 , 0x8, 0x8E);    // General protection fault (pushes an error code)
    core_int_set_gate(core_id, 14, (uint64_t)&isr14 , 0x8, 0x8E);    // Page fault (pushes an error code)
    core_int_set_gate(core_id, 15, (uint64_t)&isr15 , 0x8, 0x8E);    // Unknown Interrupt
    core_int_set_gate(core_id, 16, (uint64_t)&isr16 , 0x8, 0x8E);    // Coprocessor Fault
    core_int_set_gate(core_id, 17, (uint64_t)&isr17 , 0x8, 0x8E);    // Alignment Fault
    core_int_set_gate(core_id, 18, (uint64_t)&isr18 , 0x8, 0x8E);    // Machine Check
    core_int_set_gate(core_id, 19, (uint64_t)&isr19 , 0x8, 0x8E);    // SIMD (SSE/AVX) error
    core_int_set_gate(core_id, 20, (uint64_t)&isr20 , 0x8, 0x8E);    // Reserved
    core_int_set_gate(core_id, 21, (uint64_t)&isr21 , 0x8, 0x8E);    // Reserved
    core_int_set_gate(core_id, 22, (uint64_t)&isr22 , 0x8, 0x8E);    // Reserved
    core_int_set_gate(core_id, 23, (uint64_t)&isr23 , 0x8, 0x8E);    // Reserved
    core_int_set_gate(core_id, 24, (uint64_t)&isr24 , 0x8, 0x8E);    // Reserved
    core_int_set_gate(core_id, 25, (uint64_t)&isr25 , 0x8, 0x8E);    // Reserved
    core_int_set_gate(core_id, 26, (uint64_t)&isr26 , 0x8, 0x8E);    // Reserved
    core_int_set_gate(core_id, 27, (uint64_t)&isr27 , 0x8, 0x8E);    // Reserved
    core_int_set_gate(core_id, 28, (uint64_t)&isr28 , 0x8, 0x8E);    // Reserved
    core_int_set_gate(core_id, 29, (uint64_t)&isr29 , 0x8, 0x8E);    // Reserved
    core_int_set_gate(core_id, 30, (uint64_t)&isr30 , 0x8, 0x8E);    // Reserved
    core_int_set_gate(core_id, 31, (uint64_t)&isr31 , 0x8, 0x8E);    // Reserved

    core_int_set_gate(core_id, 32, (uint64_t)&irq0, 0x08, 0x8E);    // Timer Interrupt, IRQ0
    core_int_set_gate(core_id, 33, (uint64_t)&irq1, 0x08, 0x8E);    // Keyboard Interrupt, IRQ1
    core_int_set_gate(core_id, 34, (uint64_t)&irq2, 0x08, 0x8E);    // Cascade (for PIC chaining), IRQ2
    core_int_set_gate(core_id, 35, (uint64_t)&irq3, 0x08, 0x8E);    // COM2 (Serial Port 2), IRQ3
    core_int_set_gate(core_id, 36, (uint64_t)&irq4, 0x08, 0x8E);    // COM1 (Serial Port 1), IRQ4
    core_int_set_gate(core_id, 37, (uint64_t)&irq5, 0x08, 0x8E);    // LPT2 (Parallel Port 2) or Sound Card, IRQ5
    core_int_set_gate(core_id, 38, (uint64_t)&irq6, 0x08, 0x8E);    // Floppy Disk Controller, IRQ6
    core_int_set_gate(core_id, 39, (uint64_t)&irq7, 0x08, 0x8E);    // LPT1 (Parallel Port 1) / Spurious IRQ, IRQ7
    core_int_set_gate(core_id, 40, (uint64_t)&irq8, 0x08, 0x8E);    // Real-Time Clock (RTC), IRQ8
    core_int_set_gate(core_id, 41, (uint64_t)&irq9, 0x08, 0x8E);    // ACPI / General system use, IRQ9
    core_int_set_gate(core_id, 42, (uint64_t)&irq10, 0x08, 0x8E);   // Available (often used for SCSI or NIC), IRQ10
    core_int_set_gate(core_id, 43, (uint64_t)&irq11, 0x08, 0x8E);   // Available (often used for PCI devices), IRQ11
    core_int_set_gate(core_id, 44, (uint64_t)&irq12, 0x08, 0x8E);   // PS/2 Mouse, IRQ12
    core_int_set_gate(core_id, 45, (uint64_t)&irq13, 0x08, 0x8E);   // FPU / Floating-Point Unit (Coprocessor), IRQ13
    core_int_set_gate(core_id, 46, (uint64_t)&irq14, 0x08, 0x8E);   // Primary ATA Hard Disk Controller, IRQ14
    core_int_set_gate(core_id, 47, (uint64_t)&irq15, 0x08, 0x8E);   // Secondary ATA Hard Disk Controller, IRQ15

    core_int_set_gate(core_id, 48, (uint64_t)&irq16, 0x08, 0x8E);   // APIC Timer, IRQ16
    core_int_set_gate(core_id, 49, (uint64_t)&irq17, 0x08, 0x8E);   // HPET Timer, IRQ17
    core_int_set_gate(core_id, 50, (uint64_t)&irq18, 0x08, 0x8E);   // Available, IRQ18
}


void init_core_interrupt(uint64_t core_id){
    asm volatile("cli");

    // Setting up the Interrupt Descriptor Table Register
    core_int_ptr[core_id].limit = (sizeof(int_entry_t) * 256) - 1;
    core_int_ptr[core_id].base  = (uint64_t) &core_int_entries[core_id];

    // for safety clearing memories
    memset((void *)&core_int_entries[core_id], 0, (size_t) (sizeof(int_entry_t) * 256));

    // Setting up the Interrupt Descriptor Table
    set_core_descriptor_table(core_id);

    // Load the core's IDT
    idt_flush((uint64_t) &core_int_ptr[core_id]);
   
    asm volatile("sti");
    printf("[Info] Successfully CPU %d Interrupt Initialized.\n", core_id);
}


void init_bootstrap_interrupt(int bootstrap_core_id){
    init_core_interrupt(bootstrap_core_id);
    
    printf("[Info] Successfully Bootstrap CPU %d Interrupt Initialized.\n", bootstrap_core_id);
}


void init_application_core_interrupt(int start_core_id, int end_core_id){
    for (int core_id = start_core_id; core_id <= end_core_id; core_id++) {
        init_core_interrupt(core_id);
        printf("[Info] Successfully Application CPU %d Interrupt Initialized.\n", core_id);
    }
}


// Testing Interrupts and Debugging
void test_interrupt() {
    // printf("Testing Interrupts\n");
    // asm volatile ("div %b0" :: "a"(0)); // Int no 0
    // asm volatile ("int $0x3");   // Breakpoint int no : 3
    // asm volatile ("int $0x0");   // Division By Zero, int no : 0
    // asm volatile ("int $0xE");   // Page Fault Request, int no: 14
    // asm volatile("int $0xF");    // int no 15
    // asm volatile("int $0x10");   // int no 16
    // asm volatile("int $0x11");   // int no 17
    // asm volatile ("int $0x20");  // Interrupt Request, int no: 32, Timer Interrupt 
    asm volatile ("int $0x21");  // Interrupt Request, int no : 33, Keyboard Interrupt
    // asm volatile ("int $0x22");  // Interrupt Request, int no: 34
    // asm volatile ("int $0x30");     // Interrupt Request, int no: 48
}

void gpf_handler(registers_t *regs){
    printf("recieved interrupt: %d\n", regs->int_no);
    printf("%s\n", exception_messages[regs->int_no]);
    printf("Error Code: %x\n", regs->err_code);
    printf("CS: %x, RIP : %x\n", regs->iret_cs, regs->iret_rip);

    uint64_t *rsp = (uint64_t *)regs->iret_rsp;
    printf("Stack Contents RSP : %x\n", (uint64_t)rsp);
    
    for (int i = 0; i < 26; i++) {
        printf("  [%x] = %x\n", (uint64_t)(rsp + i), rsp[i] );
    }

    // debug_error_code(regs->err_code);
    printf("System Halted!\n");
    halt_kernel();   
}

void debug_error_code(int err_code) {
    // Print the raw error code in decimal and hexadecimal
    printf("Error Code (Decimal): %d\n", err_code);
    printf("Error Code (Hexadecimal): %d\n", err_code);
    // Decode the error code bits
    printf("Decoding Error Code:\n");

    // Bit 0: External Event
    if (err_code & 0x1) {
        printf("  - External: The fault was caused by an external event (e.g., hardware interrupt).\n");
    } else {
        printf("  - External: The fault was not caused by an external event.\n");
    }

    // Bit 1: Table Indicator (GDT/LDT)
    if (err_code & 0x2) {
        printf("  - Table: The fault involves the Local Descriptor Table (LDT).\n");
    } else {
        printf("  - Table: The fault involves the Global Descriptor Table (GDT).\n");
    }

    // Bit 2: Index (IDT/GDT)
    if (err_code & 0x4) {
        printf("  - Index: The fault involves an Interrupt Descriptor Table (IDT) entry.\n");
    } else {
        printf("  - Index: The fault involves a segment selector in the GDT/LDT.\n");
    }

    // Bits 3-15: Segment Selector Index
    int selector_index = (err_code >> 3) & 0x1FFF; // Extract bits 3-15
    printf("  - Selector Index: %d\n", selector_index);

    // Additional information based on the selector index
    if (selector_index == 0) {
        printf("    - Null Selector: The fault was caused by a null segment selector.\n");
    } else {
        printf("    - The fault was caused by a non-null segment selector.\n");
    }

    // Print a summary of the error
    printf("\nSummary:\n");
    if (err_code & 0x4) {
        printf("The fault likely occurred due to an invalid or misconfigured IDT entry.\n");
    } else {
        printf("The fault likely occurred due to an invalid or misconfigured GDT/LDT entry.\n");
    }

    printf("Check the segment selector index (%d)\n", selector_index);
}


ioapic.c is

Code: Select all

/*
IOAPIC (I/O Advanced Programmable Interrupt Controller) is a part of the Intel APIC architecture. 
It is used to route interrupts from I/O devices to the CPU. 
It is a separate chip on the motherboard and is connected to the CPU through the system bus

https://wiki.osdev.org/IOAPIC
*/

#include "../../lib/stdio.h"
#include "../../driver/io/ports.h"
#include "ioapic.h"

// Offsets for IOAPIC registers
#define IOAPIC_REGSEL   0x00
#define IOAPIC_WINDOW   0x10

// The above registers used to detect below registers
#define IOAPICID        0x00    // E/W
#define IOAPICVER       0x01    // R/O
#define IOAPICARB       0x02    // R/O
#define IOAPICREDTBL    0x10    // R/W IOAPIC Redirection Table

// IOAPIC Redirection Entry Flags
#define IOAPIC_FIXED        (0 << 8)    // Fixed delivery mode
#define IOAPIC_LOWEST       (1 << 8)    // Lowest priority delivery mode
#define IOAPIC_SMI          (2 << 8)    // System Management Interrupt
#define IOAPIC_NMI          (4 << 8)    // Non-Maskable Interrupt
#define IOAPIC_INIT         (5 << 8)    // INIT delivery mode
#define IOAPIC_EXTINT       (7 << 8)    // External Interrupt (for legacy PIC)

#define IOAPIC_LOW_ACTIVE   (1 << 13)   // Active low polarity
#define IOAPIC_HIGH_ACTIVE  (0 << 13)   // Active high polarity (default)

#define IOAPIC_EDGE_TRIG    (0 << 15)   // Edge-triggered mode (default)
#define IOAPIC_LEVEL_TRIG   (1 << 15)   // Level-triggered mode

#define IOAPIC_MASKED       (1 << 16)   // Interrupt masked (disabled)
#define IOAPIC_UNMASKED     (0 << 16)   // Interrupt unmasked (enabled)




// Global IOAPIC base address (set in madt.c)
uint32_t ioapic_addr;   

// Write to an IOAPIC register (indirect access)
void ioapic_write_reg(uint32_t reg, uint32_t value) {
    // Write the register index to the register select
    *((volatile uint32_t*)(ioapic_addr + IOAPIC_REGSEL)) = reg;
    // Write the value to the window register
    *((volatile uint32_t*)(ioapic_addr + IOAPIC_WINDOW)) = value;
}

// Read from an IOAPIC register (indirect access)
uint32_t ioapic_read_reg(uint32_t reg) {
    // Write the register index to the register select
    *((volatile uint32_t*)(ioapic_addr + IOAPIC_REGSEL)) = reg;
    // Read the value from the window register
    return *((volatile uint32_t*)(ioapic_addr + IOAPIC_WINDOW));
}

void enable_ioapic_mode() {
    // These outb() calls appear to enable IOAPIC mode;
    // ensure that the I/O port addresses and values are correct for KeblaOS.
    outb(0x22, 0x70);  // Select the IOAPIC indirect register
    outb(0x23, 0x01);  // Set the IOAPIC indirect register value to 1
}

/*
    Bit(s)	Field Name	        Description
    0-7	    Vector	            Interrupt vector number (IDT entry)
    8-10	Delivery Mode	    Specifies how the interrupt is delivered
    11	    Destination Mode	0 = Physical, 1 = Logical
    12	    Delivery Status	    0 = Idle, 1 = Send Pending (Read-Only)
    13	    Polarity	        0 = High Active, 1 = Low Active
    14	    Remote IRR	        0 = No interrupt pending, 1 = Interrupt waiting (Read-Only)
    15	    Trigger Mode	    0 = Edge Triggered, 1 = Level Triggered
    16	    Mask	            0 = Enabled, 1 = Disabled
*/

void ioapic_route_irq(uint8_t irq, uint8_t apic_id, uint8_t vector, uint32_t flags) {
    uint32_t reg_low  = IOAPICREDTBL + irq * 2;
    uint32_t reg_high = reg_low + 1;

    // High DWORD: Set destination APIC ID
    ioapic_write_reg(reg_high, (apic_id << 24));

    // Low DWORD: Vector + Flags (e.g., trigger mode, polarity)
    uint32_t low = vector | flags;
    ioapic_write_reg(reg_low, low);
}
 
// Map IRQ12 (PS/2 mouse) to interrupt vector 44 via IOAPIC
void configure_mouse_irq() {
    // Flags: Edge-triggered, Active High, Unmasked, Fixed delivery
    uint32_t flags = IOAPIC_EDGE_TRIG | IOAPIC_HIGH_ACTIVE | IOAPIC_FIXED | IOAPIC_UNMASKED;
    
    // Route IRQ12 to vector 44 for APIC ID 0 (bootstrap CPU)
    ioapic_route_irq(12, 0, 44, flags);
}

void configure_keyboard_irq(){
    // Flags: Edge-triggered, Active High, Unmasked, Fixed delivery
    uint32_t flags = IOAPIC_EDGE_TRIG | IOAPIC_HIGH_ACTIVE | IOAPIC_FIXED | IOAPIC_UNMASKED;
    
    // Route IRQ12 to vector 44 for APIC ID 0 (bootstrap CPU)
    ioapic_route_irq(1, 0, 33, flags);
}
Why mouse is not printing or showing any output in irq12?
techdude17
Member
Member
Posts: 35
Joined: Fri Dec 23, 2022 1:06 pm

Re: Why does PS2 Mouse Interrupt is not working?

Post by techdude17 »

You're going to need to provide some more info on this. Try running your code in Bochs, then use the 'info idt' (for further help you can enable I/O APIC debugging in the config file).
Octocontrabass
Member
Member
Posts: 5754
Joined: Mon Mar 25, 2013 7:01 pm

Re: Why does PS2 Mouse Interrupt is not working?

Post by Octocontrabass »

The PS/2 controller is shared by both devices plugged into it. You should split that code into its own driver specifically for the PS/2 controller so the mouse and keyboard drivers can both communicate through the PS/2 controller driver.

This may be related to the problems you're having, since your mouse initialization will interfere with the keyboard. More information from a virtual machine (e.g. QEMU's "info pic") would help.
User avatar
gamingjam60
Member
Member
Posts: 39
Joined: Sat Aug 24, 2024 10:06 pm
Libera.chat IRC: gamingjam60
Location: India
GitHub: https://github.com/baponkar
Contact:

Re: Why does PS2 Mouse Interrupt is not working?

Post by gamingjam60 »

Changing from mouse_write(status); into outb(MOUSE_DATA_PORT, status); and adding mouse_wait(1); works for me
Post Reply