805 lines
22 KiB
C
805 lines
22 KiB
C
/*++
|
||
|
||
Copyright (C) Microsoft Corporation, 1993 - 1999
|
||
|
||
Module Name:
|
||
|
||
spp.c
|
||
|
||
Abstract:
|
||
|
||
This module contains the code for standard parallel ports
|
||
(centronics mode).
|
||
|
||
Author:
|
||
|
||
Anthony V. Ercolano 1-Aug-1992
|
||
Norbert P. Kusters 22-Oct-1993
|
||
|
||
Environment:
|
||
|
||
Kernel mode
|
||
|
||
Revision History :
|
||
|
||
--*/
|
||
|
||
#include "pch.h"
|
||
|
||
ULONG
|
||
SppWriteLoopPI(
|
||
IN PUCHAR Controller,
|
||
IN PUCHAR WriteBuffer,
|
||
IN ULONG NumBytesToWrite,
|
||
IN ULONG BusyDelay
|
||
);
|
||
|
||
ULONG
|
||
SppCheckBusyDelay(
|
||
IN PPDO_EXTENSION Pdx,
|
||
IN PUCHAR WriteBuffer,
|
||
IN ULONG NumBytesToWrite
|
||
);
|
||
|
||
NTSTATUS
|
||
ParEnterSppMode(
|
||
IN PPDO_EXTENSION Pdx,
|
||
IN BOOLEAN DeviceIdRequest
|
||
)
|
||
{
|
||
UNREFERENCED_PARAMETER( DeviceIdRequest );
|
||
|
||
DD((PCE)Pdx,DDT,"ParEnterSppMode: Enter!\n");
|
||
|
||
P5SetPhase( Pdx, PHASE_FORWARD_IDLE );
|
||
Pdx->Connected = TRUE;
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
ULONG
|
||
SppWriteLoopPI(
|
||
IN PUCHAR Controller,
|
||
IN PUCHAR WriteBuffer,
|
||
IN ULONG NumBytesToWrite,
|
||
IN ULONG BusyDelay
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine outputs the given write buffer to the parallel port
|
||
using the standard centronics protocol.
|
||
|
||
Arguments:
|
||
|
||
Controller - Supplies the base address of the parallel port.
|
||
|
||
WriteBuffer - Supplies the buffer to write to the port.
|
||
|
||
NumBytesToWrite - Supplies the number of bytes to write out to the port.
|
||
|
||
BusyDelay - Supplies the number of microseconds to delay before
|
||
checking the busy bit.
|
||
|
||
Return Value:
|
||
|
||
The number of bytes successfully written out to the parallel port.
|
||
|
||
--*/
|
||
|
||
{
|
||
ULONG i;
|
||
UCHAR DeviceStatus;
|
||
BOOLEAN atPassiveIrql = FALSE;
|
||
|
||
if( KeGetCurrentIrql() == PASSIVE_LEVEL ) {
|
||
atPassiveIrql = TRUE;
|
||
}
|
||
|
||
if (!BusyDelay) {
|
||
BusyDelay = 1;
|
||
}
|
||
|
||
for (i = 0; i < NumBytesToWrite; i++) {
|
||
|
||
DeviceStatus = GetStatus(Controller);
|
||
|
||
if (PAR_ONLINE(DeviceStatus)) {
|
||
|
||
//
|
||
// Anytime we write out a character we will restart
|
||
// the count down timer.
|
||
//
|
||
|
||
P5WritePortUchar(Controller + PARALLEL_DATA_OFFSET, *WriteBuffer++);
|
||
|
||
KeStallExecutionProcessor(1);
|
||
|
||
StoreControl(Controller, (PAR_CONTROL_WR_CONTROL |
|
||
PAR_CONTROL_SLIN |
|
||
PAR_CONTROL_NOT_INIT |
|
||
PAR_CONTROL_STROBE));
|
||
|
||
KeStallExecutionProcessor(1);
|
||
|
||
StoreControl(Controller, (PAR_CONTROL_WR_CONTROL |
|
||
PAR_CONTROL_SLIN |
|
||
PAR_CONTROL_NOT_INIT));
|
||
|
||
KeStallExecutionProcessor(BusyDelay);
|
||
|
||
} else {
|
||
DD(NULL,DDT,"spp::SppWriteLoopPI - DeviceStatus = %x - NOT ONLINE\n", DeviceStatus);
|
||
break;
|
||
}
|
||
}
|
||
|
||
DD(NULL,DDT,"SppWriteLoopPI - exit - bytes written = %ld\n",i);
|
||
|
||
return i;
|
||
}
|
||
|
||
ULONG
|
||
SppCheckBusyDelay(
|
||
IN PPDO_EXTENSION Pdx,
|
||
IN PUCHAR WriteBuffer,
|
||
IN ULONG NumBytesToWrite
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine determines if the current busy delay setting is
|
||
adequate for this printer.
|
||
|
||
Arguments:
|
||
|
||
Pdx - Supplies the device extension.
|
||
|
||
WriteBuffer - Supplies the write buffer.
|
||
|
||
NumBytesToWrite - Supplies the size of the write buffer.
|
||
|
||
Return Value:
|
||
|
||
The number of bytes strobed out to the printer.
|
||
|
||
--*/
|
||
|
||
{
|
||
PUCHAR Controller;
|
||
ULONG BusyDelay;
|
||
LARGE_INTEGER Start;
|
||
LARGE_INTEGER PerfFreq;
|
||
LARGE_INTEGER End;
|
||
LARGE_INTEGER GetStatusTime;
|
||
LARGE_INTEGER CallOverhead;
|
||
UCHAR DeviceStatus;
|
||
ULONG i;
|
||
ULONG NumberOfCalls;
|
||
ULONG maxTries;
|
||
KIRQL OldIrql = PASSIVE_LEVEL;
|
||
|
||
UNREFERENCED_PARAMETER( NumBytesToWrite );
|
||
|
||
Controller = Pdx->Controller;
|
||
BusyDelay = Pdx->BusyDelay;
|
||
|
||
// If the current busy delay value is 10 or greater then something
|
||
// is weird and settle for 10.
|
||
|
||
if (Pdx->BusyDelay >= 10) {
|
||
Pdx->BusyDelayDetermined = TRUE;
|
||
return 0;
|
||
}
|
||
|
||
// Take some performance measurements.
|
||
|
||
if (0 == SppNoRaiseIrql)
|
||
KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
|
||
|
||
Start = KeQueryPerformanceCounter(&PerfFreq);
|
||
|
||
DeviceStatus = GetStatus(Controller);
|
||
|
||
End = KeQueryPerformanceCounter(&PerfFreq);
|
||
|
||
GetStatusTime.QuadPart = End.QuadPart - Start.QuadPart;
|
||
|
||
Start = KeQueryPerformanceCounter(&PerfFreq);
|
||
End = KeQueryPerformanceCounter(&PerfFreq);
|
||
|
||
if (0 == SppNoRaiseIrql)
|
||
KeLowerIrql(OldIrql);
|
||
|
||
CallOverhead.QuadPart = End.QuadPart - Start.QuadPart;
|
||
|
||
GetStatusTime.QuadPart -= CallOverhead.QuadPart;
|
||
|
||
if (GetStatusTime.QuadPart <= 0) {
|
||
GetStatusTime.QuadPart = 1;
|
||
}
|
||
|
||
// Figure out how many calls to 'GetStatus' can be made in 20 us.
|
||
|
||
NumberOfCalls = (ULONG) (PerfFreq.QuadPart*20/GetStatusTime.QuadPart/1000000) + 1;
|
||
|
||
//
|
||
// - check to make sure the device is ready to receive a byte before we start clocking
|
||
// data out
|
||
//
|
||
// DVDF - 25Jan99 - added check
|
||
//
|
||
|
||
//
|
||
// - nothing magic about 25 - just catch the case where NumberOfCalls may be bogus
|
||
// and try something reasonable - empirically NumberOfCalls has ranged from 8-24
|
||
//
|
||
maxTries = (NumberOfCalls > 25) ? 25 : NumberOfCalls;
|
||
|
||
for( i = 0 ; i < maxTries ; i++ ) {
|
||
// spin for slow device to get ready to receive data - roughly 20us max
|
||
DeviceStatus = GetStatus( Controller );
|
||
if( PAR_ONLINE( DeviceStatus ) ) {
|
||
// break out of loop as soon as device is ready
|
||
break;
|
||
}
|
||
}
|
||
if( !PAR_ONLINE( DeviceStatus ) ) {
|
||
// device is still not online - bail out
|
||
return 0;
|
||
}
|
||
|
||
// The printer is ready to accept a byte. Strobe one out
|
||
// and check out the reaction time for BUSY.
|
||
|
||
if (BusyDelay) {
|
||
|
||
if (0 == SppNoRaiseIrql)
|
||
KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
|
||
|
||
P5WritePortUchar(Controller + PARALLEL_DATA_OFFSET, *WriteBuffer++);
|
||
KeStallExecutionProcessor(1);
|
||
StoreControl(Controller, (PAR_CONTROL_WR_CONTROL |
|
||
PAR_CONTROL_SLIN |
|
||
PAR_CONTROL_NOT_INIT |
|
||
PAR_CONTROL_STROBE));
|
||
KeStallExecutionProcessor(1);
|
||
StoreControl(Controller, (PAR_CONTROL_WR_CONTROL |
|
||
PAR_CONTROL_SLIN |
|
||
PAR_CONTROL_NOT_INIT));
|
||
KeStallExecutionProcessor(BusyDelay);
|
||
|
||
for (i = 0; i < NumberOfCalls; i++) {
|
||
DeviceStatus = GetStatus(Controller);
|
||
if (!(DeviceStatus & PAR_STATUS_NOT_BUSY)) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (0 == SppNoRaiseIrql)
|
||
KeLowerIrql(OldIrql);
|
||
|
||
} else {
|
||
|
||
if (0 == SppNoRaiseIrql)
|
||
KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
|
||
|
||
P5WritePortUchar(Controller + PARALLEL_DATA_OFFSET, *WriteBuffer++);
|
||
KeStallExecutionProcessor(1);
|
||
StoreControl(Controller, (PAR_CONTROL_WR_CONTROL |
|
||
PAR_CONTROL_SLIN |
|
||
PAR_CONTROL_NOT_INIT |
|
||
PAR_CONTROL_STROBE));
|
||
KeStallExecutionProcessor(1);
|
||
StoreControl(Controller, (PAR_CONTROL_WR_CONTROL |
|
||
PAR_CONTROL_SLIN |
|
||
PAR_CONTROL_NOT_INIT));
|
||
|
||
for (i = 0; i < NumberOfCalls; i++) {
|
||
DeviceStatus = GetStatus(Controller);
|
||
if (!(DeviceStatus & PAR_STATUS_NOT_BUSY)) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (0 == SppNoRaiseIrql)
|
||
KeLowerIrql(OldIrql);
|
||
}
|
||
|
||
if (i == 0) {
|
||
|
||
// In this case the BUSY was set as soon as we checked it.
|
||
// Use this busyDelay with the PI code.
|
||
|
||
Pdx->UsePIWriteLoop = TRUE;
|
||
Pdx->BusyDelayDetermined = TRUE;
|
||
|
||
} else if (i == NumberOfCalls) {
|
||
|
||
// In this case the BUSY was never seen. This is a very fast
|
||
// printer so use the fastest code possible.
|
||
|
||
Pdx->BusyDelayDetermined = TRUE;
|
||
|
||
} else {
|
||
|
||
// The test failed. The lines showed not BUSY and then BUSY
|
||
// without strobing a byte in between.
|
||
|
||
Pdx->UsePIWriteLoop = TRUE;
|
||
Pdx->BusyDelay++;
|
||
}
|
||
|
||
return 1;
|
||
}
|
||
|
||
NTSTATUS
|
||
SppWrite(
|
||
IN PPDO_EXTENSION Pdx,
|
||
IN PVOID Buffer,
|
||
IN ULONG BytesToWrite,
|
||
OUT PULONG BytesTransferred
|
||
)
|
||
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Arguments:
|
||
|
||
Pdx - Supplies the device extension.
|
||
|
||
Return Value:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status;
|
||
UCHAR DeviceStatus;
|
||
ULONG TimerStart;
|
||
LONG CountDown;
|
||
PUCHAR IrpBuffer;
|
||
LARGE_INTEGER StartOfSpin;
|
||
LARGE_INTEGER NextQuery;
|
||
LARGE_INTEGER Difference;
|
||
BOOLEAN DoDelays;
|
||
BOOLEAN PortFree;
|
||
ULONG NumBytesWritten;
|
||
ULONG LoopNumber;
|
||
ULONG NumberOfBusyChecks;
|
||
ULONG MaxBusyDelay;
|
||
ULONG MaxBytes;
|
||
|
||
DD((PCE)Pdx,DDT,"SppWrite - enter, BytesToWrite = %d\n",BytesToWrite);
|
||
|
||
*BytesTransferred = 0; // initialize to none
|
||
|
||
IrpBuffer = (PUCHAR)Buffer;
|
||
MaxBytes = BytesToWrite;
|
||
TimerStart = Pdx->TimerStart;
|
||
CountDown = (LONG)TimerStart;
|
||
|
||
NumberOfBusyChecks = 9;
|
||
MaxBusyDelay = 0;
|
||
|
||
// Turn off the strobe in case it was left on by some other device sharing the port.
|
||
StoreControl(Pdx->Controller, (PAR_CONTROL_WR_CONTROL |
|
||
PAR_CONTROL_SLIN |
|
||
PAR_CONTROL_NOT_INIT));
|
||
|
||
PushSomeBytes:
|
||
|
||
//
|
||
// While we are strobing data we don't want to get context
|
||
// switched away. Raise up to dispatch level to prevent that.
|
||
//
|
||
// The reason we can't afford the context switch is that
|
||
// the device can't have the data strobe line on for more
|
||
// than 500 microseconds.
|
||
//
|
||
// We never want to be at raised irql form more than
|
||
// 200 microseconds, so we will do no more than 100
|
||
// bytes at a time.
|
||
//
|
||
|
||
LoopNumber = 512;
|
||
if (LoopNumber > BytesToWrite) {
|
||
LoopNumber = BytesToWrite;
|
||
}
|
||
|
||
//
|
||
// Enter the write loop
|
||
//
|
||
|
||
if (!Pdx->BusyDelayDetermined) {
|
||
DD((PCE)Pdx,DDT,"SppWrite: Calling SppCheckBusyDelay\n");
|
||
NumBytesWritten = SppCheckBusyDelay(Pdx, IrpBuffer, LoopNumber);
|
||
|
||
if (Pdx->BusyDelayDetermined) {
|
||
|
||
if (Pdx->BusyDelay > MaxBusyDelay) {
|
||
MaxBusyDelay = Pdx->BusyDelay;
|
||
NumberOfBusyChecks = 10;
|
||
}
|
||
|
||
if (NumberOfBusyChecks) {
|
||
NumberOfBusyChecks--;
|
||
Pdx->BusyDelayDetermined = FALSE;
|
||
|
||
} else {
|
||
|
||
Pdx->BusyDelay = MaxBusyDelay + 1;
|
||
}
|
||
}
|
||
|
||
} else if( Pdx->UsePIWriteLoop ) {
|
||
NumBytesWritten = SppWriteLoopPI( Pdx->Controller, IrpBuffer, LoopNumber, Pdx->BusyDelay );
|
||
} else {
|
||
NumBytesWritten = SppWriteLoopPI( Pdx->Controller, IrpBuffer, LoopNumber, 1 );
|
||
}
|
||
|
||
|
||
if (NumBytesWritten) {
|
||
|
||
CountDown = TimerStart;
|
||
IrpBuffer += NumBytesWritten;
|
||
BytesToWrite -= NumBytesWritten;
|
||
|
||
}
|
||
|
||
//
|
||
// Check to see if the io is done. If it is then call the
|
||
// code to complete the request.
|
||
//
|
||
|
||
if (!BytesToWrite) {
|
||
|
||
*BytesTransferred = MaxBytes;
|
||
|
||
status = STATUS_SUCCESS;
|
||
goto returnTarget;
|
||
|
||
} else if ((Pdx->CurrentOpIrp)->Cancel) {
|
||
|
||
//
|
||
// See if the IO has been canceled. The cancel routine
|
||
// has been removed already (when this became the
|
||
// current irp). Simply check the bit. We don't even
|
||
// need to capture the lock. If we miss a round
|
||
// it won't be that bad.
|
||
//
|
||
|
||
*BytesTransferred = MaxBytes - BytesToWrite;
|
||
|
||
status = STATUS_CANCELLED;
|
||
goto returnTarget;
|
||
|
||
} else {
|
||
|
||
//
|
||
// We've taken care of the reasons that the irp "itself"
|
||
// might want to be completed.
|
||
// printer to see if it is in a state that might
|
||
// cause us to complete the irp.
|
||
//
|
||
// First let's check if the device status is
|
||
// ok and online. If it is then simply go back
|
||
// to the byte pusher.
|
||
//
|
||
|
||
|
||
DeviceStatus = GetStatus(Pdx->Controller);
|
||
|
||
if (PAR_ONLINE(DeviceStatus)) {
|
||
goto PushSomeBytes;
|
||
}
|
||
|
||
//
|
||
// Perhaps the operator took the device off line,
|
||
// or forgot to put in enough paper. If so, then
|
||
// let's hang out here for the until the timeout
|
||
// period has expired waiting for them to make things
|
||
// all better.
|
||
//
|
||
|
||
if (PAR_PAPER_EMPTY(DeviceStatus) ||
|
||
PAR_OFF_LINE(DeviceStatus)) {
|
||
|
||
if (CountDown > 0) {
|
||
|
||
//
|
||
// We'll wait 1 second increments.
|
||
//
|
||
|
||
DD((PCE)Pdx,DDT,"decrementing countdown for PAPER_EMPTY/OFF_LINE - countDown: %d status: 0x%x\n", CountDown, DeviceStatus);
|
||
|
||
CountDown--;
|
||
|
||
// If anyone is waiting for the port then let them have it,
|
||
// since the printer is busy.
|
||
|
||
ParFreePort(Pdx);
|
||
|
||
KeDelayExecutionThread(
|
||
KernelMode,
|
||
FALSE,
|
||
&Pdx->OneSecond
|
||
);
|
||
|
||
if (!ParAllocPort(Pdx)) {
|
||
|
||
*BytesTransferred = MaxBytes - BytesToWrite;
|
||
|
||
DD((PCE)Pdx,DDT,"In SppWrite(...): returning STATUS_DEVICE_BUSY\n");
|
||
|
||
status = STATUS_DEVICE_BUSY;
|
||
goto returnTarget;
|
||
}
|
||
|
||
goto PushSomeBytes;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Timer has expired. Complete the request.
|
||
//
|
||
|
||
*BytesTransferred = MaxBytes - BytesToWrite;
|
||
|
||
DD((PCE)Pdx,DDT,"In SppWrite(...): Timer expired - DeviceStatus = %08x\n", DeviceStatus);
|
||
|
||
if (PAR_OFF_LINE(DeviceStatus)) {
|
||
|
||
DD((PCE)Pdx,DDT,"In SppWrite(...): returning STATUS_DEVICE_OFF_LINE\n");
|
||
|
||
status = STATUS_DEVICE_OFF_LINE;
|
||
goto returnTarget;
|
||
|
||
} else if (PAR_NO_CABLE(DeviceStatus)) {
|
||
|
||
DD((PCE)Pdx,DDT,"In SppWrite(...): returning STATUS_DEVICE_NOT_CONNECTED\n");
|
||
|
||
status = STATUS_DEVICE_NOT_CONNECTED;
|
||
goto returnTarget;
|
||
|
||
} else {
|
||
|
||
DD((PCE)Pdx,DDT,"In SppWrite(...): returning STATUS_DEVICE_PAPER_EMPTY\n");
|
||
|
||
status = STATUS_DEVICE_PAPER_EMPTY;
|
||
goto returnTarget;
|
||
|
||
}
|
||
}
|
||
|
||
|
||
} else if (PAR_POWERED_OFF(DeviceStatus) ||
|
||
PAR_NOT_CONNECTED(DeviceStatus) ||
|
||
PAR_NO_CABLE(DeviceStatus)) {
|
||
|
||
//
|
||
// We are in a "bad" state. Is what
|
||
// happened to the printer (power off, not connected, or
|
||
// the cable being pulled) something that will require us
|
||
// to reinitialize the printer? If we need to
|
||
// reinitialize the printer then we should complete
|
||
// this IO so that the driving application can
|
||
// choose what is the best thing to do about it's
|
||
// io.
|
||
//
|
||
|
||
DD((PCE)Pdx,DDT,"In SppWrite(...): \"bad\" state - need to reinitialize printer?");
|
||
|
||
*BytesTransferred = MaxBytes - BytesToWrite;
|
||
|
||
if (PAR_POWERED_OFF(DeviceStatus)) {
|
||
|
||
DD((PCE)Pdx,DDT,"SppWrite: returning STATUS_DEVICE_POWERED_OFF\n");
|
||
|
||
status = STATUS_DEVICE_POWERED_OFF;
|
||
goto returnTarget;
|
||
|
||
} else if (PAR_NOT_CONNECTED(DeviceStatus) ||
|
||
PAR_NO_CABLE(DeviceStatus)) {
|
||
|
||
DD((PCE)Pdx,DDT,"SppWrite: STATUS_DEVICE_NOT_CONNECTED\n");
|
||
|
||
status = STATUS_DEVICE_NOT_CONNECTED;
|
||
goto returnTarget;
|
||
|
||
}
|
||
}
|
||
|
||
//
|
||
// The device could simply be busy at this point. Simply spin
|
||
// here waiting for the device to be in a state that we
|
||
// care about.
|
||
//
|
||
// As we spin, get the system ticks. Every time that it looks
|
||
// like a second has passed, decrement the countdown. If
|
||
// it ever goes to zero, then timeout the request.
|
||
//
|
||
|
||
KeQueryTickCount(&StartOfSpin);
|
||
DoDelays = FALSE;
|
||
|
||
do {
|
||
|
||
//
|
||
// After about a second of spinning, let the rest of the
|
||
// machine have time for a second.
|
||
//
|
||
|
||
if (DoDelays) {
|
||
|
||
ParFreePort(Pdx);
|
||
PortFree = TRUE;
|
||
|
||
DD((PCE)Pdx,DDT,"Before delay thread of one second, dsr=%x DCR[%x]\n",
|
||
P5ReadPortUchar(Pdx->Controller + OFFSET_DSR),
|
||
P5ReadPortUchar(Pdx->Controller + OFFSET_DCR));
|
||
KeDelayExecutionThread(KernelMode, FALSE, &Pdx->OneSecond);
|
||
|
||
DD((PCE)Pdx,DDT,"Did delay thread of one second, CountDown=%d\n", CountDown);
|
||
|
||
CountDown--;
|
||
|
||
} else {
|
||
|
||
if (Pdx->QueryNumWaiters(Pdx->PortContext)) {
|
||
|
||
ParFreePort(Pdx);
|
||
PortFree = TRUE;
|
||
|
||
} else {
|
||
|
||
PortFree = FALSE;
|
||
}
|
||
|
||
KeQueryTickCount(&NextQuery);
|
||
|
||
Difference.QuadPart = NextQuery.QuadPart - StartOfSpin.QuadPart;
|
||
|
||
if (Difference.QuadPart*KeQueryTimeIncrement() >=
|
||
Pdx->AbsoluteOneSecond.QuadPart) {
|
||
|
||
DD((PCE)Pdx,DDT,"Countdown: %d - device Status: %x lowpart: %x highpart: %x\n",
|
||
CountDown, DeviceStatus, Difference.LowPart, Difference.HighPart);
|
||
|
||
CountDown--;
|
||
DoDelays = TRUE;
|
||
|
||
}
|
||
}
|
||
|
||
if (CountDown <= 0) {
|
||
|
||
*BytesTransferred = MaxBytes - BytesToWrite;
|
||
status = STATUS_DEVICE_BUSY;
|
||
goto returnTarget;
|
||
|
||
}
|
||
|
||
if (PortFree && !ParAllocPort(Pdx)) {
|
||
|
||
*BytesTransferred = MaxBytes - BytesToWrite;
|
||
status = STATUS_DEVICE_BUSY;
|
||
goto returnTarget;
|
||
}
|
||
|
||
DeviceStatus = GetStatus(Pdx->Controller);
|
||
|
||
} while ((!PAR_ONLINE(DeviceStatus)) &&
|
||
(!PAR_PAPER_EMPTY(DeviceStatus)) &&
|
||
(!PAR_POWERED_OFF(DeviceStatus)) &&
|
||
(!PAR_NOT_CONNECTED(DeviceStatus)) &&
|
||
(!PAR_NO_CABLE(DeviceStatus)) &&
|
||
!(Pdx->CurrentOpIrp)->Cancel);
|
||
|
||
if (CountDown != (LONG)TimerStart) {
|
||
|
||
DD((PCE)Pdx,DDT,"Leaving busy loop - countdown %d status %x\n", CountDown, DeviceStatus);
|
||
|
||
}
|
||
|
||
goto PushSomeBytes;
|
||
|
||
}
|
||
|
||
returnTarget:
|
||
// added single return point so we can save log of bytes transferred
|
||
Pdx->log.SppWriteCount += *BytesTransferred;
|
||
|
||
DD((PCE)Pdx,DDT,"SppWrite - exit, BytesTransferred = %d\n",*BytesTransferred);
|
||
|
||
return status;
|
||
|
||
}
|
||
|
||
NTSTATUS
|
||
SppQueryDeviceId(
|
||
IN PPDO_EXTENSION Pdx,
|
||
OUT PCHAR DeviceIdBuffer,
|
||
IN ULONG BufferSize,
|
||
OUT PULONG DeviceIdSize,
|
||
IN BOOLEAN bReturnRawString
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine is now a wrapper function around Par3QueryDeviceId that
|
||
preserves the interface of the original SppQueryDeviceId function.
|
||
|
||
Clients of this function should consider switching to Par3QueryDeviceId
|
||
if possible because Par3QueryDeviceId will allocate and return a pointer
|
||
to a buffer if the caller supplied buffer is too small to hold the
|
||
device ID.
|
||
|
||
Arguments:
|
||
|
||
Pdx - DeviceExtension/Legacy - used to get controller.
|
||
DeviceIdBuffer - Buffer used to return ID.
|
||
BufferSize - Size of supplied buffer.
|
||
DeviceIdSize - Size of returned ID.
|
||
bReturnRawString - Should the 2 byte size prefix be included? (TRUE==Yes)
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - ID query was successful
|
||
STATUS_BUFFER_TOO_SMALL - We were able to read an ID from the device but the caller
|
||
supplied buffer was not large enough to hold the ID. The
|
||
size required to hold the ID is returned in DeviceIdSize.
|
||
STATUS_UNSUCCESSFUL - ID query failed - Possibly interface or device is hung, missed
|
||
timeouts during the handshake, or device may not be connected.
|
||
|
||
--*/
|
||
{
|
||
PCHAR idBuffer;
|
||
|
||
DD((PCE)Pdx,DDT,"spp::SppQueryDeviceId: Enter - buffersize=%d\n", BufferSize);
|
||
if ( Pdx->Ieee1284Flags & ( 1 << Pdx->Ieee1284_3DeviceId ) ) {
|
||
idBuffer = Par3QueryDeviceId( Pdx, DeviceIdBuffer, BufferSize, DeviceIdSize, bReturnRawString, TRUE );
|
||
}
|
||
else {
|
||
idBuffer = Par3QueryDeviceId( Pdx, DeviceIdBuffer, BufferSize, DeviceIdSize, bReturnRawString, FALSE );
|
||
}
|
||
|
||
if( idBuffer == NULL ) {
|
||
//
|
||
// Error at lower level - FAIL query
|
||
//
|
||
DD((PCE)Pdx,DDT,"spp::SppQueryDeviceId: call to Par3QueryDeviceId hard FAIL\n");
|
||
return STATUS_UNSUCCESSFUL;
|
||
} else if( idBuffer != DeviceIdBuffer ) {
|
||
//
|
||
// We got a deviceId from the device, but caller's buffer was too small to hold it.
|
||
// Free the buffer and tell the caller that the supplied buffer was too small.
|
||
//
|
||
DD((PCE)Pdx,DDT,"spp::SppQueryDeviceId: buffer too small - have buffer size=%d, need buffer size=%d\n", BufferSize, *DeviceIdSize);
|
||
ExFreePool( idBuffer );
|
||
return STATUS_BUFFER_TOO_SMALL;
|
||
} else {
|
||
//
|
||
// Query succeeded using caller's buffer (idBuffer == DeviceIdBuffer)
|
||
//
|
||
DD((PCE)Pdx,DDT,"spp::SppQueryDeviceId: SUCCESS - deviceId=<%s>\n", idBuffer);
|
||
return STATUS_SUCCESS;
|
||
}
|
||
}
|
||
|
||
VOID
|
||
ParTerminateSppMode(
|
||
IN PPDO_EXTENSION Pdx
|
||
)
|
||
{
|
||
DD((PCE)Pdx,DDT,"ParTerminateSppMode\n");
|
||
Pdx->Connected = FALSE;
|
||
P5SetPhase( Pdx, PHASE_TERMINATE );
|
||
return;
|
||
}
|