[SOLVED] IRQ1 on PS2 controller commands response
Posted: Sun Feb 19, 2017 12:01 pm
Hello, I have some problems writing my PS/2 mouse driver. Whenever the controller responds to a command (read byte 0 here) with both port 1 and interrupts for port 1 enabled in controller's configuration, it fire IRQ1.
From the wiki:
When I initialize the first PS/2 device, I unmask IRQ1 and link it to my ps2kb_handler, read controller's configuration, test the device, and enable interrupts, clock and translation for the first port in controller's configuration. Still no IRQ, everything is okay. But then, I enable first port and read the controller's configuration byte. As soon as I outb'ed 0x20, IRQ1 is fired, calling my handler. That's was not expected, but no problems for now (I have a counter for bytes to be ignored, ie manually read, not added to keyboard event buffer). So my PS/2 keyboard driver is kind of working.
But when I try to set up the PS/2 mouse, same problem occurs : I unmask IRQ12, link it to ps2mouse_handler, and try to read controller's configuration. Again, as soon as the command is sent to controller, IRQ1 is fired, probably because the response is ready. And so the configuration byte ends up being handled as a keystroke. After that the kernel hangs infinitely polling in ps2_read, waiting for a response that was intercepted.
If I try to initialize the mouse before the keyboard, IRQ12 is fired on controller's response if mouse and mouse interrupts are enabled (in controller's configuration) and I ends up with the same problem (but reversed) : with unwanted IRQ12 while setting up keyboard (and IRQ12 while setting up mouse, but handled by the same workaround).
Thanks for reading me and have a nice day !
PS: I have checked with GDB that the correct bytes are sent and received, no problem with defines.
PPS: This happens on Qemu
From the wiki:
When I initialize the controller, I disable both ports, flush buffers, and disable interrupts in controller's configuration byte. No IRQ fired, as expected.If you send a command to the PS/2 controller that involves a response, the PS/2 controller will put a "response byte" into the buffer and won't generate any IRQ
When I initialize the first PS/2 device, I unmask IRQ1 and link it to my ps2kb_handler, read controller's configuration, test the device, and enable interrupts, clock and translation for the first port in controller's configuration. Still no IRQ, everything is okay. But then, I enable first port and read the controller's configuration byte. As soon as I outb'ed 0x20, IRQ1 is fired, calling my handler. That's was not expected, but no problems for now (I have a counter for bytes to be ignored, ie manually read, not added to keyboard event buffer). So my PS/2 keyboard driver is kind of working.
But when I try to set up the PS/2 mouse, same problem occurs : I unmask IRQ12, link it to ps2mouse_handler, and try to read controller's configuration. Again, as soon as the command is sent to controller, IRQ1 is fired, probably because the response is ready. And so the configuration byte ends up being handled as a keystroke. After that the kernel hangs infinitely polling in ps2_read, waiting for a response that was intercepted.
Code: Select all
// 8042.c
uint8_t ps2_read(void)
{
do ctrlr_status = inb(STAT_PORT);
while(!(ctrlr_status & STAT_OUTFULL));
uint8_t data = inb(PS2_DATA_PORT);
ctrlr_status = inb(STAT_PORT);
return data;
}
void ps2_write(uint8_t port, uint8_t data)
{
if(port==2)
outb(CMD_PORT, PS2_CTRLR_WRITE_P2IN);
do ctrlr_status = inb(STAT_PORT);
while(ctrlr_status & STAT_INFULL);
outb(PS2_DATA_PORT, data);
}
void ps2_init(void)
{
outb(CMD_PORT, PS2_CTRLR_P1_DS);
outb(CMD_PORT, PS2_CTRLR_P2_DS);
// Flush out buffers
ctrlr_status = inb(STAT_PORT);
while(ctrlr_status & STAT_OUTFULL)
{
inb(PS2_DATA_PORT);
ctrlr_status = inb(STAT_PORT);
}
outb(CMD_PORT, PS2_CTRLR_READ_RAM(0));
uint8_t config = ps2_read();
if(~config & CONFIG_PORT2_CLK_DS)
panic("Single channel PS/2 controller not supported");
outb(CMD_PORT, PS2_CTRLR_TEST_CTRLR);
if(ps2_read() != SELF_TEST_SUCCESS)
panic("PS/2 controller self test failed");
config &= ~(CONFIG_PORT1_INT|CONFIG_PORT2_INT|CONFIG_PORT1_TRANSL);
outb(CMD_PORT, PS2_CTRLR_WRITE_RAM(0));
ps2_write(1, config);
}
void ps2dev_init(ps2dev_t* dev, uint8_t port)
{
memcpy(dev->iodev.name, "PS2", 3);
dev->iodev.name[3] = '0'+port;
dev->iodev.name[4] = 0;
dev->iodev.active = false;
dev->iodev.data = dev;
dev->iodev.read = NULL;
dev->iodev.write = NULL;
dev->iodev.prev = NULL;
dev->iodev.next = NULL;
outb(CMD_PORT, PS2_CTRLR_READ_RAM(0)); // IRQ1 fired here, when port==2
uint8_t config = ps2_read(); // Kernel hangs here when port==2, waiting for data in the do-while loop
outb(CMD_PORT, port == 1 ? PS2_CTRLR_P1_TEST : PS2_CTRLR_P2_TEST);
if(ps2_read())
{
klog("PS/2 port ");
klog(dev->iodev.name+3);
panic(" test failed");
}
config &= ~ (port == 1 ? CONFIG_PORT1_CLK_DS : CONFIG_PORT2_CLK_DS);
config |= port == 1 ? CONFIG_PORT1_INT | CONFIG_PORT1_TRANSL : CONFIG_PORT2_INT;
outb(CMD_PORT, PS2_CTRLR_WRITE_RAM(0));
ps2_write(1, config);
outb(CMD_PORT, port == 1 ? PS2_CTRLR_P1_EN : PS2_CTRLR_P2_EN);
dev->expected_bytes = 1;
outb(CMD_PORT, PS2_CTRLR_READ_RAM(0)); // First IRQ1 fired between this line and the next one, when port==1
config = ps2_read();
}
// ps2kb.c
iodev_t* ps2kb_init(void)
{
rbuf.data_end = rbuf.data_start = rbuf.buf_start = buffer;
rbuf.buf_end = buffer+sizeof(buffer);
install_irq_handler(1, ps2kb_handler);
unmask_irq(1);
ps2dev_init(&ps2kb, 1);
ps2kb.iodev.read = io_ps2kb_read;
if(ps2_cmd(&ps2kb, PS2_CMD_SCAN_DS) != PS2_ACK)
panic("PS2 scan disable failed");
ps2kb.expected_bytes++;
if(ps2_cmd(&ps2kb, PS2_CMD_RESET_TEST) != PS2_ACK || ps2_read() != PS2_TEST_SUCCESS)
panic("PS2 self-test failed");
ps2kb.expected_bytes++;
if(ps2_cmd(&ps2kb, PS2_CMD_IDENTIFY) == PS2_ACK)
{
uint8_t prev = ps2kb.expected_bytes ++;
if((type[0] = ps2_read()) == 0xAB)
{
while(ps2kb.expected_bytes > prev);
type[1] = ps2_read();
}
else panic("Device on PS/2 port 1 is expected to be a keyboard, but doesn't seems to be one");
}
if(ps2_cmd(&ps2kb, PS2_CMD_ECHO) != PS2_ECHO_RES)
panic("PS/2 ECHO failed on port 1");
if(ps2_cmd(&ps2kb, PS2_CMD_SCAN_EN) != PS2_ACK)
panic("PS/2 scan enable failed port1");
return &ps2kb.iodev;
}
Thanks for reading me and have a nice day !
PS: I have checked with GDB that the correct bytes are sent and received, no problem with defines.
PPS: This happens on Qemu