mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1994 lines
48 KiB
1994 lines
48 KiB
/*++
|
|
|
|
Copyright (c) 1993 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
parlink.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the code for the parallel parlink driver.
|
|
|
|
The purpose of the PARLINK driver is to supply peer to peer
|
|
bidirectional communication along the parallel port. The
|
|
target application for this driver is RAS. Before this driver
|
|
existed RAS would allow a computer with a serial port to
|
|
connect to another computer that was connected to the network and
|
|
thus connect the networkless computer to the network. This driver
|
|
makes this capability possible on the parallel port which is faster
|
|
than serial. A minimal set of serial ioctls have been implemented
|
|
in this driver to support RAS's use so that RAS may treat the
|
|
parallel port in the same way as it treats the serial port when
|
|
providing this function.
|
|
|
|
Author:
|
|
|
|
Norbert P. Kusters 12-Nov-1993
|
|
|
|
Environment:
|
|
|
|
Kernel mode
|
|
|
|
Revision History :
|
|
|
|
--*/
|
|
|
|
#include "ntddk.h"
|
|
#include "parallel.h"
|
|
#include "parlink.h"
|
|
#include "parlog.h"
|
|
#include "ntddser.h"
|
|
|
|
#ifdef POOL_TAGGING
|
|
#ifdef ExAllocatePool
|
|
#undef ExAllocatePool
|
|
#endif
|
|
#define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,'LraP')
|
|
#endif
|
|
|
|
|
|
#if DBG
|
|
ULONG PlDebugLevel = 0;
|
|
#endif
|
|
|
|
static const PHYSICAL_ADDRESS PhysicalZero = {0};
|
|
|
|
#define MAX_LPT_NUMBER 100
|
|
|
|
#define PL_CHECK_LOOP 50000
|
|
#define PL_SLOW_CHECK_LOOP 100000
|
|
|
|
#define MAX_WRITE_RETRY_COUNT 3
|
|
|
|
#define PL_CHECK_INPUT(input,checkvalue) \
|
|
((UCHAR) ((input)&0xF8) == (checkvalue))
|
|
|
|
#define PACKET_ACKNOWLEDGE(b) PL_CHECK_INPUT(b,0xB0)
|
|
#define INCOMING_PACKET(b) PL_CHECK_INPUT(b,0xA8)
|
|
#define DCD_DEAD(b) PL_CHECK_INPUT(b,0x90)
|
|
#define LOW_NIBBLE_ACK(b) PL_CHECK_INPUT(b,0x60)
|
|
#define HIGH_NIBBLE_ACK(b) PL_CHECK_INPUT(b,0x98)
|
|
|
|
NTSTATUS
|
|
DriverEntry(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PUNICODE_STRING RegistryPath
|
|
);
|
|
|
|
BOOLEAN
|
|
PlMakeNames(
|
|
IN ULONG ParallelPortNumber,
|
|
OUT PUNICODE_STRING PortName,
|
|
OUT PUNICODE_STRING ClassName
|
|
);
|
|
|
|
VOID
|
|
PlInitializeDeviceObject(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN ULONG ParallelPortNumber
|
|
);
|
|
|
|
NTSTATUS
|
|
PlGetPortInfoFromPortDevice(
|
|
IN OUT PDEVICE_EXTENSION Extension
|
|
);
|
|
|
|
BOOLEAN
|
|
PlCreateSymbolicLink(
|
|
IN OUT PDEVICE_EXTENSION Extension,
|
|
IN PUNICODE_STRING DeviceName
|
|
);
|
|
|
|
VOID
|
|
PlReadDpc(
|
|
IN PKDPC ReadDpc,
|
|
IN PVOID Extension,
|
|
IN PVOID SystemArgument1,
|
|
IN PVOID SystemArgument2
|
|
);
|
|
|
|
VOID
|
|
PlAllocPort(
|
|
IN PDEVICE_EXTENSION Extension
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(INIT,DriverEntry)
|
|
#pragma alloc_text(INIT,PlInitializeDeviceObject)
|
|
#pragma alloc_text(INIT,PlMakeNames)
|
|
#pragma alloc_text(INIT,PlGetPortInfoFromPortDevice)
|
|
#pragma alloc_text(INIT,PlCreateSymbolicLink)
|
|
#endif
|
|
|
|
NTSTATUS
|
|
DriverEntry(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PUNICODE_STRING RegistryPath
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called at system initialization time to initialize
|
|
this driver.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - Supplies the driver object.
|
|
|
|
RegistryPath - Supplies the registry path for this driver.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - We could initialize at least one device.
|
|
STATUS_NO_SUCH_DEVICE - We could not initialize even one device.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG i;
|
|
|
|
for (i = 0; i < IoGetConfigurationInformation()->ParallelCount; i++) {
|
|
PlInitializeDeviceObject(DriverObject, i);
|
|
}
|
|
|
|
if (!DriverObject->DeviceObject) {
|
|
return STATUS_NO_SUCH_DEVICE;
|
|
}
|
|
|
|
//
|
|
// Initialize the Driver Object with driver's entry points
|
|
//
|
|
|
|
DriverObject->MajorFunction[IRP_MJ_CREATE] = PlCreateClose;
|
|
DriverObject->MajorFunction[IRP_MJ_CLOSE] = PlCreateClose;
|
|
DriverObject->MajorFunction[IRP_MJ_CLEANUP] = PlCleanup;
|
|
DriverObject->MajorFunction[IRP_MJ_READ] = PlReadWrite;
|
|
DriverObject->MajorFunction[IRP_MJ_WRITE] = PlReadWrite;
|
|
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = PlDeviceControl;
|
|
|
|
DriverObject->DriverUnload = PlUnload;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
PlLogError(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PDEVICE_OBJECT DeviceObject OPTIONAL,
|
|
IN PHYSICAL_ADDRESS P1,
|
|
IN PHYSICAL_ADDRESS P2,
|
|
IN ULONG SequenceNumber,
|
|
IN UCHAR MajorFunctionCode,
|
|
IN UCHAR RetryCount,
|
|
IN ULONG UniqueErrorValue,
|
|
IN NTSTATUS FinalStatus,
|
|
IN NTSTATUS SpecificIOStatus
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine allocates an error log entry, copies the supplied data
|
|
to it, and requests that it be written to the error log file.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - Supplies a pointer to the driver object for the
|
|
device.
|
|
|
|
DeviceObject - Supplies a pointer to the device object associated
|
|
with the device that had the error, early in
|
|
initialization, one may not yet exist.
|
|
|
|
P1,P2 - Supplies the physical addresses for the controller
|
|
ports involved with the error if they are available
|
|
and puts them through as dump data.
|
|
|
|
SequenceNumber - Supplies a ulong value that is unique to an IRP over
|
|
the life of the irp in this driver - 0 generally
|
|
means an error not associated with an irp.
|
|
|
|
MajorFunctionCode - Supplies the major function code of the irp if there
|
|
is an error associated with it.
|
|
|
|
RetryCount - Supplies the number of times a particular operation
|
|
has been retried.
|
|
|
|
UniqueErrorValue - Supplies a unique long word that identifies the
|
|
particular call to this function.
|
|
|
|
FinalStatus - Supplies the final status given to the irp that was
|
|
associated with this error. If this log entry is
|
|
being made during one of the retries this value
|
|
will be STATUS_SUCCESS.
|
|
|
|
SpecificIOStatus - Supplies the IO status for this particular error.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_ERROR_LOG_PACKET errorLogEntry;
|
|
PVOID objectToUse;
|
|
SHORT dumpToAllocate;
|
|
|
|
if (ARGUMENT_PRESENT(DeviceObject)) {
|
|
objectToUse = DeviceObject;
|
|
} else {
|
|
objectToUse = DriverObject;
|
|
}
|
|
|
|
dumpToAllocate = 0;
|
|
|
|
if (P1.LowPart != 0 || P1.HighPart != 0) {
|
|
dumpToAllocate = (SHORT) sizeof(PHYSICAL_ADDRESS);
|
|
}
|
|
|
|
if (P2.LowPart != 0 || P2.HighPart != 0) {
|
|
dumpToAllocate += (SHORT) sizeof(PHYSICAL_ADDRESS);
|
|
}
|
|
|
|
errorLogEntry = IoAllocateErrorLogEntry(objectToUse,
|
|
(UCHAR) (sizeof(IO_ERROR_LOG_PACKET) + dumpToAllocate));
|
|
|
|
if (!errorLogEntry) {
|
|
return;
|
|
}
|
|
|
|
errorLogEntry->ErrorCode = SpecificIOStatus;
|
|
errorLogEntry->SequenceNumber = SequenceNumber;
|
|
errorLogEntry->MajorFunctionCode = MajorFunctionCode;
|
|
errorLogEntry->RetryCount = RetryCount;
|
|
errorLogEntry->UniqueErrorValue = UniqueErrorValue;
|
|
errorLogEntry->FinalStatus = FinalStatus;
|
|
errorLogEntry->DumpDataSize = dumpToAllocate;
|
|
|
|
if (dumpToAllocate) {
|
|
|
|
RtlCopyMemory(errorLogEntry->DumpData, &P1, sizeof(PHYSICAL_ADDRESS));
|
|
|
|
if (dumpToAllocate > sizeof(PHYSICAL_ADDRESS)) {
|
|
|
|
RtlCopyMemory(((PUCHAR) errorLogEntry->DumpData) +
|
|
sizeof(PHYSICAL_ADDRESS), &P2,
|
|
sizeof(PHYSICAL_ADDRESS));
|
|
}
|
|
}
|
|
|
|
IoWriteErrorLogEntry(errorLogEntry);
|
|
}
|
|
|
|
VOID
|
|
PlInitializeDeviceObject(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN ULONG ParallelPortNumber
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is called for every parallel port in the system. It
|
|
will create a class device upon connecting to the port device
|
|
corresponding to it.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - Supplies the driver object.
|
|
|
|
ParallelPortNumber - Supplies the number for this port.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
UNICODE_STRING portName, className;
|
|
NTSTATUS status;
|
|
PDEVICE_OBJECT deviceObject;
|
|
PDEVICE_EXTENSION extension;
|
|
PFILE_OBJECT fileObject;
|
|
|
|
|
|
// Cobble together the port and class device names.
|
|
|
|
if (!PlMakeNames(ParallelPortNumber, &portName, &className)) {
|
|
|
|
PlDump(
|
|
PLERRORS,
|
|
("PARLINK: Could not form Unicode name string.\n")
|
|
);
|
|
|
|
PlLogError(DriverObject, NULL, PhysicalZero, PhysicalZero, 0, 0, 0, 1,
|
|
STATUS_SUCCESS, PAR_INSUFFICIENT_RESOURCES);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
// Create the device object.
|
|
|
|
status = IoCreateDevice(DriverObject, sizeof(DEVICE_EXTENSION),
|
|
&className, FILE_DEVICE_PARALLEL_PORT, 0, FALSE,
|
|
&deviceObject);
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
PlDump(
|
|
PLERRORS,
|
|
("PARLINK: Could not create a device for %wZ\n",
|
|
&className)
|
|
);
|
|
|
|
PlLogError(DriverObject, NULL, PhysicalZero, PhysicalZero, 0, 0, 0, 2,
|
|
STATUS_SUCCESS, PAR_INSUFFICIENT_RESOURCES);
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
// Now that the device has been created,
|
|
// set up the device extension.
|
|
|
|
extension = deviceObject->DeviceExtension;
|
|
RtlZeroMemory(extension, sizeof(DEVICE_EXTENSION));
|
|
|
|
extension->DeviceObject = deviceObject;
|
|
deviceObject->Flags |= DO_BUFFERED_IO;
|
|
|
|
status = IoGetDeviceObjectPointer(&portName, FILE_READ_ATTRIBUTES,
|
|
&fileObject,
|
|
&extension->PortDeviceObject);
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
PlDump(
|
|
PLERRORS,
|
|
("PARLINK: Unable to get device object pointer for port object.\n")
|
|
);
|
|
|
|
PlLogError(DriverObject, deviceObject, PhysicalZero, PhysicalZero,
|
|
0, 0, 0, 3, STATUS_SUCCESS, PAR_INSUFFICIENT_RESOURCES);
|
|
|
|
IoDeleteDevice(deviceObject);
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
ObDereferenceObject(fileObject);
|
|
|
|
extension->DeviceObject->StackSize =
|
|
extension->PortDeviceObject->StackSize + 1;
|
|
|
|
status = PlGetPortInfoFromPortDevice(extension);
|
|
if (!NT_SUCCESS(status)) {
|
|
|
|
PlDump(
|
|
PLERRORS,
|
|
("PARLINK: Can't get port info from port device.\n")
|
|
);
|
|
|
|
PlLogError(DriverObject, deviceObject, PhysicalZero, PhysicalZero,
|
|
0, 0, 0, 4, STATUS_SUCCESS, PAR_INSUFFICIENT_RESOURCES);
|
|
|
|
IoDeleteDevice(deviceObject);
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
// Set up the current irp and work queue.
|
|
|
|
InitializeListHead(&extension->ReadQueue);
|
|
InitializeListHead(&extension->WriteQueue);
|
|
extension->NeedPortAllocation = TRUE;
|
|
extension->PortAllocateIrp = NULL;
|
|
InitializeListHead(&extension->WaitQueue);
|
|
|
|
extension->WriteRetryCount = 0;
|
|
|
|
|
|
// Set up the read/write timer dpc.
|
|
|
|
KeInitializeTimer(&extension->ReadTimer);
|
|
KeInitializeDpc(&extension->ReadDpc, PlReadDpc, extension);
|
|
extension->ReadDpcTime.QuadPart = -(50*10*1000);
|
|
|
|
|
|
KeInitializeSpinLock(&extension->ControlLock);
|
|
|
|
|
|
// Set up the unload dependency tracking mechanism.
|
|
|
|
extension->UnloadDependenciesCount = 0;
|
|
KeInitializeEvent(&extension->UnloadOk, NotificationEvent, TRUE);
|
|
|
|
|
|
// Set up some state information.
|
|
|
|
extension->IsDcdUp = FALSE;
|
|
extension->WaitMask = 0;
|
|
|
|
|
|
// Set up the symbolic link for windows apps.
|
|
|
|
if (!PlCreateSymbolicLink(extension, &className)) {
|
|
|
|
PlDump(
|
|
PLERRORS,
|
|
("PARLINK: Couldn't create the symbolic link\n"
|
|
"------- for port %wZ\n",
|
|
&className)
|
|
);
|
|
|
|
PlLogError(DriverObject, deviceObject, PhysicalZero, PhysicalZero,
|
|
0, 0, 0, 5, STATUS_SUCCESS, PAR_INSUFFICIENT_RESOURCES);
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
Cleanup:
|
|
ExFreePool(portName.Buffer);
|
|
ExFreePool(className.Buffer);
|
|
}
|
|
|
|
BOOLEAN
|
|
PlMakeNames(
|
|
IN ULONG ParallelPortNumber,
|
|
OUT PUNICODE_STRING PortName,
|
|
OUT PUNICODE_STRING ClassName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine generates the names \Device\ParallelPortN and
|
|
\Device\ParallelLinkN.
|
|
|
|
Arguments:
|
|
|
|
ParallelPortNumber - Supplies the port number.
|
|
|
|
PortName - Returns the port name.
|
|
|
|
ClassName - Returns the class name.
|
|
|
|
Return Value:
|
|
|
|
FALSE - Failure.
|
|
TRUE - Success.
|
|
|
|
--*/
|
|
{
|
|
UNICODE_STRING prefix, digits;
|
|
WCHAR digitsBuffer[10];
|
|
UNICODE_STRING portSuffix, classSuffix;
|
|
NTSTATUS status;
|
|
|
|
// Put together local variables for constructing names.
|
|
|
|
RtlInitUnicodeString(&prefix, L"\\Device\\");
|
|
RtlInitUnicodeString(&portSuffix, DD_PARALLEL_PORT_BASE_NAME_U);
|
|
RtlInitUnicodeString(&classSuffix, L"ParallelLink");
|
|
digits.Length = 0;
|
|
digits.MaximumLength = 20;
|
|
digits.Buffer = digitsBuffer;
|
|
status = RtlIntegerToUnicodeString(ParallelPortNumber, 10, &digits);
|
|
if (!NT_SUCCESS(status)) {
|
|
return FALSE;
|
|
}
|
|
|
|
// Make the port name.
|
|
|
|
PortName->Length = 0;
|
|
PortName->MaximumLength = prefix.Length + portSuffix.Length +
|
|
digits.Length + sizeof(WCHAR);
|
|
PortName->Buffer = ExAllocatePool(PagedPool, PortName->MaximumLength);
|
|
if (!PortName->Buffer) {
|
|
return FALSE;
|
|
}
|
|
RtlZeroMemory(PortName->Buffer, PortName->MaximumLength);
|
|
RtlAppendUnicodeStringToString(PortName, &prefix);
|
|
RtlAppendUnicodeStringToString(PortName, &portSuffix);
|
|
RtlAppendUnicodeStringToString(PortName, &digits);
|
|
|
|
|
|
// Make the class name.
|
|
|
|
ClassName->Length = 0;
|
|
ClassName->MaximumLength = prefix.Length + classSuffix.Length +
|
|
digits.Length + sizeof(WCHAR);
|
|
ClassName->Buffer = ExAllocatePool(PagedPool, ClassName->MaximumLength);
|
|
if (!ClassName->Buffer) {
|
|
ExFreePool(PortName->Buffer);
|
|
return FALSE;
|
|
}
|
|
RtlZeroMemory(ClassName->Buffer, ClassName->MaximumLength);
|
|
RtlAppendUnicodeStringToString(ClassName, &prefix);
|
|
RtlAppendUnicodeStringToString(ClassName, &classSuffix);
|
|
RtlAppendUnicodeStringToString(ClassName, &digits);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
NTSTATUS
|
|
PlGetPortInfoFromPortDevice(
|
|
IN OUT PDEVICE_EXTENSION Extension
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will request the port information from the port driver
|
|
and fill it in the device extension.
|
|
|
|
Arguments:
|
|
|
|
Extension - Supplies the device extension.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - Success.
|
|
!STATUS_SUCCESS - Failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
KEVENT event;
|
|
PIRP irp;
|
|
PARALLEL_PORT_INFORMATION portInfo;
|
|
IO_STATUS_BLOCK ioStatus;
|
|
NTSTATUS status;
|
|
|
|
KeInitializeEvent(&event, NotificationEvent, FALSE);
|
|
|
|
irp = IoBuildDeviceIoControlRequest(IOCTL_INTERNAL_GET_PARALLEL_PORT_INFO,
|
|
Extension->PortDeviceObject,
|
|
NULL, 0, &portInfo,
|
|
sizeof(PARALLEL_PORT_INFORMATION),
|
|
TRUE, &event, &ioStatus);
|
|
|
|
if (!irp) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
status = IoCallDriver(Extension->PortDeviceObject, irp);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
status = KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
Extension->OriginalController = portInfo.OriginalController;
|
|
Extension->Controller = portInfo.Controller;
|
|
Extension->SpanOfController = portInfo.SpanOfController;
|
|
Extension->FreePort = portInfo.FreePort;
|
|
Extension->FreePortContext = portInfo.Context;
|
|
|
|
if (Extension->SpanOfController < PARALLEL_REGISTER_SPAN) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
BOOLEAN
|
|
PlCreateSymbolicLink(
|
|
IN OUT PDEVICE_EXTENSION Extension,
|
|
IN PUNICODE_STRING DeviceName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine creates a symbolic link from \DosDevices\LPT(M) to
|
|
\Device\ParallelLinkN where M is the next available LPT.
|
|
|
|
Arguments:
|
|
|
|
Extension - Supplies the device extension.
|
|
|
|
DeviceName - Supplies the device name.
|
|
|
|
Return Value:
|
|
|
|
FALSE - Failure.
|
|
TRUE - Success.
|
|
|
|
--*/
|
|
|
|
{
|
|
UNICODE_STRING prefix, digits, linkName;
|
|
WCHAR digitsBuffer[10];
|
|
NTSTATUS status;
|
|
ULONG i;
|
|
|
|
Extension->CreatedSymbolicLink = FALSE;
|
|
|
|
RtlInitUnicodeString(&prefix, L"\\DosDevices\\BLPT");
|
|
digits.Length = 0;
|
|
digits.MaximumLength = 20;
|
|
digits.Buffer = digitsBuffer;
|
|
|
|
linkName.MaximumLength = prefix.Length +
|
|
3*sizeof(WCHAR); // 2 digits + '\0'
|
|
linkName.Buffer = ExAllocatePool(PagedPool, linkName.MaximumLength);
|
|
if (!linkName.Buffer) {
|
|
return FALSE;
|
|
}
|
|
RtlZeroMemory(linkName.Buffer, linkName.MaximumLength);
|
|
|
|
for (i = 1; i < MAX_LPT_NUMBER; i++) {
|
|
|
|
status = RtlIntegerToUnicodeString(i, 10, &digits);
|
|
if (!NT_SUCCESS(status)) {
|
|
ExFreePool(linkName.Buffer);
|
|
return FALSE;
|
|
}
|
|
|
|
linkName.Length = 0;
|
|
RtlAppendUnicodeStringToString(&linkName, &prefix);
|
|
RtlAppendUnicodeStringToString(&linkName, &digits);
|
|
|
|
status = IoCreateUnprotectedSymbolicLink(&linkName, DeviceName);
|
|
|
|
if (NT_SUCCESS(status)) {
|
|
Extension->CreatedSymbolicLink = TRUE;
|
|
Extension->SymbolicLinkName = linkName;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i >= MAX_LPT_NUMBER) {
|
|
ExFreePool(linkName.Buffer);
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
NTSTATUS
|
|
PlCreateClose(
|
|
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
|
|
|
|
--*/
|
|
|
|
{
|
|
PlDump(PLIRPPATH, ("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;
|
|
}
|
|
|
|
VOID
|
|
PlCancelRoutine(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is used to cancel a queued IRP.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Supplies the device object.
|
|
|
|
Irp - Supplies the I/O request packet being cancelled.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
|
|
IoReleaseCancelSpinLock(Irp->CancelIrql);
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
Irp->IoStatus.Status = STATUS_CANCELLED;
|
|
|
|
PlDump(PLIRPPATH, ("Completing irp with cancel status.\n"));
|
|
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
}
|
|
|
|
VOID
|
|
PlCancelIrp(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN OUT PIRP Irp,
|
|
IN KIRQL CancelIrql
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine cancels the given irp assuming that the cancel
|
|
spin lock is already held.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Supplies the device object.
|
|
Irp - Supplies the irp to cancel.
|
|
CancelIrql - Supplies the cancel irql.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDRIVER_CANCEL cancelRoutine;
|
|
|
|
Irp->Cancel = TRUE;
|
|
if (cancelRoutine = Irp->CancelRoutine) {
|
|
Irp->CancelIrql = CancelIrql;
|
|
Irp->CancelRoutine = NULL;
|
|
cancelRoutine(DeviceObject, Irp);
|
|
} else {
|
|
IoReleaseCancelSpinLock(CancelIrql);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
PlCancelQueue(
|
|
IN OUT PDEVICE_OBJECT DeviceObject,
|
|
IN OUT PLIST_ENTRY Queue,
|
|
IN OUT PKIRQL CancelIrql
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine cancels all of the IRPs in the given queue.
|
|
|
|
Arguments:
|
|
|
|
Queue - Supplies the queue with the IRPs to cancel.
|
|
CancelIrql - Supplies the current cancel IRQL.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Notes:
|
|
|
|
Must be called while holding the cancel spin lock.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIRP currentIrp;
|
|
|
|
while (!IsListEmpty(Queue)) {
|
|
|
|
currentIrp = CONTAINING_RECORD(Queue->Blink, IRP,
|
|
Tail.Overlay.ListEntry);
|
|
|
|
PlCancelIrp(DeviceObject, currentIrp, *CancelIrql);
|
|
IoAcquireCancelSpinLock(CancelIrql);
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
PlCleanup(
|
|
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;
|
|
KIRQL cancelIrql;
|
|
PIRP currentIrp;
|
|
|
|
extension = DeviceObject->DeviceExtension;
|
|
|
|
IoAcquireCancelSpinLock(&cancelIrql);
|
|
|
|
if (extension->PortAllocateIrp) {
|
|
currentIrp = extension->PortAllocateIrp;
|
|
extension->PortAllocateIrp = NULL;
|
|
PlCancelIrp(DeviceObject, currentIrp, cancelIrql);
|
|
IoAcquireCancelSpinLock(&cancelIrql);
|
|
}
|
|
|
|
PlCancelQueue(DeviceObject, &extension->ReadQueue, &cancelIrql);
|
|
|
|
PlCancelQueue(DeviceObject, &extension->WriteQueue, &cancelIrql);
|
|
|
|
PlCancelQueue(DeviceObject, &extension->WaitQueue, &cancelIrql);
|
|
|
|
IoReleaseCancelSpinLock(cancelIrql);
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
|
|
PlDump(PLIRPPATH, ("Completing cleanup irp.\n"));
|
|
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
PlIncrementUnloadDependencies(
|
|
IN OUT PDEVICE_EXTENSION Extension
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine increments the number of unload dependencies.
|
|
|
|
Arguments:
|
|
|
|
Extension - Supplies the device extension.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
KIRQL oldIrql;
|
|
|
|
KeAcquireSpinLock(&Extension->ControlLock, &oldIrql);
|
|
if (++(Extension->UnloadDependenciesCount) == 1) {
|
|
KeResetEvent(&Extension->UnloadOk);
|
|
}
|
|
KeReleaseSpinLock(&Extension->ControlLock, oldIrql);
|
|
}
|
|
|
|
BOOLEAN
|
|
PlDecrementUnloadDependencies(
|
|
IN OUT PDEVICE_EXTENSION Extension
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine decrments the number of unload dependencies and
|
|
return TRUE if the dependencies count reached zero. This
|
|
routine does not set the 'UnloadOk' event. This is the responsibility
|
|
of the caller if this routine returns TRUE.
|
|
|
|
Arguments:
|
|
|
|
Extension - Supplies the device extension.
|
|
|
|
Return Value:
|
|
|
|
FALSE - The dependencies count did not reach zero.
|
|
TRUE - The dependencies count reached zero.
|
|
|
|
--*/
|
|
|
|
{
|
|
KIRQL oldIrql;
|
|
BOOLEAN r;
|
|
|
|
KeAcquireSpinLock(&Extension->ControlLock, &oldIrql);
|
|
if (--(Extension->UnloadDependenciesCount) == 0) {
|
|
r = TRUE;
|
|
} else {
|
|
r = FALSE;
|
|
}
|
|
KeReleaseSpinLock(&Extension->ControlLock, oldIrql);
|
|
|
|
return r;
|
|
}
|
|
|
|
VOID
|
|
PlFreePort(
|
|
IN PDEVICE_EXTENSION Extension
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine calls the internal free port ioctl. This routine
|
|
should be called before completing an IRP that has allocated
|
|
the port.
|
|
|
|
Arguments:
|
|
|
|
Extension - Supplies the device extension.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
Extension->FreePort(Extension->FreePortContext);
|
|
}
|
|
|
|
VOID
|
|
PlCompleteReadFromPort(
|
|
IN OUT PDEVICE_EXTENSION Extension,
|
|
IN OUT PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine completes the given read irp from the data out
|
|
on the port.
|
|
|
|
Arguments:
|
|
|
|
Extension - Supplies the device extension.
|
|
Irp - Supplies the read irp.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Notes:
|
|
|
|
This needs to run at DISPATCH_LEVEL.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION irpSp;
|
|
PUCHAR p, pend;
|
|
UCHAR input, endofpacket;
|
|
ULONG i;
|
|
|
|
// Acknowledge that we're ready to receive packet.
|
|
|
|
WriteOutput(Extension->Controller, 0x06);
|
|
PlDump(PLINFO, ("Just put out a 0x06\n"));
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
p = Irp->AssociatedIrp.SystemBuffer;
|
|
pend = p + irpSp->Parameters.Read.Length;
|
|
input = 0;
|
|
Irp->IoStatus.Status = STATUS_TIMEOUT;
|
|
|
|
for (;;) {
|
|
|
|
// Try to pick up the low nibble. Check for end of packet.
|
|
|
|
endofpacket = ((~input)|0xC0)&0xF8;
|
|
|
|
for (i = 0; i < PL_CHECK_LOOP; i++) {
|
|
|
|
input = ReadInput(Extension->Controller);
|
|
if (PL_CHECK_INPUT(input, endofpacket)) {
|
|
|
|
PlDump(PLINFO, ("Got end of packet.\n"));
|
|
i = PL_CHECK_LOOP;
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
break;
|
|
} else if (!(input&((UCHAR) 0x80)) &&
|
|
input == ReadInput(Extension->Controller) &&
|
|
input == ReadInput(Extension->Controller) &&
|
|
input == ReadInput(Extension->Controller)) {
|
|
|
|
break;
|
|
}
|
|
|
|
KeStallExecutionProcessor(1);
|
|
}
|
|
|
|
if (i == PL_CHECK_LOOP) {
|
|
PlDump(PLERRORS, ("Could not get low nibble, got %x\n", input));
|
|
break;
|
|
}
|
|
|
|
if (p == pend) {
|
|
break;
|
|
}
|
|
|
|
// acknowledge receipt of low nibble.
|
|
WriteOutput(Extension->Controller, 0xFC);
|
|
PlDump(PLINFO, ("Just put out a 0xFC\n"));
|
|
|
|
*p = (input>>3);
|
|
|
|
// Try to pick up the high nibble.
|
|
|
|
for (i = 0; i < PL_CHECK_LOOP; i++) {
|
|
|
|
input = ReadInput(Extension->Controller);
|
|
if (input&((UCHAR) 0x80) &&
|
|
input == ReadInput(Extension->Controller) &&
|
|
input == ReadInput(Extension->Controller) &&
|
|
input == ReadInput(Extension->Controller)) {
|
|
|
|
break;
|
|
}
|
|
|
|
KeStallExecutionProcessor(1);
|
|
}
|
|
|
|
if (i == PL_CHECK_LOOP) {
|
|
PlDump(PLERRORS, ("Could not get high nibble, got %x\n", input));
|
|
break;
|
|
}
|
|
|
|
// acknowledge receipt of high nibble.
|
|
WriteOutput(Extension->Controller, 0x03);
|
|
PlDump(PLINFO, ("Just put out a 0x03\n"));
|
|
|
|
*p |= ((input<<1)&0xF0);
|
|
p++;
|
|
}
|
|
|
|
|
|
// Say that we're idle here.
|
|
WriteOutput(Extension->Controller, 0x0A);
|
|
PlDump(PLINFO, ("Just put out a 0x0A\n"));
|
|
|
|
|
|
// Complete the irp with what we got. Or fail the irp if
|
|
// we didn't get anything.
|
|
|
|
if (Irp->IoStatus.Status == STATUS_SUCCESS) {
|
|
Irp->IoStatus.Information = p - (PUCHAR) Irp->AssociatedIrp.SystemBuffer;
|
|
} else if (p == pend) {
|
|
Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
PlDump(PLIRPPATH, ("Completing read irp from port.\n"));
|
|
}
|
|
|
|
BOOLEAN
|
|
PlCompleteWriteToPort(
|
|
IN OUT PDEVICE_EXTENSION Extension,
|
|
IN OUT PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine completes the given write irp to the port.
|
|
|
|
Arguments:
|
|
|
|
Extension - Supplies the device extension.
|
|
Irp - Supplies the read irp.
|
|
|
|
Return Value:
|
|
|
|
FALSE - The write operation failed.
|
|
TRUE - The write operation succeeded.
|
|
|
|
Notes:
|
|
|
|
This routine must run at DISPATCH_LEVEL.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION irpSp;
|
|
PUCHAR p;
|
|
ULONG i, j;
|
|
UCHAR input;
|
|
|
|
// Say that we're ready to transfer.
|
|
|
|
WriteOutput(Extension->Controller, 0x05);
|
|
PlDump(PLINFO, ("Just put out a 0x05\n"));
|
|
|
|
for (i = 0; i < PL_SLOW_CHECK_LOOP; i++) {
|
|
|
|
input = ReadInput(Extension->Controller);
|
|
if (PACKET_ACKNOWLEDGE(input)) {
|
|
break;
|
|
}
|
|
|
|
KeStallExecutionProcessor(1);
|
|
}
|
|
|
|
if (i == PL_SLOW_CHECK_LOOP) {
|
|
|
|
PlDump(PLERRORS, ("Could not get packet ack, got %x\n", input));
|
|
return FALSE;
|
|
}
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
p = Irp->AssociatedIrp.SystemBuffer;
|
|
|
|
for (i = 0; i < irpSp->Parameters.Write.Length; i++) {
|
|
|
|
WriteOutput(Extension->Controller, p[i]|0x10);
|
|
|
|
for (j = 0; j < PL_CHECK_LOOP; j++) {
|
|
|
|
input = ReadInput(Extension->Controller);
|
|
if (LOW_NIBBLE_ACK(input)) {
|
|
break;
|
|
}
|
|
|
|
KeStallExecutionProcessor(1);
|
|
}
|
|
|
|
if (j == PL_CHECK_LOOP) {
|
|
PlDump(PLERRORS, ("Could not get low nibble ack, got %x\n", input));
|
|
break;
|
|
}
|
|
|
|
WriteOutput(Extension->Controller, (p[i]>>4)&0x0F);
|
|
|
|
for (j = 0; j < PL_CHECK_LOOP; j++) {
|
|
|
|
input = ReadInput(Extension->Controller);
|
|
if (HIGH_NIBBLE_ACK(input)) {
|
|
break;
|
|
}
|
|
|
|
KeStallExecutionProcessor(1);
|
|
}
|
|
|
|
if (j == PL_CHECK_LOOP) {
|
|
PlDump(PLERRORS, ("Could not get low nibble ack, got %x\n", input));
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i != irpSp->Parameters.Write.Length) {
|
|
|
|
// Say that we're idle again.
|
|
WriteOutput(Extension->Controller, 0x0A);
|
|
PlDump(PLINFO, ("Just put out a 0x0A\n"));
|
|
|
|
PlDump(PLIRPPATH, ("write failed.\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
// Since the whole packet was sent, put out the end packet signal.
|
|
WriteOutput(Extension->Controller, (((~p[i-1])>>4)|0x08)&0x0F);
|
|
|
|
Irp->IoStatus.Information = irpSp->Parameters.Write.Length;
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
PlDump(PLIRPPATH, ("Completing successful write to port.\n"));
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID
|
|
PlCompleteAllWaits(
|
|
IN OUT PDEVICE_EXTENSION Extension,
|
|
IN ULONG EventMask
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine completes all of the wait IRPs with the given
|
|
event mask.
|
|
|
|
Arguments:
|
|
|
|
Extension - Supplies the device extension.
|
|
EventMask - Supplies the event that occurred.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
LIST_ENTRY list;
|
|
KIRQL cancelIrql;
|
|
PLIST_ENTRY head;
|
|
PIRP irp;
|
|
PULONG p;
|
|
|
|
InitializeListHead(&list);
|
|
|
|
|
|
// Take all of them at once while holding the cancel spin lock ...
|
|
|
|
IoAcquireCancelSpinLock(&cancelIrql);
|
|
|
|
while (!IsListEmpty(&Extension->WaitQueue)) {
|
|
|
|
head = RemoveHeadList(&Extension->WaitQueue);
|
|
irp = CONTAINING_RECORD(head, IRP, Tail.Overlay.ListEntry);
|
|
IoSetCancelRoutine(irp, NULL);
|
|
|
|
InsertTailList(&list, head);
|
|
}
|
|
|
|
IoReleaseCancelSpinLock(cancelIrql);
|
|
|
|
|
|
// ... then complete all of them.
|
|
|
|
while (!IsListEmpty(&list)) {
|
|
|
|
head = RemoveHeadList(&list);
|
|
irp = CONTAINING_RECORD(head, IRP, Tail.Overlay.ListEntry);
|
|
|
|
p = irp->AssociatedIrp.SystemBuffer;
|
|
*p = EventMask;
|
|
|
|
irp->IoStatus.Information = sizeof(ULONG);
|
|
irp->IoStatus.Status = STATUS_SUCCESS;
|
|
|
|
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
PlSetDcd(
|
|
IN OUT PDEVICE_EXTENSION Extension,
|
|
IN BOOLEAN IsDcdUp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine sets the DCD active variable in the extension
|
|
and return the wait irps if the DCD changed state.
|
|
|
|
Arguments:
|
|
|
|
Extension - Supplies the device extension.
|
|
IsDcdUp - Supplies the new DCD state.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
KIRQL oldIrql;
|
|
BOOLEAN completeWait;
|
|
|
|
KeAcquireSpinLock(&Extension->ControlLock, &oldIrql);
|
|
if (IsDcdUp == Extension->IsDcdUp) {
|
|
completeWait = FALSE;
|
|
} else {
|
|
Extension->IsDcdUp = IsDcdUp;
|
|
if (Extension->WaitMask & SERIAL_EV_RLSD) {
|
|
completeWait = TRUE;
|
|
} else {
|
|
completeWait = FALSE;
|
|
}
|
|
}
|
|
KeReleaseSpinLock(&Extension->ControlLock, oldIrql);
|
|
|
|
if (completeWait) {
|
|
PlCompleteAllWaits(Extension, SERIAL_EV_RLSD);
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
PlAllocPortCompletionRoutine(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Extension
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the completion routine for an alloc port request. This
|
|
routine is called when this driver owns the parallel port. This
|
|
routine will examine the work queue and attempt to satisfy a
|
|
read or write request.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Supplies the device object.
|
|
Irp - Supplies the I/O request packet.
|
|
Extension - Supplies the device extension.
|
|
|
|
Return Value:
|
|
|
|
STATUS_MORE_PROCESSING_REQUIRED
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION extension;
|
|
NTSTATUS status;
|
|
KIRQL oldIrql, cancelIrql;
|
|
PIRP irp;
|
|
PLIST_ENTRY head;
|
|
BOOLEAN spawnDpc;
|
|
BOOLEAN writeSucceeded;
|
|
UCHAR input;
|
|
|
|
extension = Extension;
|
|
status = Irp->IoStatus.Status;
|
|
IoAcquireCancelSpinLock(&cancelIrql);
|
|
extension->PortAllocateIrp = NULL;
|
|
IoReleaseCancelSpinLock(cancelIrql);
|
|
IoFreeIrp(Irp);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
PlDump(PLERRORS, ("Allocate port ioctl failed.\n"));
|
|
|
|
PlLogError(DeviceObject->DriverObject, DeviceObject,
|
|
extension->OriginalController, PhysicalZero,
|
|
0, 0, 0, 6, STATUS_SUCCESS, PAR_PORT_ALLOCATE_FAILED);
|
|
|
|
if (PlDecrementUnloadDependencies(extension)) {
|
|
KeSetEvent(&extension->UnloadOk, 0, FALSE);
|
|
}
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
|
|
// Check to see if the overflow bytes can be used to satisfy a read
|
|
// request.
|
|
|
|
KeRaiseIrql(DISPATCH_LEVEL, &oldIrql);
|
|
|
|
input = ReadInput(extension->Controller);
|
|
|
|
if (DCD_DEAD(input)) {
|
|
PlSetDcd(extension, FALSE);
|
|
}
|
|
|
|
IoAcquireCancelSpinLock(&cancelIrql);
|
|
|
|
if (!IsListEmpty(&extension->ReadQueue) && INCOMING_PACKET(input)) {
|
|
head = RemoveHeadList(&extension->ReadQueue);
|
|
irp = CONTAINING_RECORD(head, IRP, Tail.Overlay.ListEntry);
|
|
IoSetCancelRoutine(irp, NULL);
|
|
IoReleaseCancelSpinLock(cancelIrql);
|
|
PlCompleteReadFromPort(extension, irp);
|
|
KeLowerIrql(oldIrql);
|
|
|
|
if (irp->IoStatus.Status == STATUS_SUCCESS) {
|
|
PlSetDcd(extension, TRUE);
|
|
}
|
|
|
|
IoCompleteRequest(irp, IO_PARALLEL_INCREMENT);
|
|
PlFreePort(extension);
|
|
PlAllocPort(extension);
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
if (!IsListEmpty(&extension->WriteQueue)) {
|
|
head = RemoveHeadList(&extension->WriteQueue);
|
|
irp = CONTAINING_RECORD(head, IRP, Tail.Overlay.ListEntry);
|
|
IoSetCancelRoutine(irp, NULL);
|
|
IoReleaseCancelSpinLock(cancelIrql);
|
|
|
|
writeSucceeded = PlCompleteWriteToPort(extension, irp);
|
|
KeLowerIrql(oldIrql);
|
|
|
|
if (writeSucceeded) {
|
|
|
|
// If the call succeeded then we're done.
|
|
|
|
extension->WriteRetryCount = 0;
|
|
IoCompleteRequest(irp, IO_PARALLEL_INCREMENT);
|
|
|
|
} else if (++(extension->WriteRetryCount) >= MAX_WRITE_RETRY_COUNT) {
|
|
|
|
// If the call failed and this is this is the third time
|
|
// a write fails without a successful write then
|
|
// return STATUS_TIMEOUT and lower DCD.
|
|
|
|
PlSetDcd(extension, FALSE);
|
|
extension->WriteRetryCount = 0;
|
|
irp->IoStatus.Status = STATUS_TIMEOUT;
|
|
PlDump(PLIRPPATH, ("Completing write irp with timeout status.\n"));
|
|
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
|
|
|
} else {
|
|
|
|
// The write timed out. Try it again later.
|
|
|
|
IoAcquireCancelSpinLock(&cancelIrql);
|
|
if (irp->Cancel) {
|
|
IoReleaseCancelSpinLock(cancelIrql);
|
|
irp->IoStatus.Status = STATUS_CANCELLED;
|
|
IoCompleteRequest(irp, IO_NO_INCREMENT);
|
|
} else {
|
|
InsertHeadList(&extension->WriteQueue,
|
|
&irp->Tail.Overlay.ListEntry);
|
|
IoSetCancelRoutine(irp, PlCancelRoutine);
|
|
IoReleaseCancelSpinLock(cancelIrql);
|
|
}
|
|
}
|
|
|
|
PlFreePort(extension);
|
|
PlAllocPort(extension);
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
extension->NeedPortAllocation = TRUE;
|
|
if (IsListEmpty(&extension->ReadQueue)) {
|
|
spawnDpc = FALSE;
|
|
} else {
|
|
PlIncrementUnloadDependencies(extension);
|
|
spawnDpc = TRUE;
|
|
}
|
|
IoReleaseCancelSpinLock(cancelIrql);
|
|
KeLowerIrql(oldIrql);
|
|
|
|
if (spawnDpc) {
|
|
KeSetTimer(&extension->ReadTimer, extension->ReadDpcTime,
|
|
&extension->ReadDpc);
|
|
}
|
|
|
|
PlFreePort(extension);
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
VOID
|
|
PlAllocPort(
|
|
IN PDEVICE_EXTENSION Extension
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine calls the internal alloc port ioctl.
|
|
|
|
Arguments:
|
|
|
|
Extension - Supplies the device extension.
|
|
|
|
Return Value:
|
|
|
|
The IRP that was created for the alloc request.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIRP irp;
|
|
KIRQL cancelIrql;
|
|
PIO_STACK_LOCATION irpSp;
|
|
|
|
irp = IoAllocateIrp(Extension->DeviceObject->StackSize, FALSE);
|
|
|
|
if (!irp) {
|
|
PlDump(PLERRORS, ("Couldn't allocate irp for alloc port ioctl.\n"));
|
|
if (PlDecrementUnloadDependencies(Extension)) {
|
|
KeSetEvent(&Extension->UnloadOk, 0, FALSE);
|
|
}
|
|
return;
|
|
}
|
|
|
|
irpSp = IoGetNextIrpStackLocation(irp);
|
|
irpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
|
|
irpSp->Parameters.DeviceIoControl.IoControlCode =
|
|
IOCTL_INTERNAL_PARALLEL_PORT_ALLOCATE;
|
|
|
|
IoSetCompletionRoutine(irp, PlAllocPortCompletionRoutine, Extension,
|
|
TRUE, TRUE, TRUE);
|
|
|
|
IoAcquireCancelSpinLock(&cancelIrql);
|
|
Extension->PortAllocateIrp = irp;
|
|
IoReleaseCancelSpinLock(cancelIrql);
|
|
|
|
IoCallDriver(Extension->PortDeviceObject, irp);
|
|
}
|
|
|
|
VOID
|
|
PlReadDpc(
|
|
IN PKDPC ReadDpc,
|
|
IN PVOID Extension,
|
|
IN PVOID SystemArgument1,
|
|
IN PVOID SystemArgument2
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is a timer dpc that is set to wake up because
|
|
the other computer wasn't ready for transfer. This routine
|
|
will transfer if possible.
|
|
|
|
Arguments:
|
|
|
|
ReadDpc - Supplies this dpc.
|
|
Extension - Supplies the device extension.
|
|
SystemArgument1 - Not used.
|
|
SystemArgument2 - Not used.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_EXTENSION extension;
|
|
KIRQL cancelIrql;
|
|
BOOLEAN needAlloc;
|
|
|
|
extension = Extension;
|
|
IoAcquireCancelSpinLock(&cancelIrql);
|
|
if (needAlloc = extension->NeedPortAllocation) {
|
|
PlIncrementUnloadDependencies(extension);
|
|
extension->NeedPortAllocation = FALSE;
|
|
}
|
|
IoReleaseCancelSpinLock(cancelIrql);
|
|
|
|
if (needAlloc) {
|
|
PlAllocPort(extension);
|
|
}
|
|
|
|
if (PlDecrementUnloadDependencies(extension)) {
|
|
KeSetEvent(&extension->UnloadOk, 0, FALSE);
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
PlReadWrite(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine implements the read and write dispatch.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Supplies the device object.
|
|
|
|
Irp - Supplies the read IRP.
|
|
|
|
Return Value:
|
|
|
|
STATUS_PENDING - The request is pending.
|
|
STATUS_SUCCESS - The request was completed successfully.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION irpSp;
|
|
PDEVICE_EXTENSION extension;
|
|
KIRQL cancelIrql;
|
|
BOOLEAN needAlloc;
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
extension = DeviceObject->DeviceExtension;
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
|
|
// Check for the trivial cases.
|
|
|
|
if ((irpSp->MajorFunction == IRP_MJ_READ &&
|
|
!irpSp->Parameters.Read.Length) ||
|
|
(irpSp->MajorFunction == IRP_MJ_WRITE &&
|
|
!irpSp->Parameters.Write.Length)) {
|
|
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
PlDump(PLIRPPATH, ("Completing irp with length zero.\n"));
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
// Writes of more that one packet are not allowed.
|
|
|
|
if (irpSp->MajorFunction == IRP_MJ_WRITE &&
|
|
irpSp->Parameters.Write.Length > 2048) {
|
|
|
|
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
|
|
PlDump(PLIRPPATH, ("Completing write with invalid parameter.\n"));
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
|
|
// If the case is not trivial then queue it up and
|
|
// determine whether or not there needs to be an
|
|
// alloc sent down to the port driver.
|
|
|
|
IoAcquireCancelSpinLock(&cancelIrql);
|
|
|
|
if (Irp->Cancel) {
|
|
IoReleaseCancelSpinLock(cancelIrql);
|
|
Irp->IoStatus.Status = STATUS_CANCELLED;
|
|
PlDump(PLIRPPATH, ("Completing irp as cancelled from dispatch.\n"));
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
return STATUS_CANCELLED;
|
|
}
|
|
|
|
IoSetCancelRoutine(Irp, PlCancelRoutine);
|
|
|
|
IoMarkIrpPending(Irp);
|
|
|
|
if (irpSp->MajorFunction == IRP_MJ_READ) {
|
|
InsertTailList(&extension->ReadQueue, &Irp->Tail.Overlay.ListEntry);
|
|
} else {
|
|
InsertTailList(&extension->WriteQueue, &Irp->Tail.Overlay.ListEntry);
|
|
}
|
|
|
|
if (needAlloc = extension->NeedPortAllocation) {
|
|
PlIncrementUnloadDependencies(extension);
|
|
extension->NeedPortAllocation = FALSE;
|
|
}
|
|
|
|
IoReleaseCancelSpinLock(cancelIrql);
|
|
|
|
if (needAlloc) {
|
|
PlAllocPort(extension);
|
|
}
|
|
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
NTSTATUS
|
|
PlDeviceControlCompletionRoutine(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PVOID Extension
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the completion routine for an alloc port request that was
|
|
made with a device control request. This routine will attempt
|
|
to satisfy the device control request and then free the port.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Supplies the device object.
|
|
Irp - Supplies the I/O request packet.
|
|
Extension - Supplies the device extension.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION irpSp;
|
|
PDEVICE_EXTENSION extension;
|
|
|
|
irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
extension = Extension;
|
|
Irp->IoStatus.Information = 0;
|
|
Irp->IoStatus.Status = STATUS_SUCCESS;
|
|
|
|
switch (irpSp->Parameters.DeviceIoControl.IoControlCode) {
|
|
|
|
case IOCTL_SERIAL_SET_DTR:
|
|
// Put out an idle signal.
|
|
WriteOutput(extension->Controller, 0x0A);
|
|
break;
|
|
|
|
case IOCTL_SERIAL_CLR_DTR:
|
|
// Put out a dead signal.
|
|
WriteOutput(extension->Controller, 0x02);
|
|
|
|
PlSetDcd(extension, FALSE);
|
|
break;
|
|
|
|
default:
|
|
Irp->IoStatus.Status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
|
|
}
|
|
|
|
PlFreePort(extension);
|
|
|
|
return Irp->IoStatus.Status;
|
|
}
|
|
|
|
NTSTATUS
|
|
PlDeviceControl(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This is the device control dispatch.
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - Supplies the device object.
|
|
|
|
Irp - Supplies the read IRP.
|
|
|
|
Return Value:
|
|
|
|
STATUS_PENDING - The request is pending.
|
|
STATUS_SUCCESS - The request was completed successfully.
|
|
|
|
--*/
|
|
|
|
{
|
|
PIO_STACK_LOCATION irpSp, nextSp;
|
|
PDEVICE_EXTENSION extension;
|
|
NTSTATUS status;
|
|
PULONG p;
|
|
KIRQL oldIrql, cancelIrql;
|
|
|
|
Irp->IoStatus.Information = 0;
|
|
irpSp = IoGetCurrentIrpStackLocation(Irp);
|
|
extension = DeviceObject->DeviceExtension;
|
|
|
|
switch (irpSp->Parameters.DeviceIoControl.IoControlCode) {
|
|
|
|
case IOCTL_SERIAL_GET_COMMSTATUS:
|
|
if (irpSp->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(SERIAL_STATUS)) {
|
|
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
} else {
|
|
RtlZeroMemory(Irp->AssociatedIrp.SystemBuffer,
|
|
sizeof(SERIAL_STATUS));
|
|
Irp->IoStatus.Information = sizeof(SERIAL_STATUS);
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
break;
|
|
|
|
case IOCTL_SERIAL_GET_WAIT_MASK:
|
|
if (irpSp->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(ULONG)) {
|
|
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
} else {
|
|
p = Irp->AssociatedIrp.SystemBuffer;
|
|
KeAcquireSpinLock(&extension->ControlLock, &oldIrql);
|
|
*p = extension->WaitMask;
|
|
KeReleaseSpinLock(&extension->ControlLock, oldIrql);
|
|
status = STATUS_SUCCESS;
|
|
Irp->IoStatus.Information = sizeof(ULONG);
|
|
}
|
|
break;
|
|
|
|
case IOCTL_SERIAL_GET_MODEMSTATUS:
|
|
if (irpSp->Parameters.DeviceIoControl.OutputBufferLength <
|
|
sizeof(ULONG)) {
|
|
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
} else {
|
|
p = Irp->AssociatedIrp.SystemBuffer;
|
|
KeAcquireSpinLock(&extension->ControlLock, &oldIrql);
|
|
*p = extension->IsDcdUp ? SERIAL_DCD_STATE : 0;
|
|
KeReleaseSpinLock(&extension->ControlLock, oldIrql);
|
|
status = STATUS_SUCCESS;
|
|
Irp->IoStatus.Information = sizeof(ULONG);
|
|
}
|
|
break;
|
|
|
|
case IOCTL_SERIAL_PURGE:
|
|
if (irpSp->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(ULONG)) {
|
|
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
} else {
|
|
p = Irp->AssociatedIrp.SystemBuffer;
|
|
if ((*p) & SERIAL_PURGE_TXABORT) {
|
|
IoAcquireCancelSpinLock(&cancelIrql);
|
|
PlCancelQueue(DeviceObject, &extension->WriteQueue,
|
|
&cancelIrql);
|
|
IoReleaseCancelSpinLock(cancelIrql);
|
|
}
|
|
|
|
if ((*p) & SERIAL_PURGE_RXABORT) {
|
|
IoAcquireCancelSpinLock(&cancelIrql);
|
|
PlCancelQueue(DeviceObject, &extension->ReadQueue,
|
|
&cancelIrql);
|
|
IoReleaseCancelSpinLock(cancelIrql);
|
|
}
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
break;
|
|
|
|
case IOCTL_SERIAL_SET_WAIT_MASK:
|
|
if (irpSp->Parameters.DeviceIoControl.InputBufferLength <
|
|
sizeof(ULONG)) {
|
|
|
|
status = STATUS_BUFFER_TOO_SMALL;
|
|
} else {
|
|
|
|
// First complete any pending wait irp.
|
|
|
|
PlCompleteAllWaits(extension, 0);
|
|
|
|
|
|
// Set the wait mask.
|
|
|
|
p = Irp->AssociatedIrp.SystemBuffer;
|
|
KeAcquireSpinLock(&extension->ControlLock, &oldIrql);
|
|
extension->WaitMask = *p;
|
|
KeReleaseSpinLock(&extension->ControlLock, oldIrql);
|
|
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
break;
|
|
|
|
case IOCTL_SERIAL_WAIT_ON_MASK:
|
|
IoAcquireCancelSpinLock(&cancelIrql);
|
|
if (Irp->Cancel) {
|
|
status = STATUS_CANCELLED;
|
|
} else {
|
|
IoSetCancelRoutine(Irp, PlCancelRoutine);
|
|
InsertTailList(&extension->WaitQueue,
|
|
&Irp->Tail.Overlay.ListEntry);
|
|
IoMarkIrpPending(Irp);
|
|
status = STATUS_PENDING;
|
|
}
|
|
IoReleaseCancelSpinLock(cancelIrql);
|
|
break;
|
|
|
|
case IOCTL_SERIAL_SET_DTR:
|
|
case IOCTL_SERIAL_CLR_DTR:
|
|
|
|
// piggy back these to an alloc port call.
|
|
|
|
IoMarkIrpPending(Irp);
|
|
status = STATUS_PENDING;
|
|
nextSp = IoGetNextIrpStackLocation(Irp);
|
|
nextSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
|
|
nextSp->Parameters.DeviceIoControl.IoControlCode =
|
|
IOCTL_INTERNAL_PARALLEL_PORT_ALLOCATE;
|
|
|
|
IoSetCompletionRoutine(Irp, PlDeviceControlCompletionRoutine,
|
|
extension, TRUE, FALSE, FALSE);
|
|
|
|
IoCallDriver(extension->PortDeviceObject, Irp);
|
|
break;
|
|
|
|
default:
|
|
status = STATUS_INVALID_PARAMETER;
|
|
break;
|
|
|
|
}
|
|
|
|
if (status != STATUS_PENDING) {
|
|
Irp->IoStatus.Status = status;
|
|
IoCompleteRequest(Irp, IO_NO_INCREMENT);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
VOID
|
|
PlUnload(
|
|
IN PDRIVER_OBJECT DriverObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine loops through the device list and cleans up after
|
|
each of the devices.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - Supplies the driver object.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_OBJECT currentDevice;
|
|
PDEVICE_EXTENSION extension;
|
|
|
|
PlDump(
|
|
PLUNLOAD,
|
|
("PARLINK: In PlUnload\n")
|
|
);
|
|
|
|
while (currentDevice = DriverObject->DeviceObject) {
|
|
|
|
extension = currentDevice->DeviceExtension;
|
|
|
|
if (extension->CreatedSymbolicLink) {
|
|
IoDeleteSymbolicLink(&extension->SymbolicLinkName);
|
|
ExFreePool(extension->SymbolicLinkName.Buffer);
|
|
}
|
|
|
|
KeWaitForSingleObject(&extension->UnloadOk, Executive,
|
|
KernelMode, FALSE, NULL);
|
|
|
|
IoDeleteDevice(currentDevice);
|
|
}
|
|
}
|