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);
}
}
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");
}
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: 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);
}
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