OSDev.org https://forum.osdev.org/ |
|
[SOLVED] IRQ1 on PS2 controller commands response https://forum.osdev.org/viewtopic.php?f=1&t=31376 |
Page 1 of 1 |
Author: | Aerath [ Sun Feb 19, 2017 12:01 pm ] |
Post subject: | [SOLVED] IRQ1 on PS2 controller commands response |
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: Quote: 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 controller, I disable both ports, flush buffers, and disable interrupts in controller's configuration byte. No IRQ fired, as expected. 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: // 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; } 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 |
Author: | Octocontrabass [ Sun Feb 19, 2017 1:07 pm ] |
Post subject: | Re: PS2 controller commands fire IRQ1 on response byte recep |
A quick glance at this disassembly of an IBM AT keyboard controller firmware suggests that the keyboard controller should raise IRQ1 for every response byte. Unfortunately, you can't tell from that if every keyboard controller will raise IRQ1 for response bytes. That's part of the reason why the wiki recommends only using commands that have response bytes while IRQs and both ports are disabled. |
Author: | Aerath [ Sun Feb 19, 2017 2:45 pm ] |
Post subject: | Re: PS2 controller commands fire IRQ1 on response byte recep |
OK, I changed my initialization function, it works now, thanks to you Maybe the WARNING in the wiki article should be mitigated ? |
Author: | Octocontrabass [ Mon Feb 20, 2017 12:43 pm ] |
Post subject: | Re: PS2 controller commands fire IRQ1 on response byte recep |
I have updated the warning in the wiki to reflect actual hardware (and Qemu) behavior. Is there any particular reason you're reading the PS/2 controller configuration byte after you've enabled the PS/2 ports? You should only need to read it when you're initializing the PS/2 controller, before you're ready to enable either port. |
Author: | Aerath [ Sat Feb 25, 2017 4:43 am ] |
Post subject: | Re: PS2 controller commands fire IRQ1 on response byte recep |
Because of enabling keyboard before configuring mouse. |
Author: | dchapiesky [ Sat Feb 25, 2017 6:42 am ] |
Post subject: | Re: PS2 controller commands fire IRQ1 on response byte recep |
could you post revised code and change to [SOLVED]? Will help others in future. Cheers |
Author: | Aerath [ Sat Feb 25, 2017 8:17 am ] |
Post subject: | Re: [SOLVED] IRQ1 on PS2 controller commands response |
New working code : Code: // ****************************************************************************
// 8042.h // **************************************************************************** #ifndef __DEVICES_8042_H__ #define __DEVICES_8042_H__ #define PS2_DATA_PORT 0x60 /* * \brief Commands for PS/2 devices, to be written to DATA port * For commands with a parameter byte (set typematic, set indicators), * controller responds with ACK after both command byte and parameter byte */ #define PS2_CMD_SET_INDICATORS 0xED #define PS2_CMD_ECHO 0xEE #define PS2_CMD_IDENTIFY 0xF2 #define PS2_CMD_SET_TYPEMATIC 0xF3 #define PS2_CMD_SCAN_EN 0xF4 #define PS2_CMD_SCAN_DS 0xF5 /** Also RESET_PARAMS **/ #define PS2_CMD_RESET_PARAMS 0xF6 /** Doesn't disable scanning **/ #define PS2_CMD_RESEND 0xFE #define PS2_CMD_RESET_TEST 0xFF #define PS2_TEST_SUCCESS 0xAA #define PS2_ECHO_RES 0xEE #define PS2_ACK 0xFA #define PS2_TEST_FAIL1 0xFC #define PS2_TEST_FAIL2 0xFD #define PS2_RESEND 0xFE // **************************************************************************** // 8042.c // **************************************************************************** #define CMD_PORT 0x64 #define STAT_PORT 0x64 #define PS2_CTRLR_READ_RAM(n) (0x20|(n&0x1F)) #define PS2_CTRLR_WRITE_RAM(n) (0x60|(n&0x1F)) #define PS2_CTRLR_P2_DS 0xA7 #define PS2_CTRLR_P2_EN 0xA8 #define PS2_CTRLR_P2_TEST 0xA9 #define PS2_CTRLR_TEST_CTRLR 0xAA #define PS2_CTRLR_P1_TEST 0xAB #define PS2_CTRLR_RAM_DUMP 0xAC #define PS2_CTRLR_P1_DS 0xAD #define PS2_CTRLR_P1_EN 0xAE #define PS2_CTRLR_READ_IN 0xC0 #define PS2_CTRLR_READ_OUT 0xD0 #define PS2_CTRLR_WRITE_OUT 0xD1 #define PS2_CTRLR_WRITE_P1OUT 0xD2 #define PS2_CTRLR_WRITE_P2OUT 0xD3 #define PS2_CTRLR_WRITE_P2IN 0xD4 #define SELF_TEST_SUCCESS 0x55 #define STAT_OUTFULL (1<<0) #define STAT_INFULL (1<<1) #define STAT_SYSTEM_FLAG (1<<2) #define STAT_FOR_CONTROLER (1<<3) #define STAT_SPECIFIC (1<<4) #define STAT_MOUSE_DATA (1<<5) #define STAT_TIMEOUT_ERR (1<<6) #define STAT_PARITY_ERR (1<<7) #define CONFIG_PORT1_INT (1<<0) #define CONFIG_PORT2_INT (1<<1) #define CONFIG_SYSTEM_FLAG (1<<2) #define CONFIG_ZERO1 (1<<3) #define CONFIG_PORT1_CLK_DS (1<<4) #define CONFIG_PORT2_CLK_DS (1<<5) #define CONFIG_PORT1_TRANSL (1<<6) #define CONFIG_ZERO2 (1<<7) 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; } uint8_t ps2_read_when(ps2dev_t* dev, uint8_t target) { while(dev->expected_bytes > target); return inb(PS2_DATA_PORT); } 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"); outb(CMD_PORT, PS2_CTRLR_P1_TEST); if(ps2_read()) panic("PS/2 port 1 test failed"); outb(CMD_PORT, PS2_CTRLR_P2_TEST); if(ps2_read()) panic("PS/2 port 2 test failed"); config |= CONFIG_PORT1_INT|CONFIG_PORT2_INT|CONFIG_PORT1_TRANSL; config &= ~ (CONFIG_PORT1_CLK_DS | CONFIG_PORT2_CLK_DS); outb(CMD_PORT, PS2_CTRLR_WRITE_RAM(0)); ps2_write(1, config); } void ps2dev_init(ps2dev_t* dev, uint8_t port) { dev->type[0] = dev->type[1] = 0xFF; 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, port == 1 ? PS2_CTRLR_P1_EN : PS2_CTRLR_P2_EN); if(ps2_cmd(dev, PS2_CMD_SCAN_DS) != PS2_ACK) { klog("PS/2 scan disable failed on port "); panic(dev->iodev.name+3); } dev->expected_bytes = 1; if(ps2_cmd(dev, PS2_CMD_RESET_TEST) != PS2_ACK || ps2_read_when(dev, 0) != PS2_TEST_SUCCESS) { klog("PS/2 reset-test failed on port "); panic(dev->iodev.name+3); } dev->expected_bytes = port == 1 ? 2 : 1; if(ps2_cmd(dev, PS2_CMD_IDENTIFY) == PS2_ACK) { dev->type[0] = ps2_read_when(dev, port == 1 ? 1 : 0); if(port == 1) { if(dev->type[0] != 0xAB) panic("Device on PS/2 port 1 is expected to be a keyboard, but doesn't seems to be"); dev->type[1] = ps2_read_when(dev, 0); } } if(port == 1 && ps2_cmd(dev, PS2_CMD_ECHO) != PS2_ECHO_RES) panic("PS/2 ECHO failed on port 1"); if(ps2_cmd(dev, PS2_CMD_SCAN_EN) != PS2_ACK) { klog("PS/2 scan enable failed on port "); panic(dev->iodev.name+3); } } // **************************************************************************** // 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; return &ps2kb.iodev; } |
Page 1 of 1 | All times are UTC - 6 hours |
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group http://www.phpbb.com/ |