stuck writing keyboard driver
-
- Member
- Posts: 199
- Joined: Fri Jul 13, 2007 6:37 am
- Location: Stuttgart/Germany
- Contact:
stuck writing keyboard driver
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
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
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
Cheers,
Adam
-
- Member
- Posts: 199
- Joined: Fri Jul 13, 2007 6:37 am
- Location: Stuttgart/Germany
- Contact:
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
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
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.
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.
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.
Last edited by bewing on Tue Oct 23, 2007 12:55 pm, edited 1 time in total.
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.
-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.
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.)
- Attachments
-
- KBDASM.TXT
- (13.34 KiB) Downloaded 46 times
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.
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.
The real problem with goto is not with the control transfer, but with environments. Properly tail-recursive closures get both right.
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:
So now for example, if I want to allow the IRQ handler to handle the "A" key, I will invoke this function like this:
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:
And a constant like "KDT_KEY_A" is made in this way:
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.
Code: Select all
DWORD __SetKDTEntry (void* KDTR, DWORD Settings, DWORD ProcAddress); StdCall;
Code: Select all
INVOKE __SetKDTEntry, OFFSET KDTR, KDT_KEY_A, 0x00000000
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)
On the field with sword and shield amidst the din of dying of men's wails. War is waged and the battle will rage until only the righteous prevails.
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: there exists (especially laptop) keyboards (not necessarily old ones) that will refuse to do anything but translated scanset 2.
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.mystran wrote: the rest is relatively easy to solve with a state machine.
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.XCHG wrote:One thing that you might to want to consider about a keyboard driver is its extendibility!
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.
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.
On the field with sword and shield amidst the din of dying of men's wails. War is waged and the battle will rage until only the righteous prevails.
Hi,
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
Here's your options:bewing wrote: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.XCHG wrote:One thing that you might to want to consider about a keyboard driver is its extendibility!
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
For all things; perfection is, and will always remain, impossible to achieve in practice. However; by striving for perfection we create things that are as perfect as practically possible. Let the pursuit of perfection be our guide.
Hi Brendan!
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.
300 million rich customers is enough for me. I'm not greedy.Brendan wrote: Here's your options:
1) Don't support internationisation and have an OS that's useless to most people in the world.
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.
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.Brendan wrote: 3) Write a few keyboard drivers that rely on "scancode to unicode" conversion tables ....
Bullshit.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.
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.
Every good solution is obvious once you've found it.
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;
On the field with sword and shield amidst the din of dying of men's wails. War is waged and the battle will rage until only the righteous prevails.