Subject: Re: interrupting parallel port lossage
To: Bruce Evans , Ken Hornstein <kenh@cmf.nrl.navy.mil>
From: Bakul Shah <bakul@netcom.com>
List: port-i386
Date: 03/23/1995 09:16:20
> >C->D == *STROBE high to DATA tri-state == 0.5 us min.

bruce says:
> Do you think the software needs to delay for C->D?

Strictly speaking, yes!  Unless we set bit5 of the control
port of a bidir.  PIO port right after taking *STROBE away
we don't have to worry about it.

> If it returns from an interrupt handler, a relatively huge delay is hard
> to avoid.

Exactly!  But that is fine as the .5us are *minimum* times.

> >E->F == *ACK low to BUSY low == 7 us min.
> >F->G == BUSY low to *ACK high = 5 us min.

H&H (i.e.  The Art of Electronics) diagram is not too
specific about these times.  But I believe these are *NOT*
minimum times but typical times or perhaps, more likely,
they are *maximum* times.  Sorry about misleading you.  But
these only matter if you care about *ACK in your software.

> This is worse than I thought.  I thought F <= E for "good" printers.
> Many printer manuals hint that F == E (approximately) but don't specify
> exact enough timing.  The PC interface has the interrupt line connected
> to **ACK, so the interrupt is acknowledged soon after *ACK is asserted

I think that you are wrong about interrupt line, it is connected
to *ACK, not **ACK.  Thus you should get the interrupt when
*ACK is _deasserted_; i.e. at time G for a good printer.

What that means is for a good printer BUSY should be low
(i.e. printer is ready) by the time you are in the interrupt
handler.

>                       Perhaps the "good" printers bend the spec so that
> the PC interface works better.

I don't think so.  My characterizing E->F & F->G delays as
min times threw you off.  Sorry!

> Does the software have to worry about *ACK?  I think only *BUSY matters
> for sending data to the printer, and *ACK is specified so that
> interrupts and hardware latches can work.

The more I think about it, the more I am convinced that the
key is to keep the STROBE asserted until we see BUSY being
asserted by the printer.  Software should not worry about
*ACK at all.

> >To guard against errant printers, lptintr() should check
> >BUSY is low in a timed loop (breakout 5 us or BUSY low).
> >Normal printers won't cause any slow down.
>
> I think such checks should be left to a special test mode.

May be.  Let us at least get the good printers working :-)

> >I also noticed that the printer is never reset.  Normally I
>
> I think it is reset at boot time (if it is turned on :-).

I think it should be resettable via an ioctl if you think
it is wedged.  Even the BIOS has support for this!

ken says:
> Judging from this timing diagram, BUSY should go high before you put *STROBE
> high, right?  Maybe a check for this is really what's needed instead of
> a delay from C->D.

I think this should fix it.

> Maybe in a spin loop?  (Only check 5 times, for example).  What's the "right"
> way to do this?

The next time I can take my machine down I am going to try the
following.

int
lptintr(sc)
	struct lpt_softc *sc;
{
	int iobase = sc->sc_iobase;

#if 0
	if ((sc->sc_state & LPT_OPEN) == 0)
		return 0;
#endif

	/* is printer online and ready for output */
	if (NOT_READY_ERR())
		return 0;

	if (sc->sc_count) {
		u_char control = sc->sc_control;
		/* send char */
		outb(iobase + lpt_data, *sc->sc_cp++);
		inb(0x84);
		outb(iobase + lpt_control, control | LPC_STROBE);
		sc->sc_count--;
		if (NOT_READY()) {
			int i = 0;
			for (i = 0; i < DELAYCNT; i++) {
				inb(0x84);
				if (!NOT_READY())
					break;
			}
			if (i == DELAYCNT && NOT_READY_ERR()) {
				/* XXX recover... */
			}
		}
		outb(iobase + lpt_control, control);
		sc->sc_state |= LPT_OBUSY;
	} else
		sc->sc_state &= ~LPT_OBUSY;

	if (sc->sc_count == 0) {
		/* none, wake up the top half to get more */
		wakeup((caddr_t)sc);
	}

	return 1;
}

/* send char */ onto the taking away of the STROBE needs to be
put in a separate routine that is also used by the polling code.