NetBSD Problem Report #26358

Received: (qmail 26715 invoked by uid 605); 19 Jul 2004 01:18:07 -0000
Message-Id: <200407190101.i6J11oVf006462@Plectere.com>
Date: Sun, 18 Jul 2004 18:01:50 -0700 (PDT)
From: Paul Shupak <paul@Plectere.com>
Sender: gnats-bugs-owner@NetBSD.org
Reply-To: paul@Plectere.com
To: gnats-bugs@gnats.NetBSD.org
Subject: the "lpt" at "atppc" is VERY slow in "STD" mode w/ interrupts
X-Send-Pr-Version: 3.95

>Number:         26358
>Category:       kern
>Synopsis:       the "lpt" at "atppc" is VERY slow in "STD" mode w/ interrupts
>Confidential:   no
>Severity:       serious
>Priority:       medium
>Responsible:    kern-bug-people
>State:          open
>Class:          change-request
>Submitter-Id:   net
>Arrival-Date:   Mon Jul 19 01:19:00 +0000 2004
>Closed-Date:    
>Last-Modified:  Sat Jul 31 11:20:01 +0000 2004
>Originator:     Paul Shupak
>Release:        NetBSD 2.0G
>Organization:
>Environment:
System: NetBSD cobalt 2.0G NetBSD 2.0G (COBALT-$Revision: 1.4 $) #562: Wed Jul 14 10:22:38 PDT 2004 root@cobalt:/sys/arch/i386/compile/COBALT i386
Architecture: i386
Machine: i386
>Description:
	When using the atppc attachment in non-dma "standard" mode with
interrupts (i.e. lptctl /dev/lpt1 mode standard dma  no ieee  no intr yes)
the system becomes completely unresponsive (each character causes a sleep/
wakeup pair of events and at least 2 transitions between contexts).
>How-To-Repeat:
	My test case is a ~200 megabyte Postscript file generated by Acrobat
from a ~195 MB ".pdf" generated by scanning a page at 1200dpi 48-bit color.
On a 850MHz P3 box feeding a HP LaserJet 6MP w/35M, the current code generates
a constant 26,000 to 28,000 interrupts a second a transfers about 22K/sec to
complete the job in ~9700 seconds with the machine completely unresponsive
for the entire time.  With the changes below, the interrupt rate falls to
either 17 or 18 a second (i.e. by about one thousand and five hundred fold)
 and the transfer rate goes up to ~ 33K/sec, with the machine slow, but
responsive (new time is ~6400 seconds).

	% ls -ls page.pdf page.ps
191200 -rw-r--r--   1 root        wheel     195732947 Jul 17 06:39 page.pdf
197728 -rw-r--r--   1 root        wheel     202425804 Jul 16 21:55 page.ps
	% lptctl /dev/lpt1 mode standard dma  no ieee  no intr yes
	% time cat page.ps > /dev/lpt1

	These are VERY large test cases. AND the rate is almost exactly one
tenth of what would be expected, after the "fix" below ( is "DELAY(1)"
really one usec.? - I'll have to check).  Still, the machine is usable
during printing (just slow), versus unusable.
	Any relevant dmesg out but is below (note, there are ten com ports
sharing the interrupt, and the "atppc" is behind a "puc" -- a separate PR
will eventually address the "un-modified" code crashing machine where the
ECP BAR isn't mapped exact 0x400 past the base PP BAR - like below).

--------------------------------------------------------------------------------
NetBSD 2.0G (SVCS) #235: Sat Jul 17 19:09:28 PDT 2004
        root@svcs:/sys/arch/i386/compile/SVCS
total memory = 2047 MB
avail memory = 1996 MB
...
000:11:1 0x1415 0x9523
        10h port 0x00006800 0x00000008
        14h port 0x00006400 0x00000004
        18h port 0x00006000 0x00000020
        1ch mem  0xeb800000 0x00001000
...
cpu0: Intel Pentium III (686-class), 856.07 MHz, id 0x683
...
puc0 at pci0 dev 7 function 0: SIIG PS8000 8S PCI 16C650 (20x family) (com, com,
 com, com, com, com, com, com)
com2 at puc0 port 0: interrupting at irq 10
com2: st16650a, working fifo
com3 at puc0 port 1: interrupting at irq 10
com3: st16650a, working fifo
com4 at puc0 port 2: interrupting at irq 10
com4: st16650a, working fifo
com5 at puc0 port 3: interrupting at irq 10
com5: st16650a, working fifo
com6 at puc0 port 4: interrupting at irq 10
com6: st16650a, working fifo
com7 at puc0 port 5: interrupting at irq 10
com7: st16650a, working fifo
com8 at puc0 port 6: interrupting at irq 10
com8: st16650a, working fifo
com9 at puc0 port 7: interrupting at irq 10
com9: st16650a, working fifo
...
puc1 at pci0 dev 11 function 0: Oxford Semiconductor OX16PCI952 UARTs (com, com)
com10 at puc1 port 0: interrupting at irq 10
com10: st16650a, working fifo
com11 at puc1 port 1: interrupting at irq 10
com11: st16650a, working fifo
puc1 at pci0 dev 11 function 0: Oxford Semiconductor OX16PCI952 UARTs (com, com)
com10 at puc1 port 0: interrupting at irq 10
com10: st16650a, working fifo
com11 at puc1 port 1: interrupting at irq 10
com11: st16650a, working fifo
puc2 at pci0 dev 11 function 1: Oxford Semiconductor OX16PCI952 Parallel port (l
pt)
atppc1 at puc2 port 0: AT Parallel Port
atppc1: interrupting at irq 10
atppc1: FIFO <depth,wthr,rthr>=<16,8,8>
atppc1: capabilities=3d<INTR,FIFO,PS2,ECP,EPP>
ppbus1 at atppc1
ppbus1: IEEE1284 negotiation: modes NIBBLE/ECP
ppbus1: IEEE1284 device found.
ppbus1: Probing for PnP devices.
ppbus1: <Hewlett-Packard HP LaserJet 6MP> PJL,MLC,PCLXL,PCL,POSTSCRIPT
lpt1 at ppbus1: port mode = 1<COMPATIBLE>
...

>Fix:
144d143
< static int atppc_std_intr_handler(struct atppc_softc * const);
641,649c640
< 		if (atppc->sc_use & ATPPC_USE_INTR) {
< 			claim = atppc_std_intr_handler(atppc);
< 			if (atppc->sc_outbstart
< 			   >= (atppc->sc_outb + atppc->sc_outb_nbytes))
< 				wake_up = WRITER;
< 			else
< 				wake_up = NONE;
< 		}
< 		else if (atppc->sc_outb)
---
> 		if (atppc->sc_outb)
2015,2102d2005
< /* Write the byte currentedly pointed to in the softc's buffer in "STD" mode. */
< static void
< atppc_std_write_byte(struct atppc_softc * const atppc, unsigned char ctr)
< {
< 	/*
< 	 * NOTE: the two microseconds of delays puts a 500KHz upper bound
< 	 * on the transfer speed!  If we had a "DELAY_ONE_HALF_US()" and
< 	 * a "DELAY_ONE_QUARTER_US()", we'd more than increase the maximum
< 	 * rate by more than double (or cut the overhead by at about five
< 	 * eighths).
< 	 */
< 	/* Put data in data register */
< 	atppc_w_dtr(atppc, *(atppc->sc_outbstart));
< 	atppc_barrier_w(atppc);
< 	DELAY(1); /* DELAY_ONE_QUARTER_US()/DELAY(.25) */
< 
< 	/* Pulse strobe to indicate valid data on lines */
< 	ctr |= STROBE;
< 	atppc_w_ctr(atppc, ctr);
< 	atppc_barrier_w(atppc);
< 	DELAY(1); /* DELAY_ONE_HALF_US()/DELAY(.5) */
< 	ctr &= ~STROBE;
< 	atppc_w_ctr(atppc, ctr);
< 	atppc_barrier_w(atppc);
< 
< 	return;
< }
< 
< /*
<  * If the peripheral is ready, try to write as many bytes as we can (in
<  * "STD" mode) during one interupt request. NOTE: There is no need to go
<  * between the "top" and "bottom" parts of the driver as long as the buffer
<  * isn't empty.
<  */
< static int
< atppc_std_intr_handler(struct atppc_softc * const atppc)
< {
< 	unsigned char str;
< 	unsigned char ctr;
< 	int any;
< 
< 	/*
< 	 * NOTE: the three microseconds of delays puts a 333KHz upper bound
< 	 * on the transfer speed!  If we had a "DELAY_ONE_HALF_US()" and a
< 	 * "DELAY_ONE_QUARTER_US()", we'd triple the maximum rate (or cut
< 	 * the overhead by two thirds).
< 	 */
< 	ctr = atppc_r_ctr(atppc);
< 	atppc_barrier_r(atppc);
< 
< 	any = 0;
< 	/* try to "chain" interrupts together */
< 	while (atppc->sc_outbstart < (atppc->sc_outb + atppc->sc_outb_nbytes)) {
< 		str = atppc_r_str(atppc);
< 		atppc_barrier_r(atppc);
< 		if ((str & SPP_MASK) != SPP_READY)
< 			break;
< 		atppc_std_write_byte(atppc, ctr);
< 		atppc->sc_outbstart++;
< 		any = 1;
< 		/* Give time for the peripheral to respond */
< 		DELAY(1); /* DELAY_ONE_QUARTER_US()/DELAY(.25) */
< 	}
< 	return any;
< }
< 
< /* Sleep the write while the `real' work occurs */
< static int
< atppc_wait_buf_done_or_error(struct atppc_softc * const atppc,
< 			     const caddr_t where)
< {
< 	int error;
< 
< 	/*
< 	 * NOTE: HP DeskJets can refuse data for greater than 45 seconds
< 	 * (ex. my PhotoSmart P1100 regularly pauses and refuses data for 35
< 	 * to 40 seconds on large jobs, even longer when duplexing).
< 	 */
< 	/* Wait for interrupt for 20 * MAXBUSYWAIT - after all we're sleeping */
< 	error = ltsleep(where, PPBUSPRI | PCATCH, __func__, 20 * MAXBUSYWAIT,
< 		ATPPC_SC_LOCK(atppc));
< 
< 	if (!error && (atppc->sc_irqstat & ATPPC_IRQ_nACK))
< 		atppc->sc_irqstat &= ~ATPPC_IRQ_nACK;
< 
< 	return error;
< }
<  
2126,2127c2029,2030
< 	if (atppc->sc_use & ATPPC_USE_INTR) {
< 		/* Wait for peripheral to become ready */
---
> 	while (atppc->sc_outbstart < (atppc->sc_outb + atppc->sc_outb_nbytes)) {
> 		/* Wait for peripheral to become ready for MAXBUSYWAIT */
2132,2134c2035,2038
< 		/* Start the sequence with the first byte */
< 		atppc_std_write_byte(atppc, ctr);
< 		atppc->sc_outbstart++;
---
> 		/* Put data in data register */
> 		atppc_w_dtr(atppc, *(atppc->sc_outbstart));
> 		atppc_barrier_w(atppc);
> 		DELAY(1);
2136,2154c2040,2053
< #if 0
< 		atppc->sc_outerr = atppc_wait_interrupt(atppc,
< 			atppc->sc_outb, ATPPC_IRQ_nACK);
< #else
< 		/*
< 		 * Put the writer to sleep until the buffers empty, or an error
< 		 * occurs.
< 		 */
< 		atppc->sc_outerr = atppc_wait_buf_done_or_error(atppc,
< 								atppc->sc_outb);
< #endif /* #if 0 */
< 		if (atppc->sc_outerr)
< 			return;
< 	} else {
< 		while (atppc->sc_outbstart
< 		      < (atppc->sc_outb + atppc->sc_outb_nbytes)) {
< 			/* Wait for peripheral to become ready */
< 			atppc->sc_outerr = atppc_poll_str(atppc, SPP_READY,
< 							  SPP_MASK);
---
> 		/* Pulse strobe to indicate valid data on lines */
> 		ctr |= STROBE;
> 		atppc_w_ctr(atppc, ctr);
> 		atppc_barrier_w(atppc);
> 		DELAY(1);
> 		ctr &= ~STROBE;
> 		atppc_w_ctr(atppc, ctr);
> 		atppc_barrier_w(atppc);
> 
> 		/* Wait for nACK for MAXBUSYWAIT */
> 		timecount = 0;
> 		if (atppc->sc_use & ATPPC_USE_INTR) {
> 			atppc->sc_outerr = atppc_wait_interrupt(atppc,
> 				atppc->sc_outb, ATPPC_IRQ_nACK);
2157,2162c2056
< 
< 			atppc_std_write_byte(atppc, ctr);
< 			atppc->sc_outbstart++;
< 
< 			/* Wait for nACK for MAXBUSYWAIT */
< 			timecount = 0;
---
> 		} else {
2170,2172d2063
< 
< 			/* Update buffer position, byte count and counter */
< 			atppc->sc_outbstart++;
2174a2066,2067
> 		/* Update buffer position, byte count and counter */
> 		atppc->sc_outbstart++;
>Release-Note:
>Audit-Trail:

From: jdolecek@NetBSD.org (Jaromir Dolecek)
To: paul@Plectere.com
Cc: gnats-bugs@netbsd.org
Subject: Re: kern/26358: the "lpt" at "atppc" is VERY slow in "STD" mode w/ interrupts
Date: Sat, 31 Jul 2004 10:49:00 +0200 (CEST)

 Can you please send the patch in unified diff format? Thanks.

 Jaromir
 -- 
 Jaromir Dolecek <jdolecek@NetBSD.org>            http://www.NetBSD.cz/
 -=- We should be mindful of the potential goal, but as the Buddhist -=-
 -=- masters say, ``You may notice during meditation that you        -=-
 -=- sometimes levitate or glow.   Do not let this distract you.''   -=-

From: Paul Shupak <paul@Plectere.com>
To: jdolecek@NetBSD.org
Cc: gnats-bugs@NetBSD.org, paul@Plectere.com
Subject: Re: kern/26358: the "lpt" at "atppc" is VERY slow in "STD" mode w/ interrupts
Date: Sat, 31 Jul 2004 04:18:08 -0700 (PDT)

 	Hello Jaromir,

 >Can you please send the patch in unified diff format? Thanks.

 	No problem.  Please note the `real' bug is actually port-i386/26365
 where DELAY(small_numbers) is off (waits too long) by a factor of 4 to 12
 on different (modern) machines.  This bug, reduces the overhead of the
 sleep/wake-up's about a thousand fold by assuming that interrupt latency
 is greater than DELAY(1) -- still true even in the face of port-i386/26365.

 	Anyway;  How about a "cvs diff -u".

 	I have other local changes (to allow PCI, pnpbios and ACPI
 attachments, I've separated the I/O space into two distinct regions
 - you can not assume the the ECP registers come exactly 0x400 after
 the base registers;  In fact, in the bug report, the PCI attached
 at "lpt1" has its ECP 0x400 BEFORE the other registers").  Too avoid
 some of the confusion, I've attempted to give you two diffs; The
 first is `my' version (and is nearly 1K lines long).  The second has
 just the changes from the bug report in question (and is only ~180
 lines, but of course the line numbers are WAY off).

 	BTW, I did the original testing (the numbers in the report) with
 the top of tree and just the interrupt changes, though the bug report was
 generated running a kernel with the entire set of "atppc" changes (for
 me, ECP non-DMA still doesn't work and I haven't tried "FAST_FIFO" mode,
 because ECP should work whenever it does, except for very old peripherals).

 	I've checked and the second patch applies cleanly to the top of
 the "-current" tree (and though I haven't checked, should build correctly
 also).  NOTE:  The diff still includes a "panic()" that has been carried
 forward in my tree by cvs "merges", but seem to have been removed from CVS
 - it really should be removed (impossible to reach - dead code).

 	If you need anything else, please ask,

 	paul shupak

 	% cvs diff -u atppc.c
 ********************************************************************************
 --------------------------------------------------------------------------------
 ---- ** First case * Entire local changes - requires changes elsewhere too ** --
 --------------------------------------------------------------------------------
 ********************************************************************************
 Index: atppc.c
 ===================================================================
 RCS file: /cvsroot/src/sys/dev/ic/atppc.c,v
 retrieving revision 1.16
 diff -u -r1.16 atppc.c
 --- atppc.c	21 Apr 2004 17:38:48 -0000	1.16
 +++ atppc.c	31 Jul 2004 10:13:23 -0000
 @@ -141,6 +141,7 @@
  static int atppc_wait_interrupt(struct atppc_softc * const, const caddr_t,
  	const u_int8_t);

 +static int atppc_std_intr_handler(struct atppc_softc * const);

  /*
   * Generic attach and detach functions for atppc device. If sc_dev_ok in soft
 @@ -155,6 +156,32 @@
  	struct parport_adapter sc_parport_adapter;
  	char buf[64];

 +#if 1 && ATPPC_DEBUG
 +	{
 +	ATPPC_DPRINTF(("%s: dtr = 0x%x, str = 0x%x, ctr = 0x%x\n",
 +			lsc->sc_dev.dv_xname, atppc_r_dtr(lsc),
 +			atppc_r_str(lsc), atppc_r_ctr(lsc)));
 +	atppc_barrier_r(lsc);
 +	if (lsc->sc_has & ATPPC_HAS_ECP) {
 +		u_int8_t ecr, cnfgA, cnfgB;
 +		ecr = atppc_r_ecr(lsc);
 +		atppc_barrier_r_e(lsc);
 +		atppc_w_ecr(lsc,
 +			(ATPPC_ECR_PS2|ATPPC_SERVICE_INTR|ATPPC_nFAULT_INTR));
 +		atppc_barrier_w_e(lsc);
 +		atppc_w_ecr(lsc,
 +			(ATPPC_ECR_CFG|ATPPC_SERVICE_INTR|ATPPC_nFAULT_INTR));
 +		atppc_barrier_w_e(lsc);
 +		cnfgA = atppc_r_cnfgA(lsc);
 +		cnfgB = atppc_r_cnfgB(lsc);
 +		atppc_barrier_r_e(lsc);
 +		atppc_w_ecr(lsc, ecr);
 +		atppc_barrier_w_e(lsc);
 +		ATPPC_DPRINTF(("%s: ecr = 0x%x, cnfgA = 0x%x, cnfgB = 0x%x\n",
 +				lsc->sc_dev.dv_xname, ecr, cnfgA, cnfgB));
 +	}
 +	}
 +#endif
  	ATPPC_LOCK_INIT(lsc);

  	/* Probe and set up chipset */
 @@ -292,7 +319,7 @@

  /* Detect parallel port I/O port: taken from FreeBSD code directly. */
  int
 -atppc_detect_port(bus_space_tag_t iot, bus_space_handle_t ioh)
 +atppc_detect_port(bus_space_tag_t iot, bus_space_handle_t ioh, bus_size_t len)
  {
          /*
  	 * Much shorter than scheme used by lpt_isa_probe() and lpt_port_test()
 @@ -307,23 +334,23 @@
  	 */

  	int rval;
 -	u_int8_t ctr_sav, dtr_sav, str_sav;
 +	u_int8_t ctr_sav, dtr_sav;

 -	/* Store writtable registers' values and test if they can be read */
 -	str_sav = bus_space_read_1(iot, ioh, ATPPC_SPP_STR);
 +	if (len < 3)
 +		return 0;
 +
 +	/* Store writable registers' values and test if they can be read */
  	ctr_sav = bus_space_read_1(iot, ioh, ATPPC_SPP_CTR);
  	dtr_sav = bus_space_read_1(iot, ioh, ATPPC_SPP_DTR);
 -	bus_space_barrier(iot, ioh, 0, IO_LPTSIZE,
 -		BUS_SPACE_BARRIER_READ);
 +	bus_space_barrier(iot, ioh, 0, len, BUS_SPACE_BARRIER_READ);

          /*
  	 * Ensure PS2 ports in output mode, also read back value of control
  	 * register.
  	 */
  	bus_space_write_1(iot, ioh, ATPPC_SPP_CTR, 0x0c);
 -	bus_space_barrier(iot, ioh, 0, IO_LPTSIZE,
 -		BUS_SPACE_BARRIER_WRITE);
 -
 +	bus_space_barrier(iot, ioh, 0, len, BUS_SPACE_BARRIER_WRITE);
 +	
  	if (bus_space_read_1(iot, ioh, ATPPC_SPP_CTR) != 0x0c) {
  		rval = 0;
  	} else {
 @@ -331,19 +358,17 @@
  		 * Test if two values can be written and read from the data
  		 * register.
  		 */
 -		bus_space_barrier(iot, ioh, 0, IO_LPTSIZE,
 -			BUS_SPACE_BARRIER_READ);
 +		bus_space_barrier(iot, ioh, 0, len, BUS_SPACE_BARRIER_READ);
  		bus_space_write_1(iot, ioh, ATPPC_SPP_DTR, 0xaa);
 -		bus_space_barrier(iot, ioh, 0, IO_LPTSIZE,
 -			BUS_SPACE_BARRIER_WRITE);
 +		bus_space_barrier(iot, ioh, 0, len, BUS_SPACE_BARRIER_WRITE);
  		if (bus_space_read_1(iot, ioh, ATPPC_SPP_DTR) != 0xaa) {
  			rval = 1;
  		} else {
  			/* Second value to test */
 -			bus_space_barrier(iot, ioh, 0, IO_LPTSIZE,
 +			bus_space_barrier(iot, ioh, 0, len,
  				BUS_SPACE_BARRIER_READ);
  			bus_space_write_1(iot, ioh, ATPPC_SPP_DTR, 0x55);
 -			bus_space_barrier(iot, ioh, 0, IO_LPTSIZE,
 +			bus_space_barrier(iot, ioh, 0, len,
  				BUS_SPACE_BARRIER_WRITE);
  			if (bus_space_read_1(iot, ioh, ATPPC_SPP_DTR) != 0x55) {
  				rval = 1;
 @@ -355,17 +380,43 @@
  	}

  	/* Restore registers */
 -	bus_space_barrier(iot, ioh, 0, IO_LPTSIZE,
 -		BUS_SPACE_BARRIER_READ);
 +	bus_space_barrier(iot, ioh, 0, len, BUS_SPACE_BARRIER_READ);
  	bus_space_write_1(iot, ioh, ATPPC_SPP_CTR, ctr_sav);
  	bus_space_write_1(iot, ioh, ATPPC_SPP_DTR, dtr_sav);
 -	bus_space_write_1(iot, ioh, ATPPC_SPP_STR, str_sav);
 -	bus_space_barrier(iot, ioh, 0, IO_LPTSIZE,
 -		BUS_SPACE_BARRIER_WRITE);
 +	bus_space_barrier(iot, ioh, 0, len, BUS_SPACE_BARRIER_WRITE);

  	return rval;
  }

 +/* Detect parallel port ECP I/O port */
 +int
 +atppc_detect_ecp_port(bus_space_tag_t iot, bus_space_handle_t eioh,
 +		bus_size_t len)
 +{
 +	u_int8_t ecr_sav;
 +	u_int8_t tmp;
 +	int rval;
 +
 +	if (len < 3)
 +		return 0;
 +
 +	/* Check for ECP */
 +	ecr_sav = bus_space_read_1(iot, eioh, ATPPC_ECP_ECR);
 +	bus_space_barrier(iot, eioh, 0, len, BUS_SPACE_BARRIER_READ);
 +	rval = 0;
 +	if ((ecr_sav & ATPPC_FIFO_EMPTY) && !(ecr_sav & ATPPC_FIFO_FULL)) {
 +		bus_space_write_1(iot, eioh, ATPPC_ECP_ECR, 0x34);
 +		bus_space_barrier(iot, eioh, 0, len, BUS_SPACE_BARRIER_WRITE);
 +		tmp = bus_space_read_1(iot, eioh, ATPPC_ECP_ECR);
 +		bus_space_barrier(iot, eioh, 0, len, BUS_SPACE_BARRIER_READ);
 +		if (tmp == 0x35)
 +			rval = 1;
 +	}
 +	bus_space_write_1(iot, eioh, ATPPC_ECP_ECR, ecr_sav);
 +	bus_space_barrier(iot, eioh, 0, len, BUS_SPACE_BARRIER_WRITE);
 +	return rval;
 +}
 +
  /* Detect parallel port chipset. */
  static int
  atppc_detect_chipset(struct atppc_softc *atppc)
 @@ -386,53 +437,53 @@
  static int
  atppc_detect_generic(struct atppc_softc *atppc)
  {
 -	u_int8_t ecr_sav = atppc_r_ecr(atppc);
 +	u_int8_t ecr_sav;
  	u_int8_t ctr_sav = atppc_r_ctr(atppc);
 -	u_int8_t str_sav = atppc_r_str(atppc);
  	u_int8_t tmp;
 +
  	atppc_barrier_r(atppc);

 +	/* Save any ECP */
 +	ecr_sav = 0; /* Just to shut up GCC */
 +	if (atppc->sc_has & ATPPC_HAS_ECP) {
 +		ecr_sav = atppc_r_ecr(atppc);
 +		atppc_barrier_r_e(atppc);
 +	}
 +
  	/* Default to generic */
  	atppc->sc_type = ATPPC_TYPE_GENERIC;
  	atppc->sc_model = GENERIC;

 -	/* Check for ECP */
 -	tmp = atppc_r_ecr(atppc);
 -	atppc_barrier_r(atppc);
 -	if ((tmp & ATPPC_FIFO_EMPTY) && !(tmp & ATPPC_FIFO_FULL)) {
 -		atppc_w_ecr(atppc, 0x34);
 -		atppc_barrier_w(atppc);
 -		tmp = atppc_r_ecr(atppc);
 -		atppc_barrier_r(atppc);
 -		if (tmp == 0x35) {
 -			atppc->sc_has |= ATPPC_HAS_ECP;
 -		}
 -	}
 -
 -	/* Allow search for SMC style ECP+EPP mode */
 -	if (atppc->sc_has & ATPPC_HAS_ECP) {
 -		atppc_w_ecr(atppc, ATPPC_ECR_EPP);
 -		atppc_barrier_w(atppc);
 -	}
 -	/* Check for EPP by checking for timeout bit */
 -	if (atppc_check_epp_timeout(&(atppc->sc_dev)) != 0) {
 -		atppc->sc_has |= ATPPC_HAS_EPP;
 -		atppc->sc_epp = ATPPC_EPP_1_9;
 +	if (atppc->sc_iolen > 4) {
 +		/* Allow search for SMC style ECP+EPP mode */
  		if (atppc->sc_has & ATPPC_HAS_ECP) {
 -			/* SMC like chipset found */
 -			atppc->sc_model = SMC_LIKE;
 -			atppc->sc_type = ATPPC_TYPE_SMCLIKE;
 +			atppc_w_ecr(atppc,
 +			  (ATPPC_ECR_EPP|ATPPC_SERVICE_INTR|ATPPC_nFAULT_INTR));
 +			atppc_barrier_w_e(atppc);
 +		}
 +		/* Check for EPP by checking for timeout bit */
 +		if (atppc_check_epp_timeout(&(atppc->sc_dev)) != 0) {
 +			atppc->sc_has |= ATPPC_HAS_EPP;
 +			atppc->sc_epp = ATPPC_EPP_1_9;
 +			if (atppc->sc_has & ATPPC_HAS_ECP) {
 +				/* SMC like chipset found */
 +				atppc->sc_model = SMC_LIKE;
 +				atppc->sc_type = ATPPC_TYPE_SMCLIKE;
 +			}
  		}
  	}

  	/* Detect PS2 mode */
  	if (atppc->sc_has & ATPPC_HAS_ECP) {
  		/* Put ECP port into PS2 mode */
 -		atppc_w_ecr(atppc, ATPPC_ECR_PS2);
 -		atppc_barrier_w(atppc);
 +		atppc_w_ecr(atppc,
 +			(ATPPC_ECR_PS2|ATPPC_SERVICE_INTR|ATPPC_nFAULT_INTR));
 +		(void) atppc_r_ecr(atppc); /* Needed for some PCI attachments */
 +		atppc_barrier_w_e(atppc);
  	}
 +
  	/* Put PS2 port in input mode: writes should not be readable */
 -	atppc_w_ctr(atppc, 0x20);
 +	atppc_w_ctr(atppc, nINIT|PCD);
  	atppc_barrier_w(atppc);
  	/*
  	 * Write two values to data port: if neither are read back,
 @@ -450,12 +501,32 @@
  		if (tmp != 0x55) {
  			atppc->sc_has |= ATPPC_HAS_PS2;
  		}
 +#if 1 && defined ATPPC_DEBUG
 +		else {
 +			const u_int8_t ctr = atppc_r_ctr(atppc);
 +			atppc_barrier_r(atppc);
 +			ATPPC_DPRINTF(("atppc: dtr == 0x55, no PS/2 mode! "
 +					"ctr == 0x%x\n", ctr));
 +		}
 +#endif
  	}
 +#if 1 && defined ATPPC_DEBUG
 +	else {
 +		u_int8_t ctr = atppc_r_ctr(atppc);
 +		atppc_barrier_r(atppc);
 +		ATPPC_DPRINTF(("atppc: dtr == 0xaa, no PS/2 mode! "
 +				"ctr == 0x%x\n", ctr));
 +	}
 +#endif

  	/* Restore to previous state */
 -	atppc_w_ecr(atppc, ecr_sav);
 +	if (atppc->sc_has & ATPPC_HAS_ECP) {
 +		atppc_w_ecr(atppc, ecr_sav);
 +		atppc_barrier_w_e(atppc);
 +		(void) atppc_r_ecr(atppc); /* Needed for some PCI attachments */
 +		atppc_barrier_r_e(atppc);
 +	}
  	atppc_w_ctr(atppc, ctr_sav);
 -	atppc_w_str(atppc, str_sav);
  	atppc_barrier_w(atppc);

  	return 0;
 @@ -472,9 +543,9 @@
  #endif
  	u_int8_t ecr_sav;
  	u_int8_t ctr_sav;
 -	u_int8_t str_sav;
  	u_int8_t cc;
  	short i;
 +	int rval = 0;

  	/* If there is no ECP mode, we cannot config a FIFO */
  	if (!(atppc->sc_has & ATPPC_HAS_ECP)) {
 @@ -483,58 +554,64 @@

  	/* save registers */
  	ecr_sav = atppc_r_ecr(atppc);
 +	atppc_barrier_r_e(atppc);
  	ctr_sav = atppc_r_ctr(atppc);
 -	str_sav = atppc_r_str(atppc);
  	atppc_barrier_r(atppc);

  	/* Enter ECP configuration mode, no interrupt, no DMA */
 -	atppc_w_ecr(atppc, (ATPPC_ECR_CFG | ATPPC_SERVICE_INTR) &
 -		~ATPPC_ENABLE_DMA);
 -	atppc_barrier_w(atppc);
 +	atppc_w_ecr(atppc, ATPPC_ECR_CFG|ATPPC_SERVICE_INTR|ATPPC_nFAULT_INTR);
 +	atppc_barrier_w_e(atppc);

  	/* read PWord size - transfers in FIFO mode must be PWord aligned */
 -	atppc->sc_pword = (atppc_r_cnfgA(atppc) & ATPPC_PWORD_MASK);
 -	atppc_barrier_r(atppc);
 +	cc = atppc_r_cnfgA(atppc);
 +	atppc_barrier_r_e(atppc);
 +	atppc->sc_pword = cc & ATPPC_PWORD_MASK;

  	/* XXX 16 and 32 bits implementations not supported */
  	if (atppc->sc_pword != ATPPC_PWORD_8) {
  		ATPPC_DPRINTF(("%s(%s): FIFO PWord(%d) not supported.\n",
  			__func__, dev->dv_xname, atppc->sc_pword));
 +		rval = EINVAL;
  		goto error;
  	}

 +	/* Is there a byte in the holding reg, that doesn't count in the FIFO */
 +	atppc->sc_xboa = !(cc & ATPPC_HOLD_REG_IN_FIFO);
 +
  	/* Byte mode, reverse direction, no interrupt, no DMA */
 -	atppc_w_ecr(atppc, ATPPC_ECR_PS2 | ATPPC_SERVICE_INTR);
 +	atppc_w_ecr(atppc, ATPPC_ECR_PS2|ATPPC_SERVICE_INTR|ATPPC_nFAULT_INTR);
 +	(void) atppc_r_ecr(atppc); /* Needed for some PCI attachments */
  	atppc_w_ctr(atppc, (ctr_sav & ~IRQENABLE) | PCD);
  	/* enter ECP test mode, no interrupt, no DMA */
  	atppc_w_ecr(atppc, ATPPC_ECR_TST | ATPPC_SERVICE_INTR);
 -	atppc_barrier_w(atppc);
 +	atppc_barrier_w_e(atppc);

  	/* flush the FIFO */
  	for (i = 0; i < 1024; i++) {
  		atppc_r_fifo(atppc);
 -		atppc_barrier_r(atppc);
 +		atppc_barrier_r_e(atppc);
  		cc = atppc_r_ecr(atppc);
 -		atppc_barrier_r(atppc);
 +		atppc_barrier_r_e(atppc);
  		if (cc & ATPPC_FIFO_EMPTY)
  			break;
  	}
  	if (i >= 1024) {
  		ATPPC_DPRINTF(("%s(%s): cannot flush FIFO.\n", __func__,
  			dev->dv_xname));
 +		rval = EINVAL;
  		goto error;
  	}

  	/* Test mode, enable interrupts, no DMA */
 -	atppc_w_ecr(atppc, ATPPC_ECR_TST);
 -	atppc_barrier_w(atppc);
 +	atppc_w_ecr(atppc, (ATPPC_ECR_TST|ATPPC_nFAULT_INTR));
 +	atppc_barrier_w_e(atppc);

  	/* Determine readIntrThreshold - fill FIFO until serviceIntr is set */
  	for (i = atppc->sc_rthr = atppc->sc_fifo = 0; i < 1024; i++) {
  		atppc_w_fifo(atppc, (char)i);
 -		atppc_barrier_w(atppc);
 +		atppc_barrier_w_e(atppc);
  		cc = atppc_r_ecr(atppc);
 -		atppc_barrier_r(atppc);
 +		atppc_barrier_r_e(atppc);
  		if ((atppc->sc_rthr == 0) && (cc & ATPPC_SERVICE_INTR)) {
  			/* readThreshold reached */
  			atppc->sc_rthr = i + 1;
 @@ -547,6 +624,7 @@
  	if (i >= 1024) {
  		ATPPC_DPRINTF(("%s(%s): cannot fill FIFO.\n", __func__,
  			dev->dv_xname));
 +		rval = EINVAL;
  		goto error;
  	}

 @@ -555,21 +633,22 @@
  	atppc_barrier_w(atppc);

  	/* Clear the serviceIntr bit we've already set in the above loop */
 -	atppc_w_ecr(atppc, ATPPC_ECR_TST);
 -	atppc_barrier_w(atppc);
 +	atppc_w_ecr(atppc, (ATPPC_ECR_TST|ATPPC_nFAULT_INTR));
 +	atppc_barrier_w_e(atppc);

  	/* Determine writeIntrThreshold - empty FIFO until serviceIntr is set */
  	for (atppc->sc_wthr = 0; i > -1; i--) {
  		cc = atppc_r_fifo(atppc);
 -		atppc_barrier_r(atppc);
 +		atppc_barrier_r_e(atppc);
  		if (cc != (char)(atppc->sc_fifo - i - 1)) {
  			ATPPC_DPRINTF(("%s(%s): invalid data in FIFO.\n",
  				__func__, dev->dv_xname));
 +			rval = EINVAL;
  			goto error;
  		}

  		cc = atppc_r_ecr(atppc);
 -		atppc_barrier_r(atppc);
 +		atppc_barrier_r_e(atppc);
  		if ((atppc->sc_wthr == 0) && (cc & ATPPC_SERVICE_INTR)) {
  			/* writeIntrThreshold reached */
  			atppc->sc_wthr = atppc->sc_fifo - i;
 @@ -579,38 +658,34 @@
  			/* If FIFO empty before the last byte, error */
  			ATPPC_DPRINTF(("%s(%s): data lost in FIFO.\n", __func__,
  				dev->dv_xname));
 +			rval = EINVAL;
  			goto error;
  		}
  	}

  	/* FIFO must be empty after the last byte */
  	cc = atppc_r_ecr(atppc);
 -	atppc_barrier_r(atppc);
 +	atppc_barrier_r_e(atppc);
  	if (!(cc & ATPPC_FIFO_EMPTY)) {
  		ATPPC_DPRINTF(("%s(%s): cannot empty the FIFO.\n", __func__,
  			dev->dv_xname));
 +		rval = EINVAL;
  		goto error;
  	}

 -	/* Restore original registers */
 -	atppc_w_ctr(atppc, ctr_sav);
 -	atppc_w_str(atppc, str_sav);
 -	atppc_w_ecr(atppc, ecr_sav);
 -	atppc_barrier_w(atppc);
 -
  	/* Update capabilities */
  	atppc->sc_has |= ATPPC_HAS_FIFO;

 -	return 0;
 -
  error:
  	/* Restore original registers */
 -	atppc_w_ctr(atppc, ctr_sav);
 -	atppc_w_str(atppc, str_sav);
  	atppc_w_ecr(atppc, ecr_sav);
 +	atppc_barrier_w_e(atppc);
 +	(void) atppc_r_ecr(atppc); /* Needed for some PCI attachments */
 +	atppc_barrier_r_e(atppc);
 +	atppc_w_ctr(atppc, ctr_sav);
  	atppc_barrier_w(atppc);

 -	return (EINVAL);
 +	return rval;
  }

  /* Interrupt handler for atppc device: wakes up read/write functions */
 @@ -629,15 +704,26 @@
  	/* Record registers' status */
  	atppc->sc_str_intr = atppc_r_str(atppc);
  	atppc->sc_ctr_intr = atppc_r_ctr(atppc);
 -	atppc->sc_ecr_intr = atppc_r_ecr(atppc);
  	atppc_barrier_r(atppc);
 +	if (atppc->sc_has & ATPPC_HAS_ECP) {
 +		atppc->sc_ecr_intr = atppc_r_ecr(atppc);
 +		atppc_barrier_r_e(atppc);
 +	}

  	/* Determine cause of interrupt and wake up top half */
  	switch (atppc->sc_mode) {
  	case ATPPC_MODE_STD:
  		/* nAck pulsed for 5 usec, too fast to check reliably, assume */
  		atppc->sc_irqstat = ATPPC_IRQ_nACK;
 -		if (atppc->sc_outb)
 +		if (atppc->sc_use & ATPPC_USE_INTR) {
 +			claim = atppc_std_intr_handler(atppc);
 +			if (atppc->sc_outbstart
 +			   >= (atppc->sc_outb + atppc->sc_outb_nbytes))
 +				wake_up = WRITER;
 +			else
 +				wake_up = NONE;
 +		}
 +		else if (atppc->sc_outb)
  			wake_up = WRITER;
  		else
  			claim = 0;
 @@ -710,7 +796,7 @@
  		panic("%s: chipset is in invalid mode.", dev->dv_xname);
  	}

 -	if (claim) {
 +	if (claim)
  		switch (wake_up) {
  		case NONE:
  			break;
 @@ -722,9 +808,12 @@
  		case WRITER:
  			wakeup(atppc->sc_outb);
  			break;
 -		}
 -	}

 +		default:
 +			panic("%s: this code should not be reached.\n",
 +				__func__);
 +			break;
 +		}
  	ATPPC_UNLOCK(atppc);

  	/* Call all of the installed handlers */
 @@ -737,7 +826,7 @@
  	}

  	splx(s);
 -
 +	
  	return claim;
  }

 @@ -846,7 +935,7 @@
  	atppc->sc_inb = atppc->sc_inbstart = NULL;
  	atppc->sc_inb_nbytes = 0;

 -	if (!(error))
 +	if (!error)
  		error = atppc->sc_inerr;

  	ATPPC_UNLOCK(atppc);
 @@ -943,7 +1032,7 @@
  	if (atppc->sc_has & ATPPC_HAS_ECP) {
  		/* Read ECR with mode masked out */
  		ecr = (atppc_r_ecr(atppc) & 0x1f);
 -		atppc_barrier_r(atppc);
 +		atppc_barrier_r_e(atppc);

  		switch (mode) {
  		case PPBUS_ECP:
 @@ -995,13 +1084,13 @@
  			goto end;
  		}

 -		/* Switch to byte mode to be able to change modes. */
 -		atppc_w_ecr(atppc, ATPPC_ECR_PS2);
 -		atppc_barrier_w(atppc);
 +		/* Switch to std. mode to be able to change modes. */
 +		atppc_w_ecr(atppc, ATPPC_ECR_STD);
 +		atppc_barrier_w_e(atppc);

  		/* Update mode */
  		atppc_w_ecr(atppc, ecr);
 -		atppc_barrier_w(atppc);
 +		atppc_barrier_w_e(atppc);
  	} else {
  		switch (mode) {
  		case PPBUS_EPP:
 @@ -1126,18 +1215,20 @@
  	 * Only wait for FIFO to empty if mode is chipset is ECP-capable AND
  	 * the mode is either ECP or Fast Centronics.
  	 */
 +	if (!(atppc->sc_has & ATPPC_HAS_ECP))
 +		goto end;
 +
  	r = atppc_r_ecr(atppc);
 -	atppc_barrier_r(atppc);
 +	atppc_barrier_r_e(atppc);
  	r &= 0xe0;
 -	if (!(atppc->sc_has & ATPPC_HAS_ECP) || ((r != ATPPC_ECR_ECP)
 -		&& (r != ATPPC_ECR_FIFO))) {
 +	if ((r != ATPPC_ECR_ECP) && (r != ATPPC_ECR_FIFO)) {
  		goto end;
  	}

  	/* Wait for FIFO to empty */
  	for (i = 0; i < ((MAXBUSYWAIT/hz) * 1000000); i += 100) {
  		r = atppc_r_ecr(atppc);
 -		atppc_barrier_r(atppc);
 +		atppc_barrier_r_e(atppc);
  		if (r & ATPPC_FIFO_EMPTY) {
  			goto end;
  		}
 @@ -1479,6 +1570,7 @@
  		break;
  	case PPBUS_WECR:
  		atppc_w_ecr(atppc, byte);
 +		(void) atppc_r_ecr(atppc); /* Needed for some PCI attachments */
  		break;
  	case PPBUS_WFIFO:
  		atppc_w_fifo(atppc, byte);
 @@ -1490,6 +1582,8 @@
  	}

  	atppc_barrier(atppc);
 +	if (atppc->sc_has & ATPPC_HAS_ECP)
 +		atppc_barrier_e(atppc);

  	ATPPC_UNLOCK(atppc);
  	splx(s);
 @@ -1855,6 +1949,7 @@
  	/* Check direction bit */
  	ctr = ctr_sav;
  	atppc_barrier_r(atppc);
 +	atppc_barrier_r_e(atppc);
  	if (!(ctr & PCD)) {
  		ATPPC_DPRINTF(("%s: ecp-mode read attempted without direction "
  			"bit set.", atppc->sc_dev.dv_xname));
 @@ -1868,7 +1963,7 @@

  	while (atppc->sc_inbstart < (atppc->sc_inb + atppc->sc_inb_nbytes)) {
  		ecr = atppc_r_ecr(atppc);
 -		atppc_barrier_r(atppc);
 +		atppc_barrier_r_e(atppc);
  		if (ecr & ATPPC_FIFO_EMPTY) {
  			/* Check for invalid state */
  			if (ecr & ATPPC_FIFO_FULL) {
 @@ -1887,7 +1982,7 @@
  				/* Enable interrupts */
  				ecr &= ~ATPPC_SERVICE_INTR;
  				atppc_w_ecr(atppc, ecr);
 -				atppc_barrier_w(atppc);
 +				atppc_barrier_w_e(atppc);
  				/* Wait for FIFO to fill */
  				atppc->sc_inerr = atppc_wait_interrupt(atppc,
  					atppc->sc_inb, ATPPC_IRQ_FIFO);
 @@ -1927,8 +2022,11 @@
  		atppc->sc_inbstart += worklen;
  	}
  end:
 -	atppc_w_ctr(atppc, ctr_sav);
  	atppc_w_ecr(atppc, ecr_sav);
 +	atppc_barrier_w_e(atppc);
 +	(void) atppc_r_ecr(atppc); /* Needed for some PCI attachments */
 +	atppc_barrier_r_e(atppc);
 +	atppc_w_ctr(atppc, ctr_sav);
  	atppc_barrier_w(atppc);
  }

 @@ -1949,7 +2047,7 @@
  	ecr &= ~ATPPC_SERVICE_INTR;
  	ecr |= ATPPC_ENABLE_DMA;
  	atppc_w_ecr(atppc, ecr);
 -	atppc_barrier_w(atppc);
 +	atppc_barrier_w_e(atppc);

  	/* Wait for DMA completion */
  	atppc->sc_inerr = atppc_wait_interrupt(atppc, atppc->sc_inb,
 @@ -1965,7 +2063,7 @@
  	/* Disable DMA */
  	ecr &= ~ATPPC_ENABLE_DMA;
  	atppc_w_ecr(atppc, ecr);
 -	atppc_barrier_w(atppc);
 +	atppc_barrier_w_e(atppc);
  }

  /* Read bytes in ECP mode using PIO transfers */
 @@ -1976,21 +2074,21 @@
  	/* Disable DMA */
  	ecr &= ~ATPPC_ENABLE_DMA;
  	atppc_w_ecr(atppc, ecr);
 -	atppc_barrier_w(atppc);
 -
 +	atppc_barrier_w_e(atppc);
 +	
  	/* Read from FIFO */
  	atppc_r_fifo_multi(atppc, atppc->sc_inbstart, *length);
  }

  /* Handle errors for ECP reads */
  static void
 -atppc_ecp_read_error(struct atppc_softc *atppc, const unsigned int worklen)
 +atppc_ecp_read_error(struct atppc_softc *atppc, unsigned int worklen)
  {
  	unsigned char ecr = atppc_r_ecr(atppc);

  	/* Abort DMA if not finished */
  	if (atppc->sc_dmastat == ATPPC_DMA_STARTED) {
 -		atppc->sc_dma_abort(atppc);
 +		worklen -= atppc->sc_dma_abort(atppc);
  		ATPPC_DPRINTF(("%s: DMA interrupted.\n", __func__));
  	}

 @@ -1998,11 +2096,100 @@
  	if ((ecr & ATPPC_FIFO_EMPTY) && (ecr & ATPPC_FIFO_FULL)) {
  		ATPPC_DPRINTF(("%s: FIFO full+empty bits set.\n", __func__));
  		ATPPC_DPRINTF(("%s: reseting FIFO.\n", __func__));
 -		atppc_w_ecr(atppc, ATPPC_ECR_PS2);
 -		atppc_barrier_w(atppc);
 +		atppc_w_ecr(atppc,
 +			(ATPPC_ECR_PS2|ATPPC_SERVICE_INTR|ATPPC_nFAULT_INTR));
 +		atppc_barrier_w_e(atppc);
 +	}
 +}
 +
 +/* Write the byte currentedly pointed to in the softc's buffer in "STD" mode. */
 +static void
 +atppc_std_write_byte(struct atppc_softc * const atppc, unsigned char ctr)
 +{
 +	/*
 +	 * NOTE: the two microseconds of delays puts a 500KHz upper bound
 +	 * on the transfer speed!  If we had a "DELAY_ONE_HALF_US()" and
 +	 * a "DELAY_ONE_QUARTER_US()", we'd more than increase the maximum
 +	 * rate by more than double (or cut the overhead by at about five
 +	 * eighths).
 +	 */
 +	/* Put data in data register */
 +	atppc_w_dtr(atppc, *(atppc->sc_outbstart));
 +	atppc_barrier_w(atppc);
 +	DELAY(1); /* DELAY_ONE_QUARTER_US()/DELAY(.25) */
 +
 +	/* Pulse strobe to indicate valid data on lines */
 +	ctr |= STROBE;
 +	atppc_w_ctr(atppc, ctr);
 +	atppc_barrier_w(atppc);
 +	DELAY(1); /* DELAY_ONE_HALF_US()/DELAY(.5) */
 +	ctr &= ~STROBE;
 +	atppc_w_ctr(atppc, ctr);
 +	atppc_barrier_w(atppc);
 +
 +	return;
 +}
 +
 +/*
 + * If the peripheral is ready, try to write as many bytes as we can (in
 + * "STD" mode) during one interupt request. NOTE: There is no need to go
 + * between the "top" and "bottom" parts of the driver as long as the buffer
 + * isn't empty.
 + */
 +static int
 +atppc_std_intr_handler(struct atppc_softc * const atppc)
 +{
 +	unsigned char str;
 +	unsigned char ctr;
 +	int any;
 +
 +	/*
 +	 * NOTE: the three microseconds of delays puts a 333KHz upper bound
 +	 * on the transfer speed!  If we had a "DELAY_ONE_HALF_US()" and a
 +	 * "DELAY_ONE_QUARTER_US()", we'd triple the maximum rate (or cut
 +	 * the overhead by two thirds).
 +	 */
 +	ctr = atppc_r_ctr(atppc);
 +	atppc_barrier_r(atppc);
 +
 +	any = 0;
 +	/* try to "chain" interrupts together */
 +	while (atppc->sc_outbstart < (atppc->sc_outb + atppc->sc_outb_nbytes)) {
 +		str = atppc_r_str(atppc);
 +		atppc_barrier_r(atppc);
 +		if ((str & SPP_MASK) != SPP_READY)
 +			break;
 +		atppc_std_write_byte(atppc, ctr);
 +		atppc->sc_outbstart++;
 +		any = 1;
 +		/* Give time for the peripheral to respond */
 +		DELAY(1); /* DELAY_ONE_QUARTER_US()/DELAY(.25) */
  	}
 +	return any;
  }

 +/* Sleep the write while the `real' work occurs */
 +static int
 +atppc_wait_buf_done_or_error(struct atppc_softc * const atppc,
 +			     const caddr_t where)
 +{
 +	int error;
 +
 +	/*
 +	 * NOTE: HP DeskJets can refuse data for greater than 45 seconds
 +	 * (ex. my PhotoSmart P1100 regularly pauses and refuses data for 35
 +	 * to 40 seconds on large jobs, even longer when duplexing).
 +	 */
 +	/* Wait for interrupt for 20 * MAXBUSYWAIT - after all we're sleeping */
 +	error = ltsleep(where, PPBUSPRI | PCATCH, __func__, 20 * MAXBUSYWAIT,
 +		ATPPC_SC_LOCK(atppc));
 +
 +	if (!error && (atppc->sc_irqstat & ATPPC_IRQ_nACK))
 +		atppc->sc_irqstat &= ~ATPPC_IRQ_nACK;
 +
 +	return error;
 +}
 + 
  /*
   * Functions that write bytes to port from buffer: called from atppc_write()
   * function depending on current chipset mode. Returns number of bytes moved.
 @@ -2017,6 +2204,7 @@

  	ctr = atppc_r_ctr(atppc);
  	atppc_barrier_r(atppc);
 +
  	/* Enable interrupts if needed */
  	if (atppc->sc_use & ATPPC_USE_INTR) {
  		if (!(ctr & IRQENABLE)) {
 @@ -2026,34 +2214,43 @@
  		}
  	}

 -	while (atppc->sc_outbstart < (atppc->sc_outb + atppc->sc_outb_nbytes)) {
 -		/* Wait for peripheral to become ready for MAXBUSYWAIT */
 +	if (atppc->sc_use & ATPPC_USE_INTR) {
 +		/* Wait for peripheral to become ready */
  		atppc->sc_outerr = atppc_poll_str(atppc, SPP_READY, SPP_MASK);
  		if (atppc->sc_outerr)
  			return;

 -		/* Put data in data register */
 -		atppc_w_dtr(atppc, *(atppc->sc_outbstart));
 -		atppc_barrier_w(atppc);
 -		DELAY(1);
 -
 -		/* Pulse strobe to indicate valid data on lines */
 -		ctr |= STROBE;
 -		atppc_w_ctr(atppc, ctr);
 -		atppc_barrier_w(atppc);
 -		DELAY(1);
 -		ctr &= ~STROBE;
 -		atppc_w_ctr(atppc, ctr);
 -		atppc_barrier_w(atppc);
 +		/* Start the sequence with the first byte */
 +		atppc_std_write_byte(atppc, ctr);
 +		atppc->sc_outbstart++;

 -		/* Wait for nACK for MAXBUSYWAIT */
 -		timecount = 0;
 -		if (atppc->sc_use & ATPPC_USE_INTR) {
 -			atppc->sc_outerr = atppc_wait_interrupt(atppc,
 -				atppc->sc_outb, ATPPC_IRQ_nACK);
 +#if 0
 +		atppc->sc_outerr = atppc_wait_interrupt(atppc,
 +			atppc->sc_outb, ATPPC_IRQ_nACK);
 +#else
 +		/*
 +		 * Put the writer to sleep until the buffers empty, or an error
 +		 * occurs.
 +		 */
 +		atppc->sc_outerr = atppc_wait_buf_done_or_error(atppc,
 +								atppc->sc_outb);
 +#endif /* #if 0 */
 +		if (atppc->sc_outerr)
 +			return;
 +	} else {
 +		while (atppc->sc_outbstart
 +		      < (atppc->sc_outb + atppc->sc_outb_nbytes)) {
 +			/* Wait for peripheral to become ready */
 +			atppc->sc_outerr = atppc_poll_str(atppc, SPP_READY,
 +							  SPP_MASK);
  			if (atppc->sc_outerr)
  				return;
 -		} else {
 +
 +			atppc_std_write_byte(atppc, ctr);
 +			atppc->sc_outbstart++;
 +
 +			/* Wait for nACK for MAXBUSYWAIT */
 +			timecount = 0;
  			/* Try to catch the pulsed acknowledgement */
  			atppc->sc_outerr = atppc_poll_str(atppc, 0, nACK);
  			if (atppc->sc_outerr)
 @@ -2061,10 +2258,11 @@
  			atppc->sc_outerr = atppc_poll_str(atppc, nACK, nACK);
  			if (atppc->sc_outerr)
  				return;
 +
 +			/* Update buffer position, byte count and counter */
 +			atppc->sc_outbstart++;
  		}

 -		/* Update buffer position, byte count and counter */
 -		atppc->sc_outbstart++;
  	}
  }

 @@ -2259,7 +2463,7 @@
  		timecount = 0;
  		if (atppc->sc_use & ATPPC_USE_INTR) {
  			ecr = atppc_r_ecr(atppc);
 -			atppc_barrier_w(atppc);
 +			atppc_barrier_w_e(atppc);

  			/* Wait for interrupt */
  			for (;;) {
 @@ -2277,7 +2481,7 @@
  				/* Enable service interrupt */
  				ecr &= ~ATPPC_SERVICE_INTR;
  				atppc_w_ecr(atppc, ecr);
 -				atppc_barrier_w(atppc);
 +				atppc_barrier_w_e(atppc);

  				atppc->sc_outerr = atppc_wait_interrupt(atppc,
  					atppc->sc_outb, ATPPC_IRQ_FIFO);
 @@ -2294,7 +2498,7 @@
  				timecount++) {

  				ecr = atppc_r_ecr(atppc);
 -				atppc_barrier_r(atppc);
 +				atppc_barrier_r_e(atppc);
  				if (ecr & ATPPC_FIFO_EMPTY) {
  					if (ecr & ATPPC_FIFO_FULL) {
  						atppc->sc_outerr = EIO;
 @@ -2322,13 +2526,13 @@

  static void
  atppc_fifo_write_error(struct atppc_softc * const atppc,
 -	const unsigned int worklen)
 +	unsigned int worklen)
  {
  	unsigned char ecr = atppc_r_ecr(atppc);

  	/* Abort DMA if not finished */
  	if (atppc->sc_dmastat == ATPPC_DMA_STARTED) {
 -		atppc->sc_dma_abort(atppc);
 +		worklen - atppc->sc_dma_abort(atppc);
  		ATPPC_DPRINTF(("%s: DMA interrupted.\n", __func__));
  	}

 @@ -2350,15 +2554,16 @@

  		/* Determine how many bytes remain in FIFO */
  		for (i = 0; i < atppc->sc_fifo; i++) {
 -			atppc_w_fifo(atppc, (unsigned char)i);
  			ecr = atppc_r_ecr(atppc);
 -			atppc_barrier_r(atppc);
 +			atppc_barrier_r_e(atppc);
  			if (ecr & ATPPC_FIFO_FULL)
  				break;
 +			atppc_w_fifo(atppc, (unsigned char)i);
  		}
 -		bytes_left = (atppc->sc_fifo) - (i + 1);
 -		ATPPC_DPRINTF(("%s: %d bytes left in FIFO.\n", 	__func__,
 -			bytes_left));
 +		bytes_left = atppc->sc_fifo - i + atppc->sc_xboa;
 +		ATPPC_DPRINTF(("%s: %d bytes left in FIFO%s.\n", __func__,
 +			bytes_left - atppc->sc_xboa,
 +			atppc->sc_xboa ? " 1 byte in hreg" : ""));

  		/* Update counter */
  		atppc->sc_outbstart += (worklen - bytes_left);
 @@ -2368,8 +2573,10 @@
  	}

  	ATPPC_DPRINTF(("%s: reseting FIFO.\n", __func__));
 -	atppc_w_ecr(atppc, ATPPC_ECR_PS2);
 -	atppc_barrier_w(atppc);
 +	atppc_w_ecr(atppc, ATPPC_ECR_STD|ATPPC_SERVICE_INTR|ATPPC_nFAULT_INTR);
 +	atppc_barrier_w_e(atppc);
 +	(void) atppc_r_ecr(atppc); /* Needed for some PCI attachments */
 +	atppc_barrier_r_e(atppc);
  }

  /*
 @@ -2400,23 +2607,22 @@
  	return error;
  }

 -/* Wait for interrupt for MAXBUSYWAIT: returns 0 if acknowledge received. */
 +/* Wait for interrupt for 6*MAXBUSYWAIT: returns 0 if acknowledge received. */
  static int
  atppc_wait_interrupt(struct atppc_softc * const atppc, const caddr_t where,
  	const u_int8_t irqstat)
  {
 -	int error = EIO;
 +	int error;

  	atppc->sc_irqstat &= ~irqstat;

 -	/* Wait for interrupt for MAXBUSYWAIT */
 -	error = ltsleep(where, PPBUSPRI | PCATCH, __func__, MAXBUSYWAIT,
 +	/* Wait for interrupt for 6 * MAXBUSYWAIT -- after all we're sleeping */
 +	/* NOTE: HP DeskJets can refuse data for greater than 15 seconds */
 +	error = ltsleep(where, PPBUSPRI | PCATCH, __func__, 6 * MAXBUSYWAIT,
  		ATPPC_SC_LOCK(atppc));

 -	if (!(error) && (atppc->sc_irqstat & irqstat)) {
 +	if (!error && (atppc->sc_irqstat & irqstat))
  		atppc->sc_irqstat &= ~irqstat;
 -		error = 0;
 -	}

  	return error;

 ********************************************************************************
 --------------------------------------------------------------------------------
 ------ ** Second case * Just kern/26358 changes ** -----------------------------
 --------------------------------------------------------------------------------
 ********************************************************************************
 Index: atppc.c
 ===================================================================
 RCS file: /cvsroot/src/sys/dev/ic/atppc.c,v
 retrieving revision 1.16
 diff -u -r1.16 atppc.c
 --- atppc.c	21 Apr 2004 17:38:48 -0000	1.16
 +++ atppc.c	31 Jul 2004 10:13:23 -0000
 @@ -141,6 +141,7 @@
  static int atppc_wait_interrupt(struct atppc_softc * const, const caddr_t,
  	const u_int8_t);

 +static int atppc_std_intr_handler(struct atppc_softc * const);

  /*
   * Generic attach and detach functions for atppc device. If sc_dev_ok in soft
 @@ -629,15 +704,26 @@
  	/* Record registers' status */
  	atppc->sc_str_intr = atppc_r_str(atppc);
  	atppc->sc_ctr_intr = atppc_r_ctr(atppc);
 -	atppc->sc_ecr_intr = atppc_r_ecr(atppc);
  	atppc_barrier_r(atppc);
 +	if (atppc->sc_has & ATPPC_HAS_ECP) {
 +		atppc->sc_ecr_intr = atppc_r_ecr(atppc);
 +		atppc_barrier_r(atppc);
 +	}

  	/* Determine cause of interrupt and wake up top half */
  	switch (atppc->sc_mode) {
  	case ATPPC_MODE_STD:
  		/* nAck pulsed for 5 usec, too fast to check reliably, assume */
  		atppc->sc_irqstat = ATPPC_IRQ_nACK;
 -		if (atppc->sc_outb)
 +		if (atppc->sc_use & ATPPC_USE_INTR) {
 +			claim = atppc_std_intr_handler(atppc);
 +			if (atppc->sc_outbstart
 +			   >= (atppc->sc_outb + atppc->sc_outb_nbytes))
 +				wake_up = WRITER;
 +			else
 +				wake_up = NONE;
 +		}
 +		else if (atppc->sc_outb)
  			wake_up = WRITER;
  		else
  			claim = 0;
 @@ -710,7 +796,7 @@
  		panic("%s: chipset is in invalid mode.", dev->dv_xname);
  	}

 -	if (claim) {
 +	if (claim)
  		switch (wake_up) {
  		case NONE:
  			break;
 @@ -722,9 +808,12 @@
  		case WRITER:
  			wakeup(atppc->sc_outb);
  			break;
 -		}
 -	}

 +		default:
 +			panic("%s: this code should not be reached.\n",
 +				__func__);
 +			break;
 +		}
  	ATPPC_UNLOCK(atppc);

  	/* Call all of the installed handlers */
 @@ -1998,11 +2096,100 @@
  	if ((ecr & ATPPC_FIFO_EMPTY) && (ecr & ATPPC_FIFO_FULL)) {
  		ATPPC_DPRINTF(("%s: FIFO full+empty bits set.\n", __func__));
  		ATPPC_DPRINTF(("%s: reseting FIFO.\n", __func__));
 -		atppc_w_ecr(atppc, ATPPC_ECR_PS2);
 -		atppc_barrier_w(atppc);
 +		atppc_w_ecr(atppc,
 +			(ATPPC_ECR_PS2|ATPPC_SERVICE_INTR|ATPPC_nFAULT_INTR));
 +		atppc_barrier_w(atppc);
 +	}
 +}
 +
 +/* Write the byte currentedly pointed to in the softc's buffer in "STD" mode. */
 +static void
 +atppc_std_write_byte(struct atppc_softc * const atppc, unsigned char ctr)
 +{
 +	/*
 +	 * NOTE: the two microseconds of delays puts a 500KHz upper bound
 +	 * on the transfer speed!  If we had a "DELAY_ONE_HALF_US()" and
 +	 * a "DELAY_ONE_QUARTER_US()", we'd more than increase the maximum
 +	 * rate by more than double (or cut the overhead by at about five
 +	 * eighths).
 +	 */
 +	/* Put data in data register */
 +	atppc_w_dtr(atppc, *(atppc->sc_outbstart));
 +	atppc_barrier_w(atppc);
 +	DELAY(1); /* DELAY_ONE_QUARTER_US()/DELAY(.25) */
 +
 +	/* Pulse strobe to indicate valid data on lines */
 +	ctr |= STROBE;
 +	atppc_w_ctr(atppc, ctr);
 +	atppc_barrier_w(atppc);
 +	DELAY(1); /* DELAY_ONE_HALF_US()/DELAY(.5) */
 +	ctr &= ~STROBE;
 +	atppc_w_ctr(atppc, ctr);
 +	atppc_barrier_w(atppc);
 +
 +	return;
 +}
 +
 +/*
 + * If the peripheral is ready, try to write as many bytes as we can (in
 + * "STD" mode) during one interupt request. NOTE: There is no need to go
 + * between the "top" and "bottom" parts of the driver as long as the buffer
 + * isn't empty.
 + */
 +static int
 +atppc_std_intr_handler(struct atppc_softc * const atppc)
 +{
 +	unsigned char str;
 +	unsigned char ctr;
 +	int any;
 +
 +	/*
 +	 * NOTE: the three microseconds of delays puts a 333KHz upper bound
 +	 * on the transfer speed!  If we had a "DELAY_ONE_HALF_US()" and a
 +	 * "DELAY_ONE_QUARTER_US()", we'd triple the maximum rate (or cut
 +	 * the overhead by two thirds).
 +	 */
 +	ctr = atppc_r_ctr(atppc);
 +	atppc_barrier_r(atppc);
 +
 +	any = 0;
 +	/* try to "chain" interrupts together */
 +	while (atppc->sc_outbstart < (atppc->sc_outb + atppc->sc_outb_nbytes)) {
 +		str = atppc_r_str(atppc);
 +		atppc_barrier_r(atppc);
 +		if ((str & SPP_MASK) != SPP_READY)
 +			break;
 +		atppc_std_write_byte(atppc, ctr);
 +		atppc->sc_outbstart++;
 +		any = 1;
 +		/* Give time for the peripheral to respond */
 +		DELAY(1); /* DELAY_ONE_QUARTER_US()/DELAY(.25) */
  	}
 +	return any;
  }

 +/* Sleep the write while the `real' work occurs */
 +static int
 +atppc_wait_buf_done_or_error(struct atppc_softc * const atppc,
 +			     const caddr_t where)
 +{
 +	int error;
 +
 +	/*
 +	 * NOTE: HP DeskJets can refuse data for greater than 45 seconds
 +	 * (ex. my PhotoSmart P1100 regularly pauses and refuses data for 35
 +	 * to 40 seconds on large jobs, even longer when duplexing).
 +	 */
 +	/* Wait for interrupt for 20 * MAXBUSYWAIT - after all we're sleeping */
 +	error = ltsleep(where, PPBUSPRI | PCATCH, __func__, 20 * MAXBUSYWAIT,
 +		ATPPC_SC_LOCK(atppc));
 +
 +	if (!error && (atppc->sc_irqstat & ATPPC_IRQ_nACK))
 +		atppc->sc_irqstat &= ~ATPPC_IRQ_nACK;
 +
 +	return error;
 +}
 + 
  /*
   * Functions that write bytes to port from buffer: called from atppc_write()
   * function depending on current chipset mode. Returns number of bytes moved.
 @@ -2017,6 +2204,7 @@

  	ctr = atppc_r_ctr(atppc);
  	atppc_barrier_r(atppc);
 +
  	/* Enable interrupts if needed */
  	if (atppc->sc_use & ATPPC_USE_INTR) {
  		if (!(ctr & IRQENABLE)) {
 @@ -2026,34 +2214,43 @@
  		}
  	}

 -	while (atppc->sc_outbstart < (atppc->sc_outb + atppc->sc_outb_nbytes)) {
 -		/* Wait for peripheral to become ready for MAXBUSYWAIT */
 +	if (atppc->sc_use & ATPPC_USE_INTR) {
 +		/* Wait for peripheral to become ready */
  		atppc->sc_outerr = atppc_poll_str(atppc, SPP_READY, SPP_MASK);
  		if (atppc->sc_outerr)
  			return;

 -		/* Put data in data register */
 -		atppc_w_dtr(atppc, *(atppc->sc_outbstart));
 -		atppc_barrier_w(atppc);
 -		DELAY(1);
 -
 -		/* Pulse strobe to indicate valid data on lines */
 -		ctr |= STROBE;
 -		atppc_w_ctr(atppc, ctr);
 -		atppc_barrier_w(atppc);
 -		DELAY(1);
 -		ctr &= ~STROBE;
 -		atppc_w_ctr(atppc, ctr);
 -		atppc_barrier_w(atppc);
 +		/* Start the sequence with the first byte */
 +		atppc_std_write_byte(atppc, ctr);
 +		atppc->sc_outbstart++;

 -		/* Wait for nACK for MAXBUSYWAIT */
 -		timecount = 0;
 -		if (atppc->sc_use & ATPPC_USE_INTR) {
 -			atppc->sc_outerr = atppc_wait_interrupt(atppc,
 -				atppc->sc_outb, ATPPC_IRQ_nACK);
 +#if 0
 +		atppc->sc_outerr = atppc_wait_interrupt(atppc,
 +			atppc->sc_outb, ATPPC_IRQ_nACK);
 +#else
 +		/*
 +		 * Put the writer to sleep until the buffers empty, or an error
 +		 * occurs.
 +		 */
 +		atppc->sc_outerr = atppc_wait_buf_done_or_error(atppc,
 +								atppc->sc_outb);
 +#endif /* #if 0 */
 +		if (atppc->sc_outerr)
 +			return;
 +	} else {
 +		while (atppc->sc_outbstart
 +		      < (atppc->sc_outb + atppc->sc_outb_nbytes)) {
 +			/* Wait for peripheral to become ready */
 +			atppc->sc_outerr = atppc_poll_str(atppc, SPP_READY,
 +							  SPP_MASK);
  			if (atppc->sc_outerr)
  				return;
 -		} else {
 +
 +			atppc_std_write_byte(atppc, ctr);
 +			atppc->sc_outbstart++;
 +
 +			/* Wait for nACK for MAXBUSYWAIT */
 +			timecount = 0;
  			/* Try to catch the pulsed acknowledgement */
  			atppc->sc_outerr = atppc_poll_str(atppc, 0, nACK);
  			if (atppc->sc_outerr)
 @@ -2061,10 +2258,11 @@
  			atppc->sc_outerr = atppc_poll_str(atppc, nACK, nACK);
  			if (atppc->sc_outerr)
  				return;
 +
 +			/* Update buffer position, byte count and counter */
 +			atppc->sc_outbstart++;
  		}

 -		/* Update buffer position, byte count and counter */
 -		atppc->sc_outbstart++;
  	}
  }

 @@ -2400,23 +2607,22 @@
  	return error;
  }

 -/* Wait for interrupt for MAXBUSYWAIT: returns 0 if acknowledge received. */
 +/* Wait for interrupt for 6*MAXBUSYWAIT: returns 0 if acknowledge received. */
  static int
  atppc_wait_interrupt(struct atppc_softc * const atppc, const caddr_t where,
  	const u_int8_t irqstat)
  {
 -	int error = EIO;
 +	int error;

  	atppc->sc_irqstat &= ~irqstat;

 -	/* Wait for interrupt for MAXBUSYWAIT */
 -	error = ltsleep(where, PPBUSPRI | PCATCH, __func__, MAXBUSYWAIT,
 +	/* Wait for interrupt for 6 * MAXBUSYWAIT -- after all we're sleeping */
 +	/* NOTE: HP DeskJets can refuse data for greater than 15 seconds */
 +	error = ltsleep(where, PPBUSPRI | PCATCH, __func__, 6 * MAXBUSYWAIT,
  		ATPPC_SC_LOCK(atppc));

 -	if (!(error) && (atppc->sc_irqstat & irqstat)) {
 +	if (!error && (atppc->sc_irqstat & irqstat))
  		atppc->sc_irqstat &= ~irqstat;
 -		error = 0;
 -	}

  	return error;
  }
>Unformatted:

NetBSD Home
NetBSD PR Database Search

(Contact us) $NetBSD: query-full-pr,v 1.39 2013/11/01 18:47:49 spz Exp $
$NetBSD: gnats_config.sh,v 1.8 2006/05/07 09:23:38 tsutsui Exp $
Copyright © 1994-2007 The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.