4118 lines
104 KiB
C
4118 lines
104 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1993 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
parclass.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module contains the code for the parallel class driver.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Anthony V. Ercolano 1-Aug-1992
|
|||
|
Norbert P. Kusters 22-Oct-1993
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
Kernel mode
|
|||
|
|
|||
|
Revision History :
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "ntddk.h"
|
|||
|
#include "ntddser.h"
|
|||
|
#include "parallel.h"
|
|||
|
#include "parclass.h"
|
|||
|
#include "parlog.h"
|
|||
|
|
|||
|
#ifdef POOL_TAGGING
|
|||
|
#ifdef ExAllocatePool
|
|||
|
#undef ExAllocatePool
|
|||
|
#endif
|
|||
|
#define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,'CraP')
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// This is the actual definition of ParDebugLevel.
|
|||
|
// Note that it is only defined if this is a "debug"
|
|||
|
// build.
|
|||
|
//
|
|||
|
#if DBG
|
|||
|
extern ULONG ParDebugLevel = 0;
|
|||
|
#endif
|
|||
|
|
|||
|
static const PHYSICAL_ADDRESS PhysicalZero = {0};
|
|||
|
|
|||
|
//
|
|||
|
// Give a timeout of 300 seconds. Some postscript printers will
|
|||
|
// buffer up a lot of commands then proceed to render what they
|
|||
|
// have. The printer will then refuse to accept any characters
|
|||
|
// until it's done with the rendering. This render process can
|
|||
|
// take a while. We'll give it 300 seconds.
|
|||
|
//
|
|||
|
// Note that an application can change this value.
|
|||
|
//
|
|||
|
#define PAR_WRITE_TIMEOUT_VALUE 300
|
|||
|
|
|||
|
//
|
|||
|
// Busy, PE
|
|||
|
//
|
|||
|
|
|||
|
#define PAR_PAPER_EMPTY( Status ) ( \
|
|||
|
(Status & PAR_STATUS_PE) )
|
|||
|
|
|||
|
#ifdef JAPAN // IBM-J printers
|
|||
|
|
|||
|
//
|
|||
|
// Support for IBM-J printers.
|
|||
|
//
|
|||
|
// When the printer operates in Japanese (PS55) mode, it redefines
|
|||
|
// the meaning of parallel lines so that extended error status can
|
|||
|
// be reported. It is roughly compatible with PC/AT, but we have to
|
|||
|
// take care of a few cases where the status looks like PC/AT error
|
|||
|
// condition.
|
|||
|
//
|
|||
|
// Status Busy /AutoFdXT Paper Empty Select /Fault
|
|||
|
// ------ ---- --------- ----------- ------ ------
|
|||
|
// Not RMR 1 1 1 1 1
|
|||
|
// Head Alarm 1 1 1 1 0
|
|||
|
// ASF Jam 1 1 1 0 0
|
|||
|
// Paper Empty 1 0 1 0 0
|
|||
|
// No Error 0 0 0 1 1
|
|||
|
// Can Req 1 0 0 0 1
|
|||
|
// Deselect 1 0 0 0 0
|
|||
|
//
|
|||
|
// The printer keeps "Not RMR" during the parallel port
|
|||
|
// initialization, then it takes "Paper Empty", "No Error"
|
|||
|
// or "Deselect". Other status can be thought as an
|
|||
|
// H/W error condition.
|
|||
|
//
|
|||
|
// Namely, "Not RMR" conflicts with PAR_NO_CABLE and "Deselect"
|
|||
|
// should also be regarded as another PAR_OFF_LINE. When the
|
|||
|
// status is PAR_PAPER_EMPTY, the initialization is finished
|
|||
|
// (we should not send init purlse again.)
|
|||
|
//
|
|||
|
// See ParInitializeDevice() for more information.
|
|||
|
//
|
|||
|
// [takashim]
|
|||
|
|
|||
|
#define PAR_OFF_LINE_COMMON( Status ) ( \
|
|||
|
(Status & PAR_STATUS_NOT_ERROR) && \
|
|||
|
((Status & PAR_STATUS_NOT_BUSY) ^ PAR_STATUS_NOT_BUSY) && \
|
|||
|
!(Status & PAR_STATUS_SLCT) )
|
|||
|
|
|||
|
#define PAR_OFF_LINE_IBM55( Status ) ( \
|
|||
|
((Status & PAR_STATUS_NOT_BUSY) ^ PAR_STATUS_NOT_BUSY) && \
|
|||
|
((Status & PAR_STATUS_PE) ^ PAR_STATUS_PE) && \
|
|||
|
((Status & PAR_STATUS_SLCT) ^ PAR_STATUS_SLCT) && \
|
|||
|
((Status & PAR_STATUS_NOT_ERROR) ^ PAR_STATUS_NOT_ERROR))
|
|||
|
|
|||
|
#define PAR_PAPER_EMPTY2( Status ) ( \
|
|||
|
((Status & PAR_STATUS_NOT_BUSY) ^ PAR_STATUS_NOT_BUSY) && \
|
|||
|
(Status & PAR_STATUS_PE) && \
|
|||
|
((Status & PAR_STATUS_SLCT) ^ PAR_STATUS_SLCT) && \
|
|||
|
((Status & PAR_STATUS_NOT_ERROR) ^ PAR_STATUS_NOT_ERROR))
|
|||
|
|
|||
|
//
|
|||
|
// Redefine this for Japan.
|
|||
|
//
|
|||
|
|
|||
|
#define PAR_OFF_LINE( Status ) ( \
|
|||
|
PAR_OFF_LINE_COMMON( Status ) || \
|
|||
|
PAR_OFF_LINE_IBM55( Status ))
|
|||
|
|
|||
|
#else // JAPAN
|
|||
|
//
|
|||
|
// Busy, not select, not error
|
|||
|
//
|
|||
|
|
|||
|
#define PAR_OFF_LINE( Status ) ( \
|
|||
|
(Status & PAR_STATUS_NOT_ERROR) && \
|
|||
|
((Status & PAR_STATUS_NOT_BUSY) ^ PAR_STATUS_NOT_BUSY) && \
|
|||
|
!(Status & PAR_STATUS_SLCT) )
|
|||
|
|
|||
|
#endif // JAPAN
|
|||
|
|
|||
|
//
|
|||
|
// error, ack, not busy
|
|||
|
//
|
|||
|
|
|||
|
#define PAR_POWERED_OFF( Status ) ( \
|
|||
|
((Status & PAR_STATUS_NOT_ERROR) ^ PAR_STATUS_NOT_ERROR) && \
|
|||
|
((Status & PAR_STATUS_NOT_ACK) ^ PAR_STATUS_NOT_ACK) && \
|
|||
|
(Status & PAR_STATUS_NOT_BUSY))
|
|||
|
|
|||
|
//
|
|||
|
// not error, not busy, not select
|
|||
|
//
|
|||
|
|
|||
|
#define PAR_NOT_CONNECTED( Status ) ( \
|
|||
|
(Status & PAR_STATUS_NOT_ERROR) && \
|
|||
|
(Status & PAR_STATUS_NOT_BUSY) &&\
|
|||
|
!(Status & PAR_STATUS_SLCT) )
|
|||
|
|
|||
|
//
|
|||
|
// not error, not busy
|
|||
|
//
|
|||
|
|
|||
|
#define PAR_OK(Status) ( \
|
|||
|
(Status & PAR_STATUS_NOT_ERROR) && \
|
|||
|
((Status & PAR_STATUS_PE) ^ PAR_STATUS_PE) && \
|
|||
|
(Status & PAR_STATUS_NOT_BUSY) )
|
|||
|
|
|||
|
//
|
|||
|
// busy, select, not error
|
|||
|
//
|
|||
|
|
|||
|
#define PAR_POWERED_ON(Status) ( \
|
|||
|
((Status & PAR_STATUS_NOT_BUSY) ^ PAR_STATUS_NOT_BUSY) && \
|
|||
|
(Status & PAR_STATUS_SLCT) && \
|
|||
|
(Status & PAR_STATUS_NOT_ERROR))
|
|||
|
|
|||
|
//
|
|||
|
// busy, not error
|
|||
|
//
|
|||
|
|
|||
|
#define PAR_BUSY(Status) (\
|
|||
|
(( Status & PAR_STATUS_NOT_BUSY) ^ PAR_STATUS_NOT_BUSY) && \
|
|||
|
( Status & PAR_STATUS_NOT_ERROR ) )
|
|||
|
|
|||
|
//
|
|||
|
// selected
|
|||
|
//
|
|||
|
|
|||
|
#define PAR_SELECTED(Status) ( \
|
|||
|
( Status & PAR_STATUS_SLCT ) )
|
|||
|
|
|||
|
//
|
|||
|
// No cable attached.
|
|||
|
//
|
|||
|
#define PAR_NO_CABLE(Status) ( \
|
|||
|
((Status & PAR_STATUS_NOT_BUSY) ^ PAR_STATUS_NOT_BUSY) && \
|
|||
|
(Status & PAR_STATUS_NOT_ACK) && \
|
|||
|
(Status & PAR_STATUS_PE) && \
|
|||
|
(Status & PAR_STATUS_SLCT) && \
|
|||
|
(Status & PAR_STATUS_NOT_ERROR))
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
DriverEntry(
|
|||
|
IN PDRIVER_OBJECT DriverObject,
|
|||
|
IN PUNICODE_STRING RegistryPath
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
ParLogError(
|
|||
|
IN PDRIVER_OBJECT DriverObject,
|
|||
|
IN PDEVICE_OBJECT DeviceObject OPTIONAL,
|
|||
|
IN PHYSICAL_ADDRESS P1,
|
|||
|
IN PHYSICAL_ADDRESS P2,
|
|||
|
IN ULONG SequenceNumber,
|
|||
|
IN UCHAR MajorFunctionCode,
|
|||
|
IN UCHAR RetryCount,
|
|||
|
IN ULONG UniqueErrorValue,
|
|||
|
IN NTSTATUS FinalStatus,
|
|||
|
IN NTSTATUS SpecificIOStatus
|
|||
|
);
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
ParMakeNames(
|
|||
|
IN ULONG ParallelPortNumber,
|
|||
|
OUT PUNICODE_STRING PortName,
|
|||
|
OUT PUNICODE_STRING ClassName,
|
|||
|
OUT PUNICODE_STRING LinkName
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
ParInitializeClassDevice(
|
|||
|
IN PDRIVER_OBJECT DriverObject,
|
|||
|
IN PUNICODE_STRING RegistryPath,
|
|||
|
IN ULONG ParallelPortNumber
|
|||
|
);
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ParGetPortInfoFromPortDevice(
|
|||
|
IN OUT PDEVICE_EXTENSION Extension
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
ParDeferDeviceInitialization(
|
|||
|
IN OUT PDEVICE_EXTENSION Extension
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
ParDeferredInitCallback(
|
|||
|
IN OUT PVOID Context
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
ParCheckParameters(
|
|||
|
IN PUNICODE_STRING RegistryPath,
|
|||
|
IN OUT PDEVICE_EXTENSION Extension
|
|||
|
);
|
|||
|
|
|||
|
UCHAR
|
|||
|
ParReinitializeDevice(
|
|||
|
IN PDEVICE_EXTENSION Extension
|
|||
|
);
|
|||
|
|
|||
|
UCHAR
|
|||
|
ParInitializeDevice(
|
|||
|
IN PDEVICE_EXTENSION Extension
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
ParNotInitError(
|
|||
|
IN PDEVICE_EXTENSION Extension,
|
|||
|
IN UCHAR deviceStatus
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
ParFreePort(
|
|||
|
IN PDEVICE_EXTENSION Extension
|
|||
|
);
|
|||
|
|
|||
|
ULONG
|
|||
|
ParCheckBusyDelay(
|
|||
|
IN PDEVICE_EXTENSION Extension,
|
|||
|
IN PUCHAR WriteBuffer,
|
|||
|
IN ULONG NumBytesToWrite
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
ParWriteOutData(
|
|||
|
PDEVICE_EXTENSION Extension
|
|||
|
);
|
|||
|
|
|||
|
UCHAR
|
|||
|
ParManageIoDevice(
|
|||
|
IN PDEVICE_EXTENSION Extension,
|
|||
|
OUT PUCHAR Status,
|
|||
|
OUT PUCHAR Control
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
ParStartIo(
|
|||
|
IN PDEVICE_EXTENSION Extension
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
ParallelThread(
|
|||
|
IN PVOID Context
|
|||
|
);
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ParCreateSystemThread(
|
|||
|
PDEVICE_EXTENSION Extension
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
ParCancelRequest(
|
|||
|
PDEVICE_OBJECT DeviceObject,
|
|||
|
PIRP Irp
|
|||
|
);
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ParAllocPortCompletionRoutine(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp,
|
|||
|
IN PVOID Context
|
|||
|
);
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
ParAllocPort(
|
|||
|
IN PDEVICE_EXTENSION Extension
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
ParReleasePortInfoToPortDevice(
|
|||
|
IN PDEVICE_EXTENSION Extension
|
|||
|
);
|
|||
|
|
|||
|
#ifdef ALLOC_PRAGMA
|
|||
|
#pragma alloc_text(INIT,DriverEntry)
|
|||
|
#pragma alloc_text(INIT,ParMakeNames)
|
|||
|
#pragma alloc_text(INIT,ParInitializeClassDevice)
|
|||
|
#pragma alloc_text(INIT,ParCheckParameters)
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// Keep track of OPEN and CLOSE.
|
|||
|
//
|
|||
|
ULONG OpenCloseReferenceCount = 1;
|
|||
|
PFAST_MUTEX OpenCloseMutex = NULL;
|
|||
|
|
|||
|
#define ParClaimDriver() \
|
|||
|
ExAcquireFastMutex(OpenCloseMutex); \
|
|||
|
if(++OpenCloseReferenceCount == 1) { \
|
|||
|
MmResetDriverPaging(DriverEntry); \
|
|||
|
} \
|
|||
|
ExReleaseFastMutex(OpenCloseMutex); \
|
|||
|
|
|||
|
#define ParReleaseDriver() \
|
|||
|
ExAcquireFastMutex(OpenCloseMutex); \
|
|||
|
if(--OpenCloseReferenceCount == 0) { \
|
|||
|
MmPageEntireDriver(DriverEntry); \
|
|||
|
} \
|
|||
|
ExReleaseFastMutex(OpenCloseMutex); \
|
|||
|
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
DriverEntry(
|
|||
|
IN PDRIVER_OBJECT DriverObject,
|
|||
|
IN PUNICODE_STRING RegistryPath
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is called at system initialization time to initialize
|
|||
|
this driver.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DriverObject - Supplies the driver object.
|
|||
|
|
|||
|
RegistryPath - Supplies the registry path for this driver.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_SUCCESS - We could initialize at least one device.
|
|||
|
STATUS_NO_SUCH_DEVICE - We could not initialize even one device.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
ULONG i;
|
|||
|
|
|||
|
//
|
|||
|
// allocate the mutex to protect driver reference count
|
|||
|
//
|
|||
|
|
|||
|
OpenCloseMutex = ExAllocatePool(NonPagedPool, sizeof(FAST_MUTEX));
|
|||
|
if (!OpenCloseMutex) {
|
|||
|
|
|||
|
//
|
|||
|
// NOTE - we could probably do without bailing here and just
|
|||
|
// leave a note for ourselves to never page out, but since we
|
|||
|
// don't have enough memory to allocate a mutex we should probably
|
|||
|
// avoid leaving the driver paged in at all times
|
|||
|
//
|
|||
|
|
|||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
|
|||
|
ExInitializeFastMutex(OpenCloseMutex);
|
|||
|
|
|||
|
for (i = 0; i < IoGetConfigurationInformation()->ParallelCount; i++) {
|
|||
|
ParInitializeClassDevice(DriverObject, RegistryPath, i);
|
|||
|
}
|
|||
|
|
|||
|
if (!DriverObject->DeviceObject) {
|
|||
|
return STATUS_NO_SUCH_DEVICE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Initialize the Driver Object with driver's entry points
|
|||
|
//
|
|||
|
|
|||
|
DriverObject->MajorFunction[IRP_MJ_CREATE] = ParCreateOpen;
|
|||
|
DriverObject->MajorFunction[IRP_MJ_CLOSE] = ParClose;
|
|||
|
DriverObject->MajorFunction[IRP_MJ_CLEANUP] = ParCleanup;
|
|||
|
DriverObject->MajorFunction[IRP_MJ_READ] = ParReadWrite;
|
|||
|
DriverObject->MajorFunction[IRP_MJ_WRITE] = ParReadWrite;
|
|||
|
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ParDeviceControl;
|
|||
|
DriverObject->MajorFunction[IRP_MJ_QUERY_INFORMATION] =
|
|||
|
ParQueryInformationFile;
|
|||
|
DriverObject->MajorFunction[IRP_MJ_SET_INFORMATION] =
|
|||
|
ParSetInformationFile;
|
|||
|
DriverObject->DriverUnload = ParUnload;
|
|||
|
|
|||
|
//
|
|||
|
// page out the driver if we can
|
|||
|
//
|
|||
|
|
|||
|
ParReleaseDriver();
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
ParLogError(
|
|||
|
IN PDRIVER_OBJECT DriverObject,
|
|||
|
IN PDEVICE_OBJECT DeviceObject OPTIONAL,
|
|||
|
IN PHYSICAL_ADDRESS P1,
|
|||
|
IN PHYSICAL_ADDRESS P2,
|
|||
|
IN ULONG SequenceNumber,
|
|||
|
IN UCHAR MajorFunctionCode,
|
|||
|
IN UCHAR RetryCount,
|
|||
|
IN ULONG UniqueErrorValue,
|
|||
|
IN NTSTATUS FinalStatus,
|
|||
|
IN NTSTATUS SpecificIOStatus
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine allocates an error log entry, copies the supplied data
|
|||
|
to it, and requests that it be written to the error log file.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DriverObject - Supplies a pointer to the driver object for the
|
|||
|
device.
|
|||
|
|
|||
|
DeviceObject - Supplies a pointer to the device object associated
|
|||
|
with the device that had the error, early in
|
|||
|
initialization, one may not yet exist.
|
|||
|
|
|||
|
P1,P2 - Supplies the physical addresses for the controller
|
|||
|
ports involved with the error if they are available
|
|||
|
and puts them through as dump data.
|
|||
|
|
|||
|
SequenceNumber - Supplies a ulong value that is unique to an IRP over
|
|||
|
the life of the irp in this driver - 0 generally
|
|||
|
means an error not associated with an irp.
|
|||
|
|
|||
|
MajorFunctionCode - Supplies the major function code of the irp if there
|
|||
|
is an error associated with it.
|
|||
|
|
|||
|
RetryCount - Supplies the number of times a particular operation
|
|||
|
has been retried.
|
|||
|
|
|||
|
UniqueErrorValue - Supplies a unique long word that identifies the
|
|||
|
particular call to this function.
|
|||
|
|
|||
|
FinalStatus - Supplies the final status given to the irp that was
|
|||
|
associated with this error. If this log entry is
|
|||
|
being made during one of the retries this value
|
|||
|
will be STATUS_SUCCESS.
|
|||
|
|
|||
|
SpecificIOStatus - Supplies the IO status for this particular error.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PIO_ERROR_LOG_PACKET errorLogEntry;
|
|||
|
PVOID objectToUse;
|
|||
|
SHORT dumpToAllocate;
|
|||
|
|
|||
|
if (ARGUMENT_PRESENT(DeviceObject)) {
|
|||
|
objectToUse = DeviceObject;
|
|||
|
} else {
|
|||
|
objectToUse = DriverObject;
|
|||
|
}
|
|||
|
|
|||
|
dumpToAllocate = 0;
|
|||
|
|
|||
|
if (P1.LowPart != 0 || P1.HighPart != 0) {
|
|||
|
dumpToAllocate = (SHORT) sizeof(PHYSICAL_ADDRESS);
|
|||
|
}
|
|||
|
|
|||
|
if (P2.LowPart != 0 || P2.HighPart != 0) {
|
|||
|
dumpToAllocate += (SHORT) sizeof(PHYSICAL_ADDRESS);
|
|||
|
}
|
|||
|
|
|||
|
errorLogEntry = IoAllocateErrorLogEntry(objectToUse,
|
|||
|
(UCHAR) (sizeof(IO_ERROR_LOG_PACKET) + dumpToAllocate));
|
|||
|
|
|||
|
if (!errorLogEntry) {
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
errorLogEntry->ErrorCode = SpecificIOStatus;
|
|||
|
errorLogEntry->SequenceNumber = SequenceNumber;
|
|||
|
errorLogEntry->MajorFunctionCode = MajorFunctionCode;
|
|||
|
errorLogEntry->RetryCount = RetryCount;
|
|||
|
errorLogEntry->UniqueErrorValue = UniqueErrorValue;
|
|||
|
errorLogEntry->FinalStatus = FinalStatus;
|
|||
|
errorLogEntry->DumpDataSize = dumpToAllocate;
|
|||
|
|
|||
|
if (dumpToAllocate) {
|
|||
|
|
|||
|
RtlCopyMemory(errorLogEntry->DumpData, &P1, sizeof(PHYSICAL_ADDRESS));
|
|||
|
|
|||
|
if (dumpToAllocate > sizeof(PHYSICAL_ADDRESS)) {
|
|||
|
|
|||
|
RtlCopyMemory(((PUCHAR) errorLogEntry->DumpData) +
|
|||
|
sizeof(PHYSICAL_ADDRESS), &P2,
|
|||
|
sizeof(PHYSICAL_ADDRESS));
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
IoWriteErrorLogEntry(errorLogEntry);
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
ParMakeNames(
|
|||
|
IN ULONG ParallelPortNumber,
|
|||
|
OUT PUNICODE_STRING PortName,
|
|||
|
OUT PUNICODE_STRING ClassName,
|
|||
|
OUT PUNICODE_STRING LinkName
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine generates the names \Device\ParallelPortN,
|
|||
|
\Device\ParallelN, and \DosDevices\LPT(N+1) where N is
|
|||
|
'ParallelPortNumber'. This routine will allocate pool
|
|||
|
so that the buffers of these unicode strings need to
|
|||
|
be eventually freed.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
ParallelPortNumber - Supplies the port number.
|
|||
|
|
|||
|
PortName - Returns the port name.
|
|||
|
|
|||
|
ClassName - Returns the class name.
|
|||
|
|
|||
|
LinkName - Returns the link name.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
FALSE - Failure.
|
|||
|
TRUE - Success.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
UNICODE_STRING prefix, digits, linkPrefix, linkDigits;
|
|||
|
WCHAR digitsBuffer[10], linkDigitsBuffer[10];
|
|||
|
UNICODE_STRING portSuffix, classSuffix, linkSuffix;
|
|||
|
NTSTATUS status;
|
|||
|
|
|||
|
// Put together local variables for constructing names.
|
|||
|
|
|||
|
RtlInitUnicodeString(&prefix, L"\\Device\\");
|
|||
|
RtlInitUnicodeString(&linkPrefix, L"\\DosDevices\\");
|
|||
|
RtlInitUnicodeString(&portSuffix, DD_PARALLEL_PORT_BASE_NAME_U);
|
|||
|
RtlInitUnicodeString(&classSuffix, DEFAULT_NT_SUFFIX);
|
|||
|
RtlInitUnicodeString(&linkSuffix, DEFAULT_PARALLEL_NAME);
|
|||
|
digits.Length = 0;
|
|||
|
digits.MaximumLength = 20;
|
|||
|
digits.Buffer = digitsBuffer;
|
|||
|
linkDigits.Length = 0;
|
|||
|
linkDigits.MaximumLength = 20;
|
|||
|
linkDigits.Buffer = linkDigitsBuffer;
|
|||
|
status = RtlIntegerToUnicodeString(ParallelPortNumber, 10, &digits);
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
status = RtlIntegerToUnicodeString(ParallelPortNumber + 1, 10, &linkDigits);
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// Make the port name.
|
|||
|
|
|||
|
PortName->Length = 0;
|
|||
|
PortName->MaximumLength = prefix.Length + portSuffix.Length +
|
|||
|
digits.Length + sizeof(WCHAR);
|
|||
|
PortName->Buffer = ExAllocatePool(PagedPool, PortName->MaximumLength);
|
|||
|
if (!PortName->Buffer) {
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
RtlZeroMemory(PortName->Buffer, PortName->MaximumLength);
|
|||
|
RtlAppendUnicodeStringToString(PortName, &prefix);
|
|||
|
RtlAppendUnicodeStringToString(PortName, &portSuffix);
|
|||
|
RtlAppendUnicodeStringToString(PortName, &digits);
|
|||
|
|
|||
|
|
|||
|
// Make the class name.
|
|||
|
|
|||
|
ClassName->Length = 0;
|
|||
|
ClassName->MaximumLength = prefix.Length + classSuffix.Length +
|
|||
|
digits.Length + sizeof(WCHAR);
|
|||
|
ClassName->Buffer = ExAllocatePool(PagedPool, ClassName->MaximumLength);
|
|||
|
if (!ClassName->Buffer) {
|
|||
|
ExFreePool(PortName->Buffer);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
RtlZeroMemory(ClassName->Buffer, ClassName->MaximumLength);
|
|||
|
RtlAppendUnicodeStringToString(ClassName, &prefix);
|
|||
|
RtlAppendUnicodeStringToString(ClassName, &classSuffix);
|
|||
|
RtlAppendUnicodeStringToString(ClassName, &digits);
|
|||
|
|
|||
|
|
|||
|
// Make the link name.
|
|||
|
|
|||
|
LinkName->Length = 0;
|
|||
|
LinkName->MaximumLength = linkPrefix.Length + linkSuffix.Length +
|
|||
|
linkDigits.Length + sizeof(WCHAR);
|
|||
|
LinkName->Buffer = ExAllocatePool(PagedPool, LinkName->MaximumLength);
|
|||
|
if (!LinkName->Buffer) {
|
|||
|
ExFreePool(PortName->Buffer);
|
|||
|
ExFreePool(ClassName->Buffer);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
RtlZeroMemory(LinkName->Buffer, LinkName->MaximumLength);
|
|||
|
RtlAppendUnicodeStringToString(LinkName, &linkPrefix);
|
|||
|
RtlAppendUnicodeStringToString(LinkName, &linkSuffix);
|
|||
|
RtlAppendUnicodeStringToString(LinkName, &linkDigits);
|
|||
|
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
ParInitializeClassDevice(
|
|||
|
IN PDRIVER_OBJECT DriverObject,
|
|||
|
IN PUNICODE_STRING RegistryPath,
|
|||
|
IN ULONG ParallelPortNumber
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is called for every parallel port in the system. It
|
|||
|
will create a class device upon connecting to the port device
|
|||
|
corresponding to it.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DriverObject - Supplies the driver object.
|
|||
|
|
|||
|
RegistryPath - Supplies the registry path.
|
|||
|
|
|||
|
ParallelPortNumber - Supplies the number for this port.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
UNICODE_STRING portName, className, linkName;
|
|||
|
NTSTATUS status;
|
|||
|
PDEVICE_OBJECT deviceObject;
|
|||
|
PDEVICE_EXTENSION extension;
|
|||
|
|
|||
|
//
|
|||
|
// Cobble together the port, class, and windows symbolic names.
|
|||
|
//
|
|||
|
|
|||
|
if (!ParMakeNames(ParallelPortNumber, &portName, &className, &linkName)) {
|
|||
|
|
|||
|
ParLogError(DriverObject, NULL, PhysicalZero, PhysicalZero, 0, 0, 0, 1,
|
|||
|
STATUS_SUCCESS, PAR_INSUFFICIENT_RESOURCES);
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARERRORS,
|
|||
|
("PARALLEL: Could not form Unicode name string.\n")
|
|||
|
);
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// Create the device object.
|
|||
|
|
|||
|
status = IoCreateDevice(DriverObject, sizeof(DEVICE_EXTENSION),
|
|||
|
&className, FILE_DEVICE_PARALLEL_PORT, 0, TRUE,
|
|||
|
&deviceObject);
|
|||
|
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
|
|||
|
ParLogError(DriverObject, NULL, PhysicalZero, PhysicalZero, 0, 0, 0, 2,
|
|||
|
STATUS_SUCCESS, PAR_INSUFFICIENT_RESOURCES);
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARERRORS,
|
|||
|
("PARALLEL: Could not create a device for %wZ\n",
|
|||
|
&className)
|
|||
|
);
|
|||
|
|
|||
|
ExFreePool(linkName.Buffer);
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// Now that the device has been created,
|
|||
|
// set up the device extension.
|
|||
|
|
|||
|
extension = deviceObject->DeviceExtension;
|
|||
|
|
|||
|
RtlZeroMemory(extension, sizeof(DEVICE_EXTENSION));
|
|||
|
|
|||
|
extension->DeviceObject = deviceObject;
|
|||
|
deviceObject->Flags |= DO_BUFFERED_IO;
|
|||
|
|
|||
|
status = IoGetDeviceObjectPointer(&portName, FILE_READ_ATTRIBUTES,
|
|||
|
&extension->PortDeviceFileObject,
|
|||
|
&extension->PortDeviceObject);
|
|||
|
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
|
|||
|
ParLogError(DriverObject, NULL, PhysicalZero, PhysicalZero, 0, 0, 0, 3,
|
|||
|
STATUS_SUCCESS, PAR_CANT_FIND_PORT_DRIVER);
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARERRORS,
|
|||
|
("PARALLEL: Unable to get device object pointer for port object.\n")
|
|||
|
);
|
|||
|
|
|||
|
IoDeleteDevice(deviceObject);
|
|||
|
|
|||
|
ExFreePool(linkName.Buffer);
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
extension->DeviceObject->StackSize =
|
|||
|
extension->PortDeviceObject->StackSize + 1;
|
|||
|
|
|||
|
InitializeListHead(&extension->WorkQueue);
|
|||
|
|
|||
|
KeInitializeSemaphore(&extension->RequestSemaphore, 0, MAXLONG);
|
|||
|
|
|||
|
extension->TimerStart = PAR_WRITE_TIMEOUT_VALUE;
|
|||
|
|
|||
|
extension->Initialized = FALSE;
|
|||
|
extension->Initializing = FALSE;
|
|||
|
extension->DeferredWorkItem = NULL;
|
|||
|
|
|||
|
status = ParGetPortInfoFromPortDevice(extension);
|
|||
|
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
|
|||
|
ParLogError(DriverObject, NULL, PhysicalZero, PhysicalZero, 0, 0, 0, 4,
|
|||
|
STATUS_SUCCESS, PAR_CANT_FIND_PORT_DRIVER);
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARERRORS,
|
|||
|
("PARALLEL: Can't get port info from port device.\n")
|
|||
|
);
|
|||
|
|
|||
|
ObDereferenceObject(extension->PortDeviceFileObject);
|
|||
|
|
|||
|
IoDeleteDevice(deviceObject);
|
|||
|
|
|||
|
ExFreePool(linkName.Buffer);
|
|||
|
goto Cleanup;
|
|||
|
}
|
|||
|
|
|||
|
extension->BusyDelay = 0;
|
|||
|
extension->BusyDelayDetermined = FALSE;
|
|||
|
|
|||
|
if (extension->OriginalController.HighPart == 0 &&
|
|||
|
extension->OriginalController.LowPart == (ULONG) extension->Controller) {
|
|||
|
|
|||
|
extension->UsePIWriteLoop = FALSE;
|
|||
|
} else {
|
|||
|
extension->UsePIWriteLoop = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// Set up some constants.
|
|||
|
|
|||
|
extension->AbsoluteOneSecond.QuadPart = 10*1000*1000;
|
|||
|
extension->OneSecond.QuadPart = -(extension->AbsoluteOneSecond.QuadPart);
|
|||
|
|
|||
|
// Try to initialize the printer here. This is done here so that printers that
|
|||
|
// change modes upon reset can be set to the desired mode before the first print.
|
|||
|
|
|||
|
//
|
|||
|
// Queue up a workitem to initialize the device (so we don't take forever loading).
|
|||
|
//
|
|||
|
|
|||
|
ParDeferDeviceInitialization(extension);
|
|||
|
|
|||
|
// Now setup the symbolic link for windows.
|
|||
|
|
|||
|
status = IoCreateUnprotectedSymbolicLink(&linkName, &className);
|
|||
|
|
|||
|
if (NT_SUCCESS(status)) {
|
|||
|
|
|||
|
// We were able to create the symbolic link, so record this
|
|||
|
// value in the extension for cleanup at unload time.
|
|||
|
|
|||
|
extension->CreatedSymbolicLink = TRUE;
|
|||
|
extension->SymbolicLinkName = linkName;
|
|||
|
|
|||
|
// Write out the result of the symbolic link to the registry.
|
|||
|
|
|||
|
status = RtlWriteRegistryValue(RTL_REGISTRY_DEVICEMAP,
|
|||
|
L"PARALLEL PORTS",
|
|||
|
className.Buffer,
|
|||
|
REG_SZ,
|
|||
|
linkName.Buffer,
|
|||
|
linkName.Length + sizeof(WCHAR));
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Oh well, couldn't create the symbolic link.
|
|||
|
//
|
|||
|
|
|||
|
extension->CreatedSymbolicLink = FALSE;
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARERRORS,
|
|||
|
("PARALLEL: Couldn't create the symbolic link\n"
|
|||
|
"-------- for port %wZ\n",
|
|||
|
&className)
|
|||
|
);
|
|||
|
|
|||
|
ParLogError(DriverObject, deviceObject, extension->OriginalController,
|
|||
|
PhysicalZero, 0, 0, 0, 5, status, PAR_NO_SYMLINK_CREATED);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
ExFreePool(linkName.Buffer);
|
|||
|
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
|
|||
|
//
|
|||
|
// Oh well, it didn't work. Just go to cleanup.
|
|||
|
//
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARERRORS,
|
|||
|
("PARALLEL: Couldn't create the device map entry\n"
|
|||
|
"-------- for port %wZ\n",
|
|||
|
&className)
|
|||
|
);
|
|||
|
|
|||
|
ParLogError(DriverObject, deviceObject, extension->OriginalController,
|
|||
|
PhysicalZero, 0, 0, 0, 6, status,
|
|||
|
PAR_NO_DEVICE_MAP_CREATED);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// Check the registry for parameters.
|
|||
|
|
|||
|
ParCheckParameters(RegistryPath, extension);
|
|||
|
|
|||
|
|
|||
|
Cleanup:
|
|||
|
ExFreePool(portName.Buffer);
|
|||
|
ExFreePool(className.Buffer);
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
ParReleasePortInfoToPortDevice(
|
|||
|
IN PDEVICE_EXTENSION Extension
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine will release the port information back to the port driver.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Extension - Supplies the device extension.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
KEVENT event;
|
|||
|
PIRP irp;
|
|||
|
IO_STATUS_BLOCK ioStatus;
|
|||
|
NTSTATUS status;
|
|||
|
|
|||
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|||
|
|
|||
|
irp = IoBuildDeviceIoControlRequest(IOCTL_INTERNAL_RELEASE_PARALLEL_PORT_INFO,
|
|||
|
Extension->PortDeviceObject,
|
|||
|
NULL, 0, NULL, 0,
|
|||
|
TRUE, &event, &ioStatus);
|
|||
|
|
|||
|
if (!irp) {
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
status = IoCallDriver(Extension->PortDeviceObject, irp);
|
|||
|
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ParGetPortInfoFromPortDevice(
|
|||
|
IN OUT PDEVICE_EXTENSION Extension
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine will request the port information from the port driver
|
|||
|
and fill it in the device extension.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Extension - Supplies the device extension.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_SUCCESS - Success.
|
|||
|
!STATUS_SUCCESS - Failure.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
KEVENT event;
|
|||
|
PIRP irp;
|
|||
|
PARALLEL_PORT_INFORMATION portInfo;
|
|||
|
IO_STATUS_BLOCK ioStatus;
|
|||
|
NTSTATUS status;
|
|||
|
|
|||
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|||
|
|
|||
|
irp = IoBuildDeviceIoControlRequest(IOCTL_INTERNAL_GET_PARALLEL_PORT_INFO,
|
|||
|
Extension->PortDeviceObject,
|
|||
|
NULL, 0, &portInfo,
|
|||
|
sizeof(PARALLEL_PORT_INFORMATION),
|
|||
|
TRUE, &event, &ioStatus);
|
|||
|
|
|||
|
if (!irp) {
|
|||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
|
|||
|
status = IoCallDriver(Extension->PortDeviceObject, irp);
|
|||
|
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
status = KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|||
|
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
status = ioStatus.Status;
|
|||
|
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
return(status);
|
|||
|
}
|
|||
|
|
|||
|
Extension->OriginalController = portInfo.OriginalController;
|
|||
|
Extension->Controller = portInfo.Controller;
|
|||
|
Extension->SpanOfController = portInfo.SpanOfController;
|
|||
|
Extension->FreePort = portInfo.FreePort;
|
|||
|
Extension->QueryNumWaiters = portInfo.QueryNumWaiters;
|
|||
|
Extension->PortContext = portInfo.Context;
|
|||
|
|
|||
|
if (Extension->SpanOfController < PARALLEL_REGISTER_SPAN) {
|
|||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
ParDeferredInitCallback(
|
|||
|
IN OUT PVOID Context
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine initializes the printer by allocating the parallel port,
|
|||
|
calling 'ParInitializeDevice', and deallocating the parallel port.
|
|||
|
|
|||
|
It also clears the Initializing flag upon completion and frees the workitem.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Extension - Supplies the device extension.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
KEVENT event;
|
|||
|
PIRP irp;
|
|||
|
IO_STATUS_BLOCK ioStatus;
|
|||
|
NTSTATUS status;
|
|||
|
LARGE_INTEGER timeout;
|
|||
|
KIRQL oldIrql;
|
|||
|
PDEVICE_EXTENSION Extension = (PDEVICE_EXTENSION) Context;
|
|||
|
BOOLEAN gotPort = FALSE;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Page the entire driver in until we've completed our initialization
|
|||
|
//
|
|||
|
|
|||
|
ParClaimDriver();
|
|||
|
|
|||
|
//
|
|||
|
// Try to allocate the port
|
|||
|
//
|
|||
|
|
|||
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|||
|
|
|||
|
irp = IoBuildDeviceIoControlRequest(IOCTL_INTERNAL_PARALLEL_PORT_ALLOCATE,
|
|||
|
Extension->PortDeviceObject,
|
|||
|
NULL, 0, NULL, 0, TRUE, &event,
|
|||
|
&ioStatus);
|
|||
|
|
|||
|
if (!irp) {
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
IoCallDriver(Extension->PortDeviceObject, irp);
|
|||
|
|
|||
|
|
|||
|
// We're willing to wait at most 5 seconds for the port. Otherwise,
|
|||
|
// we'll just initialize the printer later.
|
|||
|
|
|||
|
timeout.QuadPart = -(50*1000*1000);
|
|||
|
|
|||
|
status = KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, &timeout);
|
|||
|
|
|||
|
if (status == STATUS_TIMEOUT) {
|
|||
|
|
|||
|
KeRaiseIrql(APC_LEVEL, &oldIrql);
|
|||
|
|
|||
|
if (KeReadStateEvent(&event) == 0) {
|
|||
|
IoCancelIrp(irp);
|
|||
|
}
|
|||
|
|
|||
|
KeLowerIrql(oldIrql);
|
|||
|
|
|||
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|||
|
}
|
|||
|
|
|||
|
if (!NT_SUCCESS(ioStatus.Status)) {
|
|||
|
// We didn't get the port, so jump to the end
|
|||
|
goto DeferredInitCleanup;
|
|||
|
}
|
|||
|
|
|||
|
gotPort = TRUE;
|
|||
|
|
|||
|
//
|
|||
|
// The port was successfully allocated. Intialize the device
|
|||
|
//
|
|||
|
|
|||
|
ParInitializeDevice(Extension);
|
|||
|
|
|||
|
|
|||
|
DeferredInitCleanup:
|
|||
|
|
|||
|
//
|
|||
|
// set the initializing flag to false, release the port driver and the port driver
|
|||
|
// info (so it unlocks it's memory) and free the workitem
|
|||
|
//
|
|||
|
|
|||
|
Extension->Initializing = FALSE;
|
|||
|
|
|||
|
if(gotPort) ParFreePort(Extension);
|
|||
|
|
|||
|
ParReleasePortInfoToPortDevice(Extension);
|
|||
|
|
|||
|
if(Extension->DeferredWorkItem) ExFreePool(Extension->DeferredWorkItem);
|
|||
|
|
|||
|
ParReleaseDriver();
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
ParCheckParameters(
|
|||
|
IN PUNICODE_STRING RegistryPath,
|
|||
|
IN OUT PDEVICE_EXTENSION Extension
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine reads the parameters section of the registry and modifies
|
|||
|
the device extension as specified by the parameters.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
RegistryPath - Supplies the registry path.
|
|||
|
|
|||
|
Extension - Supplies the device extension.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
UNICODE_STRING parameters;
|
|||
|
UNICODE_STRING path;
|
|||
|
RTL_QUERY_REGISTRY_TABLE paramTable[4];
|
|||
|
ULONG usePIWriteLoop;
|
|||
|
ULONG zero = 0;
|
|||
|
ULONG useNT35Priority;
|
|||
|
NTSTATUS status;
|
|||
|
|
|||
|
RtlInitUnicodeString(¶meters, L"\\Parameters");
|
|||
|
path.Length = 0;
|
|||
|
path.MaximumLength = RegistryPath->Length +
|
|||
|
parameters.Length +
|
|||
|
sizeof(WCHAR);
|
|||
|
path.Buffer = ExAllocatePool(PagedPool, path.MaximumLength);
|
|||
|
|
|||
|
if (!path.Buffer) {
|
|||
|
return;
|
|||
|
}
|
|||
|
RtlZeroMemory(path.Buffer, path.MaximumLength);
|
|||
|
RtlAppendUnicodeStringToString(&path, RegistryPath);
|
|||
|
RtlAppendUnicodeStringToString(&path, ¶meters);
|
|||
|
|
|||
|
RtlZeroMemory(paramTable, sizeof(paramTable));
|
|||
|
|
|||
|
paramTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|||
|
paramTable[0].Name = L"UsePIWriteLoop";
|
|||
|
paramTable[0].EntryContext = &usePIWriteLoop;
|
|||
|
paramTable[0].DefaultType = REG_DWORD;
|
|||
|
paramTable[0].DefaultData = &zero;
|
|||
|
paramTable[0].DefaultLength = sizeof(ULONG);
|
|||
|
|
|||
|
paramTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|||
|
paramTable[1].Name = L"UseNT35Priority";
|
|||
|
paramTable[1].EntryContext = &useNT35Priority;
|
|||
|
paramTable[1].DefaultType = REG_DWORD;
|
|||
|
paramTable[1].DefaultData = &zero;
|
|||
|
paramTable[1].DefaultLength = sizeof(ULONG);
|
|||
|
|
|||
|
paramTable[2].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|||
|
paramTable[2].Name = L"InitializationTimeout";
|
|||
|
paramTable[2].EntryContext = &(Extension->InitializationTimeout);
|
|||
|
paramTable[2].DefaultType = REG_DWORD;
|
|||
|
paramTable[2].DefaultData = &zero;
|
|||
|
paramTable[2].DefaultLength = sizeof(ULONG);
|
|||
|
|
|||
|
status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE | RTL_REGISTRY_OPTIONAL,
|
|||
|
path.Buffer, paramTable, NULL, NULL);
|
|||
|
|
|||
|
if (NT_SUCCESS(status)) {
|
|||
|
|
|||
|
if(usePIWriteLoop) {
|
|||
|
Extension->UsePIWriteLoop = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
if(useNT35Priority) {
|
|||
|
Extension->UseNT35Priority = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
if(Extension->InitializationTimeout == 0) {
|
|||
|
Extension->InitializationTimeout = 15;
|
|||
|
}
|
|||
|
} else {
|
|||
|
|
|||
|
Extension->InitializationTimeout = 15;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
ExFreePool(path.Buffer);
|
|||
|
}
|
|||
|
|
|||
|
UCHAR
|
|||
|
ParReinitializeDevice(
|
|||
|
IN PDEVICE_EXTENSION Extension
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is invoked to reinitialize the parallel port device. If the
|
|||
|
Initializing flag in the device extension is set, it will wait for it to
|
|||
|
complete then return. If the flag is not set, it will call ParInitializeDevice.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Extension - the device extension
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
the last value that we got from the status register.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
if(!Extension->Initializing) {
|
|||
|
|
|||
|
return ParInitializeDevice(Extension);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// wait for the current initialization attempt to finish
|
|||
|
//
|
|||
|
|
|||
|
do {
|
|||
|
|
|||
|
KeDelayExecutionThread(KernelMode, FALSE, &Extension->OneSecond);
|
|||
|
|
|||
|
} while (Extension->Initializing);
|
|||
|
|
|||
|
return GetStatus(Extension->Controller);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
UCHAR
|
|||
|
ParInitializeDevice(
|
|||
|
IN PDEVICE_EXTENSION Extension
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is invoked to initialize the parallel port drive.
|
|||
|
It performs the following actions:
|
|||
|
|
|||
|
o Send INIT to the driver and if the device is online, it sends
|
|||
|
SLIN
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Context - Really the device extension.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
The last value that we got from the status register.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
KIRQL oldIrql;
|
|||
|
LONG countDown;
|
|||
|
UCHAR deviceStatus;
|
|||
|
LARGE_INTEGER startOfSpin;
|
|||
|
LARGE_INTEGER nextQuery;
|
|||
|
LARGE_INTEGER difference;
|
|||
|
BOOLEAN doDelays;
|
|||
|
|
|||
|
deviceStatus = GetStatus(Extension->Controller);
|
|||
|
ParDump(
|
|||
|
PARINITDEV,
|
|||
|
("PARALLEL: In ParInitializeDevice - device status is %x\n"
|
|||
|
" Initialized: %x\n",
|
|||
|
deviceStatus,
|
|||
|
Extension->Initialized)
|
|||
|
);
|
|||
|
|
|||
|
if (!Extension->Initialized) {
|
|||
|
|
|||
|
//
|
|||
|
// Clear the register.
|
|||
|
//
|
|||
|
|
|||
|
if (GetControl(Extension->Controller) &
|
|||
|
PAR_CONTROL_NOT_INIT) {
|
|||
|
|
|||
|
//
|
|||
|
// We should stall for at least 60 microseconds after
|
|||
|
// the init.
|
|||
|
//
|
|||
|
|
|||
|
KeRaiseIrql(
|
|||
|
DISPATCH_LEVEL,
|
|||
|
&oldIrql
|
|||
|
);
|
|||
|
StoreControl(
|
|||
|
Extension->Controller,
|
|||
|
(UCHAR)(PAR_CONTROL_WR_CONTROL | PAR_CONTROL_SLIN)
|
|||
|
);
|
|||
|
KeStallExecutionProcessor(60);
|
|||
|
KeLowerIrql(oldIrql);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
StoreControl(
|
|||
|
Extension->Controller,
|
|||
|
(UCHAR)(PAR_CONTROL_WR_CONTROL | PAR_CONTROL_NOT_INIT |
|
|||
|
PAR_CONTROL_SLIN)
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// Spin waiting for the device to initialize.
|
|||
|
//
|
|||
|
|
|||
|
countDown = Extension->InitializationTimeout;
|
|||
|
doDelays = FALSE;
|
|||
|
KeQueryTickCount(&startOfSpin);
|
|||
|
ParDump(
|
|||
|
PARINITDEV,
|
|||
|
("PARALLEL: Starting init wait loop\n")
|
|||
|
);
|
|||
|
do {
|
|||
|
|
|||
|
//
|
|||
|
// first spin in a tight loop for one second waiting for the
|
|||
|
// device to initialize. Once that fails, check every second
|
|||
|
// until our countdown runs out.
|
|||
|
//
|
|||
|
|
|||
|
if (!doDelays) {
|
|||
|
|
|||
|
KeQueryTickCount(&nextQuery);
|
|||
|
|
|||
|
difference.QuadPart = nextQuery.QuadPart - startOfSpin.QuadPart;
|
|||
|
|
|||
|
ASSERT(KeQueryTimeIncrement() <= MAXLONG);
|
|||
|
if (difference.QuadPart*KeQueryTimeIncrement() >=
|
|||
|
Extension->AbsoluteOneSecond.QuadPart) {
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARINITDEV,
|
|||
|
("PARALLEL: Did spin of one second\n"
|
|||
|
" startOfSpin: %x nextQuery: %x\n",
|
|||
|
startOfSpin.LowPart,nextQuery.LowPart)
|
|||
|
);
|
|||
|
ParDump(
|
|||
|
PARINITDEV,
|
|||
|
("PARALLEL: parintialize 1 seconds wait\n")
|
|||
|
);
|
|||
|
countDown--;
|
|||
|
doDelays = TRUE;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
#ifdef JAPAN // ParInitializeDevice()
|
|||
|
//
|
|||
|
// IBM PS/55 Printer support.
|
|||
|
//
|
|||
|
// The printer is keeping PAR_NO_CABLE status
|
|||
|
// during initialization. we won't break at the status.
|
|||
|
// We suceed in initialization when the status is PE.
|
|||
|
// Some of the printers always tell this status when the
|
|||
|
// init pulse is sent (the printer will eject paper if it
|
|||
|
// is there, and you never suceed in initialization
|
|||
|
// unless you are satisfied with PE.)
|
|||
|
//
|
|||
|
|
|||
|
if (PAR_OFF_LINE(deviceStatus) ||
|
|||
|
PAR_POWERED_OFF(deviceStatus) ||
|
|||
|
PAR_NOT_CONNECTED(deviceStatus) ||
|
|||
|
PAR_PAPER_EMPTY2(deviceStatus))
|
|||
|
#else
|
|||
|
if (PAR_OFF_LINE(deviceStatus) ||
|
|||
|
PAR_POWERED_OFF(deviceStatus) ||
|
|||
|
PAR_NOT_CONNECTED(deviceStatus))
|
|||
|
#endif // JAPAN
|
|||
|
{
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
KeDelayExecutionThread(KernelMode, FALSE, &Extension->OneSecond);
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARINITDEV,
|
|||
|
("PARALLEL: Did delay thread of one second\n")
|
|||
|
);
|
|||
|
|
|||
|
countDown--;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
if (countDown <= 0) {
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARINITDEV,
|
|||
|
("PARALLEL: leaving with init timeout - status %x\n",
|
|||
|
deviceStatus)
|
|||
|
);
|
|||
|
Extension->Initialized = FALSE;
|
|||
|
return deviceStatus;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
deviceStatus = GetStatus(Extension->Controller);
|
|||
|
|
|||
|
} while (!PAR_OK(deviceStatus));
|
|||
|
|
|||
|
#if defined(JAPAN)
|
|||
|
if(PAR_OK(deviceStatus) || PAR_OFF_LINE(deviceStatus) ||
|
|||
|
PAR_PAPER_EMPTY2(deviceStatus))
|
|||
|
#else // JAPAN
|
|||
|
if (PAR_OK(deviceStatus) || PAR_OFF_LINE(deviceStatus))
|
|||
|
#endif // JAPAN
|
|||
|
{
|
|||
|
ParDump(
|
|||
|
PARINITDEV,
|
|||
|
("PARALLEL: device is set to initialized\n")
|
|||
|
);
|
|||
|
Extension->Initialized = TRUE;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARINITDEV,
|
|||
|
("PARALLEL: In ParInitializeDevice - leaving with device status is %x\n",
|
|||
|
deviceStatus)
|
|||
|
);
|
|||
|
return deviceStatus;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
ParNotInitError(
|
|||
|
IN PDEVICE_EXTENSION Extension,
|
|||
|
IN UCHAR deviceStatus
|
|||
|
)
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
PIRP irp = Extension->CurrentOpIrp;
|
|||
|
|
|||
|
if (PAR_OFF_LINE(deviceStatus)) {
|
|||
|
|
|||
|
irp->IoStatus.Status = STATUS_DEVICE_OFF_LINE;
|
|||
|
ParDump(
|
|||
|
PARSTARTER,
|
|||
|
("PARALLEL: starter - off line\n"
|
|||
|
"-------- STATUS/INFORMATON: %x/%x\n",
|
|||
|
irp->IoStatus.Status,
|
|||
|
irp->IoStatus.Information)
|
|||
|
);
|
|||
|
|
|||
|
} else if (PAR_NO_CABLE(deviceStatus)) {
|
|||
|
|
|||
|
irp->IoStatus.Status = STATUS_DEVICE_NOT_CONNECTED;
|
|||
|
ParDump(
|
|||
|
PARSTARTER,
|
|||
|
("PARALLEL: starter - no cable - not connect status\n"
|
|||
|
"-------- STATUS/INFORMATON: %x/%x\n",
|
|||
|
irp->IoStatus.Status,
|
|||
|
irp->IoStatus.Information)
|
|||
|
);
|
|||
|
|
|||
|
} else if (PAR_PAPER_EMPTY(deviceStatus)) {
|
|||
|
|
|||
|
irp->IoStatus.Status = STATUS_DEVICE_PAPER_EMPTY;
|
|||
|
ParDump(
|
|||
|
PARSTARTER,
|
|||
|
("PARALLEL: starter - paper empty\n"
|
|||
|
"-------- STATUS/INFORMATON: %x/%x\n",
|
|||
|
irp->IoStatus.Status,
|
|||
|
irp->IoStatus.Information)
|
|||
|
);
|
|||
|
|
|||
|
} else if (PAR_POWERED_OFF(deviceStatus)) {
|
|||
|
|
|||
|
irp->IoStatus.Status = STATUS_DEVICE_POWERED_OFF;
|
|||
|
ParDump(
|
|||
|
PARSTARTER,
|
|||
|
("PARALLEL: starter - power off\n"
|
|||
|
"-------- STATUS/INFORMATON: %x/%x\n",
|
|||
|
irp->IoStatus.Status,
|
|||
|
irp->IoStatus.Information)
|
|||
|
);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
irp->IoStatus.Status = STATUS_DEVICE_NOT_CONNECTED;
|
|||
|
ParDump(
|
|||
|
PARSTARTER,
|
|||
|
("PARALLEL: starter - not conn\n"
|
|||
|
"-------- STATUS/INFORMATON: %x/%x\n",
|
|||
|
irp->IoStatus.Status,
|
|||
|
irp->IoStatus.Information)
|
|||
|
);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
ParFreePort(
|
|||
|
IN PDEVICE_EXTENSION Extension
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine calls the internal free port ioctl. This routine
|
|||
|
should be called before completing an IRP that has allocated
|
|||
|
the port.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Extension - Supplies the device extension.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
Extension->FreePort(Extension->PortContext);
|
|||
|
}
|
|||
|
|
|||
|
ULONG
|
|||
|
ParWriteLoopPI(
|
|||
|
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.
|
|||
|
|
|||
|
Notes:
|
|||
|
|
|||
|
This routine runs at DISPATCH_LEVEL.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
ULONG i;
|
|||
|
UCHAR deviceStatus;
|
|||
|
|
|||
|
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.
|
|||
|
//
|
|||
|
|
|||
|
WRITE_PORT_UCHAR(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 {
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARPUSHER,
|
|||
|
("PARALLEL: Initiate IO - device is not on line, status: %x\n",
|
|||
|
deviceStatus)
|
|||
|
);
|
|||
|
|
|||
|
break;
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return i;
|
|||
|
}
|
|||
|
|
|||
|
ULONG
|
|||
|
ParCheckBusyDelay(
|
|||
|
IN PDEVICE_EXTENSION Extension,
|
|||
|
IN PUCHAR WriteBuffer,
|
|||
|
IN ULONG NumBytesToWrite
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine determines if the current busy delay setting is
|
|||
|
adequate for this printer.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Extension - 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 = Extension->Controller;
|
|||
|
ULONG busyDelay = Extension->BusyDelay;
|
|||
|
LARGE_INTEGER start, perfFreq, end, getStatusTime, callOverhead;
|
|||
|
UCHAR deviceStatus;
|
|||
|
ULONG numberOfCalls, i;
|
|||
|
KIRQL oldIrql;
|
|||
|
|
|||
|
// If the current busy delay value is 10 or greater then something
|
|||
|
// is weird and settle for 10.
|
|||
|
|
|||
|
if (Extension->BusyDelay >= 10) {
|
|||
|
Extension->BusyDelayDetermined = TRUE;
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
// Take some performance measurements.
|
|||
|
|
|||
|
KeRaiseIrql(HIGH_LEVEL, &oldIrql);
|
|||
|
start = KeQueryPerformanceCounter(&perfFreq);
|
|||
|
deviceStatus = GetStatus(controller);
|
|||
|
end = KeQueryPerformanceCounter(&perfFreq);
|
|||
|
getStatusTime.QuadPart = end.QuadPart - start.QuadPart;
|
|||
|
|
|||
|
start = KeQueryPerformanceCounter(&perfFreq);
|
|||
|
end = KeQueryPerformanceCounter(&perfFreq);
|
|||
|
KeLowerIrql(oldIrql);
|
|||
|
callOverhead.QuadPart = end.QuadPart - start.QuadPart;
|
|||
|
getStatusTime.QuadPart -= callOverhead.QuadPart;
|
|||
|
if (getStatusTime.QuadPart <= 0) {
|
|||
|
getStatusTime.QuadPart = 1;
|
|||
|
}
|
|||
|
|
|||
|
// if (!PAR_ONLINE(deviceStatus)) {
|
|||
|
// return 0;
|
|||
|
// }
|
|||
|
|
|||
|
// Figure out how many calls to 'GetStatus' can be made in 20 us.
|
|||
|
|
|||
|
numberOfCalls = (ULONG) (perfFreq.QuadPart*20/getStatusTime.QuadPart/1000000) + 1;
|
|||
|
|
|||
|
// The printer is ready to accept the a byte. Strobe one out
|
|||
|
// and check out the reaction time for BUSY.
|
|||
|
|
|||
|
if (busyDelay) {
|
|||
|
|
|||
|
KeRaiseIrql(HIGH_LEVEL, &oldIrql);
|
|||
|
|
|||
|
WRITE_PORT_UCHAR(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;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
KeLowerIrql(oldIrql);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
KeRaiseIrql(HIGH_LEVEL, &oldIrql);
|
|||
|
|
|||
|
WRITE_PORT_UCHAR(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;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
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.
|
|||
|
|
|||
|
Extension->UsePIWriteLoop = TRUE;
|
|||
|
Extension->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.
|
|||
|
|
|||
|
Extension->BusyDelayDetermined = TRUE;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
// The test failed. The lines showed not BUSY and then BUSY
|
|||
|
// without strobing a byte in between.
|
|||
|
|
|||
|
Extension->UsePIWriteLoop = TRUE;
|
|||
|
Extension->BusyDelay++;
|
|||
|
}
|
|||
|
|
|||
|
return 1;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
ParWriteOutData(
|
|||
|
PDEVICE_EXTENSION Extension
|
|||
|
)
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
PIRP irp = Extension->CurrentOpIrp;
|
|||
|
KIRQL cancelIrql;
|
|||
|
UCHAR deviceStatus;
|
|||
|
KIRQL oldIrql;
|
|||
|
ULONG timerStart = Extension->TimerStart;
|
|||
|
LONG countDown = (LONG)timerStart;
|
|||
|
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(irp);
|
|||
|
ULONG bytesToWrite = irpSp->Parameters.Write.Length;
|
|||
|
PUCHAR irpBuffer = (PUCHAR)irp->AssociatedIrp.SystemBuffer;
|
|||
|
LARGE_INTEGER startOfSpin;
|
|||
|
LARGE_INTEGER nextQuery;
|
|||
|
LARGE_INTEGER difference;
|
|||
|
BOOLEAN doDelays, portFree;
|
|||
|
ULONG numBytesWritten, loopNumber;
|
|||
|
ULONG numberOfBusyChecks = 9;
|
|||
|
ULONG maxBusyDelay = 0;
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARTHREAD,
|
|||
|
("PARALLEL: timerStart is: %d\n",
|
|||
|
timerStart)
|
|||
|
);
|
|||
|
|
|||
|
// Turn off the strobe in case it was left on by some other device sharing
|
|||
|
// the port.
|
|||
|
StoreControl(Extension->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;
|
|||
|
}
|
|||
|
|
|||
|
KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);
|
|||
|
|
|||
|
if (!Extension->BusyDelayDetermined) {
|
|||
|
numBytesWritten = ParCheckBusyDelay(Extension, irpBuffer, loopNumber);
|
|||
|
if (Extension->BusyDelayDetermined) {
|
|||
|
if (Extension->BusyDelay > maxBusyDelay) {
|
|||
|
maxBusyDelay = Extension->BusyDelay;
|
|||
|
numberOfBusyChecks = 10;
|
|||
|
}
|
|||
|
if (numberOfBusyChecks) {
|
|||
|
numberOfBusyChecks--;
|
|||
|
Extension->BusyDelayDetermined = FALSE;
|
|||
|
} else {
|
|||
|
Extension->BusyDelay = maxBusyDelay + 1;
|
|||
|
}
|
|||
|
}
|
|||
|
} else if (Extension->UsePIWriteLoop) {
|
|||
|
numBytesWritten = ParWriteLoopPI(Extension->Controller, irpBuffer,
|
|||
|
loopNumber, Extension->BusyDelay);
|
|||
|
} else {
|
|||
|
numBytesWritten = ParWriteLoop(Extension->Controller, irpBuffer,
|
|||
|
loopNumber);
|
|||
|
}
|
|||
|
|
|||
|
KeLowerIrql(oldIrql);
|
|||
|
|
|||
|
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) {
|
|||
|
|
|||
|
irp->IoStatus.Status = STATUS_SUCCESS;
|
|||
|
irp->IoStatus.Information = irpSp->Parameters.Write.Length;
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARIRPPATH,
|
|||
|
("PARALLEL: About to complete IRP in pusher - wrote ok\n"
|
|||
|
"irp: %x status: %x Information: %x\n",
|
|||
|
irp,
|
|||
|
irp->IoStatus.Status,
|
|||
|
irp->IoStatus.Information)
|
|||
|
);
|
|||
|
IoAcquireCancelSpinLock(&cancelIrql);
|
|||
|
Extension->CurrentOpIrp = NULL;
|
|||
|
IoReleaseCancelSpinLock(cancelIrql);
|
|||
|
|
|||
|
IoCompleteRequest(
|
|||
|
irp,
|
|||
|
IO_PARALLEL_INCREMENT
|
|||
|
);
|
|||
|
|
|||
|
ParFreePort(Extension);
|
|||
|
|
|||
|
return;
|
|||
|
|
|||
|
//
|
|||
|
// 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.
|
|||
|
//
|
|||
|
|
|||
|
} else if (irp->Cancel) {
|
|||
|
|
|||
|
irp->IoStatus.Status = STATUS_CANCELLED;
|
|||
|
irp->IoStatus.Information = 0;
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARIRPPATH,
|
|||
|
("PARALLEL: About to complete IRP in pusher - cancelled\n"
|
|||
|
"irp: %x status: %x Information: %x\n",
|
|||
|
irp,
|
|||
|
irp->IoStatus.Status,
|
|||
|
irp->IoStatus.Information)
|
|||
|
);
|
|||
|
IoAcquireCancelSpinLock(&cancelIrql);
|
|||
|
Extension->CurrentOpIrp = NULL;
|
|||
|
IoReleaseCancelSpinLock(cancelIrql);
|
|||
|
|
|||
|
IoCompleteRequest(
|
|||
|
irp,
|
|||
|
IO_NO_INCREMENT
|
|||
|
);
|
|||
|
|
|||
|
ParFreePort(Extension);
|
|||
|
|
|||
|
return;
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// 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.
|
|||
|
//
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// 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(Extension->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.
|
|||
|
//
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARTHREAD,
|
|||
|
("PARALLEL: decrementing countdown for pe/ol\n"
|
|||
|
" countDown: %d status: %x\n",
|
|||
|
countDown,deviceStatus)
|
|||
|
);
|
|||
|
countDown--;
|
|||
|
|
|||
|
// If anyone is waiting for the port then let them have it,
|
|||
|
// since the printer is busy.
|
|||
|
|
|||
|
ParFreePort(Extension);
|
|||
|
|
|||
|
KeDelayExecutionThread(
|
|||
|
KernelMode,
|
|||
|
FALSE,
|
|||
|
&Extension->OneSecond
|
|||
|
);
|
|||
|
|
|||
|
if (!ParAllocPort(Extension)) {
|
|||
|
IoAcquireCancelSpinLock(&cancelIrql);
|
|||
|
Extension->CurrentOpIrp = NULL;
|
|||
|
IoReleaseCancelSpinLock(cancelIrql);
|
|||
|
irp->IoStatus.Information =
|
|||
|
irpSp->Parameters.Write.Length - bytesToWrite;
|
|||
|
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
goto PushSomeBytes;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Timer has expired. Complete the request.
|
|||
|
//
|
|||
|
|
|||
|
irp->IoStatus.Information =
|
|||
|
irpSp->Parameters.Write.Length - bytesToWrite;
|
|||
|
if (PAR_OFF_LINE(deviceStatus)) {
|
|||
|
|
|||
|
irp->IoStatus.Status = STATUS_DEVICE_OFF_LINE;
|
|||
|
ParDump(
|
|||
|
PARIRPPATH,
|
|||
|
("PARALLEL: About to complete IRP in pusher - offline\n"
|
|||
|
"irp: %x status: %x Information: %x\n",
|
|||
|
irp,
|
|||
|
irp->IoStatus.Status,
|
|||
|
irp->IoStatus.Information)
|
|||
|
);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
irp->IoStatus.Status = STATUS_DEVICE_PAPER_EMPTY;
|
|||
|
ParDump(
|
|||
|
PARIRPPATH,
|
|||
|
("PARALLEL: About to complete IRP in pusher - PE\n"
|
|||
|
"irp: %x status: %x Information: %x\n",
|
|||
|
irp,
|
|||
|
irp->IoStatus.Status,
|
|||
|
irp->IoStatus.Information)
|
|||
|
);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
IoAcquireCancelSpinLock(&cancelIrql);
|
|||
|
Extension->CurrentOpIrp = NULL;
|
|||
|
IoReleaseCancelSpinLock(cancelIrql);
|
|||
|
|
|||
|
IoCompleteRequest(
|
|||
|
irp,
|
|||
|
IO_PARALLEL_INCREMENT
|
|||
|
);
|
|||
|
|
|||
|
ParFreePort(Extension);
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
} else if (PAR_POWERED_OFF(deviceStatus) ||
|
|||
|
PAR_NOT_CONNECTED(deviceStatus) ||
|
|||
|
PAR_NO_CABLE(deviceStatus)) {
|
|||
|
|
|||
|
//
|
|||
|
// Wimper, wimper, something "bad" happened. 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.
|
|||
|
//
|
|||
|
|
|||
|
irp->IoStatus.Information = 0;
|
|||
|
Extension->Initialized = FALSE;
|
|||
|
|
|||
|
if (PAR_POWERED_OFF(deviceStatus)) {
|
|||
|
|
|||
|
irp->IoStatus.Status = STATUS_DEVICE_POWERED_OFF;
|
|||
|
ParDump(
|
|||
|
PARIRPPATH,
|
|||
|
("PARALLEL: About to complete IRP in pusher - OFF\n"
|
|||
|
"irp: %x status: %x Information: %x\n",
|
|||
|
irp,
|
|||
|
irp->IoStatus.Status,
|
|||
|
irp->IoStatus.Information)
|
|||
|
);
|
|||
|
|
|||
|
} else if (PAR_NOT_CONNECTED(deviceStatus) ||
|
|||
|
PAR_NO_CABLE(deviceStatus)) {
|
|||
|
|
|||
|
irp->IoStatus.Status = STATUS_DEVICE_NOT_CONNECTED;
|
|||
|
ParDump(
|
|||
|
PARIRPPATH,
|
|||
|
("PARALLEL: About to complete IRP in pusher - NOT CONN\n"
|
|||
|
"irp: %x status: %x Information: %x\n",
|
|||
|
irp,
|
|||
|
irp->IoStatus.Status,
|
|||
|
irp->IoStatus.Information)
|
|||
|
);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
IoAcquireCancelSpinLock(&cancelIrql);
|
|||
|
Extension->CurrentOpIrp = NULL;
|
|||
|
IoReleaseCancelSpinLock(cancelIrql);
|
|||
|
|
|||
|
IoCompleteRequest(
|
|||
|
irp,
|
|||
|
IO_PARALLEL_INCREMENT
|
|||
|
);
|
|||
|
|
|||
|
ParFreePort(Extension);
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// 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(Extension);
|
|||
|
portFree = TRUE;
|
|||
|
|
|||
|
KeDelayExecutionThread(KernelMode, FALSE, &Extension->OneSecond);
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARINITDEV,
|
|||
|
("PARALLEL: Did delay thread of one second\n")
|
|||
|
);
|
|||
|
|
|||
|
countDown--;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
if (Extension->QueryNumWaiters(Extension->PortContext)) {
|
|||
|
ParFreePort(Extension);
|
|||
|
portFree = TRUE;
|
|||
|
} else {
|
|||
|
portFree = FALSE;
|
|||
|
}
|
|||
|
|
|||
|
KeQueryTickCount(&nextQuery);
|
|||
|
|
|||
|
difference.QuadPart = nextQuery.QuadPart - startOfSpin.QuadPart;
|
|||
|
|
|||
|
if (difference.QuadPart*KeQueryTimeIncrement() >=
|
|||
|
Extension->AbsoluteOneSecond.QuadPart) {
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARTHREAD,
|
|||
|
("PARALLEL: Countdown: %d - device Status: %x lowpart: %x highpart: %x\n",
|
|||
|
countDown,deviceStatus,difference.LowPart,difference.HighPart)
|
|||
|
);
|
|||
|
countDown--;
|
|||
|
doDelays = TRUE;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
if (countDown <= 0) {
|
|||
|
irp->IoStatus.Status = STATUS_DEVICE_BUSY;
|
|||
|
irp->IoStatus.Information =
|
|||
|
irpSp->Parameters.Write.Length - bytesToWrite;
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARIRPPATH,
|
|||
|
("PARALLEL: About to complete IRP in pusher - T-OUT\n"
|
|||
|
"irp: %x status: %x Information: %x\n",
|
|||
|
irp,
|
|||
|
irp->IoStatus.Status,
|
|||
|
irp->IoStatus.Information)
|
|||
|
);
|
|||
|
IoAcquireCancelSpinLock(&cancelIrql);
|
|||
|
Extension->CurrentOpIrp = NULL;
|
|||
|
IoReleaseCancelSpinLock(cancelIrql);
|
|||
|
|
|||
|
IoCompleteRequest(
|
|||
|
irp,
|
|||
|
IO_PARALLEL_INCREMENT
|
|||
|
);
|
|||
|
|
|||
|
if (!portFree) {
|
|||
|
ParFreePort(Extension);
|
|||
|
}
|
|||
|
|
|||
|
return;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
if (portFree && !ParAllocPort(Extension)) {
|
|||
|
IoAcquireCancelSpinLock(&cancelIrql);
|
|||
|
Extension->CurrentOpIrp = NULL;
|
|||
|
IoReleaseCancelSpinLock(cancelIrql);
|
|||
|
irp->IoStatus.Information =
|
|||
|
irpSp->Parameters.Write.Length - bytesToWrite;
|
|||
|
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
deviceStatus = GetStatus(Extension->Controller);
|
|||
|
|
|||
|
} while ((!PAR_ONLINE(deviceStatus)) &&
|
|||
|
(!PAR_PAPER_EMPTY(deviceStatus)) &&
|
|||
|
(!PAR_POWERED_OFF(deviceStatus)) &&
|
|||
|
(!PAR_NOT_CONNECTED(deviceStatus)) &&
|
|||
|
(!PAR_NO_CABLE(deviceStatus)) &&
|
|||
|
!irp->Cancel);
|
|||
|
|
|||
|
if (countDown != (LONG)timerStart) {
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARTHREAD,
|
|||
|
("PARALLEL: Leaving busy loop - countdown %d status %x\n",
|
|||
|
countDown,deviceStatus)
|
|||
|
);
|
|||
|
|
|||
|
}
|
|||
|
goto PushSomeBytes;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
return;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
UCHAR
|
|||
|
ParManageIoDevice(
|
|||
|
IN PDEVICE_EXTENSION Extension,
|
|||
|
OUT PUCHAR Status,
|
|||
|
OUT PUCHAR Control
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description :
|
|||
|
|
|||
|
This routine does the IoControl commands.
|
|||
|
|
|||
|
Arguments :
|
|||
|
|
|||
|
Extension - The parallel device extension.
|
|||
|
|
|||
|
Status - The pointer to the location to return the device status.
|
|||
|
|
|||
|
Control - The pointer to the location to return the device control.
|
|||
|
|
|||
|
Return Value :
|
|||
|
|
|||
|
NONE.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PIO_STACK_LOCATION irpSp =
|
|||
|
IoGetCurrentIrpStackLocation(Extension->CurrentOpIrp);
|
|||
|
|
|||
|
if (irpSp->Parameters.DeviceIoControl.IoControlCode ==
|
|||
|
IOCTL_PAR_SET_INFORMATION) {
|
|||
|
|
|||
|
PPAR_SET_INFORMATION irpBuffer =
|
|||
|
Extension->CurrentOpIrp->AssociatedIrp.SystemBuffer;
|
|||
|
|
|||
|
Extension->Initialized = FALSE;
|
|||
|
*Status = ParReinitializeDevice(Extension);
|
|||
|
|
|||
|
} else if (irpSp->Parameters.DeviceIoControl.IoControlCode ==
|
|||
|
IOCTL_PAR_QUERY_INFORMATION) {
|
|||
|
|
|||
|
*Status = GetStatus(Extension->Controller);
|
|||
|
*Control = GetControl(Extension->Controller);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
return *Status;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
ParTerminateNibbleMode(
|
|||
|
IN PUCHAR Controller
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine terminates the interface back to compatibility mode.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Controller - Supplies the parallel port's controller address.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
LARGE_INTEGER wait35ms, start, end;
|
|||
|
UCHAR dcr, dsr;
|
|||
|
|
|||
|
wait35ms.QuadPart = (35*10*1000) + KeQueryTimeIncrement();
|
|||
|
|
|||
|
dcr = DCR_NEUTRAL;
|
|||
|
WRITE_PORT_UCHAR(Controller + DCR_OFFSET, dcr);
|
|||
|
KeQueryTickCount(&start);
|
|||
|
for (;;) {
|
|||
|
|
|||
|
KeQueryTickCount(&end);
|
|||
|
|
|||
|
dsr = READ_PORT_UCHAR(Controller + DSR_OFFSET);
|
|||
|
if (!(dsr&DSR_PTR_CLK)) {
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
if ((end.QuadPart - start.QuadPart)*KeQueryTimeIncrement() >
|
|||
|
wait35ms.QuadPart) {
|
|||
|
|
|||
|
// We couldn't negotiate back to compatibility mode.
|
|||
|
// just terminate.
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
dcr |= DCR_NOT_HOST_BUSY;
|
|||
|
WRITE_PORT_UCHAR(Controller + DCR_OFFSET, dcr);
|
|||
|
|
|||
|
KeQueryTickCount(&start);
|
|||
|
for (;;) {
|
|||
|
|
|||
|
KeQueryTickCount(&end);
|
|||
|
|
|||
|
dsr = READ_PORT_UCHAR(Controller + DSR_OFFSET);
|
|||
|
if (dsr&DSR_PTR_CLK) {
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
if ((end.QuadPart - start.QuadPart)*KeQueryTimeIncrement() >
|
|||
|
wait35ms.QuadPart) {
|
|||
|
|
|||
|
// The required response is not there. Continue anyway.
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
dcr &= ~DCR_NOT_HOST_BUSY;
|
|||
|
WRITE_PORT_UCHAR(Controller + DCR_OFFSET, dcr);
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ParEnterNibbleMode(
|
|||
|
IN PUCHAR Controller,
|
|||
|
IN BOOLEAN DeviceIdRequest
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine performs 1284 negotiation with the peripheral to the
|
|||
|
nibble 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.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
UCHAR extensibility;
|
|||
|
UCHAR dsr, dcr;
|
|||
|
LARGE_INTEGER wait35ms, start, end;
|
|||
|
BOOLEAN xFlag;
|
|||
|
|
|||
|
extensibility = 0x00;
|
|||
|
|
|||
|
if (DeviceIdRequest) {
|
|||
|
extensibility |= 0x04;
|
|||
|
}
|
|||
|
|
|||
|
dcr = DCR_NEUTRAL;
|
|||
|
WRITE_PORT_UCHAR(Controller + DCR_OFFSET, dcr);
|
|||
|
KeStallExecutionProcessor(1);
|
|||
|
|
|||
|
WRITE_PORT_UCHAR(Controller + DATA_OFFSET, extensibility);
|
|||
|
KeStallExecutionProcessor(1);
|
|||
|
|
|||
|
dcr &= ~DCR_NOT_1284_ACTIVE;
|
|||
|
dcr |= DCR_NOT_HOST_BUSY;
|
|||
|
WRITE_PORT_UCHAR(Controller + DCR_OFFSET, dcr);
|
|||
|
|
|||
|
wait35ms.QuadPart = (35*10*1000) + KeQueryTimeIncrement();
|
|||
|
KeQueryTickCount(&start);
|
|||
|
for (;;) {
|
|||
|
|
|||
|
KeQueryTickCount(&end);
|
|||
|
|
|||
|
dsr = READ_PORT_UCHAR(Controller + DSR_OFFSET);
|
|||
|
if ((dsr&DSR_ACK_DATA_REQ) &&
|
|||
|
(dsr&DSR_XFLAG) &&
|
|||
|
(dsr&DSR_NOT_DATA_AVAIL) &&
|
|||
|
!(dsr&DSR_PTR_CLK)) {
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
if ((end.QuadPart - start.QuadPart)*KeQueryTimeIncrement() >
|
|||
|
wait35ms.QuadPart) {
|
|||
|
|
|||
|
dcr |= DCR_NOT_1284_ACTIVE;
|
|||
|
dcr &= ~DCR_NOT_HOST_BUSY;
|
|||
|
WRITE_PORT_UCHAR(Controller + DCR_OFFSET, dcr);
|
|||
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
dcr |= DCR_NOT_HOST_CLK;
|
|||
|
WRITE_PORT_UCHAR(Controller + DCR_OFFSET, dcr);
|
|||
|
|
|||
|
KeStallExecutionProcessor(1);
|
|||
|
|
|||
|
dcr &= ~DCR_NOT_HOST_CLK;
|
|||
|
dcr &= ~DCR_NOT_HOST_BUSY;
|
|||
|
WRITE_PORT_UCHAR(Controller + DCR_OFFSET, dcr);
|
|||
|
|
|||
|
KeQueryTickCount(&start);
|
|||
|
for (;;) {
|
|||
|
|
|||
|
KeQueryTickCount(&end);
|
|||
|
|
|||
|
dsr = READ_PORT_UCHAR(Controller + DSR_OFFSET);
|
|||
|
if (dsr&DSR_PTR_CLK) {
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
if ((end.QuadPart - start.QuadPart)*KeQueryTimeIncrement() >
|
|||
|
wait35ms.QuadPart) {
|
|||
|
|
|||
|
dcr |= DCR_NOT_1284_ACTIVE;
|
|||
|
WRITE_PORT_UCHAR(Controller + DCR_OFFSET, dcr);
|
|||
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
xFlag = dsr&DSR_XFLAG ? TRUE : FALSE;
|
|||
|
if (extensibility && !xFlag) {
|
|||
|
|
|||
|
// The requested mode is not supported so
|
|||
|
// terminate into compatibility mode.
|
|||
|
|
|||
|
ParTerminateNibbleMode(Controller);
|
|||
|
|
|||
|
return STATUS_INVALID_DEVICE_REQUEST;
|
|||
|
}
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ParNibbleModeRead(
|
|||
|
IN PDEVICE_EXTENSION Extension,
|
|||
|
IN PVOID Buffer,
|
|||
|
IN ULONG BufferSize,
|
|||
|
OUT PULONG BytesTransfered
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine performs a 1284 nibble mode read 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.
|
|||
|
|
|||
|
BytesTransfered - Returns the number of bytes transferred.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PUCHAR controller = Extension->Controller;
|
|||
|
NTSTATUS status;
|
|||
|
PUCHAR p = Buffer;
|
|||
|
LARGE_INTEGER wait35ms, start, end;
|
|||
|
UCHAR dsr, dcr, nibble[2];
|
|||
|
ULONG i, j;
|
|||
|
|
|||
|
// Read nibbles according to 1284 spec.
|
|||
|
|
|||
|
wait35ms.QuadPart = (35*10*1000) + KeQueryTimeIncrement();
|
|||
|
dcr = DCR_RESERVED | DCR_NOT_INIT;
|
|||
|
for (i = 0; i < BufferSize; i++) {
|
|||
|
|
|||
|
dsr = READ_PORT_UCHAR(controller + DSR_OFFSET);
|
|||
|
|
|||
|
if (dsr&DSR_NOT_DATA_AVAIL) {
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
for (j = 0; j < 2; j++) {
|
|||
|
|
|||
|
dcr |= DCR_NOT_HOST_BUSY;
|
|||
|
WRITE_PORT_UCHAR(controller + DCR_OFFSET, dcr);
|
|||
|
|
|||
|
KeQueryTickCount(&start);
|
|||
|
for (;;) {
|
|||
|
|
|||
|
KeQueryTickCount(&end);
|
|||
|
|
|||
|
dsr = READ_PORT_UCHAR(controller + DSR_OFFSET);
|
|||
|
if (!(dsr&DSR_PTR_CLK)) {
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
if ((end.QuadPart - start.QuadPart)*KeQueryTimeIncrement() >
|
|||
|
wait35ms.QuadPart) {
|
|||
|
|
|||
|
dcr &= ~DCR_NOT_HOST_BUSY;
|
|||
|
WRITE_PORT_UCHAR(controller + DCR_OFFSET, dcr);
|
|||
|
return STATUS_IO_DEVICE_ERROR;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
nibble[j] = READ_PORT_UCHAR(controller + DSR_OFFSET);
|
|||
|
|
|||
|
dcr &= ~DCR_NOT_HOST_BUSY;
|
|||
|
WRITE_PORT_UCHAR(controller + DCR_OFFSET, dcr);
|
|||
|
|
|||
|
KeQueryTickCount(&start);
|
|||
|
for (;;) {
|
|||
|
|
|||
|
KeQueryTickCount(&end);
|
|||
|
|
|||
|
dsr = READ_PORT_UCHAR(controller + DSR_OFFSET);
|
|||
|
if (dsr&DSR_PTR_CLK) {
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
if ((end.QuadPart - start.QuadPart)*KeQueryTimeIncrement() >
|
|||
|
wait35ms.QuadPart) {
|
|||
|
|
|||
|
return STATUS_IO_DEVICE_ERROR;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
p[i] = (((nibble[0]&0x38)>>3)&0x07) |
|
|||
|
((nibble[0]&0x80) ? 0x00 : 0x08);
|
|||
|
p[i] |= (((nibble[1]&0x38)<<1)&0x70) |
|
|||
|
((nibble[1]&0x80) ? 0x00 : 0x80);
|
|||
|
}
|
|||
|
|
|||
|
*BytesTransfered = i;
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ParQueryDeviceId(
|
|||
|
IN PDEVICE_EXTENSION Extension,
|
|||
|
OUT PUCHAR DeviceIdBuffer,
|
|||
|
IN ULONG BufferSize,
|
|||
|
OUT PULONG DeviceIdSize
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine queries the 1284 device id from the device.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Extension - Supplies the device extension.
|
|||
|
|
|||
|
DeviceIdBuffer - Supplies a buffer to receive the device id string.
|
|||
|
|
|||
|
BufferSize - Supplies the number of bytes in the buffer.
|
|||
|
|
|||
|
DeviceIdSize - Returns the number of bytes in the device id string.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_SUCCESS - Success.
|
|||
|
|
|||
|
STATUS_BUFFER_TOO_SMALL - The device id was not returned because the buffer
|
|||
|
was too small. 'DeviceIdSize' will return the
|
|||
|
required size of the buffer.
|
|||
|
|
|||
|
otherwise - Failure.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PUCHAR controller = Extension->Controller;
|
|||
|
NTSTATUS status;
|
|||
|
UCHAR sizeBuf[2];
|
|||
|
ULONG numBytes;
|
|||
|
USHORT size;
|
|||
|
|
|||
|
*DeviceIdSize = 0;
|
|||
|
|
|||
|
// Try to negotiate the peripheral into nibble mode device id request.
|
|||
|
|
|||
|
status = ParEnterNibbleMode(controller, TRUE);
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
// Try to read the Device id from nibble mode.
|
|||
|
|
|||
|
status = ParNibbleModeRead(Extension, sizeBuf, 2, &numBytes);
|
|||
|
if (NT_SUCCESS(status) && numBytes != 2) {
|
|||
|
status = STATUS_IO_DEVICE_ERROR;
|
|||
|
}
|
|||
|
|
|||
|
if (NT_SUCCESS(status)) {
|
|||
|
|
|||
|
size = sizeBuf[0]*0x100 + sizeBuf[1];
|
|||
|
*DeviceIdSize = size - sizeof(USHORT);
|
|||
|
if (*DeviceIdSize > BufferSize) {
|
|||
|
status = STATUS_BUFFER_TOO_SMALL;
|
|||
|
}
|
|||
|
|
|||
|
if (NT_SUCCESS(status)) {
|
|||
|
status = ParNibbleModeRead(Extension, DeviceIdBuffer,
|
|||
|
*DeviceIdSize, &numBytes);
|
|||
|
|
|||
|
if (NT_SUCCESS(status) && numBytes != *DeviceIdSize) {
|
|||
|
status = STATUS_IO_DEVICE_ERROR;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
ParTerminateNibbleMode(controller);
|
|||
|
|
|||
|
WRITE_PORT_UCHAR(controller + DCR_OFFSET, DCR_NEUTRAL);
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
ParReadIo(
|
|||
|
IN PDEVICE_EXTENSION Extension
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine implements a READ request with the extension's current irp.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Extension - Supplies the device extension.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PIRP irp = Extension->CurrentOpIrp;
|
|||
|
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(irp);
|
|||
|
KIRQL cancelIrql;
|
|||
|
UCHAR deviceStatus;
|
|||
|
NTSTATUS status;
|
|||
|
ULONG bytes;
|
|||
|
|
|||
|
if (!Extension->Initialized) {
|
|||
|
deviceStatus = ParReinitializeDevice(Extension);
|
|||
|
}
|
|||
|
|
|||
|
if (!Extension->Initialized) {
|
|||
|
|
|||
|
// The device didn't initialize, so complete with error.
|
|||
|
|
|||
|
ParNotInitError(Extension, deviceStatus);
|
|||
|
IoAcquireCancelSpinLock(&cancelIrql);
|
|||
|
Extension->CurrentOpIrp = NULL;
|
|||
|
IoReleaseCancelSpinLock(cancelIrql);
|
|||
|
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
|||
|
ParFreePort(Extension);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
bytes = 0;
|
|||
|
status = ParEnterNibbleMode(Extension->Controller, FALSE);
|
|||
|
if (NT_SUCCESS(status)) {
|
|||
|
status = ParNibbleModeRead(Extension,
|
|||
|
irp->AssociatedIrp.SystemBuffer,
|
|||
|
irpSp->Parameters.Read.Length,
|
|||
|
&bytes);
|
|||
|
if (NT_SUCCESS(status)) {
|
|||
|
ParTerminateNibbleMode(Extension->Controller);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
irp->IoStatus.Status = status;
|
|||
|
irp->IoStatus.Information = bytes;
|
|||
|
IoAcquireCancelSpinLock(&cancelIrql);
|
|||
|
Extension->CurrentOpIrp = NULL;
|
|||
|
IoReleaseCancelSpinLock(cancelIrql);
|
|||
|
IoCompleteRequest(irp, IO_PARALLEL_INCREMENT);
|
|||
|
ParFreePort(Extension);
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
ParStartIo(
|
|||
|
IN PDEVICE_EXTENSION Extension
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine starts an I/O operation for the driver and
|
|||
|
then returns
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Extension - The parallel device extension
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PIRP irp = Extension->CurrentOpIrp;
|
|||
|
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(irp);
|
|||
|
KIRQL cancelIrql;
|
|||
|
UCHAR deviceStatus;
|
|||
|
ULONG idLength;
|
|||
|
NTSTATUS ntStatus;
|
|||
|
|
|||
|
// Allocate the port.
|
|||
|
|
|||
|
if (!ParAllocPort(Extension)) {
|
|||
|
|
|||
|
// If the allocation didn't succeed then fail this IRP.
|
|||
|
|
|||
|
IoAcquireCancelSpinLock(&cancelIrql);
|
|||
|
Extension->CurrentOpIrp = NULL;
|
|||
|
IoReleaseCancelSpinLock(cancelIrql);
|
|||
|
irp->IoStatus.Information = 0;
|
|||
|
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARIRPPATH,
|
|||
|
("PARALLEL: In startio with IRP: %x\n",
|
|||
|
irp)
|
|||
|
);
|
|||
|
if (irpSp->MajorFunction == IRP_MJ_WRITE) {
|
|||
|
|
|||
|
UCHAR deviceStatus;
|
|||
|
if (!Extension->Initialized) {
|
|||
|
|
|||
|
deviceStatus = ParReinitializeDevice(Extension);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
if (!Extension->Initialized) {
|
|||
|
|
|||
|
ParNotInitError(
|
|||
|
Extension,
|
|||
|
deviceStatus
|
|||
|
);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
ParWriteOutData(
|
|||
|
Extension
|
|||
|
);
|
|||
|
return;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
} else if (irpSp->MajorFunction == IRP_MJ_READ) {
|
|||
|
|
|||
|
ParReadIo(Extension);
|
|||
|
return;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
UCHAR status;
|
|||
|
UCHAR control;
|
|||
|
|
|||
|
ParManageIoDevice(
|
|||
|
Extension,
|
|||
|
&status,
|
|||
|
&control
|
|||
|
);
|
|||
|
|
|||
|
if (irpSp->Parameters.DeviceIoControl.IoControlCode ==
|
|||
|
IOCTL_PAR_SET_INFORMATION) {
|
|||
|
|
|||
|
if (!Extension->Initialized) {
|
|||
|
|
|||
|
ParNotInitError(
|
|||
|
Extension,
|
|||
|
status
|
|||
|
);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
irp->IoStatus.Status = STATUS_SUCCESS;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
} else if (irpSp->Parameters.DeviceIoControl.IoControlCode ==
|
|||
|
IOCTL_PAR_QUERY_INFORMATION) {
|
|||
|
|
|||
|
PPAR_QUERY_INFORMATION irpBuffer = irp->AssociatedIrp.SystemBuffer;
|
|||
|
|
|||
|
irp->IoStatus.Status = STATUS_SUCCESS;
|
|||
|
|
|||
|
//
|
|||
|
// Interpretating Status & Control
|
|||
|
//
|
|||
|
|
|||
|
irpBuffer->Status = 0x0;
|
|||
|
|
|||
|
if (PAR_POWERED_OFF(status) ||
|
|||
|
PAR_NO_CABLE(status)) {
|
|||
|
|
|||
|
irpBuffer->Status =
|
|||
|
(UCHAR)(irpBuffer->Status | PARALLEL_POWER_OFF);
|
|||
|
|
|||
|
} else if (PAR_PAPER_EMPTY(status)) {
|
|||
|
|
|||
|
irpBuffer->Status =
|
|||
|
(UCHAR)(irpBuffer->Status | PARALLEL_PAPER_EMPTY);
|
|||
|
|
|||
|
} else if (PAR_OFF_LINE(status)) {
|
|||
|
|
|||
|
irpBuffer->Status =
|
|||
|
(UCHAR)(irpBuffer->Status | PARALLEL_OFF_LINE);
|
|||
|
|
|||
|
} else if (PAR_NOT_CONNECTED(status)) {
|
|||
|
|
|||
|
irpBuffer->Status =
|
|||
|
(UCHAR)(irpBuffer->Status | PARALLEL_NOT_CONNECTED);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
if (PAR_BUSY(status)) {
|
|||
|
|
|||
|
irpBuffer->Status =
|
|||
|
(UCHAR)(irpBuffer->Status | PARALLEL_BUSY);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
if (PAR_SELECTED(status)) {
|
|||
|
|
|||
|
irpBuffer->Status =
|
|||
|
(UCHAR)(irpBuffer->Status | PARALLEL_SELECTED);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
irp->IoStatus.Information =
|
|||
|
sizeof( PAR_QUERY_INFORMATION );
|
|||
|
|
|||
|
} else if (irpSp->Parameters.DeviceIoControl.IoControlCode ==
|
|||
|
IOCTL_PAR_QUERY_DEVICE_ID) {
|
|||
|
|
|||
|
if (!Extension->Initialized) {
|
|||
|
deviceStatus = ParReinitializeDevice(Extension);
|
|||
|
}
|
|||
|
|
|||
|
if (!Extension->Initialized) {
|
|||
|
ParNotInitError(Extension, deviceStatus);
|
|||
|
} else {
|
|||
|
|
|||
|
ntStatus = ParQueryDeviceId(Extension,
|
|||
|
irp->AssociatedIrp.SystemBuffer,
|
|||
|
irpSp->Parameters.DeviceIoControl.OutputBufferLength,
|
|||
|
&idLength);
|
|||
|
|
|||
|
irp->IoStatus.Status = ntStatus;
|
|||
|
if (NT_SUCCESS(ntStatus)) {
|
|||
|
irp->IoStatus.Information = idLength;
|
|||
|
} else {
|
|||
|
irp->IoStatus.Information = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
} else if (irpSp->Parameters.DeviceIoControl.IoControlCode ==
|
|||
|
IOCTL_PAR_QUERY_DEVICE_ID_SIZE) {
|
|||
|
|
|||
|
if (!Extension->Initialized) {
|
|||
|
deviceStatus = ParReinitializeDevice(Extension);
|
|||
|
}
|
|||
|
|
|||
|
if (!Extension->Initialized) {
|
|||
|
ParNotInitError(Extension, deviceStatus);
|
|||
|
} else {
|
|||
|
|
|||
|
ntStatus = ParQueryDeviceId(Extension, NULL, 0, &idLength);
|
|||
|
if (ntStatus == STATUS_BUFFER_TOO_SMALL) {
|
|||
|
irp->IoStatus.Status = STATUS_SUCCESS;
|
|||
|
irp->IoStatus.Information =
|
|||
|
sizeof(PAR_DEVICE_ID_SIZE_INFORMATION);
|
|||
|
((PPAR_DEVICE_ID_SIZE_INFORMATION)
|
|||
|
irp->AssociatedIrp.SystemBuffer)->DeviceIdSize = idLength;
|
|||
|
|
|||
|
} else {
|
|||
|
irp->IoStatus.Status = ntStatus;
|
|||
|
irp->IoStatus.Information = 0;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
PSERIAL_TIMEOUTS new = irp->AssociatedIrp.SystemBuffer;
|
|||
|
|
|||
|
//
|
|||
|
// The only other thing let through is setting
|
|||
|
// the timer start.
|
|||
|
//
|
|||
|
|
|||
|
Extension->TimerStart = new->WriteTotalTimeoutConstant / 1000;
|
|||
|
irp->IoStatus.Status = STATUS_SUCCESS;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARIRPPATH,
|
|||
|
("PARALLEL: About to complete IRP in startio\n"
|
|||
|
"Irp: %x status: %x Information: %x\n",
|
|||
|
irp,
|
|||
|
irp->IoStatus.Status,
|
|||
|
irp->IoStatus.Information)
|
|||
|
);
|
|||
|
|
|||
|
IoAcquireCancelSpinLock(&cancelIrql);
|
|||
|
Extension->CurrentOpIrp = NULL;
|
|||
|
IoReleaseCancelSpinLock(cancelIrql);
|
|||
|
|
|||
|
IoCompleteRequest(
|
|||
|
irp,
|
|||
|
IO_NO_INCREMENT
|
|||
|
);
|
|||
|
|
|||
|
ParFreePort(Extension);
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
ParallelThread(
|
|||
|
IN PVOID Context
|
|||
|
)
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
PDEVICE_EXTENSION extension = Context;
|
|||
|
LONG threadPriority = -2;
|
|||
|
KIRQL oldIrql;
|
|||
|
|
|||
|
//
|
|||
|
// Lower ourselves down just at tad so that we compete a
|
|||
|
// little less.
|
|||
|
// If the registry indicates we should be running at the old
|
|||
|
// priority, don't lower our priority as much.
|
|||
|
//
|
|||
|
|
|||
|
if(extension->UseNT35Priority) {
|
|||
|
threadPriority = -1;
|
|||
|
}
|
|||
|
|
|||
|
KeSetBasePriorityThread(
|
|||
|
KeGetCurrentThread(),
|
|||
|
threadPriority
|
|||
|
);
|
|||
|
|
|||
|
do {
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Wait for a request from the dispatch routines.
|
|||
|
// KeWaitForSingleObject won't return error here - this thread
|
|||
|
// isn't alertable and won't take APCs, and we're not passing in
|
|||
|
// a timeout.
|
|||
|
//
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARTHREAD,
|
|||
|
("PARALLEL: semaphore state before wait - %d\n",
|
|||
|
extension->RequestSemaphore.Header.SignalState)
|
|||
|
);
|
|||
|
KeWaitForSingleObject(
|
|||
|
&extension->RequestSemaphore,
|
|||
|
UserRequest,
|
|||
|
KernelMode,
|
|||
|
FALSE,
|
|||
|
NULL
|
|||
|
);
|
|||
|
ParDump(
|
|||
|
PARTHREAD,
|
|||
|
("PARALLEL: semaphore state after wait - %d\n",
|
|||
|
extension->RequestSemaphore.Header.SignalState)
|
|||
|
);
|
|||
|
|
|||
|
if ( extension->TimeToTerminateThread ) {
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARCONFIG,
|
|||
|
("PARALLEL: Thread asked to kill itself\n")
|
|||
|
);
|
|||
|
|
|||
|
PsTerminateSystemThread( STATUS_SUCCESS );
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// While we are manipulating the queue we capture the
|
|||
|
// cancel spin lock.
|
|||
|
//
|
|||
|
|
|||
|
IoAcquireCancelSpinLock(&oldIrql);
|
|||
|
|
|||
|
ASSERT(!extension->CurrentOpIrp);
|
|||
|
while (!IsListEmpty(&extension->WorkQueue)) {
|
|||
|
|
|||
|
PLIST_ENTRY headOfList;
|
|||
|
PIRP currentIrp;
|
|||
|
|
|||
|
headOfList = RemoveHeadList(&extension->WorkQueue);
|
|||
|
currentIrp = CONTAINING_RECORD(
|
|||
|
headOfList,
|
|||
|
IRP,
|
|||
|
Tail.Overlay.ListEntry
|
|||
|
);
|
|||
|
|
|||
|
IoSetCancelRoutine(
|
|||
|
currentIrp,
|
|||
|
NULL
|
|||
|
);
|
|||
|
|
|||
|
extension->CurrentOpIrp = currentIrp;
|
|||
|
IoReleaseCancelSpinLock(oldIrql);
|
|||
|
|
|||
|
//
|
|||
|
// Do the Io.
|
|||
|
//
|
|||
|
|
|||
|
ParStartIo(
|
|||
|
extension
|
|||
|
);
|
|||
|
|
|||
|
IoAcquireCancelSpinLock(&oldIrql);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
IoReleaseCancelSpinLock(oldIrql);
|
|||
|
|
|||
|
} while (TRUE);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ParCreateSystemThread(
|
|||
|
PDEVICE_EXTENSION Extension
|
|||
|
)
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS status;
|
|||
|
HANDLE threadHandle;
|
|||
|
|
|||
|
//
|
|||
|
// Start the thread and capture the thread handle into the extension
|
|||
|
//
|
|||
|
|
|||
|
status = PsCreateSystemThread(
|
|||
|
&threadHandle,
|
|||
|
THREAD_ALL_ACCESS,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
NULL,
|
|||
|
ParallelThread,
|
|||
|
Extension
|
|||
|
);
|
|||
|
|
|||
|
if (!NT_ERROR(status)) {
|
|||
|
|
|||
|
//
|
|||
|
// We've got the thread. Now get a pointer to it.
|
|||
|
//
|
|||
|
|
|||
|
status = ObReferenceObjectByHandle(
|
|||
|
threadHandle,
|
|||
|
THREAD_ALL_ACCESS,
|
|||
|
NULL,
|
|||
|
KernelMode,
|
|||
|
&Extension->ThreadObjectPointer,
|
|||
|
NULL
|
|||
|
);
|
|||
|
|
|||
|
if (NT_ERROR(status)) {
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARIRPPATH,
|
|||
|
("PARALLEL: Bad status on open from ref by handle: %x\n",
|
|||
|
status)
|
|||
|
);
|
|||
|
|
|||
|
Extension->TimeToTerminateThread = TRUE;
|
|||
|
KeReleaseSemaphore(
|
|||
|
&Extension->RequestSemaphore,
|
|||
|
0,
|
|||
|
1,
|
|||
|
FALSE
|
|||
|
);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// Now that we have a reference to the thread
|
|||
|
// we can simply close the handle.
|
|||
|
//
|
|||
|
|
|||
|
ZwClose(threadHandle);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARIRPPATH,
|
|||
|
("PARALLEL: Bad status on open from ref by handle: %x\n",
|
|||
|
status)
|
|||
|
);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ParCreateOpen(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is the dispatch for a create requests.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - Supplies the device object.
|
|||
|
|
|||
|
Irp - Supplies the I/O request packet.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_SUCCESS - Success.
|
|||
|
!STATUS_SUCCESS - Failure.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS returnStatus;
|
|||
|
PDEVICE_EXTENSION extension = DeviceObject->DeviceExtension;
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARIRPPATH,
|
|||
|
("PARALLEL: In create/open with IRP: %x\n",
|
|||
|
Irp)
|
|||
|
);
|
|||
|
|
|||
|
Irp->IoStatus.Information = 0;
|
|||
|
|
|||
|
// Lock in code.
|
|||
|
|
|||
|
ParClaimDriver();
|
|||
|
|
|||
|
// Lock in the port driver.
|
|||
|
|
|||
|
ParGetPortInfoFromPortDevice(extension);
|
|||
|
|
|||
|
extension->TimeToTerminateThread = FALSE;
|
|||
|
extension->ThreadObjectPointer = NULL;
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARTHREAD,
|
|||
|
("PARALLEL: open initializing - state before init - %d\n",
|
|||
|
extension->RequestSemaphore.Header.SignalState)
|
|||
|
);
|
|||
|
|
|||
|
KeInitializeSemaphore(&extension->RequestSemaphore, 0, MAXLONG);
|
|||
|
|
|||
|
if (IoGetCurrentIrpStackLocation(Irp)->Parameters.Create.Options
|
|||
|
& FILE_DIRECTORY_FILE) {
|
|||
|
|
|||
|
returnStatus = Irp->IoStatus.Status = STATUS_NOT_A_DIRECTORY;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
returnStatus = Irp->IoStatus.Status = ParCreateSystemThread(extension);
|
|||
|
}
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARIRPPATH,
|
|||
|
("PARALLEL: About to complete IRP in create/open\n"
|
|||
|
"Irp: %x status: %x Information: %x\n",
|
|||
|
Irp,
|
|||
|
Irp->IoStatus.Status,
|
|||
|
Irp->IoStatus.Information)
|
|||
|
);
|
|||
|
|
|||
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|||
|
|
|||
|
return returnStatus;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ParClose(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is the dispatch for a close requests.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - Supplies the device object.
|
|||
|
|
|||
|
Irp - Supplies the I/O request packet.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_SUCCESS - Success.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION extension;
|
|||
|
NTSTATUS statusOfWait;
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARIRPPATH,
|
|||
|
("PARALLEL: In close with IRP: %x\n",
|
|||
|
Irp)
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// Set the semaphore that will wake up the thread, which
|
|||
|
// will then notice that the thread is supposed to die.
|
|||
|
//
|
|||
|
|
|||
|
extension = DeviceObject->DeviceExtension;
|
|||
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|||
|
Irp->IoStatus.Information = 0;
|
|||
|
extension->TimeToTerminateThread = TRUE;
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARTHREAD,
|
|||
|
("PARALLEL: close releasing - state before release - %d\n",
|
|||
|
extension->RequestSemaphore.Header.SignalState)
|
|||
|
);
|
|||
|
|
|||
|
KeReleaseSemaphore(&extension->RequestSemaphore, 0, 1, FALSE);
|
|||
|
|
|||
|
//
|
|||
|
// Wait on the thread handle, when the wait is satisfied, the
|
|||
|
// thread has gone away.
|
|||
|
//
|
|||
|
|
|||
|
statusOfWait = KeWaitForSingleObject(extension->ThreadObjectPointer,
|
|||
|
UserRequest, KernelMode, FALSE, NULL);
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARTHREAD,
|
|||
|
("PARALLEL: return status of waiting for thread to die: %x\n",
|
|||
|
statusOfWait)
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// Thread is gone. Status is successful for the close.
|
|||
|
// Defreference the pointer to the thread object.
|
|||
|
//
|
|||
|
|
|||
|
ObDereferenceObject(extension->ThreadObjectPointer);
|
|||
|
extension->ThreadObjectPointer = NULL;
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARIRPPATH,
|
|||
|
("PARALLEL: About to complete IRP in close\n"
|
|||
|
"Irp: %x status: %x Information: %x\n",
|
|||
|
Irp,
|
|||
|
Irp->IoStatus.Status,
|
|||
|
Irp->IoStatus.Information)
|
|||
|
);
|
|||
|
|
|||
|
// Allow the port driver to be paged.
|
|||
|
|
|||
|
ParReleasePortInfoToPortDevice(extension);
|
|||
|
|
|||
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|||
|
|
|||
|
|
|||
|
// Unlock the code that was locked during the open.
|
|||
|
|
|||
|
ParReleaseDriver();
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ParCleanup(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is the dispatch for a cleanup requests.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - Supplies the device object.
|
|||
|
|
|||
|
Irp - Supplies the I/O request packet.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_SUCCESS - Success.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION extension;
|
|||
|
KIRQL cancelIrql;
|
|||
|
PDRIVER_CANCEL cancelRoutine;
|
|||
|
PIRP currentLastIrp;
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARIRPPATH,
|
|||
|
("PARALLEL: In cleanup with IRP: %x\n",
|
|||
|
Irp)
|
|||
|
);
|
|||
|
|
|||
|
extension = DeviceObject->DeviceExtension;
|
|||
|
|
|||
|
//
|
|||
|
// While the list is not empty, go through and cancel each irp.
|
|||
|
//
|
|||
|
|
|||
|
IoAcquireCancelSpinLock(&cancelIrql);
|
|||
|
|
|||
|
//
|
|||
|
// Clean the list from back to front.
|
|||
|
//
|
|||
|
|
|||
|
while (!IsListEmpty(&extension->WorkQueue)) {
|
|||
|
|
|||
|
currentLastIrp = CONTAINING_RECORD(extension->WorkQueue.Blink,
|
|||
|
IRP, Tail.Overlay.ListEntry);
|
|||
|
|
|||
|
RemoveEntryList(extension->WorkQueue.Blink);
|
|||
|
|
|||
|
cancelRoutine = currentLastIrp->CancelRoutine;
|
|||
|
currentLastIrp->CancelIrql = cancelIrql;
|
|||
|
currentLastIrp->CancelRoutine = NULL;
|
|||
|
currentLastIrp->Cancel = TRUE;
|
|||
|
|
|||
|
cancelRoutine(DeviceObject, currentLastIrp);
|
|||
|
|
|||
|
IoAcquireCancelSpinLock(&cancelIrql);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If there is a current irp then mark it as cancelled.
|
|||
|
//
|
|||
|
|
|||
|
if (extension->CurrentOpIrp) {
|
|||
|
extension->CurrentOpIrp->Cancel = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
IoReleaseCancelSpinLock(cancelIrql);
|
|||
|
|
|||
|
Irp->IoStatus.Information = 0;
|
|||
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARIRPPATH,
|
|||
|
("PARALLEL: About to complete IRP in cleanup\n"
|
|||
|
"Irp: %x status: %x Information: %x\n",
|
|||
|
Irp,
|
|||
|
Irp->IoStatus.Status,
|
|||
|
Irp->IoStatus.Information)
|
|||
|
);
|
|||
|
|
|||
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
ParCancelRequest(
|
|||
|
PDEVICE_OBJECT DeviceObject,
|
|||
|
PIRP Irp
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is used to cancel any request in the parallel driver.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - Pointer to the device object for this device
|
|||
|
|
|||
|
Irp - Pointer to the IRP to be canceled.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
//
|
|||
|
// The only reason that this irp can be on the queue is
|
|||
|
// if it's not the current irp. Pull it off the queue
|
|||
|
// and complete it as canceled.
|
|||
|
//
|
|||
|
|
|||
|
ASSERT(!IsListEmpty(&Irp->Tail.Overlay.ListEntry));
|
|||
|
|
|||
|
RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
|
|||
|
IoReleaseCancelSpinLock(Irp->CancelIrql);
|
|||
|
Irp->IoStatus.Status = STATUS_CANCELLED;
|
|||
|
Irp->IoStatus.Information = 0;
|
|||
|
ParDump(
|
|||
|
PARIRPPATH,
|
|||
|
("PARALLEL: About to complete IRP in cancel routine\n"
|
|||
|
"Irp: %x status: %x Information: %x\n",
|
|||
|
Irp,
|
|||
|
Irp->IoStatus.Status,
|
|||
|
Irp->IoStatus.Information)
|
|||
|
);
|
|||
|
IoCompleteRequest(
|
|||
|
Irp,
|
|||
|
IO_NO_INCREMENT
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ParAllocPortCompletionRoutine(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp,
|
|||
|
IN PVOID Event
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is the completion routine for a port allocate request.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - Supplies the device object.
|
|||
|
Irp - Supplies the I/O request packet.
|
|||
|
Context - Supplies the notification event.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_MORE_PROCESSING_REQUIRED - The Irp still requires processing.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
KeSetEvent((PKEVENT) Event, 0, FALSE);
|
|||
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
ParAllocPort(
|
|||
|
IN PDEVICE_EXTENSION Extension
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine takes the given Irp and sends it down as a port allocate
|
|||
|
request. When this request completes, the Irp will be queued for
|
|||
|
processing.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Extension - Supplies the device extension.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
FALSE - The port was not successfully allocated.
|
|||
|
TRUE - The port was successfully allocated.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PIO_STACK_LOCATION nextSp;
|
|||
|
KEVENT event;
|
|||
|
PIRP irp = Extension->CurrentOpIrp;
|
|||
|
BOOLEAN b;
|
|||
|
NTSTATUS status;
|
|||
|
LARGE_INTEGER timeout;
|
|||
|
|
|||
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|||
|
|
|||
|
nextSp = IoGetNextIrpStackLocation(irp);
|
|||
|
nextSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
|
|||
|
nextSp->Parameters.DeviceIoControl.IoControlCode =
|
|||
|
IOCTL_INTERNAL_PARALLEL_PORT_ALLOCATE;
|
|||
|
|
|||
|
IoSetCompletionRoutine(irp, ParAllocPortCompletionRoutine, &event,
|
|||
|
TRUE, TRUE, TRUE);
|
|||
|
|
|||
|
IoCallDriver(Extension->PortDeviceObject, irp);
|
|||
|
|
|||
|
timeout.QuadPart = -((LONGLONG) Extension->TimerStart*10*1000*1000);
|
|||
|
|
|||
|
status = KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, &timeout);
|
|||
|
|
|||
|
if (status == STATUS_TIMEOUT) {
|
|||
|
IoCancelIrp(irp);
|
|||
|
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|||
|
}
|
|||
|
|
|||
|
b = NT_SUCCESS(irp->IoStatus.Status);
|
|||
|
if (!b) {
|
|||
|
irp->IoStatus.Status = STATUS_DEVICE_BUSY;
|
|||
|
}
|
|||
|
|
|||
|
return b;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ParReadWrite(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is the dispatch for read and write requests.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - Supplies the device object.
|
|||
|
|
|||
|
Irp - Supplies the I/O request packet.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_SUCCESS - Success.
|
|||
|
STATUS_PENDING - Request pending.
|
|||
|
STATUS_INVALID_PARAMETER - Invalid parameter.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PIO_STACK_LOCATION irpSp;
|
|||
|
NTSTATUS status;
|
|||
|
PDEVICE_EXTENSION extension;
|
|||
|
KIRQL oldIrql;
|
|||
|
|
|||
|
Irp->IoStatus.Information = 0;
|
|||
|
irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|||
|
extension = DeviceObject->DeviceExtension;
|
|||
|
|
|||
|
if ((irpSp->Parameters.Write.ByteOffset.HighPart != 0) ||
|
|||
|
(irpSp->Parameters.Write.ByteOffset.LowPart != 0)) {
|
|||
|
|
|||
|
status = STATUS_INVALID_PARAMETER;
|
|||
|
} else if (irpSp->Parameters.Write.Length == 0) {
|
|||
|
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
} else {
|
|||
|
status = STATUS_PENDING;
|
|||
|
}
|
|||
|
|
|||
|
if (status == STATUS_PENDING) {
|
|||
|
|
|||
|
IoAcquireCancelSpinLock(&oldIrql);
|
|||
|
|
|||
|
if (Irp->Cancel) {
|
|||
|
IoReleaseCancelSpinLock(oldIrql);
|
|||
|
status = STATUS_CANCELLED;
|
|||
|
} else {
|
|||
|
IoMarkIrpPending(Irp);
|
|||
|
IoSetCancelRoutine(Irp, ParCancelRequest);
|
|||
|
InsertTailList(&extension->WorkQueue, &Irp->Tail.Overlay.ListEntry);
|
|||
|
IoReleaseCancelSpinLock(oldIrql);
|
|||
|
KeReleaseSemaphore(&extension->RequestSemaphore, 0, 1, FALSE);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (status != STATUS_PENDING) {
|
|||
|
Irp->IoStatus.Status = status;
|
|||
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|||
|
}
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ParDeviceControl(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is the dispatch for device control requests.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - Supplies the device object.
|
|||
|
|
|||
|
Irp - Supplies the I/O request packet.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_SUCCESS - Success.
|
|||
|
STATUS_PENDING - Request pending.
|
|||
|
STATUS_BUFFER_TOO_SMALL - Buffer too small.
|
|||
|
STATUS_INVALID_PARAMETER - Invalid io control request.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PIO_STACK_LOCATION irpSp;
|
|||
|
PPAR_SET_INFORMATION setInfo;
|
|||
|
NTSTATUS status;
|
|||
|
PSERIAL_TIMEOUTS serialTimeouts;
|
|||
|
PDEVICE_EXTENSION extension;
|
|||
|
KIRQL oldIrql;
|
|||
|
|
|||
|
Irp->IoStatus.Information = 0;
|
|||
|
irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|||
|
extension = DeviceObject->DeviceExtension;
|
|||
|
|
|||
|
switch (irpSp->Parameters.DeviceIoControl.IoControlCode) {
|
|||
|
|
|||
|
case IOCTL_PAR_SET_INFORMATION:
|
|||
|
setInfo = Irp->AssociatedIrp.SystemBuffer;
|
|||
|
|
|||
|
if (irpSp->Parameters.DeviceIoControl.InputBufferLength <
|
|||
|
sizeof(PAR_SET_INFORMATION)) {
|
|||
|
|
|||
|
status = STATUS_BUFFER_TOO_SMALL;
|
|||
|
} else if (setInfo->Init != PARALLEL_INIT) {
|
|||
|
status = STATUS_INVALID_PARAMETER;
|
|||
|
} else {
|
|||
|
status = STATUS_PENDING;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case IOCTL_PAR_QUERY_INFORMATION :
|
|||
|
if (irpSp->Parameters.DeviceIoControl.OutputBufferLength <
|
|||
|
sizeof(PAR_QUERY_INFORMATION)) {
|
|||
|
|
|||
|
status = STATUS_BUFFER_TOO_SMALL;
|
|||
|
} else {
|
|||
|
status = STATUS_PENDING;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case IOCTL_SERIAL_SET_TIMEOUTS:
|
|||
|
serialTimeouts = Irp->AssociatedIrp.SystemBuffer;
|
|||
|
|
|||
|
if (irpSp->Parameters.DeviceIoControl.InputBufferLength <
|
|||
|
sizeof(SERIAL_TIMEOUTS)) {
|
|||
|
|
|||
|
status = STATUS_BUFFER_TOO_SMALL;
|
|||
|
} else if (serialTimeouts->WriteTotalTimeoutConstant < 2000) {
|
|||
|
status = STATUS_INVALID_PARAMETER;
|
|||
|
} else {
|
|||
|
status = STATUS_PENDING;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case IOCTL_SERIAL_GET_TIMEOUTS:
|
|||
|
serialTimeouts = Irp->AssociatedIrp.SystemBuffer;
|
|||
|
|
|||
|
if (irpSp->Parameters.DeviceIoControl.OutputBufferLength <
|
|||
|
sizeof(SERIAL_TIMEOUTS)) {
|
|||
|
|
|||
|
status = STATUS_BUFFER_TOO_SMALL;
|
|||
|
} else {
|
|||
|
|
|||
|
//
|
|||
|
// We don't need to synchronize the read.
|
|||
|
//
|
|||
|
|
|||
|
RtlZeroMemory(serialTimeouts, sizeof(SERIAL_TIMEOUTS));
|
|||
|
serialTimeouts->WriteTotalTimeoutConstant =
|
|||
|
1000*extension->TimerStart;
|
|||
|
|
|||
|
Irp->IoStatus.Information = sizeof(SERIAL_TIMEOUTS);
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case IOCTL_PAR_QUERY_DEVICE_ID:
|
|||
|
if (irpSp->Parameters.DeviceIoControl.OutputBufferLength == 0) {
|
|||
|
status = STATUS_BUFFER_TOO_SMALL;
|
|||
|
} else {
|
|||
|
status = STATUS_PENDING;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case IOCTL_PAR_QUERY_DEVICE_ID_SIZE:
|
|||
|
if (irpSp->Parameters.DeviceIoControl.OutputBufferLength <
|
|||
|
sizeof(PAR_DEVICE_ID_SIZE_INFORMATION)) {
|
|||
|
|
|||
|
status = STATUS_BUFFER_TOO_SMALL;
|
|||
|
} else {
|
|||
|
status = STATUS_PENDING;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
default :
|
|||
|
status = STATUS_INVALID_PARAMETER;
|
|||
|
break;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
if (status == STATUS_PENDING) {
|
|||
|
|
|||
|
IoAcquireCancelSpinLock(&oldIrql);
|
|||
|
|
|||
|
if (Irp->Cancel) {
|
|||
|
IoReleaseCancelSpinLock(oldIrql);
|
|||
|
status = STATUS_CANCELLED;
|
|||
|
} else {
|
|||
|
IoMarkIrpPending(Irp);
|
|||
|
IoSetCancelRoutine(Irp, ParCancelRequest);
|
|||
|
InsertTailList(&extension->WorkQueue, &Irp->Tail.Overlay.ListEntry);
|
|||
|
IoReleaseCancelSpinLock(oldIrql);
|
|||
|
KeReleaseSemaphore(&extension->RequestSemaphore, 0, 1, FALSE);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (status != STATUS_PENDING) {
|
|||
|
Irp->IoStatus.Status = status;
|
|||
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|||
|
}
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ParQueryInformationFile(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is used to query the end of file information on
|
|||
|
the opened parallel port. Any other file information request
|
|||
|
is retured with an invalid parameter.
|
|||
|
|
|||
|
This routine always returns an end of file of 0.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - Supplies the device object.
|
|||
|
|
|||
|
Irp - Supplies the I/O request packet.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_SUCCESS - Success.
|
|||
|
STATUS_INVALID_PARAMETER - Invalid file information request.
|
|||
|
STATUS_BUFFER_TOO_SMALL - Buffer too small.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS status;
|
|||
|
PIO_STACK_LOCATION irpSp;
|
|||
|
PFILE_STANDARD_INFORMATION stdInfo;
|
|||
|
PFILE_POSITION_INFORMATION posInfo;
|
|||
|
|
|||
|
UNREFERENCED_PARAMETER(DeviceObject);
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARIRPPATH,
|
|||
|
("PARALLEL: In query information file with Irp: %x\n",
|
|||
|
Irp)
|
|||
|
);
|
|||
|
|
|||
|
Irp->IoStatus.Information = 0;
|
|||
|
irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|||
|
|
|||
|
switch (irpSp->Parameters.QueryFile.FileInformationClass) {
|
|||
|
|
|||
|
case FileStandardInformation:
|
|||
|
if (irpSp->Parameters.QueryFile.Length <
|
|||
|
sizeof(FILE_STANDARD_INFORMATION)) {
|
|||
|
|
|||
|
status = STATUS_BUFFER_TOO_SMALL;
|
|||
|
} else {
|
|||
|
|
|||
|
stdInfo = Irp->AssociatedIrp.SystemBuffer;
|
|||
|
stdInfo->AllocationSize.QuadPart = 0;
|
|||
|
stdInfo->EndOfFile = stdInfo->AllocationSize;
|
|||
|
stdInfo->NumberOfLinks = 0;
|
|||
|
stdInfo->DeletePending = FALSE;
|
|||
|
stdInfo->Directory = FALSE;
|
|||
|
|
|||
|
Irp->IoStatus.Information = sizeof(FILE_STANDARD_INFORMATION);
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case FilePositionInformation:
|
|||
|
if (irpSp->Parameters.SetFile.Length <
|
|||
|
sizeof(FILE_POSITION_INFORMATION)) {
|
|||
|
|
|||
|
status = STATUS_BUFFER_TOO_SMALL;
|
|||
|
} else {
|
|||
|
|
|||
|
posInfo = Irp->AssociatedIrp.SystemBuffer;
|
|||
|
posInfo->CurrentByteOffset.QuadPart = 0;
|
|||
|
|
|||
|
Irp->IoStatus.Information = sizeof(FILE_POSITION_INFORMATION);
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
status = STATUS_INVALID_PARAMETER;
|
|||
|
break;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
Irp->IoStatus.Status = status;
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARIRPPATH,
|
|||
|
("PARALLEL: About to complete IRP in query infomration\n"
|
|||
|
"Irp: %x status: %x Information: %x\n",
|
|||
|
Irp,
|
|||
|
Irp->IoStatus.Status,
|
|||
|
Irp->IoStatus.Information)
|
|||
|
);
|
|||
|
|
|||
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ParSetInformationFile(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is used to set the end of file information on
|
|||
|
the opened parallel port. Any other file information request
|
|||
|
is retured with an invalid parameter.
|
|||
|
|
|||
|
This routine always ignores the actual end of file since
|
|||
|
the query information code always returns an end of file of 0.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - Supplies the device object.
|
|||
|
|
|||
|
Irp - Supplies the I/O request packet.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_SUCCESS - Success.
|
|||
|
STATUS_INVALID_PARAMETER - Invalid file information request.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
NTSTATUS status;
|
|||
|
|
|||
|
UNREFERENCED_PARAMETER(DeviceObject);
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARIRPPATH,
|
|||
|
("PARALLEL: In set information with IRP: %x\n",
|
|||
|
Irp)
|
|||
|
);
|
|||
|
|
|||
|
Irp->IoStatus.Information = 0L;
|
|||
|
if (IoGetCurrentIrpStackLocation(Irp)->
|
|||
|
Parameters.SetFile.FileInformationClass == FileEndOfFileInformation) {
|
|||
|
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
} else {
|
|||
|
status = STATUS_INVALID_PARAMETER;
|
|||
|
}
|
|||
|
|
|||
|
Irp->IoStatus.Status = status;
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARIRPPATH,
|
|||
|
("PARALLEL: About to complete IRP in set infomration\n"
|
|||
|
"Irp: %x status: %x Information: %x\n",
|
|||
|
Irp,
|
|||
|
Irp->IoStatus.Status,
|
|||
|
Irp->IoStatus.Information)
|
|||
|
);
|
|||
|
|
|||
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
ParUnload(
|
|||
|
IN PDRIVER_OBJECT DriverObject
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine loops through the device list and cleans up after
|
|||
|
each of the devices.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DriverObject - Supplies the driver object.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PDEVICE_OBJECT currentDevice;
|
|||
|
PDEVICE_EXTENSION extension;
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARUNLOAD,
|
|||
|
("PARALLEL: In ParUnload\n")
|
|||
|
);
|
|||
|
|
|||
|
while (currentDevice = DriverObject->DeviceObject) {
|
|||
|
|
|||
|
extension = currentDevice->DeviceExtension;
|
|||
|
|
|||
|
if (extension->CreatedSymbolicLink) {
|
|||
|
IoDeleteSymbolicLink(&extension->SymbolicLinkName);
|
|||
|
RtlDeleteRegistryValue(RTL_REGISTRY_DEVICEMAP,
|
|||
|
L"PARALLEL PORTS",
|
|||
|
extension->SymbolicLinkName.Buffer);
|
|||
|
ExFreePool(extension->SymbolicLinkName.Buffer);
|
|||
|
}
|
|||
|
|
|||
|
ObDereferenceObject(extension->PortDeviceFileObject);
|
|||
|
|
|||
|
IoDeleteDevice(currentDevice);
|
|||
|
}
|
|||
|
|
|||
|
ExFreePool(OpenCloseMutex);
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
ParDeferDeviceInitialization(
|
|||
|
IN OUT PDEVICE_EXTENSION Extension
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine trys to queue a work item to initialize the printer which may
|
|||
|
be attached to the parallel port. If there are not enough resources to
|
|||
|
defer the work item it will try to initialize the port directly.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Extension - the device extension
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
ParDump(PARINITDEV, ("ParDeferDeviceInitialization(%lx)\n", Extension));
|
|||
|
|
|||
|
Extension->DeferredWorkItem = ExAllocatePool(NonPagedPool, sizeof(WORK_QUEUE_ITEM));
|
|||
|
|
|||
|
if(Extension->DeferredWorkItem != NULL) {
|
|||
|
|
|||
|
ParDump(PARINITDEV,
|
|||
|
("ParDeferDeviceInitialization - work item allocated (%lx)\n",
|
|||
|
Extension->DeferredWorkItem
|
|||
|
));
|
|||
|
|
|||
|
ExInitializeWorkItem(Extension->DeferredWorkItem,
|
|||
|
ParDeferredInitCallback,
|
|||
|
Extension);
|
|||
|
|
|||
|
ExQueueWorkItem(Extension->DeferredWorkItem,
|
|||
|
DelayedWorkQueue);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
ParDump(PARERRORS,
|
|||
|
("ParDeferDeviceInitialization - work item not allocated."
|
|||
|
"Directly initializing\n",
|
|||
|
Extension->DeferredWorkItem
|
|||
|
));
|
|||
|
//
|
|||
|
// Not enough resources to create the work item so we'll just do this the
|
|||
|
// old fashioned way and chew up boot time
|
|||
|
//
|
|||
|
|
|||
|
ParDeferredInitCallback((PVOID) Extension);
|
|||
|
|
|||
|
return;
|
|||
|
}
|