[comp.os.minix] PS/2 level-triggered interrupts

ast@cs.vu.nl (Andy Tanenbaum) (01/17/89)

I sort of take back what I said earlier about the 8259A.  I went and found a
data sheet and tried to make some sense out of it.  I am not a programmable
interrupt controller wizard, so take this with a metric ton of salt.

The data sheet says: "The interrupt request must be removed before the EOI
command is issued or the CPU interrupt is enabled to prevent a second
interrupt from occurring."

I suspect what they are trying to say is that if the EOI is issued when the
level-triggered interrupt is still asserted, you get another interrupt.
Thus, there is no way to have the 8259A fix the problem.  The floppy disk
controller must be given some sort of command to tell it to negate the
interrupt.  Furthermore, every other device (clock, hard disk, network, 
etc) that can generate interrupts has the same problem.

I wonder if somebody over in comp.sys.ibm can help.  I'll post a query there.
Andy Tanenbaum (ast@cs.vu.nl)

srw%pws1@Sun.COM (Scott Wiesner) (01/18/89)

From article <1907@ast.cs.vu.nl>, by ast@cs.vu.nl (Andy Tanenbaum):
> I suspect what they are trying to say is that if the EOI is issued when the
> level-triggered interrupt is still asserted, you get another interrupt.
> Thus, there is no way to have the 8259A fix the problem.  The floppy disk
> controller must be given some sort of command to tell it to negate the
> interrupt.  Furthermore, every other device (clock, hard disk, network, 
> etc) that can generate interrupts has the same problem.

Yes!  That's what my earlier article was trying to say.  For the floppy, 
the command happens to be a sense interrupt status command, which has
a value of 8.  On some devices (like the serial port), just reading the
status (which you do anyway) causes the interrupt to reset.  

Scott

wheels@mks.UUCP (Gerry Wheeler) (01/19/89)

In article <1907@ast.cs.vu.nl>, ast@cs.vu.nl (Andy Tanenbaum) writes:
> I suspect what they are trying to say is that if the EOI is issued when the
> level-triggered interrupt is still asserted, you get another interrupt.
> The floppy disk
> controller must be given some sort of command to tell it to negate the
> interrupt.

While I'm not familiar with the PS/2 family, that sounds similar to
other computers I have used, and is really the essence of using level
sensitive interrupts.  If you have several IO boards connected to a
single interrupt line, perhaps several serial ports, there may be more
that one asserting the interrupt at one time.  You get the interrupt,
poll the devices to find who caused it, service the first device found,
it releases the interrupt, you send EOI, and get another interrupt to
remind you there is someone else still awaiting service.

In theory, this seems far superior to edge sensitive interrupts, because
in that case you cannot have more than one device on an interrupt.  In
practice, when it can't be made to work, the superiority may not be
appreciated.  :-)

-- 
     Gerry Wheeler                           Phone: (519)884-2251
Mortice Kern Systems Inc.               UUCP: uunet!watmath!mks!wheels
   35 King St. North                             BIX: join mks
Waterloo, Ontario  N2J 2W9                  CompuServe: 73260,1043

jms@systime.UUCP (John Skelton) (01/23/89)

In article <1907@ast.cs.vu.nl> you write:
>I suspect what they are trying to say is that if the EOI is issued when the
>level-triggered interrupt is still asserted, you get another interrupt.

This is correct.

Approximately the opposite occurs with edge-triggered interrupts.
In an interrupt routine, if you do nothing to remove the interrupt request
from the I/O device, issue an EOI, and return (IRET, most likely)...
Result: no more interrupts from that device, unless/until the device removes
its interrupt request and re-asserts it.

The Intel/NEC data sheets for the 8272/765 mention using Sense Interrupt Status
multiple times until you get an error (value 0x80, I recall).  This is
so that you know that the chip has told you of every event it had
outstanding -- including any that happened while you were in your interrupt
routine.  Given that at some time the chip had no more to say, it will have
removed its interrupt request.  Another event may, of course, happen while
you are finishing off processing in your interrupt routine.  So long as
you get the software handling the interaction between 765 and 8259A right
things will work, whether another event occurs before your IRET or not.
(If another event does occur, another interrupt will come in after you
leave your interrupt routine.  Well, actually, the request will be pending
as soon as the event occurs... you'll get the interrupt when you re-enable
interrupts, which is normally done by the IRET.)

The problem is getting the interaction between 765 and 8259A right. 

With edge-triggering, it's usually enough to issue a non-specific EOI as
soon as the interrupt routine starts; you don't need to persuade the I/O
device to remove its interrupt request until later.  If there can be
multiple reasons for the interrupt (e.g. several I/O events recognised by
one device, or several I/O devices sharing an interrupt line -- OR'ed
together, that is), you *MAY* need to service every event.  The *MAY* is
there to allow for devices which dutifully lower and raise their interrupt
request every time you service ONE event.  Note that the 765 does NOT do
this -- if it has several things to tell you about, it just leaves its
request outstanding.  In fact, many chips used with Intel micros leave
their interrupt requests raised in just this way.

Level-triggering is better/worse/different...  What you certainly must
*NOT* do is to issue the EOI before servicing the device.  Doing this
is likely to result in the 8259A seeing what it thinks is a new request.
Worse, this request will probably vanish when you service the actual I/O
device.  The 8259A issues a type 7 interrupt in this situation -- in
theory (i.e. as if the device attached to its interrupt request 7 pin
had interrupted).

It is possible to get these type 7 interrupts in edge-triggering, but
not quite so easy :-).  An example would be doing I/O with interrupts
temporarily disabled, and re-enabling at just the wrong time.  This
would be when the non-interrupt-driven code had nearly finished servicing
the cause of the interrupt request (i.e. has just satisfied the I/O
hardware), and then re-enables interrupts very promptly.  Normally,
this possibility can be ignored, but devices with high interrupt rates
(e.g. RS232) can be the cause, as can very fast CPU's.

The "in theory" above is because the 8259A doesn't do much in the way of
latching interrupt requests.  In fact, I think it ONLY latches requests
when it sees the CPU circuitry issuing Interrupt Acknowledge cycles.
In a sense, the 8259A is a priority-encoder asking the CPU for an interrupt
sequence.  The Intel '86 family CPU's don't latch interrupt request, so if
the 8259A experiences an interrupt request which vanishes, the CPU will
never notice it if its interrupt recognition was disabled at the time.  If,
however, the CPU does "see" and act on the interrupt request from the
8259, it will do some special hardware Interrupt Acknowledge cycles.
The 8259A *HAS* to respond to these with *SOMETHING*.  In the sad case
where you caused a transient interrupt request to the 8259A from some I/O
device, and no other device on that particular 8259A happens to need an
interrupt servicing at the instant when the Interrupt Acknowledge cycles
come in, the 8259A just pretends that it was a type 7 interrupt.

Personally, I don't like the way Intel do interrupts; the PDP-11 was
much nicer.  However, the Intel way does work 100% given exactly correct
code.  It's such a shame that the code is hard to get right, and that the
data sheets for such devices as the 8259A and NEC 765 / Intel 8272 are
so bad!!

Enough.  Sorry.  Hope this is some use.  I have Intel-produced flowchart(s)
of sense interrupt status, etc. code somewhere if you want copies.
 

stevel@eleazar.dartmouth.edu (Steve Ligett) (01/25/89)

I think the best way of fixing the interrupt problem for floppy disks is
to call fdc_results within disk_int and return the status bytes in the
message.

Or, read out the first status byte *only*, in disk_int.  It is reading
this status byte that turns off the interrupt request from the fdc.

My PC isn't yet up to running Minix, but here's what I'd try:

1.  On receiving an interrupt, read the main status reg (3f4).
    If both bits 4 and 5 (NON_DMA & FDC_BUSY) are zero, a Seek
    or Recalibrate caused the interrupt, so issue the Sense
    Interrupt Status command.

2.  Either call fdc-results or read out status reg 0 to turn off
    the interrupt.  Return the result(s) in the message to the floppy
    task.

3.  Modify the code after each
    receive (HARDWARE, &mess);
    in the floppy task to handle the results properly.
Steve Ligett       steve.ligett@dartmouth.edu or
(decvax harvard linus true)!dartvax!steve.ligett

steve@basser.oz (Stephen Russell) (01/25/89)

In article <11932@dartvax.Dartmouth.EDU> stevel@eleazar.dartmouth.edu (Steve Ligett) writes:
>I think the best way of fixing the interrupt problem for floppy disks is
>to call fdc_results within disk_int and return the status bytes in the
>message.
> [ rest deleted ]

Although this may fix the floppy driver, it becomes real messy when
adding drivers for mice, network boards, hi-res graphics, et.al. If
the problem is that the _drivers_ are the only code that can reset the
interrupting device properly, then the only solution seems to be to abandon
the "all interrupts go through one place" strategy. One (reasonably) clean
solution may be something like this:

- Use a common interrupt handler only for the purposes of saving the
registers. The common handler then does an indirect call through a local
table of interrupt handler addresses. This assumes that the common handler
has some _quick_ way to identify the source of the interrupt (the 8259?).

- Device drivers "register" a handler at start-up for their interrupt
number. The private handler looks after reading the status, etc, clears
the interrupt request, re-enables interrupts (which have been disabled long
enough by now), and returns to the common handler, which sends the message
to the driver task.

- Until a handler is registered, the call table contains the address of
a panic routine. This is probably a bit drastic - can the interrupt just
be ignored, at least initially?

- Where do the private handlers save the device status? I guess static
storage in the driver code would be fine, so long as there are no overruns.
These are only likely for keyboard and RS232 input - the rest of the
devices won't interrupt again until given something to do by the driver
task. Drivers that experience overruns could disable the device input interrupt
until the task is activated. Incrementing a counter each time the interrupt
handler is called, which is reset by the task, allows overruns to be detected.

The advantage of this approach is that the device interrupt handling
logic is where it should be: in the code for each device driver, not in
the interrupt handler.  If the cost of the indirect call is too much
extra burden, then we are stuck with lots of little bits of assembler
for each device, which are triggered directly by the interrupt. This
maximises speed, at the cost of duplication of effort among the
drivers.

[It's been a while since I looked at Minix. If lots has changed since
1.1, and the above comments are a heap of noise, my apologies.]

ast@cs.vu.nl (Andy Tanenbaum) (01/26/89)

In article <11932@dartvax.Dartmouth.EDU> stevel@eleazar.dartmouth.edu (Steve Ligett) writes:
>I think the best way of fixing the interrupt problem for floppy disks is
>to call fdc_results within disk_int and return the status bytes in the
>message.

I am not sure if this is a suitable solution in general, i.e., one probably
needs analogous hacks for the printer, ethernet, hard disk, serial port, etc.,
and that comes close to putting a fair amount of the logic in the interrupt
routine, something that has been avoided so far.

Andy Tanenbaum (ast@cs.vu.nl)

stevel@eleazar.dartmouth.edu (Steve Ligett) (01/30/89)

In article <1974@ast.cs.vu.nl> ast@cs.vu.nl (Andy Tanenbaum) writes:
>In article <11932@dartvax.Dartmouth.EDU> stevel@eleazar.dartmouth.edu (Steve Ligett) writes:
>>I think the best way of fixing the interrupt problem for floppy disks is
>>to call fdc_results within disk_int and return the status bytes in the
>>message.
>
>I am not sure if this is a suitable solution in general, i.e., one probably
>needs analogous hacks for the printer, ethernet, hard disk, serial port, etc.,
>and that comes close to putting a fair amount of the logic in the interrupt
>routine, something that has been avoided so far.
>
>Andy Tanenbaum (ast@cs.vu.nl)

I should have said why I suggested that option.  There were 4 options
that I looked at:

1.  Read out the main status reg of the fdc.  Sadly, that didn't affect
the INT line.

2.  Mask interrupts in the 8259.  That's what Steve Ackerman has
working, I believe.  The problem with that is that you've now thrown
away the reason for level-sensitive interrupts, and I thought it was too
early in the game to give them up.

3.  Read out a status reg as I suggested above.  I didn't mean to say I
thought it was a great idea, just that I hadn't found one better.

4.  Set the bit in the DOR (3f2) to mask interrupts.  Now, this one
sounds good.  However, it didn't seem to work, when I tried it in DEBUG.
But, I think I goofed in my testing, and will try again later this week,
unless someone else has tried and posts results.  This way sounds like
the best way that I can see - it masks interrupts from the fdc, but
nothing else.  More later.

Steve Ligett       steve.ligett@dartmouth.edu or
(decvax harvard linus true)!dartvax!steve.ligett

ast@cs.vu.nl (Andy Tanenbaum) (01/30/89)

In article <12009@dartvax.Dartmouth.EDU> stevel@eleazar.dartmouth.edu (Steve Ligett) writes:
>2.  Mask interrupts in the 8259.  That's what Steve Ackerman has
>working, I believe.  The problem with that is that you've now thrown
>away the reason for level-sensitive interrupts, and I thought it was too
>early in the game to give them up.

That is also the solution I proposed a couple of days ago.  I don't think
it defeats the purpose of level-triggered interrupts.  Its purpose is to
get around the fact that there aren't enough interrupt lines.  By masking out
interrupt 14 until the floppy disk task runs, it is true that you inhibit
interrupts by any other device on that line.  However, when the floppy disk
task runs and senses the FDC and sends the EOI, the other device will then
generate its interrupt.  I don't think anything is lost.

The solution proposed by Steve Ligett, which is also what Adrie Koolen did,
has the disadvantage of requiring the interrupt routine to do whatever magic
thing is needed to cause it to negate the interrupt.  For the floppy it is
a sense.  Heaven knows what it is for an Ethernet board or a laser printer.
I prefer not to have a lot of device specific code in the interrupt handlers
if possible.  Another point is that for some devices it may take a fair
amount of work to make the thing shut up, which means running for a long
time with interrupts disabled.

More discussion on the pros and cons of the various methods is welcome.

Andy Tanenbaum (ast@cs.vu.nl)