/*++ Copyright (C) Microsoft Corporation, 1993 - 1999 Module Name: swecp.c Abstract: Enhanced Capabilities Port (ECP) This module contains the code to perform all ECP related tasks (including ECP Software and ECP Hardware modes.) Author: Tim Wells (WESTTEK) - April 16, 1997 Environment: Kernel mode Revision History : --*/ #include "pch.h" #include "ecp.h" BOOLEAN ParIsEcpSwWriteSupported( IN PDEVICE_EXTENSION Extension ); BOOLEAN ParIsEcpSwReadSupported( IN PDEVICE_EXTENSION Extension ); NTSTATUS ParEnterEcpSwMode( IN PDEVICE_EXTENSION Extension, IN BOOLEAN DeviceIdRequest ); VOID ParCleanupSwEcpPort( IN PDEVICE_EXTENSION Extension ); VOID ParTerminateEcpMode( IN PDEVICE_EXTENSION Extension ); NTSTATUS ParEcpSetAddress( IN PDEVICE_EXTENSION Extension, IN UCHAR Address ); NTSTATUS ParEcpSwWrite( IN PDEVICE_EXTENSION Extension, IN PVOID Buffer, IN ULONG BufferSize, OUT PULONG BytesTransferred ); NTSTATUS ParEcpSwRead( IN PDEVICE_EXTENSION Extension, IN PVOID Buffer, IN ULONG BufferSize, OUT PULONG BytesTransferred ); NTSTATUS ParEcpForwardToReverse( IN PDEVICE_EXTENSION Extension ); NTSTATUS ParEcpReverseToForward( IN PDEVICE_EXTENSION Extension ); BOOLEAN ParIsEcpSwWriteSupported( 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_SW) return FALSE; if (Extension->ProtocolModesSupported & ECP_SW) return TRUE; Status = ParEnterEcpSwMode (Extension, FALSE); ParTerminateEcpMode (Extension); if (NT_SUCCESS(Status)) { Extension->ProtocolModesSupported |= ECP_SW; return TRUE; } return FALSE; } BOOLEAN ParIsEcpSwReadSupported( IN PDEVICE_EXTENSION Extension ) /*++ Routine Description: This routine determines whether or not ECP mode is suported in the read direction (need to be able to float the data register drivers in order to do byte wide reads) by trying negotiate when asked. Arguments: Extension - The device extension. Return Value: BOOLEAN. --*/ { NTSTATUS Status; if (!(Extension->HardwareCapabilities & PPT_ECP_PRESENT) && !(Extension->HardwareCapabilities & PPT_BYTE_PRESENT)) { // Only use ECP Software in the reverse direction if an // ECR is present or we know that we can put the data register into Byte mode. return FALSE; } if (Extension->BadProtocolModes & ECP_SW) return FALSE; if (Extension->ProtocolModesSupported & ECP_SW) return TRUE; // Must use SWECP Enter and Terminate for this test. // Internel state machines will fail otherwise. --dvrh Status = ParEnterEcpSwMode (Extension, FALSE); ParTerminateEcpMode (Extension); if (NT_SUCCESS(Status)) { Extension->ProtocolModesSupported |= ECP_SW; return TRUE; } return FALSE; } NTSTATUS ParEnterEcpSwMode( 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; if ( Extension->ModeSafety == SAFE_MODE ) { if (DeviceIdRequest) { Status = IeeeEnter1284Mode (Extension, ECP_EXTENSIBILITY | DEVICE_ID_REQ); } else { Status = IeeeEnter1284Mode (Extension, ECP_EXTENSIBILITY); } } else { ParDump2(PARINFO, ("ParEnterEcpSwMode: In UNSAFE_MODE.\n")); Extension->Connected = TRUE; } if (NT_SUCCESS(Status)) { Status = ParEcpSetupPhase(Extension); } return Status; } VOID ParCleanupSwEcpPort( 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; UCHAR dcr; // Contents of DCR Controller = Extension->Controller; //---------------------------------------------------------------------- // Set data bus for output //---------------------------------------------------------------------- dcr = READ_PORT_UCHAR(Controller + OFFSET_DCR); // Get content of DCR dcr = UPDATE_DCR( dcr, DIR_WRITE, DONT_CARE, DONT_CARE, DONT_CARE, DONT_CARE, DONT_CARE ); WRITE_PORT_UCHAR( Controller + OFFSET_DCR, dcr ); return; } VOID ParTerminateEcpMode( 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. --*/ { ParCleanupSwEcpPort(Extension); if ( Extension->ModeSafety == SAFE_MODE ) { IeeeTerminate1284Mode (Extension); } else { ParDump2(PARINFO, ("ParTerminateEcpMode: In UNSAFE_MODE.\n")); Extension->Connected = FALSE; } return; } NTSTATUS ParEcpSetAddress( 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. --*/ { PUCHAR Controller; PUCHAR DCRController; UCHAR dsr; UCHAR dcr; ParDump2( PARENTRY, ("ParEcpSetAddress: Start: Channel [%x]\n", Address)); Controller = Extension->Controller; DCRController = Controller + OFFSET_DCR; // // Event 34 // // HostAck low indicates a command byte Extension->CurrentEvent = 34; dcr = READ_PORT_UCHAR(DCRController); dcr = UPDATE_DCR( dcr, DIR_WRITE, DONT_CARE, DONT_CARE, DONT_CARE, INACTIVE, DONT_CARE ); WRITE_PORT_UCHAR(DCRController, dcr); // Place the channel address on the bus // Bit 7 of the byte sent must be 1 to indicate that this is an address // instead of run length count. // WRITE_PORT_UCHAR(Controller + DATA_OFFSET, (UCHAR)(Address | 0x80)); // // Event 35 // // Start handshake by dropping HostClk Extension->CurrentEvent = 35; dcr = UPDATE_DCR( dcr, DIR_WRITE, DONT_CARE, DONT_CARE, DONT_CARE, DONT_CARE, INACTIVE ); WRITE_PORT_UCHAR(DCRController, dcr); // =============== Periph State 36 ===============8 // PeriphAck/PtrBusy = High (signals state 36) // PeriphClk/PtrClk = Don't Care // nAckReverse/AckDataReq = Don't Care // XFlag = Don't Care // nPeriphReq/nDataAvail = Don't Care Extension->CurrentEvent = 35; if (!CHECK_DSR(Controller, ACTIVE, DONT_CARE, DONT_CARE, DONT_CARE, DONT_CARE, DEFAULT_RECEIVE_TIMEOUT)) { ParDump2(PARERRORS, ("ECP::SendChannelAddress:State 36 Failed: Controller %x\n", Controller)); // Make sure both HostAck and HostClk are high before leaving // HostClk should be high in forward transfer except when handshaking // HostAck should be high to indicate that what follows is data (not commands) // dcr = UPDATE_DCR( dcr, DONT_CARE, DONT_CARE, DONT_CARE, DONT_CARE, ACTIVE, ACTIVE ); WRITE_PORT_UCHAR(DCRController, dcr); return STATUS_IO_DEVICE_ERROR; } // // Event 37 // // Finish handshake by raising HostClk // HostClk is high when it's 0 // Extension->CurrentEvent = 37; dcr = UPDATE_DCR( dcr, DONT_CARE, DONT_CARE, DONT_CARE, DONT_CARE, DONT_CARE, ACTIVE ); WRITE_PORT_UCHAR(DCRController, dcr); // =============== Periph State 32 ===============8 // PeriphAck/PtrBusy = Low (signals state 32) // PeriphClk/PtrClk = Don't Care // nAckReverse/AckDataReq = Don't Care // XFlag = Don't Care // nPeriphReq/nDataAvail = Don't Care Extension->CurrentEvent = 32; if (!CHECK_DSR(Controller, INACTIVE, DONT_CARE, DONT_CARE, DONT_CARE, DONT_CARE, DEFAULT_RECEIVE_TIMEOUT)) { ParDump2(PARERRORS, ("ECP::SendChannelAddress:State 32 Failed: Controller %x\n", Controller)); // Make sure both HostAck and HostClk are high before leaving // HostClk should be high in forward transfer except when handshaking // HostAck should be high to indicate that what follows is data (not commands) // dcr = UPDATE_DCR( dcr, DONT_CARE, DONT_CARE, DONT_CARE, DONT_CARE, ACTIVE, ACTIVE ); WRITE_PORT_UCHAR(DCRController, dcr); return STATUS_IO_DEVICE_ERROR; } // Make sure both HostAck and HostClk are high before leaving // HostClk should be high in forward transfer except when handshaking // HostAck should be high to indicate that what follows is data (not commands) // dcr = UPDATE_DCR( dcr, DONT_CARE, DONT_CARE, DONT_CARE, DONT_CARE, ACTIVE, ACTIVE ); WRITE_PORT_UCHAR(DCRController, dcr); ParDump2( PAREXIT, ("ParEcpSetAddress, Exit [%d]\n", NT_SUCCESS(STATUS_SUCCESS))); return STATUS_SUCCESS; } NTSTATUS ParEcpSwWrite( 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 software 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 Controller; NTSTATUS Status = STATUS_SUCCESS; PUCHAR pBuffer; LARGE_INTEGER Timeout; LARGE_INTEGER StartWrite; LARGE_INTEGER Wait; LARGE_INTEGER Start; LARGE_INTEGER End; UCHAR dsr; UCHAR dcr; ULONG i; Controller = Extension->Controller; pBuffer = Buffer; Status = ParTestEcpWrite(Extension); if (!NT_SUCCESS(Status)) { Extension->CurrentPhase = PHASE_UNKNOWN; Extension->Connected = FALSE; ParDump2(PARERRORS,("ParEcpSwWrite: Invalid Entry State\r\n")); goto ParEcpSwWrite_ExitLabel; } Wait.QuadPart = DEFAULT_RECEIVE_TIMEOUT * 10 * 1000 + KeQueryTimeIncrement(); Timeout.QuadPart = Extension->AbsoluteOneSecond.QuadPart * Extension->TimerStart; KeQueryTickCount(&StartWrite); dcr = GetControl (Controller); // clear direction bit - enable output dcr &= ~(DCR_DIRECTION); StoreControl(Controller, dcr); KeStallExecutionProcessor(1); for (i = 0; i < BufferSize; i++) { // // Event 34 // Extension->CurrentEvent = 34; WRITE_PORT_UCHAR(Controller + DATA_OFFSET, *pBuffer++); // // Event 35 // Extension->CurrentEvent = 35; dcr &= ~DCR_AUTOFEED; dcr |= DCR_STROBE; StoreControl (Controller, dcr); // // Waiting for Event 36 // Extension->CurrentEvent = 36; while (TRUE) { KeQueryTickCount(&End); dsr = GetStatus(Controller); if (!(dsr & DSR_NOT_BUSY)) { break; } if ((End.QuadPart - StartWrite.QuadPart) * KeQueryTimeIncrement() > Timeout.QuadPart) { dsr = GetStatus(Controller); if (!(dsr & DSR_NOT_BUSY)) { break; } // // Return the device to Idle. // dcr &= ~(DCR_STROBE); StoreControl (Controller, dcr); *BytesTransferred = i; Extension->log.SwEcpWriteCount += *BytesTransferred; return STATUS_DEVICE_BUSY; } } // // Event 37 // Extension->CurrentEvent = 37; dcr &= ~DCR_STROBE; StoreControl (Controller, dcr); // // Waiting for Event 32 // Extension->CurrentEvent = 32; KeQueryTickCount(&Start); while (TRUE) { KeQueryTickCount(&End); dsr = GetStatus(Controller); if (dsr & DSR_NOT_BUSY) { break; } if ((End.QuadPart - Start.QuadPart) * KeQueryTimeIncrement() > Wait.QuadPart) { dsr = GetStatus(Controller); if (dsr & DSR_NOT_BUSY) { break; } #if DVRH_BUS_RESET_ON_ERROR BusReset(Controller+OFFSET_DCR); // Pass in the dcr address #endif *BytesTransferred = i; Extension->log.SwEcpWriteCount += *BytesTransferred; return STATUS_IO_DEVICE_ERROR; } } } ParEcpSwWrite_ExitLabel: *BytesTransferred = i; Extension->log.SwEcpWriteCount += *BytesTransferred; ParDumpReg(PAREXIT | PARECPTRACE, ("ParEcpSwWrite: Exit[%d] BytesTransferred[%lx]", NT_SUCCESS(Status), (long)*BytesTransferred), Controller + ECR_OFFSET, Controller + OFFSET_DCR, Controller + OFFSET_DSR); return Status; } NTSTATUS ParEcpSwRead( IN PDEVICE_EXTENSION Extension, IN PVOID Buffer, IN ULONG BufferSize, OUT PULONG BytesTransferred ) /*++ Routine Description: This routine performs a 1284 ECP mode read under software 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. --*/ { PUCHAR Controller; PUCHAR pBuffer; USHORT usTime; LARGE_INTEGER End; UCHAR dsr; UCHAR dcr; ULONG i; UCHAR ecr; Controller = Extension->Controller; pBuffer = Buffer; dcr = GetControl (Controller); Extension->CurrentPhase = PHASE_REVERSE_XFER; // // Put ECR into PS/2 mode and float the drivers. // if (Extension->HardwareCapabilities & PPT_ECP_PRESENT) { // Save off the ECR register ecr = READ_PORT_UCHAR(Controller + ECR_OFFSET); #if (1 == PARCHIP_ECR_ARBITRATOR) #else WRITE_PORT_UCHAR(Controller + ECR_OFFSET, DEFAULT_ECR_PS2); #endif } dcr |= DCR_DIRECTION; StoreControl (Controller, dcr); KeStallExecutionProcessor(1); for (i = 0; i < BufferSize; i++) { // dvtw - READ TIMEOUTS // // If it is the first byte then give it more time // if (!(GetStatus (Controller) & DSR_NOT_FAULT) || i == 0) { usTime = DEFAULT_RECEIVE_TIMEOUT; } else { usTime = IEEE_MAXTIME_TL; } // *************** State 43 Reverse Phase ***************8 // PeriphAck/PtrBusy = DONT CARE // PeriphClk/PtrClk = LOW ( State 43 ) // nAckReverse/AckDataReq = LOW // XFlag = HIGH // nPeriphReq/nDataAvail = DONT CARE Extension->CurrentEvent = 43; if (!CHECK_DSR(Controller, DONT_CARE, INACTIVE, INACTIVE, ACTIVE, DONT_CARE, usTime)) { Extension->CurrentPhase = PHASE_UNKNOWN; dcr &= ~DCR_DIRECTION; StoreControl (Controller, dcr); // restore ecr register if (Extension->HardwareCapabilities & PPT_ECP_PRESENT) { #if (1 == PARCHIP_ECR_ARBITRATOR) #else WRITE_PORT_UCHAR(Controller + ECR_OFFSET, DEFAULT_ECR_COMPATIBILITY); #endif WRITE_PORT_UCHAR(Controller + ECR_OFFSET, ecr); } *BytesTransferred = i; Extension->log.SwEcpReadCount += *BytesTransferred; return STATUS_IO_DEVICE_ERROR; } // *************** State 44 Setup Phase ***************8 // DIR = DONT CARE // IRQEN = DONT CARE // 1284/SelectIn = DONT CARE // nReverseReq/**(ECP only)= DONT CARE // HostAck/HostBusy = HIGH ( State 44 ) // HostClk/nStrobe = DONT CARE // Extension->CurrentEvent = 44; dcr = READ_PORT_UCHAR(Controller + OFFSET_DCR); dcr = UPDATE_DCR(dcr, DONT_CARE, DONT_CARE, DONT_CARE, DONT_CARE, ACTIVE, DONT_CARE); WRITE_PORT_UCHAR(Controller + OFFSET_DCR, dcr); // *************** State 45 Reverse Phase ***************8 // PeriphAck/PtrBusy = DONT CARE // PeriphClk/PtrClk = HIGH ( State 45 ) // nAckReverse/AckDataReq = LOW // XFlag = HIGH // nPeriphReq/nDataAvail = DONT CARE Extension->CurrentEvent = 45; if (!CHECK_DSR(Controller, DONT_CARE, ACTIVE, INACTIVE, ACTIVE, DONT_CARE, IEEE_MAXTIME_TL)) { Extension->CurrentPhase = PHASE_UNKNOWN; dcr &= ~DCR_DIRECTION; StoreControl (Controller, dcr); // restore ecr register if (Extension->HardwareCapabilities & PPT_ECP_PRESENT) { #if (1 == PARCHIP_ECR_ARBITRATOR) #else WRITE_PORT_UCHAR(Controller + ECR_OFFSET, DEFAULT_ECR_COMPATIBILITY); #endif WRITE_PORT_UCHAR(Controller + ECR_OFFSET, ecr); } *BytesTransferred = i; Extension->log.SwEcpReadCount += *BytesTransferred; return STATUS_IO_DEVICE_ERROR; } // // Read the data // *pBuffer = READ_PORT_UCHAR (Controller + DATA_OFFSET); pBuffer++; // *************** State 46 Setup Phase ***************8 // DIR = DONT CARE // IRQEN = DONT CARE // 1284/SelectIn = DONT CARE // nReverseReq/**(ECP only)= DONT CARE // HostAck/HostBusy = LOW ( State 46 ) // HostClk/nStrobe = DONT CARE // Extension->CurrentEvent = 46; dcr = READ_PORT_UCHAR(Controller + OFFSET_DCR); dcr = UPDATE_DCR(dcr, DONT_CARE, DONT_CARE, DONT_CARE, DONT_CARE, INACTIVE, DONT_CARE); WRITE_PORT_UCHAR(Controller + OFFSET_DCR, dcr); } Extension->CurrentPhase = PHASE_REVERSE_IDLE; dcr &= ~DCR_DIRECTION; StoreControl (Controller, dcr); // restore ecr register if (Extension->HardwareCapabilities & PPT_ECP_PRESENT) { #if (1 == PARCHIP_ECR_ARBITRATOR) #else WRITE_PORT_UCHAR(Controller + ECR_OFFSET, DEFAULT_ECR_COMPATIBILITY); #endif WRITE_PORT_UCHAR(Controller + ECR_OFFSET, ecr); } *BytesTransferred = i; Extension->log.SwEcpReadCount += *BytesTransferred; return STATUS_SUCCESS; } NTSTATUS ParEcpForwardToReverse( IN PDEVICE_EXTENSION Extension ) /*++ Routine Description: This routine reverses the channel (ECP). Arguments: Extension - Supplies the device extension. --*/ { PUCHAR Controller; LARGE_INTEGER Wait35ms; LARGE_INTEGER Start; LARGE_INTEGER End; UCHAR dsr; UCHAR dcr; UCHAR ecr; Controller = Extension->Controller; Wait35ms.QuadPart = 10*35*1000 + KeQueryTimeIncrement(); dcr = GetControl (Controller); // // Put ECR into PS/2 mode to flush the FIFO. // // Save off the ECR register // Note: Don't worry about checking to see if it's // safe to touch the ecr since we've already checked // that before we allowed this mode to be activated. ecr = READ_PORT_UCHAR(Controller + ECR_OFFSET); #if (1 == PARCHIP_ECR_ARBITRATOR) #else WRITE_PORT_UCHAR(Controller + ECR_OFFSET, DEFAULT_ECR_PS2); #endif // // Event 38 // Extension->CurrentEvent = 38; dcr |= DCR_AUTOFEED; StoreControl (Controller, dcr); KeStallExecutionProcessor(1); // // Event 39 // Extension->CurrentEvent = 39; dcr &= ~DCR_NOT_INIT; StoreControl (Controller, dcr); // // Wait for Event 40 // Extension->CurrentEvent = 40; KeQueryTickCount(&Start); while (TRUE) { KeQueryTickCount(&End); dsr = GetStatus(Controller); if (!(dsr & DSR_PERROR)) { break; } if ((End.QuadPart - Start.QuadPart) * KeQueryTimeIncrement() > Wait35ms.QuadPart) { dsr = GetStatus(Controller); if (!(dsr & DSR_PERROR)) { break; } #if DVRH_BUS_RESET_ON_ERROR BusReset(Controller+OFFSET_DCR); // Pass in the dcr address #endif // restore the ecr register if (Extension->HardwareCapabilities & PPT_ECP_PRESENT) { #if (1 == PARCHIP_ECR_ARBITRATOR) #else WRITE_PORT_UCHAR(Controller + ECR_OFFSET, DEFAULT_ECR_COMPATIBILITY); #endif WRITE_PORT_UCHAR(Controller + ECR_OFFSET, ecr); } ParDump2(PARERRORS,("ParEcpForwardToReverse: Failed to get State 40\r\n")); return STATUS_IO_DEVICE_ERROR; } } // restore the ecr register if (Extension->HardwareCapabilities & PPT_ECP_PRESENT) { #if (1 == PARCHIP_ECR_ARBITRATOR) #else WRITE_PORT_UCHAR(Controller + ECR_OFFSET, DEFAULT_ECR_COMPATIBILITY); #endif WRITE_PORT_UCHAR(Controller + ECR_OFFSET, ecr); } Extension->CurrentPhase = PHASE_REVERSE_IDLE; return STATUS_SUCCESS; } NTSTATUS ParEcpReverseToForward( IN PDEVICE_EXTENSION Extension ) /*++ Routine Description: This routine puts the channel back into forward mode (ECP). Arguments: Extension - Supplies the device extension. --*/ { PUCHAR Controller; LARGE_INTEGER Wait35ms; LARGE_INTEGER Start; LARGE_INTEGER End; UCHAR dsr; UCHAR dcr; UCHAR ecr; Controller = Extension->Controller; Wait35ms.QuadPart = 10*35*1000 + KeQueryTimeIncrement(); dcr = GetControl (Controller); // // Put ECR into PS/2 mode to flush the FIFO. // // Save off the ECR register // Note: Don't worry about checking to see if it's // safe to touch the ecr since we've already checked // that before we allowed this mode to be activated. ecr = READ_PORT_UCHAR(Controller + ECR_OFFSET); #if (1 == PARCHIP_ECR_ARBITRATOR) #else WRITE_PORT_UCHAR(Controller + ECR_OFFSET, DEFAULT_ECR_PS2); #endif // // Event 47 // Extension->CurrentEvent = 47; dcr |= DCR_NOT_INIT; StoreControl (Controller, dcr); // // Wait for Event 49 // Extension->CurrentEvent = 49; KeQueryTickCount(&Start); while (TRUE) { KeQueryTickCount(&End); dsr = GetStatus(Controller); if (dsr & DSR_PERROR) { break; } if ((End.QuadPart - Start.QuadPart) * KeQueryTimeIncrement() > Wait35ms.QuadPart) { dsr = GetStatus(Controller); if (dsr & DSR_PERROR) { break; } #if DVRH_BUS_RESET_ON_ERROR BusReset(Controller+OFFSET_DCR); // Pass in the dcr address #endif // Restore the ecr register if (Extension->HardwareCapabilities & PPT_ECP_PRESENT) { #if (1 == PARCHIP_ECR_ARBITRATOR) #else WRITE_PORT_UCHAR(Controller + ECR_OFFSET, DEFAULT_ECR_COMPATIBILITY); #endif WRITE_PORT_UCHAR(Controller + ECR_OFFSET, ecr); } ParDump2(PARERRORS,("ParEcpReverseToForward: Failed to get State 49\r\n")); return STATUS_IO_DEVICE_ERROR; } } // restore the ecr register if (Extension->HardwareCapabilities & PPT_ECP_PRESENT) { #if (1 == PARCHIP_ECR_ARBITRATOR) #else WRITE_PORT_UCHAR(Controller + ECR_OFFSET, DEFAULT_ECR_COMPATIBILITY); #endif WRITE_PORT_UCHAR(Controller + ECR_OFFSET, ecr); } Extension->CurrentPhase = PHASE_FORWARD_IDLE; return STATUS_SUCCESS; }