OSDev.org

The Place to Start for Operating System Developers
It is currently Fri Mar 29, 2024 1:43 am

All times are UTC - 6 hours




Post new topic Reply to topic  [ 7 posts ] 
Author Message
 Post subject: Problems with getting scancode set
PostPosted: Wed Apr 18, 2018 8:28 am 
Offline
Member
Member

Joined: Fri Apr 13, 2018 10:18 am
Posts: 32
Location: Melbourne, VIC, Australia
Hi guys,
I am having quite a bit of problem dealing with getting/setting the scancode set for my PS/2 keyboard (and mouse?) driver. The code will work on emulators such as Bochs or Qemu, but wants me to press a key and then display garble on real hardware. Can someone help me with that?
This is the driver's code by the way:
Code:
#include <kernel/i8042.h>
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include <kernel/io.h>
#include <kernel/tty.h>
/*
* Initialize the Intel 8042 PS/2 Controller.
* Input: nothing
* Return: error code:
*  - 0: No error
*  - 1: PS/2 controller doesn't exist (will happen on Apple machines)
*  - 2: Self test failed
*  - 3: No error, dual channel PS/2 available
*  - 4: First PS/2 port test failed because clock line stuck low
*  - 5: First PS/2 port test failed because clock line stuck high
*  - 6: First PS/2 port test failed because data line stuck low
*  - 7: First PS/2 port test failed because data line stuck high
*  - 8: Second PS/2 port test failed because clock line stuck low
*  - 9: Second PS/2 port test failed because clock line stuck high
*  - 10: Second PS/2 port test failed because data line stuck low
*  - 11: Second PS/2 port test failed because data line stuck high
*  - 12: Cannot set keyboard scan code set to 1
*  - 13: Cannot initialize keyboard
*  - 14: Keyboard failed to respond to echo
*/
uint8_t i8042Init(void) {
   uint8_t returnCode = 0; // return code in case everything goes well
   // disable all PS/2 ports
   outb(0x64, 0xad);
   outb(0x64, 0xa7);
   inb(0x60); // flush output buffer
   outb(0x64, 0x20); // read CCB
   while(!(inb(0x64) & 0x01));
   uint8_t ccb = inb(0x60);
   ccb &= 0x34;
   outb(0x64, 0x60); // write CCB
   while(inb(0x64) & 0x01);
   outb(0x60, ccb);
   outb(0x64, 0xaa); // perform controller self test
   while(!(inb(0x64) & 0x01));
   if(inb(0x60) != 0x55) return 2; // self test failed
   outb(0x64, 0x60); // rewrite CCB, just in case that self test reset the controller
   while(inb(0x64) & 0x01);
   outb(0x60, ccb);
   if(ccb & 0x20) returnCode = 0; // single channel controller detected
   else {
      outb(0x64, 0xa8); // enable second PS/2 port
      outb(0x64, 0x20);
      while(!(inb(0x64) & 0x01));
      if(!(inb(0x60) & 0x20)) {
         returnCode = 3;
         outb(0x64, 0xa7); //disable second PS/2 port again
      }
   }
   uint8_t testResult;
   outb(0x64, 0xab); // test first port
   while(!(inb(0x64) & 0x01));
   testResult = inb(0x60);
   if(testResult != 0) return (testResult + 3);
   if(returnCode == 3) {
      // test second port
      outb(0x64, 0xa9);
      while(!(inb(0x64) & 0x01));
      testResult = inb(0x60);
      if(testResult != 0) return (testResult + 7);
   }
   outb(0x64, 0xae); // re-enable the first port
   if(returnCode == 3) outb(0x64, 0xa8); // re-enable the second port if possible
   
   /* initialize keyboard */
   while(1) {
      i8042SendData(0, 0xFF);
      uint8_t tmp = i8042GetScancode();
      if(tmp == 0xAA) break;
      else if((tmp == 0xFC) || (tmp == 0xFD)) return 13;
   }
   
   i8042DisableSecondPort(); // disable the second port
   
   /* send echo */
   uint8_t echoResponse;
   for(uint8_t i = 0; i < 3; i++) {
      i8042SendData(0, 0xEE);
      echoResponse = i8042GetScancode();
      if(echoResponse == 0xEE) break;
   }
   if(echoResponse != 0xEE) return 14;
   
   return returnCode;
}

uint8_t i8042GetScancode(void) {
   while(!(inb(0x64) & 1));
   return inb(0x60);
}

void i8042SendData(uint8_t port, uint8_t data) {
   if(port != 0) outb(0x64, 0xd4);
   while(inb(0x64) & 2);
   outb(0x60, data);
}

void i8042SetScancodeSet(uint8_t set) {
   uint8_t response;
   while(1) {
      i8042SendData(0, 0xf0);
      while(1) {
         response = inb(0x60);
         if((response == 0xFE) || (response == 0xFA)) break;
      }
      if(response != 0xFE) break;
   }
   while(1) {
      i8042SendData(0, set);
      while(1) {
         response = inb(0x60);
         if((response != 0xAA) && (response != 0xFA) && (response != 0x00)) break;
      }
      if(response != 0xFE) break;
   }
}

uint8_t i8042GetScancodeSet(void) {
   uint8_t response;
   while(1) {
      i8042SendData(0, 0xf0);
      while(1) {
         response = inb(0x60);
         if((response == 0xFE) || (response == 0xFA)) break;
      }
      if(response != 0xFE) break;
   }
   while(1) {
      i8042SendData(0, 0);
      while(1) {
         response = inb(0x60);
         if((response != 0xAA) && (response != 0xFA) && (response != 0x00)) break;
      }
      if(response != 0xFE) break;
   }
   return response;
}

/* Based on knusbaum's work */

#define ESC 27
#define BS 8
#define EOT 4

static const uint8_t scSet1Lower[256] = {
    0x00,  ESC,  '1',  '2',     /* 0x00 */
     '3',  '4',  '5',  '6',     /* 0x04 */
     '7',  '8',  '9',  '0',     /* 0x08 */
     '-',  '=',   BS, '\t',     /* 0x0C */
     'q',  'w',  'e',  'r',     /* 0x10 */
     't',  'y',  'u',  'i',     /* 0x14 */
     'o',  'p',  '[',  ']',     /* 0x18 */
    '\n', 0x00,  'a',  's',     /* 0x1C */
     'd',  'f',  'g',  'h',     /* 0x20 */
     'j',  'k',  'l',  ';',     /* 0x24 */
    '\'',  '`', 0x00, '\\',     /* 0x28 */
     'z',  'x',  'c',  'v',     /* 0x2C */
     'b',  'n',  'm',  ',',     /* 0x30 */
     '.',  '/', 0x00,  '*',     /* 0x34 */
    0x00,  ' ', 0x00, 0x00,     /* 0x38 */
    0x00, 0x00, 0x00, 0x00,     /* 0x3C */
    0x00, 0x00, 0x00, 0x00,     /* 0x40 */
    0x00, 0x00, 0x00,  '7',     /* 0x44 */
     '8',  '9',  '-',  '4',     /* 0x48 */
     '5',  '6',  '+',  '1',     /* 0x4C */
     '2',  '3',  '0',  '.',     /* 0x50 */
    0x00, 0x00, 0x00, 0x00,     /* 0x54 */
    0x00, 0x00, 0x00, 0x00      /* 0x58 */
};

static const uint8_t scSet1Upper[256] = {
    0x00,  ESC,  '!',  '@',     /* 0x00 */
     '#',  '$',  '%',  '^',     /* 0x04 */
     '&',  '*',  '(',  ')',     /* 0x08 */
     '_',  '+',   BS, '\t',     /* 0x0C */
     'Q',  'W',  'E',  'R',     /* 0x10 */
     'T',  'Y',  'U',  'I',     /* 0x14 */
     'O',  'P',  '{',  '}',     /* 0x18 */
    '\n', 0x00,  'A',  'S',     /* 0x1C */
     'D',  'F',  'G',  'H',     /* 0x20 */
     'J',  'K',  'L',  ':',     /* 0x24 */
     '"',  '~', 0x00,  '|',     /* 0x28 */
     'Z',  'X',  'C',  'V',     /* 0x2C */
     'B',  'N',  'M',  '<',     /* 0x30 */
     '>',  '?', 0x00,  '*',     /* 0x34 */
    0x00,  ' ', 0x00, 0x00,     /* 0x38 */
    0x00, 0x00, 0x00, 0x00,     /* 0x3C */
    0x00, 0x00, 0x00, 0x00,     /* 0x40 */
    0x00, 0x00, 0x00,  '7',     /* 0x44 */
     '8',  '9',  '-',  '4',     /* 0x48 */
     '5',  '6',  '+',  '1',     /* 0x4C */
     '2',  '3',  '0',  '.',     /* 0x50 */
    0x00, 0x00, 0x00, 0x00,     /* 0x54 */
    0x00, 0x00, 0x00, 0x00      /* 0x58 */
};

# define KBBUFFLEN 128

static uint8_t shift, ctrl, keypresses[256], caps = 0;
static uint8_t kbBuff[KBBUFFLEN], kbBuffHd, kbBuffTl;

static void pollKbInput() {
   uint8_t nextHd = (kbBuffHd + 1) % KBBUFFLEN;
   if(nextHd == kbBuffTl) return;
   
   if(!(inb(0x64) & 1)) return;
   uint8_t byte = inb(0x60);
   if(byte == 0) return;
   
   if(byte & 0x80) {
      uint8_t pressedByte = byte & 0x7f;
      if(pressedByte == 0x2a) shift &= 0x02;
      else if(pressedByte == 0x36) shift &= 0x01;
      else if(pressedByte == 0x1d) ctrl = 0;
      
      keypresses[pressedByte] = 0;
      return;
   }
   
   if(keypresses[byte] < 10 && keypresses[byte] > 0) {
      keypresses[byte]++;
      return;
   }
   keypresses[byte]++;
   
   if(byte == 0x2a) {
      shift |= 0x01;
      return;
   } else if(byte == 0x36) {
      shift |= 0x02;
      return;
   } else if(byte == 0x1d) {
      ctrl = 1;
      return;
   } else if(byte == 0x3a) {
      caps ^= 1;
      return;
   }
   
   const uint8_t *codes;
   if(ctrl) {
      if(scSet1Lower[byte] == 'd') {
         // ctrl+d = EOT
         kbBuff[kbBuffHd] = EOT;
         kbBuffHd = nextHd;
         return;
      }
   } else if((shift != 0) && (caps == 0)) codes = scSet1Upper;
   else if((shift == 0) && (caps != 0)) codes = scSet1Upper;
   else codes = scSet1Lower;
   
   uint8_t ascii = codes[byte];
   if(ascii != 0) {
      kbBuff[kbBuffHd] = ascii;
      kbBuffHd = nextHd;
      return;
   }
}

char i8042GetChar(void) {
   while(1) {
      pollKbInput();
      if(kbBuffHd != kbBuffTl) {
         char c = kbBuff[kbBuffTl];
         kbBuffTl = (kbBuffTl + 1) % KBBUFFLEN;
         pollKbInput();
         return c;
      }
   }
}

void i8042EnableFirstPort(void) {
   outb(0x64, 0xae);
}

void i8042DisableFirstPort(void) {
   outb(0x64, 0xad);
}

void i8042EnableSecondPort(void) {
   outb(0x64, 0xa8);
}

void i8042DisableSecondPort(void) {
   outb(0x64, 0xa7);
}


EDIT: Bochs will hang during PS/2 keyboard initialization because of the initialize keyboard and/or echo portion.

_________________
Just a procrastinating uni student doing stupid things (or not doing them at all)...

SysX: https://github.com/itsmevjnk/sysx.git


Top
 Profile  
 
 Post subject: Re: Problems with getting scancode set
PostPosted: Wed Apr 18, 2018 6:27 pm 
Offline
Member
Member
User avatar

Joined: Sat Nov 22, 2014 6:33 pm
Posts: 934
Location: USA
I only have two comments.

The first, when someone posts a question of "why won't it work" and then posts 100+ lines of code, usually the first thing most people do is to hit the back button to move on to the next question. No one likes to see 100+ lines of code when the question is "why won't it work". Sorry.

Second, you have to wait for the ps2 controller to be ready to read or write a value to the command and/or data ports. You can't just simply read a byte from the port and expect it to be valid.

https://wiki.osdev.org/PS/2_Keyboard might be a good place to start.

Write a few (inline) routines that wait for the PS/2 to be ready for a command/data write before writing a command/data as well as a read routine. Then replace the lines you have with calls to these routines and see what you find out.

Be sure to use timeouts or you might be waiting a while on occasion.

Ben
- http://www.fysnet.net/input_and_output_devices.htm


Top
 Profile  
 
 Post subject: Re: Problems with getting scancode set
PostPosted: Thu Apr 19, 2018 1:03 am 
Offline
Member
Member

Joined: Fri Apr 13, 2018 10:18 am
Posts: 32
Location: Melbourne, VIC, Australia
This is the exact problem I am getting: When I send 0xF0 (the command to get scan code set) to port 0x60, the keyboard sends back 0xFA, which is the ACK byte. After that, when I send the parameter (in this case is 0), the keyboard gives back 0xFE. Any subsequent attempts to send that byte will result in the keyboard sending 0xFE back. This doesn't happen on emulators: it only happens on real hardware.
EDIT: Any subsequent attempts to send that command again still causes the same thing to happen.

_________________
Just a procrastinating uni student doing stupid things (or not doing them at all)...

SysX: https://github.com/itsmevjnk/sysx.git


Top
 Profile  
 
 Post subject: Re: Problems with getting scancode set
PostPosted: Thu Apr 19, 2018 3:03 am 
Offline
Member
Member

Joined: Thu Jul 05, 2007 8:58 am
Posts: 223
Few questions:

- Do you wait until the PS/2 controller indicates it is ready for the next byte (by checking input buffer status bit)?
- Does the hardware you are testing on have a physical PS/2 controller and keyboard, or is emulation used?


Top
 Profile  
 
 Post subject: Re: Problems with getting scancode set
PostPosted: Thu Apr 19, 2018 4:13 am 
Offline
Member
Member

Joined: Fri Apr 13, 2018 10:18 am
Posts: 32
Location: Melbourne, VIC, Australia
davidv1992 wrote:
Few questions:

- Do you wait until the PS/2 controller indicates it is ready for the next byte (by checking input buffer status bit)?
- Does the hardware you are testing on have a physical PS/2 controller and keyboard, or is emulation used?

1. I waited until the PS/2 controller's input buffer status bit to clear and then write the data.
2. The hardware I am testing on has a physical PS/2 keyboard.

_________________
Just a procrastinating uni student doing stupid things (or not doing them at all)...

SysX: https://github.com/itsmevjnk/sysx.git


Top
 Profile  
 
 Post subject: Re: Problems with getting scancode set
PostPosted: Thu Apr 19, 2018 4:36 am 
Offline
Member
Member

Joined: Fri Apr 13, 2018 10:18 am
Posts: 32
Location: Melbourne, VIC, Australia
Nevermind, I got it to work. Apparently I accidentally set the USB Keyboard Support to Enabled in the BIOS settings, which messes up with the PS/2 controller. Thanks for pointing that out!

_________________
Just a procrastinating uni student doing stupid things (or not doing them at all)...

SysX: https://github.com/itsmevjnk/sysx.git


Top
 Profile  
 
 Post subject: Re: Problems with getting scancode set
PostPosted: Thu Apr 19, 2018 12:22 pm 
Offline
Member
Member
User avatar

Joined: Sat Nov 22, 2014 6:33 pm
Posts: 934
Location: USA
weedboi6969 wrote:
Nevermind, I got it to work. Apparently I accidentally set the USB Keyboard Support to Enabled in the BIOS settings, which messes up with the PS/2 controller. Thanks for pointing that out!

Yes it does. :-) Eventually, after you have become comfortable programming hardware, you can skip the BIOS and disable it yourself before hand, making sure that this doesn't happen again.

Ben

- http://www.fysnet.net/the_universal_serial_bus.htm


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: DotBot [Bot], Google [Bot], Majestic-12 [Bot] and 162 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