1416 lines
48 KiB
C
1416 lines
48 KiB
C
/*++
|
||
|
||
Copyright (C) Microsoft Corporation, 1993 - 2000
|
||
|
||
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"
|
||
|
||
VOID
|
||
ParCleanupHwEcpPort(
|
||
IN PPDO_EXTENSION Pdx
|
||
)
|
||
/*++
|
||
|
||
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.
|
||
|
||
--*/
|
||
{
|
||
//----------------------------------------------------------------------
|
||
// Set the ECR to mode 001 (PS2 Mode).
|
||
//----------------------------------------------------------------------
|
||
Pdx->ClearChipMode( Pdx->PortContext, ECR_ECP_PIO_MODE );
|
||
Pdx->PortHWMode = HW_MODE_PS2;
|
||
|
||
ParCleanupSwEcpPort(Pdx);
|
||
|
||
//----------------------------------------------------------------------
|
||
// Set the ECR to mode 000 (Compatibility Mode).
|
||
//----------------------------------------------------------------------
|
||
Pdx->PortHWMode = HW_MODE_COMPATIBILITY;
|
||
}
|
||
|
||
// Drain data from Shadow Buffer
|
||
VOID PptEcpHwDrainShadowBuffer(
|
||
IN Queue *pShadowBuffer,
|
||
IN PUCHAR lpsBufPtr,
|
||
IN ULONG dCount,
|
||
OUT ULONG *fifoCount)
|
||
{
|
||
*fifoCount = 0;
|
||
|
||
if( Queue_IsEmpty( pShadowBuffer ) ) {
|
||
return;
|
||
}
|
||
|
||
while( dCount > 0 ) {
|
||
// 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.
|
||
return;
|
||
}
|
||
++lpsBufPtr;
|
||
--dCount; // Decrement count.
|
||
++(*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 PPDO_EXTENSION Pdx)
|
||
{
|
||
NTSTATUS nError = STATUS_SUCCESS;
|
||
Queue *pShadowBuffer;
|
||
UCHAR bData;
|
||
PUCHAR wPortDFIFO = Pdx->EcrController; // IO address of ECP Data FIFO
|
||
PUCHAR wPortECR = Pdx->EcrController + ECR_OFFSET; // IO address of Extended Control Register (ECR)
|
||
|
||
// 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 = &(Pdx->ShadowBuffer);
|
||
|
||
#if 1 == DBG_SHOW_BYTES
|
||
if( DbgShowBytes ) {
|
||
DbgPrint("r: ");
|
||
}
|
||
#endif
|
||
|
||
while ((P5ReadPortUchar(wPortECR) & ECR_FIFO_EMPTY) == 0 ) {
|
||
// Break out the Port Read so we can observe the data if needed
|
||
bData = P5ReadPortUchar(wPortDFIFO);
|
||
|
||
#if 1 == DBG_SHOW_BYTES
|
||
if( DbgShowBytes ) {
|
||
DbgPrint("%02x ",bData);
|
||
}
|
||
#endif
|
||
|
||
// Put byte in queue.
|
||
if (FALSE == Queue_Enqueue(pShadowBuffer, bData)) {
|
||
DD((PCE)Pdx,DDT,"ParEcpHwEmptyFIFO - Shadow buffer full, FIFO not empty\n");
|
||
nError = STATUS_BUFFER_OVERFLOW;
|
||
goto ParEcpHwEmptyFIFO_ExitLabel;
|
||
}
|
||
}
|
||
|
||
#if 1 == DBG_SHOW_BYTES
|
||
if( DbgShowBytes ) {
|
||
DbgPrint("zz\n");
|
||
}
|
||
#endif
|
||
|
||
if( ( !Queue_IsEmpty(pShadowBuffer) && (Pdx->P12843DL.bEventActive) )) {
|
||
KeSetEvent(Pdx->P12843DL.Event, 0, FALSE);
|
||
}
|
||
|
||
ParEcpHwEmptyFIFO_ExitLabel:
|
||
return nError;
|
||
}
|
||
|
||
//=========================================================
|
||
// HardwareECP::ExitForwardPhase
|
||
//
|
||
// Description : Exit from HWECP Forward Phase to the common phase (FWD IDLE, PS/2)
|
||
//
|
||
//=========================================================
|
||
NTSTATUS ParEcpHwExitForwardPhase( IN PPDO_EXTENSION Pdx )
|
||
{
|
||
NTSTATUS status;
|
||
|
||
DD((PCE)Pdx,DDT,"ParEcpHwExitForwardPhase\n");
|
||
|
||
// 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( Pdx );
|
||
|
||
P5SetPhase( Pdx, PHASE_FORWARD_IDLE );
|
||
|
||
return status;
|
||
}
|
||
|
||
//=========================================================
|
||
// HardwareECP::EnterReversePhase
|
||
//
|
||
// Description : Go from the common phase to HWECP Reverse Phase
|
||
//
|
||
//=========================================================
|
||
NTSTATUS PptEcpHwEnterReversePhase( IN PPDO_EXTENSION Pdx )
|
||
{
|
||
NTSTATUS status;
|
||
PUCHAR Controller;
|
||
PUCHAR wPortECR; // I/O address of Extended Control Register
|
||
PUCHAR wPortDCR; // I/O address of Device Control Register
|
||
UCHAR dcr;
|
||
|
||
Controller = Pdx->Controller;
|
||
wPortECR = Pdx->EcrController + ECR_OFFSET;
|
||
wPortDCR = Controller + OFFSET_DCR;
|
||
|
||
|
||
// EnterReversePhase assumes that we are in PHASE_FORWARD_IDLE,
|
||
// and that the ECPMode is set to PS/2 mode at entry.
|
||
|
||
//----------------------------------------------------------------------
|
||
// Set the ECR to mode 001 (PS2 Mode).
|
||
//----------------------------------------------------------------------
|
||
Pdx->ClearChipMode( Pdx->PortContext, ECR_ECP_PIO_MODE );
|
||
|
||
// We need to be in PS/2 (BiDi) mode in order to disable the host
|
||
// driving the data lines when we flip the direction bit in the DCR.
|
||
// This is a requirement for entering ECP state 38 in the 1284 spec.
|
||
// Changed - 2000-02-11
|
||
status = Pdx->TrySetChipMode( Pdx->PortContext, ECR_BYTE_MODE );
|
||
// ignore status - subsequent operations may still work
|
||
|
||
Pdx->PortHWMode = HW_MODE_PS2;
|
||
|
||
if ( Pdx->ModeSafety == SAFE_MODE ) {
|
||
|
||
// Reverse the bus first (using ECP::EnterReversePhase)
|
||
status = ParEcpEnterReversePhase(Pdx);
|
||
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) ) {
|
||
DD((PCE)Pdx,DDT,"PptEcpHwEnterReversePhase: State 40 failed\n");
|
||
status = ParEcpHwRecoverPort( Pdx, RECOVER_28 );
|
||
if ( NT_SUCCESS(status))
|
||
status = STATUS_LINK_FAILED;
|
||
goto PptEcpHwEnterReversePhase_ExitLabel;
|
||
} else {
|
||
P5SetPhase( Pdx, PHASE_REVERSE_IDLE );
|
||
}
|
||
}
|
||
} else {
|
||
//----------------------------------------------------------------------
|
||
// Set Dir=1 in DCR for reading.
|
||
//----------------------------------------------------------------------
|
||
dcr = P5ReadPortUchar( wPortDCR ); // Get content of DCR.
|
||
dcr = UPDATE_DCR( dcr, DIR_READ, DONT_CARE, DONT_CARE, DONT_CARE, DONT_CARE, DONT_CARE );
|
||
P5WritePortUchar(wPortDCR, dcr);
|
||
}
|
||
|
||
//----------------------------------------------------------------------
|
||
// Set the ECR to mode 011 (ECP Mode). DmaEnable=0.
|
||
//----------------------------------------------------------------------
|
||
status = Pdx->TrySetChipMode ( Pdx->PortContext, ECR_ECP_PIO_MODE );
|
||
if ( !NT_SUCCESS(status) ) {
|
||
DD((PCE)Pdx,DDT,"PptEcpHwEnterReversePhase - TrySetChipMode failed\n");
|
||
}
|
||
Pdx->PortHWMode = HW_MODE_ECP;
|
||
|
||
//----------------------------------------------------------------------
|
||
// Set nStrobe=0 and nAutoFd=0 in DCR, so that ECP HW can control.
|
||
//----------------------------------------------------------------------
|
||
dcr = P5ReadPortUchar( wPortDCR ); // Get content of DCR.
|
||
dcr = UPDATE_DCR( dcr, DIR_READ, DONT_CARE, DONT_CARE, DONT_CARE, ACTIVE, ACTIVE);
|
||
P5WritePortUchar( wPortDCR, dcr );
|
||
|
||
// Set the phase variable to ReverseIdle
|
||
P5SetPhase( Pdx, PHASE_REVERSE_IDLE );
|
||
|
||
PptEcpHwEnterReversePhase_ExitLabel:
|
||
|
||
return status;
|
||
}
|
||
|
||
//=========================================================
|
||
// HardwareECP::ExitReversePhase
|
||
//
|
||
// Description : Get out of HWECP Reverse Phase to the common state
|
||
//
|
||
//=========================================================
|
||
NTSTATUS ParEcpHwExitReversePhase( IN PPDO_EXTENSION Pdx )
|
||
{
|
||
NTSTATUS nError = STATUS_SUCCESS;
|
||
UCHAR bDCR;
|
||
UCHAR bECR;
|
||
PUCHAR wPortECR;
|
||
PUCHAR wPortDCR;
|
||
PUCHAR Controller;
|
||
|
||
DD((PCE)Pdx,DDT,"ParEcpHwExitReversePhase - enter\n");
|
||
Controller = Pdx->Controller;
|
||
wPortECR = Pdx->EcrController + ECR_OFFSET;
|
||
wPortDCR = Controller + OFFSET_DCR;
|
||
|
||
//----------------------------------------------------------------------
|
||
// Set status byte to indicate Reverse To Forward Mode.
|
||
//----------------------------------------------------------------------
|
||
P5SetPhase( Pdx, PHASE_REV_TO_FWD );
|
||
|
||
if ( Pdx->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 = P5ReadPortUchar(wPortDCR); // Get content of DCR.
|
||
bDCR = UPDATE_DCR( bDCR, DONT_CARE, DONT_CARE, DONT_CARE, ACTIVE, DONT_CARE, DONT_CARE );
|
||
P5WritePortUchar(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) ) {
|
||
DD((PCE)Pdx,DDW,"ParEcpHwExitReversePhase: Periph failed state 48/49.\n");
|
||
nError = ParEcpHwRecoverPort( Pdx, RECOVER_37 ); // Reset port.
|
||
if( NT_SUCCESS(nError) ) {
|
||
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 = P5ReadPortUchar(wPortECR); // Get content of ECR.
|
||
if ((bECR & ECR_FIFO_EMPTY) == 0) { // Check if FIFO is not empty.
|
||
if( (nError = ParEcpHwEmptyFIFO(Pdx)) != STATUS_SUCCESS ) {
|
||
DD((PCE)Pdx,DDT,"ParEcpHwExitReversePhase: Attempt to empty ECP chip failed.\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 );
|
||
P5WritePortUchar(wPortDCR, bDCR );
|
||
|
||
} // SAFE_MODE
|
||
|
||
//----------------------------------------------------------------------
|
||
// Set the ECR to PS2 Mode so we can change bus direction.
|
||
//----------------------------------------------------------------------
|
||
Pdx->ClearChipMode( Pdx->PortContext, ECR_ECP_PIO_MODE );
|
||
Pdx->PortHWMode = HW_MODE_PS2;
|
||
|
||
|
||
//----------------------------------------------------------------------
|
||
// Set Dir=0 (Write) in DCR.
|
||
//----------------------------------------------------------------------
|
||
bDCR = P5ReadPortUchar(wPortDCR);
|
||
bDCR = UPDATE_DCR( bDCR, DIR_WRITE, DONT_CARE, DONT_CARE, DONT_CARE, DONT_CARE, DONT_CARE );
|
||
P5WritePortUchar(wPortDCR, bDCR );
|
||
|
||
|
||
//----------------------------------------------------------------------
|
||
// Set the ECR back to ECP Mode. DmaEnable=0.
|
||
//----------------------------------------------------------------------
|
||
nError = Pdx->TrySetChipMode ( Pdx->PortContext, ECR_ECP_PIO_MODE );
|
||
Pdx->PortHWMode = HW_MODE_ECP;
|
||
|
||
|
||
P5SetPhase( Pdx, PHASE_FORWARD_IDLE );
|
||
|
||
return nError;
|
||
}
|
||
|
||
BOOLEAN
|
||
PptEcpHwHaveReadData (
|
||
IN PPDO_EXTENSION Pdx
|
||
)
|
||
{
|
||
Queue *pQueue;
|
||
|
||
// check shadow buffer
|
||
pQueue = &(Pdx->ShadowBuffer);
|
||
if (!Queue_IsEmpty(pQueue)) {
|
||
return TRUE;
|
||
}
|
||
|
||
// check periph
|
||
if (ParEcpHaveReadData(Pdx))
|
||
return TRUE;
|
||
|
||
// Check if FIFO is not empty.
|
||
return (BOOLEAN)( (UCHAR)0 == (P5ReadPortUchar(Pdx->EcrController + ECR_OFFSET) & ECR_FIFO_EMPTY) );
|
||
}
|
||
|
||
NTSTATUS
|
||
ParEcpHwHostRecoveryPhase(
|
||
IN PPDO_EXTENSION Pdx
|
||
)
|
||
{
|
||
NTSTATUS status = 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( !Pdx->bIsHostRecoverSupported ) {
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
DD((PCE)Pdx,DDT,"ParEcpHwHostRecoveryPhase - enter\n");
|
||
|
||
// Calculate I/O port addresses for common registers
|
||
pPortDCR = Pdx->Controller + OFFSET_DCR;
|
||
pPortDSR = Pdx->Controller + OFFSET_DSR;
|
||
pPortECR = Pdx->EcrController + ECR_OFFSET;
|
||
|
||
// Set the ECR to mode 001 (PS2 Mode)
|
||
// Don't need to flip to Byte mode. The ECR arbitrator will handle this.
|
||
Pdx->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 = P5ReadPortUchar(pPortDCR); // Get content of DCR.
|
||
bDCR = UPDATE_DCR( bDCR, DIR_READ, DONT_CARE, DONT_CARE, DONT_CARE, DONT_CARE, DONT_CARE );
|
||
P5WritePortUchar(pPortDCR, bDCR );
|
||
|
||
// Check the DCR to see if it has been stomped on
|
||
bDCR = P5ReadPortUchar( 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 = P5ReadPortUchar( 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 );
|
||
P5WritePortUchar( 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( Pdx->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 );
|
||
P5WritePortUchar( 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( Pdx->Controller, DONT_CARE, ACTIVE, ACTIVE, ACTIVE, DONT_CARE, IEEE_MAXTIME_TL) ) {
|
||
// Let the host drive the bus again.
|
||
bDCR = P5ReadPortUchar(pPortDCR); // Get content of DCR.
|
||
bDCR = UPDATE_DCR( bDCR, DIR_WRITE, DONT_CARE, DONT_CARE, DONT_CARE, DONT_CARE, DONT_CARE );
|
||
P5WritePortUchar(pPortDCR, bDCR );
|
||
|
||
// Recovery is complete, let the caller decide what to do now
|
||
status = STATUS_SUCCESS;
|
||
P5SetPhase( Pdx, PHASE_FORWARD_IDLE );
|
||
} else {
|
||
status = STATUS_IO_TIMEOUT;
|
||
DD((PCE)Pdx,DDW,"ParEcpHwHostRecoveryPhase - error prior to state 75\n");
|
||
}
|
||
} else {
|
||
status = STATUS_IO_TIMEOUT;
|
||
DD((PCE)Pdx,DDW,"ParEcpHwHostRecoveryPhase - error prior to state 73\n");
|
||
}
|
||
} else {
|
||
#if DVRH_BUS_RESET_ON_ERROR
|
||
BusReset(pPortDCR); // Pass in the dcr address
|
||
#endif
|
||
DD((PCE)Pdx,DDT, "ParEcpHwHostRecoveryPhase: VE_LINK_FAILURE \n");
|
||
status = STATUS_LINK_FAILED;
|
||
}
|
||
} else {
|
||
DD((PCE)Pdx,DDW,"ParEcpHwHostRecoveryPhase: VE_PORT_STOMPED\n");
|
||
status = STATUS_DEVICE_PROTOCOL_ERROR;
|
||
}
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
// Make sure both HostAck and HostClk are high before leaving
|
||
// Also let the host drive the bus again.
|
||
bDCR = P5ReadPortUchar( pPortDCR );
|
||
bDCR = UPDATE_DCR( bDCR, DIR_WRITE, DONT_CARE, DONT_CARE, DONT_CARE, ACTIVE, ACTIVE );
|
||
P5WritePortUchar( pPortDCR, bDCR );
|
||
|
||
// [[REVISIT]] pSDCB->wCurrentPhase = PHASE_UNKNOWN;
|
||
}
|
||
|
||
// Set the ECR to ECP mode, disable DMA
|
||
status = Pdx->TrySetChipMode ( Pdx->PortContext, ECR_ECP_PIO_MODE );
|
||
|
||
Pdx->PortHWMode = HW_MODE_ECP;
|
||
|
||
DD((PCE)Pdx,DDT,"ParEcpHwHostRecoveryPhase - Exit w/status = %x\n", status);
|
||
|
||
return status;
|
||
}
|
||
|
||
NTSTATUS
|
||
ParEcpHwRead(
|
||
IN PPDO_EXTENSION Pdx,
|
||
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:
|
||
|
||
Pdx - 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 status = 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
|
||
PUCHAR wPortDSR = Pdx->Controller + DSR_OFFSET;
|
||
PUCHAR wPortECR = Pdx->EcrController + ECR_OFFSET;
|
||
PUCHAR wPortDFIFO = Pdx->EcrController;
|
||
LARGE_INTEGER WaitPerByteTimer;
|
||
LARGE_INTEGER StartPerByteTimer;
|
||
LARGE_INTEGER EndPerByteTimer;
|
||
BOOLEAN bResetTimer = TRUE;
|
||
ULONG wBurstCount; // Calculated amount of data in FIFO
|
||
UCHAR ecrFIFO;
|
||
|
||
WaitPerByteTimer.QuadPart = (35 * 10 * 1000) + KeQueryTimeIncrement();
|
||
|
||
//----------------------------------------------------------------------
|
||
// Set status byte to indicate Reverse Transfer Phase.
|
||
//----------------------------------------------------------------------
|
||
P5SetPhase( Pdx, PHASE_REVERSE_XFER );
|
||
|
||
//----------------------------------------------------------------------
|
||
// We've already checked the shadow in ParRead. So go right to the
|
||
// Hardware FIFO and pull more data across.
|
||
//----------------------------------------------------------------------
|
||
KeQueryTickCount(&StartPerByteTimer); // Start the timer
|
||
|
||
ParEcpHwRead_ReadLoopStart:
|
||
//------------------------------------------------------------------
|
||
// Determine whether the FIFO has any data and respond accordingly
|
||
//------------------------------------------------------------------
|
||
ecrFIFO = (UCHAR)(P5ReadPortUchar(wPortECR) & (UCHAR)ECR_FIFO_MASK);
|
||
|
||
if (ECR_FIFO_FULL == ecrFIFO) {
|
||
|
||
wBurstCount = ( dCount > Pdx->FifoDepth ? Pdx->FifoDepth : dCount );
|
||
dCount -= wBurstCount;
|
||
|
||
P5ReadPortBufferUchar(wPortDFIFO, lpsBufPtr, wBurstCount);
|
||
lpsBufPtr += wBurstCount;
|
||
|
||
bResetTimer = TRUE;
|
||
|
||
} 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 = P5ReadPortUchar(wPortDFIFO);
|
||
lpsBufPtr++;
|
||
dCount--;
|
||
|
||
bResetTimer = TRUE;
|
||
|
||
} else { // ECR_FIFO_EMPTY
|
||
|
||
DD((PCE)Pdx,DDW,"ParEcpHwRead - ECR_FIFO_EMPTY - slow or bad periph?\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.
|
||
|
||
bResetTimer = FALSE;
|
||
|
||
} // ECR_FIFO_EMPTY a.k.a. else clause of (ECR_FIFO_FULL == ecrFIFO)
|
||
|
||
if (dCount == 0)
|
||
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;
|
||
}
|
||
|
||
}
|
||
|
||
goto ParEcpHwRead_ReadLoopStart;
|
||
|
||
ParEcpHwRead_ReadLoopEnd:
|
||
|
||
P5SetPhase( Pdx, PHASE_REVERSE_IDLE );
|
||
|
||
*BytesTransferred = BufferSize - dCount; // Set current count.
|
||
|
||
Pdx->log.HwEcpReadCount += *BytesTransferred;
|
||
|
||
if (0 == *BytesTransferred) {
|
||
bDSR = P5ReadPortUchar(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.
|
||
//
|
||
DD((PCE)Pdx,DDE,"ParEcpHwRead - read timout with nPeriphRequest asserted and no data read - error - STATUS_IO_TIMEOUT\n");
|
||
status = STATUS_IO_TIMEOUT;
|
||
if ((TRUE == Pdx->P12843DL.bEventActive) ) {
|
||
//
|
||
// Signal transport that it should try another read
|
||
//
|
||
KeSetEvent(Pdx->P12843DL.Event, 0, FALSE);
|
||
}
|
||
}
|
||
}
|
||
|
||
DD((PCE)Pdx,DDT,"ParEcpHwRead: Exit - status=%x, BytesTransferred[%d] dsr[%02x] dcr[%02x] ecr[%02x]\n",
|
||
status, *BytesTransferred, P5ReadPortUchar(wPortDSR),
|
||
P5ReadPortUchar(Pdx->Controller + OFFSET_DCR), P5ReadPortUchar(wPortECR));
|
||
|
||
#if 1 == DBG_SHOW_BYTES
|
||
if( DbgShowBytes ) {
|
||
if( NT_SUCCESS( status ) && (*BytesTransferred > 0) ) {
|
||
const ULONG maxBytes = 32;
|
||
ULONG i;
|
||
PUCHAR bytePtr = (PUCHAR)Buffer;
|
||
DbgPrint("R: ");
|
||
for( i=0 ; (i < *BytesTransferred) && (i < maxBytes ) ; ++i ) {
|
||
DbgPrint("%02x ",*bytePtr++);
|
||
}
|
||
if( *BytesTransferred > maxBytes ) {
|
||
DbgPrint("... ");
|
||
}
|
||
DbgPrint("zz\n");
|
||
}
|
||
}
|
||
#endif
|
||
|
||
return status;
|
||
} // ParEcpHwRead
|
||
|
||
NTSTATUS
|
||
ParEcpHwRecoverPort(
|
||
PPDO_EXTENSION Pdx,
|
||
UCHAR bRecoverCode
|
||
)
|
||
{
|
||
NTSTATUS status = 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
|
||
|
||
DD((PCE)Pdx,DDT,"ParEcpHwRecoverPort: enter %d\n", bRecoverCode );
|
||
|
||
// Calculate I/O port addresses for common registers
|
||
wPortDCR = Pdx->Controller + OFFSET_DCR;
|
||
wPortDSR = Pdx->Controller + OFFSET_DSR;
|
||
wPortECR = Pdx->EcrController + ECR_OFFSET;
|
||
wPortData = Pdx->Controller + OFFSET_DATA;
|
||
|
||
|
||
//----------------------------------------------------------------------
|
||
// Check if port is stomped.
|
||
//----------------------------------------------------------------------
|
||
bDCR = P5ReadPortUchar(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
|
||
DD((PCE)Pdx,DDE,"ParEcpHwRecoverPort - port stomped\n");
|
||
status = 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(Pdx );
|
||
|
||
//----------------------------------------------------------------------
|
||
// Set the ECR to PS2 Mode so we can change bus direction.
|
||
//----------------------------------------------------------------------
|
||
Pdx->ClearChipMode( Pdx->PortContext, ECR_ECP_PIO_MODE );
|
||
Pdx->PortHWMode = HW_MODE_PS2;
|
||
|
||
//----------------------------------------------------------------------
|
||
// Assert nSelectIn low, nInit high, nStrobe high, and nAutoFd high.
|
||
//----------------------------------------------------------------------
|
||
bDCR = P5ReadPortUchar(wPortDCR); // Get content of DCR.
|
||
bDCR = UPDATE_DCR( bDCR, DIR_WRITE, DONT_CARE, INACTIVE, ACTIVE, ACTIVE, ACTIVE );
|
||
P5WritePortUchar(wPortDCR, bDCR);
|
||
P5WritePortUchar(wPortData, bRecoverCode); // Output the error ID
|
||
KeStallExecutionProcessor(100); // Hold long enough to capture
|
||
P5WritePortUchar(wPortData, 0); // Now clear the data lines.
|
||
|
||
|
||
//----------------------------------------------------------------------
|
||
// Set the ECR to mode 000 (Compatibility Mode).
|
||
//----------------------------------------------------------------------
|
||
// Nothing needs to be done here.
|
||
Pdx->PortHWMode = HW_MODE_COMPATIBILITY;
|
||
|
||
|
||
//----------------------------------------------------------------------
|
||
// Check for any link errors if nothing bad found yet.
|
||
//----------------------------------------------------------------------
|
||
bDSR = P5ReadPortUchar(wPortDSR); // Get content of DSR.
|
||
bDSRmasked = (UCHAR)(bDSR | 0x07); // Set first 3 bits (don't cares).
|
||
|
||
if( NT_SUCCESS(status) ) {
|
||
|
||
if (bDSRmasked != 0xDF) {
|
||
|
||
DD((PCE)Pdx,DDE,"ParEcpHwRecoverPort - DSR Exp value: 0xDF, Act value: 0x%X\n",bDSRmasked);
|
||
|
||
// Get DSR again just to make sure...
|
||
bDSR = P5ReadPortUchar(wPortDSR); // Get content of DSR.
|
||
bDSRmasked = (UCHAR)(bDSR | 0x07); // Set first 3 bits (don't cares).
|
||
|
||
if( (CHKPRNOFF1 == bDSRmasked ) || (CHKPRNOFF2 == bDSRmasked ) ) { // Check for printer off.
|
||
DD((PCE)Pdx,DDW,"ParEcpHwRecoverPort - DSR value: 0x%X, Printer Off\n", bDSRmasked);
|
||
status = STATUS_DEVICE_POWERED_OFF;
|
||
} else {
|
||
if( CHKNOCABLE == bDSRmasked ) { // Check for cable unplugged.
|
||
DD((PCE)Pdx,DDW,"ParEcpHwRecoverPort - DSR value: 0x%X, Cable Unplugged\n",bDSRmasked);
|
||
status = STATUS_DEVICE_NOT_CONNECTED;
|
||
} else {
|
||
DD((PCE)Pdx,DDW,"ParEcpHwRecoverPort - DSR value: 0x%X, Unknown error\n",bDSRmasked);
|
||
status = STATUS_LINK_FAILED;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
//----------------------------------------------------------------------
|
||
// Set status byte to indicate Compatibility Mode.
|
||
//----------------------------------------------------------------------
|
||
P5SetPhase( Pdx, PHASE_FORWARD_IDLE );
|
||
|
||
return status;
|
||
|
||
} // ParEcpHwRecoverPort
|
||
|
||
NTSTATUS
|
||
ParEcpHwSetAddress(
|
||
IN PPDO_EXTENSION Pdx,
|
||
IN UCHAR Address
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Sets the ECP Address.
|
||
|
||
Arguments:
|
||
|
||
Pdx - Supplies the device extension.
|
||
|
||
Address - The bus address to be set.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status = 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;
|
||
|
||
DD((PCE)Pdx,DDT,"ParEcpHwSetAddress, Start\n");
|
||
|
||
// Calculate I/O port addresses for common registers
|
||
wPortDSR = Pdx->Controller + DSR_OFFSET;
|
||
wPortECR = Pdx->EcrController + ECR_OFFSET;
|
||
wPortAFIFO = Pdx->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
|
||
P5SetPhase( Pdx, PHASE_FORWARD_XFER );
|
||
|
||
|
||
//----------------------------------------------------------------------
|
||
// Send ECP channel address to AFIFO.
|
||
//----------------------------------------------------------------------
|
||
if ( ! ( TEST_ECR_FIFO( P5ReadPortUchar( wPortECR), ECR_FIFO_EMPTY ) ? TRUE :
|
||
CheckPort( wPortECR, ECR_FIFO_MASK, ECR_FIFO_EMPTY, IEEE_MAXTIME_TL ) ) ) {
|
||
|
||
status = ParEcpHwHostRecoveryPhase(Pdx);
|
||
DD((PCE)Pdx,DDT,"ParEcpHwSetAddress: FIFO full, timeout sending ECP channel address\n");
|
||
status = 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).
|
||
P5WritePortUchar(wPortAFIFO, (UCHAR)(Address | 0x80));
|
||
}
|
||
|
||
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 device to
|
||
// complete the last PeriphAck handshake before returning success.
|
||
|
||
if ( Pdx->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 = P5ReadPortUchar( wPortECR );
|
||
bDSR = P5ReadPortUchar( 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) {
|
||
DD((PCE)Pdx,DDT,"ParEcpHwSetAddress, timeout during synch\n");
|
||
bDone = TRUE;
|
||
status = ParEcpHwHostRecoveryPhase(Pdx);
|
||
status = STATUS_IO_DEVICE_ERROR;
|
||
}
|
||
|
||
}
|
||
|
||
} // of while...
|
||
|
||
} // if bSynchWrites...
|
||
|
||
}
|
||
|
||
if ( NT_SUCCESS(status) ) {
|
||
// Update the state to reflect that we are back in an idle phase
|
||
P5SetPhase( Pdx, PHASE_FORWARD_IDLE );
|
||
} else if ( status == STATUS_IO_DEVICE_ERROR ) {
|
||
// Update the state to reflect that we are back in an idle phase
|
||
P5SetPhase( Pdx, PHASE_FORWARD_IDLE );
|
||
}
|
||
|
||
return status;
|
||
}
|
||
|
||
NTSTATUS
|
||
ParEcpHwSetupPhase(
|
||
IN PPDO_EXTENSION Pdx
|
||
)
|
||
/*++
|
||
|
||
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
|
||
|
||
// Calculate I/O port addresses for common registers
|
||
pPortDCR = Pdx->Controller + OFFSET_DCR;
|
||
pPortDSR = Pdx->Controller + OFFSET_DSR;
|
||
pPortECR = Pdx->EcrController + ECR_OFFSET;
|
||
|
||
// 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 = P5ReadPortUchar(pPortDCR); // Get content of DCR.
|
||
bDCR = UPDATE_DCR( bDCR, DONT_CARE, DONT_CARE, DONT_CARE, DONT_CARE, INACTIVE, DONT_CARE );
|
||
P5WritePortUchar( pPortDCR, bDCR );
|
||
|
||
// for some reason dvdr doesn't want an extra check in UNSAFE_MODE
|
||
if ( Pdx->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(Pdx->Controller, INACTIVE, ACTIVE, ACTIVE, ACTIVE, DONT_CARE,
|
||
IEEE_MAXTIME_TL ) )
|
||
{
|
||
// Any failure leaves us in an unknown state to recover from.
|
||
P5SetPhase( Pdx, PHASE_UNKNOWN );
|
||
Status = STATUS_IO_DEVICE_ERROR;
|
||
goto HWECP_SetupPhaseExitLabel;
|
||
}
|
||
}
|
||
|
||
//----------------------------------------------------------------------
|
||
// Set the ECR to mode 001 (PS2 Mode).
|
||
//----------------------------------------------------------------------
|
||
Status = Pdx->TrySetChipMode ( Pdx->PortContext, ECR_ECP_PIO_MODE );
|
||
// 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 );
|
||
P5WritePortUchar( pPortDCR, bDCR );
|
||
|
||
// Set the ECR to ECP mode, disable DMA
|
||
|
||
Pdx->PortHWMode = HW_MODE_ECP;
|
||
|
||
// If setup was successful, mark the new ECP phase.
|
||
P5SetPhase( Pdx, PHASE_FORWARD_IDLE );
|
||
|
||
Status = STATUS_SUCCESS;
|
||
|
||
HWECP_SetupPhaseExitLabel:
|
||
|
||
DD((PCE)Pdx,DDT,"ParEcpHwSetupPhase - exit w/status=%x\n",Status);
|
||
|
||
return Status;
|
||
}
|
||
|
||
NTSTATUS ParEcpHwWaitForEmptyFIFO(IN PPDO_EXTENSION Pdx)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine will babysit the Fifo.
|
||
|
||
Arguments:
|
||
|
||
Pdx - 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 = Pdx->Controller + OFFSET_DSR;
|
||
wPortECR = Pdx->EcrController + ECR_OFFSET;
|
||
wPortDCR = Pdx->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 = P5ReadPortUchar(wPortECR);
|
||
bDSR = P5ReadPortUchar(wPortDSR);
|
||
bDCR = P5ReadPortUchar(wPortDCR);
|
||
|
||
#if 0 // one bit differs - keep alternate around until we know which to really use
|
||
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
|
||
DD((PCE)Pdx,DDT,"ParEcpHwWaitForEmptyFIFO: timeout during synch\n");
|
||
status = STATUS_IO_TIMEOUT;
|
||
bDone = TRUE;
|
||
}
|
||
}
|
||
} // of while...
|
||
|
||
return status;
|
||
}
|
||
|
||
NTSTATUS
|
||
ParEcpHwWrite(
|
||
IN PPDO_EXTENSION Pdx,
|
||
IN PVOID Buffer,
|
||
IN ULONG BufferSize,
|
||
OUT PULONG BytesTransferred
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Writes data to the peripheral using the ECP protocol under hardware
|
||
control.
|
||
|
||
Arguments:
|
||
|
||
Pdx - 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;
|
||
UCHAR dsr;
|
||
UCHAR ecr;
|
||
UCHAR ecrFIFO;
|
||
LARGE_INTEGER WaitPerByteTimer;
|
||
LARGE_INTEGER StartPerByteTimer;
|
||
LARGE_INTEGER EndPerByteTimer;
|
||
BOOLEAN bResetTimer = TRUE;
|
||
ULONG wBurstCount; // Length of burst to write when FIFO empty
|
||
PUCHAR pBuffer;
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
|
||
wPortDSR = Pdx->Controller + DSR_OFFSET;
|
||
wPortECR = Pdx->EcrController + ECR_OFFSET;
|
||
wPortDFIFO = Pdx->EcrController;
|
||
pBuffer = Buffer;
|
||
|
||
status = ParTestEcpWrite(Pdx);
|
||
if (!NT_SUCCESS(status)) {
|
||
P5SetPhase( Pdx, PHASE_UNKNOWN );
|
||
Pdx->Connected = FALSE;
|
||
DD((PCE)Pdx,DDT,"ParEcpHwWrite: Invalid Entry State\n");
|
||
goto ParEcpHwWrite_ExitLabel; // Use a goto so we can see Debug info located at the end of proc!
|
||
}
|
||
|
||
P5SetPhase( Pdx, PHASE_FORWARD_XFER );
|
||
//----------------------------------------------------------------------
|
||
// Setup Timer Stuff.
|
||
//----------------------------------------------------------------------
|
||
// we wait up to 35 milliseconds.
|
||
WaitPerByteTimer.QuadPart = (35 * 10 * 1000) + KeQueryTimeIncrement(); // 35ms
|
||
|
||
// 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)(P5ReadPortUchar(wPortECR) & ECR_FIFO_MASK);
|
||
|
||
if ( ECR_FIFO_EMPTY == ecrFIFO ) {
|
||
wBurstCount = (bytesToWrite > Pdx->FifoDepth) ? Pdx->FifoDepth : bytesToWrite;
|
||
bytesToWrite -= wBurstCount;
|
||
|
||
P5WritePortBufferUchar(wPortDFIFO, pBuffer, wBurstCount);
|
||
pBuffer += wBurstCount;
|
||
|
||
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 in the FIFO.
|
||
P5WritePortUchar(wPortDFIFO, *pBuffer++);
|
||
bytesToWrite--;
|
||
bResetTimer = TRUE;
|
||
} else { // ECR_FIFO_FULL
|
||
// 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 ) {
|
||
KeQueryTickCount(&StartPerByteTimer);
|
||
bResetTimer = FALSE;
|
||
}
|
||
|
||
KeQueryTickCount(&EndPerByteTimer);
|
||
if ((EndPerByteTimer.QuadPart - StartPerByteTimer.QuadPart) * KeQueryTimeIncrement() > WaitPerByteTimer.QuadPart) {
|
||
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.
|
||
}
|
||
|
||
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 (Pdx->bSynchWrites ) {
|
||
BOOLEAN bDone = FALSE;
|
||
|
||
|
||
KeQueryTickCount(&StartPerByteTimer);
|
||
|
||
while( !bDone ) {
|
||
ecr = P5ReadPortUchar(wPortECR);
|
||
dsr = P5ReadPortUchar(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 ) ) {
|
||
// FIFO is empty, exit without error.
|
||
bDone = TRUE;
|
||
} else {
|
||
|
||
KeQueryTickCount(&EndPerByteTimer);
|
||
if ((EndPerByteTimer.QuadPart - StartPerByteTimer.QuadPart) * KeQueryTimeIncrement() > WaitPerByteTimer.QuadPart) {
|
||
// FIFO not empty, timeout occurred, exit with error.
|
||
status = STATUS_TIMEOUT;
|
||
bDone = TRUE;
|
||
}
|
||
}
|
||
} // of while...
|
||
}
|
||
}
|
||
|
||
P5SetPhase( Pdx, PHASE_FORWARD_IDLE );
|
||
|
||
ParEcpHwWrite_ExitLabel:
|
||
|
||
*BytesTransferred = BufferSize - bytesToWrite;
|
||
|
||
Pdx->log.HwEcpWriteCount += *BytesTransferred;
|
||
|
||
DD((PCE)Pdx,DDT,"ParEcpHwWrite: exit w/status=%x, BytesTransferred=%d, dsr=%02x dcr=%02x, ecr=%02x\n",
|
||
status, *BytesTransferred, P5ReadPortUchar(wPortDSR), P5ReadPortUchar(Pdx->Controller + OFFSET_DCR), P5ReadPortUchar(wPortECR));
|
||
|
||
#if 1 == DBG_SHOW_BYTES
|
||
if( DbgShowBytes ) {
|
||
if( NT_SUCCESS( status ) && (*BytesTransferred > 0) ) {
|
||
const ULONG maxBytes = 32;
|
||
ULONG i;
|
||
PUCHAR bytePtr = (PUCHAR)Buffer;
|
||
DbgPrint("W: ");
|
||
for( i=0 ; (i < *BytesTransferred) && (i < maxBytes) ; ++i ) {
|
||
DbgPrint("%02x ",*bytePtr++);
|
||
}
|
||
if( *BytesTransferred > maxBytes ) {
|
||
DbgPrint("... ");
|
||
}
|
||
DbgPrint("zz\n");
|
||
}
|
||
}
|
||
#endif
|
||
|
||
return status;
|
||
}
|
||
|
||
NTSTATUS
|
||
ParEnterEcpHwMode(
|
||
IN PPDO_EXTENSION Pdx,
|
||
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;
|
||
|
||
Controller = Pdx->Controller;
|
||
|
||
if ( Pdx->ModeSafety == SAFE_MODE ) {
|
||
if (DeviceIdRequest) {
|
||
Status = IeeeEnter1284Mode (Pdx, ECP_EXTENSIBILITY | DEVICE_ID_REQ);
|
||
} else {
|
||
Status = IeeeEnter1284Mode (Pdx, ECP_EXTENSIBILITY);
|
||
}
|
||
} else {
|
||
Pdx->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);
|
||
Pdx->PortHWMode = HW_MODE_PS2;
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
Status = ParEcpHwSetupPhase(Pdx);
|
||
Pdx->bSynchWrites = TRUE; // NOTE this is a temp hack!!! dvrh
|
||
if (!Pdx->bShadowBuffer)
|
||
{
|
||
Queue_Create(&(Pdx->ShadowBuffer), Pdx->FifoDepth * 2);
|
||
Pdx->bShadowBuffer = TRUE;
|
||
}
|
||
Pdx->IsIeeeTerminateOk = TRUE;
|
||
}
|
||
|
||
return Status;
|
||
}
|
||
|
||
BOOLEAN
|
||
ParIsEcpHwSupported(
|
||
IN PPDO_EXTENSION Pdx
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine determines whether or not ECP mode is suported
|
||
in the write direction by trying to negotiate when asked.
|
||
|
||
Arguments:
|
||
|
||
Pdx - The device extension.
|
||
|
||
Return Value:
|
||
|
||
BOOLEAN.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS Status;
|
||
|
||
if (Pdx->BadProtocolModes & ECP_HW_NOIRQ)
|
||
return FALSE;
|
||
|
||
if (Pdx->ProtocolModesSupported & ECP_HW_NOIRQ)
|
||
return TRUE;
|
||
|
||
if (!(Pdx->HardwareCapabilities & PPT_ECP_PRESENT))
|
||
return FALSE;
|
||
|
||
if (0 == Pdx->FifoWidth)
|
||
return FALSE;
|
||
|
||
if (Pdx->ProtocolModesSupported & ECP_SW)
|
||
return TRUE;
|
||
|
||
// Must use HWECP Enter and Terminate for this test.
|
||
// Internel state machines will fail otherwise. --dvrh
|
||
Status = ParEnterEcpHwMode (Pdx, FALSE);
|
||
ParTerminateHwEcpMode (Pdx);
|
||
|
||
if (NT_SUCCESS(Status)) {
|
||
|
||
Pdx->ProtocolModesSupported |= ECP_HW_NOIRQ;
|
||
return TRUE;
|
||
}
|
||
return FALSE;
|
||
}
|
||
|
||
VOID
|
||
ParTerminateHwEcpMode(
|
||
IN PPDO_EXTENSION Pdx
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine terminates the interface back to compatibility mode.
|
||
|
||
Arguments:
|
||
|
||
Controller - Supplies the parallel port's controller address.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
|
||
// 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( Pdx->ModeSafety == SAFE_MODE ) {
|
||
|
||
switch( Pdx->CurrentPhase ) {
|
||
|
||
case PHASE_FORWARD_IDLE: // Legal state to terminate from
|
||
break;
|
||
|
||
case PHASE_TERMINATE: // already terminated, nothing to do
|
||
DD((PCE)Pdx,DDW,"ParTerminateHwEcpMode - Already Terminated - Why are we trying to terminate again?\n");
|
||
goto target_exit;
|
||
break;
|
||
|
||
case PHASE_REVERSE_IDLE: // Flip bus to forward so we can terminate
|
||
{
|
||
NTSTATUS status = ParEcpHwExitReversePhase( Pdx );
|
||
if( STATUS_SUCCESS == status ) {
|
||
status = ParEcpEnterForwardPhase( Pdx );
|
||
}
|
||
}
|
||
break;
|
||
|
||
case PHASE_FORWARD_XFER:
|
||
case PHASE_REVERSE_XFER:
|
||
default:
|
||
DD((PCE)Pdx,DDE,"ParTerminateHwEcpMode - Invalid Phase [%x] for termination\n", Pdx->CurrentPhase);
|
||
// Don't know what to do here!?!
|
||
}
|
||
|
||
ParEcpHwWaitForEmptyFIFO( Pdx );
|
||
|
||
ParCleanupHwEcpPort( Pdx );
|
||
|
||
IeeeTerminate1284Mode( Pdx );
|
||
|
||
} else {
|
||
// UNSAFE_MODE
|
||
ParCleanupHwEcpPort(Pdx);
|
||
Pdx->Connected = FALSE;
|
||
}
|
||
|
||
target_exit:
|
||
|
||
return;
|
||
}
|
||
|