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;
|
||
}
|
||
}
|