Problems with getting scancode set
Posted: Wed Apr 18, 2018 8:28 am
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:
EDIT: Bochs will hang during PS/2 keyboard initialization because of the initialize keyboard and/or echo portion.
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: Select all
#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);
}