Keyboard driver advice

Question about which tools to use, bugs, the best way to implement a function, etc should go here. Don't forget to see if your question is answered in the wiki first! When in doubt post here.
Post Reply
DevilGladiator
Posts: 11
Joined: Mon Aug 08, 2016 7:56 am

Keyboard driver advice

Post by DevilGladiator »

Hi, I have made some progress in my os and now I'm implementing a keyboard driver, but i have some questions about how to do it correctly:

First of all in the osdev wiki article about it keyboard it says the following:
The command isn't completed until you've received an ACK for it. For example, if you send a command and the keyboard responds with "0xFE (resend)" then you have to send the command again (possibly limited to 3 retries before you give up and assume the keyboard doesn't support the command you're sending or there's been a hardware failure).
So i have implemented some resend code in my keyboard send data function like this:

Code: Select all

enum
{
    keyboard_data_port   = 0x60,
    keyboard_ack_port    = 0x61,
    keyboard_command_port= 0x64
} ports;

int keyboard_send_data(uint8_t data)
{
	uint8_t response = 0;

	keyboard_wait();
	outport_byte(keyboard_data_port, data);

	response = keyboard_read_buffer();

	if(response == resend)
	{
		for(uint8_t i = 0; i < 3; i++)
		{
                       	keyboard_wait();
			outport_byte(keyboard_data_port, data);
			response = keyboard_read_buffer();

			if(keyboard_read_buffer() != resend)
				return response;
		}
		return -1;
	}
	else
		return response;
}
bool keyboard_wait(void) //Wait until the keyboard is not busy, if the keyboard is still busy it will return false
{
	for(uint32_t i = 0; i < 1000; i++)
	{
		if( ( keyboard_read_status() & keyboard_mask_input_buffer ) == 0 )
			return true;
	}

	return false;
}
But i have some doubts, first of all i was wondering if i also should call keyboard wait before reading the keyboard buffer.
Also, i have implemented a handler for the keyboard IRQ:

Code: Select all

unsigned char read_key()
{
	static unsigned char key_code, value;

	key_code = keyboard_read_buffer();
	value =  inport_byte(keyboard_ack_port);
	outport_byte(keyboard_ack_port, value | 0x80);
	outport_byte(keyboard_ack_port, value );

	return key_code;
}


void keyboard_handler()
{
	unsigned char scancode = 0;
	unsigned char character = 0;
	key_pressed = false;

	if( ( keyboard_read_status() & keyboard_mask_input_buffer ) == 0)  //Only read a key if there is something to read
	{
		scancode = read_key();

		if( scancode & 0x80) //If a key is released and it was a ****, control ot alt set the mode to false
		{
			switch(kbd_unshifted[scancode -128])
			{
				case KEY_LSHIFT:
				case KEY_RSHIFT:
					shift = false;
					break;

				case KEY_LCTRL:
				case KEY_RCTRL:
					control = false;
					break;

				case KEY_LALT:
				case KEY_RALT:
					alt = false;
					break;
			}
		}
		else
		{
			key_pressed = true;
			switch(kbd_unshifted[scancode]) //Check the shift, control, caps lock and alt keyd
			{
				case KEY_CAPSLOCK:
					caps_lock = !caps_lock;
					break;
				case KEY_LSHIFT:
				case KEY_RSHIFT:
					shift = true;
					break;
				case KEY_LCTRL:
				case KEY_RCTRL:
					control = true;
					break;
				case KEY_LALT:
				case KEY_RALT:
					alt = true;
					break;
			}
			if(!control || !alt)
			{
				if(shift)
					character = kbd_shifted[scancode];
				else if(caps_lock)
					character = kbd_caps[scancode];
				else
					character = kbd_unshifted[scancode];
			}

			if(buffer_end < 127)
			{
				buffer_end++;
			}
			keyboard_buffer[buffer_end] = character;
		}
	}
}


unsigned char keyboard_getch()
{
	while(buffer_end == 0); //If there are no key strokes wait until we get one
	disable_interrupts();
	
	for(unsigned char i = 0; i < buffer_end; i++)
	{
		keyboard_buffer[i] = keyboard_buffer[i+1];
	}
	buffer_end--;
	enable_interrupts();

	return keyboard_buffer[0];
}
But now that I'm trying to implement a scanf function I'm facing some issues, because everytime i press the shift, control or bloq mayus key for example they get stored in the keyboard_buffer, wich may be usefull later (to for example abort a process pressing control + c) but are not as usefull when I just want to read from the keyboard, so I have some questions:

1. I have been thinking of declaring an array of bools that store what keys are being pressed and then use that information in my getch function to filter out the ones that can't be pressed (F2 or insert for example) Do you think this is a good idea?
2. Also in my current implementation every key stroke is stored in the keyboard buffer even if no process is reading from the keyboard so when a proccess starts reading from the keyboard it first gets values that aren't necesary, to fix that i have a function that simply changes the buffer end to 0 but i don't think that is a fancy way of doing it. Any ideas?

Thank you in advance and if you see something that is wrong or could be improved comment it please!
User avatar
narke
Member
Member
Posts: 119
Joined: Wed Dec 26, 2007 3:37 am
Location: France

Re: Keyboard driver advice

Post by narke »

I think that you could strore key strokes in the buffer when they are printable characters, according to http://wiki.osdev.org/PS/2_Keyboard (Scan Code Set 1) most scancodes which are less than 0x80.
I'm doing in that way.
OS for PowerPC Macs: https://github.com/narke/Einherjar
Operating system: colorForth computing environment for x86.: https://github.com/narke/Roentgenium
onlyonemac
Member
Member
Posts: 1146
Joined: Sat Mar 01, 2014 2:59 pm

Re: Keyboard driver advice

Post by onlyonemac »

Generally there are two keyboard APIs provided by an operating system. The first is the key array/bitmap - this stores the state (pressed or released) of each key, identified by some OS-standardised and meaningful way (preferably *not* the scancode from the PS/2 driver, as that depends somewhat on the keyboard layout and scancode set in use, and using your own set of meaningless numbers will make game developer's job a lot harder). The second is the buffer of characters typed. The operating system might also allow a process to attach a callback to particular keyboard events, such as a particular key being pressed or released.

The key array should be initialised such that each key is in the "released" state, and the keyboard buffer should be empty. Then you would do something as follows:
  • When a key is pressed: Mark it as pressed in the key array. If it's a printable character, add it to the end of the keyboard buffer. If there are any callbacks attached to this key being pressed, call them.
  • When a key repeats: If it's a printable character, add it to the end of the keyboard buffer. If there are any callbacks attached to this key repeating, call them.
  • When a key is released: Mark it as released in the key array. If there are any callbacks attached to this key being released, call them.
Note that in determining if a key is being pressed or repeated, you might need to look in the key array to see if it's already marked as pressed - if not, then it's being pressed; if yes, then it's repeating. As far as I can remember, the PS/2 protocol sends key repeats exactly the same as keys being pressed. (Alternatively, you might want to ignore repeat presses from the PS/2 driver and implement your own key repeat system, where you can change the repeat delay/rate and decide for yourself which keys get repeated and when.)
When you start writing an OS you do the minimum possible to get the x86 processor in a usable state, then you try to get as far away from it as possible.

Syntax checkup:
Wrong: OS's, IRQ's, zero'ing
Right: OSes, IRQs, zeroing
User avatar
SpyderTL
Member
Member
Posts: 1074
Joined: Sun Sep 19, 2010 10:05 pm

Re: Keyboard driver advice

Post by SpyderTL »

Do you support, or do you ever want to support multiple keyboards?

For example, I'm using a USB Keyboard/Mouse combination on my laptop, which also has a PS/2 connected keyboard, built in. In Windows, I can type on one for a while, and then type on the other, with no problems. But interesting questions arise, like what if I press Shift on one keyboard, and hit A on the other keyboard? I get: A

Surprise! Windows keeps up with both keyboards, and one keyboard can affect the other. (I honestly wouldn't have expected that.)

But these questions you will need to answer, and design your input system to handle this situation properly. A bitmap of bools is a good idea, but different keyboards have different keys. So, in addition to a bitmap per keyboard, you may also need a standardized bitmap, per user, keeping track of what keys each user has pressed on all of the keyboards related to their current session.

Another idea is to use the USB Human Interface Device (HID) approach, which "collects" all of the pressed keys for each key press/release event. So, if you press Shift-F, and then release both keys, you'll get 4 events: Press Shift, Press F (+Shift), Release F (+Shift), Release Shift.

But, yes, you will need both a list of keys that are pressed/not pressed, AND an event system that will fire off an event every time the keyboard state changes.
Project: OZone
Source: GitHub
Current Task: LIB/OBJ file support
"The more they overthink the plumbing, the easier it is to stop up the drain." - Montgomery Scott
DevilGladiator
Posts: 11
Joined: Mon Aug 08, 2016 7:56 am

Re: Keyboard driver advice

Post by DevilGladiator »

onlyonemac wrote:Generally there are two keyboard APIs provided by an operating system. The first is the key array/bitmap - this stores the state (pressed or released) of each key, identified by some OS-standardised and meaningful way (preferably *not* the scancode from the PS/2 driver, as that depends somewhat on the keyboard layout and scancode set in use, and using your own set of meaningless numbers will make game developer's job a lot harder). The second is the buffer of characters typed. The operating system might also allow a process to attach a callback to particular keyboard events, such as a particular key being pressed or released.

The key array should be initialised such that each key is in the "released" state, and the keyboard buffer should be empty. Then you would do something as follows:
  • When a key is pressed: Mark it as pressed in the key array. If it's a printable character, add it to the end of the keyboard buffer. If there are any callbacks attached to this key being pressed, call them.
  • When a key repeats: If it's a printable character, add it to the end of the keyboard buffer. If there are any callbacks attached to this key repeating, call them.
  • When a key is released: Mark it as released in the key array. If there are any callbacks attached to this key being released, call them.
Note that in determining if a key is being pressed or repeated, you might need to look in the key array to see if it's already marked as pressed - if not, then it's being pressed; if yes, then it's repeating. As far as I can remember, the PS/2 protocol sends key repeats exactly the same as keys being pressed. (Alternatively, you might want to ignore repeat presses from the PS/2 driver and implement your own key repeat system, where you can change the repeat delay/rate and decide for yourself which keys get repeated and when.)
Sorry for the delay, I'll make a simple bool keystate array that stores the information of the most frequent keys and also a keyboard map that translates the current keyboard scancodes to the universal keystate array, and also a keyboard buffer with the printable characters in it as you suggest, it seems to be the best option

SpyderTL wrote:Do you support, or do you ever want to support multiple keyboards?

For example, I'm using a USB Keyboard/Mouse combination on my laptop, which also has a PS/2 connected keyboard, built in. In Windows, I can type on one for a while, and then type on the other, with no problems. But interesting questions arise, like what if I press Shift on one keyboard, and hit A on the other keyboard? I get: A

Surprise! Windows keeps up with both keyboards, and one keyboard can affect the other. (I honestly wouldn't have expected that.)

But these questions you will need to answer, and design your input system to handle this situation properly. A bitmap of bools is a good idea, but different keyboards have different keys. So, in addition to a bitmap per keyboard, you may also need a standardized bitmap, per user, keeping track of what keys each user has pressed on all of the keyboards related to their current session.

Another idea is to use the USB Human Interface Device (HID) approach, which "collects" all of the pressed keys for each key press/release event. So, if you press Shift-F, and then release both keys, you'll get 4 events: Press Shift, Press F (+Shift), Release F (+Shift), Release Shift.

But, yes, you will need both a list of keys that are pressed/not pressed, AND an event system that will fire off an event every time the keyboard state changes.
Wow, multiple keyboards, for now i'll try to keep things simple (this is my first kernel) but using the HID seems like a good idea
onlyonemac
Member
Member
Posts: 1146
Joined: Sat Mar 01, 2014 2:59 pm

Re: Keyboard driver advice

Post by onlyonemac »

If I were to implement support for multiple keyboards, I would do it like this (not saying that this is the right/best way, but just the way that I would do it that sounds simple and effective).

Building on my previous post, I would ignore any repeated key presses or key repeats from the keyboard; I would just say "when a key is pressed, mark it as pressed in the bitmap and fire a key press event (unless it was already marked as pressed)" and "when a key is released, mark it as released in the bitmap and fire a key release event (unless it was already marked as released)". Then, depending on the configured key repeat rate, I would fire "key repeat" events for each key that is currently pressed (I know that this would require separate tracking for each key, so I'd probably need an array storing the number of system ticks until the next repeat for each key individually, and fire the event when a particular timer reaches zero - alternatively to be more efficient I could just repeat the last key pressed, so I'd just need to keep track of which key was pressed last and a single repeat timeout).

To handle multiple keyboards, then, I would just combine the key presses and key releases from each keyboard. So for example keyboard A sends a key press signal for key '1', so I mark key '1' as pressed in the bitmap and fire the event. If keyboard B then sends a key press signal for key '1', I would see that key '1' is already marked as pressed in the bitmap and ignore the signal (i.e. don't send another event, don't reset the repeat timeout, etc.). If keyboard A then sends a key release signal for key '1', I would mark key '1' as released in the bitmap and fire the event. If keyboard B then sends a key release signal for key '1', I would see that key '1' is already marked as released in the bitmap and ignore the signal (i.e. don't send another event). I think this is kind of what SpyderTL was describing, but explained a bit more simply perhaps.

(Alternatively you could keep track of which keyboards have which keys pressed and only send the key release event when the same key has been released on all the keyboards, but that's a lot more complicated and potentially requires more RAM to store more bitmaps plus an array, and it's probably not necessary anyway.)
When you start writing an OS you do the minimum possible to get the x86 processor in a usable state, then you try to get as far away from it as possible.

Syntax checkup:
Wrong: OS's, IRQ's, zero'ing
Right: OSes, IRQs, zeroing
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: Keyboard driver advice

Post by Brendan »

Hi,
onlyonemac wrote:If I were to implement support for multiple keyboards, I would do it like this (not saying that this is the right/best way, but just the way that I would do it that sounds simple and effective).
For multiple keyboards, the first thing I'd want to do is be able to combine it with multiple displays in a "multiple users sharing same computer, where each user has one keyboard and one display" way. For this reason; at the lower levels (e.g. keyboard driver) the keyboards would need to remain independent (one keyboard driver can't be allowed to interfere with another).



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.
User avatar
SpyderTL
Member
Member
Posts: 1074
Joined: Sun Sep 19, 2010 10:05 pm

Re: Keyboard driver advice

Post by SpyderTL »

Brendan wrote:For multiple keyboards, the first thing I'd want to do is be able to combine it with multiple displays in a "multiple users sharing same computer, where each user has one keyboard and one display" way. For this reason; at the lower levels (e.g. keyboard driver) the keyboards would need to remain independent (one keyboard driver can't be allowed to interfere with another).
I would want the user(s) to decide how the keyboards should work. Assigning a keyboard to a screen is getting into virtual machine territory, so if you have support for virtual machines, maybe this is where this keyboard to screen mapping logic should live.

Ideally, who decides which hardware devices (screen, 3d accelerator, keyboard, sound card, network card, etc.) each "application" should have access to? The OS? The application? The user?

Traditionally, by default, the OS tries to completely hide the actual hardware from the application, instead giving it access to a virtual device that the OS can manage. The keyboard is no different.
Project: OZone
Source: GitHub
Current Task: LIB/OBJ file support
"The more they overthink the plumbing, the easier it is to stop up the drain." - Montgomery Scott
onlyonemac
Member
Member
Posts: 1146
Joined: Sat Mar 01, 2014 2:59 pm

Re: Keyboard driver advice

Post by onlyonemac »

SpyderTL wrote:I would want the user(s) to decide how the keyboards should work. Assigning a keyboard to a screen is getting into virtual machine territory, so if you have support for virtual machines, maybe this is where this keyboard to screen mapping logic should live.

Ideally, who decides which hardware devices (screen, 3d accelerator, keyboard, sound card, network card, etc.) each "application" should have access to? The OS? The application? The user?
Warning: don't get into this kind of discussion with Brendan.
SpyderTL wrote:Traditionally, by default, the OS tries to completely hide the actual hardware from the application, instead giving it access to a virtual device that the OS can manage. The keyboard is no different.
Yes, so the userspace application just sees a keyboard but it's up to the OS where the data that the application receives from that keyboard comes from (whether it's a single physical keyboard, multiple physical keyboards, and on-screen keyboard, and so on).
When you start writing an OS you do the minimum possible to get the x86 processor in a usable state, then you try to get as far away from it as possible.

Syntax checkup:
Wrong: OS's, IRQ's, zero'ing
Right: OSes, IRQs, zeroing
User avatar
Brendan
Member
Member
Posts: 8561
Joined: Sat Jan 15, 2005 12:00 am
Location: At his keyboard!
Contact:

Re: Keyboard driver advice

Post by Brendan »

Hi,
SpyderTL wrote:
Brendan wrote:For multiple keyboards, the first thing I'd want to do is be able to combine it with multiple displays in a "multiple users sharing same computer, where each user has one keyboard and one display" way. For this reason; at the lower levels (e.g. keyboard driver) the keyboards would need to remain independent (one keyboard driver can't be allowed to interfere with another).
I would want the user(s) to decide how the keyboards should work. Assigning a keyboard to a screen is getting into virtual machine territory, so if you have support for virtual machines, maybe this is where this keyboard to screen mapping logic should live.

Ideally, who decides which hardware devices (screen, 3d accelerator, keyboard, sound card, network card, etc.) each "application" should have access to? The OS? The application? The user?
Traditionally, for "multi-seat" the admin decides which devices get assigned to which "seat". It is only for user-related devices (keyboards, mice, microphones, displays, etc); and has nothing at all to do with any other hardware (memory, CPUs, network cards, storage devices, etc); and has nothing to do with virtual machines.

If an OS supports "multiple users at same time" (e.g. anything from plain old telnet all the way up to modern remote desktop like X or Windows terminal services), which is almost all desktop/server OSs since old time-sharing mainframes in the 1960s; then "multiple users at same time on same computer" is a triviality that requires almost nothing extra (e.g. it's little more than "remote desktop that isn't actually remote").


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.
Post Reply