Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

1426 lines
34 KiB

/*++
Copyright (c) 1993 Microsoft Corporation
Module Name:
parsimp.c
Abstract:
This module contains the code for a simple parallel class driver.
Unload and Cleanup are supported. The model for grabing and
releasing the parallel port is embodied in the code for IRP_MJ_READ.
Other IRP requests could be implemented similarly.
Basically, every READ requests that comes in gets
passed down to the port driver as a parallel port allocate
request. This IRP will return to this driver when the driver
owns the port. By uncommenting the '#define TIMEOUT_ALLOCS'
line below, code will be added to timeout the allocate
requests after a specified amount of time and return
STATUS_IO_TIMEOUT for that read request.
Device drivers who wish to use the parallel port interrupt
can uncomment the '#define INTERRUPT_NEEDED' line below.
This will add support for using the interrupt in a shared manner
so that many parallel class drivers can share it.
Author:
Norbert P. Kusters 25-Jan-1994
Environment:
Kernel mode
Revision History :
--*/
#include "ntddk.h"
#include "parallel.h"
// #define TIMEOUT_ALLOCS 1
// #define INTERRUPT_NEEDED 1
#include "parsimp.h"
#include "parlog.h"
static const PHYSICAL_ADDRESS PhysicalZero = {0};
NTSTATUS
DriverEntry(
IN PDRIVER_OBJECT DriverObject,
IN PUNICODE_STRING RegistryPath
);
BOOLEAN
ParMakeNames(
IN ULONG ParallelPortNumber,
OUT PUNICODE_STRING PortName,
OUT PUNICODE_STRING ClassName,
OUT PUNICODE_STRING LinkName
);
VOID
ParInitializeDeviceObject(
IN PDRIVER_OBJECT DriverObject,
IN ULONG ParallelPortNumber
);
NTSTATUS
ParGetPortInfoFromPortDevice(
IN OUT PDEVICE_EXTENSION Extension
);
#ifdef INTERRUPT_NEEDED
NTSTATUS
ParInitializeInterruptObject(
IN PDRIVER_OBJECT DriverObject,
IN OUT PDEVICE_EXTENSION Extension
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT,ParInitializeInterruptObject)
#endif
#endif // INTERRUPT_NEEDED
VOID
ParAllocPort(
IN PDEVICE_EXTENSION Extension
);
#ifdef ALLOC_PRAGMA
#pragma alloc_text(INIT,DriverEntry)
#pragma alloc_text(INIT,ParInitializeDeviceObject)
#pragma alloc_text(INIT,ParMakeNames)
#pragma alloc_text(INIT,ParGetPortInfoFromPortDevice)
#endif
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;
for (i = 0; i < IoGetConfigurationInformation()->ParallelCount; i++) {
ParInitializeDeviceObject(DriverObject, i);
}
if (!DriverObject->DeviceObject) {
return STATUS_NO_SUCH_DEVICE;
}
//
// Initialize the Driver Object with driver's entry points
//
DriverObject->MajorFunction[IRP_MJ_CREATE] = ParCreateClose;
DriverObject->MajorFunction[IRP_MJ_CLOSE] = ParCreateClose;
DriverObject->MajorFunction[IRP_MJ_READ] = ParRead;
DriverObject->MajorFunction[IRP_MJ_CLEANUP] = ParCleanup;
DriverObject->DriverUnload = ParUnload;
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);
}
VOID
ParInitializeDeviceObject(
IN PDRIVER_OBJECT DriverObject,
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.
ParallelPortNumber - Supplies the number for this port.
Return Value:
None.
--*/
{
UNICODE_STRING portName, className, linkName;
NTSTATUS status;
PDEVICE_OBJECT deviceObject;
PDEVICE_EXTENSION extension;
PFILE_OBJECT fileObject;
// Cobble together the port and class device names.
if (!ParMakeNames(ParallelPortNumber, &portName, &className, &linkName)) {
ParLogError(DriverObject, NULL, PhysicalZero, PhysicalZero, 0, 0, 0, 1,
STATUS_SUCCESS, PAR_INSUFFICIENT_RESOURCES);
return;
}
// Create the device object.
status = IoCreateDevice(DriverObject, sizeof(DEVICE_EXTENSION),
&className, FILE_DEVICE_PARALLEL_PORT, 0, FALSE,
&deviceObject);
if (!NT_SUCCESS(status)) {
ParLogError(DriverObject, NULL, PhysicalZero, PhysicalZero, 0, 0, 0, 2,
STATUS_SUCCESS, PAR_INSUFFICIENT_RESOURCES);
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,
&fileObject,
&extension->PortDeviceObject);
if (!NT_SUCCESS(status)) {
ParLogError(DriverObject, deviceObject, PhysicalZero, PhysicalZero,
0, 0, 0, 3, STATUS_SUCCESS, PAR_CANT_FIND_PORT_DRIVER);
IoDeleteDevice(deviceObject);
ExFreePool(linkName.Buffer);
goto Cleanup;
}
ObDereferenceObject(fileObject);
extension->DeviceObject->StackSize =
extension->PortDeviceObject->StackSize + 1;
// Initialize an empty work queue.
InitializeListHead(&extension->WorkQueue);
extension->CurrentIrp = NULL;
// Get the port information from the port device object.
status = ParGetPortInfoFromPortDevice(extension);
if (!NT_SUCCESS(status)) {
ParLogError(DriverObject, deviceObject, PhysicalZero, PhysicalZero,
0, 0, 0, 4, STATUS_SUCCESS, PAR_CANT_FIND_PORT_DRIVER);
IoDeleteDevice(deviceObject);
ExFreePool(linkName.Buffer);
goto Cleanup;
}
// Set up the symbolic link for windows apps.
status = IoCreateUnprotectedSymbolicLink(&linkName, &className);
if (!NT_SUCCESS(status)) {
ParLogError(DriverObject, deviceObject, extension->OriginalController,
PhysicalZero, 0, 0, 0, 5, STATUS_SUCCESS,
PAR_NO_SYMLINK_CREATED);
extension->CreatedSymbolicLink = FALSE;
ExFreePool(linkName.Buffer);
goto Cleanup;
}
// 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;
#ifdef INTERRUPT_NEEDED
// Ignore interrupts until we own the port.
extension->IgnoreInterrupts = TRUE;
// Connect the interrupt.
IoInitializeDpcRequest(extension->DeviceObject, ParDpcForIsr);
status = ParInitializeInterruptObject(DriverObject, extension);
if (!NT_SUCCESS(status)) {
ParLogError(DriverObject, deviceObject, extension->OriginalController,
PhysicalZero, 0, 0, 0, 6, STATUS_SUCCESS,
PAR_INTERRUPT_NOT_INITIALIZED);
IoDeleteDevice(deviceObject);
ExFreePool(linkName.Buffer);
goto Cleanup;
}
#endif
#ifdef TIMEOUT_ALLOCS
// Prepare an timeout value of 5 seconds for port allocation.
KeInitializeTimer(&extension->AllocTimer);
KeInitializeDpc(&extension->AllocTimerDpc, ParAllocTimerDpc, extension);
extension->AllocTimeout.QuadPart = -(5*1000*10000);
extension->CurrentIrpRefCount = 0;
KeInitializeSpinLock(&extension->ControlLock);
extension->TimedOut = FALSE;
#endif
Cleanup:
ExFreePool(portName.Buffer);
ExFreePool(className.Buffer);
}
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 and
\Device\ParallelSimpleN, \DosDevices\LPTSIMPLEn.
Arguments:
ParallelPortNumber - Supplies the port number.
PortName - Returns the port name.
ClassName - Returns the class name.
LinkName - Returns the symbolic 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, L"ParallelSimple");
RtlInitUnicodeString(&linkSuffix, L"LPTSIMPLE");
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;
}
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;
}
Extension->OriginalController = portInfo.OriginalController;
Extension->Controller = portInfo.Controller;
Extension->SpanOfController = portInfo.SpanOfController;
Extension->FreePort = portInfo.FreePort;
Extension->FreePortContext = portInfo.Context;
if (Extension->SpanOfController < PARALLEL_REGISTER_SPAN) {
return STATUS_INSUFFICIENT_RESOURCES;
}
return status;
}
#ifdef INTERRUPT_NEEDED
NTSTATUS
ParInitializeInterruptObject(
IN PDRIVER_OBJECT DriverObject,
IN OUT PDEVICE_EXTENSION Extension
)
/*++
Routine Description:
This routine sets up this devices interrupt routine by
registering it with the port device.
Arguments:
DriverObject - Supplies the driver object.
Extension - Supplies the device extension.
Return Value:
STATUS_SUCCESS - The interrupt was successfully connected.
Otherwise, an error occurred.
--*/
{
KEVENT event;
PIRP irp;
PARALLEL_INTERRUPT_SERVICE_ROUTINE interruptService;
PARALLEL_INTERRUPT_INFORMATION interruptInfo;
IO_STATUS_BLOCK ioStatus;
NTSTATUS status;
// Get the interrupt information from the port device.
KeInitializeEvent(&event, NotificationEvent, FALSE);
interruptService.InterruptServiceRoutine = ParInterruptService;
interruptService.InterruptServiceContext = Extension;
interruptService.DeferredPortCheckRoutine = ParDeferredPortCheck;
interruptService.DeferredPortCheckContext = Extension;
irp = IoBuildDeviceIoControlRequest(IOCTL_INTERNAL_PARALLEL_CONNECT_INTERRUPT,
Extension->PortDeviceObject,
&interruptService,
sizeof(PARALLEL_INTERRUPT_SERVICE_ROUTINE),
&interruptInfo,
sizeof(PARALLEL_INTERRUPT_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->InterruptObject = interruptInfo.InterruptObject;
Extension->TryAllocatePortAtInterruptLevel =
interruptInfo.TryAllocatePortAtInterruptLevel;
Extension->TryAllocateContext = interruptInfo.Context;
return status;
}
#endif // INTERRUPT_NEEDED
VOID
ParAllocPortWithNext(
IN OUT PDEVICE_EXTENSION Extension
)
/*++
Routine Description:
This routine takes the next IRP on the work queue and passes it
down to the port device to allocate the port.
Arguments:
Extension - Supplies the device extension.
Return Value:
None.
--*/
{
KIRQL cancelIrql;
PLIST_ENTRY head;
IoAcquireCancelSpinLock(&cancelIrql);
Extension->CurrentIrp = NULL;
if (IsListEmpty(&Extension->WorkQueue)) {
IoReleaseCancelSpinLock(cancelIrql);
} else {
head = RemoveHeadList(&Extension->WorkQueue);
Extension->CurrentIrp = CONTAINING_RECORD(head, IRP,
Tail.Overlay.ListEntry);
IoSetCancelRoutine(Extension->CurrentIrp, NULL);
IoReleaseCancelSpinLock(cancelIrql);
ParAllocPort(Extension);
}
}
#ifdef INTERRUPT_NEEDED
BOOLEAN
ParSetTrue(
OUT PVOID Context
)
{
*((PBOOLEAN) Context) = TRUE;
return(TRUE);
}
VOID
ParDpcForIsr(
IN PKDPC Dpc,
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Extension
)
/*++
Routine Description:
This is the DPC for the interrupt service routine below.
Arguments:
Dpc - Supplies the DPC.
DeviceObject - Supplies the device object.
Irp - Supplies the I/O request packet.
Extension - Supplies the device extension.
Return Value:
None.
--*/
{
PDEVICE_EXTENSION extension;
extension = Extension;
//
// Perform defered actions for Interrupt service routine.
//
//
// Complete the IRP, free the port, and start up the next IRP in
// the queue.
//
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_PARALLEL_INCREMENT);
KeSynchronizeExecution(extension->InterruptObject,
ParSetTrue,
&extension->IgnoreInterrupts);
extension->FreePort(extension->FreePortContext);
ParAllocPortWithNext(extension);
}
BOOLEAN
ParInterruptService(
IN PKINTERRUPT Interrupt,
IN OUT PVOID Extension
)
/*++
Routine Description:
This routine is the interrupt service routine for this parallel
driver. If this device does not own the parallel port then it
will return FALSE so as to let this shared interrupt be taken
by another driver.
Arguments:
Interrupt - Supplies the interrupt object.
Extension - Supplies the device extension.
Return Value:
FALSE - The interrupt was not handled.
TRUE - The interrupt was handled.
--*/
{
PDEVICE_EXTENSION extension;
extension = Extension;
if (extension->IgnoreInterrupts) {
// The port is not allocated by this driver.
// If appropriate this device can try to grab the port if it
// is free.
if (!extension->TryAllocatePortAtInterruptLevel(
extension->TryAllocateContext)) {
return FALSE;
}
}
// Do some stuff and then queue a DPC to complete the request and
// free the port.
IoRequestDpc(extension->DeviceObject, extension->CurrentIrp, extension);
return TRUE;
}
VOID
ParDeferredPortCheck(
IN PVOID Extension
)
/*++
Routine Description:
This routine is called when the parallel port is inactive.
It makes sure that interrupts are enabled.
Arguments:
Extension - Supplies the device extension.
Return Value:
None.
--*/
{
PDEVICE_EXTENSION extension = Extension;
UCHAR u;
// Make sure that interrupts are turned on.
u = READ_PORT_UCHAR(extension->Controller + 2);
WRITE_PORT_UCHAR(extension->Controller + 2, u | 0x10);
}
BOOLEAN
ParSetFalse(
OUT PVOID Context
)
{
*((PBOOLEAN) Context) = FALSE;
return(TRUE);
}
#endif // INTERRUPT_NEEDED
VOID
ParCompleteRequest(
IN PDEVICE_EXTENSION Extension
)
/*++
Routine Description:
This routine completes the 'CurrentIrp' after it was returned
from the port driver.
Arguments:
Extension - Supplies the device extension.
Return Value:
None.
--*/
{
PIRP Irp;
Irp = Extension->CurrentIrp;
// If the allocate failed, then fail this request and try again
// with the next IRP.
if (!NT_SUCCESS(Irp->IoStatus.Status)) {
#ifdef TIMEOUT_ALLOCS
if (Extension->TimedOut) {
Irp->IoStatus.Status = STATUS_IO_TIMEOUT;
}
#endif
IoCompleteRequest(Irp, IO_NO_INCREMENT);
ParAllocPortWithNext(Extension);
return;
}
#ifdef INTERRUPT_NEEDED
KeSynchronizeExecution(Extension->InterruptObject,
ParSetFalse,
&Extension->IgnoreInterrupts);
#endif
//
// This is where the driver specific stuff should go. The driver
// has exclusive access to the parallel port in this space.
//
#ifdef INTERRUPT_NEEDED
//
// We're waiting for an interrupt before we can complete the IRP.
//
#else
//
// Complete the IRP, free the port, and start up the next IRP in
// the queue.
//
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_PARALLEL_INCREMENT);
Extension->FreePort(Extension->FreePortContext);
ParAllocPortWithNext(Extension);
#endif
}
#ifdef TIMEOUT_ALLOCS
VOID
ParAllocTimerDpc(
IN PKDPC Dpc,
IN PVOID Extension,
IN PVOID SystemArgument1,
IN PVOID SystemArgument2
)
/*++
Routine Description:
This routine is called when an allocate request times out.
This routine cancels the current irp, unless it is being
processed.
Arguments:
Dpc - Supplies the DPC.
Extension - Supplies the device extension.
SystemArgument1 - Ignored.
SystemArgument2 - Ignored.
Return Value:
None.
--*/
{
PDEVICE_EXTENSION extension;
KIRQL oldIrql;
LONG irpRef;
extension = Extension;
extension->TimedOut = TRUE;
// Try to cancel the IRP.
IoCancelIrp(extension->CurrentIrp);
KeAcquireSpinLock(&extension->ControlLock, &oldIrql);
ASSERT(extension->CurrentIrpRefCount & IRP_REF_TIMER);
irpRef = (extension->CurrentIrpRefCount -= IRP_REF_TIMER);
KeReleaseSpinLock(&extension->ControlLock, oldIrql);
if (!irpRef) {
// Complete the request if the request was not completed
// by the completion routine.
ParCompleteRequest(extension);
}
}
#endif
VOID
ParCancel(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This is the cancel routine for this driver.
Arguments:
DeviceObject - Supplies the device object.
Irp - Supplies the I/O request packet.
Return Value:
None.
--*/
{
RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
IoReleaseCancelSpinLock(Irp->CancelIrql);
Irp->IoStatus.Information = 0;
Irp->IoStatus.Status = STATUS_CANCELLED;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
}
NTSTATUS
ParCreateClose(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine is the dispatch for create requests.
Arguments:
DeviceObject - Supplies the device object.
Irp - Supplies the I/O request packet.
Return Value:
STATUS_SUCCESS - Success.
STATUS_NOT_A_DIRECTORY - This device is not a directory.
--*/
{
PIO_STACK_LOCATION irpSp;
NTSTATUS status;
irpSp = IoGetCurrentIrpStackLocation(Irp);
if (irpSp->MajorFunction == IRP_MJ_CREATE &&
irpSp->Parameters.Create.Options & FILE_DIRECTORY_FILE) {
status = STATUS_NOT_A_DIRECTORY;
} else {
status = STATUS_SUCCESS;
}
Irp->IoStatus.Status = status;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return status;
}
NTSTATUS
ParReadCompletionRoutine(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Extension
)
/*++
Routine Description:
This is the completion routine for the device control request.
This driver has exclusive access to the parallel port in this
routine.
Arguments:
DeviceObject - Supplies the device object.
Irp - Supplies the I/O request packet.
Extension - Supplies the device extension.
Return Value:
STATUS_MORE_PROCESSING_REQUIRED
--*/
{
PDEVICE_EXTENSION extension;
KIRQL oldIrql;
LONG irpRef;
extension = Extension;
#ifdef TIMEOUT_ALLOCS
// Try to cancel the timer.
if (KeCancelTimer(&extension->AllocTimer)) {
// The timer was cancelled. The completion routine has the IRP.
extension->CurrentIrpRefCount = 0;
} else {
// We're in contention with the timer DPC. Establish who
// will complete the IRP via the 'CurrentIrpRefCount'.
KeAcquireSpinLock(&extension->ControlLock, &oldIrql);
ASSERT(extension->CurrentIrpRefCount & IRP_REF_COMPLETION_ROUTINE);
irpRef = (extension->CurrentIrpRefCount -= IRP_REF_COMPLETION_ROUTINE);
KeReleaseSpinLock(&extension->ControlLock, oldIrql);
if (irpRef) {
// The IRP will be completed by the timer DPC.
return STATUS_MORE_PROCESSING_REQUIRED;
}
}
// At this point, the timer DPC is guaranteed not to
// mess with the CurrentIrp.
#endif
ParCompleteRequest(extension);
// If the IRP was completed. It was completed with 'IoCompleteRequest'.
return STATUS_MORE_PROCESSING_REQUIRED;
}
VOID
ParAllocPort(
IN PDEVICE_EXTENSION Extension
)
/*++
Routine Description:
This routine takes the 'CurrentIrp' and sends it down to the
port driver as an allocate parallel port request.
Arguments:
Extension - Supplies the device extension.
Return Value:
None.
--*/
{
PIO_STACK_LOCATION nextSp;
nextSp = IoGetNextIrpStackLocation(Extension->CurrentIrp);
nextSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
nextSp->Parameters.DeviceIoControl.IoControlCode =
IOCTL_INTERNAL_PARALLEL_PORT_ALLOCATE;
IoSetCompletionRoutine(Extension->CurrentIrp,
ParReadCompletionRoutine,
Extension, TRUE, TRUE, TRUE);
#ifdef TIMEOUT_ALLOCS
Extension->TimedOut = FALSE;
Extension->CurrentIrpRefCount = IRP_REF_TIMER + IRP_REF_COMPLETION_ROUTINE;
KeSetTimer(&Extension->AllocTimer, Extension->AllocTimeout,
&Extension->AllocTimerDpc);
#endif
IoCallDriver(Extension->PortDeviceObject, Extension->CurrentIrp);
}
NTSTATUS
ParRead(
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_PENDING - Request pending.
--*/
{
PDEVICE_EXTENSION extension;
KIRQL cancelIrql;
BOOLEAN allocatePort;
extension = DeviceObject->DeviceExtension;
IoAcquireCancelSpinLock(&cancelIrql);
if (extension->CurrentIrp) {
IoSetCancelRoutine(Irp, ParCancel);
InsertTailList(&extension->WorkQueue, &Irp->Tail.Overlay.ListEntry);
allocatePort = FALSE;
} else {
extension->CurrentIrp = Irp;
allocatePort = TRUE;
}
IoReleaseCancelSpinLock(cancelIrql);
IoMarkIrpPending(Irp);
if (allocatePort) {
ParAllocPort(extension);
}
return STATUS_PENDING;
}
NTSTATUS
ParCleanup(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp
)
/*++
Routine Description:
This routine completes all IRPs on the work queue.
Arguments:
DeviceObject - Supplies the device object.
Irp - Supplies the I/O request packet.
Return Value:
STATUS_SUCCESS
--*/
{
PDEVICE_EXTENSION extension;
KIRQL cancelIrql;
PLIST_ENTRY head;
PIRP irp;
extension = DeviceObject->DeviceExtension;
IoAcquireCancelSpinLock(&cancelIrql);
while (!IsListEmpty(&extension->WorkQueue)) {
head = RemoveHeadList(&extension->WorkQueue);
irp = CONTAINING_RECORD(head, IRP, Tail.Overlay.ListEntry);
irp->Cancel = TRUE;
irp->CancelIrql = cancelIrql;
irp->CancelRoutine = NULL;
ParCancel(DeviceObject, irp);
IoAcquireCancelSpinLock(&cancelIrql);
}
IoReleaseCancelSpinLock(cancelIrql);
Irp->IoStatus.Status = STATUS_SUCCESS;
Irp->IoStatus.Information = 0;
IoCompleteRequest(Irp, IO_NO_INCREMENT);
return STATUS_SUCCESS;
}
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;
KEVENT event;
PARALLEL_INTERRUPT_SERVICE_ROUTINE interruptService;
PIRP irp;
IO_STATUS_BLOCK ioStatus;
while (currentDevice = DriverObject->DeviceObject) {
extension = currentDevice->DeviceExtension;
if (extension->CreatedSymbolicLink) {
IoDeleteSymbolicLink(&extension->SymbolicLinkName);
ExFreePool(extension->SymbolicLinkName.Buffer);
}
#ifdef INTERRUPT_NEEDED
KeInitializeEvent(&event, NotificationEvent, FALSE);
interruptService.InterruptServiceRoutine = ParInterruptService;
interruptService.InterruptServiceContext = extension;
interruptService.DeferredPortCheckRoutine = ParDeferredPortCheck;
interruptService.DeferredPortCheckContext = extension;
irp = IoBuildDeviceIoControlRequest(IOCTL_INTERNAL_PARALLEL_DISCONNECT_INTERRUPT,
extension->PortDeviceObject,
&interruptService,
sizeof(PARALLEL_INTERRUPT_SERVICE_ROUTINE),
NULL, 0, TRUE, &event, &ioStatus);
if (irp &&
NT_SUCCESS(IoCallDriver(extension->PortDeviceObject, irp))) {
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
}
#endif
IoDeleteDevice(currentDevice);
}
}