If you have never thought about this kind of thing before, you may
be thinking, "Gee, my printer is 250 CPS. That means there can't be
more than 1/250th of a second between characters. Not enough time
to do anything useful." But of course it is enough time. In fact,
to the CPU, it's a very long time. The 486/33 that I am writing
this on will process 100,000 instructions or more in that 250th of
a second.
Even if you have thought about it, you still might think "So what's
the big deal? Send a character, let the CPU do something else until
the interrupt, and loop back. Simple.". Well, the driver could do
that, and it would work, but it wouldn't be a real good way to do
it. Consider that the data that's going to be printed is coming
from Somewhere. The driver could just read a character of that data
and shove it out the port as described. But that leaves the
original source of those characters all tied up until the printer
has printed every last byte. Wouldn't it be better to copy
Somewhere into a local buffer and print it out from that, thereby
freeing the original source?
Of course it would. So the driver for this printer allocates a
local buffer (which is a CLIST, by the way- in case you've seen
that term before and wondered what it was) and copies the Somewhere
bytes into that. The clist buffer is only so big, of course, so the
driver has to keep trying to print some characters while it's
filling the clist, and if Somewhere is big enough it may end up
tying up the process that's trying to get that stuff printed, but
the general effect is that it will get released sooner than it
would otherwise.
When the driver can't do anything else right now (the clist is full
and the printer isn't ready for another character), it goes to
sleep. That lets the kernel give some time to something else that
needs attention. But, until the clist is full, the driver should
keep on transferring characters.
So when that interrupt that says the printer is ready again does
come, our driver program could be doing almost anything. It could
be filling the clist, or it could be sleeping because the clist is
full. The interrupt says "I'm ready now", so it's time to send
another character. This is where it gets complicated.
It's a bit beyond the scope if this article to show exactly how
this can happen, but if the writer of this device driver is not
very careful, the driver can end up sleeping for ever. The problem
is trying to do two things: set a flag that says " I'm sleeping"
and then putting itself to sleep. An interrupt that sneaks in
between those two instructions is what will do the damage: it will
clear the flag that says "I'm sleeping", and then issue a "wakeup"
call to somebody that's already awake and working. No harm done
there, but then the interrupted process gets control again, does go
to sleep, but nobody knows that because the flag has been
cleared!
If that's totally confusing, don't feel bad. People who write this
kind of code get confused too. The important thing to grasp is that
the interrupt can come at any time, and the driver has to be
prepared to deal with it. The way it does this is by saying "Don't
send me any more interrupts for a while". Why not just say that
until the driver is nicely prepared to handle them again? Well,
there are two problems with that. One is that doing so may prevent
other devices from issuing interrupts, which tends to slow thing
down more than a bit.
The second problem doesn't apply to the printer we are considering
here, but what about a serial port that is receiving data which the
driver is responsible for sending along somewhere else? That serial
port usually has a buffer of it's own, but it's pretty small. When
it wants attention ("take some of these characters from me!") it
pretty much wants it NOW. The driver can't go off on an extended
vacation and come back to work on this side of things when it feels
up to the task. If a driver tried that, the serial port would
overflow and data would be lost.
The driver also has to be prepared to handle both extra and totally
missing interrupts. An interrupt can arrive out of nowhere for no
reason whatsoever. Worse, the device might never issue any
interrupts at all. That apparently happens frequently with parallel
printer ports. To handle it, the driver writer has to make some
guesses about how long is a safe enough time to wait before
proceeding.
And that brings us to the other difficulty in writing driver code:
the finicky hardware itself. As a general fuzzy sort of thing, most
hardware works by the CPU writing something Somewhere and perhaps
reading a status from Somewhere or Somewhere else.
All very nice, but the particular piece of hardware can be
extremely fussy about how you write it and how fast you attempt to
read back that status. Often you have to write to several different
places and then send a "strobe" to say "go ahead, work on what I
just gave you".
Let's take that parallel printer port as an example. One book
suggests that on initialization, the printer port should send back
a status of hexadecimal "e0". My printer port reports back "e2".
This kind of unexpected behaviour is what gives device driver
writers headaches.
But, you probably never will have to write a device driver.
Whatever equipment you are adding undoubtedly comes with it's own
driver and it's probably a lot better than you or I could write
anyway.
Next: SCO Device Drivers Part
III
© 1998 Anthony Lawrence. All rights reserved.
Got something to add? Send me email.
More Articles by Tony Lawrence © 2012-07-14 Tony Lawrence
Writing in C or C++ is like running a chain saw with all the safety guards removed. (Bob Gray)
Printer Friendly Version
Understanding Device Drivers Part II Copyright © December 1998 Tony Lawrence
Have you tried Searching this site?
This is a Unix/Linux resource website. It contains technical articles about Unix, Linux and general computing related subjects, opinion, news, help files, how-to's, tutorials and more.
Contact us
Printer Friendly Version