/*++

Copyright (C) Microsoft Corporation, 1993 - 1999

Module Name:

    hwecp.c

Abstract:

    This module contains code for the host to utilize HardwareECP if it has been
    detected and successfully enabled.

Author:

    Robbie Harris (Hewlett-Packard) 21-May-1998

Environment:

    Kernel mode

Revision History :

--*/

#include "pch.h"
#include "hwecp.h"

VOID ParCleanupHwEcpPort(IN  PDEVICE_EXTENSION   Extension)
/*++

Routine Description:

   Cleans up prior to a normal termination from ECP mode.  Puts the
   port HW back into Compatibility mode.

Arguments:

    Controller  - Supplies the parallel port's controller address.

Return Value:

    None.

--*/
{
    PUCHAR      Controller;
    NTSTATUS    nError = STATUS_SUCCESS;
    // UCHAR       bDCR;           // Contents of DCR

    Controller = Extension->Controller;

    //----------------------------------------------------------------------
    // Set the ECR to mode 001 (PS2 Mode).
    //----------------------------------------------------------------------
    #if (1 == PARCHIP_ECR_ARBITRATOR)
        Extension->ClearChipMode( Extension->PortContext, ECR_ECP_PIO_MODE );
    #else
        #if (0 == DVRH_USE_PARPORT_ECP_ADDR)
            WRITE_PORT_UCHAR(Controller + ECR_OFFSET, DEFAULT_ECR_PS2);
        #else
            WRITE_PORT_UCHAR(Extension->EcrController + ECR_OFFSET, DEFAULT_ECR_PS2);
        #endif
    #endif
    Extension->PortHWMode = HW_MODE_PS2;

    ParCleanupSwEcpPort(Extension);

    //----------------------------------------------------------------------
    // Set the ECR to mode 000 (Compatibility Mode).
    //----------------------------------------------------------------------
    #if (1 == PARCHIP_ECR_ARBITRATOR)
        // Nothing to do!
    #else
        #if (0 == DVRH_USE_PARPORT_ECP_ADDR)
            WRITE_PORT_UCHAR(Controller + ECR_OFFSET, DEFAULT_ECR_COMPATIBILITY);
        #else
            WRITE_PORT_UCHAR(Extension->EcrController + ECR_OFFSET, DEFAULT_ECR_COMPATIBILITY);
        #endif
    #endif
    Extension->PortHWMode = HW_MODE_COMPATIBILITY;
}

VOID ParEcpHwDrainShadowBuffer(
    IN  Queue  *pShadowBuffer,
    IN  PUCHAR  lpsBufPtr,
    IN  ULONG   dCount,
    OUT ULONG  *fifoCount)
{
    *fifoCount = 0;
    
    if (Queue_IsEmpty(pShadowBuffer)) {
        ParDump2(PARINFO,("ParEcpHwDrainShadowBuffer: No data in Shadow\r\n"));
        return;
    }

    while ( dCount > 0 ) {
        // LAC FRAME  13Jan98
        // Break out the Queue_Dequeue from the pointer increment so we can
        // observe the data if needed.
        if (FALSE == Queue_Dequeue(pShadowBuffer, lpsBufPtr)) {  // Get byte from queue.
            ParDump2(PARERRORS,("ParEcpHwDrainShadowBuffer: ShadowBuffer Bad\r\n"));
            return;
        }
        ParDump2(PARINFO,("ParEcpHwDrainShadowBuffer: read data byte %02x\r\n",(int)*lpsBufPtr));
        lpsBufPtr++;
        dCount--;                       // Decrement count.
        (*fifoCount)++;
    }

#if DBG
    if (*fifoCount) {
        ParTimerCheck(("ParEcpHwDrainShadowBuffer:  read %d bytes from shadow\r\n", *fifoCount ));
    }
#endif
    ParDump2( PARINFO, ("ParEcpHwDrainShadowBuffer:  read %d bytes from shadow\r\n", *fifoCount ));
}

//============================================================================
// NAME:    HardwareECP::EmptyFIFO()
//  
//      Empties HW FIFO into a shadow buffer.  This must be done before
//      turning the direction from reverse to forward, if the printer has
//      stuffed data in that no one has read yet.
//
// PARAMETERS: 
//      Controller      - Supplies the base address of the parallel port.
//
// RETURNS: STATUS_SUCCESS or ....
//
// NOTES:
//      Called ZIP_EmptyFIFO in the original 16 bit code.
//
//============================================================================
NTSTATUS ParEcpHwEmptyFIFO(IN  PDEVICE_EXTENSION   Extension)
{
    NTSTATUS   nError = STATUS_SUCCESS;
    Queue      *pShadowBuffer;
	UCHAR      bData;
    #if (0 == DVRH_USE_PARPORT_ECP_ADDR)
        PUCHAR     wPortDFIFO = Extension->Controller + ECP_DFIFO_OFFSET;  // IO address of ECP Data FIFO
        PUCHAR     wPortECR = Extension->Controller + ECR_OFFSET;    // IO address of Extended Control Register (ECR)
    #else
        PUCHAR     wPortDFIFO = Extension->EcrController;  // IO address of ECP Data FIFO
        PUCHAR     wPortECR = Extension->EcrController + ECR_OFFSET;    // IO address of Extended Control Register (ECR)
    #endif
    
    // While data exists in the FIFO, read it and put it into shadow buffer.
    // If the shadow buffer fills up before the FIFO is exhausted, an
    // error condition exists.

    pShadowBuffer = &(Extension->ShadowBuffer);
    while ((READ_PORT_UCHAR(wPortECR) & ECR_FIFO_EMPTY) == 0 )
    {
		// LAC FRAME  13Jan98
		// Break out the Port Read so we can observe the data if needed
		bData = READ_PORT_UCHAR(wPortDFIFO);
        if (FALSE == Queue_Enqueue(pShadowBuffer, bData))    // Put byte in queue.
        {
            ParDump2(PARERRORS, ( "ParEcpHwEmptyFIFO:  Shadow buffer full, FIFO not empty\r\n" ));
            nError = STATUS_BUFFER_OVERFLOW;
            goto ParEcpHwEmptyFIFO_ExitLabel;
        }
        ParDump2(PARINFO,("ParEcpHwEmptyFIFO: Enqueue data %02x\r\n",(int)bData));
    }

    if( ( !Queue_IsEmpty(pShadowBuffer) && (Extension->P12843DL.bEventActive) )) {
        KeSetEvent(Extension->P12843DL.Event, 0, FALSE);
    }

ParEcpHwEmptyFIFO_ExitLabel:
    return nError;
}   // ParEcpHwEmptyFIFO

// LAC ENTEREXIT  5Dec97
//=========================================================
// HardwareECP::ExitForwardPhase
//
// Description : Exit from HWECP Forward Phase to the common phase
//               (FWD IDLE, PS/2)
//
// Input Parameters : Controller, pPortInfoStruct
//
// Modifies :
//
// Pre-conditions :
//
// Post-conditions :
//
// Returns :
//
//=========================================================
NTSTATUS ParEcpHwExitForwardPhase( IN  PDEVICE_EXTENSION   Extension )
{
    NTSTATUS status;
    PUCHAR wPortECR;       // I/O address of ECR

	ParDump2( PARENTRY, ("ParEcpHwExitForwardPhase: Entry\r\n") );
    #if (0 == DVRH_USE_PARPORT_ECP_ADDR)
        wPortECR = Extension->Controller + ECR_OFFSET;
    #else
        wPortECR = Extension->EcrController + ECR_OFFSET;
    #endif

	// First, there could be data in the FIFO.  Wait for it to empty
	// and then put the bus in the common state (PHASE_FORWARD_IDLE with
	// ECRMode set to PS/2
	status = ParEcpHwWaitForEmptyFIFO( Extension );

    Extension->CurrentPhase = PHASE_FORWARD_IDLE;
    ParDumpReg(PAREXIT, ("ParEcpHwExitForwardPhase: Exit[%d]", NT_SUCCESS(status)),
                wPortECR,
                Extension->Controller + OFFSET_DCR,
                Extension->Controller + OFFSET_DSR);
	
	return( status );
}	

// LAC ENTEREXIT  5Dec97
//=========================================================
// HardwareECP::EnterReversePhase
//
// Description : Go from the common phase to HWECP Reverse Phase
//
// Input Parameters : Controller, pPortInfoStruct
//
// Modifies :
//
// Pre-conditions :
//
// Post-conditions :
//
// Returns :
//
//=========================================================
NTSTATUS ParEcpHwEnterReversePhase( IN  PDEVICE_EXTENSION   Extension )
{
    NTSTATUS status;
    PUCHAR Controller;
    PUCHAR wPortECR;       // I/O address of Extended Control Register
    PUCHAR wPortDCR;       // I/O address of Device Control Register
    UCHAR  dcr;
    
//    ParTimerCheck(("ParEcpHwEnterReversePhase: Start\r\n"));
    Controller = Extension->Controller;
#if (0 == PARCHIP_ECR_ARBITRATOR)
    wPortECR = Controller + ECR_OFFSET;
#else
    wPortECR = Extension->EcrController + ECR_OFFSET;
#endif
    wPortDCR = Controller + OFFSET_DCR;


    ParDumpReg(PARENTRY, ("ParEcpHwEnterReversePhase: Enter"),
                wPortECR,
                wPortDCR,
                Controller + OFFSET_DSR);
	
	// EnterReversePhase assumes that we are in PHASE_FORWARD_IDLE,
	// and that the ECPMode is set to PS/2 mode at entry.
	//volatile UCHAR ecr = READ_PORT_UCHAR( Controller + ECR_OFFSET ) & 0xe0;
	//HPKAssert( ((PHASE_FORWARD_IDLE == pPortInfoStruct->CurrentPhase) && (0x20 == ecr)), 
	//          ("HardwareECP::EnterReversePhase: Bad initial state (%d/%x)\r\n",(int)pPortInfoStruct->CurrentPhase,(int)ecr) );

    //----------------------------------------------------------------------
    // Set the ECR to mode 001 (PS2 Mode).
    //----------------------------------------------------------------------
    #if (1 == PARCHIP_ECR_ARBITRATOR)
        Extension->ClearChipMode( Extension->PortContext, ECR_ECP_PIO_MODE );
    #else
        WRITE_PORT_UCHAR(wPortECR, DEFAULT_ECR_PS2);
    #endif
    Extension->PortHWMode = HW_MODE_PS2;

    if ( Extension->ModeSafety == SAFE_MODE ) {

    	// Reverse the bus first (using ECP::EnterReversePhase)
	    status = ParEcpEnterReversePhase(Extension);
    	if ( NT_SUCCESS(status) )
	    {
    		//----------------------------------------------------------------------
	    	// Wait for nAckReverse low (ECP State 40)
		    //----------------------------------------------------------------------
    		if ( !CHECK_DSR(Controller, DONT_CARE, DONT_CARE, INACTIVE, ACTIVE, DONT_CARE,
	    	                IEEE_MAXTIME_TL) )
		    {
    		    ParDump2(PARERRORS,("ParEcpHwEnterReversePhase: State 40 failed\r\n"));
                status = ParEcpHwRecoverPort( Extension, RECOVER_28 );
                if ( NT_SUCCESS(status))
    	    		status = STATUS_LINK_FAILED;
                goto ParEcpHwEnterReversePhase_ExitLabel;
    		}
    		else
	    	{
		    	ParDump2(PARECPTRACE, ("ParEcpHwEnterReversePhase: Phase_RevIdle. Setup HW ECR\r\n"));
			    Extension->CurrentPhase = PHASE_REVERSE_IDLE;
    		}
        }
	} else {
        //----------------------------------------------------------------------
        // Set Dir=1 in DCR for reading.
        //----------------------------------------------------------------------
        dcr = READ_PORT_UCHAR( wPortDCR );     // Get content of DCR.
        dcr = UPDATE_DCR( dcr, DIR_READ, DONT_CARE, DONT_CARE, DONT_CARE, DONT_CARE, DONT_CARE );
        WRITE_PORT_UCHAR(wPortDCR, dcr);
	}

    //----------------------------------------------------------------------
    // Set the ECR to mode 011 (ECP Mode).  DmaEnable=0.
    //----------------------------------------------------------------------
    ParDump2(PARECPTRACE,("ParEcpHwEnterReversePhase: Before Setting Harware DCR - %x.\n", READ_PORT_UCHAR(wPortDCR) ));
    #if (1 == PARCHIP_ECR_ARBITRATOR)
        status = Extension->TrySetChipMode ( Extension->PortContext, ECR_ECP_PIO_MODE );
        if ( !NT_SUCCESS(status) )
        {
            ParDump2(PARERRORS,("ParEcpHwEnterReversePhase: TrySetChipMode failed\r\n"));
        }
    #else
        WRITE_PORT_UCHAR( wPortECR, DEFAULT_ECR_ECP );
    #endif
    Extension->PortHWMode = HW_MODE_ECP;

    ParDump2(PARECPTRACE,("ParEcpHwEnterReversePhase: After Setting Harware Before nStrobe and nAutoFd DCR - %x.\n", READ_PORT_UCHAR(wPortDCR) ));

    //----------------------------------------------------------------------
    // Set nStrobe=0 and nAutoFd=0 in DCR, so that ECP HW can control.
    //----------------------------------------------------------------------
    dcr = READ_PORT_UCHAR( wPortDCR );               // Get content of DCR.
    dcr = UPDATE_DCR( dcr, DIR_READ, DONT_CARE, DONT_CARE, DONT_CARE, ACTIVE, ACTIVE);
    WRITE_PORT_UCHAR( wPortDCR, dcr );

    ParDump2(PARECPTRACE,("ParEcpHwEnterReversePhase: After nStrobe and nAutoFd DCR - %x.\n", READ_PORT_UCHAR(wPortDCR) ));

    // Set the phase variable to ReverseIdle
    Extension->CurrentPhase = PHASE_REVERSE_IDLE;

ParEcpHwEnterReversePhase_ExitLabel:
//    ParTimerCheck(("ParEcpHwEnterReversePhase: End\r\n"));
    ParDumpReg(PAREXIT, ("ParEcpHwEnterReversePhase: Exit[%d]", NT_SUCCESS(status)),
                wPortECR,
                wPortDCR,
                Controller + OFFSET_DSR);

	return( status );
}

//=========================================================
// HardwareECP::ExitReversePhase
//
// Description : Get out of HWECP Reverse Phase to the common state
//
// Input Parameters : Controller, pPortInfoStruct
//
// Modifies :
//
// Pre-conditions :
//
// Post-conditions :
//
// Returns :
//
//=========================================================
NTSTATUS ParEcpHwExitReversePhase( IN  PDEVICE_EXTENSION   Extension )
{
    NTSTATUS nError = STATUS_SUCCESS;
    UCHAR   bDCR;
    UCHAR   bECR;
    PUCHAR wPortECR;
    PUCHAR wPortDCR;
    PUCHAR Controller;

	ParDump2( PARENTRY, ("ParEcpHwExitReversePhase: Entry\r\n") );
//    ParTimerCheck(("ParEcpHwExitReversePhase: Start\r\n"));
    Controller = Extension->Controller;
    #if (0 == DVRH_USE_PARPORT_ECP_ADDR)
        wPortECR = Controller + ECR_OFFSET;
    #else
        wPortECR = Extension->EcrController + ECR_OFFSET;
    #endif
    wPortDCR = Controller + OFFSET_DCR;

    //----------------------------------------------------------------------
    // Set status byte to indicate Reverse To Forward Mode.
    //----------------------------------------------------------------------
    Extension->CurrentPhase = PHASE_REV_TO_FWD;
	
    if ( Extension->ModeSafety == SAFE_MODE ) {

        //----------------------------------------------------------------------
        // Assert nReverseRequest high.  This should stop further data transfer
        // into the FIFO.  [[REVISIT:  does the chip handle this correctly
        // if it occurs in the middle of a byte transfer (states 43-46)??
        // Answer (10/9/95) no, it doesn't!!]]
        //----------------------------------------------------------------------
        bDCR = READ_PORT_UCHAR(wPortDCR);               // Get content of DCR.
        bDCR = UPDATE_DCR( bDCR, DONT_CARE, DONT_CARE, DONT_CARE, ACTIVE, DONT_CARE, DONT_CARE );
        WRITE_PORT_UCHAR(wPortDCR, bDCR );

        //----------------------------------------------------------------------
        // Wait for PeriphAck low and PeriphClk high (ECP state 48) together
        // with nAckReverse high (ECP state 49).
        //----------------------------------------------------------------------
        if ( ! CHECK_DSR(Controller,
                        INACTIVE, ACTIVE, ACTIVE, ACTIVE, DONT_CARE,
                        DEFAULT_RECEIVE_TIMEOUT ) )
        {
            ParDump2( PARERRORS, ("ParEcpHwExitReversePhase: Periph failed state 48/49.\r\n"));
            nError = ParEcpHwRecoverPort( Extension, RECOVER_37 );   // Reset port.
            if (NT_SUCCESS(nError))
            {
                ParDump2( PARERRORS, ("ParEcpHwExitReversePhase: State 48/49 Failure. RecoverPort Invoked.\r\n"));
                return STATUS_LINK_FAILED;
            }
            return nError;
        }

        //-----------------------------------------------------------------------
        // Empty the HW FIFO of any bytes that may have already come in.
        // This must be done before changing ECR modes because the FIFO is reset
        // when that occurs.
        //-----------------------------------------------------------------------
        bECR = READ_PORT_UCHAR(wPortECR);               // Get content of ECR.
        if ((bECR & ECR_FIFO_EMPTY) == 0)       // Check if FIFO is not empty.
        {
            if ((nError = ParEcpHwEmptyFIFO(Extension)) != STATUS_SUCCESS)
            {
                ParDump2( PARERRORS, ("ParEcpHwExitReversePhase: Attempt to empty ECP chip failed.\r\n"));
                return nError;
            }
        }

        //----------------------------------------------------------------------
        // Assert HostAck and HostClk high.  [[REVISIT:  is this necessary? 
        //    should already be high...]]
        //----------------------------------------------------------------------
        bDCR = UPDATE_DCR( bDCR, DONT_CARE, DONT_CARE, DONT_CARE, DONT_CARE, ACTIVE, ACTIVE );
        WRITE_PORT_UCHAR(wPortDCR, bDCR );

    } // SAFE_MODE

    //----------------------------------------------------------------------
    // Set the ECR to PS2 Mode so we can change bus direction.
    //----------------------------------------------------------------------
    #if (1 == PARCHIP_ECR_ARBITRATOR)
        Extension->ClearChipMode( Extension->PortContext, ECR_ECP_PIO_MODE );
    #else
        WRITE_PORT_UCHAR(wPortECR, DEFAULT_ECR_PS2);
    #endif
    Extension->PortHWMode = HW_MODE_PS2;


    //----------------------------------------------------------------------
    // Set Dir=0 (Write) in DCR.
    //----------------------------------------------------------------------
    bDCR = READ_PORT_UCHAR(wPortDCR);
    bDCR = UPDATE_DCR( bDCR, DIR_WRITE, DONT_CARE, DONT_CARE, DONT_CARE, DONT_CARE, DONT_CARE );
    WRITE_PORT_UCHAR(wPortDCR, bDCR );


    //----------------------------------------------------------------------
    // Set the ECR back to ECP Mode.  DmaEnable=0.
    //----------------------------------------------------------------------
    #if (1 == PARCHIP_ECR_ARBITRATOR)
        nError = Extension->TrySetChipMode ( Extension->PortContext, ECR_ECP_PIO_MODE );
    #else
        WRITE_PORT_UCHAR(wPortECR, DEFAULT_ECR_ECP);
    #endif
    Extension->PortHWMode = HW_MODE_ECP;


    Extension->CurrentPhase = PHASE_FORWARD_IDLE;

//    ParTimerCheck(("ParEcpHwExitReversePhase: End\r\n"));
    ParDumpReg(PAREXIT, ("ParEcpHwExitReversePhase: Exit[%d]", NT_SUCCESS(nError)),
                wPortECR,
                wPortDCR,
                Extension->Controller + OFFSET_DSR);

    return(nError);
}

BOOLEAN
ParEcpHwHaveReadData (
    IN  PDEVICE_EXTENSION   Extension
    )
{
    Queue     *pQueue;

    // check shadow buffer
    pQueue = &(Extension->ShadowBuffer);
    if (!Queue_IsEmpty(pQueue)) {
        return TRUE;
    }

    // check periph
    if (ParEcpHaveReadData(Extension))
        return TRUE;

    // Check if FIFO is not empty.
    #if (0 == DVRH_USE_PARPORT_ECP_ADDR)
        return (BOOLEAN)( (UCHAR)0 == (READ_PORT_UCHAR(Extension->Controller + ECR_OFFSET) & ECR_FIFO_EMPTY) );
    #else
        return (BOOLEAN)( (UCHAR)0 == (READ_PORT_UCHAR(Extension->EcrController + ECR_OFFSET) & ECR_FIFO_EMPTY) );
    #endif
}

NTSTATUS
ParEcpHwHostRecoveryPhase(
    IN  PDEVICE_EXTENSION   Extension
    )
{
    NTSTATUS   nError = STATUS_SUCCESS;
    PUCHAR    pPortDCR;       // I/O address of Device Control Register
    PUCHAR    pPortDSR;       // I/O address of Device Status Register
    PUCHAR    pPortECR;       // I/O address of Extended Control Register
    UCHAR    bDCR;           // Contents of DCR
    UCHAR    bDSR;           // Contents of DSR

    if (!Extension->bIsHostRecoverSupported)
    {
        ParDump2( PARENTRY, ( "ParEcpHwHostRecoveryPhase: Host Recovery not supported\r\n"));
        return STATUS_SUCCESS;
    }

    ParDump2( PARENTRY, ( "ParEcpHwHostRecoveryPhase: Host Recovery Start\r\n"));

	// Calculate I/O port addresses for common registers
    pPortDCR = Extension->Controller + OFFSET_DCR;
    pPortDSR = Extension->Controller + OFFSET_DSR;
    #if (0 == DVRH_USE_PARPORT_ECP_ADDR)
        pPortECR = Controller + OFFSET_ECR;
    #else
        pPortECR = Extension->EcrController + ECR_OFFSET;
    #endif

    // Set the ECR to mode 001 (PS2 Mode)
    #if (1 == PARCHIP_ECR_ARBITRATOR)
        // Don't need to flip to Byte mode.  The ECR arbitrator will handle this.
    #else
        WRITE_PORT_UCHAR(pPortECR, DEFAULT_ECR_PS2);
    #endif
    Extension->PortHWMode = HW_MODE_PS2;

    // Set Dir=1 in DCR to disable host bus drive, because the peripheral may 
    // try to drive the bus during host recovery phase.  We are not really going
    // to let any data handshake across, because we don't set HostAck low, and
    // we don't enable the ECP chip during this phase.
    bDCR = READ_PORT_UCHAR(pPortDCR);               // Get content of DCR.
    bDCR = UPDATE_DCR( bDCR, DIR_READ, DONT_CARE, DONT_CARE, DONT_CARE, DONT_CARE, DONT_CARE );
    WRITE_PORT_UCHAR(pPortDCR, bDCR );

    // Check the DCR to see if it has been stomped on
    bDCR = READ_PORT_UCHAR( pPortDCR );
    if ( TEST_DCR( bDCR, DIR_WRITE, DONT_CARE, ACTIVE, ACTIVE, DONT_CARE, DONT_CARE ) )
    {
        // DCR ok, now test DSR for valid state, ignoring PeriphAck since it could change
        bDSR = READ_PORT_UCHAR( pPortDSR );
        // 11/21/95 LLL, CGM: change test to look for XFlag high
        if ( TEST_DSR( bDSR, DONT_CARE, ACTIVE, ACTIVE, ACTIVE, DONT_CARE ) )
        {
            // Drop ReverseRequest to initiate host recovery
            bDCR = UPDATE_DCR( bDCR, DONT_CARE, DONT_CARE, DONT_CARE, INACTIVE, DONT_CARE, DONT_CARE );
            WRITE_PORT_UCHAR( pPortDCR, bDCR );

            // Wait for nAckReverse response
            // 11/21/95 LLL, CGM: tightened test to include PeriphClk and XFlag.
            //                "ZIP_HRP: state 73, DSR" 
            if ( CHECK_DSR( Extension->Controller,
                            DONT_CARE, ACTIVE, INACTIVE, ACTIVE, DONT_CARE, 
                            IEEE_MAXTIME_TL))
            {
                // Yes, raise nReverseRequest, HostClk and HostAck (HostAck high so HW can drive)
                bDCR = UPDATE_DCR( bDCR, DONT_CARE, DONT_CARE, DONT_CARE, ACTIVE, ACTIVE, ACTIVE );
                WRITE_PORT_UCHAR( pPortDCR, bDCR );

                // Wait for nAckReverse response
                // 11/21/95 LLL, CGM: tightened test to include XFlag and PeriphClk.
                //         "ZIP_HRP: state 75, DSR"
                if ( CHECK_DSR( Extension->Controller,
                                DONT_CARE, ACTIVE, ACTIVE, ACTIVE, DONT_CARE, 
                                IEEE_MAXTIME_TL))
                {
                    // Let the host drive the bus again.
                    bDCR = READ_PORT_UCHAR(pPortDCR);               // Get content of DCR.
                    bDCR = UPDATE_DCR( bDCR, DIR_WRITE, DONT_CARE, DONT_CARE, DONT_CARE, DONT_CARE, DONT_CARE );
                    WRITE_PORT_UCHAR(pPortDCR, bDCR );

                    // Recovery is complete, let the caller decide what to do now
                    nError = STATUS_SUCCESS;
                    Extension->CurrentPhase = PHASE_FORWARD_IDLE;
                }
                else
                {
                    nError = STATUS_IO_TIMEOUT;
                    ParDump2( PARERRORS, ( "ParEcpHwHostRecoveryPhase: Error prior to state 75 \r\n"));
                }
            }
            else
            {
                nError = STATUS_IO_TIMEOUT;
                ParDump2( PARERRORS, ( "ParEcpHwHostRecoveryPhase: Error prior to state 73 \r\n"));
			}
        }
        else
        {
            #if DVRH_BUS_RESET_ON_ERROR
                BusReset(pPortDCR);  // Pass in the dcr address
            #endif
            ParDump2( PARERRORS, ( "ParEcpHwHostRecoveryPhase: VE_LINK_FAILURE \r\n"));
            nError = STATUS_LINK_FAILED;
		}
    }
    else
    {
        ParDump2( PARERRORS, ( "ParEcpHwHostRecoveryPhase: VE_PORT_STOMPED \r\n"));
        nError = STATUS_DEVICE_PROTOCOL_ERROR;
   }

    if (!NT_SUCCESS(nError))
    {
        // Make sure both HostAck and HostClk are high before leaving
        // Also let the host drive the bus again.
        bDCR = READ_PORT_UCHAR( pPortDCR );
        bDCR = UPDATE_DCR( bDCR, DIR_WRITE, DONT_CARE, DONT_CARE, DONT_CARE, ACTIVE, ACTIVE );
        WRITE_PORT_UCHAR( pPortDCR, bDCR );

        // [[REVISIT]] pSDCB->wCurrentPhase = PHASE_UNKNOWN;
    }

    // Set the ECR to ECP mode, disable DMA
    #if (1 == PARCHIP_ECR_ARBITRATOR)
        nError = Extension->TrySetChipMode ( Extension->PortContext, ECR_ECP_PIO_MODE );
    #else
        WRITE_PORT_UCHAR(pPortECR, DEFAULT_ECR_ECP);
    #endif
    Extension->PortHWMode = HW_MODE_ECP;

    ParDump2( PAREXIT, ( "ParEcpHwHostRecoveryPhase:: Exit %d\r\n", NT_SUCCESS(nError)));
    return(nError);
}

NTSTATUS
ParEcpHwRead(
    IN  PDEVICE_EXTENSION   Extension,
    IN  PVOID               Buffer,
    IN  ULONG               BufferSize,
    OUT PULONG              BytesTransferred
    )

/*++

Routine Description:

    This routine performs a 1284 ECP mode read under Hardware control
    into the given buffer for no more than 'BufferSize' bytes.

Arguments:

    Extension           - Supplies the device extension.

    Buffer              - Supplies the buffer to read into.

    BufferSize          - Supplies the number of bytes in the buffer.

    BytesTransferred     - Returns the number of bytes transferred.

--*/

{
    NTSTATUS  nError = STATUS_SUCCESS;
    PUCHAR    lpsBufPtr = (PUCHAR)Buffer;    // Pointer to buffer cast to desired data type
    ULONG     dCount = BufferSize;             // Working copy of caller's original request count
    UCHAR     bDSR;               // Contents of DSR
    UCHAR     bPeriphRequest;     // Calculated state of nPeriphReq signal, used in loop
    ULONG     dFifoCount = 0;         // Amount of data pulled from FIFO shadow at start of read
    PUCHAR    wPortDSR = Extension->Controller + DSR_OFFSET;
    #if (0 == DVRH_USE_PARPORT_ECP_ADDR)
        PUCHAR    wPortECR = Extension->Controller + ECR_OFFSET;
        PUCHAR    wPortDFIFO = Extension->Controller + ECP_DFIFO_OFFSET;
    #else
        PUCHAR    wPortECR = Extension->EcrController + ECR_OFFSET;
        PUCHAR    wPortDFIFO = Extension->EcrController;
    #endif
    #if (1 == DVRH_USE_HW_MAXTIME)
        LARGE_INTEGER   WaitOverallTimer;
        LARGE_INTEGER   StartOverallTimer;
        LARGE_INTEGER   EndOverallTimer;
    #else
        LARGE_INTEGER   WaitPerByteTimer;
        LARGE_INTEGER   StartPerByteTimer;
        LARGE_INTEGER   EndPerByteTimer;
        BOOLEAN         bResetTimer = TRUE;
    #endif
    ULONG           wBurstCount;        // Calculated amount of data in FIFO
    UCHAR       ecrFIFO;

    ParTimerCheck(("ParEcpHwRead: Start BufferSize[%d]\r\n", BufferSize));

    #if (1 == DVRH_USE_HW_MAXTIME)
        // Look for limit to overall time spent in this routine.  If bytes are just barely
        // trickling in, we don't want to stay here forever.
        WaitOverallTimer.QuadPart = (990 * 10 * 1000) + KeQueryTimeIncrement();
        //        WaitOverallTimer.QuadPart = (DEFAULT_RECEIVE_TIMEOUT * 10 * 1000) + KeQueryTimeIncrement();
    #else
        WaitPerByteTimer.QuadPart = (35 * 10 * 1000) + KeQueryTimeIncrement();
    #endif

    //----------------------------------------------------------------------
    // Set status byte to indicate Reverse Transfer Phase.
    //----------------------------------------------------------------------
    Extension->CurrentPhase = PHASE_REVERSE_XFER;

    //----------------------------------------------------------------------
    // We've already checked the shadow in ParRead. So go right to the
    // Hardware FIFO and pull more data across.
    //----------------------------------------------------------------------
    #if (1 == DVRH_USE_HW_MAXTIME)
        KeQueryTickCount(&StartOverallTimer);   // Start the timer
    #else
        KeQueryTickCount(&StartPerByteTimer);   // Start the timer
    #endif

ParEcpHwRead_ReadLoopStart:
    //------------------------------------------------------------------
    // Determine whether the FIFO has any data and respond accordingly
    //------------------------------------------------------------------
    ecrFIFO = (UCHAR)(READ_PORT_UCHAR(wPortECR) & (UCHAR)ECR_FIFO_MASK);

    if (ECR_FIFO_FULL == ecrFIFO)
    {
        ParDump2(PARINFO, ("ParEcpHwRead: ECR_FIFO_FULL\r\n"));
        wBurstCount = ( dCount > Extension->FifoDepth ? Extension->FifoDepth : dCount );
        dCount -= wBurstCount;

        #if (1 == PAR_USE_BUFFER_READ_WRITE)
            READ_PORT_BUFFER_UCHAR(wPortDFIFO, lpsBufPtr, wBurstCount);
            lpsBufPtr += wBurstCount;
  	        ParDump2(PARINFO,("ParEcpHwRead: Read FIFOBurst\r\n"));
        #else
            while ( wBurstCount-- )
            {
                *lpsBufPtr = READ_PORT_UCHAR(wPortDFIFO);
		        ParDump2(PARINFO,("ParEcpHwRead: Full FIFO: Read byte value %02x\r\n",(int)*lpsBufPtr));
                lpsBufPtr++;
            }
        #endif
        #if (0 == DVRH_USE_HW_MAXTIME)
            bResetTimer = TRUE;
        #endif
    }
    else if (ECR_FIFO_SOME_DATA == ecrFIFO)
    {
        // Read just one byte at a time, since we don't know exactly how much is
        // in the FIFO.
        *lpsBufPtr = READ_PORT_UCHAR(wPortDFIFO);
        lpsBufPtr++;
        dCount--;
        #if (0 == DVRH_USE_HW_MAXTIME)
            bResetTimer = TRUE;
        #endif
    }
    else    // ECR_FIFO_EMPTY
    {

        ParDump2(PARINFO, ("ParEcpHwRead: ECR_FIFO_EMPTY\r\n"));
        // Nothing to do. We either have a slow peripheral or a bad peripheral.
        // We don't have a good way to figure out if its bad.  Let's chew up our
        // time and hope for the best.
        #if (0 == DVRH_USE_HW_MAXTIME)
            bResetTimer = FALSE;
        #endif
    }   //  ECR_FIFO_EMPTY a.k.a. else clause of (ECR_FIFO_FULL == ecrFIFO)

    if (dCount == 0)
        goto ParEcpHwRead_ReadLoopEnd;
    else
    {
        #if (1 == DVRH_USE_HW_MAXTIME)
            // Limit the overall time we spend in this loop.
            KeQueryTickCount(&EndOverallTimer);
            if (((EndOverallTimer.QuadPart - StartOverallTimer.QuadPart) * KeQueryTimeIncrement()) > WaitOverallTimer.QuadPart)
                goto ParEcpHwRead_ReadLoopEnd;
        #else
            // Limit the overall time we spend in this loop.
            if (bResetTimer)
            {
                bResetTimer = FALSE;
                KeQueryTickCount(&StartPerByteTimer);   // Restart the timer
            }
            else
            {
                KeQueryTickCount(&EndPerByteTimer);
                if (((EndPerByteTimer.QuadPart - StartPerByteTimer.QuadPart) * KeQueryTimeIncrement()) > WaitPerByteTimer.QuadPart)
                    goto ParEcpHwRead_ReadLoopEnd;
            }
        #endif
    }

    goto ParEcpHwRead_ReadLoopStart;
ParEcpHwRead_ReadLoopEnd:

	ParDump2(PARECPTRACE,("ParEcpHwRead: Phase_RevIdle\r\n"));
    Extension->CurrentPhase = PHASE_REVERSE_IDLE;

    *BytesTransferred  = BufferSize - dCount;      // Set current count.

    Extension->log.HwEcpReadCount += *BytesTransferred;

    if (0 == *BytesTransferred)
    {
        bDSR = READ_PORT_UCHAR(wPortDSR);
        bPeriphRequest = (UCHAR)TEST_DSR( bDSR, DONT_CARE, DONT_CARE, DONT_CARE, DONT_CARE, INACTIVE );
        // Only flag a timeout error if the device still said it had data to send.
        if ( bPeriphRequest )
        {
            //
            // Periph still says that it has data, but we timed out trying to read the data.
            //
            ParDump2(PARERRORS, ("ParEcpHwRead: read timout with nPeriphRequest asserted and no data read\r\n"));
            nError = STATUS_IO_TIMEOUT;
            if ((TRUE == Extension->P12843DL.bEventActive) ) {
                //
                // Signal transport that it should try another read
                //
                KeSetEvent(Extension->P12843DL.Event, 0, FALSE);
            }
        }
    }
    ParTimerCheck(("ParEcpHwRead: Exit[%d] BytesTransferred[%d]\r\n", NT_SUCCESS(nError), *BytesTransferred));

    ParDumpReg(PAREXIT, ("ParEcpHwRead: Exit[%d] BytesTransferred[%d]", NT_SUCCESS(nError), *BytesTransferred),
                wPortECR,
                Extension->Controller + OFFSET_DCR,
                Extension->Controller + OFFSET_DSR);

    return nError;
}   // ParEcpHwRead

NTSTATUS
ParEcpHwRecoverPort(
    PDEVICE_EXTENSION Extension,
    UCHAR  bRecoverCode
    )
{
    NTSTATUS   nError = STATUS_SUCCESS;
    PUCHAR    wPortDCR;       // IO address of Device Control Register (DCR)
    PUCHAR    wPortDSR;       // IO address of Device Status Register (DSR)
    PUCHAR    wPortECR;       // IO address of Extended Control Register (ECR)
    PUCHAR    wPortData;      // IO address of Data Register
    UCHAR    bDCR;           // Contents of DCR
    UCHAR    bDSR;           // Contents of DSR
    UCHAR    bDSRmasked;     // DSR after masking low order bits

    ParDump2( PARENTRY, ( "ParEcpHwRecoverPort:  enter %d\r\n", bRecoverCode ));

    // Calculate I/O port addresses for common registers
    wPortDCR = Extension->Controller + OFFSET_DCR;
    wPortDSR = Extension->Controller + OFFSET_DSR;
    #if (0 == DVRH_USE_PARPORT_ECP_ADDR)
        wPortECR = Extension->Controller + ECR_OFFSET;
    #else
        wPortECR = Extension->EcrController + ECR_OFFSET;
    #endif
    wPortData = Extension->Controller + OFFSET_DATA;


    //----------------------------------------------------------------------
    // Check if port is stomped.
    //----------------------------------------------------------------------
    bDCR = READ_PORT_UCHAR(wPortDCR);               // Get content of DCR.

    if ( ! TEST_DCR( bDCR, DONT_CARE, DONT_CARE, ACTIVE, DONT_CARE, DONT_CARE, DONT_CARE ) )
    {
        #if DVRH_BUS_RESET_ON_ERROR
            BusReset(wPortDCR);  // Pass in the dcr address
        #endif
        ParDump2( PARERRORS, ( "!ParEcpHwRecoverPort:  port stomped.\r\n"));
        nError = STATUS_DEVICE_PROTOCOL_ERROR;
    }


    //----------------------------------------------------------------------
    // Attempt a termination phase to get the peripheral recovered.
    // Ignore the error return, we've already got that figured out.
    //----------------------------------------------------------------------
    IeeeTerminate1284Mode(Extension );

    //----------------------------------------------------------------------
    // Set the ECR to PS2 Mode so we can change bus direction.
    //----------------------------------------------------------------------
    #if (1 == PARCHIP_ECR_ARBITRATOR)
        Extension->ClearChipMode( Extension->PortContext, ECR_ECP_PIO_MODE );
    #else
        WRITE_PORT_UCHAR(wPortECR, DEFAULT_ECR_PS2);
    #endif
    Extension->PortHWMode = HW_MODE_PS2;

    //----------------------------------------------------------------------
    // Assert nSelectIn low, nInit high, nStrobe high, and nAutoFd high.
    //----------------------------------------------------------------------
    bDCR = READ_PORT_UCHAR(wPortDCR);             // Get content of DCR.
    bDCR = UPDATE_DCR( bDCR, DIR_WRITE, DONT_CARE, INACTIVE, ACTIVE, ACTIVE, ACTIVE );
    WRITE_PORT_UCHAR(wPortDCR, bDCR);
    WRITE_PORT_UCHAR(wPortData, bRecoverCode);      // Output the error ID
    KeStallExecutionProcessor(100);                 // Hold long enough to capture
    WRITE_PORT_UCHAR(wPortData, 0);                 // Now clear the data lines.


    //----------------------------------------------------------------------
    // Set the ECR to mode 000 (Compatibility Mode).
    //----------------------------------------------------------------------
    #if (1 == PARCHIP_ECR_ARBITRATOR)
        // Nothing needs to be done here.
    #else
        WRITE_PORT_UCHAR(wPortECR, DEFAULT_ECR_COMPATIBILITY);
    #endif
    Extension->PortHWMode = HW_MODE_COMPATIBILITY;


    //----------------------------------------------------------------------
    // Check for any link errors if nothing bad found yet.
    //----------------------------------------------------------------------
    bDSR = READ_PORT_UCHAR(wPortDSR);               // Get content of DSR.
    bDSRmasked = (UCHAR)(bDSR | 0x07);              // Set first 3 bits (don't cares).

    if (NT_SUCCESS(nError))
    {
        if (bDSRmasked != 0xDF)
        {
            ParDump2( PARERRORS, ("!ParEcpHwRecoverPort:  DSR Exp value: 0xDF, Act value: 0x%X\r\n", bDSRmasked));

            // Get DSR again just to make sure...
            bDSR = READ_PORT_UCHAR(wPortDSR);           // Get content of DSR.
            bDSRmasked = (UCHAR)(bDSR | 0x07);          // Set first 3 bits (don't cares).

            if ( (bDSRmasked == CHKPRNOFF1) || (bDSRmasked == CHKPRNOFF2) ) // Check for printer off.
            {
                ParDump2( PARERRORS, ("!ParEcpHwRecoverPort:  DSR value: 0x%X, Printer Off.\r\n", bDSRmasked));
                nError = STATUS_DEVICE_POWERED_OFF;
            }
            else 
            {
                if (bDSRmasked == CHKNOCABLE)   // Check for cable unplugged.
                {
                    ParDump2( PARERRORS, ("!ParEcpHwRecoverPort:  DSR value: 0x%X, Cable Unplugged.\r\n", bDSRmasked));
                    nError = STATUS_DEVICE_NOT_CONNECTED;
                }
                else
                {
                    nError = STATUS_LINK_FAILED;
                }
            }
        }
    }

    //----------------------------------------------------------------------
    // Set status byte to indicate Compatibility Mode.
    //----------------------------------------------------------------------
    Extension->CurrentPhase = PHASE_FORWARD_IDLE;

    ParDump2( PAREXIT, ( "ParEcpHwRecoverPort:  exit, return = 0x%X\r\n", NT_SUCCESS(nError) ));

    return nError;

}   // ParEcpHwRecoverPort

NTSTATUS
ParEcpHwSetAddress(
    IN  PDEVICE_EXTENSION   Extension,
    IN  UCHAR               Address
    )

/*++

Routine Description:

    Sets the ECP Address.
    
Arguments:

    Extension           - Supplies the device extension.

    Address             - The bus address to be set.
    
Return Value:

    None.

--*/
{
    NTSTATUS   nError = STATUS_SUCCESS;
    PUCHAR    wPortDSR;       // IO address of Device Status Register
    PUCHAR    wPortECR;       // IO address of Extended Control Register
    PUCHAR    wPortAFIFO;     // IO address of ECP Address FIFO
    UCHAR    bDSR;           // Contents of DSR
    UCHAR    bECR;           // Contents of ECR
    BOOLEAN    bDone;

    ParDump2( PARENTRY, ("ParEcpHwSetAddress, Start\r\n"));

    // Calculate I/O port addresses for common registers
    wPortDSR = Extension->Controller + DSR_OFFSET;
    #if (0 == DVRH_USE_PARPORT_ECP_ADDR)
        wPortECR = Extension->Controller + ECR_OFFSET;
    #else
        wPortECR = Extension->EcrController + ECR_OFFSET;
    #endif
    wPortAFIFO = Extension->Controller + AFIFO_OFFSET;

    //----------------------------------------------------------------------
    // Check for any link errors.
    //----------------------------------------------------------------------
    //ZIP_CHECK_PORT( DONT_CARE, DONT_CARE, ACTIVE, ACTIVE, DONT_CARE, DONT_CARE,
    //                "ZIP_SCA: init DCR", RECOVER_40, errorExit );

    //ZIP_CHECK_LINK( DONT_CARE, ACTIVE, ACTIVE, ACTIVE, DONT_CARE,
    //                "ZIP_SCA: init DSR", RECOVER_41, errorExit );


    // Set state to indicate ECP forward transfer phase
    Extension->CurrentPhase = PHASE_FORWARD_XFER;


    //----------------------------------------------------------------------
    // Send ECP channel address to AFIFO.
    //----------------------------------------------------------------------
    if ( ! ( TEST_ECR_FIFO( READ_PORT_UCHAR( wPortECR), ECR_FIFO_EMPTY ) ? TRUE : 
             CheckPort( wPortECR, ECR_FIFO_MASK, ECR_FIFO_EMPTY, 
                        IEEE_MAXTIME_TL ) ) )
    {
        nError = ParEcpHwHostRecoveryPhase(Extension);
        ParDump2(PARERRORS, ("ParEcpHwSetAddress: FIFO full, timeout sending ECP channel address\r\n"));
        nError = STATUS_IO_DEVICE_ERROR;
    }
    else
    {

        // Send the address byte.  The most significant bit must be set to distinquish
        // it as an address (as opposed to a run-length compression count).
        WRITE_PORT_UCHAR(wPortAFIFO, (UCHAR)(Address | 0x80));


    }

    if ( NT_SUCCESS(nError) )
    {
        // If there have been no previous errors, and synchronous writes
        // have been requested, wait for the FIFO to empty and the device to
        // complete the last PeriphAck handshake before returning success.
        if ( Extension->bSynchWrites )
        {
            LARGE_INTEGER   Wait;
            LARGE_INTEGER   Start;
            LARGE_INTEGER   End;

            // we wait up to 35 milliseconds.
            Wait.QuadPart = (IEEE_MAXTIME_TL * 10 * 1000) + KeQueryTimeIncrement();  // 35ms

            KeQueryTickCount(&Start);

            bDone = FALSE;
            while ( ! bDone )
            {
                bECR = READ_PORT_UCHAR( wPortECR );
                bDSR = READ_PORT_UCHAR( wPortDSR );
                // LLL/CGM 10/9/95: Tighten up link test - PeriphClk high
                if ( TEST_ECR_FIFO( bECR, ECR_FIFO_EMPTY ) &&
                     TEST_DSR( bDSR, INACTIVE, ACTIVE, ACTIVE, ACTIVE, DONT_CARE ) )
                {
                    bDone = TRUE;
                }
                else
                {
                    KeQueryTickCount(&End);
                    if ((End.QuadPart - Start.QuadPart) * KeQueryTimeIncrement() > Wait.QuadPart)
                    {
                        ParDump2( PARERRORS, ("ParEcpHwSetAddress, timeout during synch\r\n"));
                        bDone = TRUE;
                        nError = ParEcpHwHostRecoveryPhase(Extension);
                        nError = STATUS_IO_DEVICE_ERROR;
                    }
                }
            } // of while...
        } // if bSynchWrites...
    }

    if ( NT_SUCCESS(nError) )
    {
        // Update the state to reflect that we are back in an idle phase
        Extension->CurrentPhase = PHASE_FORWARD_IDLE;
    }
    else if ( nError == STATUS_IO_DEVICE_ERROR )
    {
        // Update the state to reflect that we are back in an idle phase
        Extension->CurrentPhase = PHASE_FORWARD_IDLE;
    }       

    ParDumpReg(PAREXIT, ("ParEcpHwSetAddress: Exit[%d]", NT_SUCCESS(nError)),
                wPortECR,
                Extension->Controller + OFFSET_DCR,
                wPortDSR);

    return nError;
}

NTSTATUS
ParEcpHwSetupPhase(
    IN  PDEVICE_EXTENSION   Extension
    )
/*++

Routine Description:

    This routine performs 1284 Setup Phase.

Arguments:

    Controller      - Supplies the port address.

Return Value:

    STATUS_SUCCESS  - Successful negotiation.

    otherwise       - Unsuccessful negotiation.

--*/
{
    NTSTATUS   Status = STATUS_SUCCESS;
    PUCHAR    pPortDCR;       // IO address of Device Control Register (DCR)
    PUCHAR    pPortDSR;       // IO address of Device Status Register (DSR)
    PUCHAR    pPortECR;       // IO address of Extended Control Register (ECR)
    UCHAR    bDCR;           // Contents of DCR

    ParDump2(PARENTRY,("HardwareECP::SetupPhase: Start\r\n"));

    // Calculate I/O port addresses for common registers
    pPortDCR = Extension->Controller + OFFSET_DCR;
    pPortDSR = Extension->Controller + OFFSET_DSR;
    #if (0 == DVRH_USE_PARPORT_ECP_ADDR)
        pPortECR = Extension->Controller + ECR_OFFSET;
    #else
        pPortECR = Extension->EcrController + ECR_OFFSET;
    #endif

    // Get the DCR and make sure port hasn't been stomped
    //ZIP_CHECK_PORT( DIR_WRITE, DONT_CARE, ACTIVE, ACTIVE, DONT_CARE, DONT_CARE,
    //                "ZIP_SP: init DCR", RECOVER_44, exit1 );


    // Set HostAck low
    bDCR = READ_PORT_UCHAR(pPortDCR);               // Get content of DCR.
    bDCR = UPDATE_DCR( bDCR, DONT_CARE, DONT_CARE, DONT_CARE, DONT_CARE, INACTIVE, DONT_CARE );
    WRITE_PORT_UCHAR( pPortDCR, bDCR );

    // for some reason dvdr doesn't want an extra check in UNSAFE_MODE
    if ( Extension->ModeSafety == SAFE_MODE ) {
        // Wait for nAckReverse to go high
        // LLL/CGM 10/9/95:  look for PeriphAck low, PeriphClk high as per 1284 spec.
        if ( !CHECK_DSR(Extension->Controller, INACTIVE, ACTIVE, ACTIVE, ACTIVE, DONT_CARE,
                        IEEE_MAXTIME_TL ) )
        {
            // Any failure leaves us in an unknown state to recover from.
            Extension->CurrentPhase = PHASE_UNKNOWN;
            Status = STATUS_IO_DEVICE_ERROR;
            goto HWECP_SetupPhaseExitLabel;
        }
    }

    //----------------------------------------------------------------------
    // Set the ECR to mode 001 (PS2 Mode).
    //----------------------------------------------------------------------
    #if (1 == PARCHIP_ECR_ARBITRATOR)
        Status = Extension->TrySetChipMode ( Extension->PortContext, ECR_ECP_PIO_MODE );            
    #else
        WRITE_PORT_UCHAR(pPortECR, DEFAULT_ECR_PS2);
    #endif
    // Set DCR:  DIR=0 for output, HostAck and HostClk high so HW can drive
    bDCR = UPDATE_DCR( bDCR, DIR_WRITE, DONT_CARE, DONT_CARE, DONT_CARE, ACTIVE, ACTIVE );
    WRITE_PORT_UCHAR( pPortDCR, bDCR );

    // Set the ECR to ECP mode, disable DMA
    #if (1 == PARCHIP_ECR_ARBITRATOR)
        // Nothing needs to be done here
    #else
        WRITE_PORT_UCHAR( pPortECR, DEFAULT_ECR_ECP );
    #endif
    Extension->PortHWMode = HW_MODE_ECP;

    // If setup was successful, mark the new ECP phase.
    Extension->CurrentPhase = PHASE_FORWARD_IDLE;

    Status = STATUS_SUCCESS;

HWECP_SetupPhaseExitLabel:

    ParDump2(PARENTRY,("HardwareECP::SetupPhase: End [%d]\r\n", NT_SUCCESS(Status)));
    return Status;
}

NTSTATUS ParEcpHwWaitForEmptyFIFO(IN PDEVICE_EXTENSION   Extension)
/*++

Routine Description:

    This routine will babysit the Fifo.

Arguments:

    Extension  - The device extension.

Return Value:

    NTSTATUS.

--*/
{
    UCHAR           bDSR;         // Contents of DSR
    UCHAR           bECR;         // Contents of ECR
    UCHAR           bDCR;         // Contents of ECR
    BOOLEAN         bDone = FALSE;
    PUCHAR          wPortDSR;
    PUCHAR          wPortECR;
    PUCHAR          wPortDCR;
    LARGE_INTEGER   Wait;
    LARGE_INTEGER   Start;
    LARGE_INTEGER   End;
    NTSTATUS        status = STATUS_SUCCESS;

    // Calculate I/O port addresses for common registers 
    wPortDSR = Extension->Controller + OFFSET_DSR;
    #if (0 == DVRH_USE_PARPORT_ECP_ADDR)
        wPortECR = Extension->Controller + ECR_OFFSET;
    #else
        wPortECR = Extension->EcrController + ECR_OFFSET;
    #endif
    wPortDCR = Extension->Controller + OFFSET_DCR;

    Wait.QuadPart = (330 * 10 * 1000) + KeQueryTimeIncrement();  // 330ms
    
    KeQueryTickCount(&Start);

    //--------------------------------------------------------------------
    // wait for the FIFO to empty and the last
    // handshake of PeriphAck to complete before returning success.
    //--------------------------------------------------------------------

    while ( ! bDone )
    {
        bECR = READ_PORT_UCHAR(wPortECR);
        bDSR = READ_PORT_UCHAR(wPortDSR);
        bDCR = READ_PORT_UCHAR(wPortDCR);
        
#if 0
        if ( TEST_ECR_FIFO( bECR, ECR_FIFO_EMPTY ) &&
            TEST_DCR( bDCR, INACTIVE, INACTIVE, ACTIVE, ACTIVE, DONT_CARE, ACTIVE ) &&
            TEST_DSR( bDSR, INACTIVE, ACTIVE, ACTIVE, ACTIVE, DONT_CARE ) )  {
#else
        if ( TEST_ECR_FIFO( bECR, ECR_FIFO_EMPTY ) &&
            TEST_DCR( bDCR, INACTIVE, DONT_CARE, ACTIVE, ACTIVE, DONT_CARE, ACTIVE ) &&
            TEST_DSR( bDSR, INACTIVE, ACTIVE, ACTIVE, ACTIVE, DONT_CARE ) )  {
#endif
            
            // FIFO is empty, exit without error.
            bDone = TRUE;

        } else {
        
            KeQueryTickCount(&End);
            
            if ((End.QuadPart - Start.QuadPart) * KeQueryTimeIncrement() > Wait.QuadPart) {
                
                // FIFO not empty, timeout occurred, exit with error.
                // NOTE: There is not a good way to determine how many bytes
                // are stuck in the fifo
                ParDump2( PARERRORS, ("ParEcpHwWaitForEmptyFIFO: timeout during synch\r\n"));
                status = STATUS_IO_TIMEOUT;
                bDone = TRUE;
            }
        }
     } // of while...
     
     return status;
}

NTSTATUS
ParEcpHwWrite(
    IN  PDEVICE_EXTENSION   Extension,
    IN  PVOID               Buffer,
    IN  ULONG               BufferSize,
    OUT PULONG              BytesTransferred
    )
/*++

Routine Description:

    Writes data to the peripheral using the ECP protocol under hardware
    control.
    
Arguments:

    Extension           - Supplies the device extension.

    Buffer              - Supplies the buffer to write from.

    BufferSize          - Supplies the number of bytes in the buffer.

    BytesTransferred     - Returns the number of bytes transferred.
    
Return Value:

    None.

--*/
{
    PUCHAR          wPortDSR;
    PUCHAR          wPortECR;
    PUCHAR          wPortDFIFO;
    ULONG           bytesToWrite = BufferSize;
    // ULONG           i;
    // UCHAR           dcr;
    UCHAR           dsr, ecr;
    UCHAR           ecrFIFO;
    LARGE_INTEGER   WaitPerByteTimer;
    LARGE_INTEGER   StartPerByteTimer;
    LARGE_INTEGER   EndPerByteTimer;
    #if (1 == DVRH_USE_HW_MAXTIME)
        LARGE_INTEGER   WaitOverallTimer;
        LARGE_INTEGER   StartOverallTimer;
        LARGE_INTEGER   EndOverallTimer;
    #endif
    BOOLEAN         bResetTimer = TRUE;
    ULONG           wBurstCount;    // Length of burst to write when FIFO empty
    PUCHAR          pBuffer;
    NTSTATUS        Status = STATUS_SUCCESS;

    wPortDSR = Extension->Controller + DSR_OFFSET;
    #if (0 == DVRH_USE_PARPORT_ECP_ADDR)
        wPortECR = Extension->Controller+ ECR_OFFSET;
        wPortDFIFO = Extension->Controller + ECP_DFIFO_OFFSET;
    #else
        wPortECR = Extension->EcrController + ECR_OFFSET;
        wPortDFIFO = Extension->EcrController;
    #endif
    pBuffer    = Buffer;

    ParTimerCheck(("ParEcpHwWrite: Start bytesToWrite[%d]\r\n", bytesToWrite));
    
    Status = ParTestEcpWrite(Extension);
    if (!NT_SUCCESS(Status))
    {
        Extension->CurrentPhase = PHASE_UNKNOWN;                     
        Extension->Connected = FALSE;                                
        ParDump2(PARERRORS,("ParEcpHwWrite: Invalid Entry State\r\n"));
        goto ParEcpHwWrite_ExitLabel;       // Use a goto so we can see Debug info located at the end of proc!
    }

    Extension->CurrentPhase = PHASE_FORWARD_XFER;
    //----------------------------------------------------------------------
    // Setup Timer Stuff.
    //----------------------------------------------------------------------
    // we wait up to 35 milliseconds.
    WaitPerByteTimer.QuadPart = (35 * 10 * 1000) + KeQueryTimeIncrement();  // 35ms
    #if (1 == DVRH_USE_HW_MAXTIME)
        WaitOverallTimer.QuadPart = (330 * 10 * 1000) + KeQueryTimeIncrement();  // 35ms
        //WaitOverallTimer.QuadPart = (35 * 10 * 1000) + KeQueryTimeIncrement();  // 35ms
    #endif
    
    // Set up the overall timer that limits how much time is spent per call
    // to this function.
    #if (1 == DVRH_USE_HW_MAXTIME)
        KeQueryTickCount(&StartOverallTimer);
    #endif

    // Set up the timer that limits the time allowed for per-byte handshakes.
    KeQueryTickCount(&StartPerByteTimer);

    //----------------------------------------------------------------------
    // Send the data to the DFIFO.
    //----------------------------------------------------------------------

HWECP_WriteLoop_Start:

    //------------------------------------------------------------------
    // Determine whether the FIFO has space and respond accordingly.
    //------------------------------------------------------------------
    ecrFIFO = (UCHAR)(READ_PORT_UCHAR(wPortECR) & ECR_FIFO_MASK);

    if ( ECR_FIFO_EMPTY == ecrFIFO )
    {
        wBurstCount = (bytesToWrite > Extension->FifoDepth) ? Extension->FifoDepth : bytesToWrite;
        bytesToWrite -= wBurstCount;

        #if (PAR_USE_BUFFER_READ_WRITE == 1)
  			ParDump2(PARINFO,("ParEcpHwWrite: FIFOBurst\r\n"));
            WRITE_PORT_BUFFER_UCHAR(wPortDFIFO, pBuffer, wBurstCount);
            pBuffer += wBurstCount;
        #else
            while ( wBurstCount-- ) 
            {
    			ParDump2(PARINFO,("ParEcpHwWrite: FIFOBurst: %02x\r\n",(int)*pBuffer));
                WRITE_PORT_UCHAR(wPortDFIFO, *pBuffer++);
            }
        #endif

        bResetTimer = TRUE;
    }
    else if (ECR_FIFO_SOME_DATA == ecrFIFO)
    {
        // Write just one byte at a time, since we don't know exactly how much
        // room there is.
        ParDump2(PARINFO,("ParEcpHwWrite: OneByte: %02x\r\n",(int)*pBuffer));
        WRITE_PORT_UCHAR(wPortDFIFO, *pBuffer++);
        bytesToWrite--;
        bResetTimer = TRUE;
    }
    else {    //  ECR_FIFO_FULL
        ParDump2(PARINFO,("ParEcpHwWrite: ECR_FIFO_FULL ecr=%02x\r\n",(int)READ_PORT_UCHAR(wPortECR)));
        // Need to figure out whether to keep attempting to send, or to quit
        // with a timeout status.

        // Reset the per-byte timer if a byte was received since the last
        // timer check.
        if ( bResetTimer )
        {
   			ParDump2(PARINFO,("ParEcpHwWrite: ECR_FIFO_FULL Reset Timer\r\n"));
            KeQueryTickCount(&StartPerByteTimer);
            bResetTimer = FALSE;
        }

        KeQueryTickCount(&EndPerByteTimer);
        if ((EndPerByteTimer.QuadPart - StartPerByteTimer.QuadPart) * KeQueryTimeIncrement() > WaitPerByteTimer.QuadPart)
        {
            ParDump2(PARERRORS,("ParEcpHwWrite: ECR_FIFO_FULL Timeout ecr=%02x\r\n",(int)READ_PORT_UCHAR(wPortECR)));
            Status = STATUS_TIMEOUT;
            // Peripheral is either busy or stalled.  If the peripheral
            // is busy then they should be using SWECP to allow for
            // relaxed timings.  Let's punt!
            goto HWECP_WriteLoop_End;
        }
    }

    if (bytesToWrite == 0)
    {
        goto HWECP_WriteLoop_End; // Transfer completed.
    }

    #if (1 == DVRH_USE_HW_MAXTIME)
        // Limit the overall time we spend in this loop, in case the
        // peripheral is taking data at a slow overall rate.
        KeQueryTickCount(&EndOverallTimer);
        if ((EndOverallTimer.QuadPart - StartOverallTimer.QuadPart) * KeQueryTimeIncrement() > WaitOverallTimer.QuadPart)
	    {
		    ParDump2(PARERRORS,("ParEcpHwWrite: OverAll Timer expired!\r\n"));
            Status = STATUS_TIMEOUT;
            goto HWECP_WriteLoop_End;
	    }
    #endif
    goto HWECP_WriteLoop_Start; // Start over

HWECP_WriteLoop_End:

    if ( NT_SUCCESS(Status) )
    {
        // If there have been no previous errors, and synchronous writes
        // have been requested, wait for the FIFO to empty and the last
        // handshake of PeriphAck to complete before returning success.
        if (Extension->bSynchWrites )
        {
            BOOLEAN         bDone = FALSE;

			ParDump2(PARINFO,("ParEcpHwWrite: Waiting for FIFO to empty\r\n"));
            #if (0 == DVRH_USE_HW_MAXTIME)
                KeQueryTickCount(&StartPerByteTimer);
            #endif
            while ( ! bDone )
            {
                ecr = READ_PORT_UCHAR(wPortECR);
                dsr = READ_PORT_UCHAR(wPortDSR);
                // LLL/CGM 10/9/95: tighten up DSR test - PeriphClk should be high
                if ( TEST_ECR_FIFO( ecr, ECR_FIFO_EMPTY ) &&
                     TEST_DSR( dsr, INACTIVE, ACTIVE, ACTIVE, ACTIVE, DONT_CARE ) )
                {
                    ParDump2(PARINFO,("ParEcpHwWrite: FIFO is now empty\r\n"));
                    // FIFO is empty, exit without error.
                    bDone = TRUE;
                }
                else
                {
                #if (1 == DVRH_USE_HW_MAXTIME)
                    KeQueryTickCount(&EndOverallTimer);
                    if ((EndOverallTimer.QuadPart - StartOverallTimer.QuadPart) * KeQueryTimeIncrement() > WaitOverallTimer.QuadPart)
                #else
                    KeQueryTickCount(&EndPerByteTimer);
                    if ((EndPerByteTimer.QuadPart - StartPerByteTimer.QuadPart) * KeQueryTimeIncrement() > WaitPerByteTimer.QuadPart)
                #endif
                    {
            			ParDump2(PARERRORS,("ParEcpHwWrite: FIFO didn't empty. dsr[%x] ecr[%x]\r\n", dsr, ecr));
                        // FIFO not empty, timeout occurred, exit with error.
                        Status = STATUS_TIMEOUT;
                        bDone = TRUE;
                        //HPKAssertOnError(0, ("ZIP_FTP, timeout during synch\r\n"));
                    }
                }
            } // of while...
        }
    }

    Extension->CurrentPhase = PHASE_FORWARD_IDLE;

ParEcpHwWrite_ExitLabel:

    *BytesTransferred = BufferSize - bytesToWrite;

    Extension->log.HwEcpWriteCount += *BytesTransferred;

    ParTimerCheck(("ParEcpHwWrite: Exit[%d] BytesTransferred[%d]\r\n", NT_SUCCESS(Status), (long)*BytesTransferred));
    //    ParDumpReg(PAREXIT | PARECPTRACE, ("ParEcpHwWrite: Exit[%d] BytesTransferred[%d]", NT_SUCCESS(Status), (long)*BytesTransferred),
    ParDumpReg(PAREXIT, ("ParEcpHwWrite: Exit[%d] BytesTransferred[%d]", NT_SUCCESS(Status), (long)*BytesTransferred),
                wPortECR,
                Extension->Controller + OFFSET_DCR,
                wPortDSR);

    return Status;
}

NTSTATUS
ParEnterEcpHwMode(
    IN  PDEVICE_EXTENSION   Extension,
    IN  BOOLEAN             DeviceIdRequest
    )
/*++

Routine Description:

    This routine performs 1284 negotiation with the peripheral to the
    ECP mode protocol.

Arguments:

    Controller      - Supplies the port address.

    DeviceIdRequest - Supplies whether or not this is a request for a device
                        id.

Return Value:

    STATUS_SUCCESS  - Successful negotiation.

    otherwise       - Unsuccessful negotiation.

--*/
{
    NTSTATUS Status = STATUS_SUCCESS;
    PUCHAR Controller;

    ParDump2(PARENTRY,("ParEnterEcpHwMode: Start EcrController %x\n", Extension->EcrController));
    Controller = Extension->Controller;

    if ( Extension->ModeSafety == SAFE_MODE ) {
        if (DeviceIdRequest) {
            Status = IeeeEnter1284Mode (Extension, ECP_EXTENSIBILITY | DEVICE_ID_REQ);
        } else {
            Status = IeeeEnter1284Mode (Extension, ECP_EXTENSIBILITY);
        }
    } else {
        ParDump2(PARINFO, ("ParEnterEcpHwMode:: UNSAFE_MODE.\n"));
        Extension->Connected = TRUE;
    }
    
    // LAC ENTEREXIT  5Dec97
    // Make sure that the ECR is in PS/2 mode, and that wPortHWMode
    // has the correct value.  (This is the common entry mode);
    #if (0 == PARCHIP_ECR_ARBITRATOR)
        WRITE_PORT_UCHAR( Controller + ECR_OFFSET, DEFAULT_ECR_PS2 );
    #endif    
    Extension->PortHWMode = HW_MODE_PS2;

    if (NT_SUCCESS(Status)) {
        Status = ParEcpHwSetupPhase(Extension);
        Extension->bSynchWrites = TRUE;     // NOTE this is a temp hack!!!  dvrh
        if (!Extension->bShadowBuffer)
        {
            Queue_Create(&(Extension->ShadowBuffer), Extension->FifoDepth * 2);		
            Extension->bShadowBuffer = TRUE;
        }
        Extension->IsIeeeTerminateOk = TRUE;
    }

    return Status;
}

BOOLEAN
ParIsEcpHwSupported(
    IN  PDEVICE_EXTENSION   Extension
    )
/*++

Routine Description:

    This routine determines whether or not ECP mode is suported
    in the write direction by trying to negotiate when asked.

Arguments:

    Extension  - The device extension.

Return Value:

    BOOLEAN.

--*/
{
    NTSTATUS Status;

    if (Extension->BadProtocolModes & ECP_HW_NOIRQ)
        return FALSE;

    if (Extension->ProtocolModesSupported & ECP_HW_NOIRQ)
        return TRUE;

    if (!(Extension->HardwareCapabilities & PPT_ECP_PRESENT))
        return FALSE;

    if (0 == Extension->FifoWidth)
        return FALSE;
        
    if (Extension->ProtocolModesSupported & ECP_SW)
        return TRUE;

    // Must use HWECP Enter and Terminate for this test.
    // Internel state machines will fail otherwise.  --dvrh
    Status = ParEnterEcpHwMode (Extension, FALSE);
    ParTerminateHwEcpMode (Extension);

    if (NT_SUCCESS(Status)) {

        Extension->ProtocolModesSupported |= ECP_HW_NOIRQ;
        return TRUE;
    }
    return FALSE;
}

VOID
ParTerminateHwEcpMode(
    IN  PDEVICE_EXTENSION   Extension
    )
/*++

Routine Description:

    This routine terminates the interface back to compatibility mode.

Arguments:

    Controller  - Supplies the parallel port's controller address.

Return Value:

    None.

--*/
{
    ParDump2( PARENTRY, ("HWECP::Terminate Entry CurrentPhase %d\r\n", Extension->CurrentPhase));
	// Need to check current phase -- if its reverse, need to flip bus
	// If its not forward -- its an incorrect phase and termination will fail.
    if ( Extension->ModeSafety == SAFE_MODE ) {

        switch (Extension->CurrentPhase)
	    {
		    case  PHASE_FORWARD_IDLE:	// Legal state to terminate
    		{
	    		break;
		    }
            case PHASE_REVERSE_IDLE:	// Flip the bus so we can terminate
            {
                NTSTATUS status = ParEcpHwExitReversePhase( Extension );

            	if ( STATUS_SUCCESS == status )
            	{
            		status = ParEcpEnterForwardPhase(Extension );
        	    }
                else
                {
                    ParDump2( PARERRORS, ("HWECP::Terminate Couldn't flip the bus\r\n"));
                }
                break;
            }
		    case  PHASE_FORWARD_XFER:
            case  PHASE_REVERSE_XFER:
	    	{
                ParDump2( PARERRORS, ("HWECP::Terminate invalid wCurrentPhase (XFer in progress) \r\n"));
    			//status = VE_BUSY;
                // Dunno what to do here.  We probably will confuse the peripheral.
		    	break;
    		}
	    	// LAC TERMINATED  13Jan98
		    // Included PHASE_TERMINATE in the switch so we won't return an
    		// error if we are already terminated.
	    	case PHASE_TERMINATE:
		    {
    			// We are already terminated, nothing to do
	    		break;
		    }	
            default:
            {
                ParDump2( PARERRORS, ("ECP::Terminate VE_CORRUPT: invalid CurrentPhase %d\r\n", Extension->CurrentPhase));
                //status = VE_CORRUPT;
                // Dunno what to do here.  We're lost and don't have a map to figure
                // out where we are!
                break;
            }
	    }

        ParDump2(PARINFO, ("HWECP::Terminate - Test ECPChanAddr\r\n"));

        ParEcpHwWaitForEmptyFIFO(Extension);
        ParCleanupHwEcpPort(Extension);
        IeeeTerminate1284Mode (Extension);
    } else {
        ParCleanupHwEcpPort(Extension);
        ParDump2(PARINFO, ("HWECP::Terminate - UNSAFE_MODE\r\n"));
        Extension->Connected = FALSE;
    }
    return;
}