Skip site navigation (1)Skip section navigation (2)
Date:      Thu, 31 Aug 95 22:01 CDT
From:      uhclem@nemesis.lonestar.org (Frank Durda IV)
To:        freebsd-hackers@freebsd.org
Cc:        uhclem@nemesis.lonestar.org
Subject:   Re: How to abort a DMA transfer?
Message-ID:  <m0soMMh-0004vyC@nemesis.lonestar.org>

next in thread | raw e-mail | index | archive | help
[0]Yes, as long as `single mode' is appropriate for you, there's no need
[0]to worry about TC.  TC is intented for continuous mode.  Well, i've
[0]just noticed that the PC DMAC cannot even generate an interrupt when
[0]ready...  hmm, go figure, the Z80 DMAC did it. :)
[0]
[0]And yes, for `single mode', the masking trick will do it.  The
[0]peripheral device will issue a DRQ signal for each transfered
[0]byte/word, and masking would prevent the DMAC from accepting new DRQs
[0]for this channel.  Aborting a continuous mode transfer would not be so
[0]easy (or even impossible at all).

Actually, masking is the correct procedure for all transfer modes on the
8237, even autoinit mode, which is frequently used for audio operations
since it allows seamless DMA transfers with no under/overruns.

You are generally correct about TC.  All the TC signal does is when the
counter on any channel in the DMA controller goes from one to zero, TC is
asserted.  What the peripherals are supposed to if they want to generate an
interrupt when the transfer is through, is that peripheral device is
supposed to look at (-DACK%d && TC && DEVICE_DMA_ACTIVE) and then latch an
IRQ%d for the 8259 interrupt controller.  Since there is only one TC
signal, it is important that only the peripheral who is transferring data
at that moment honor the TC signal.

The host CPU will eventually investigate the interrupt by having some driver
poll the hardware associated with the peripheral, NOT the DMA controller.
If a peripheral doesn't want an interrupt associated with the DMA counter
reaching zero, it doesn't implement the circuitry to monitor TC.

Some sound cards realize that when the TC hits zero it means the DMA
is now idle and that is really too late, so they don't use TC and
instead allow the driver to program in a local counter value, which
is usually set lower than the value programmed into the DMA.  This means
the peripheral can interrupt the CPU in advance of the DMA "running dry",
allowing the CPU to be ready to reprogram the DMA the instant it finishes
what it is doing, rather than incurring the latency later.

This also means that two or more different devices could share a DMA channel,
by tristating DRQ%d when idle and only honoring -DACK%d when the device knows
it is expecting the DMA to go active.  (Iomega PC2B boards forgot this
minor point and will transfer data even if they are not supposed to.)


So, if you want to abort a 8237 DMA transfer of any kind, simply mask the
bit for that DMA channel in the 8237.  Note: You can't interrupt an individual
transfer (byte or burst) in progress.  Think about it...  if the DMA is
running, how is your OUT instruction going to be performed?  
The CPU has to be bus master for the OUT to be performed.

Since the 8237 DMA re-evaluates DMA channel priorities constantly, even if
the DMA had already asserted HOLD (to request the bus from the CPU) when
the OUT actually took place, the processor would still grant the bus to the
DMA controller.  The DMA controller would look for the highest-priority
DMA source remaining (your interrupt is masked now) at that instant,
and if none remained, the DMA will release HOLD and the processor will
get the bus back after a few clocks.   

There is a deadly race condition in this area, but if I remember right,
you can't get into it via mis-programming the DMA, UNLESS you cause the DMA
controller to be RESET.  You should not do this.   Effectively the CPU
can give up the bus and the DMA doesn't do anything, including giving the
bus back.  Very annoying and after 16msec or so, all is over since
refresh on main memory has started failing.

So, mask the DMA controller, then go do what you have to do to get the
transfer aborted in the peripheral hardware.  In some extremely stupid
hardware (I could mention a few), you may have to program the DMA to
transfer one more byte to a garbage target to get the peripheral hardware
to go back to an idle state.   Most hardware these days isn't that
stupid.

Technically, you are supposed to mask the DMA channel, program the other
settings (direction, address, length, etc), issue commands to the
peripheral and then unmask the DMA channel once the peripheral commands have
been accepted.  The last two steps can be done out of order without
harm, but you must always program the DMA channel while it is masked to
avoid spraying data all over the place in the event the peripheral
unexpected asserts DRQ%d.

If you need to pad-out an aborted buffer, once you have masked the
DMA, you can ask it how many bytes it still had to go and what
address it was to write to next.  Your driver can then fill in the
remaining area or do what needs to be done.


Don't forget that the 8237 was designed for use with the 8085 and
really isn't suited to the job that IBM gave it in the original PC.
That's why the upper eight bits of DMA addressing appear to be lashed-on.
They are.  Look at the schematics of the original PC and you will
the upper bits are kept in external latches that are enabled whenever
the DMA is too.  Very kludgy.


Frank Durda IV <uhclem@nemesis.lonestar.org>|"How do I know?
or uhclem%nemesis@fw.ast.com (Fastest Route)| The 8237 is my fiend, no I 
...letni!rwsys!nemesis!uhclem               | mean it's my friend, well...
...decvax!fw.ast.com!nemesis!uhclem         | maybe I had it right earlier."





Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?m0soMMh-0004vyC>