1205 lines
29 KiB
C
1205 lines
29 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (C) Microsoft Corporation, 1993 - 1999
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
parvdm.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
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
Kernel mode
|
|||
|
|
|||
|
Revision History :
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "ntosp.h"
|
|||
|
#include "parallel.h"
|
|||
|
#include "ntddvdm.h"
|
|||
|
#include "parvdm.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
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
ParReleasePortInfoToPortDevice(
|
|||
|
IN OUT PDEVICE_EXTENSION Extension
|
|||
|
);
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ParAllocPort(
|
|||
|
IN PDEVICE_EXTENSION Extension,
|
|||
|
IN PIRP Irp
|
|||
|
);
|
|||
|
|
|||
|
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
|
|||
|
);
|
|||
|
#ifdef ALLOC_PRAGMA
|
|||
|
#pragma alloc_text(INIT,DriverEntry)
|
|||
|
#pragma alloc_text(INIT,ParInitializeDeviceObject)
|
|||
|
#pragma alloc_text(INIT,ParMakeNames)
|
|||
|
#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;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
//
|
|||
|
// 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++) {
|
|||
|
ParInitializeDeviceObject(DriverObject, i);
|
|||
|
}
|
|||
|
|
|||
|
if (!DriverObject->DeviceObject) {
|
|||
|
if( OpenCloseMutex ) {
|
|||
|
ExFreePool( OpenCloseMutex );
|
|||
|
OpenCloseMutex = NULL;
|
|||
|
}
|
|||
|
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_DEVICE_CONTROL] = ParDeviceControl;
|
|||
|
DriverObject->DriverUnload = ParUnload;
|
|||
|
|
|||
|
//
|
|||
|
// page out the driver if we can
|
|||
|
//
|
|||
|
|
|||
|
ParReleaseDriver();
|
|||
|
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ParOpenFileAgainstParport(PDEVICE_EXTENSION extension)
|
|||
|
{
|
|||
|
NTSTATUS status;
|
|||
|
|
|||
|
status = IoGetDeviceObjectPointer(&extension->ParPortName, FILE_READ_ATTRIBUTES,
|
|||
|
&extension->ParPortFileObject,
|
|||
|
&extension->PortDeviceObject);
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
ParCloseFileAgainstParport(PDEVICE_EXTENSION extension)
|
|||
|
{
|
|||
|
if( extension->ParPortFileObject ) {
|
|||
|
ObDereferenceObject( extension->ParPortFileObject );
|
|||
|
extension->ParPortFileObject = NULL;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
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;
|
|||
|
|
|||
|
PAGED_CODE();
|
|||
|
|
|||
|
// 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);
|
|||
|
ExFreePool(portName.Buffer);
|
|||
|
ExFreePool(className.Buffer);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// 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);
|
|||
|
ExFreePool(portName.Buffer);
|
|||
|
ExFreePool(className.Buffer);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
ObDereferenceObject(fileObject);
|
|||
|
|
|||
|
extension->DeviceObject->StackSize =
|
|||
|
extension->PortDeviceObject->StackSize + 1;
|
|||
|
|
|||
|
|
|||
|
// We don't own parallel ports initially
|
|||
|
|
|||
|
extension->PortOwned = FALSE;
|
|||
|
|
|||
|
// 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);
|
|||
|
ExFreePool(portName.Buffer);
|
|||
|
ExFreePool(className.Buffer);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// 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;
|
|||
|
|
|||
|
Cleanup:
|
|||
|
// release the port info so the port driver can be paged out
|
|||
|
ParReleasePortInfoToPortDevice(extension);
|
|||
|
// ExFreePool(portName.Buffer); - save this in extension for
|
|||
|
// future CreateFiles against parport
|
|||
|
if( portName.Buffer ) {
|
|||
|
RtlInitUnicodeString( &extension->ParPortName, 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\ParallelVdmN, \DosDevices\PARVDMN.
|
|||
|
|
|||
|
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"ParallelVdm");
|
|||
|
RtlInitUnicodeString(&linkSuffix, L"$VDMLPT");
|
|||
|
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
|
|||
|
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;
|
|||
|
}
|
|||
|
|
|||
|
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;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
ParCompleteRequest(
|
|||
|
IN PDEVICE_EXTENSION Extension,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine completes the 'CurrentIrp' after it was returned
|
|||
|
from the port driver.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Extension - Supplies the device extension.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
// DbgPrint("ParVDMCompleteRequest: Enter with irp = %#08x\n", Irp);
|
|||
|
//
|
|||
|
// If the allocate failed, then fail this request
|
|||
|
//
|
|||
|
|
|||
|
if (!NT_SUCCESS(Irp->IoStatus.Status)) {
|
|||
|
// failed to allocate port, release port info back to port driver
|
|||
|
// and paged ourselves out.
|
|||
|
ParReleasePortInfoToPortDevice(Extension);
|
|||
|
ParReleaseDriver();
|
|||
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|||
|
Extension->CreateOpenLock = 0;
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// This is where the driver specific stuff should go. The driver
|
|||
|
// has exclusive access to the parallel port in this space.
|
|||
|
//
|
|||
|
|
|||
|
// DbgPrint("ParVDMCompleteRequest: We own the port\n");
|
|||
|
Extension->PortOwned = TRUE;
|
|||
|
|
|||
|
//
|
|||
|
// 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);
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
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
|
|||
|
ParCreateOpen(
|
|||
|
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;
|
|||
|
PDEVICE_EXTENSION extension = DeviceObject->DeviceExtension;
|
|||
|
|
|||
|
//
|
|||
|
// Enforce exclusive access to this device
|
|||
|
//
|
|||
|
if( InterlockedExchange( &extension->CreateOpenLock, 1 ) ) {
|
|||
|
Irp->IoStatus.Status = STATUS_ACCESS_DENIED;
|
|||
|
Irp->IoStatus.Information = 0;
|
|||
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|||
|
return STATUS_ACCESS_DENIED;
|
|||
|
}
|
|||
|
|
|||
|
if(extension->PortOwned) {
|
|||
|
//
|
|||
|
// Do an early exit if we can detect that another client has
|
|||
|
// already acquired the port for exclusive use.
|
|||
|
//
|
|||
|
Irp->IoStatus.Status = STATUS_ACCESS_DENIED;
|
|||
|
Irp->IoStatus.Information = 0;
|
|||
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|||
|
extension->CreateOpenLock = 0;
|
|||
|
return STATUS_ACCESS_DENIED;
|
|||
|
}
|
|||
|
|
|||
|
irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|||
|
|
|||
|
if (irpSp->MajorFunction == IRP_MJ_CREATE &&
|
|||
|
irpSp->Parameters.Create.Options & FILE_DIRECTORY_FILE) {
|
|||
|
//
|
|||
|
// Bail out if client thinks that we are a directory.
|
|||
|
//
|
|||
|
Irp->IoStatus.Status = STATUS_NOT_A_DIRECTORY;
|
|||
|
Irp->IoStatus.Information = 0;
|
|||
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|||
|
extension->CreateOpenLock = 0;
|
|||
|
return STATUS_NOT_A_DIRECTORY;
|
|||
|
}
|
|||
|
|
|||
|
// DVDF - open FILE against parport device
|
|||
|
status = ParOpenFileAgainstParport( extension );
|
|||
|
if( !NT_SUCCESS( status ) ) {
|
|||
|
//
|
|||
|
// We couldn't open a handle to the parport device - bail out
|
|||
|
//
|
|||
|
Irp->IoStatus.Status = STATUS_ACCESS_DENIED;
|
|||
|
Irp->IoStatus.Information = 0;
|
|||
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|||
|
extension->CreateOpenLock = 0;
|
|||
|
return STATUS_ACCESS_DENIED;
|
|||
|
}
|
|||
|
|
|||
|
//Lock in driver code
|
|||
|
ParClaimDriver();
|
|||
|
|
|||
|
// lock in ParPort driver
|
|||
|
ParGetPortInfoFromPortDevice(extension);
|
|||
|
|
|||
|
// try to allocate the port for our exclusive use
|
|||
|
status = ParAllocPort(extension, Irp);
|
|||
|
|
|||
|
// DbgPrint("ParVDMDeviceControl: ParAllocPort returned %#08lx\n");
|
|||
|
|
|||
|
if( !NT_SUCCESS( status ) ) {
|
|||
|
extension->CreateOpenLock = 0;
|
|||
|
}
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
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;
|
|||
|
|
|||
|
|
|||
|
extension = DeviceObject->DeviceExtension;
|
|||
|
|
|||
|
if (!extension->PortOwned)
|
|||
|
return STATUS_ACCESS_DENIED;
|
|||
|
|
|||
|
|
|||
|
// free the port for other uses
|
|||
|
extension->FreePort(extension->FreePortContext);
|
|||
|
extension->PortOwned = FALSE;
|
|||
|
|
|||
|
// Allow the port driver to be paged.
|
|||
|
ParReleasePortInfoToPortDevice(extension);
|
|||
|
|
|||
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|||
|
|
|||
|
// DVDF - close our FILE
|
|||
|
ParCloseFileAgainstParport(extension);
|
|||
|
|
|||
|
// Unlock the code that was locked during the open.
|
|||
|
|
|||
|
ParReleaseDriver();
|
|||
|
|
|||
|
// DbgPrint("ParVDMClose: Close device, we no longer own the port\n");
|
|||
|
|
|||
|
extension->CreateOpenLock = 0;
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ParAllocateCompletionRoutine(
|
|||
|
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
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
KIRQL oldIrql;
|
|||
|
LONG irpRef;
|
|||
|
|
|||
|
if( Irp->PendingReturned ) {
|
|||
|
IoMarkIrpPending( Irp );
|
|||
|
}
|
|||
|
|
|||
|
ParCompleteRequest(Extension, Irp);
|
|||
|
|
|||
|
// If the IRP was completed. It was completed with 'IoCompleteRequest'.
|
|||
|
|
|||
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
ParAllocPort(
|
|||
|
IN PDEVICE_EXTENSION Extension,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
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;
|
|||
|
// DbgPrint("ParVDMAllocPort: Entry\n");
|
|||
|
|
|||
|
nextSp = IoGetNextIrpStackLocation(Irp);
|
|||
|
nextSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
|
|||
|
nextSp->Parameters.DeviceIoControl.IoControlCode =
|
|||
|
IOCTL_INTERNAL_PARALLEL_PORT_ALLOCATE;
|
|||
|
|
|||
|
IoSetCompletionRoutine(Irp,
|
|||
|
ParAllocateCompletionRoutine,
|
|||
|
Extension, TRUE, TRUE, TRUE);
|
|||
|
|
|||
|
// DbgPrint("ParVDMAllocPort: Sending Request and exiting\n");
|
|||
|
return IoCallDriver(Extension->PortDeviceObject, Irp);
|
|||
|
}
|
|||
|
|
|||
|
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_PENDING - Request pending.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION extension;
|
|||
|
PIO_STACK_LOCATION currentStack;
|
|||
|
NTSTATUS status = STATUS_INVALID_PARAMETER;
|
|||
|
|
|||
|
extension = DeviceObject->DeviceExtension;
|
|||
|
currentStack = IoGetCurrentIrpStackLocation(Irp);
|
|||
|
|
|||
|
switch(currentStack->Parameters.DeviceIoControl.IoControlCode) {
|
|||
|
|
|||
|
case IOCTL_VDM_PAR_WRITE_DATA_PORT: {
|
|||
|
|
|||
|
// DbgPrint("ParVDMDeviceControl: IOCTL_VDM_PAR_WRITE_DATA_PORT\n");
|
|||
|
if(extension->PortOwned) {
|
|||
|
UCHAR *data = (PUCHAR) Irp->AssociatedIrp.SystemBuffer;
|
|||
|
ULONG length = currentStack->Parameters.DeviceIoControl.InputBufferLength;
|
|||
|
|
|||
|
Irp->IoStatus.Information = 0;
|
|||
|
|
|||
|
if (length == 1) {
|
|||
|
WRITE_PORT_UCHAR(extension->Controller +
|
|||
|
PARALLEL_DATA_OFFSET,
|
|||
|
*data);
|
|||
|
} else {
|
|||
|
|
|||
|
for(; length != 0; length--, data++) {
|
|||
|
WRITE_PORT_UCHAR(extension->Controller +
|
|||
|
PARALLEL_DATA_OFFSET,
|
|||
|
*data);
|
|||
|
// KeStallExecutionProcessor(1);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
status = STATUS_ACCESS_DENIED;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
case IOCTL_VDM_PAR_READ_STATUS_PORT: {
|
|||
|
|
|||
|
// DbgPrint("ParVDMDeviceControl: IOCTL_VDM_PAR_READ_STATUS_PORT\n");
|
|||
|
if (extension->PortOwned) {
|
|||
|
|
|||
|
if(currentStack->Parameters.DeviceIoControl.OutputBufferLength >= sizeof(UCHAR)) {
|
|||
|
|
|||
|
*(PUCHAR)(Irp->AssociatedIrp.SystemBuffer) =
|
|||
|
READ_PORT_UCHAR(extension->Controller +
|
|||
|
PARALLEL_STATUS_OFFSET);
|
|||
|
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
Irp->IoStatus.Information = sizeof(UCHAR);
|
|||
|
|
|||
|
} else {
|
|||
|
status = STATUS_INVALID_PARAMETER;
|
|||
|
}
|
|||
|
} else {
|
|||
|
status = STATUS_ACCESS_DENIED;
|
|||
|
}
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
case IOCTL_VDM_PAR_WRITE_CONTROL_PORT: {
|
|||
|
|
|||
|
// DbgPrint("ParVDMDeviceControl: IOCTL_VDM_PAR_WRITE_CONTROL_PORT\n");
|
|||
|
if(extension->PortOwned) {
|
|||
|
|
|||
|
if(currentStack->Parameters.DeviceIoControl.OutputBufferLength >= sizeof(UCHAR)) {
|
|||
|
|
|||
|
WRITE_PORT_UCHAR(
|
|||
|
extension->Controller + PARALLEL_CONTROL_OFFSET,
|
|||
|
*(PUCHAR)(Irp->AssociatedIrp.SystemBuffer)
|
|||
|
);
|
|||
|
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
Irp->IoStatus.Information = sizeof(UCHAR);
|
|||
|
} else {
|
|||
|
status = STATUS_INVALID_PARAMETER;
|
|||
|
}
|
|||
|
} else {
|
|||
|
status = STATUS_ACCESS_DENIED;
|
|||
|
}
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
default: {
|
|||
|
// DbgPrint("ParVDMDeviceControl: Unknown IOCTL\n");
|
|||
|
status = STATUS_INVALID_PARAMETER;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// DbgPrint("ParVDMDeviceControl: Exit with status %#08lx\n", status);
|
|||
|
|
|||
|
Irp->IoStatus.Status = status;
|
|||
|
IoCompleteRequest(Irp, IO_PARALLEL_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;
|
|||
|
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);
|
|||
|
}
|
|||
|
|
|||
|
if( extension->ParPortName.Buffer ) {
|
|||
|
RtlFreeUnicodeString( &extension->ParPortName );
|
|||
|
}
|
|||
|
|
|||
|
IoDeleteDevice(currentDevice);
|
|||
|
}
|
|||
|
|
|||
|
if( OpenCloseMutex ) {
|
|||
|
ExFreePool( OpenCloseMutex );
|
|||
|
OpenCloseMutex = NULL;
|
|||
|
}
|
|||
|
}
|