OSDev.org

The Place to Start for Operating System Developers
It is currently Thu Mar 28, 2024 8:19 am

All times are UTC - 6 hours




Post new topic Reply to topic  [ 7 posts ] 
Author Message
 Post subject: [SOLVED] IRQ1 on PS2 controller commands response
PostPosted: Sun Feb 19, 2017 12:01 pm 
Offline

Joined: Thu Jun 16, 2016 6:03 am
Posts: 10
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


Last edited by Aerath on Sat Feb 25, 2017 8:02 am, edited 2 times in total.

Top
 Profile  
 
 Post subject: Re: PS2 controller commands fire IRQ1 on response byte recep
PostPosted: Sun Feb 19, 2017 1:07 pm 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 5099
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.


Top
 Profile  
 
 Post subject: Re: PS2 controller commands fire IRQ1 on response byte recep
PostPosted: Sun Feb 19, 2017 2:45 pm 
Offline

Joined: Thu Jun 16, 2016 6:03 am
Posts: 10
OK, I changed my initialization function, it works now, thanks to you :)
Maybe the WARNING in the wiki article should be mitigated ?


Top
 Profile  
 
 Post subject: Re: PS2 controller commands fire IRQ1 on response byte recep
PostPosted: Mon Feb 20, 2017 12:43 pm 
Offline
Member
Member

Joined: Mon Mar 25, 2013 7:01 pm
Posts: 5099
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.


Top
 Profile  
 
 Post subject: Re: PS2 controller commands fire IRQ1 on response byte recep
PostPosted: Sat Feb 25, 2017 4:43 am 
Offline

Joined: Thu Jun 16, 2016 6:03 am
Posts: 10
Because of enabling keyboard before configuring mouse.


Top
 Profile  
 
 Post subject: Re: PS2 controller commands fire IRQ1 on response byte recep
PostPosted: Sat Feb 25, 2017 6:42 am 
Offline
Member
Member
User avatar

Joined: Sun Dec 25, 2016 1:54 am
Posts: 204
could you post revised code and change to [SOLVED]? Will help others in future. Cheers

_________________
Plagiarize. Plagiarize. Let not one line escape thine eyes...


Top
 Profile  
 
 Post subject: Re: [SOLVED] IRQ1 on PS2 controller commands response
PostPosted: Sat Feb 25, 2017 8:17 am 
Offline

Joined: Thu Jun 16, 2016 6:03 am
Posts: 10
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;
}


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 7 posts ] 

All times are UTC - 6 hours


Who is online

Users browsing this forum: Bing [Bot] and 59 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group