Code: Select all
Ok, I think I got more than the half of the picture - my problem was/is, that I don't really know how to implement it...
So, if I press a key, my keyboard driver puts the ASCII code into a queue. That's fine.
Yes, in a ukernel a keyboard interrupt generates a msg which is passed to
the terminal task. In a monolithic kernel each interrupt should buffer
that event and eventually move the data to the correspondent
terminal input buffer.
Code: Select all
And when a user process does a getch(), the terminal driver gets the character from this input queue and gives it to the user.
But how is this done? The way from "keyboard driver -> terminal driver" is clear, but the way from "user -> terminal driver" isn't. For example, getch() function does nothing else than a READ (systemcall). And the read() function is not in the user library, but in the kernel library, isn't it? But what is the terminal driver? A function that is called on every READ? Or a task that is always running?
Do you get my problem?
Well, that is an alternative. The whole thing can be complicated for a
generic terminal or console driver because of the different BPS for each
device... For example the FreeBSD terminal driver first tries to know
the current console bps in order to know how to process the events...
FreeBSD uses what it calls "silos". they are really buffers. For each input character (a keyboard interrupt, for example) this silos are filled and the
terminal driver is called directly only when a special key appears (^c for example).
I'll assume you're writing just a keyboard-monitor driver
Now, this is how bsd does it...
The tty driver will have an input queue and an output queue. On a monolithic kernel you should copy the input to the input queue and then the terminal driver will have to act accordingly (cooked mode, raw mode, flush, etc). A call to getch() can efectually call read() to stdin and the stdin driver will know it has to read from the current driver input queue.
In FreeBSD each driver has a generic way of getting input. All calls
to read from a character-special device, result in a call to the device driver's d_read entry with a device number, a uio structure describing the data to be read, and a flag specifying whether the I/O is nonblocking. Terminal device drivers use the device number to locate the tty structure for the device, then call the line discilpine l_read entry to process the system call.
You also have two input queues. One for canonical mode and one for raw mode. Finally, ttread() checks for data in the appropiate queue (canonical or raw) and if no data are present, returns error if the terminal is using nonblocking I/O. Otherwise it sleeps on the address of the raw queue. When ttread() is awakened, it restarts processing from the beginning because the terminal state or process group might have changred while it was asleep.
When characters are present in the queue for which ttread() is awaiting, they are removed from the queue one at a time with getc() and are copied out to the user's buffer with ureadc(). This is because of you reading in kernel space and the getc() caller in user-space.
In canonical mode, certain characters receive special processing as they are removed from the queue: If there were no previous characters, the EOF results in the read returning zero characters, and that is interpreted by user programs as an effective EOF...
Hope it helps