📄 spp.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.
//
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -