mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2009 lines
49 KiB
2009 lines
49 KiB
/*++
|
|
|
|
Copyright (c) 1990, 1991, 1992, 1993 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
pardrvr.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the code that does the non-initialization work
|
|
of the parallel driver.
|
|
|
|
Environment:
|
|
|
|
Kernel mode
|
|
|
|
Revision History :
|
|
|
|
Complete rewrite to make it thread based and polled.
|
|
|
|
|
|
--*/
|
|
|
|
//
|
|
// Note that we include ntddser that we can use the serials
|
|
// timeout structure and ioctl to set the timeout for a write.
|
|
//
|
|
|
|
#include <stddef.h>
|
|
#include "ntddk.h"
|
|
#include "ntddpar.h"
|
|
#include "ntddser.h"
|
|
#include "par.h"
|
|
#include "parlog.h"
|
|
|
|
//
|
|
// Busy, PE
|
|
//
|
|
|
|
#define PAR_PAPER_EMPTY( Status ) ( \
|
|
(Status & PAR_STATUS_PE) )
|
|
|
|
//
|
|
// 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) )
|
|
|
|
//
|
|
// 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) )
|
|
|
|
//
|
|
// not error, not busy, selected.
|
|
//
|
|
#define PAR_ONLINE(Status) ( \
|
|
(Status & PAR_STATUS_NOT_ERROR) && \
|
|
(Status & PAR_STATUS_NOT_BUSY) && \
|
|
((Status & PAR_STATUS_PE) ^ PAR_STATUS_PE) && \
|
|
(Status & PAR_STATUS_SLCT) )
|
|
|
|
//
|
|
// 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))
|
|
|
|
typedef struct _LOAD_PACKET {
|
|
NTSTATUS *Status;
|
|
PPAR_DEVICE_EXTENSION Extension;
|
|
WORK_QUEUE_ITEM WorkQueueItem;
|
|
KEVENT Event;
|
|
} LOAD_PACKET,*PLOAD_PACKET;
|
|
|
|
VOID
|
|
ParallelThread(
|
|
IN PVOID Context
|
|
);
|
|
|
|
VOID
|
|
ParWriteOutData(
|
|
PPAR_DEVICE_EXTENSION Extension
|
|
);
|
|
|
|
VOID
|
|
ParCreateSystemThread(
|
|
PVOID Context
|
|
);
|
|
|
|
VOID
|
|
ParNotInitError(
|
|
IN PPAR_DEVICE_EXTENSION Extension,
|
|
IN UCHAR deviceStatus
|
|
);
|
|
|
|
|
|
UCHAR
|
|
ParInitializeDevice(
|
|
IN PPAR_DEVICE_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 for up to 15 seconds waiting for the device
|
|
// to initialize.
|
|
//
|
|
|
|
countDown = 15;
|
|
doDelays = FALSE;
|
|
KeQueryTickCount(&startOfSpin);
|
|
ParDump(
|
|
PARINITDEV,
|
|
("PARALLEL: Starting init wait loop\n")
|
|
);
|
|
do {
|
|
|
|
//
|
|
// After about a second of spinning, let the rest of
|
|
// the machine have time for one second.
|
|
//
|
|
|
|
if (doDelays) {
|
|
|
|
difference.QuadPart = -(Extension->AbsoluteOneSecond.QuadPart);
|
|
KeDelayExecutionThread(
|
|
KernelMode,
|
|
FALSE,
|
|
&difference
|
|
);
|
|
ParDump(
|
|
PARINITDEV,
|
|
("PARALLEL: Did delay thread of one second\n")
|
|
);
|
|
countDown--;
|
|
|
|
} else {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
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_BUSY(deviceStatus) ||
|
|
((!PAR_OK(deviceStatus)) &&
|
|
(!PAR_OFF_LINE(deviceStatus)) &&
|
|
(!PAR_POWERED_OFF(deviceStatus)) &&
|
|
(!PAR_NOT_CONNECTED(deviceStatus)) &&
|
|
(!PAR_NO_CABLE(deviceStatus))));
|
|
|
|
if (PAR_OK(deviceStatus) || PAR_OFF_LINE(deviceStatus)) {
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
ParCreateOpen(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
{
|
|
|
|
NTSTATUS returnStatus;
|
|
PPAR_DEVICE_EXTENSION extension = DeviceObject->DeviceExtension;
|
|
PLOAD_PACKET loadPacket;
|
|
|
|
ParDump(
|
|
PARIRPPATH,
|
|
("PARALLEL: In create/open with IRP: %x\n",
|
|
Irp)
|
|
);
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
if (!extension->Initialized) {
|
|
|
|
UCHAR deviceStatus = ParInitializeDevice(extension);
|
|
|
|
if (!extension->Initialized) {
|
|
|
|
extension->CurrentOpIrp = Irp;
|
|
|
|
ParNotInitError(
|
|
extension,
|
|
deviceStatus
|
|
);
|
|
|
|
extension->CurrentOpIrp = NULL;
|
|
returnStatus = Irp->IoStatus.Status;
|
|
goto AllDone;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
extension->TimeToTerminateThread = FALSE;
|
|
extension->ThreadObjectPointer = NULL;
|
|
ParDump(
|
|
PARTHREAD,
|
|
("PARALLEL: open initializing - state before init - %d\n",
|
|
extension->RequestSemaphore.Header.SignalState)
|
|
);
|
|
KeInitializeSemaphore(
|
|
&extension->RequestSemaphore,
|
|
0L,
|
|
MAXLONG
|
|
);
|
|
|
|
if (IoGetCurrentIrpStackLocation(Irp)->Parameters.Create.Options
|
|
& FILE_DIRECTORY_FILE) {
|
|
|
|
returnStatus = Irp->IoStatus.Status = STATUS_NOT_A_DIRECTORY;
|
|
|
|
} else {
|
|
|
|
loadPacket = ExAllocatePool(
|
|
NonPagedPool,
|
|
sizeof(LOAD_PACKET)
|
|
);
|
|
|
|
if (!loadPacket) {
|
|
|
|
returnStatus = Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
} else {
|
|
|
|
loadPacket->Status = &Irp->IoStatus.Status;
|
|
loadPacket->Extension = extension;
|
|
KeInitializeEvent(
|
|
&loadPacket->Event,
|
|
NotificationEvent,
|
|
FALSE
|
|
);
|
|
ExInitializeWorkItem(
|
|
&loadPacket->WorkQueueItem,
|
|
ParCreateSystemThread,
|
|
loadPacket
|
|
);
|
|
ExQueueWorkItem(
|
|
&loadPacket->WorkQueueItem,
|
|
DelayedWorkQueue
|
|
);
|
|
KeWaitForSingleObject(
|
|
&loadPacket->Event,
|
|
UserRequest,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL
|
|
);
|
|
|
|
returnStatus = Irp->IoStatus.Status;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
AllDone:;
|
|
|
|
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;
|
|
|
|
}
|
|
|
|
VOID
|
|
ParCreateSystemThread(
|
|
PVOID Context
|
|
)
|
|
|
|
{
|
|
|
|
HANDLE threadHandle;
|
|
//
|
|
// This function is executing in the context of a system
|
|
// worker thread. It is used so that we can create a
|
|
// thread in the context of the system process.
|
|
//
|
|
|
|
PLOAD_PACKET lp = Context;
|
|
|
|
//
|
|
// Start the thread and capture the thread handle into the extension
|
|
//
|
|
|
|
*lp->Status = PsCreateSystemThread(
|
|
&threadHandle,
|
|
THREAD_ALL_ACCESS,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
ParallelThread,
|
|
lp->Extension
|
|
);
|
|
|
|
if (!NT_ERROR(*lp->Status)) {
|
|
|
|
//
|
|
// We've got the thread. Now get a pointer to it.
|
|
//
|
|
|
|
*lp->Status = ObReferenceObjectByHandle(
|
|
threadHandle,
|
|
THREAD_ALL_ACCESS,
|
|
NULL,
|
|
KernelMode,
|
|
&lp->Extension->ThreadObjectPointer,
|
|
NULL
|
|
);
|
|
|
|
if (NT_ERROR(*lp->Status)) {
|
|
|
|
ParDump(
|
|
PARIRPPATH,
|
|
("PARALLEL: Bad status on open from ref by handle: %x\n",
|
|
*lp->Status)
|
|
);
|
|
|
|
lp->Extension->TimeToTerminateThread = TRUE;
|
|
KeReleaseSemaphore(
|
|
&lp->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",
|
|
*lp->Status)
|
|
);
|
|
|
|
}
|
|
|
|
//
|
|
// We're all done. Let the open code proceed.
|
|
//
|
|
|
|
KeSetEvent(
|
|
&lp->Event,
|
|
0,
|
|
FALSE
|
|
);
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
ParClose(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
{
|
|
|
|
PPAR_DEVICE_EXTENSION extension = DeviceObject->DeviceExtension;
|
|
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.
|
|
//
|
|
|
|
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)
|
|
);
|
|
IoCompleteRequest(
|
|
Irp,
|
|
IO_NO_INCREMENT
|
|
);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
NTSTATUS
|
|
ParCleanup(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
{
|
|
|
|
KIRQL cancelIrql;
|
|
PPAR_DEVICE_EXTENSION extension = DeviceObject->DeviceExtension;
|
|
|
|
ParDump(
|
|
PARIRPPATH,
|
|
("PARALLEL: In cleanup with IRP: %x\n",
|
|
Irp)
|
|
);
|
|
|
|
//
|
|
// 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)) {
|
|
|
|
PDRIVER_CANCEL cancelRoutine;
|
|
PIRP 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.Status = STATUS_SUCCESS;
|
|
Irp->IoStatus.Information=0L;
|
|
|
|
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;
|
|
}
|
|
|
|
NTSTATUS
|
|
ParDispatch(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the main dispatch routine for the parallel port driver.
|
|
It is given a pointer to the IRP for the current request and
|
|
it determines what to do with it. If the request is valid and doen't
|
|
have any parameter errors, then it is placed into the work queue.
|
|
Otherwise it is not completed and an appropriate error is returned.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Pointer to the device object for this device
|
|
|
|
Irp - Pointer to the IRP for the current request
|
|
|
|
Return Value:
|
|
|
|
The function value is the final status of call
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
PPAR_DEVICE_EXTENSION extension = DeviceObject->DeviceExtension;
|
|
|
|
ParDump(
|
|
PARIRPPATH,
|
|
("PARALLEL: In main dispatch with IRP: %x\n"
|
|
"MAIN: %d io control code: %d\n",
|
|
Irp,
|
|
irpSp->MajorFunction,
|
|
irpSp->Parameters.DeviceIoControl.IoControlCode)
|
|
);
|
|
Irp->IoStatus.Information=0L;
|
|
switch(irpSp->MajorFunction) {
|
|
|
|
case IRP_MJ_WRITE:
|
|
|
|
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_PENDING;
|
|
}
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case IRP_MJ_DEVICE_CONTROL:
|
|
|
|
switch (irpSp->Parameters.DeviceIoControl.IoControlCode) {
|
|
|
|
case IOCTL_PAR_SET_INFORMATION :
|
|
|
|
if (irpSp->Parameters.DeviceIoControl.InputBufferLength <
|
|
1) {
|
|
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
|
|
} else {
|
|
|
|
PPAR_SET_INFORMATION irpBuffer =
|
|
Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
//
|
|
// INIT is required.
|
|
//
|
|
|
|
if (!(irpBuffer->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: {
|
|
|
|
PSERIAL_TIMEOUTS NewTimeouts =
|
|
((PSERIAL_TIMEOUTS)(Irp->AssociatedIrp.SystemBuffer));
|
|
|
|
if (irpSp->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(SERIAL_TIMEOUTS)) {
|
|
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
|
|
} else if (NewTimeouts->WriteTotalTimeoutConstant < 2000) {
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
|
|
}
|
|
|
|
status = STATUS_PENDING;
|
|
|
|
break;
|
|
|
|
}
|
|
case IOCTL_SERIAL_GET_TIMEOUTS:
|
|
|
|
if (irpSp->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(SERIAL_TIMEOUTS)) {
|
|
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
break;
|
|
|
|
}
|
|
|
|
//
|
|
// We don't need to synchronize the read.
|
|
//
|
|
|
|
RtlZeroMemory(
|
|
Irp->AssociatedIrp.SystemBuffer,
|
|
sizeof(SERIAL_TIMEOUTS)
|
|
);
|
|
Irp->IoStatus.Information = sizeof(SERIAL_TIMEOUTS);
|
|
((PSERIAL_TIMEOUTS)Irp->AssociatedIrp.SystemBuffer)->
|
|
WriteTotalTimeoutConstant =
|
|
extension->TimerStart * 1000;
|
|
|
|
break;
|
|
|
|
default :
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
|
|
}
|
|
|
|
Irp->IoStatus.Status = status;
|
|
|
|
if (status == STATUS_PENDING) {
|
|
|
|
KIRQL oldIrql;
|
|
|
|
//
|
|
// Acquire the cancel spin lock and put it on the
|
|
// io queue. Then hit the semaphore and return.
|
|
//
|
|
|
|
IoAcquireCancelSpinLock(&oldIrql);
|
|
|
|
if (Irp->Cancel) {
|
|
|
|
IoReleaseCancelSpinLock(oldIrql);
|
|
|
|
status = STATUS_CANCELLED;
|
|
ParDump(
|
|
PARIRPPATH,
|
|
("PARALLEL: About to CANCEL IRP in main dispatch\n"
|
|
"Irp: %x status: %x Information: %x\n",
|
|
Irp,
|
|
Irp->IoStatus.Status,
|
|
Irp->IoStatus.Information)
|
|
);
|
|
IoCompleteRequest(
|
|
Irp,
|
|
IO_NO_INCREMENT
|
|
);
|
|
|
|
} else {
|
|
|
|
ParDump(
|
|
PARIRPPATH,
|
|
("PARALLEL: About to QUEUE IRP in main dispatch\n"
|
|
"Irp: %x status: %x Information: %x\n",
|
|
Irp,
|
|
Irp->IoStatus.Status,
|
|
Irp->IoStatus.Information)
|
|
);
|
|
Irp->IoStatus.Status = STATUS_PENDING;
|
|
IoMarkIrpPending(Irp);
|
|
IoSetCancelRoutine(
|
|
Irp,
|
|
ParCancelRequest
|
|
);
|
|
|
|
InsertTailList(
|
|
&extension->WorkQueue,
|
|
&Irp->Tail.Overlay.ListEntry
|
|
);
|
|
|
|
IoReleaseCancelSpinLock(oldIrql);
|
|
|
|
ParDump(
|
|
PARTHREAD,
|
|
("PARALLEL: dispatch releasing - state before release - %d\n",
|
|
extension->RequestSemaphore.Header.SignalState)
|
|
);
|
|
KeReleaseSemaphore(
|
|
&extension->RequestSemaphore,
|
|
(KPRIORITY)0,
|
|
1,
|
|
FALSE
|
|
);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
ParDump(
|
|
PARIRPPATH,
|
|
("PARALLEL: About to complete IRP in main dispatch\n"
|
|
"Irp: %x status: %x Information: %x\n",
|
|
Irp,
|
|
Irp->IoStatus.Status,
|
|
Irp->IoStatus.Information)
|
|
);
|
|
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 - Pointer to the device object for this device
|
|
|
|
Irp - Pointer to the IRP for the current request
|
|
|
|
Return Value:
|
|
|
|
The function value is the final status of the call
|
|
|
|
--*/
|
|
|
|
{
|
|
//
|
|
// The status that gets returned to the caller and
|
|
// set in the Irp.
|
|
//
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
//
|
|
// The current stack location. This contains all of the
|
|
// information we need to process this particular request.
|
|
//
|
|
PIO_STACK_LOCATION irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
|
|
UNREFERENCED_PARAMETER(DeviceObject);
|
|
|
|
ParDump(
|
|
PARIRPPATH,
|
|
("PARALLEL: In query information file with Irp: %x\n",
|
|
Irp)
|
|
);
|
|
Irp->IoStatus.Information = 0L;
|
|
if (irpSp->Parameters.QueryFile.FileInformationClass ==
|
|
FileStandardInformation) {
|
|
|
|
PFILE_STANDARD_INFORMATION buf = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
buf->AllocationSize.QuadPart = 0;
|
|
buf->EndOfFile = buf->AllocationSize;
|
|
buf->NumberOfLinks = 0;
|
|
buf->DeletePending = FALSE;
|
|
buf->Directory = FALSE;
|
|
Irp->IoStatus.Information = sizeof(FILE_STANDARD_INFORMATION);
|
|
|
|
} else if (irpSp->Parameters.QueryFile.FileInformationClass ==
|
|
FilePositionInformation) {
|
|
|
|
((PFILE_POSITION_INFORMATION)Irp->AssociatedIrp.SystemBuffer)->
|
|
CurrentByteOffset.QuadPart = 0;
|
|
Irp->IoStatus.Information = sizeof(FILE_POSITION_INFORMATION);
|
|
|
|
} else {
|
|
|
|
status = STATUS_INVALID_PARAMETER;
|
|
|
|
}
|
|
|
|
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 - Pointer to the device object for this device
|
|
|
|
Irp - Pointer to the IRP for the current request
|
|
|
|
Return Value:
|
|
|
|
The function value is the final status of the call
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
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_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;
|
|
|
|
}
|
|
|
|
UCHAR
|
|
ParManageIoDevice(
|
|
IN PPAR_DEVICE_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 = ParInitializeDevice(Extension);
|
|
|
|
} else if (irpSp->Parameters.DeviceIoControl.IoControlCode ==
|
|
IOCTL_PAR_QUERY_INFORMATION) {
|
|
|
|
*Status = GetStatus(Extension->Controller);
|
|
*Control = GetControl(Extension->Controller);
|
|
|
|
}
|
|
|
|
return *Status;
|
|
|
|
}
|
|
|
|
VOID
|
|
ParNotInitError(
|
|
IN PPAR_DEVICE_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
|
|
ParStartIo(
|
|
IN PPAR_DEVICE_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;
|
|
|
|
ParDump(
|
|
PARIRPPATH,
|
|
("PARALLEL: In startio with IRP: %x\n",
|
|
irp)
|
|
);
|
|
if (irpSp->MajorFunction == IRP_MJ_WRITE) {
|
|
|
|
UCHAR deviceStatus;
|
|
if (!Extension->Initialized) {
|
|
|
|
deviceStatus = ParInitializeDevice(Extension);
|
|
|
|
}
|
|
|
|
if (!Extension->Initialized) {
|
|
|
|
ParNotInitError(
|
|
Extension,
|
|
deviceStatus
|
|
);
|
|
|
|
} else {
|
|
|
|
ParWriteOutData(
|
|
Extension
|
|
);
|
|
return;
|
|
|
|
}
|
|
|
|
} else if (irpSp->MajorFunction == IRP_MJ_DEVICE_CONTROL) {
|
|
|
|
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)(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 {
|
|
|
|
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
|
|
);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
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
|
|
);
|
|
|
|
}
|
|
|
|
VOID
|
|
ParallelThread(
|
|
IN PVOID Context
|
|
)
|
|
|
|
{
|
|
|
|
PPAR_DEVICE_EXTENSION extension = Context;
|
|
KIRQL oldIrql;
|
|
|
|
//
|
|
// Lower ourselves down just at tad so that we compete a
|
|
// little less.
|
|
//
|
|
|
|
KeSetBasePriorityThread(
|
|
KeGetCurrentThread(),
|
|
-1
|
|
);
|
|
|
|
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);
|
|
|
|
}
|
|
VOID
|
|
ParWriteOutData(
|
|
PPAR_DEVICE_EXTENSION Extension
|
|
)
|
|
|
|
{
|
|
|
|
PIRP irp = Extension->CurrentOpIrp;
|
|
KIRQL cancelIrql;
|
|
UCHAR deviceStatus;
|
|
LONG bytesAtATime;
|
|
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;
|
|
|
|
ParDump(
|
|
PARTHREAD,
|
|
("PARALLEL: timerStart is: %d\n",
|
|
timerStart)
|
|
);
|
|
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.
|
|
//
|
|
|
|
KeRaiseIrql(
|
|
DISPATCH_LEVEL,
|
|
&oldIrql
|
|
);
|
|
StoreControl(
|
|
Extension->Controller,
|
|
(UCHAR)(PAR_CONTROL_WR_CONTROL |
|
|
PAR_CONTROL_SLIN |
|
|
PAR_CONTROL_NOT_INIT)
|
|
);
|
|
|
|
for (
|
|
bytesAtATime = 100;
|
|
bytesAtATime && bytesToWrite;
|
|
bytesAtATime--
|
|
) {
|
|
|
|
deviceStatus = GetStatus(Extension->Controller);
|
|
|
|
if (PAR_ONLINE(deviceStatus)) {
|
|
|
|
//
|
|
// Anytime we write out a character we will restart
|
|
// the count down timer.
|
|
//
|
|
|
|
countDown = timerStart;
|
|
WRITE_PORT_UCHAR(
|
|
Extension->Controller+PARALLEL_DATA_OFFSET,
|
|
(UCHAR)*irpBuffer
|
|
);
|
|
KeStallExecutionProcessor((ULONG)1);
|
|
StoreControl(
|
|
Extension->Controller,
|
|
(UCHAR)(PAR_CONTROL_WR_CONTROL |
|
|
PAR_CONTROL_SLIN |
|
|
PAR_CONTROL_NOT_INIT |
|
|
PAR_CONTROL_STROBE)
|
|
);
|
|
KeStallExecutionProcessor((ULONG)1);
|
|
StoreControl(
|
|
Extension->Controller,
|
|
(UCHAR)(PAR_CONTROL_WR_CONTROL |
|
|
PAR_CONTROL_SLIN |
|
|
PAR_CONTROL_NOT_INIT)
|
|
);
|
|
KeStallExecutionProcessor((ULONG)1);
|
|
irpBuffer++;
|
|
bytesToWrite--;
|
|
|
|
} else {
|
|
|
|
ParDump(
|
|
PARPUSHER,
|
|
("PARALLEL: Initiate IO - device is not on line, status: %x\n",
|
|
deviceStatus)
|
|
);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
KeLowerIrql(oldIrql);
|
|
|
|
//
|
|
// 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
|
|
);
|
|
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
|
|
);
|
|
|
|
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.
|
|
//
|
|
|
|
if (PAR_OK(deviceStatus) && 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--;
|
|
KeDelayExecutionThread(
|
|
KernelMode,
|
|
FALSE,
|
|
&Extension->OneSecond
|
|
);
|
|
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
|
|
);
|
|
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
|
|
);
|
|
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) {
|
|
|
|
difference.QuadPart = -(Extension->AbsoluteOneSecond.QuadPart);
|
|
KeDelayExecutionThread(
|
|
KernelMode,
|
|
FALSE,
|
|
&difference
|
|
);
|
|
ParDump(
|
|
PARINITDEV,
|
|
("PARALLEL: Did delay thread of one second\n")
|
|
);
|
|
countDown--;
|
|
|
|
} else {
|
|
|
|
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
|
|
);
|
|
|
|
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;
|
|
|
|
}
|