Keyboard driver

  • Public id: Rand_cs

How the keyboard works in the previous article has said, was based on Linux 0.11 but not the system, this article to Xv6 keyboard driver program as an example to systematically describe how the keyboard is working. About the driver before the disk that said, it is the hardware physical interface package, so to understand the keyboard driver, also to understand some of the keyboard physical interface.

There are two chips related to the keyboard, one is the keyboard encoder I8048, the other is the keyboard controller I8042, respectively.

Keyboard encoder

The keyboard encoder is located at the keyboard. Its main function is to monitor the pressing and bouncing of the key, and then encode the two states and send them to the keyboard controller.

The code mentioned above is called keyboard scanning code, encoding a total of three kinds, corresponding also have three sets of keyboard scanning code, each set of keyboard scanning code specific how to code will not say, see the link behind. Most of today’s keyboards use the second scan code, but there is no rule out using the first and third, so for compatibility, the keyboard controller will be converted to the first scan code. Of course, this is the default situation, the specific use of which scan code, whether the controller conversion, or depends on whether the hardware support and specific Settings, you are interested in the link at the end of the article.

Therefore, the first set of keyboard scan codes still needs to be said, a key will be pressed and then it will be bounced, so each key will have two states, that is, each key will correspond to two scan codes, the code when the key is pressed is called makecodemakecodemakecode. Bounce the encoding is called break code (breakcodebreakcodebreakcode).

The on and off codes of most keys are 8 bits and 1 byte, but some control keys such as Ctrl and Alt, additional keys such as Insert, keypad areas such as /, and arrow keys are 2 or more bytes. Scan codes that have more than one byte usually start with 0xE00xE00xE0. Only PauseBreakPause BreakPauseBreak one key starts with 0xE10xE10xE1.

Relationship between broken codes and Pass codes: Broken codes = Pass codes +0x80 Broken codes = Pass codes +0x80 Broken codes = Pass codes +0x80 Broken codes. 0x800x800x80 binary represents 1000 00001000\00001000 0000, so for broken code and pass code can be understood as follows, they are composed of 8 bits, the highest bit 7 represents the key state, 1 represents the press, 0 represents the bounce.

Keyboard controller

The keyboard controller (I8042), which is not inside the keyboard, is integrated on the Southbridge chip. It mainly receives the keyboard scan code from the keyboard encoder, does some processing (such as the second scan code to the first set), and then triggers the interrupt notification CPU to read the scan code.

The keyboard controller has four 8-bit registers, Status Register and Control Register, both of which share the same port 0x64. Read is a Status Register and write is a Control Register. Input Buffer and Output Buffer, which share the same port 0x60, are Output buffers for reading and Input buffers for writing.

Status register:

Bit0:1 indicates that the output cache is full and cleared after CPU reads. The scan code from the encoder is right here.

Bit1:1 indicates that the input cache is full and is cleared after being read by the controller.

Control register:

Write the 0x64 port to send commands to the controller. Note that the commands are sent to the controller itself rather than to the hardware keyboard. The control of the keyboard is indirectly controlled by the controller, so only the keyboard needs to be operated.

The command controller writes the command byte to port 0x64, which is usually one byte. If there are two bytes, the second byte is written to port 0x60. Because you are writing to the cache represented by port 0x60, you need to determine whether the cache is empty.

For example, when entering the protected mode and setting A20A20A20, check whether the input cache is empty first. If it is empty, the controller has taken the data and can continue. Otherwise, the loop waits:

Inb $0x64,%al # Wait for not busy testb $0x2,%al JNZ seta20.1Copy the code

The command 0xD10xD10xD1 is then written to port 0x64, indicating that the Output port is ready to be written, and the bytes written to port 0x60 will be put into the Output port.

Inb $0x64,%al # Wait for not busy same as testb $0x2,%al JNZ seta20.2movb $0xdf,%al # 0xdf -> port 0x60 Open A20 outb %al,$0x60Copy the code

Similarly, first check whether the input buffer is empty, and then write the second byte 0xDF0xDF0xDF command. This byte is sent to the Output port, which is also a control port. Bit2 controls the switch of A20A20A20. So if the command byte 0xDD0xDD0xDD is turned off A20A20A20.

That’s all for the keyboard controller, only the xV6-related parts. See the links at the end of this article for other parts of the same interest.

XV6

The driver is the package of the physical interface of the hardware, so is the keyboard driver. Its main function is to convert the read scan code into the information needed by the computer, such as characters, signals and so on. Xv6 in this aspect of the implementation of the relatively simple, only to achieve the character conversion, some function control keys, let’s take a look.

Hkbd.hkbd.h header file defines the port number, control keys such as Ctrl, special keys such as UP, and the most important mapping table, let’s look at the general mapping table:

static uchar normalmap[256] =
{
  NO,   0x1B.'1'.'2'.'3'.'4'.'5'.'6'.// 0x00
  '7'.'8'.'9'.'0'.The '-'.'='.'\b'.'\t'.'q'.'w'.'e'.'r'.'t'.'y'.'u'.'i'.// 0x10
  'o'.'p'.'['.'] '.'\n', NO,   'a'.'s'.'d'.'f'.'g'.'h'.'j'.'k'.'l'.'; '.// 0x20
  '\' '.'`,  NO,   '\ \'.'z'.'x'.'c'.'v'.'b'.'n'.'m'.', '.'. '.'/',  NO,   The '*'.// 0x30
  NO,   ' ',  NO,   NO,   NO,   NO,   NO,   NO,
  NO,   NO,   NO,   NO,   NO,   NO,   NO,   '7'.// 0x40
  '8'.'9'.The '-'.'4'.'5'.'6'.'+'.'1'.'2'.'3'.'0'.'. ',  NO,   NO,   NO,   NO,   // 0x50
  [0x9C] '\n'.// KP_Enter
  [0xB5] '/'.// KP_Div
  [0xC8] KEY_UP,    [0xD0] KEY_DN,
  [0xC9] KEY_PGUP,  [0xD1] KEY_PGDN,
  [0xCB] KEY_LF,    [0xCD] KEY_RT,
  [0x97] KEY_HOME,  [0xCF] KEY_END,
  [0xD2] KEY_INS,   [0xD3] KEY_DEL
};
Copy the code

The keyboard scan code is the representative of a key, but it is not what we want, what we want is the meaning of the key, for example, the general code of the number key 1 is 0x020X020x02, 0x020X020x02 is obviously not what we want, what we want is the number 1, so we need a mapping relationship to convert. A collection of all key mappings is the above mapping table. It’s a big array, and the subscript is the scan code for this key, and the content is what it says.

This is the general case, but of course there are also non-general cases, such as Shift, CapsLock, Ctrl, etc., when these controls are pressed, the meaning of other keys when pressed is different, so there is another mapping table, I won’t list it here, there are too many, you can refer to the code directly. For example, when the Shift key is held down followed by the number key 1, the passcode 0x020x020x02 should map to! Instead of 1.

Ckbd.c inside the source code:

int kbdgetc(void)
{
  static uint shift;      // Shift uses bit to record control keys such as Shift, CTRL
  static uchar *charcode[4] = {
    normalmap, shiftmap, ctlmap, ctlmap
  };       / / map
  uint st, data, c;

  st = inb(KBSTATP);
  if((st & KBS_DIB) == 0)     // The output buffer is not enough to be read with the in command
    return - 1;
  data = inb(KBDATAP);      // Read data from the output buffer

  if(data == 0xE0) {// Passcode key starting with e0
    shift |= E0ESC;       / / record e0
    return 0;
  } else if(data & 0x80) {// Break code, table key up
    // Key released
    data = (shift & E0ESC ? data : data & 0x7F);     
    shift &= ~(shiftcode[data] | E0ESC);             
    return 0;
  } else if(shift & E0ESC){   // Scan code next to 0xE0
    // Last character was an E0 escape; or with 0x80
    data |= 0x80;
    shift &= ~E0ESC;
  }

  shift |= shiftcode[data];   // Record the status of control keys, such as Shift,Ctrl,Alt
  shift ^= togglecode[data];  // Record control key status, such as CapsLock, NumLock, ScrollLock
  c = charcode[shift & (CTL | SHIFT)][data]; // Get the contents of the mapping table, that is, the meaning of the key
  if(shift & CAPSLOCK){
    if('a' <= c && c <= 'z')
      c += 'A' - 'a';
    else if('A' <= c && c <= 'Z')
      c += 'a' - 'A';
  }
  return c;
}
Copy the code

This program can be regarded as a minimalist keyboard driver, but also the main body of the keyboard interrupt service procedures, complete the keyboard scan code to the required information conversion. Here is a careful analysis:

I said there are multiple mapping tables, multiple ways of mapping, so how do you know which one to use? The variable shiftShiftShift is called shiftShiftShift, but it doesn’t just record the state of the Shift key.

#define SHIFT           (1<<0)
#define CTL             (1<<1)
#define ALT             (1<<2)

#define CAPSLOCK        (1<<3)
#define NUMLOCK         (1<<4)
#define SCROLLLOCK      (1<<5)

#define E0ESC           (1<<6)   // The pass and break codes start with E0
Copy the code

From this way of defining the control keys, we can see that the way to record the control keys using ShiftShiftShift is to use bitwise operations.

Charcodecharcodecharcode represents a two-dimensional array, which can be viewed as a collection of mapping tables. Select the mapping table based on shiftshiftshift.

st = inb(KBSTATP);
if((st & KBS_DIB) == 0)     // The output buffer is empty and cannot be read with the in command
  return - 1;
data = inb(KBDATAP);      // Read data from the output buffer
Copy the code

These lines are used to read the keyboard scan code, which is placed in the output buffer. To read the scan code, first read the current state from the status register to STSTST, and then perform and operation to take out the 0th bit, indicating the state of the output buffer. If it is 0, it means that the output buffer register is empty and -1 is returned if it cannot read. A value of 1 indicates that the output buffer register is full and ready to read, so the scan code is then read from the output buffer on port 0x60 to the datadatadata.

if(data == 0xE0) {// Passcode key starting with e0
    shift |= E0ESC;       / / record e0
    return 0;
}
Copy the code

If the scan code is 0xE0, it indicates that the key is a special key and the scan code is longer than 8 bytes. In this case, mark the shiftShift variable to return the key directly and wait for the next data to come

else if(data & 0x80) {// Break code, table key up
    // Key released
    data = (shift & E0ESC ? data : data & 0x7F);     
    shift &= ~(shiftcode[data] | E0ESC);             
    return 0;
}
Copy the code

Data & 0x80Data \ \& 0x80Data & 0x80 is 1, indicating that the seventh bit is 1, indicating that the data is a break code. No extra work is required to receive the break code, but if the break code is a break code for a control key, The control should be removed from the record in ShiftShiftShift.

So know read datadatadata said which control key, so the shiftcodeshiftcodeshiftcode mapping:

static uchar shiftcode[256] ={  [0x1D] CTL,  [0x2A] SHIFT,  [0x36] SHIFT,  [0x38] ALT,  [0x9D] CTL,  [0xB8] ALT,};
Copy the code

Some controls use generic and some use broken codes, which is why the datadatadata assignment statement that uses the conditional expression must exist. Because shiftcodeshiftcodeshiftcode mapping in the Shift key when not break code, so converted into code. Private thought so mapping is chaotic, behind in KBD. CKBD. CKBD. There are some statements in c meaning also not too clear, or should the mapping relationship to completion, then saves that datadatadata assignment statements, make the back of the written statement more clear. If datadatadata is a broken code, there is no need to do anything else. If it is a broken code for the control key, just clear the control key information recorded in ShiftShiftShift.

else if(shift & E0ESC){       // Last character was an E0 escape; or with 0x80 data |= 0x80; shift &= ~E0ESC; }
Copy the code

This situation corresponds to the keyboard scan code immediately following 0xE00xE00xE0. The keyboard scan code has multiple bytes and exists in pairs, namely, E0h XXhE0h XXhE0h\ XXh\ E0h\ XXhE0h XXhE0h XXh, Each time you receive XXhXXhXXh, remove the E0E0E0 message recorded in the ShiftShift key. As for the front and a data ∣ = 0 x80data \ | = 0 x80 data ∣ = 0 x80 or associated with the mapping table of xv6 design, has many same meaning of key on the keyboard, xv6 some key mapping relationship with break code for mapping, such as the division sign key /.

shift |= shiftcode[data];   // Record the state of the control key, such as Shift,Ctrl,Altshift ^= togglecode[data]; // Record control key status, such as CapsLock, NumLock, ScrollLock
Copy the code

These two sentences to record the state of the control key, divided into two cases, two operations. You can see the difference between them. To achieve key combinations, Shift,Ctrl,Alt need to be held down to work, but not when pressed. A control like CapsLock, on the other hand, only needs to be pressed once, even after being flipped. So one uses or, one uses xOR, and it should be easy to simulate the process yourself.

c = charcode[shift & (CTL | SHIFT)][data];   If (shift & CAPSLOCK){// If ('a' <= c && c <= 'z') // Uppercase c += 'a' - 'a'; Else if (' A '< = c & c < =' Z ') / / capital has shrunk to write c + = 'A' - 'A'; }
Copy the code

Select the mapping table according to the control key information recorded in ShiftShiftShift, and obtain the meaning of the keyboard scan code according to datadatadata. Because the CapsLock and Shift keys have the same function, extra case is required if C is a normal 26-letter character.

That’s all there is to know about the xv6 keyboard driver, but there are still some features left unsaid, such as the Ctrl key combination function, keyboard buffer, etc., which are covered in a separate file, let’s go into more details later.

Here are some more common questions, as mentioned in the first keyboard post:

To use the combination key, press the control key first. These controls are identified by the keyboard’s interrupt program shiftShiftShift. Press the control key first, the program sets the press state for the control key, and then processes the incoming key to check whether there is a control key to press, so as to make a different operation.

The combination keys are pressed in order, but there is no order requirement when they are played. As can be seen from the above key handler, only the passcode key handler is doing the work, while the broken key handler returns all the keys directly except the identification bit of the control key that needs to be reset. So when you use the keyboard to control input, the key is important, not the key up, so as long as the key is right, how to play is not important.

Holding down a key consistently triggers a keyboard interrupt. With a regular character key, the computer screen may print a character all the time. If there are some control keys, the driver may constantly set the key to the pressed state. Of course, whether or not the driver logs the last keystroke depends on the implementation, and most do not, as xv6 does, processing a scan code for every keystroke that is triggered.

Finally, the keyboard driver is also used to encapsulate the physical interface of the keyboard, such as reading state, reading scan code and so on. The keyboard itself uses the keyboard scan code, each key has its own keyboard scan code, one is the general code table press, a table broken code table bounce. This keyboard scan code is just uniquely identifying a key, and you can think of the keyboard scan code as the physical meaning of a key, but that’s not what we want, what we want is the logical meaning of the key. So there needs to be a transition between the physical and the logical, and that’s what mapping tables are for.

There are a variety of keys on the keyboard that can be used in combination, and they represent many and miscellaneous meanings and functions. Xv6 only implements some of them, but it’s enough to get the essence of what’s going on. No matter what function to achieve through the key, or just a simple use of the logical meaning represented by a key, is to first obtain the key scan code, and then through the mapping table into the required information, what function to work on it.

Good this article ends here, what error also please criticize pointer, also welcome everybody to discuss with me exchange study progress together.

Reference:

www.win.tue.nl/~aeb/linux/…

Wiki.osdev.org/ “8042” _PS / 2…