PS/2 Mouse data reporting not firing irq12

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
TimonGaertner123
Posts: 6
Joined: Thu Sep 23, 2021 3:08 am

PS/2 Mouse data reporting not firing irq12

Post by TimonGaertner123 »

Hello,

I have Interrupts working (PIT->IRQ0, Keyboard->IRQ1).
I initiate PIC and IDT before the PS2 initialization.
I use qemu as an emulator.
I set bit 1 of the PS/2 Controller Configuration Byte to one.
I reset the PS2 mouse, set it to default and activate data reporting.

Unfortunately, no IRQ12 is raised.
There are many forum articles about the same topic, but I can't see their solutions being a problem in my code.

if i move the mouse irq1 no longer works indicating that their is an irq but it's not handled right (by reading 0x60)
If i constantly read the data port (with a loop), i get the inputs from the mouse

github repo: https://github.com/TimonGaertner/yaku/tree/input-rework

Here is my ps2 init code...

Code: Select all

bool ps2_data_response_req = false; // if true don't handle irq as normal
static bool dual_channel;
static bool ps2_port1;
static bool ps2_port2;
uint8_t ps2_response_count = 0;

// Wait until the PS/2 controller's input buffer is clear.
// Use this before WRITING to the controller.
uint8_t ps2_wait_input(void) {
    uint64_t timeout = 100000UL;
    while (--timeout) {
        if (!(io_inb(PS2_STATUS) & 2)) {
            return 0;
        }
    }
    return 1;
}

// Wait until the PS/2 controller's output buffer is filled.
// Use this before READING from the controller.
uint8_t ps2_wait_output(void) {
    uint64_t timeout = 100000UL;
    while (--timeout) {
        if (io_inb(PS2_STATUS) & 1) {
            return 0;
        }
    }
    return 1;
}

void ps2_disable(void) {
    ps2_wait_input();
    io_outb(PS2_COMMAND, PS2_DISABLE_PORT1);
    ps2_wait_input();
    io_outb(PS2_COMMAND, PS2_DISABLE_PORT2);
    pic_mask_irq(1);
    pic_mask_irq(12);
}

void ps2_enable(void) {
    pic_unmask_irq(1);
    pic_unmask_irq(12);
    ps2_wait_input();
    io_outb(PS2_COMMAND, PS2_ENABLE_PORT1);
    ps2_wait_input();
    io_outb(PS2_COMMAND, PS2_ENABLE_PORT2);
}

void ps2_write_command(uint8_t cmdbyte) {
    ps2_disable();
    ps2_wait_input();
    io_outb(PS2_COMMAND, cmdbyte);
    ps2_enable();
}

uint8_t ps2_write_command_read_data(uint8_t cmdbyte) {
    ps2_write_command(cmdbyte);
    return ps2_read_data();
}

uint8_t ps2_read_status(void) {
    ps2_wait_output();
    return io_inb(PS2_STATUS);
}

uint8_t ps2_read_data(void) {
    ps2_wait_output();
    return io_inb(PS2_DATA);
}

uint8_t ps2_write_data(uint8_t cmdbyte) {
    ps2_data_response_req = true;
    ps2_wait_input();
    io_outb(PS2_DATA, cmdbyte);
    return ps2_read_data();
}

void ps2_write_command_arg(uint8_t cmdbyte, uint8_t arg) {
    ps2_write_command(cmdbyte);
    ps2_write_data(arg);
}

uint8_t ps2_write_data_arg(uint8_t cmdbyte, uint8_t arg) {
    uint8_t cmdbyte_response = ps2_write_data(cmdbyte);

    // if cmdbyte isn't acknowledged, return response
    if (cmdbyte_response != 0xFA) {
        return cmdbyte_response;
    }
    return ps2_write_data(arg);
}

void ps2_init(void) {
    ps2_write_command(PS2_DISABLE_PORT1);
    ps2_write_command(PS2_DISABLE_PORT2);

    // Clear the input buffer.
    size_t timeout = 1024;
    while ((io_inb(PS2_STATUS) & 1) && timeout > 0) {
        timeout--;
        io_inb(PS2_DATA);
    }

    if (timeout == 0) {
        // panic("ps2: prob. no existing PS/2");
        return;
    }

    // Enable interrupt lines, enable translation.
    uint8_t status = ps2_write_command_read_data(PS2_READ_CONFIG);

    status |= (PS2_PORT1_IRQ | PS2_PORT2_IRQ | PS2_PORT1_TLATE);
    ps2_write_command_arg(PS2_WRITE_CONFIG, status);
    status = ps2_write_command_read_data(PS2_READ_CONFIG);
    serial_printf("ps2: status: %d\n", status);
    if (ps2_write_command_read_data(0xAA) != 0x55) {
        // panic(ps2: self-test on init failed)
        ;
    }
    // checks if it's a dual-channel ps2-controller
    if (!(ps2_write_command_read_data(0xAE) & (1 >> 5))) {
        ps2_write_command(PS2_DISABLE_PORT2);
        dual_channel = true;
    } else {
        dual_channel = false;
    }

    // test PS/2 ports and exit init if both fail
    // port 1
    if (ps2_write_command_read_data(0xAB) == 0x00) {
        ps2_port1 = true;
    }

    if (dual_channel) {
        // port 2
        if (ps2_write_command_read_data(0xA9) == 0x00) {
            ps2_port2 = true;
        }
    }

    if (!ps2_port1 && !ps2_port2) {
        // panic(ps2 init: neither port passed test);
        return;
    }

    if (ps2_port1) {
        ps2_write_command(PS2_ENABLE_PORT1);
        ps2_write_data(0xFF);
    }

    // mouse not implemented yet
    if (ps2_port2) {
        ps2_write_command(PS2_ENABLE_PORT2);
        ps2_write_command(0xD4);
        ps2_write_data(0xFF);
        ps2_read_data();
        ps2_read_data();
        ps2_write_command(0xD4);
        ps2_write_data(0xF6);
        ps2_read_data();
        ps2_read_data();
        ps2_write_command(0xD4);
        ps2_write_data(0xF4);
        ps2_read_data();

        // serial_printf("ps2: mouse init, %d\n", ps2_read_data());
        // 0xD4: sends next byte to PS/2-Port: 2
        // ps2_write_command_arg(0xD4, 0xFF);
    }
}
c code for idt

Code: Select all

#include "idt.h"

#include <types.h>

static idt_desc_t idt[IDT_MAX_DESCRIPTORS];

static idtr_t idtr;

extern uint64_t isr_stub_table[];

void idt_set_descriptor(uint8_t vector, uintptr_t isr, uint8_t flags) {
    idt_desc_t* descriptor = &idt[vector];

    descriptor->base_low = isr & 0xFFFF;
    descriptor->cs = 5 << 3;
    descriptor->ist = 0;
    descriptor->attributes = flags;
    descriptor->base_mid = (isr >> 16) & 0xFFFF;
    descriptor->base_high = (isr >> 32) & 0xFFFFFFFF;
    descriptor->rsv0 = 0;
}

void idt_init() {
    idtr.base = (uintptr_t)&idt;
    idtr.limit = (uint16_t)sizeof(idt_desc_t) * IDT_MAX_DESCRIPTORS - 1;

    for (uint8_t vector = 0; vector < IDT_CPU_EXCEPTION_COUNT; vector++) {
        idt_set_descriptor(vector, isr_stub_table[vector], IDT_ATTRIBUTE_TRAP_GATE);
    }

    for (uint8_t vector = IDT_CPU_EXCEPTION_COUNT;
         vector < IDT_CPU_EXCEPTION_COUNT + IDT_IRQ_COUNT; vector++) {
        idt_set_descriptor(vector, isr_stub_table[vector], IDT_ATTRIBUTE_INT_GATE);
    }

    idt_reload(&idtr);
    asm volatile("sti");
}
asm code for isrs

Code: Select all

extern isr_exception_handler
extern pic_send_eoi


%macro pushagrd 0
    push rax
    push rbx
    push rcx
    push rdx
    push rsi
    push rdi
%endmacro

%macro popagrd 0
    pop rdi
    pop rsi
    pop rdx
    pop rcx
    pop rbx
    pop rax
%endmacro

%macro pushacrd 0
    mov rax, cr0
    push rax
    mov rax, cr2
    push rax
    mov rax, cr3
    push rax
    mov rax, cr4
    push rax
%endmacro

%macro popacrd 0
    pop rax
    mov cr4, rax
    pop rax
    mov cr3, rax
    pop rax
    mov cr2, rax
    pop rax
    mov cr0, rax
%endmacro

%macro isr_wrapper_before 0
    push rbp
    mov rbp, rsp
    pushagrd
    pushacrd
    mov ax, ds
    push rax
    push qword 0
    mov ax, 0x10
    lea rdi, [rsp + 0x10]
%endmacro

%macro isr_wrapper_after 0
    pop rax
    pop rax
    popacrd
    popagrd
    pop rbp
    add rsp, 0x10
    iretq
%endmacro

%macro isr_err_stub 1
isr_stub_%+%1:
    push %1
    isr_wrapper_before
    call isr_exception_handler
    isr_wrapper_after
%endmacro

%macro isr_no_err_stub 1
isr_stub_%+%1:
    push 0
    push %1
    isr_wrapper_before
    call isr_exception_handler
    isr_wrapper_after
%endmacro

%macro isr_irq_stub 2
isr_stub_%+%1:
    push 0
    push %1
    isr_wrapper_before
    call isr_irq%+%2
    mov rdi, %2
    call pic_send_eoi
    isr_wrapper_after
%endmacro


isr_no_err_stub 0
isr_no_err_stub 1
isr_no_err_stub 2
isr_no_err_stub 3
isr_no_err_stub 4
isr_no_err_stub 5
isr_no_err_stub 6
isr_no_err_stub 7
isr_err_stub    8
isr_no_err_stub 9
isr_err_stub    10
isr_err_stub    11
isr_err_stub    12
isr_err_stub    13
isr_err_stub    14
isr_no_err_stub 15
isr_no_err_stub 16
isr_err_stub    17
isr_no_err_stub 18
isr_no_err_stub 19
isr_no_err_stub 20
isr_no_err_stub 21
isr_no_err_stub 22
isr_no_err_stub 23
isr_no_err_stub 24
isr_no_err_stub 25
isr_no_err_stub 26
isr_no_err_stub 27
isr_no_err_stub 28
isr_no_err_stub 29
isr_err_stub    30
isr_no_err_stub 31
%assign vec 32
%assign irq 0
%rep 15
    extern isr_irq%+irq
    isr_irq_stub vec, irq
    %assign vec vec+1
    %assign irq irq+1
%endrep


global isr_stub_table
isr_stub_table:
    %assign i 0
    %rep 47
        dq isr_stub_%+i
        %assign i i+1
    %endrep
code for pic

Code: Select all

#include "pic.h"

#include <io.h>
#include <types.h>
#include <drivers/serial.h>

void pic_mask_irq(uint8_t irq) {
    uint16_t port;
    uint8_t value;

    if (irq < 8) {
        port = PIC_MASTER_DATA;
    } else {
        port = PIC_SLAVE_DATA;
        irq -= 8;
    }
    value = io_inb(port) | (1 << irq);
    io_outb(port, value);
}

void pic_unmask_irq(uint8_t irq) {
    uint16_t port;
    uint8_t value;

    if (irq < 8) {
        port = PIC_MASTER_DATA;
    } else {
        port = PIC_SLAVE_DATA;
        irq -= 8;
    }
    value = io_inb(port) & ~(1 << irq);
    io_outb(port, value);
}

void pic_remap_offsets(uint8_t offset) {
    uint8_t master_mask, slave_mask;

    master_mask = io_inb(PIC_MASTER_DATA);
    slave_mask = io_inb(PIC_SLAVE_DATA);

    io_outb(PIC_MASTER_COMMAND, PIC_ICW1_INIT | PIC_ICW1_ICW4);
    io_wait();
    io_outb(PIC_SLAVE_COMMAND, PIC_ICW1_INIT | PIC_ICW1_ICW4);
    io_wait();
    io_outb(PIC_MASTER_DATA, offset);
    io_wait();
    io_outb(PIC_SLAVE_DATA, offset + 0x08);
    io_wait();
    io_outb(PIC_MASTER_DATA, 0x04);
    io_wait();
    io_outb(PIC_SLAVE_DATA, 0x02);
    io_wait();

    io_outb(PIC_MASTER_DATA, PIC_ICW4_8086);
    io_wait();
    io_outb(PIC_SLAVE_DATA, PIC_ICW4_8086);
    io_wait();

    io_outb(PIC_MASTER_DATA, master_mask);
    io_outb(PIC_SLAVE_DATA, slave_mask);
}

void pic_send_eoi(uint8_t irq) {
    if (irq < 8) {
        io_outb(PIC_MASTER_COMMAND, PIC_EOI);
    }
    io_outb(PIC_SLAVE_COMMAND, PIC_EOI);
}

void pic_init(void) {
    pic_remap_offsets(0x20);
    for (uint8_t irq = 0; irq < 16; irq++) {
        pic_mask_irq(irq);
    }
    pic_unmask_irq(0);
    pic_unmask_irq(1);
    pic_unmask_irq(9);
    pic_unmask_irq(12);
}

static uint16_t __pic_get_irq_reg(uint16_t ocw3) {
    /* OCW3 to PIC CMD to get the register values.  PIC2 is chained, and
     * represents IRQs 8-15.  PIC1 is IRQs 0-7, with 2 being the chain */
    io_outb(PIC_MASTER_COMMAND, ocw3);
    io_outb(PIC_SLAVE_COMMAND, ocw3);
    return (io_inb(PIC_MASTER_COMMAND) << 8) | io_inb(PIC_SLAVE_COMMAND);
}

/* Returns the combined value of the cascaded PICs irq request register */
uint16_t pic_get_irr(void) {
    return __pic_get_irq_reg(PIC_READ_IRR);
}

/* Returns the combined value of the cascaded PICs in-service register */
uint16_t pic_get_isr(void) {
    return __pic_get_irq_reg(PIC_READ_ISR);
}
Forum Sources:
viewtopic.php?f=1&t=13156
viewtopic.php?f=1&t=27984
viewtopic.php?f=1&t=8323

Wiki sources:
https://wiki.osdev.org/%228042%22_PS/2_Controller
https://wiki.osdev.org/PS/2_Mouse
Last edited by TimonGaertner123 on Sun Mar 13, 2022 9:52 am, edited 2 times in total.
Klakap
Member
Member
Posts: 297
Joined: Sat Mar 10, 2018 10:16 am

Re: PS/2 Mouse data reporting not firing irq12

Post by Klakap »

Well, that’s too little of your code. Can you please provide link to your repository, or at least show your full code for setting interupts and PS/2?
TimonGaertner123
Posts: 6
Joined: Thu Sep 23, 2021 3:08 am

Re: PS/2 Mouse data reporting not firing irq12

Post by TimonGaertner123 »

Klakap wrote:Well, that’s too little of your code. Can you please provide link to your repository, or at least show your full code for setting interupts and PS/2?
edited my post, now there's a link to the github and some code that might be important
Octocontrabass
Member
Member
Posts: 5563
Joined: Mon Mar 25, 2013 7:01 pm

Re: PS/2 Mouse data reporting not firing irq12

Post by Octocontrabass »

You should be using a cross-compiler.

You need to pass -mno-red-zone to your compiler if you're not using the IST for every interrupt. Otherwise, interrupts will clobber the red zone. It's also a good idea to pass -mgeneral-regs-only, so that interrupts will only need to save and restore general-purpose registers.

I may have missed it, but it looks like you're not linking with libgcc.

Your "pushagrd" and "popagrd" macros are missing some registers. Interrupts will clobber some of the missing registers, leading to hard-to-diagnose bugs.

You shouldn't save and restore control registers on every interrupt. Accessing these registers is extremely slow, and most interrupts will not need to access them at all.

Having a dedicated IRQ handler stub like this won't work once you start using APICs or MSIs.

You're very unlikely to stumble across any hardware requiring this type of I/O delay in a 64-bit OS.

This function enables the keyboard and mouse ports and their respective IRQs, but it's called in several places where you don't want to do those things.

You don't need to wait for the output buffer to fill when reading the status register.

The self-test resets some PS/2 controllers to power-on defaults. Either remove the self-test or do it first so it doesn't interfere with the rest of your initialization.

What exactly is enabling the keyboard port supposed to do here? And what kind of response are you expecting?
TimonGaertner123
Posts: 6
Joined: Thu Sep 23, 2021 3:08 am

Re: PS/2 Mouse data reporting not firing irq12

Post by TimonGaertner123 »

Octocontrabass wrote:You should be using a cross-compiler.

You need to pass -mno-red-zone to your compiler if you're not using the IST for every interrupt. Otherwise, interrupts will clobber the red zone. It's also a good idea to pass -mgeneral-regs-only, so that interrupts will only need to save and restore general-purpose registers.

I may have missed it, but it looks like you're not linking with libgcc.

Your "pushagrd" and "popagrd" macros are missing some registers. Interrupts will clobber some of the missing registers, leading to hard-to-diagnose bugs.

You shouldn't save and restore control registers on every interrupt. Accessing these registers is extremely slow, and most interrupts will not need to access them at all.

Having a dedicated IRQ handler stub like this won't work once you start using APICs or MSIs.

You're very unlikely to stumble across any hardware requiring this type of I/O delay in a 64-bit OS.

This function enables the keyboard and mouse ports and their respective IRQs, but it's called in several places where you don't want to do those things.

You don't need to wait for the output buffer to fill when reading the status register.

The self-test resets some PS/2 controllers to power-on defaults. Either remove the self-test or do it first so it doesn't interfere with the rest of your initialization.

What exactly is enabling the keyboard port supposed to do here? And what kind of response are you expecting?
thanks for the helping response, i will address these issues and come back, and tell whether it helped

edit: solution was to unmask irq2
Post Reply