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