Page 1 of 4

stuck writing keyboard driver

Posted: Fri Aug 24, 2007 7:59 am
by sancho1980
hi

i'm still trying to write a meaningful keyboard driver, but im really stuck, cuz i dont see the system behind the scancodes
most keyboard events emit only one scancode, and then some have 2 and 2 keys produce even 4 scancodes
would someone please sketch a meaningful algorithm of what i have to check first in the keyboard isr and how to elegantly map keyboard events to ascii characters or special events?

thanks

martin

Posted: Fri Aug 24, 2007 9:24 am
by AJ
http://www.win.tue.nl/~aeb/linux/kbd/scancodes.html is the first google hit for 'keyboard scancodes'. It seems an extremely full reference which explains the entire keyboard communication protocol.

Cheers,
Adam

Posted: Fri Aug 24, 2007 9:54 am
by sancho1980
yeah
i know this document
where im stuck is how to best write the driver w/o ending up with spaghetti code
so many things to keep in mind:

-e0, e1 scancodes
-shift, ctrl pressed

etc..

what i was asking for is a sketch of how to write the driver, algorithm-wise..in what sequence would you check the scancodes, what state variables does the keyboard have, etc...?

thanks

martin

Posted: Fri Aug 24, 2007 7:21 pm
by frank
Store the status of shift, control, and alt somewhere. For example you could set shift_on to true when the shift key is pressed and to false when it is released. Next you need to maintain a state machine of sorts when you receive a keycode of E0 just set a flag and then treat the next keycode differently based on that flag.

Posted: Fri Aug 24, 2007 8:27 pm
by bewing
Here's mine. I prefer scanset 1, because the keyup sequences seem more obvious (and it's also the default that you get during a boot). It IS complicated to peel apart the scancodes. You're free to use this one if you want. It keeps a 128 entry circular buffer of the last translated codes. It also keeps a record of what codes got translated into what outputs. Some of the scancode lists I've seen in the past have been wrong. The Pause/Break scancode, especially.

This code has seemed to work perfectly, on real hardware. It LOOKS long, especially for an interrupt handler, but the code jumps forward very rapidly as possible codes are ruled out.

Posted: Fri Aug 24, 2007 11:30 pm
by exkor
My algorithm:
-when e0,e1 scan codes received(key pressed) i remember that it'll be extended key, set bit 0 in some variable for extended key, exit kbd handler
-when regular scancode received(key pressed) check for shift,ctrl,caps,numlock; set corresponding bits 1,2,3,4 in the same variable
-use the bits variable to select right address for the conversion table
-use the address and the key scancode to pick right symbol from the table(max 128 scancodes on the keyboards as far as I know)
-when any key released I clear the ctrl,shift,caps,numLock,ext key bits in the flags variable

Flags variable always has index of the right table to be used for conversion when user presses a key.

Such method allows to assign any number for any key on the kbd - user application will always know that 01h is F1, 2h-F2 .... 20h-Home 21h-End regardless of the keyboard in use.

Posted: Tue Oct 23, 2007 12:54 pm
by bewing
There were 2 small bugs in the first kbd driver I posted above. This version is a little bit prettier, and shows a partial implementation of the PS2 mouse. (kbd layout is for US keyboard.)

Posted: Wed Oct 24, 2007 4:20 pm
by mystran
And I've said and seen this said a couple of times, but remember, that there exists (especially laptop) keyboards (not necessarily old ones) that will refuse to do anything but translated scanset 2.

Anyway, keep the modifiers in separate flags, and the rest is relatively easy to solve with a state machine. If you spend approximately 8 hours (a working day?) learning state machines inside out, you'll never regret it later.

Posted: Wed Oct 24, 2007 5:08 pm
by XCHG
One thing that you might to want to consider about a keyboard driver is its extendibility! For example, in my kernel, I have set up an array that I have called KDT (Keyboard Descriptor Table). This array points to various procedures/functions that are supposed to handle a series of key strokes. The prototype for the procedure that registers these other handler procedures/functions is:

Code: Select all

DWORD __SetKDTEntry (void* KDTR, DWORD Settings, DWORD ProcAddress); StdCall;
So now for example, if I want to allow the IRQ handler to handle the "A" key, I will invoke this function like this:

Code: Select all

INVOKE  __SetKDTEntry, OFFSET KDTR, KDT_KEY_A, 0x00000000
The 0x00000000 passed to the [ProcAddress] parameter means that I don't have any especial procedure to handle the "A" key. And if I want the Back Space key to be processed by a procedure, I will do something like this:

Code: Select all

INVOKE  __SetKDTEntry, OFFSET KDTR, KDT_KEY_BACKSPACE ,OFFSET __KeyHandlerBackspace

And a constant like "KDT_KEY_A" is made in this way:

Code: Select all

KDT_KEY_A     EQU       MAKEDWORD(0x01, 'a' , 'A' , SCANCODE_A)
Where Byte#0 is the scan code of that key, Byte#1 is the upper case letter for that character, Byte#2 is the Lower Case and 0x01 which is the most significant byte specifies whether this key is actually handled or not. Anyway, the whole point is to try to make some driver that is really re-usable and then easy to use.

Posted: Thu Oct 25, 2007 5:35 pm
by bewing
mystran wrote: there exists (especially laptop) keyboards (not necessarily old ones) that will refuse to do anything but translated scanset 2.
Translated scanset 2 *IS* scanset 1, which is one of the reasons I use scanset 1. All keyboards must support it, because it is what DOS/BIOS requires during the boot.
mystran wrote: the rest is relatively easy to solve with a state machine.
There are good arguments both ways, but over time I have come to decide in my case that simple, clean, tight code is almost always preferable to some "conceptually pure" algorithm.
XCHG wrote:One thing that you might to want to consider about a keyboard driver is its extendibility!
I happen to completely disagree. Extensibility is the first giant step toward writing huge slow applications. If you are writing a user app, and you don't care that it will be huge and slow, then feel free to make it extensible. But an OS should be small and fast. If you want to make it extesible to some extent, then add support for boot or runtime patches to the kernel. But keep the kernel as slick as possible.

Posted: Fri Oct 26, 2007 1:45 am
by XCHG
bewing,

I do agree with your comments. A kernel must work fast but I don't remember mentioning that I like to write a kernel that works slowly! The scheme that I provided above involves a simple array with each element equal to 8-bytes. When IRQ1 is issued, the kernel will look into the array for any keys that have to be handled by a separate procedure. It will then call that procedure. If not, then it will handle itself. I don't know how that could slow the kernel down. I prefer to write such thing instead of hard-coding all the handled keys into the kernel's code. So when I want to handle one extended key, I will have to change the structure of my nested if-end ifs.

Posted: Fri Oct 26, 2007 3:23 am
by Brendan
Hi,
bewing wrote:
XCHG wrote:One thing that you might to want to consider about a keyboard driver is its extendibility!
I happen to completely disagree. Extensibility is the first giant step toward writing huge slow applications. If you are writing a user app, and you don't care that it will be huge and slow, then feel free to make it extensible. But an OS should be small and fast. If you want to make it extesible to some extent, then add support for boot or runtime patches to the kernel. But keep the kernel as slick as possible.
Here's your options:

1) Don't support internationisation and have an OS that's useless to most people in the world.

2) Buy hundreds of keyboards and write a seperate device driver for each nation's keyboard in your "spare" time, and still have an OS that's useless to a large number of people.

3) Write a few keyboard drivers that rely on "scancode to unicode" conversion tables; and a utility that allows end-users to create/modify their own "scancode to unicode" conversion tables; and allow a graphical "input method editor" to sit between the keyboard driver and the rest of the OS (for languages like Japanese, Chinese, Korean, etc, where there's thousands of characters and only hundreds of keys).


Cheers,

Brendan

Posted: Tue Oct 30, 2007 7:44 am
by bewing
Hi Brendan!
Brendan wrote: Here's your options:

1) Don't support internationisation and have an OS that's useless to most people in the world.
300 million rich customers is enough for me. I'm not greedy. :wink:

Also, one of the main design priorities in my OS is memory efficiency, and Unicode takes twice as much memory to store as ASCII -- so I decided a year ago that I'm not going to support Unicode (except in special fonts) in my US version.

Brendan wrote: 3) Write a few keyboard drivers that rely on "scancode to unicode" conversion tables ....
Yes, if I ever create an internationalized version (which I probably won't), it will certainly have a much more generalized, slower, uglier, more bloated keyboard driver written by some wageslave computer science grad, with all the features you speak of, and more. I simply think it is a mistake to design internationalization into an OS as a primary goal, unless your primary market is going to be the EU.

Posted: Tue Oct 30, 2007 8:28 am
by Solar
bewing wrote:I simply think it is a mistake to design internationalization into an OS as a primary goal, unless your primary market is going to be the EU.
Bullshit.

The world is much larger than US and EU combined, and even in the US there will be some José Average who won't be too happy about an OS that cannot even handle his name correctly.

Not to speak about the fact that the US and the EU combined don't count much against the asian market, and even ISO Latin-9 doesn't cut it there...

Dismissing very real requirements because you don't want to be bothered with the added complexity does not give an OS designer high marks in my book.

Posted: Tue Oct 30, 2007 8:30 am
by XCHG
I agree although I had a hard time supporting UTF-8 in my file system. By the way, I have coded a UTF-8 Unicode unit in NASM that features the below functions/procedures. If anyone is interested, let me know and I will make it available for download:

Code: Select all

; DWORD   __UTF8BytesNeededForCharacter (DWORD UTF8Character); StdCall;
; Boolean __UTF8IsValidCharacter (DWORD UTF8Character); StdCall;
; DWORD   __UTFEncodeCharacter (DWORD CharacterCode); StdCall;
; DWORD   __UTF8BytesNeededForCharacterByASCIICharacter (DWORD ASCIICharacter); StdCall;
; DWORD   __UTF8ReadOneCharacterFromString (const char* InString); StdCall;