2786 lines
75 KiB
C
2786 lines
75 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1993 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
parport.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module contains the code for the parallel port driver.
|
|||
|
|
|||
|
This driver creates a device for each parallel port on the
|
|||
|
system. It increments 'IoGetConfigurationInformation()->ParallelCount'
|
|||
|
once for each parallel port. Each device created (\Device\ParallelPort0,
|
|||
|
\DeviceParallelPort1,...) supports a number of internal ioctls that
|
|||
|
allow parallel class drivers to get information about the parallel port and
|
|||
|
to share the parallel port.
|
|||
|
|
|||
|
IOCTL_INTERNAL_GET_PARALLEL_PORT_INFO returns the location
|
|||
|
and span of the register set for the parallel port. This ioctl
|
|||
|
also returns callback functions for 'FreePort', 'TryAllocatePort',
|
|||
|
and 'QueryNumWaiters' (more on these below). This ioctl
|
|||
|
should be called by a class driver during 'DriverEntry' and the
|
|||
|
information added to the class driver's device extension.
|
|||
|
|
|||
|
A parallel class driver should never touch the parallel port
|
|||
|
registers returned by IOCTL_INTERNAL_GET_PARALLEL_PORT_INFO
|
|||
|
without first allocating the port from the parallel port driver.
|
|||
|
|
|||
|
The port can be allocated from IRQL <= DISPATCH_LEVEL by calling
|
|||
|
IOCTL_INTERNAL_PARALLEL_PORT_ALLOCATE, or 'TryAllocatePort'.
|
|||
|
The first call is the simplest: the IRP will be queued in the
|
|||
|
parallel port driver until the port is free and then will return
|
|||
|
with a successful status. The class driver may cancel this IRP
|
|||
|
at any time which serves as a mechanism to timeout an allocate
|
|||
|
request. It is often convenient to use an incomming read or
|
|||
|
write IRP and pass it down to the port driver as an ALLOCATE. That
|
|||
|
way the class driver may avoid having to do its own queueing.
|
|||
|
The 'TryAllocatePort' call returns immediately from the port
|
|||
|
driver with a TRUE status if the port was allocated or an
|
|||
|
FALSE status if the port was busy.
|
|||
|
|
|||
|
Once the port is allocated, it is owned by the allocating class
|
|||
|
driver until a 'FreePort' call is made. This releases the port
|
|||
|
and wakes up the next caller.
|
|||
|
|
|||
|
The 'QueryNumWaiters' call which can be made at IRQL <= DISPATCH_LEVEL
|
|||
|
is useful to check and see if there are other class drivers waiting for the
|
|||
|
port. In this way, a class driver that needs to hold on to the
|
|||
|
port for an extended period of time (e.g. tape backup) can let
|
|||
|
go of the port if it detects another class driver needing service.
|
|||
|
|
|||
|
If a class driver wishes to use the parallel port's interrupt then
|
|||
|
it should connect to this interrupt by calling
|
|||
|
IOCTL_INTERNAL_PARALLEL_CONNECT_INTERRUPT during its DriverEntry
|
|||
|
routine. Besides giving the port driver an interrupt service routine
|
|||
|
the class driver may optionally give the port driver a deferred
|
|||
|
port check routine. Such a routine would be called whenever the
|
|||
|
port is left free. This would allow the class driver to make sure
|
|||
|
that interrupts were turned on whenever the port was left idle.
|
|||
|
If the driver using the interrupt has an Unload routine
|
|||
|
then it should call IOCTL_INTERNAL_PARALLEL_DISCONNECT_INTERRUPT
|
|||
|
in its Unload routine.
|
|||
|
|
|||
|
If a class driver's interrupt service routine is called when this
|
|||
|
class driver does not own the port, the class driver may attempt
|
|||
|
to grap the port quickly if it is free by calling the
|
|||
|
'TryAllocatePortAtInterruptLevel' function. This function is returned
|
|||
|
when the class driver connects to the interrupt. The class driver may
|
|||
|
also use the 'FreePortFromInterruptLevel' function to free the port.
|
|||
|
|
|||
|
Please refer to the PARSIMP driver code for a template of a simple
|
|||
|
parallel class driver. This template implements simple allocation and
|
|||
|
freeing of the parallel port on an IRP by IRP basis. It also
|
|||
|
has optional code for timing out allocation requests and for
|
|||
|
using the parallel port interrupt.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Anthony V. Ercolano 1-Aug-1992
|
|||
|
Norbert P. Kusters 18-Oct-1993
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
Kernel mode
|
|||
|
|
|||
|
Revision History :
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "ntddk.h"
|
|||
|
#include "parallel.h"
|
|||
|
#include "parport.h"
|
|||
|
#include "parlog.h"
|
|||
|
|
|||
|
#ifdef POOL_TAGGING
|
|||
|
#ifdef ExAllocatePool
|
|||
|
#undef ExAllocatePool
|
|||
|
#endif
|
|||
|
#define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,'PraP')
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// This is the actual definition of ParDebugLevel.
|
|||
|
// Note that it is only defined if this is a "debug"
|
|||
|
// build.
|
|||
|
//
|
|||
|
#if DBG
|
|||
|
extern ULONG PptDebugLevel = 0;
|
|||
|
#endif
|
|||
|
|
|||
|
static const PHYSICAL_ADDRESS PhysicalZero = {0};
|
|||
|
|
|||
|
typedef struct _PARALLEL_FIRMWARE_DATA {
|
|||
|
PDRIVER_OBJECT DriverObject;
|
|||
|
ULONG ControllersFound;
|
|||
|
LIST_ENTRY ConfigList;
|
|||
|
} PARALLEL_FIRMWARE_DATA, *PPARALLEL_FIRMWARE_DATA;
|
|||
|
|
|||
|
typedef enum _PPT_MEM_COMPARES {
|
|||
|
AddressesAreEqual,
|
|||
|
AddressesOverlap,
|
|||
|
AddressesAreDisjoint
|
|||
|
} PPT_MEM_COMPARES, *PPPT_MEM_COMPARES;
|
|||
|
|
|||
|
typedef struct _SYNCHRONIZED_COUNT_CONTEXT {
|
|||
|
PLONG Count;
|
|||
|
LONG NewCount;
|
|||
|
} SYNCHRONIZED_COUNT_CONTEXT, *PSYNCHRONIZED_COUNT_CONTEXT;
|
|||
|
|
|||
|
typedef struct _SYNCHRONIZED_LIST_CONTEXT {
|
|||
|
PLIST_ENTRY List;
|
|||
|
PLIST_ENTRY NewEntry;
|
|||
|
} SYNCHRONIZED_LIST_CONTEXT, *PSYNCHRONIZED_LIST_CONTEXT;
|
|||
|
|
|||
|
typedef struct _SYNCHRONIZED_DISCONNECT_CONTEXT {
|
|||
|
PDEVICE_EXTENSION Extension;
|
|||
|
PPARALLEL_INTERRUPT_SERVICE_ROUTINE IsrInfo;
|
|||
|
} SYNCHRONIZED_DISCONNECT_CONTEXT, *PSYNCHRONIZED_DISCONNECT_CONTEXT;
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
DriverEntry(
|
|||
|
IN PDRIVER_OBJECT DriverObject,
|
|||
|
IN PUNICODE_STRING RegistryPath
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
PptLogError(
|
|||
|
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
|
|||
|
);
|
|||
|
|
|||
|
PPT_MEM_COMPARES
|
|||
|
PptMemCompare(
|
|||
|
IN PHYSICAL_ADDRESS A,
|
|||
|
IN ULONG SpanOfA,
|
|||
|
IN PHYSICAL_ADDRESS B,
|
|||
|
IN ULONG SpanOfB
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
PptGetConfigInfo(
|
|||
|
IN PDRIVER_OBJECT DriverObject,
|
|||
|
IN PUNICODE_STRING RegistryPath,
|
|||
|
OUT PLIST_ENTRY ConfigList
|
|||
|
);
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
PptConfigCallBack(
|
|||
|
IN PVOID Context,
|
|||
|
IN PUNICODE_STRING PathName,
|
|||
|
IN INTERFACE_TYPE BusType,
|
|||
|
IN ULONG BusNumber,
|
|||
|
IN PKEY_VALUE_FULL_INFORMATION* BusInformation,
|
|||
|
IN CONFIGURATION_TYPE ControllerType,
|
|||
|
IN ULONG ControllerNumber,
|
|||
|
IN PKEY_VALUE_FULL_INFORMATION* ControllerInformation,
|
|||
|
IN CONFIGURATION_TYPE PeripheralType,
|
|||
|
IN ULONG PeripheralNumber,
|
|||
|
IN PKEY_VALUE_FULL_INFORMATION* PeripheralInformation
|
|||
|
);
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
PptItemCallBack(
|
|||
|
IN PVOID Context,
|
|||
|
IN PUNICODE_STRING PathName,
|
|||
|
IN INTERFACE_TYPE BusType,
|
|||
|
IN ULONG BusNumber,
|
|||
|
IN PKEY_VALUE_FULL_INFORMATION* BusInformation,
|
|||
|
IN CONFIGURATION_TYPE ControllerType,
|
|||
|
IN ULONG ControllerNumber,
|
|||
|
IN PKEY_VALUE_FULL_INFORMATION* ControllerInformation,
|
|||
|
IN CONFIGURATION_TYPE PeripheralType,
|
|||
|
IN ULONG PeripheralNumber,
|
|||
|
IN PKEY_VALUE_FULL_INFORMATION* PeripheralInformation
|
|||
|
);
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
PptInitializeController(
|
|||
|
IN PDRIVER_OBJECT DriverObject,
|
|||
|
IN PUNICODE_STRING RegistryPath,
|
|||
|
IN PCONFIG_DATA ConfigData
|
|||
|
);
|
|||
|
|
|||
|
PVOID
|
|||
|
PptGetMappedAddress(
|
|||
|
IN INTERFACE_TYPE BusType,
|
|||
|
IN ULONG BusNumber,
|
|||
|
IN PHYSICAL_ADDRESS IoAddress,
|
|||
|
IN ULONG NumberOfBytes,
|
|||
|
IN ULONG AddressSpace,
|
|||
|
OUT PBOOLEAN MappedAddress
|
|||
|
);
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
PptPutInConfigList(
|
|||
|
IN PDRIVER_OBJECT DriverObject,
|
|||
|
IN OUT PLIST_ENTRY ConfigList,
|
|||
|
IN PCONFIG_DATA New
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
PptFreePortDpc(
|
|||
|
IN PKDPC Dpc,
|
|||
|
IN OUT PVOID Extension,
|
|||
|
IN PVOID SystemArgument1,
|
|||
|
IN PVOID SystemArgument2
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
PptReportResourcesDevice(
|
|||
|
IN PDEVICE_EXTENSION Extension,
|
|||
|
IN BOOLEAN ClaimInterrupt,
|
|||
|
OUT PBOOLEAN ConflictDetected
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
PptUnReportResourcesDevice(
|
|||
|
IN OUT PDEVICE_EXTENSION Extension
|
|||
|
);
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
PptInterruptService(
|
|||
|
IN PKINTERRUPT Interrupt,
|
|||
|
IN PVOID Extension
|
|||
|
);
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
PptTryAllocatePort(
|
|||
|
IN PVOID Extension
|
|||
|
);
|
|||
|
|
|||
|
VOID
|
|||
|
PptFreePort(
|
|||
|
IN PVOID Extension
|
|||
|
);
|
|||
|
|
|||
|
ULONG
|
|||
|
PptQueryNumWaiters(
|
|||
|
IN PVOID Extension
|
|||
|
);
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
PptIsNecR98Machine(
|
|||
|
void
|
|||
|
);
|
|||
|
|
|||
|
|
|||
|
#ifdef ALLOC_PRAGMA
|
|||
|
#pragma alloc_text(INIT,DriverEntry)
|
|||
|
#pragma alloc_text(INIT,PptGetConfigInfo)
|
|||
|
#pragma alloc_text(INIT,PptConfigCallBack)
|
|||
|
#pragma alloc_text(INIT,PptItemCallBack)
|
|||
|
#pragma alloc_text(INIT,PptInitializeController)
|
|||
|
#pragma alloc_text(INIT,PptGetMappedAddress)
|
|||
|
#pragma alloc_text(INIT,PptPutInConfigList)
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// Keep track of GET and RELEASE port info.
|
|||
|
//
|
|||
|
ULONG PortInfoReferenceCount = 0;
|
|||
|
PFAST_MUTEX PortInfoMutex = NULL;
|
|||
|
|
|||
|
|
|||
|
|
|||
|
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.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
LIST_ENTRY configList;
|
|||
|
PCONFIG_DATA currentConfig;
|
|||
|
PLIST_ENTRY head;
|
|||
|
|
|||
|
PptGetConfigInfo(DriverObject, RegistryPath, &configList);
|
|||
|
|
|||
|
//
|
|||
|
// Initialize each item in the list of configuration records.
|
|||
|
//
|
|||
|
|
|||
|
while (!IsListEmpty(&configList)) {
|
|||
|
|
|||
|
head = RemoveHeadList(&configList);
|
|||
|
currentConfig = CONTAINING_RECORD(head, CONFIG_DATA, ConfigList);
|
|||
|
|
|||
|
PptInitializeController(DriverObject, RegistryPath, currentConfig);
|
|||
|
}
|
|||
|
|
|||
|
if (!DriverObject->DeviceObject) {
|
|||
|
return STATUS_NO_SUCH_DEVICE;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Initialize the Driver Object with driver's entry points
|
|||
|
//
|
|||
|
|
|||
|
DriverObject->MajorFunction[IRP_MJ_CREATE] = PptDispatchCreateClose;
|
|||
|
DriverObject->MajorFunction[IRP_MJ_CLOSE] = PptDispatchCreateClose;
|
|||
|
DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] =
|
|||
|
PptDispatchDeviceControl;
|
|||
|
DriverObject->MajorFunction[IRP_MJ_CLEANUP] = PptDispatchCleanup;
|
|||
|
DriverObject->DriverUnload = PptUnload;
|
|||
|
|
|||
|
//
|
|||
|
// Let this driver be paged until someone requests PORT_INFO.
|
|||
|
//
|
|||
|
PortInfoMutex = ExAllocatePool(NonPagedPool, sizeof(FAST_MUTEX));
|
|||
|
if (!PortInfoMutex) {
|
|||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
ExInitializeFastMutex(PortInfoMutex);
|
|||
|
|
|||
|
MmPageEntireDriver(DriverEntry);
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
PptLogError(
|
|||
|
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 (PptMemCompare(P1, 1, PhysicalZero, 1) != AddressesAreEqual) {
|
|||
|
dumpToAllocate = (SHORT) sizeof(PHYSICAL_ADDRESS);
|
|||
|
}
|
|||
|
|
|||
|
if (PptMemCompare(P2, 1, PhysicalZero, 1) != AddressesAreEqual) {
|
|||
|
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);
|
|||
|
}
|
|||
|
|
|||
|
PPT_MEM_COMPARES
|
|||
|
PptMemCompare(
|
|||
|
IN PHYSICAL_ADDRESS A,
|
|||
|
IN ULONG SpanOfA,
|
|||
|
IN PHYSICAL_ADDRESS B,
|
|||
|
IN ULONG SpanOfB
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine compares two phsical address.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
A - Supplies one half of the comparison.
|
|||
|
|
|||
|
SpanOfA - Supplies the span of A in units of bytes.
|
|||
|
|
|||
|
B - Supplies the other half of the comparison.
|
|||
|
|
|||
|
SpanOfB - Supplies the span of B in units of bytes.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
The result of the comparison.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
LARGE_INTEGER a, b;
|
|||
|
LARGE_INTEGER lower, higher;
|
|||
|
ULONG lowerSpan;
|
|||
|
|
|||
|
a.LowPart = A.LowPart;
|
|||
|
a.HighPart = A.HighPart;
|
|||
|
b.LowPart = B.LowPart;
|
|||
|
b.HighPart = B.HighPart;
|
|||
|
|
|||
|
if (a.QuadPart == b.QuadPart) {
|
|||
|
return AddressesAreEqual;
|
|||
|
}
|
|||
|
|
|||
|
if (a.QuadPart > b.QuadPart) {
|
|||
|
higher = a;
|
|||
|
lower = b;
|
|||
|
lowerSpan = SpanOfB;
|
|||
|
} else {
|
|||
|
higher = b;
|
|||
|
lower = a;
|
|||
|
lowerSpan = SpanOfA;
|
|||
|
}
|
|||
|
|
|||
|
if (higher.QuadPart - lower.QuadPart >= lowerSpan) {
|
|||
|
return AddressesAreDisjoint;
|
|||
|
}
|
|||
|
|
|||
|
return AddressesOverlap;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
PptGetConfigInfo(
|
|||
|
IN PDRIVER_OBJECT DriverObject,
|
|||
|
IN PUNICODE_STRING RegistryPath,
|
|||
|
OUT PLIST_ENTRY ConfigList
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine will "return" a list of configuration records
|
|||
|
for the parallel ports to initialize.
|
|||
|
|
|||
|
It will first query the firmware data. It will then look
|
|||
|
for "user" specified parallel ports in the registry. It
|
|||
|
will place the user specified parallel ports in the passed
|
|||
|
in list.
|
|||
|
|
|||
|
After it finds all of the user specified ports, it will
|
|||
|
attempt to add the firmware parallel ports into the passed
|
|||
|
in lists. The insert in the list code detects conflicts
|
|||
|
and rejects a new port. In this way we can prevent
|
|||
|
firmware found ports from overiding information specified
|
|||
|
by the "user".
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DriverObject - Supplies the driver object.
|
|||
|
|
|||
|
RegistryPath - Supplies the registry path for this driver.
|
|||
|
|
|||
|
ConfigList - Returns a list of configuration records for
|
|||
|
the parallel ports to initialize.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PARALLEL_FIRMWARE_DATA firmware;
|
|||
|
CONFIGURATION_TYPE sc;
|
|||
|
INTERFACE_TYPE iType;
|
|||
|
PLIST_ENTRY head;
|
|||
|
PCONFIG_DATA firmwareData;
|
|||
|
|
|||
|
InitializeListHead(ConfigList);
|
|||
|
|
|||
|
RtlZeroMemory(&firmware, sizeof(PARALLEL_FIRMWARE_DATA));
|
|||
|
firmware.DriverObject = DriverObject;
|
|||
|
firmware.ControllersFound = IoGetConfigurationInformation()->ParallelCount;
|
|||
|
InitializeListHead(&firmware.ConfigList);
|
|||
|
|
|||
|
//
|
|||
|
// First we query the hardware registry for all of
|
|||
|
// the firmware defined ports. We loop over all of
|
|||
|
// the busses.
|
|||
|
//
|
|||
|
|
|||
|
sc = ParallelController;
|
|||
|
for (iType = 0; iType < MaximumInterfaceType; iType++) {
|
|||
|
IoQueryDeviceDescription(&iType, NULL, &sc, NULL, NULL, NULL,
|
|||
|
PptConfigCallBack, &firmware);
|
|||
|
}
|
|||
|
|
|||
|
while (!IsListEmpty(&firmware.ConfigList)) {
|
|||
|
|
|||
|
head = RemoveHeadList(&firmware.ConfigList);
|
|||
|
firmwareData = CONTAINING_RECORD(head, CONFIG_DATA, ConfigList);
|
|||
|
|
|||
|
if (!PptPutInConfigList(DriverObject, ConfigList, firmwareData)) {
|
|||
|
|
|||
|
PptLogError(DriverObject, NULL, PhysicalZero, PhysicalZero,
|
|||
|
0, 0, 0, 1, STATUS_SUCCESS, PAR_USER_OVERRIDE);
|
|||
|
|
|||
|
ParDump(PARERRORS,
|
|||
|
("PARPORT: Conflict detected with user data for firmware port %wZ\n"
|
|||
|
"-------- User data will overides firmware data\n",
|
|||
|
&firmwareData->NtNameForPort));
|
|||
|
|
|||
|
ExFreePool(firmwareData->NtNameForPort.Buffer);
|
|||
|
ExFreePool(firmwareData);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
PptConfigCallBack(
|
|||
|
IN PVOID Context,
|
|||
|
IN PUNICODE_STRING PathName,
|
|||
|
IN INTERFACE_TYPE BusType,
|
|||
|
IN ULONG BusNumber,
|
|||
|
IN PKEY_VALUE_FULL_INFORMATION* BusInformation,
|
|||
|
IN CONFIGURATION_TYPE ControllerType,
|
|||
|
IN ULONG ControllerNumber,
|
|||
|
IN PKEY_VALUE_FULL_INFORMATION* ControllerInformation,
|
|||
|
IN CONFIGURATION_TYPE PeripheralType,
|
|||
|
IN ULONG PeripheralNumber,
|
|||
|
IN PKEY_VALUE_FULL_INFORMATION* PeripheralInformation
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is used to acquire all of the configuration
|
|||
|
information for each parallel controller found by the firmware
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Context - Supplies the pointer to the list head of the list
|
|||
|
of configuration records that we are building up.
|
|||
|
|
|||
|
PathName - Not Used.
|
|||
|
|
|||
|
BusType - Supplies the bus type. Internal, Isa, ...
|
|||
|
|
|||
|
BusNumber - Supplies which bus number if we are on a multibus
|
|||
|
system.
|
|||
|
|
|||
|
BusInformation - Not Used.
|
|||
|
|
|||
|
ControllerType - Supplies the controller type. Should always be
|
|||
|
ParallelController.
|
|||
|
|
|||
|
ControllerNumber - Supplies which controller number if there is more
|
|||
|
than one controller in the system.
|
|||
|
|
|||
|
ControllerInformation - Supplies an array of pointers to the three pieces
|
|||
|
of registry information.
|
|||
|
|
|||
|
PeripheralType - Undefined for this call.
|
|||
|
|
|||
|
PeripheralNumber - Undefined for this call.
|
|||
|
|
|||
|
PeripheralInformation - Undefined for this call.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_SUCCESS - Success.
|
|||
|
STATUS_INSUFFICIENT_RESOURCES - Not all of the resource information
|
|||
|
could be acquired.
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PPARALLEL_FIRMWARE_DATA config = Context;
|
|||
|
PCM_FULL_RESOURCE_DESCRIPTOR controllerData;
|
|||
|
PCONFIG_DATA controller;
|
|||
|
ULONG i;
|
|||
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR partial;
|
|||
|
BOOLEAN foundPort, foundInterrupt;
|
|||
|
WCHAR ntNumberBuffer[100];
|
|||
|
UNICODE_STRING ntNumberString;
|
|||
|
UNICODE_STRING temp;
|
|||
|
|
|||
|
ASSERT(ControllerType == ParallelController);
|
|||
|
|
|||
|
//
|
|||
|
// Bail if some fool wrote a loader.
|
|||
|
//
|
|||
|
|
|||
|
if (!ControllerInformation[IoQueryDeviceConfigurationData]->DataLength) {
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
controllerData =
|
|||
|
(PCM_FULL_RESOURCE_DESCRIPTOR)
|
|||
|
(((PUCHAR)ControllerInformation[IoQueryDeviceConfigurationData]) +
|
|||
|
ControllerInformation[IoQueryDeviceConfigurationData]->DataOffset);
|
|||
|
|
|||
|
controller = ExAllocatePool(PagedPool, sizeof(CONFIG_DATA));
|
|||
|
if (!controller) {
|
|||
|
PptLogError(config->DriverObject, NULL, PhysicalZero, PhysicalZero, 0,
|
|||
|
0, 0, 2, STATUS_SUCCESS, PAR_INSUFFICIENT_RESOURCES);
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARERRORS,
|
|||
|
("PARPORT: Couldn't allocate memory for the configuration data\n"
|
|||
|
"-------- for firmware data\n")
|
|||
|
);
|
|||
|
|
|||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
RtlZeroMemory(controller, sizeof(CONFIG_DATA));
|
|||
|
InitializeListHead(&controller->ConfigList);
|
|||
|
controller->InterfaceType = BusType;
|
|||
|
controller->BusNumber = BusNumber;
|
|||
|
|
|||
|
//
|
|||
|
// We need to get the following information out of the partial
|
|||
|
// resource descriptors.
|
|||
|
//
|
|||
|
// The base address and span covered by the parallel controllers
|
|||
|
// registers.
|
|||
|
//
|
|||
|
// It is not defined how these appear in the partial resource
|
|||
|
// lists, so we will just loop over all of them. If we find
|
|||
|
// something we don't recognize, we drop that information on
|
|||
|
// the floor. When we have finished going through all the
|
|||
|
// partial information, we validate that we got the above
|
|||
|
// information.
|
|||
|
//
|
|||
|
|
|||
|
foundInterrupt = foundPort = FALSE;
|
|||
|
for (i = 0; i < controllerData->PartialResourceList.Count; i++) {
|
|||
|
|
|||
|
partial = &controllerData->PartialResourceList.PartialDescriptors[i];
|
|||
|
|
|||
|
if (partial->Type == CmResourceTypePort) {
|
|||
|
|
|||
|
foundPort = TRUE;
|
|||
|
|
|||
|
controller->SpanOfController = partial->u.Port.Length;
|
|||
|
controller->Controller = partial->u.Port.Start;
|
|||
|
controller->AddressSpace = partial->Flags;
|
|||
|
|
|||
|
if((controller->SpanOfController == 0x1000) &&
|
|||
|
(PptIsNecR98Machine())) {
|
|||
|
|
|||
|
ParDump(0, ("parport!PptConfigCallBack - "
|
|||
|
"found R98 machine with firmware bug. "
|
|||
|
"Truncating SpanOfController to 8\n"));
|
|||
|
controller->SpanOfController = 8;
|
|||
|
}
|
|||
|
|
|||
|
} else if (partial->Type == CmResourceTypeInterrupt) {
|
|||
|
|
|||
|
foundInterrupt = TRUE;
|
|||
|
|
|||
|
controller->InterruptLevel = partial->u.Interrupt.Level;
|
|||
|
controller->InterruptVector = partial->u.Interrupt.Vector;
|
|||
|
controller->InterruptAffinity = partial->u.Interrupt.Affinity;
|
|||
|
if (partial->Flags & CM_RESOURCE_INTERRUPT_LATCHED) {
|
|||
|
controller->InterruptMode = Latched;
|
|||
|
} else {
|
|||
|
controller->InterruptMode = LevelSensitive;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (!foundPort || !foundInterrupt) {
|
|||
|
|
|||
|
PptLogError(config->DriverObject, NULL, PhysicalZero, PhysicalZero, 0,
|
|||
|
0, 0, 3, STATUS_SUCCESS, PAR_NOT_ENOUGH_CONFIG_INFO);
|
|||
|
|
|||
|
ExFreePool(controller);
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// The following are so the we can form the
|
|||
|
// name following the \Device
|
|||
|
// and the default name that will be symbolic
|
|||
|
// linked to the device and the object directory
|
|||
|
// that link will go in.
|
|||
|
//
|
|||
|
|
|||
|
ntNumberString.Length = 0;
|
|||
|
ntNumberString.MaximumLength = 100;
|
|||
|
ntNumberString.Buffer = ntNumberBuffer;
|
|||
|
|
|||
|
if (!NT_SUCCESS(RtlIntegerToUnicodeString(config->ControllersFound,
|
|||
|
10, &ntNumberString))) {
|
|||
|
|
|||
|
PptLogError(config->DriverObject, NULL, controller->Controller,
|
|||
|
PhysicalZero, 0, 0, 0, 4, STATUS_SUCCESS,
|
|||
|
PAR_INSUFFICIENT_RESOURCES);
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARERRORS,
|
|||
|
("PARPORT: Couldn't convert NT controller number\n"
|
|||
|
"-------- to unicode for firmware data: %d\n",
|
|||
|
config->ControllersFound)
|
|||
|
);
|
|||
|
|
|||
|
ExFreePool(controller);
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
RtlInitUnicodeString(&controller->NtNameForPort, NULL);
|
|||
|
RtlInitUnicodeString(&temp, DD_PARALLEL_PORT_BASE_NAME_U);
|
|||
|
|
|||
|
controller->NtNameForPort.MaximumLength = temp.Length +
|
|||
|
ntNumberString.Length + sizeof(WCHAR);
|
|||
|
|
|||
|
controller->NtNameForPort.Buffer = ExAllocatePool(PagedPool,
|
|||
|
controller->NtNameForPort.MaximumLength);
|
|||
|
|
|||
|
if (!controller->NtNameForPort.Buffer) {
|
|||
|
|
|||
|
PptLogError(config->DriverObject, NULL, controller->Controller,
|
|||
|
PhysicalZero, 0, 0, 0, 5, STATUS_SUCCESS,
|
|||
|
PAR_INSUFFICIENT_RESOURCES);
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARERRORS,
|
|||
|
("PARPORT: Couldn't allocate memory for NT\n"
|
|||
|
"-------- name for NT firmware data: %d\n",
|
|||
|
config->ControllersFound)
|
|||
|
);
|
|||
|
|
|||
|
ExFreePool(controller);
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
RtlZeroMemory(controller->NtNameForPort.Buffer,
|
|||
|
controller->NtNameForPort.MaximumLength);
|
|||
|
|
|||
|
RtlAppendUnicodeStringToString(&controller->NtNameForPort, &temp);
|
|||
|
|
|||
|
RtlAppendUnicodeStringToString(&controller->NtNameForPort, &ntNumberString);
|
|||
|
|
|||
|
InsertTailList(&config->ConfigList, &controller->ConfigList);
|
|||
|
|
|||
|
config->ControllersFound++;
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
PptItemCallBack(
|
|||
|
IN PVOID Context,
|
|||
|
IN PUNICODE_STRING PathName,
|
|||
|
IN INTERFACE_TYPE BusType,
|
|||
|
IN ULONG BusNumber,
|
|||
|
IN PKEY_VALUE_FULL_INFORMATION* BusInformation,
|
|||
|
IN CONFIGURATION_TYPE ControllerType,
|
|||
|
IN ULONG ControllerNumber,
|
|||
|
IN PKEY_VALUE_FULL_INFORMATION* ControllerInformation,
|
|||
|
IN CONFIGURATION_TYPE PeripheralType,
|
|||
|
IN ULONG PeripheralNumber,
|
|||
|
IN PKEY_VALUE_FULL_INFORMATION* PeripheralInformation
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This IoQueryDeviceDescription callback merely sets the context
|
|||
|
argument to TRUE.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Context - Supplies a boolean to set to TRUE.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_SUCCESS - Success.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
*((BOOLEAN *)Context) = TRUE;
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
PptPutInConfigList(
|
|||
|
IN PDRIVER_OBJECT DriverObject,
|
|||
|
IN OUT PLIST_ENTRY ConfigList,
|
|||
|
IN PCONFIG_DATA New
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Given a new config record and a config list, this routine
|
|||
|
will perform a check to make sure that the new record doesn't
|
|||
|
conflict with old records.
|
|||
|
|
|||
|
If everything checks out it will insert the new config record
|
|||
|
into the config list.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DriverObject - Supplies the driver we are attempting to get
|
|||
|
configuration information for.
|
|||
|
|
|||
|
ConfigList - Supplies the listhead for a list of configuration
|
|||
|
records for ports to control.
|
|||
|
|
|||
|
New - Supplies a pointer to new configuration record to add.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
FALSE - The new record was not added to the config list.
|
|||
|
TRUE - The new record was added to the config list.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PHYSICAL_ADDRESS PhysicalMax;
|
|||
|
PLIST_ENTRY current;
|
|||
|
PCONFIG_DATA oldConfig;
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARCONFIG,
|
|||
|
("PARPORT: Attempting to add %wZ\n"
|
|||
|
"-------- to the config list\n"
|
|||
|
"-------- PortAddress is %x\n"
|
|||
|
"-------- BusNumber is %d\n"
|
|||
|
"-------- BusType is %d\n"
|
|||
|
"-------- AddressSpace is %d\n",
|
|||
|
&New->NtNameForPort,
|
|||
|
New->Controller.LowPart,
|
|||
|
New->BusNumber,
|
|||
|
New->InterfaceType,
|
|||
|
New->AddressSpace
|
|||
|
)
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// We don't support any boards whose memory wraps around
|
|||
|
// the physical address space.
|
|||
|
//
|
|||
|
|
|||
|
PhysicalMax.LowPart = (ULONG)~0;
|
|||
|
PhysicalMax.HighPart = ~0;
|
|||
|
if (PptMemCompare(New->Controller, New->SpanOfController,
|
|||
|
PhysicalMax, 0) != AddressesAreDisjoint) {
|
|||
|
|
|||
|
PptLogError(DriverObject, NULL, New->Controller, PhysicalZero,
|
|||
|
0, 0, 0, 6, STATUS_SUCCESS, PAR_DEVICE_TOO_HIGH);
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARERRORS,
|
|||
|
("PARPORT: Error in config record for %wZ\n"
|
|||
|
"-------- registers rap around physical memory\n",
|
|||
|
&New->NtNameForPort)
|
|||
|
);
|
|||
|
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
//
|
|||
|
// Go through the list looking for previous devices
|
|||
|
// with the same address.
|
|||
|
//
|
|||
|
|
|||
|
for (current = ConfigList->Flink; current != ConfigList;
|
|||
|
current = current->Flink) {
|
|||
|
|
|||
|
|
|||
|
oldConfig = CONTAINING_RECORD(current, CONFIG_DATA, ConfigList);
|
|||
|
|
|||
|
//
|
|||
|
// We only care about ports that are on the same bus.
|
|||
|
//
|
|||
|
|
|||
|
if (oldConfig->InterfaceType == New->InterfaceType &&
|
|||
|
oldConfig->BusNumber == New->BusNumber) {
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARCONFIG,
|
|||
|
("PARPORT: Comparing it to %wZ\n"
|
|||
|
"-------- already in the config list\n"
|
|||
|
"-------- PortAddress is %x\n"
|
|||
|
"-------- BusNumber is %d\n"
|
|||
|
"-------- BusType is %d\n"
|
|||
|
"-------- AddressSpace is %d\n",
|
|||
|
&oldConfig->NtNameForPort,
|
|||
|
oldConfig->Controller.LowPart,
|
|||
|
oldConfig->BusNumber,
|
|||
|
oldConfig->InterfaceType,
|
|||
|
oldConfig->AddressSpace
|
|||
|
)
|
|||
|
);
|
|||
|
|
|||
|
if (PptMemCompare(New->Controller, New->SpanOfController,
|
|||
|
oldConfig->Controller,
|
|||
|
oldConfig->SpanOfController) !=
|
|||
|
AddressesAreDisjoint) {
|
|||
|
|
|||
|
PptLogError(DriverObject, NULL, New->Controller,
|
|||
|
oldConfig->Controller, 0, 0, 0, 7,
|
|||
|
STATUS_SUCCESS, PAR_CONTROL_OVERLAP);
|
|||
|
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
InsertTailList(ConfigList, &New->ConfigList);
|
|||
|
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
PptCleanupDevice(
|
|||
|
IN PDEVICE_EXTENSION Extension
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine will deallocate all of the memory used for
|
|||
|
a particular device. It will also disconnect any resources
|
|||
|
if need be.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Extension - Pointer to the device extension which is getting
|
|||
|
rid of all it's resources.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
ParDump(
|
|||
|
PARUNLOAD,
|
|||
|
("PARPORT: in PptCleanupDevice for extension: %x\n",Extension)
|
|||
|
);
|
|||
|
|
|||
|
if (Extension && Extension->UnMapRegisters) {
|
|||
|
MmUnmapIoSpace(Extension->PortInfo.Controller,
|
|||
|
Extension->PortInfo.SpanOfController);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
PptInitializeController(
|
|||
|
IN PDRIVER_OBJECT DriverObject,
|
|||
|
IN PUNICODE_STRING RegistryPath,
|
|||
|
IN PCONFIG_DATA ConfigData
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Really too many things to mention here. In general, it forms
|
|||
|
and sets up names, creates the device, translates bus relative
|
|||
|
items...
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DriverObject - Supplies the driver object to be used to create the
|
|||
|
device object.
|
|||
|
|
|||
|
RegistryPath - Supplies the registry path for this port driver.
|
|||
|
|
|||
|
ConfigData - Supplies the configuration record for this port.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_SUCCCESS if everything went ok. A !NT_SUCCESS status
|
|||
|
otherwise.
|
|||
|
|
|||
|
Notes:
|
|||
|
|
|||
|
This routine will deallocate the config data.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
UNICODE_STRING uniNameString, uniDeviceString;
|
|||
|
NTSTATUS status;
|
|||
|
PDEVICE_EXTENSION extension;
|
|||
|
BOOLEAN conflict;
|
|||
|
PDEVICE_OBJECT deviceObject;
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARCONFIG,
|
|||
|
("PARPORT: Initializing for configuration record of %wZ\n",
|
|||
|
&ConfigData->NtNameForPort)
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// Form a name like \Device\ParallelPort0.
|
|||
|
//
|
|||
|
// First we allocate space for the name.
|
|||
|
//
|
|||
|
|
|||
|
RtlInitUnicodeString(&uniNameString, NULL);
|
|||
|
RtlInitUnicodeString(&uniDeviceString, L"\\Device\\");
|
|||
|
uniNameString.MaximumLength = uniDeviceString.Length +
|
|||
|
ConfigData->NtNameForPort.Length +
|
|||
|
sizeof(WCHAR);
|
|||
|
uniNameString.Buffer = ExAllocatePool(PagedPool, uniNameString.MaximumLength);
|
|||
|
if (!uniNameString.Buffer) {
|
|||
|
|
|||
|
PptLogError(DriverObject, NULL, ConfigData->Controller, PhysicalZero,
|
|||
|
0, 0, 0, 8, STATUS_SUCCESS, PAR_INSUFFICIENT_RESOURCES);
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARERRORS,
|
|||
|
("PARPORT: Could not form Unicode name string for %wZ\n",
|
|||
|
&ConfigData->NtNameForPort)
|
|||
|
);
|
|||
|
|
|||
|
ExFreePool(ConfigData->NtNameForPort.Buffer);
|
|||
|
ExFreePool(ConfigData);
|
|||
|
|
|||
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
|
|||
|
RtlZeroMemory(uniNameString.Buffer, uniNameString.MaximumLength);
|
|||
|
RtlAppendUnicodeStringToString(&uniNameString, &uniDeviceString);
|
|||
|
RtlAppendUnicodeStringToString(&uniNameString, &ConfigData->NtNameForPort);
|
|||
|
|
|||
|
//
|
|||
|
// Create the device object for this device.
|
|||
|
//
|
|||
|
|
|||
|
status = IoCreateDevice(DriverObject, sizeof(DEVICE_EXTENSION),
|
|||
|
&uniNameString, FILE_DEVICE_PARALLEL_PORT,
|
|||
|
0, FALSE, &deviceObject);
|
|||
|
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
|
|||
|
PptLogError(DriverObject, NULL, ConfigData->Controller, PhysicalZero,
|
|||
|
0, 0, 0, 9, STATUS_SUCCESS, PAR_INSUFFICIENT_RESOURCES);
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARERRORS,
|
|||
|
("PARPORT: Could not create a device for %wZ\n",
|
|||
|
&ConfigData->NtNameForPort)
|
|||
|
);
|
|||
|
|
|||
|
ExFreePool(uniNameString.Buffer);
|
|||
|
ExFreePool(ConfigData->NtNameForPort.Buffer);
|
|||
|
ExFreePool(ConfigData);
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// We have created the device, increment the counter in the
|
|||
|
// IO system that keep track. Anyplace that we do an IoDeleteDevice
|
|||
|
// we need to decrement.
|
|||
|
//
|
|||
|
|
|||
|
IoGetConfigurationInformation()->ParallelCount++;
|
|||
|
|
|||
|
//
|
|||
|
// The device object has a pointer to an area of non-paged
|
|||
|
// pool allocated for this device. This will be the device
|
|||
|
// extension.
|
|||
|
//
|
|||
|
|
|||
|
extension = deviceObject->DeviceExtension;
|
|||
|
|
|||
|
//
|
|||
|
// Zero all of the memory associated with the device
|
|||
|
// extension.
|
|||
|
//
|
|||
|
|
|||
|
RtlZeroMemory(extension, sizeof(DEVICE_EXTENSION));
|
|||
|
|
|||
|
//
|
|||
|
// Get a "back pointer" to the device object.
|
|||
|
//
|
|||
|
|
|||
|
extension->DeviceObject = deviceObject;
|
|||
|
|
|||
|
//
|
|||
|
// Initialize 'WorkQueue' in extension.
|
|||
|
//
|
|||
|
|
|||
|
InitializeListHead(&extension->WorkQueue);
|
|||
|
extension->WorkQueueCount = -1;
|
|||
|
|
|||
|
//
|
|||
|
// Map the memory for the control registers for the parallel device
|
|||
|
// into virtual memory.
|
|||
|
//
|
|||
|
|
|||
|
extension->PortInfo.Controller = PptGetMappedAddress(
|
|||
|
ConfigData->InterfaceType, ConfigData->BusNumber,
|
|||
|
ConfigData->Controller, ConfigData->SpanOfController,
|
|||
|
(BOOLEAN) ConfigData->AddressSpace, &extension->UnMapRegisters);
|
|||
|
|
|||
|
if (!extension->PortInfo.Controller) {
|
|||
|
|
|||
|
PptLogError(deviceObject->DriverObject, deviceObject,
|
|||
|
ConfigData->Controller, PhysicalZero, 0, 0, 0, 10,
|
|||
|
STATUS_SUCCESS, PAR_REGISTERS_NOT_MAPPED);
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARERRORS,
|
|||
|
("PARPORT: Could not map memory for device registers for %wZ\n",
|
|||
|
&ConfigData->NtNameForPort)
|
|||
|
);
|
|||
|
|
|||
|
extension->UnMapRegisters = FALSE;
|
|||
|
status = STATUS_NONE_MAPPED;
|
|||
|
|
|||
|
goto ExtensionCleanup;
|
|||
|
}
|
|||
|
|
|||
|
extension->PortInfo.OriginalController = ConfigData->Controller;
|
|||
|
extension->PortInfo.SpanOfController = ConfigData->SpanOfController;
|
|||
|
extension->PortInfo.FreePort = PptFreePort;
|
|||
|
extension->PortInfo.TryAllocatePort = PptTryAllocatePort;
|
|||
|
extension->PortInfo.QueryNumWaiters = PptQueryNumWaiters;
|
|||
|
extension->PortInfo.Context = extension;
|
|||
|
|
|||
|
//
|
|||
|
// Save the configuration information about the interrupt.
|
|||
|
//
|
|||
|
extension->AddressSpace = ConfigData->AddressSpace;
|
|||
|
extension->InterfaceType = ConfigData->InterfaceType;
|
|||
|
extension->BusNumber = ConfigData->BusNumber;
|
|||
|
extension->InterruptLevel = ConfigData->InterruptLevel;
|
|||
|
extension->InterruptVector = ConfigData->InterruptVector;
|
|||
|
extension->InterruptAffinity = ConfigData->InterruptAffinity;
|
|||
|
extension->InterruptMode = ConfigData->InterruptMode;
|
|||
|
|
|||
|
//
|
|||
|
// Start off with an empty list of interrupt service routines.
|
|||
|
// Also, start off without the interrupt connected.
|
|||
|
//
|
|||
|
InitializeListHead(&extension->IsrList);
|
|||
|
extension->InterruptObject = NULL;
|
|||
|
extension->InterruptRefCount = 0;
|
|||
|
|
|||
|
//
|
|||
|
// Initialize the free port DPC.
|
|||
|
//
|
|||
|
KeInitializeDpc(&extension->FreePortDpc, PptFreePortDpc, extension);
|
|||
|
|
|||
|
//
|
|||
|
// Now the extension is all set up. Just report the resources.
|
|||
|
//
|
|||
|
|
|||
|
PptReportResourcesDevice(extension, FALSE, &conflict);
|
|||
|
|
|||
|
if (conflict) {
|
|||
|
|
|||
|
status = STATUS_NO_SUCH_DEVICE;
|
|||
|
|
|||
|
PptLogError(deviceObject->DriverObject, deviceObject,
|
|||
|
ConfigData->Controller, PhysicalZero, 0, 0, 0, 11,
|
|||
|
STATUS_SUCCESS, PAR_ADDRESS_CONFLICT);
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARERRORS,
|
|||
|
("PARPORT: Could not claim the device registers for %wZ\n",
|
|||
|
&ConfigData->NtNameForPort)
|
|||
|
);
|
|||
|
|
|||
|
goto ExtensionCleanup;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// If it was requested that the port be disabled, now is the
|
|||
|
// time to do it.
|
|||
|
//
|
|||
|
|
|||
|
if (ConfigData->DisablePort) {
|
|||
|
|
|||
|
status = STATUS_NO_SUCH_DEVICE;
|
|||
|
|
|||
|
PptLogError(deviceObject->DriverObject, deviceObject,
|
|||
|
ConfigData->Controller, PhysicalZero, 0, 0, 0, 12,
|
|||
|
STATUS_SUCCESS, PAR_DISABLED_PORT);
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARERRORS,
|
|||
|
("PARPORT: Port %wZ disabled as requested\n",
|
|||
|
&ConfigData->NtNameForPort)
|
|||
|
);
|
|||
|
|
|||
|
goto ExtensionCleanup;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Common error path cleanup. If the status is
|
|||
|
// bad, get rid of the device extension, device object
|
|||
|
// and any memory associated with it.
|
|||
|
//
|
|||
|
|
|||
|
ExtensionCleanup:
|
|||
|
|
|||
|
ExFreePool(uniNameString.Buffer);
|
|||
|
ExFreePool(ConfigData->NtNameForPort.Buffer);
|
|||
|
ExFreePool(ConfigData);
|
|||
|
|
|||
|
if (NT_ERROR(status)) {
|
|||
|
|
|||
|
PptCleanupDevice(extension);
|
|||
|
IoDeleteDevice(deviceObject);
|
|||
|
IoGetConfigurationInformation()->ParallelCount--;
|
|||
|
}
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
PVOID
|
|||
|
PptGetMappedAddress(
|
|||
|
IN INTERFACE_TYPE BusType,
|
|||
|
IN ULONG BusNumber,
|
|||
|
IN PHYSICAL_ADDRESS IoAddress,
|
|||
|
IN ULONG NumberOfBytes,
|
|||
|
IN ULONG AddressSpace,
|
|||
|
OUT PBOOLEAN MappedAddress
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine maps an IO address to system address space.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
BusType - Supplies the type of bus - eisa, mca, isa.
|
|||
|
|
|||
|
IoBusNumber - Supplies the bus number.
|
|||
|
|
|||
|
IoAddress - Supplies the base device address to be mapped.
|
|||
|
|
|||
|
NumberOfBytes - Supplies the number of bytes for which the address is
|
|||
|
valid.
|
|||
|
|
|||
|
AddressSpace - Supplies whether the address is in io space or memory.
|
|||
|
|
|||
|
MappedAddress - Supplies whether the address was mapped. This only has
|
|||
|
meaning if the address returned is non-null.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
The mapped address.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PHYSICAL_ADDRESS cardAddress;
|
|||
|
PVOID address;
|
|||
|
|
|||
|
if (!HalTranslateBusAddress(BusType, BusNumber, IoAddress, &AddressSpace,
|
|||
|
&cardAddress)) {
|
|||
|
|
|||
|
AddressSpace = 1;
|
|||
|
cardAddress.QuadPart = 0;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Map the device base address into the virtual address space
|
|||
|
// if the address is in memory space.
|
|||
|
//
|
|||
|
|
|||
|
if (!AddressSpace) {
|
|||
|
|
|||
|
address = MmMapIoSpace(cardAddress, NumberOfBytes, FALSE);
|
|||
|
*MappedAddress = (address ? TRUE : FALSE);
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
address = (PVOID) cardAddress.LowPart;
|
|||
|
*MappedAddress = FALSE;
|
|||
|
}
|
|||
|
|
|||
|
return address;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
PptReportResourcesDevice(
|
|||
|
IN PDEVICE_EXTENSION Extension,
|
|||
|
IN BOOLEAN ClaimInterrupt,
|
|||
|
OUT PBOOLEAN ConflictDetected
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine reports the resources used for a device that
|
|||
|
is "ready" to run. If some conflict was detected, it doesn't
|
|||
|
matter, the reources are reported.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Extension - Supplies the device extension of the device we are
|
|||
|
reporting resources for.
|
|||
|
|
|||
|
ClaimInterrupt - Supplies whether or not to claim the interrupt.
|
|||
|
|
|||
|
ConflictDetected - Returns whether or not a conflict was detected.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PCM_RESOURCE_LIST resourceList;
|
|||
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR partial;
|
|||
|
UNICODE_STRING className;
|
|||
|
ULONG sizeofResourceList;
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARCONFIG,
|
|||
|
("PARPORT: In PptReportResourcesDevice\n"
|
|||
|
"-------- for extension %x\n",
|
|||
|
Extension)
|
|||
|
);
|
|||
|
|
|||
|
//
|
|||
|
// The resource list for a device will consist of
|
|||
|
//
|
|||
|
// The resource list record itself with a count
|
|||
|
// of one for the single "built in" full resource
|
|||
|
// descriptor.
|
|||
|
//
|
|||
|
// The built-in full resource descriptor will contain
|
|||
|
// the bus type and busnumber and the built in partial
|
|||
|
// resource list.
|
|||
|
//
|
|||
|
// The built in partial resource list will have a count of 2:
|
|||
|
//
|
|||
|
// 1) The base register physical address and it's span.
|
|||
|
//
|
|||
|
// 2) The interrupt.
|
|||
|
//
|
|||
|
|
|||
|
sizeofResourceList = sizeof(CM_RESOURCE_LIST);
|
|||
|
if (ClaimInterrupt) {
|
|||
|
sizeofResourceList += sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR);
|
|||
|
}
|
|||
|
resourceList = ExAllocatePool(PagedPool, sizeofResourceList);
|
|||
|
|
|||
|
if (!resourceList) {
|
|||
|
|
|||
|
//
|
|||
|
// Oh well, can't allocate the memory. Act as though
|
|||
|
// we succeeded.
|
|||
|
//
|
|||
|
|
|||
|
PptLogError(Extension->DeviceObject->DriverObject,
|
|||
|
Extension->DeviceObject,
|
|||
|
Extension->PortInfo.OriginalController,
|
|||
|
PhysicalZero, 0, 0, 0, 13, STATUS_SUCCESS,
|
|||
|
PAR_INSUFFICIENT_RESOURCES);
|
|||
|
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
RtlZeroMemory(resourceList, sizeofResourceList);
|
|||
|
|
|||
|
resourceList->Count = 1;
|
|||
|
|
|||
|
resourceList->List[0].InterfaceType = Extension->InterfaceType;
|
|||
|
resourceList->List[0].BusNumber = Extension->BusNumber;
|
|||
|
resourceList->List[0].PartialResourceList.Count = 1;
|
|||
|
partial = &resourceList->List[0].PartialResourceList.PartialDescriptors[0];
|
|||
|
|
|||
|
//
|
|||
|
// Account for the space used by the controller.
|
|||
|
//
|
|||
|
|
|||
|
partial->Type = CmResourceTypePort;
|
|||
|
partial->ShareDisposition = CmResourceShareDeviceExclusive;
|
|||
|
partial->Flags = (USHORT) Extension->AddressSpace;
|
|||
|
partial->u.Port.Start = Extension->PortInfo.OriginalController;
|
|||
|
partial->u.Port.Length = Extension->PortInfo.SpanOfController;
|
|||
|
|
|||
|
if (ClaimInterrupt) {
|
|||
|
partial++;
|
|||
|
resourceList->List[0].PartialResourceList.Count += 1;
|
|||
|
partial->Type = CmResourceTypeInterrupt;
|
|||
|
partial->ShareDisposition = CmResourceShareShared;
|
|||
|
if (Extension->InterruptMode == Latched) {
|
|||
|
partial->Flags = CM_RESOURCE_INTERRUPT_LATCHED;
|
|||
|
} else {
|
|||
|
partial->Flags = CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE;
|
|||
|
}
|
|||
|
partial->u.Interrupt.Vector = Extension->InterruptVector;
|
|||
|
partial->u.Interrupt.Level = Extension->InterruptLevel;
|
|||
|
partial->u.Interrupt.Affinity = Extension->InterruptAffinity;
|
|||
|
}
|
|||
|
|
|||
|
RtlInitUnicodeString(&className, L"LOADED PARALLEL DRIVER RESOURCES");
|
|||
|
|
|||
|
IoReportResourceUsage(&className, Extension->DeviceObject->DriverObject,
|
|||
|
NULL, 0, Extension->DeviceObject, resourceList,
|
|||
|
sizeofResourceList, FALSE, ConflictDetected);
|
|||
|
|
|||
|
ExFreePool(resourceList);
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
PptUnReportResourcesDevice(
|
|||
|
IN OUT PDEVICE_EXTENSION Extension
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
Purge the resources for this particular device.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Extension - The device extension of the device we are *un*reporting
|
|||
|
resources for.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
CM_RESOURCE_LIST resourceList;
|
|||
|
UNICODE_STRING className;
|
|||
|
BOOLEAN junkBoolean;
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARUNLOAD,
|
|||
|
("PARPORT: In PptUnreportResourcesDevice\n"
|
|||
|
"-------- for extension %x of port %x\n",
|
|||
|
Extension, Extension->PortInfo.OriginalController.LowPart)
|
|||
|
);
|
|||
|
|
|||
|
RtlZeroMemory(&resourceList, sizeof(CM_RESOURCE_LIST));
|
|||
|
|
|||
|
resourceList.Count = 0;
|
|||
|
|
|||
|
RtlInitUnicodeString(&className, L"LOADED PARALLEL DRIVER RESOURCES");
|
|||
|
|
|||
|
IoReportResourceUsage(&className, Extension->DeviceObject->DriverObject,
|
|||
|
NULL, 0, Extension->DeviceObject, &resourceList,
|
|||
|
sizeof(CM_RESOURCE_LIST), FALSE, &junkBoolean);
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
PptConnectInterrupt(
|
|||
|
IN PDEVICE_EXTENSION Extension
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine connects the port interrupt service routine
|
|||
|
to the interrupt.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Extension - Supplies the device extension.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
NTSTATUS code.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
BOOLEAN conflict;
|
|||
|
ULONG interruptVector;
|
|||
|
KIRQL irql;
|
|||
|
KAFFINITY affinity;
|
|||
|
NTSTATUS status = STATUS_SUCCESS;
|
|||
|
|
|||
|
PptReportResourcesDevice(Extension, TRUE, &conflict);
|
|||
|
|
|||
|
if (conflict) {
|
|||
|
status = STATUS_NO_SUCH_DEVICE;
|
|||
|
}
|
|||
|
|
|||
|
if (NT_SUCCESS(status)) {
|
|||
|
|
|||
|
//
|
|||
|
// Connect the interrupt.
|
|||
|
//
|
|||
|
|
|||
|
interruptVector = HalGetInterruptVector(Extension->InterfaceType,
|
|||
|
Extension->BusNumber,
|
|||
|
Extension->InterruptLevel,
|
|||
|
Extension->InterruptVector,
|
|||
|
&irql, &affinity);
|
|||
|
|
|||
|
status = IoConnectInterrupt(&Extension->InterruptObject,
|
|||
|
PptInterruptService, Extension, NULL,
|
|||
|
interruptVector, irql, irql,
|
|||
|
Extension->InterruptMode, TRUE, affinity,
|
|||
|
FALSE);
|
|||
|
}
|
|||
|
|
|||
|
if (!NT_SUCCESS(status)) {
|
|||
|
|
|||
|
PptReportResourcesDevice(Extension, FALSE, &conflict);
|
|||
|
|
|||
|
PptLogError(Extension->DeviceObject->DriverObject,
|
|||
|
Extension->DeviceObject,
|
|||
|
Extension->PortInfo.OriginalController,
|
|||
|
PhysicalZero, 0, 0, 0, 14,
|
|||
|
status, PAR_INTERRUPT_CONFLICT);
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARERRORS,
|
|||
|
("PARPORT: Could not connect to interrupt for %x\n",
|
|||
|
Extension->PortInfo.OriginalController.LowPart)
|
|||
|
);
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
PptDisconnectInterrupt(
|
|||
|
IN PDEVICE_EXTENSION Extension
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine disconnects the port interrupt service routine
|
|||
|
from the interrupt.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Extension - Supplies the device extension.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
BOOLEAN conflict;
|
|||
|
|
|||
|
IoDisconnectInterrupt(Extension->InterruptObject);
|
|||
|
PptReportResourcesDevice(Extension, FALSE, &conflict);
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
PptDispatchCreateClose(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is the dispatch for create and close requests. This
|
|||
|
request completes successfully.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - Supplies the device object.
|
|||
|
Irp - Supplies the I/O request packet.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_SUCCESS - Success.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
ParDump(
|
|||
|
PARIRPPATH,
|
|||
|
("PARPORT: In create or close with IRP: %x\n",
|
|||
|
Irp)
|
|||
|
);
|
|||
|
|
|||
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|||
|
Irp->IoStatus.Information = 0;
|
|||
|
|
|||
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
PptSynchronizedIncrement(
|
|||
|
IN OUT PVOID SyncContext
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine increments the 'Count' variable in the context and returns
|
|||
|
its new value in the 'NewCount' variable.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
SyncContext - Supplies the synchronize count context.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
((PSYNCHRONIZED_COUNT_CONTEXT) SyncContext)->NewCount =
|
|||
|
++(*(((PSYNCHRONIZED_COUNT_CONTEXT) SyncContext)->Count));
|
|||
|
return(TRUE);
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
PptSynchronizedDecrement(
|
|||
|
IN OUT PVOID SyncContext
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine decrements the 'Count' variable in the context and returns
|
|||
|
its new value in the 'NewCount' variable.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
SyncContext - Supplies the synchronize count context.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
((PSYNCHRONIZED_COUNT_CONTEXT) SyncContext)->NewCount =
|
|||
|
--(*(((PSYNCHRONIZED_COUNT_CONTEXT) SyncContext)->Count));
|
|||
|
return(TRUE);
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
PptSynchronizedRead(
|
|||
|
IN OUT PVOID SyncContext
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine reads the 'Count' variable in the context and returns
|
|||
|
its value in the 'NewCount' variable.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
SyncContext - Supplies the synchronize count context.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
((PSYNCHRONIZED_COUNT_CONTEXT) SyncContext)->NewCount =
|
|||
|
*(((PSYNCHRONIZED_COUNT_CONTEXT) SyncContext)->Count);
|
|||
|
return(TRUE);
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
PptSynchronizedQueue(
|
|||
|
IN PVOID Context
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine adds the given list entry to the given list.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Context - Supplies the synchronized list context.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PSYNCHRONIZED_LIST_CONTEXT listContext;
|
|||
|
|
|||
|
listContext = Context;
|
|||
|
InsertTailList(listContext->List, listContext->NewEntry);
|
|||
|
return(TRUE);
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
PptSynchronizedDisconnect(
|
|||
|
IN PVOID Context
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine removes the given list entry from the ISR
|
|||
|
list.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Context - Supplies the synchronized disconnect context.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
FALSE - The given list entry was not removed from the list.
|
|||
|
TRUE - The given list entry was removed from the list.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PSYNCHRONIZED_DISCONNECT_CONTEXT disconnectContext;
|
|||
|
PKSERVICE_ROUTINE ServiceRoutine;
|
|||
|
PVOID ServiceContext;
|
|||
|
PLIST_ENTRY current;
|
|||
|
PISR_LIST_ENTRY listEntry;
|
|||
|
|
|||
|
disconnectContext = Context;
|
|||
|
ServiceRoutine = disconnectContext->IsrInfo->InterruptServiceRoutine;
|
|||
|
ServiceContext = disconnectContext->IsrInfo->InterruptServiceContext;
|
|||
|
|
|||
|
for (current = disconnectContext->Extension->IsrList.Flink;
|
|||
|
current != &(disconnectContext->Extension->IsrList);
|
|||
|
current = current->Flink) {
|
|||
|
|
|||
|
listEntry = CONTAINING_RECORD(current, ISR_LIST_ENTRY, ListEntry);
|
|||
|
if (listEntry->ServiceRoutine == ServiceRoutine &&
|
|||
|
listEntry->ServiceContext == ServiceContext) {
|
|||
|
|
|||
|
RemoveEntryList(current);
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
PptCancelRoutine(
|
|||
|
IN OUT PDEVICE_OBJECT DeviceObject,
|
|||
|
IN OUT PIRP Irp
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is called on when the given IRP is cancelled. It
|
|||
|
will dequeue this IRP off the work queue and complete the
|
|||
|
request as CANCELLED. If it can't get if off the queue then
|
|||
|
this routine will ignore the CANCEL request since the IRP
|
|||
|
is about to complete anyway.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - Supplies the device object.
|
|||
|
|
|||
|
Irp - Supplies the IRP.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION extension;
|
|||
|
SYNCHRONIZED_COUNT_CONTEXT syncContext;
|
|||
|
|
|||
|
extension = DeviceObject->DeviceExtension;
|
|||
|
syncContext.Count = &extension->WorkQueueCount;
|
|||
|
if (extension->InterruptRefCount) {
|
|||
|
KeSynchronizeExecution(extension->InterruptObject,
|
|||
|
PptSynchronizedDecrement,
|
|||
|
&syncContext);
|
|||
|
} else {
|
|||
|
PptSynchronizedDecrement(&syncContext);
|
|||
|
}
|
|||
|
RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
|
|||
|
IoReleaseCancelSpinLock(Irp->CancelIrql);
|
|||
|
|
|||
|
Irp->IoStatus.Information = 0;
|
|||
|
Irp->IoStatus.Status = STATUS_CANCELLED;
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARIRPPATH,
|
|||
|
("PARPORT: About to complete IRP in cancel routine\n"
|
|||
|
"Irp: %x status: %x Information: %x\n",
|
|||
|
Irp,
|
|||
|
Irp->IoStatus.Status,
|
|||
|
Irp->IoStatus.Information)
|
|||
|
);
|
|||
|
|
|||
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
PptFreePortDpc(
|
|||
|
IN PKDPC Dpc,
|
|||
|
IN OUT PVOID Extension,
|
|||
|
IN PVOID SystemArgument1,
|
|||
|
IN PVOID SystemArgument2
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is a DPC that will free the port and if necessary
|
|||
|
complete an alloc request that is waiting.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Dpc - Not used.
|
|||
|
|
|||
|
Extension - Supplies the device extension.
|
|||
|
|
|||
|
SystemArgument1 - Not used.
|
|||
|
|
|||
|
SystemArgument2 - Not used.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PptFreePort(Extension);
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
PptTryAllocatePortAtInterruptLevel(
|
|||
|
IN PVOID Context
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is called at interrupt level to quickly allocate
|
|||
|
the parallel port if it is available. This call will fail
|
|||
|
if the port is not available.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Context - Supplies the device extension.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
FALSE - The port was not allocated.
|
|||
|
TRUE - The port was successfully allocated.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
if (((PDEVICE_EXTENSION) Context)->WorkQueueCount == -1) {
|
|||
|
((PDEVICE_EXTENSION) Context)->WorkQueueCount = 0;
|
|||
|
return(TRUE);
|
|||
|
} else {
|
|||
|
return(FALSE);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
PptFreePortFromInterruptLevel(
|
|||
|
IN PVOID Context
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine frees the port that was allocated at interrupt level.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Context - Supplies the device extension.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
// If no one is waiting for the port then this is simple operation,
|
|||
|
// otherwise queue a DPC to free the port later on.
|
|||
|
|
|||
|
if (((PDEVICE_EXTENSION) Context)->WorkQueueCount == 0) {
|
|||
|
((PDEVICE_EXTENSION) Context)->WorkQueueCount = -1;
|
|||
|
} else {
|
|||
|
KeInsertQueueDpc(&((PDEVICE_EXTENSION) Context)->FreePortDpc, NULL, NULL);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
PptInterruptService(
|
|||
|
IN PKINTERRUPT Interrupt,
|
|||
|
IN PVOID Extension
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine services the interrupt for the parallel port.
|
|||
|
This routine will call out to all of the interrupt routines
|
|||
|
that connected with this device via
|
|||
|
IOCTL_INTERNAL_PARALLEL_CONNECT_INTERRUPT in order until
|
|||
|
one of them returns TRUE.
|
|||
|
|
|||
|
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;
|
|||
|
PLIST_ENTRY current;
|
|||
|
PISR_LIST_ENTRY isrListEntry;
|
|||
|
|
|||
|
extension = Extension;
|
|||
|
for (current = extension->IsrList.Flink;
|
|||
|
current != &extension->IsrList;
|
|||
|
current = current->Flink) {
|
|||
|
|
|||
|
isrListEntry = CONTAINING_RECORD(current, ISR_LIST_ENTRY, ListEntry);
|
|||
|
if (isrListEntry->ServiceRoutine(Interrupt, isrListEntry->ServiceContext)) {
|
|||
|
return(TRUE);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return(FALSE);
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
PptTryAllocatePort(
|
|||
|
IN PVOID Extension
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine attempts to allocate the port. If the port is
|
|||
|
available then the call will succeed with the port allocated.
|
|||
|
If the port is not available the then call will fail
|
|||
|
immediately.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Extension - Supplies the device extension.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
FALSE - The port was not allocated.
|
|||
|
TRUE - The port was allocated.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION extension = Extension;
|
|||
|
KIRQL cancelIrql;
|
|||
|
BOOLEAN b;
|
|||
|
|
|||
|
if (extension->InterruptRefCount) {
|
|||
|
b = KeSynchronizeExecution(extension->InterruptObject,
|
|||
|
PptTryAllocatePortAtInterruptLevel,
|
|||
|
extension);
|
|||
|
} else {
|
|||
|
IoAcquireCancelSpinLock(&cancelIrql);
|
|||
|
b = PptTryAllocatePortAtInterruptLevel(extension);
|
|||
|
IoReleaseCancelSpinLock(cancelIrql);
|
|||
|
}
|
|||
|
|
|||
|
return(b);
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
PptTraversePortCheckList(
|
|||
|
IN PVOID Extension
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine traverses the deferred port check routines. This
|
|||
|
call must be synchronized at interrupt level so that real
|
|||
|
interrupts are blocked until these routines are completed.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Extension - Supplies the device extension.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
FALSE - The port is in use so no action taken by this routine.
|
|||
|
TRUE - All of the deferred interrupt routines were called.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION extension = Extension;
|
|||
|
PLIST_ENTRY current;
|
|||
|
PISR_LIST_ENTRY checkEntry;
|
|||
|
|
|||
|
// First check to make sure that the port is still free.
|
|||
|
|
|||
|
if (extension->WorkQueueCount >= 0) {
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
for (current = extension->IsrList.Flink;
|
|||
|
current != &extension->IsrList;
|
|||
|
current = current->Flink) {
|
|||
|
|
|||
|
checkEntry = CONTAINING_RECORD(current,
|
|||
|
ISR_LIST_ENTRY,
|
|||
|
ListEntry);
|
|||
|
|
|||
|
if (checkEntry->DeferredPortCheckRoutine) {
|
|||
|
checkEntry->DeferredPortCheckRoutine(checkEntry->CheckContext);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
PptFreePort(
|
|||
|
IN PVOID Extension
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine frees the port.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Extension - Supplies the device extension.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION extension = Extension;
|
|||
|
SYNCHRONIZED_COUNT_CONTEXT syncContext;
|
|||
|
KIRQL cancelIrql;
|
|||
|
PLIST_ENTRY head;
|
|||
|
PIRP irp;
|
|||
|
ULONG interruptRefCount;
|
|||
|
|
|||
|
syncContext.Count = &extension->WorkQueueCount;
|
|||
|
|
|||
|
IoAcquireCancelSpinLock(&cancelIrql);
|
|||
|
|
|||
|
if (extension->InterruptRefCount) {
|
|||
|
KeSynchronizeExecution(extension->InterruptObject,
|
|||
|
PptSynchronizedDecrement,
|
|||
|
&syncContext);
|
|||
|
} else {
|
|||
|
PptSynchronizedDecrement(&syncContext);
|
|||
|
}
|
|||
|
|
|||
|
if (syncContext.NewCount >= 0) {
|
|||
|
head = RemoveHeadList(&extension->WorkQueue);
|
|||
|
irp = CONTAINING_RECORD(head, IRP, Tail.Overlay.ListEntry);
|
|||
|
IoSetCancelRoutine(irp, NULL);
|
|||
|
IoReleaseCancelSpinLock(cancelIrql);
|
|||
|
|
|||
|
irp->IoStatus.Status = STATUS_SUCCESS;
|
|||
|
IoCompleteRequest(irp, IO_PARALLEL_INCREMENT);
|
|||
|
|
|||
|
} else {
|
|||
|
interruptRefCount = extension->InterruptRefCount;
|
|||
|
IoReleaseCancelSpinLock(cancelIrql);
|
|||
|
if (interruptRefCount) {
|
|||
|
KeSynchronizeExecution(extension->InterruptObject,
|
|||
|
PptTraversePortCheckList,
|
|||
|
extension);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
ULONG
|
|||
|
PptQueryNumWaiters(
|
|||
|
IN PVOID Extension
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine returns the number of irps queued waiting for
|
|||
|
the parallel port.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Extension - Supplies the device extension.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
The number of irps queued waiting for the port.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION extension = Extension;
|
|||
|
KIRQL cancelIrql;
|
|||
|
SYNCHRONIZED_COUNT_CONTEXT syncContext;
|
|||
|
|
|||
|
syncContext.Count = &extension->WorkQueueCount;
|
|||
|
if (extension->InterruptRefCount) {
|
|||
|
KeSynchronizeExecution(extension->InterruptObject,
|
|||
|
PptSynchronizedRead,
|
|||
|
&syncContext);
|
|||
|
} else {
|
|||
|
IoAcquireCancelSpinLock(&cancelIrql);
|
|||
|
PptSynchronizedRead(&syncContext);
|
|||
|
IoReleaseCancelSpinLock(cancelIrql);
|
|||
|
}
|
|||
|
|
|||
|
return((syncContext.NewCount >= 0) ? ((ULONG) syncContext.NewCount) : 0);
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
PptDispatchDeviceControl(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine is the dispatch for INTERNAL_DEVICE_CONTROLs.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - Supplies the device object.
|
|||
|
|
|||
|
Irp - Supplies the I/O request packet.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_SUCCESS - Success.
|
|||
|
STATUS_PENDING - The request is pending.
|
|||
|
STATUS_INVALID_PARAMETER - Invalid parameter.
|
|||
|
STATUS_CANCELLED - The request was cancelled.
|
|||
|
STATUS_BUFFER_TOO_SMALL - The supplied buffer is too small.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PIO_STACK_LOCATION irpSp;
|
|||
|
PDEVICE_EXTENSION extension;
|
|||
|
NTSTATUS status;
|
|||
|
PPARALLEL_PORT_INFORMATION portInfo;
|
|||
|
PMORE_PARALLEL_PORT_INFORMATION morePortInfo;
|
|||
|
KIRQL cancelIrql;
|
|||
|
SYNCHRONIZED_COUNT_CONTEXT syncContext;
|
|||
|
PPARALLEL_INTERRUPT_SERVICE_ROUTINE isrInfo;
|
|||
|
PPARALLEL_INTERRUPT_INFORMATION interruptInfo;
|
|||
|
PISR_LIST_ENTRY isrListEntry;
|
|||
|
SYNCHRONIZED_LIST_CONTEXT listContext;
|
|||
|
SYNCHRONIZED_DISCONNECT_CONTEXT disconnectContext;
|
|||
|
BOOLEAN disconnectInterrupt;
|
|||
|
BOOLEAN deferredRoutinePresent;
|
|||
|
|
|||
|
irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARIRPPATH,
|
|||
|
("PARPORT: In internal device control dispatch with IRP: %x\n"
|
|||
|
"Io control code: %d\n",
|
|||
|
Irp,
|
|||
|
irpSp->Parameters.DeviceIoControl.IoControlCode)
|
|||
|
);
|
|||
|
|
|||
|
extension = DeviceObject->DeviceExtension;
|
|||
|
Irp->IoStatus.Information = 0;
|
|||
|
|
|||
|
switch (irpSp->Parameters.DeviceIoControl.IoControlCode) {
|
|||
|
|
|||
|
case IOCTL_INTERNAL_PARALLEL_PORT_ALLOCATE:
|
|||
|
|
|||
|
IoAcquireCancelSpinLock(&cancelIrql);
|
|||
|
|
|||
|
if (Irp->Cancel) {
|
|||
|
status = STATUS_CANCELLED;
|
|||
|
} else {
|
|||
|
syncContext.Count = &extension->WorkQueueCount;
|
|||
|
if (extension->InterruptRefCount) {
|
|||
|
KeSynchronizeExecution(extension->InterruptObject,
|
|||
|
PptSynchronizedIncrement,
|
|||
|
&syncContext);
|
|||
|
} else {
|
|||
|
PptSynchronizedIncrement(&syncContext);
|
|||
|
}
|
|||
|
if (syncContext.NewCount) {
|
|||
|
|
|||
|
IoSetCancelRoutine(Irp, PptCancelRoutine);
|
|||
|
IoMarkIrpPending(Irp);
|
|||
|
InsertTailList(&extension->WorkQueue,
|
|||
|
&Irp->Tail.Overlay.ListEntry);
|
|||
|
status = STATUS_PENDING;
|
|||
|
|
|||
|
} else {
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARIRPPATH,
|
|||
|
("PARPORT: Completing ALLOCATE request in dispatch\n")
|
|||
|
);
|
|||
|
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
IoReleaseCancelSpinLock(cancelIrql);
|
|||
|
break;
|
|||
|
|
|||
|
case IOCTL_INTERNAL_GET_PARALLEL_PORT_INFO:
|
|||
|
|
|||
|
if (irpSp->Parameters.DeviceIoControl.OutputBufferLength <
|
|||
|
sizeof(PARALLEL_PORT_INFORMATION)) {
|
|||
|
|
|||
|
status = STATUS_BUFFER_TOO_SMALL;
|
|||
|
} else {
|
|||
|
|
|||
|
Irp->IoStatus.Information = sizeof(PARALLEL_PORT_INFORMATION);
|
|||
|
portInfo = Irp->AssociatedIrp.SystemBuffer;
|
|||
|
*portInfo = extension->PortInfo;
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
|
|||
|
ExAcquireFastMutex(PortInfoMutex);
|
|||
|
if (++PortInfoReferenceCount == 1) {
|
|||
|
MmResetDriverPaging(DriverEntry);
|
|||
|
}
|
|||
|
ExReleaseFastMutex(PortInfoMutex);
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case IOCTL_INTERNAL_RELEASE_PARALLEL_PORT_INFO:
|
|||
|
|
|||
|
ExAcquireFastMutex(PortInfoMutex);
|
|||
|
if (--PortInfoReferenceCount == 0) {
|
|||
|
MmPageEntireDriver(DriverEntry);
|
|||
|
}
|
|||
|
ExReleaseFastMutex(PortInfoMutex);
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
break;
|
|||
|
|
|||
|
case IOCTL_INTERNAL_GET_MORE_PARALLEL_PORT_INFO:
|
|||
|
|
|||
|
if (irpSp->Parameters.DeviceIoControl.OutputBufferLength <
|
|||
|
sizeof(MORE_PARALLEL_PORT_INFORMATION)) {
|
|||
|
|
|||
|
status = STATUS_BUFFER_TOO_SMALL;
|
|||
|
} else {
|
|||
|
|
|||
|
Irp->IoStatus.Information = sizeof(MORE_PARALLEL_PORT_INFORMATION);
|
|||
|
morePortInfo = Irp->AssociatedIrp.SystemBuffer;
|
|||
|
morePortInfo->InterfaceType = extension->InterfaceType;
|
|||
|
morePortInfo->BusNumber = extension->BusNumber;
|
|||
|
morePortInfo->InterruptLevel = extension->InterruptLevel;
|
|||
|
morePortInfo->InterruptVector = extension->InterruptVector;
|
|||
|
morePortInfo->InterruptAffinity = extension->InterruptAffinity;
|
|||
|
morePortInfo->InterruptMode = extension->InterruptMode;
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case IOCTL_INTERNAL_PARALLEL_CONNECT_INTERRUPT:
|
|||
|
|
|||
|
if (irpSp->Parameters.DeviceIoControl.InputBufferLength <
|
|||
|
sizeof(PARALLEL_INTERRUPT_SERVICE_ROUTINE) ||
|
|||
|
irpSp->Parameters.DeviceIoControl.OutputBufferLength <
|
|||
|
sizeof(PARALLEL_INTERRUPT_INFORMATION)) {
|
|||
|
|
|||
|
status = STATUS_BUFFER_TOO_SMALL;
|
|||
|
} else {
|
|||
|
isrInfo = Irp->AssociatedIrp.SystemBuffer;
|
|||
|
interruptInfo = Irp->AssociatedIrp.SystemBuffer;
|
|||
|
IoAcquireCancelSpinLock(&cancelIrql);
|
|||
|
if (extension->InterruptRefCount) {
|
|||
|
++extension->InterruptRefCount;
|
|||
|
IoReleaseCancelSpinLock(cancelIrql);
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
} else {
|
|||
|
IoReleaseCancelSpinLock(cancelIrql);
|
|||
|
status = PptConnectInterrupt(extension);
|
|||
|
if (NT_SUCCESS(status)) {
|
|||
|
IoAcquireCancelSpinLock(&cancelIrql);
|
|||
|
++extension->InterruptRefCount;
|
|||
|
IoReleaseCancelSpinLock(cancelIrql);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
if (NT_SUCCESS(status)) {
|
|||
|
isrListEntry = ExAllocatePool(NonPagedPool,
|
|||
|
sizeof(ISR_LIST_ENTRY));
|
|||
|
|
|||
|
if (isrListEntry) {
|
|||
|
|
|||
|
isrListEntry->ServiceRoutine =
|
|||
|
isrInfo->InterruptServiceRoutine;
|
|||
|
isrListEntry->ServiceContext =
|
|||
|
isrInfo->InterruptServiceContext;
|
|||
|
isrListEntry->DeferredPortCheckRoutine =
|
|||
|
isrInfo->DeferredPortCheckRoutine;
|
|||
|
isrListEntry->CheckContext =
|
|||
|
isrInfo->DeferredPortCheckContext;
|
|||
|
|
|||
|
// Put the ISR_LIST_ENTRY onto the ISR list.
|
|||
|
|
|||
|
listContext.List = &extension->IsrList;
|
|||
|
listContext.NewEntry = &isrListEntry->ListEntry;
|
|||
|
KeSynchronizeExecution(extension->InterruptObject,
|
|||
|
PptSynchronizedQueue,
|
|||
|
&listContext);
|
|||
|
|
|||
|
interruptInfo->InterruptObject =
|
|||
|
extension->InterruptObject;
|
|||
|
interruptInfo->TryAllocatePortAtInterruptLevel =
|
|||
|
PptTryAllocatePortAtInterruptLevel;
|
|||
|
interruptInfo->FreePortFromInterruptLevel =
|
|||
|
PptFreePortFromInterruptLevel;
|
|||
|
interruptInfo->Context =
|
|||
|
extension;
|
|||
|
|
|||
|
Irp->IoStatus.Information =
|
|||
|
sizeof(PARALLEL_INTERRUPT_INFORMATION);
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
|
|||
|
} else {
|
|||
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
case IOCTL_INTERNAL_PARALLEL_DISCONNECT_INTERRUPT:
|
|||
|
if (irpSp->Parameters.DeviceIoControl.InputBufferLength <
|
|||
|
sizeof(PARALLEL_INTERRUPT_SERVICE_ROUTINE)) {
|
|||
|
|
|||
|
status = STATUS_BUFFER_TOO_SMALL;
|
|||
|
} else {
|
|||
|
isrInfo = Irp->AssociatedIrp.SystemBuffer;
|
|||
|
|
|||
|
// Take the ISR out of the ISR list.
|
|||
|
|
|||
|
IoAcquireCancelSpinLock(&cancelIrql);
|
|||
|
if (extension->InterruptRefCount) {
|
|||
|
IoReleaseCancelSpinLock(cancelIrql);
|
|||
|
|
|||
|
disconnectContext.Extension = extension;
|
|||
|
disconnectContext.IsrInfo = isrInfo;
|
|||
|
if (KeSynchronizeExecution(extension->InterruptObject,
|
|||
|
PptSynchronizedDisconnect,
|
|||
|
&disconnectContext)) {
|
|||
|
|
|||
|
status = STATUS_SUCCESS;
|
|||
|
IoAcquireCancelSpinLock(&cancelIrql);
|
|||
|
if (--extension->InterruptRefCount == 0) {
|
|||
|
disconnectInterrupt = TRUE;
|
|||
|
} else {
|
|||
|
disconnectInterrupt = FALSE;
|
|||
|
}
|
|||
|
IoReleaseCancelSpinLock(cancelIrql);
|
|||
|
|
|||
|
} else {
|
|||
|
status = STATUS_INVALID_PARAMETER;
|
|||
|
disconnectInterrupt = FALSE;
|
|||
|
}
|
|||
|
} else {
|
|||
|
IoReleaseCancelSpinLock(cancelIrql);
|
|||
|
disconnectInterrupt = FALSE;
|
|||
|
status = STATUS_INVALID_PARAMETER;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// Disconnect the interrupt if appropriate.
|
|||
|
|
|||
|
if (disconnectInterrupt) {
|
|||
|
PptDisconnectInterrupt(extension);
|
|||
|
}
|
|||
|
}
|
|||
|
break;
|
|||
|
|
|||
|
default:
|
|||
|
status = STATUS_INVALID_PARAMETER;
|
|||
|
break;
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
if (status != STATUS_PENDING) {
|
|||
|
Irp->IoStatus.Status = status;
|
|||
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|||
|
}
|
|||
|
|
|||
|
return status;
|
|||
|
}
|
|||
|
|
|||
|
NTSTATUS
|
|||
|
PptDispatchCleanup(
|
|||
|
IN PDEVICE_OBJECT DeviceObject,
|
|||
|
IN PIRP Irp
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine cancels all of the IRPs currently queued on
|
|||
|
the given device.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceObject - Supplies the device object.
|
|||
|
|
|||
|
Irp - Supplies the cleanup IRP.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
STATUS_SUCCESS - Success.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PDEVICE_EXTENSION extension;
|
|||
|
PIRP irp;
|
|||
|
KIRQL cancelIrql;
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARIRPPATH,
|
|||
|
("PARPORT: In cleanup with IRP: %x\n",
|
|||
|
Irp)
|
|||
|
);
|
|||
|
|
|||
|
extension = DeviceObject->DeviceExtension;
|
|||
|
|
|||
|
IoAcquireCancelSpinLock(&cancelIrql);
|
|||
|
|
|||
|
while (!IsListEmpty(&extension->WorkQueue)) {
|
|||
|
|
|||
|
irp = CONTAINING_RECORD(extension->WorkQueue.Blink,
|
|||
|
IRP, Tail.Overlay.ListEntry);
|
|||
|
|
|||
|
irp->Cancel = TRUE;
|
|||
|
irp->CancelIrql = cancelIrql;
|
|||
|
irp->CancelRoutine = NULL;
|
|||
|
PptCancelRoutine(DeviceObject, irp);
|
|||
|
|
|||
|
IoAcquireCancelSpinLock(&cancelIrql);
|
|||
|
}
|
|||
|
|
|||
|
IoReleaseCancelSpinLock(cancelIrql);
|
|||
|
|
|||
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|||
|
Irp->IoStatus.Information = 0;
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARIRPPATH,
|
|||
|
("PARPORT: About to complete IRP in cleanup\n"
|
|||
|
"Irp: %x status: %x Information: %x\n",
|
|||
|
Irp,
|
|||
|
Irp->IoStatus.Status,
|
|||
|
Irp->IoStatus.Information)
|
|||
|
);
|
|||
|
|
|||
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|||
|
|
|||
|
return STATUS_SUCCESS;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
PptUnload(
|
|||
|
IN PDRIVER_OBJECT DriverObject
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine cleans up all of the memory associated with
|
|||
|
any of the devices belonging to the driver. It will
|
|||
|
loop through the device list.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DriverObject - Supplies the driver object controling all of the
|
|||
|
devices.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
PDEVICE_OBJECT currentDevice;
|
|||
|
PDEVICE_EXTENSION extension;
|
|||
|
PLIST_ENTRY head;
|
|||
|
PISR_LIST_ENTRY entry;
|
|||
|
|
|||
|
ParDump(
|
|||
|
PARUNLOAD,
|
|||
|
("PARPORT: In ParUnload\n")
|
|||
|
);
|
|||
|
|
|||
|
while (currentDevice = DriverObject->DeviceObject) {
|
|||
|
|
|||
|
extension = currentDevice->DeviceExtension;
|
|||
|
|
|||
|
if (extension->InterruptRefCount) {
|
|||
|
PptDisconnectInterrupt(extension);
|
|||
|
}
|
|||
|
PptCleanupDevice(extension);
|
|||
|
|
|||
|
while (!IsListEmpty(&extension->IsrList)) {
|
|||
|
head = RemoveHeadList(&extension->IsrList);
|
|||
|
entry = CONTAINING_RECORD(head, ISR_LIST_ENTRY, ListEntry);
|
|||
|
ExFreePool(entry);
|
|||
|
}
|
|||
|
|
|||
|
PptUnReportResourcesDevice(extension);
|
|||
|
|
|||
|
IoDeleteDevice(currentDevice);
|
|||
|
IoGetConfigurationInformation()->ParallelCount--;
|
|||
|
}
|
|||
|
|
|||
|
ExFreePool(PortInfoMutex);
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
PptIsNecR98Machine(
|
|||
|
void
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This routine checks the machine type in the registry to determine
|
|||
|
if this is an Nec R98 machine.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
TRUE - this machine is an R98
|
|||
|
FALSE - this machine is not
|
|||
|
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
UNICODE_STRING path;
|
|||
|
RTL_QUERY_REGISTRY_TABLE paramTable[2];
|
|||
|
NTSTATUS status;
|
|||
|
|
|||
|
UNICODE_STRING identifierString;
|
|||
|
UNICODE_STRING necR98Identifier;
|
|||
|
UNICODE_STRING necR98JIdentifier;
|
|||
|
|
|||
|
RtlInitUnicodeString(&path, L"\\Registry\\Machine\\HARDWARE\\DESCRIPTION\\System");
|
|||
|
RtlInitUnicodeString(&necR98Identifier, L"NEC-R98");
|
|||
|
RtlInitUnicodeString(&necR98JIdentifier, L"NEC-J98");
|
|||
|
|
|||
|
|
|||
|
identifierString.Length = 0;
|
|||
|
identifierString.MaximumLength = 32;
|
|||
|
identifierString.Buffer = ExAllocatePool(PagedPool, identifierString.MaximumLength);
|
|||
|
|
|||
|
if(!identifierString.Buffer) return FALSE;
|
|||
|
|
|||
|
RtlZeroMemory(paramTable, sizeof(paramTable));
|
|||
|
paramTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT |
|
|||
|
RTL_QUERY_REGISTRY_REQUIRED;
|
|||
|
paramTable[0].Name = L"Identifier";
|
|||
|
paramTable[0].EntryContext = &identifierString;
|
|||
|
paramTable[0].DefaultType = REG_SZ;
|
|||
|
paramTable[0].DefaultData = &path;
|
|||
|
paramTable[0].DefaultLength = 0;
|
|||
|
|
|||
|
status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE,
|
|||
|
path.Buffer,
|
|||
|
paramTable,
|
|||
|
NULL,
|
|||
|
NULL);
|
|||
|
|
|||
|
|
|||
|
if(NT_SUCCESS(status)) {
|
|||
|
|
|||
|
if((RtlCompareUnicodeString(&identifierString,
|
|||
|
&necR98Identifier, FALSE) == 0) ||
|
|||
|
(RtlCompareUnicodeString(&identifierString,
|
|||
|
&necR98JIdentifier, FALSE) == 0)) {
|
|||
|
|
|||
|
ParDump(0, ("parport!PptIsNecR98Machine - this an R98 machine\n"));
|
|||
|
ExFreePool(identifierString.Buffer);
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
} else {
|
|||
|
|
|||
|
ParDump(0, ("parport!PptIsNecR98Machine - "
|
|||
|
"RtlQueryRegistryValues failed [status 0x%x]\n", status));
|
|||
|
ExFreePool(identifierString.Buffer);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
ParDump(0, ("parport!PptIsNecR98Machine - "
|
|||
|
"this is not an R98 machine\n"));
|
|||
|
ExFreePool(identifierString.Buffer);
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
|