Keyboard Input in Minix

    The purpose of this note is to indicate many of the details involved when a user process gets input from the keyboard in the Minix operating system. Three other processes are involved in making this happen, namely the File Server (FS), the TTY task and the Clock task. In addition, hardware and software interrupts are involved. Keep your eye on the bouncing key code and ASCII code!! What I've learned about this issue, I figured out by picking through the source code, and the discussions in Subsection 2.6.7, 2.6.8 (which we've discussed in detail already) and 3.9.3 (which we still need to discuss). You should especially read 3.9.3 in combination with the following notes. Trying to understand this code can cause a headache, but trying to deal with it in the absense of any understanding is likely to cause a bigger headache. Here goes....

    Let's start with the hardware interrupt that occurs whenever a key is pressed or released. We've talked about some of this already. Since the IRQ for a keyboard interrupt is one, the interrupt is handled by hwint01 (p.593), which uses irq_table (see p. 580) to find kbd_hw_int(), which is in kernel/keyboard.c on page 701. This uses scan_keyboard() (p. 706), which in turn uses in_byte() (in klib386.s on p. 624) to actually get the key code (which is not ASCII code) associated with the key that was pressed or released. Upon returning back to kbd_hw_int(), we find the instruction kb = kb_addr(), which actually means kb = kb_lines (see p. 699), where kb_lines is an array of kb_s structures (see p. 700). After storing the key code in the ibuf array in the appropriate kb_s structure, the function kbd_hw_int() uses the macro force_timeout() to set the global variable tty_timeout to zero. Later, when clock_handler() (p. 673) executes as part of handling a clock (hardware) interrupt, this will force a call (at line 11452) to tty_wakeup() (p. 697) which will send a message via interrupt() (p. 604) (and mini_send()) to the TTY task, informing it that there is keyboard input available for whoever wishes to have it. That's it in so far as hardware interrupts serving is concerned.

    To see what's going on with the TTY process, look at its main loop on page 679. Notice receive(...) used to receive messages. During clock interrupt handling, a "hardware message" was sent, causing TTY to skip over the body of its main loop, and start a new iteration of this loop. The first thing it sees at the top of this loop is a for loop that repeatedly calls handle_events(). One of these calls will handle the keyboard input that occurred. The code for handle_events() is on pages 686-7. This winds up calling kb_read() (p. 701-2) indirectly by means of the instruction at line 12279:
(*tp->tty_devread)(tp);
Note that *tp is a pointer to a struct tty_t, and the definition for such can be found on pages 675-6. Now kb_read() generally results in calling in_process (p. 688-91), which may do fancy stuff in connection with special keys, but which generally results in an ASCII character being inserted into the array *tp->inbuf (input circular buffer = input queue) via the instruction at line 12515:
*tp->tty_inhead++ = ch;
Upon returning from kb_read() to handle_events(), a call will be made to in_transfer() This checks to see if some process is waiting for KB (i.e. keyboard) input. If so, the KB input is transfered to it, and then tty_reply() is used to send a message to FS (not to the user process that requested the KB input), to let it know that the transfer has been made, so that FS can then tell the process that requested the input, and let it start running again.

    OK, now let's come in from the other end. A user process wants to request some KB input. How does it do this, and what happens? The user needs to issue a READ system call in conncetion with the pseudo-file /dev/tty. As with all system calls, this results in a software interrupt ("trap"). These are handled by s_call (p. 595) much as hwint?? handles hardware interrupts. Now s_call calls sys_call() (p. 605) which uses mini_send() to send a request message to FS, and uses mini_rec() to get a reply back from FS. The user process suspends (blocks) while waiting for this reply from FS.

    Now look at the main loop for the FS process. This is on page 829. It uses get_work(), which calls receive(), to get a message requesting something. Let's assume that this message is a request for KB input. The call_vector array (p. 803) steers main() to the appropraite function, in this case, to do_read(). This calls read_write() (p. 843), which seeing that the input is suppose to come from a "character special file" (/dev/tty pseudo-file), calls dev_io() at line 23500. This code is on page 894. A message is sent to the TTY task via the instruction at line 27056:
(*dmap[major].dmap_rw)(task, & dev_mess);
Look at dmap on p. 804-5 and you'll see that the above translates to the following:
call_task(TTY, & dev_mess);
The code for call_task() is on page 898. Here is where the FS communication with the TTY task takes place.

    So now go back and look again at the main loop for the TTY task (p. 679) to see how it behaves in response to this message from FS. Upon getting the keyboard read request, it calls do_read() (p. 680). This will immediately grab KB input if it is already available in the buffer (i.e. KB input queue, i.e.*tp->tty_inbuf). An appropriate message is sent back to FS.

    A final suggestion. In order to follow the flow of inter-processes communication involved with keyboard input, pay close attention to Figure 3-37 on page 252 and the discussion concerning it.