windows-nt/Source/XPSP1/NT/drivers/parallel/parport2/utils.c
2020-09-26 16:20:57 +08:00

3495 lines
95 KiB
C
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation, 1998 - 1999
//
// File: util.c
//
//--------------------------------------------------------------------------
#include "pch.h"
#define IDMFG 0
#define IDMDL 1
#define NUMOFBROTHERPRODUCT 14
CHAR *XflagOnEvent24Devices[NUMOFBROTHERPRODUCT][2] =
{
// Brother
{"Brother", "MFC" }, // 0
{"Brother", "FAX" }, // 1
{"Brother", "HL-P" }, // 2
{"Brother", "DCP" }, // 3
// PB
{"PitneyBowes", "1630" }, // 4
{"PitneyBowes", "1640" }, // 5
// LEGEND
{"LEGEND", "LJ6112MFC" }, // 6
{"LEGEND", "LJ6212MFC" }, // 7
//
{"HBP", "MFC 6550" }, // 8
{"HBP", "OMNI L621" }, // 9
{"HBP", "LJ 6106MFC" }, // 10
{"HBP", "LJ 6206MFC" }, // 11
// P2500
{"Legend", "LJ6012MFP" }, // 12
//Terminater
{NULL, NULL } // 13
};
NTSTATUS
PptAcquireRemoveLockOrFailIrp(PDEVICE_OBJECT DevObj, PIRP Irp)
{
PFDO_EXTENSION fdx = DevObj->DeviceExtension;
NTSTATUS status = PptAcquireRemoveLock( &fdx->RemoveLock, Irp );
if( status != STATUS_SUCCESS ) {
P4CompleteRequest( Irp, status, Irp->IoStatus.Information );
}
return status;
}
NTSTATUS
PptDispatchPreProcessIrp(
IN PDEVICE_OBJECT DevObj,
IN PIRP Irp
)
/*++
- Acquire removelock
- If(!Special Handling IRP) {
check if we are running, stalled
--*/
{
PFDO_EXTENSION Fdx = DevObj->DeviceExtension;
NTSTATUS status = PptAcquireRemoveLock(&Fdx->RemoveLock, Irp);
UNREFERENCED_PARAMETER(DevObj);
UNREFERENCED_PARAMETER(Irp);
if ( !NT_SUCCESS( status ) ) {
//
// Someone gave us a pnp irp after a remove. Unthinkable!
//
PptAssertMsg("Someone gave us a PnP IRP after a Remove",FALSE);
P4CompleteRequest( Irp, status, 0 );
}
return status;
}
NTSTATUS
PptSynchCompletionRoutine(
IN PDEVICE_OBJECT DevObj,
IN PIRP Irp,
IN PKEVENT Event
)
/*++
Routine Description:
This routine is for use with synchronous IRP processing.
All it does is signal an event, so the driver knows it
can continue.
Arguments:
DriverObject - Pointer to driver object created by system.
Irp - Irp that just completed
Event - Event we'll signal to say Irp is done
Return Value:
None.
--*/
{
UNREFERENCED_PARAMETER( Irp );
UNREFERENCED_PARAMETER( DevObj );
KeSetEvent((PKEVENT) Event, 0, FALSE);
return (STATUS_MORE_PROCESSING_REQUIRED);
}
PWSTR
PptGetPortNameFromPhysicalDeviceObject(
PDEVICE_OBJECT PhysicalDeviceObject
)
/*++
Routine Description:
Retrieve the PortName for the ParPort from the registry. This PortName
will be used as the symbolic link name for the end of chain device
object created by ParClass for this ParPort.
*** This function allocates pool. ExFreePool must be called when
result is no longer needed.
Arguments:
PortDeviceObject - The ParPort Device Object
Return Value:
PortName - if successful
NULL - otherwise
--*/
{
NTSTATUS status;
HANDLE hKey;
PKEY_VALUE_FULL_INFORMATION buffer;
ULONG bufferLength;
ULONG resultLength;
PWSTR valueNameWstr;
UNICODE_STRING valueName;
PWSTR portName = NULL;
PAGED_CODE ();
//
// try to open the registry key
//
status = IoOpenDeviceRegistryKey(PhysicalDeviceObject,
PLUGPLAY_REGKEY_DEVICE,
STANDARD_RIGHTS_ALL,
&hKey);
if( !NT_SUCCESS(status) ) {
// unable to open key, bail out
DD(NULL,DDT,"PptGetPortNameFromPhysicalDeviceObject(): FAIL w/status = %x\n", status);
return NULL;
}
//
// we have a handle to the registry key
//
// loop trying to read registry value until either we succeed or
// we get a hard failure, grow the result buffer as needed
//
bufferLength = 0; // we will ask how large a buffer we need
buffer = NULL;
valueNameWstr = (PWSTR)L"PortName";
RtlInitUnicodeString(&valueName, valueNameWstr);
status = STATUS_BUFFER_TOO_SMALL;
while(status == STATUS_BUFFER_TOO_SMALL) {
status = ZwQueryValueKey(hKey,
&valueName,
KeyValueFullInformation,
buffer,
bufferLength,
&resultLength);
if(status == STATUS_BUFFER_TOO_SMALL) {
//
// buffer too small, free it and allocate a larger buffer
//
if(buffer) ExFreePool(buffer);
buffer = ExAllocatePool(PagedPool | POOL_COLD_ALLOCATION, resultLength);
bufferLength = resultLength;
if(!buffer) {
// unable to allocate pool, clean up and exit
ZwClose(hKey);
return NULL;
}
}
} // end while BUFFER_TOO_SMALL
//
// query is complete
//
// no longer need the handle so close it
ZwClose(hKey);
// check the status of our query
if( !NT_SUCCESS(status) ) {
if(buffer) ExFreePool(buffer);
return NULL;
}
// make sure we have a buffer
if( buffer ) {
// sanity check our result
if( (buffer->Type != REG_SZ) || (!buffer->DataLength) ) {
ExFreePool(buffer); // query succeeded, so we know we have a buffer
return NULL;
}
//
// result looks ok, copy PortName to its own allocation of the proper size
// and return a pointer to it
//
portName = ExAllocatePool(PagedPool | POOL_COLD_ALLOCATION, buffer->DataLength);
if(!portName) {
// unable to allocate pool, clean up and exit
ExFreePool(buffer);
return NULL;
}
RtlCopyMemory(portName, (PUCHAR)buffer + buffer->DataOffset, buffer->DataLength);
ExFreePool( buffer );
}
return portName;
}
NTSTATUS
PptConnectInterrupt(
IN PFDO_EXTENSION Fdx
)
/*++
Routine Description:
This routine connects the port interrupt service routine
to the interrupt.
Arguments:
Fdx - Supplies the device extension.
Return Value:
NTSTATUS code.
--*/
{
NTSTATUS Status = STATUS_SUCCESS;
if (!Fdx->FoundInterrupt) {
return STATUS_NOT_SUPPORTED;
}
//
// Connect the interrupt.
//
Status = IoConnectInterrupt(&Fdx->InterruptObject,
PptInterruptService,
Fdx,
NULL,
Fdx->InterruptVector,
Fdx->InterruptLevel,
Fdx->InterruptLevel,
Fdx->InterruptMode,
TRUE,
Fdx->InterruptAffinity,
FALSE);
if (!NT_SUCCESS(Status)) {
PptLogError(Fdx->DeviceObject->DriverObject,
Fdx->DeviceObject,
Fdx->PortInfo.OriginalController,
PhysicalZero, 0, 0, 0, 14,
Status, PAR_INTERRUPT_CONFLICT);
}
return Status;
}
VOID
PptDisconnectInterrupt(
IN PFDO_EXTENSION Fdx
)
/*++
Routine Description:
This routine disconnects the port interrupt service routine
from the interrupt.
Arguments:
Fdx - Supplies the device extension.
Return Value:
None.
--*/
{
IoDisconnectInterrupt(Fdx->InterruptObject);
}
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.
--*/
{
PFDO_EXTENSION Fdx = DeviceObject->DeviceExtension;
SYNCHRONIZED_COUNT_CONTEXT SyncContext;
DD((PCE)Fdx,DDT,"CANCEL IRP %x\n", Irp);
SyncContext.Count = &Fdx->WorkQueueCount;
if (Fdx->InterruptRefCount) {
KeSynchronizeExecution( Fdx->InterruptObject, PptSynchronizedDecrement, &SyncContext );
} else {
PptSynchronizedDecrement( &SyncContext );
}
RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
IoReleaseCancelSpinLock(Irp->CancelIrql);
PptReleaseRemoveLock(&Fdx->RemoveLock, Irp);
P4CompleteRequest( Irp, STATUS_CANCELLED, 0 );
}
VOID
PptFreePortDpc(
IN PKDPC Dpc,
IN OUT PVOID Fdx,
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.
Fdx - Supplies the device extension.
SystemArgument1 - Not used.
SystemArgument2 - Not used.
Return Value:
None.
--*/
{
UNREFERENCED_PARAMETER( Dpc );
UNREFERENCED_PARAMETER( SystemArgument1 );
UNREFERENCED_PARAMETER( SystemArgument2 );
PptFreePort(Fdx);
}
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 (((PFDO_EXTENSION) Context)->WorkQueueCount == -1) {
((PFDO_EXTENSION) Context)->WorkQueueCount = 0;
( (PFDO_EXTENSION)Context )->WmiPortAllocFreeCounts.PortAllocates++;
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 (((PFDO_EXTENSION) Context)->WorkQueueCount == 0) {
((PFDO_EXTENSION) Context)->WorkQueueCount = -1;
} else {
KeInsertQueueDpc(&((PFDO_EXTENSION) Context)->FreePortDpc, NULL, NULL);
}
}
BOOLEAN
PptInterruptService(
IN PKINTERRUPT Interrupt,
IN PVOID Fdx
)
/*++
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.
Fdx - Supplies the device extension.
Return Value:
FALSE - The interrupt was not handled.
TRUE - The interrupt was handled.
--*/
{
PLIST_ENTRY Current;
PISR_LIST_ENTRY IsrListEntry;
PFDO_EXTENSION fdx = Fdx;
for( Current = fdx->IsrList.Flink; Current != &fdx->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 Fdx
)
/*++
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:
Fdx - Supplies the device extension.
Return Value:
FALSE - The port was not allocated.
TRUE - The port was allocated.
--*/
{
PFDO_EXTENSION fdx = Fdx;
KIRQL CancelIrql;
BOOLEAN b;
if (fdx->InterruptRefCount) {
b = KeSynchronizeExecution( fdx->InterruptObject, PptTryAllocatePortAtInterruptLevel, fdx );
} else {
IoAcquireCancelSpinLock(&CancelIrql);
b = PptTryAllocatePortAtInterruptLevel(fdx);
IoReleaseCancelSpinLock(CancelIrql);
}
DD((PCE)fdx,DDT,"PptTryAllocatePort on %x returned %x\n", fdx->PortInfo.Controller, b);
return b;
}
BOOLEAN
PptTraversePortCheckList(
IN PVOID Fdx
)
/*++
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:
Fdx - 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.
--*/
{
PFDO_EXTENSION fdx = Fdx;
PLIST_ENTRY Current;
PISR_LIST_ENTRY CheckEntry;
//
// First check to make sure that the port is still free.
//
if (fdx->WorkQueueCount >= 0) {
return FALSE;
}
for (Current = fdx->IsrList.Flink;
Current != &fdx->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 Fdx
)
/*++
Routine Description:
This routine frees the port.
Arguments:
Fdx - Supplies the device extension.
Return Value:
None.
--*/
{
PFDO_EXTENSION fdx = Fdx;
SYNCHRONIZED_COUNT_CONTEXT SyncContext;
KIRQL CancelIrql;
PLIST_ENTRY Head;
PIRP Irp;
PIO_STACK_LOCATION IrpSp;
ULONG InterruptRefCount;
PPARALLEL_1284_COMMAND Command;
BOOLEAN Allocated;
DD((PCE)fdx,DDT,"PptFreePort\n");
SyncContext.Count = &fdx->WorkQueueCount;
IoAcquireCancelSpinLock( &CancelIrql );
if (fdx->InterruptRefCount) {
KeSynchronizeExecution( fdx->InterruptObject, PptSynchronizedDecrement, &SyncContext );
} else {
PptSynchronizedDecrement( &SyncContext );
}
IoReleaseCancelSpinLock( CancelIrql );
//
// Log the free for WMI
//
fdx->WmiPortAllocFreeCounts.PortFrees++;
//
// Port is free, check for queued ALLOCATE and/or SELECT requests
//
Allocated = FALSE;
while( !Allocated && SyncContext.NewCount >= 0 ) {
//
// We have ALLOCATE and/or SELECT requests queued, satisfy the first request
//
IoAcquireCancelSpinLock(&CancelIrql);
Head = RemoveHeadList(&fdx->WorkQueue);
if( Head == &fdx->WorkQueue ) {
// queue is empty - we're done - exit while loop
IoReleaseCancelSpinLock(CancelIrql);
break;
}
Irp = CONTAINING_RECORD(Head, IRP, Tail.Overlay.ListEntry);
PptSetCancelRoutine(Irp, NULL);
if ( Irp->Cancel ) {
Irp->IoStatus.Status = STATUS_CANCELLED;
// Irp was cancelled so have to get next in line
SyncContext.Count = &fdx->WorkQueueCount;
if (fdx->InterruptRefCount) {
KeSynchronizeExecution(fdx->InterruptObject, PptSynchronizedDecrement, &SyncContext);
} else {
PptSynchronizedDecrement(&SyncContext);
}
IoReleaseCancelSpinLock(CancelIrql);
} else {
Allocated = TRUE;
IoReleaseCancelSpinLock(CancelIrql);
// Finding out what kind of IOCTL it was
IrpSp = IoGetCurrentIrpStackLocation(Irp);
// Check to see if we need to select a
if (IrpSp->Parameters.DeviceIoControl.IoControlCode == IOCTL_INTERNAL_SELECT_DEVICE ) {
// request at head of queue was a SELECT
// so call the select function with the device command saying we already have port
Command = (PPARALLEL_1284_COMMAND)Irp->AssociatedIrp.SystemBuffer;
Command->CommandFlags |= PAR_HAVE_PORT_KEEP_PORT;
// Call Function to try to select device
Irp->IoStatus.Status = PptTrySelectDevice( Fdx, Command );
} else {
// request at head of queue was an ALLOCATE
Irp->IoStatus.Status = STATUS_SUCCESS;
}
// Note that another Allocate request has been granted (WMI counts allocs)
fdx->WmiPortAllocFreeCounts.PortAllocates++;
}
// Remove remove lock on Irp and Complete request whether the Irp
// was cancelled or we acquired the port
PptReleaseRemoveLock(&fdx->RemoveLock, Irp);
P4CompleteRequest( Irp, Irp->IoStatus.Status, Irp->IoStatus.Information );
}
if( !Allocated ) {
//
// ALLOCATE/SELECT request queue was empty
//
IoAcquireCancelSpinLock(&CancelIrql);
InterruptRefCount = fdx->InterruptRefCount;
IoReleaseCancelSpinLock(CancelIrql);
if ( InterruptRefCount ) {
KeSynchronizeExecution( fdx->InterruptObject, PptTraversePortCheckList, fdx );
}
}
return;
}
ULONG
PptQueryNumWaiters(
IN PVOID Fdx
)
/*++
Routine Description:
This routine returns the number of irps queued waiting for
the parallel port.
Arguments:
Fdx - Supplies the device extension.
Return Value:
The number of irps queued waiting for the port.
--*/
{
PFDO_EXTENSION fdx = Fdx;
KIRQL CancelIrql;
SYNCHRONIZED_COUNT_CONTEXT SyncContext;
LONG count;
SyncContext.Count = &fdx->WorkQueueCount;
if (fdx->InterruptRefCount) {
KeSynchronizeExecution(fdx->InterruptObject,
PptSynchronizedRead,
&SyncContext);
} else {
IoAcquireCancelSpinLock(&CancelIrql);
PptSynchronizedRead(&SyncContext);
IoReleaseCancelSpinLock(CancelIrql);
}
count = (SyncContext.NewCount >= 0) ? ((ULONG)SyncContext.NewCount) : 0;
if( fdx->FdoWaitingOnPort ) {
++count;
}
return count;
}
PVOID
PptSetCancelRoutine(PIRP Irp, PDRIVER_CANCEL CancelRoutine)
{
// #pragma warning( push )
// 4054: 'type cast' : from function pointer to data pointer
// 4055: 'type cast' : from data pointer to function pointer
// 4152: nonstandard extension, function/data pointer conversion in expression
#pragma warning( disable : 4054 4055 4152 )
return IoSetCancelRoutine(Irp, CancelRoutine);
// #pragma warning( pop )
}
// this is the version from Win2k parclass
BOOLEAN
CheckPort(
IN PUCHAR wPortAddr,
IN UCHAR bMask,
IN UCHAR bValue,
IN USHORT msTimeDelay
)
/*++
Routine Description:
This routine will loop for a given time period (actual time is
passed in as an arguement) and wait for the dsr to match
predetermined value (dsr value is passed in).
Arguments:
wPortAddr - Supplies the base address of the parallel port + some offset.
This will have us point directly to the dsr (controller + 1).
bMask - Mask used to determine which bits we are looking at
bValue - Value we are looking for.
msTimeDelay - Max time to wait for peripheral response (in ms)
Return Value:
TRUE if a dsr match was found.
FALSE if the time period expired before a match was found.
--*/
{
UCHAR dsr;
LARGE_INTEGER Wait;
LARGE_INTEGER Start;
LARGE_INTEGER End;
// Do a quick check in case we have one stinkingly fast peripheral!
dsr = P5ReadPortUchar(wPortAddr);
if ((dsr & bMask) == bValue)
return TRUE;
Wait.QuadPart = (msTimeDelay * 10 * 1000) + KeQueryTimeIncrement();
KeQueryTickCount(&Start);
CheckPort_Start:
KeQueryTickCount(&End);
dsr = P5ReadPortUchar(wPortAddr);
if ((dsr & bMask) == bValue)
return TRUE;
if ((End.QuadPart - Start.QuadPart) * KeQueryTimeIncrement() > Wait.QuadPart)
{
// We timed out!!!
// do one last check
dsr = P5ReadPortUchar(wPortAddr);
if ((dsr & bMask) == bValue)
return TRUE;
#if DVRH_BUS_RESET_ON_ERROR
BusReset(wPortAddr+1); // Pass in the dcr address
#endif
#if DBG
DD(NULL,DDW,"CheckPort: Timeout\n");
{
int i;
for (i = 3; i < 8; i++) {
if ((bMask >> i) & 1) {
if (((bValue >> i) & 1) != ((dsr >> i) & 1)) {
DD(NULL,DDW,"Bit %d is %d and should be %d!!!\n", i, (dsr >> i) & 1, (bValue >> i) & 1);
}
}
}
}
#endif
goto CheckPort_TimeOut;
}
goto CheckPort_Start;
CheckPort_TimeOut:
return FALSE;
}
NTSTATUS
PptBuildParallelPortDeviceName(
IN ULONG Number,
OUT PUNICODE_STRING DeviceName
)
/*++
Routine Description:
Build a Device Name of the form: \Device\ParallelPortN
*** On success this function returns allocated memory that must be freed by the caller
Arguments:
DriverObject - ParPort driver object
PhysicalDeviceObject - PDO whose stack the ParPort FDO will attach to
DeviceObject - ParPort FDO
UniNameString - the DeviceName (e.g., \Device\ParallelPortN)
PortName - the "LPTx" PortName from the devnode
PortNumber - the "N" in \Device\ParallelPortN
Return Value:
STATUS_SUCCESS on success
error status otherwise
--*/
{
UNICODE_STRING uniDeviceString;
UNICODE_STRING uniBaseNameString;
UNICODE_STRING uniPortNumberString;
WCHAR wcPortNum[10];
NTSTATUS status;
//
// Init strings
//
RtlInitUnicodeString( DeviceName, NULL );
RtlInitUnicodeString( &uniDeviceString, (PWSTR)L"\\Device\\" );
RtlInitUnicodeString( &uniBaseNameString, (PWSTR)DD_PARALLEL_PORT_BASE_NAME_U );
//
// Convert Port Number to UNICODE_STRING
//
uniPortNumberString.Length = 0;
uniPortNumberString.MaximumLength = sizeof( wcPortNum );
uniPortNumberString.Buffer = wcPortNum;
status = RtlIntegerToUnicodeString( Number, 10, &uniPortNumberString);
if( !NT_SUCCESS( status ) ) {
return status;
}
//
// Compute size required and alloc a buffer
//
DeviceName->MaximumLength = (USHORT)( uniDeviceString.Length +
uniBaseNameString.Length +
uniPortNumberString.Length +
sizeof(UNICODE_NULL) );
DeviceName->Buffer = ExAllocatePool( PagedPool | POOL_COLD_ALLOCATION, DeviceName->MaximumLength );
if( NULL == DeviceName->Buffer ) {
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlZeroMemory( DeviceName->Buffer, DeviceName->MaximumLength );
//
// Catenate the parts to construct the DeviceName
//
RtlAppendUnicodeStringToString(DeviceName, &uniDeviceString);
RtlAppendUnicodeStringToString(DeviceName, &uniBaseNameString);
RtlAppendUnicodeStringToString(DeviceName, &uniPortNumberString);
return STATUS_SUCCESS;
}
NTSTATUS
PptInitializeDeviceExtension(
IN PDRIVER_OBJECT DriverObject,
IN PDEVICE_OBJECT PhysicalDeviceObject,
IN PDEVICE_OBJECT DeviceObject,
IN PUNICODE_STRING UniNameString,
IN PWSTR PortName,
IN ULONG PortNumber
)
/*++
Routine Description:
Initialize a ParPort FDO DeviceExtension
Arguments:
DriverObject - ParPort driver object
PhysicalDeviceObject - PDO whose stack the ParPort FDO will attach to
DeviceObject - ParPort FDO
UniNameString - the DeviceName (e.g., \Device\ParallelPortN)
PortName - the "LPTx" PortName from the devnode
PortNumber - the "N" in \Device\ParallelPortN
Return Value:
STATUS_SUCCESS on success
error status otherwise
--*/
{
PFDO_EXTENSION Fdx = DeviceObject->DeviceExtension;
RtlZeroMemory( Fdx, sizeof(FDO_EXTENSION) );
//
// Signature1 helps confirm that we are looking at a Parport DeviceExtension
//
Fdx->Signature1 = PARPORT_TAG;
Fdx->Signature2 = PARPORT_TAG;
//
// Standard Info
//
Fdx->DriverObject = DriverObject;
Fdx->PhysicalDeviceObject = PhysicalDeviceObject;
Fdx->DeviceObject = DeviceObject;
Fdx->PnpInfo.PortNumber = PortNumber; // this is the "N" in \Device\ParallelPortN
//
// We are an FDO
//
Fdx->DevType = DevTypeFdo;
//
// Mutual Exclusion initialization
//
IoInitializeRemoveLock(&Fdx->RemoveLock, PARPORT_TAG, 1, 10);
ExInitializeFastMutex(&Fdx->OpenCloseMutex);
ExInitializeFastMutex(&Fdx->ExtensionFastMutex);
//
// chipset detection initialization - redundant, but safer
//
Fdx->NationalChipFound = FALSE;
Fdx->NationalChecked = FALSE;
//
// List Head for List of PDOs to delete during driver unload, if not before
//
InitializeListHead(&Fdx->DevDeletionListHead);
//
// Initialize 'WorkQueue' - a Queue for Allocate and Select requests
//
InitializeListHead(&Fdx->WorkQueue);
Fdx->WorkQueueCount = -1;
//
// Initialize Exports - Exported via Internal IOCTLs
//
Fdx->PortInfo.FreePort = PptFreePort;
Fdx->PortInfo.TryAllocatePort = PptTryAllocatePort;
Fdx->PortInfo.QueryNumWaiters = PptQueryNumWaiters;
Fdx->PortInfo.Context = Fdx;
Fdx->PnpInfo.HardwareCapabilities = PPT_NO_HARDWARE_PRESENT;
Fdx->PnpInfo.TrySetChipMode = PptSetChipMode;
Fdx->PnpInfo.ClearChipMode = PptClearChipMode;
Fdx->PnpInfo.TrySelectDevice = PptTrySelectDevice;
Fdx->PnpInfo.DeselectDevice = PptDeselectDevice;
Fdx->PnpInfo.Context = Fdx;
Fdx->PnpInfo.PortName = PortName;
//
// Save location info in common extension
//
{
ULONG bufLen = sizeof("LPTxF");
PCHAR buffer = ExAllocatePool( NonPagedPool, bufLen );
if( buffer ) {
RtlZeroMemory( buffer, bufLen );
_snprintf( buffer, bufLen, "%.4SF", PortName );
Fdx->Location = buffer;
}
}
//
// Empty list of interrupt service routines, interrupt NOT connected
//
InitializeListHead( &Fdx->IsrList );
Fdx->InterruptObject = NULL;
Fdx->InterruptRefCount = 0;
//
// Initialize the free port DPC.
//
KeInitializeDpc( &Fdx->FreePortDpc, PptFreePortDpc, Fdx );
//
// Save Device Name in our extension
//
{
ULONG bufferLength = UniNameString->MaximumLength + sizeof(UNICODE_NULL);
Fdx->DeviceName.Buffer = ExAllocatePool(NonPagedPool, bufferLength);
if( !Fdx->DeviceName.Buffer ) {
return STATUS_INSUFFICIENT_RESOURCES;
}
RtlZeroMemory( Fdx->DeviceName.Buffer, bufferLength );
Fdx->DeviceName.Length = 0;
Fdx->DeviceName.MaximumLength = UniNameString->MaximumLength;
RtlCopyUnicodeString( &Fdx->DeviceName, UniNameString );
}
//
// Port is in default mode and mode has not been set
// by a lower filter driver
//
Fdx->PnpInfo.CurrentMode = INITIAL_MODE;
Fdx->FilterMode = FALSE;
return STATUS_SUCCESS;
}
NTSTATUS
PptGetPortNumberFromLptName(
IN PWSTR PortName,
OUT PULONG PortNumber
)
/*++
Routine Description:
Verify that the LptName is of the form LPTn, if so then return
the integer value of n
Arguments:
PortName - the PortName extracted from the devnode - expected to be
of the form: "LPTn"
PortNumber - points to the UNLONG that will hold the result on success
Return Value:
STATUS_SUCCESS on success - *PortNumber will contain the integer value of n
error status otherwise
--*/
{
NTSTATUS status;
UNICODE_STRING str;
//
// Verify that the PortName looks like LPTx where x is a number
//
if( PortName[0] != L'L' || PortName[1] != L'P' || PortName[2] != L'T' ) {
DD(NULL,DDE,"PptGetPortNumberFromLptName - name prefix doesn't look like LPT\n");
return STATUS_UNSUCCESSFUL;
}
//
// prefix is LPT, check for integer suffix with value > 0
//
RtlInitUnicodeString( &str, (PWSTR)&PortName[3] );
status = RtlUnicodeStringToInteger( &str, 10, PortNumber );
if( !NT_SUCCESS( status ) ) {
DD(NULL,DDT,"util::PptGetPortNumberFromLptName - name suffix doesn't look like an integer\n");
return STATUS_UNSUCCESSFUL;
}
if( *PortNumber == 0 ) {
DD(NULL,DDT,"util::PptGetPortNumberFromLptName - name suffix == 0 - FAIL - Invalid value\n");
return STATUS_UNSUCCESSFUL;
}
DD(NULL,DDT,"util::PptGetPortNumberFromLptName - LPT name suffix= %d\n", *PortNumber);
return STATUS_SUCCESS;
}
PDEVICE_OBJECT
PptBuildFdo(
IN PDRIVER_OBJECT DriverObject,
IN PDEVICE_OBJECT PhysicalDeviceObject
)
/*++
Routine Description:
This routine constructs and initializes a parport FDO
Arguments:
DriverObject - Pointer to the parport driver object
PhysicalDeviceObject - Pointer to the PDO whose stack we will attach to
Return Value:
Pointer to the new ParPort Device Object on Success
NULL otherwise
--*/
{
UNICODE_STRING uniNameString = {0,0,0};
ULONG portNumber = 0;
PWSTR portName = NULL;
NTSTATUS status = STATUS_SUCCESS;
PDEVICE_OBJECT deviceObject = NULL;
//
// Get the LPTx name for this port from the registry.
//
// The initial LPTx name for a port is determined by the ports class installer
// msports.dll, but the name can subsequently be changed by the user via
// a device manager property page.
//
portName = PptGetPortNameFromPhysicalDeviceObject( PhysicalDeviceObject );
if( NULL == portName ) {
DD(NULL,DDE,"PptBuildFdo - get LPTx Name from registry - FAILED\n");
goto targetExit;
}
//
// Extract the preferred port number N to use for the \Device\ParallelPortN
// DeviceName from the LPTx name
//
// Preferred DeviceName for LPT(n) is ParallelPort(n-1) - e.g., LPT3 -> ParallelPort2
//
status = PptGetPortNumberFromLptName( portName, &portNumber );
if( !NT_SUCCESS( status ) ) {
DD(NULL,DDE,"PptBuildFdo - extract portNumber from LPTx Name - FAILED\n");
ExFreePool( portName );
goto targetExit;
}
--portNumber; // convert 1 (LPT) based number to 0 (ParallelPort) based number
//
// Build a DeviceName of the form: \Device\ParallelPortN
//
status = PptBuildParallelPortDeviceName(portNumber, &uniNameString);
if( !NT_SUCCESS( status ) ) {
// we couldn't make up a name - bail out
DD(NULL,DDE,"PptBuildFdo - Build ParallelPort DeviceName - FAILED\n");
ExFreePool( portName );
goto targetExit;
}
//
// Create the device object for this device.
//
status = IoCreateDevice(DriverObject, sizeof(FDO_EXTENSION), &uniNameString,
FILE_DEVICE_PARALLEL_PORT, FILE_DEVICE_SECURE_OPEN, FALSE, &deviceObject);
if( STATUS_OBJECT_NAME_COLLISION == status ) {
//
// Preferred DeviceName already exists - try made up names
//
DD(NULL,DDW,"PptBuildFdo - Initial Device Creation FAILED - Name Collision\n");
//
// use an offset so that our made up names won't collide with
// the preferred names of ports that have not yet started
// (start with ParallelPort8)
//
#define PPT_CLASSNAME_OFFSET 7
portNumber = PPT_CLASSNAME_OFFSET;
do {
RtlFreeUnicodeString( &uniNameString );
++portNumber;
status = PptBuildParallelPortDeviceName(portNumber, &uniNameString);
if( !NT_SUCCESS( status ) ) {
// we couldn't make up a name - bail out
DD(NULL,DDE,"PptBuildFdo - Build ParallelPort DeviceName - FAILED\n");
ExFreePool( portName );
goto targetExit;
}
DD(NULL,DDT,"PptBuildFdo - Trying Device Creation <%wZ>\n", &uniNameString);
status = IoCreateDevice(DriverObject, sizeof(FDO_EXTENSION), &uniNameString,
FILE_DEVICE_PARALLEL_PORT, FILE_DEVICE_SECURE_OPEN, FALSE, &deviceObject);
} while( STATUS_OBJECT_NAME_COLLISION == status );
}
if( !NT_SUCCESS( status ) ) {
// we got a failure other than a name collision - bail out
DD(NULL,DDE,"PptBuildFdo - Device Creation FAILED - status=%x\n",status);
deviceObject = NULL;
ExFreePool( portName );
goto targetExit;
}
//
// We have a deviceObject - Initialize DeviceExtension
//
status = PptInitializeDeviceExtension( DriverObject, PhysicalDeviceObject, deviceObject,
&uniNameString, portName, portNumber );
if( !NT_SUCCESS( status ) ) {
// failure initializing the device extension - clean up and bail out
DD(NULL,DDE,"PptBuildFdo - Device Initialization FAILED - status=%x\n",status);
IoDeleteDevice( deviceObject );
deviceObject = NULL;
ExFreePool( portName );
goto targetExit;
}
//
// Propagate the power pagable flag of the PDO to our new FDO
//
if( PhysicalDeviceObject->Flags & DO_POWER_PAGABLE ) {
deviceObject->Flags |= DO_POWER_PAGABLE;
}
DD(NULL,DDT,"PptBuildFdo - SUCCESS\n");
targetExit:
RtlFreeUnicodeString( &uniNameString );
return deviceObject;
}
VOID
PptPdoGetPortInfoFromFdo( PDEVICE_OBJECT Pdo )
{
PPDO_EXTENSION pdx = Pdo->DeviceExtension;
PDEVICE_OBJECT fdo = pdx->Fdo;
PFDO_EXTENSION fdx = fdo->DeviceExtension;
pdx->OriginalController = fdx->PortInfo.OriginalController;
pdx->Controller = fdx->PortInfo.Controller;
pdx->SpanOfController = fdx->PortInfo.SpanOfController;
pdx->TryAllocatePort = fdx->PortInfo.TryAllocatePort;
pdx->FreePort = fdx->PortInfo.FreePort;
pdx->QueryNumWaiters = fdx->PortInfo.QueryNumWaiters;
pdx->PortContext = fdx->PortInfo.Context;
pdx->EcrController = fdx->PnpInfo.EcpController;
pdx->HardwareCapabilities = fdx->PnpInfo.HardwareCapabilities;
pdx->TrySetChipMode = fdx->PnpInfo.TrySetChipMode;
pdx->ClearChipMode = fdx->PnpInfo.ClearChipMode;
pdx->TrySelectDevice = fdx->PnpInfo.TrySelectDevice;
pdx->DeselectDevice = fdx->PnpInfo.DeselectDevice;
pdx->FifoDepth = fdx->PnpInfo.FifoDepth;
pdx->FifoWidth = fdx->PnpInfo.FifoWidth;
}
VOID
P4WritePortNameToDevNode( PDEVICE_OBJECT Pdo, PCHAR Location )
{
HANDLE handle;
NTSTATUS status;
WCHAR portName[8]; // expect: L"LPTx:" (L"LPTx.y:" for DaisyChain PDOs)
PPDO_EXTENSION pdx = Pdo->DeviceExtension;
RtlZeroMemory( portName, sizeof(portName) );
PptAssert( NULL != Location );
switch( pdx->PdoType ) {
case PdoTypeLegacyZip:
case PdoTypeDaisyChain:
// At least one vendor uses the y from LPTx.y to determine the
// location of their device in the 1284.3 daisy chain. We
// have chastised this vendor for using this undocumented
// interface and they have apologized profusely and promised
// to try to avoid using undocumented interfaces in the future
// (at least without telling us that they are doing so).
_snwprintf( portName, sizeof(portName)/sizeof(WCHAR), L"%.6S:\0", Location );
PptAssert( 7 == wcslen(portName) );
break;
case PdoTypeRawPort:
case PdoTypeEndOfChain:
// don't confuse printing with the .4 suffix for an EndOfChain device
_snwprintf( portName, sizeof(portName)/sizeof(WCHAR), L"%.4S:\0", Location );
PptAssert( 5 == wcslen(portName) );
break;
default:
DD((PCE)pdx,DDE,"P4WritePortNameToDevNode - invalid pdx->PdoType\n");
}
PptAssert( wcsncmp( portName, L"LPT", sizeof(L"LPT")/sizeof(WCHAR)) ) ;
status = IoOpenDeviceRegistryKey( Pdo, PLUGPLAY_REGKEY_DEVICE, KEY_ALL_ACCESS, &handle );
if( STATUS_SUCCESS == status ) {
UNICODE_STRING name;
RtlInitUnicodeString( &name, L"PortName" );
ZwSetValueKey( handle, &name, 0, REG_SZ, portName, (wcslen(portName)+1)*sizeof(WCHAR) );
ZwClose(handle);
}
}
PCHAR
P4ReadRawIeee1284DeviceId(
IN PUCHAR Controller
)
{
IEEE_STATE ieeeState = { 0, // CurrentEvent
PHASE_FORWARD_IDLE, // CurrentPhase
FALSE, // Connected in IEEE mode?
FALSE, // IsIeeeTerminateOk
FAMILY_NONE }; // ProtocolFamily - Centronics => FAMILY_NONE
NTSTATUS status;
PCHAR devIdBuffer = NULL;
ULONG bytesTransferred = 0;
ULONG tryCount = 1;
const ULONG maxTries = 3;
const ULONG minValidDevId = 14; // 2 size bytes + "MFG:x;" + "MDL:y;"
BOOLEAN ignoreXflag = FALSE;
ULONG deviceIndex;
targetRetry:
status = P4IeeeEnter1284Mode( Controller, ( NIBBLE_EXTENSIBILITY | DEVICE_ID_REQ ), &ieeeState );
if( STATUS_SUCCESS == status ) {
// Negotiation for 1284 device ID succeeded
const ULONG tmpBufLen = 1024; // reasonable max length for IEEE 1284 Device ID string
PCHAR tmpBuf = ExAllocatePool( PagedPool, tmpBufLen );
if( tmpBuf ) {
RtlZeroMemory( tmpBuf, tmpBufLen );
// try to read the 1284 device ID from the peripheral
ieeeState.CurrentPhase = PHASE_NEGOTIATION;
status = P4NibbleModeRead( Controller, tmpBuf, tmpBufLen-1, &bytesTransferred, &ieeeState );
if( NT_SUCCESS( status ) ) {
UCHAR highLengthByte = 0xff & tmpBuf[0];
UCHAR lowLengthByte = 0xff & tmpBuf[1];
PCHAR idString = &tmpBuf[2];
DD(NULL,DDT,"P4ReadRawIeee1284DeviceId - len:%02x %02x - string:<%s>\n",highLengthByte,lowLengthByte,idString);
if( highLengthByte > 2 ) {
DD(NULL,DDT,"P4ReadRawIeee1284DeviceId - len:%02x %02x - looks bogus - ignore this ID\n",highLengthByte,lowLengthByte);
devIdBuffer = NULL;
} else {
if( bytesTransferred >= minValidDevId ) {
// looks like this might be a valid 1284 id
devIdBuffer = ExAllocatePool( PagedPool, bytesTransferred + 1 );
if( devIdBuffer ) {
ULONG length = (highLengthByte * 256) + lowLengthByte;
ULONG truncationIndex = ( (length >= minValidDevId) && (length < bytesTransferred) ) ? length : bytesTransferred;
RtlCopyMemory( devIdBuffer, tmpBuf, bytesTransferred );
devIdBuffer[ truncationIndex ] = '\0';
} else {
DD(NULL,DDT,"P4ReadRawIeee1284DeviceId - P4IeeeEnter1284Mode FAILED - no pool for devIdBuffer\n");
}
}
}
} else {
DD(NULL,DDT,"P4ReadRawIeee1284DeviceId - P4NibbleModeRead FAILED - looks like no device there\n");
}
ExFreePool( tmpBuf );
} else {
DD(NULL,DDT,"P4ReadRawIeee1284DeviceId - P4IeeeEnter1284Mode FAILED - no pool for tmpBuf\n");
}
ieeeState.ProtocolFamily = FAMILY_REVERSE_NIBBLE;
//check brother product
if(devIdBuffer &&
( strstr(devIdBuffer+2,"Brother") ||
strstr(devIdBuffer+2,"PitneyBowes") ||
strstr(devIdBuffer+2,"LEGEND") ||
strstr(devIdBuffer+2,"Legend") ||
strstr(devIdBuffer+2,"HBP") ))
{
// look for device that needs to ignore XFlag on event 24
for(deviceIndex = 0; deviceIndex < NUMOFBROTHERPRODUCT;
deviceIndex++)
{
if(XflagOnEvent24Devices[deviceIndex][0] == NULL)
{
break;
}
if(strstr(devIdBuffer+2,
XflagOnEvent24Devices[deviceIndex][IDMFG] ) )
{
if(strstr(devIdBuffer+2,
XflagOnEvent24Devices[deviceIndex][IDMDL] ) )
{
// found a match, so set our flag and get out
ignoreXflag = TRUE;
break;
}
}
}
}
if(ignoreXflag)
{
// work around Brother's firmware handling of XFlag on Event 24
P4IeeeTerminate1284Mode( Controller, &ieeeState, IgnoreXFlagOnEvent24 );
} else {
// normal handling
P4IeeeTerminate1284Mode( Controller, &ieeeState, UseXFlagOnEvent24 );
}
} else {
DD(NULL,DDT,"P4ReadRawIeee1284DeviceId - P4IeeeEnter1284Mode FAILED - looks like no device there\n");
}
//
// add retry if we got some bytes, but not enough for a valid ID
//
if( (NULL == devIdBuffer) && // we didn't get an ID
(bytesTransferred > 0 ) && // peripheral reported some bytes
(bytesTransferred < minValidDevId ) && // but not enough
(tryCount < maxTries ) ) { // we haven't exhausted our retries
++tryCount;
bytesTransferred = 0;
goto targetRetry;
}
return devIdBuffer;
}
VOID
P4ReleaseBus( PDEVICE_OBJECT Fdo )
{
PFDO_EXTENSION fdx = Fdo->DeviceExtension;
DD((PCE)fdx,DDT,"P4ReleaseBus\n");
fdx->FdoWaitingOnPort = FALSE;
if( 0 == d3 ) {
PptFreePort( fdx );
}
}
NTSTATUS
P4CompleteRequest(
IN PIRP Irp,
IN NTSTATUS Status,
IN ULONG_PTR Information
)
{
P5TraceIrpCompletion( Irp );
Irp->IoStatus.Status = Status;
Irp->IoStatus.Information = Information;
IoCompleteRequest( Irp, IO_NO_INCREMENT );
return Status;
}
NTSTATUS
P4CompleteRequestReleaseRemLock(
IN PIRP Irp,
IN NTSTATUS Status,
IN ULONG_PTR Information,
IN PIO_REMOVE_LOCK RemLock
)
{
P4CompleteRequest( Irp, Status, Information );
PptReleaseRemoveLock( RemLock, Irp );
return Status;
}
// pcutil.c follows:
//============================================================================
// NAME: BusReset()
//
// Performs a bus reset as defined in Chapter 7.2 of the
// 1284-1994 spec.
//
// PARAMETERS:
// DCRController - Supplies the base address of of the DCR.
//
// RETURNS:
// nothing
//============================================================================
void BusReset(
IN PUCHAR DCRController
)
{
UCHAR dcr;
dcr = P5ReadPortUchar(DCRController);
// Set 1284 and nInit low.
dcr = UPDATE_DCR(dcr, DONT_CARE, DONT_CARE, INACTIVE, INACTIVE, DONT_CARE, DONT_CARE);
P5WritePortUchar(DCRController, dcr);
KeStallExecutionProcessor(100); // Legacy Zip will hold what looks to be
// a bus reset for 9us. Since this proc is used
// to trigger a logic analyzer... let's hold
// for 100us
}
BOOLEAN
CheckTwoPorts(
PUCHAR pPortAddr1,
UCHAR bMask1,
UCHAR bValue1,
PUCHAR pPortAddr2,
UCHAR bMask2,
UCHAR bValue2,
USHORT msTimeDelay
)
{
UCHAR bPort1;
UCHAR bPort2;
LARGE_INTEGER Wait;
LARGE_INTEGER Start;
LARGE_INTEGER End;
// Do a quick check in case we have one stinkingly fast peripheral!
bPort1 = P5ReadPortUchar( pPortAddr1 );
if ( ( bPort1 & bMask1 ) == bValue1 ) {
return TRUE;
}
bPort2 = P5ReadPortUchar( pPortAddr2 );
if ( ( bPort2 & bMask2 ) == bValue2 ) {
return FALSE;
}
Wait.QuadPart = (msTimeDelay * 10 * 1000) + KeQueryTimeIncrement();
KeQueryTickCount(&Start);
for(;;) {
KeQueryTickCount(&End);
bPort1 = P5ReadPortUchar( pPortAddr1 );
if ( ( bPort1 & bMask1 ) == bValue1 ) {
return TRUE;
}
bPort2 = P5ReadPortUchar( pPortAddr2 );
if ( ( bPort2 & bMask2 ) == bValue2 ) {
return FALSE;
}
if ((End.QuadPart - Start.QuadPart) * KeQueryTimeIncrement() > Wait.QuadPart) {
// We timed out!!! - Recheck the values
bPort1 = P5ReadPortUchar( pPortAddr1 );
if ( ( bPort1 & bMask1 ) == bValue1 ) {
return TRUE;
}
bPort2 = P5ReadPortUchar( pPortAddr2 );
if ( ( bPort2 & bMask2 ) == bValue2 ) {
return FALSE;
}
#if DVRH_BUS_RESET_ON_ERROR
BusReset(pPortAddr1+1); // Pass in the dcr address
#endif
// Device never responded, return timeout status.
return FALSE;
}
} // forever;
} // CheckPort2...
PWSTR
ParCreateWideStringFromUnicodeString(PUNICODE_STRING UnicodeString)
/*++
Routine Description:
Create a UNICODE_NULL terminated WSTR given a UNICODE_STRING.
This function allocates PagedPool, copies the UNICODE_STRING buffer
to the allocation, and appends a UNICODE_NULL to terminate the WSTR
*** This function allocates pool. ExFreePool must be called to free
the allocation when the buffer is no longer needed.
Arguments:
UnicodeString - The source
Return Value:
PWSTR - if successful
NULL - otherwise
--*/
{
PWSTR buffer;
ULONG length = UnicodeString->Length;
buffer = ExAllocatePool( PagedPool, length + sizeof(UNICODE_NULL) );
if(!buffer) {
return NULL; // unable to allocate pool, bail out
} else {
RtlCopyMemory(buffer, UnicodeString->Buffer, length);
buffer[length/2] = UNICODE_NULL;
return buffer;
}
}
VOID
ParInitializeExtension1284Info(
IN PPDO_EXTENSION Pdx
)
// make this a function since it is now called from two places:
// - 1) when initializing a new devobj
// - 2) from CreateOpen
{
USHORT i;
Pdx->Connected = FALSE;
if (DefaultModes)
{
USHORT rev = (USHORT) (DefaultModes & 0xffff);
USHORT fwd = (USHORT)((DefaultModes & 0xffff0000)>>16);
switch (fwd)
{
case BOUNDED_ECP:
Pdx->IdxForwardProtocol = BOUNDED_ECP_FORWARD;
break;
case ECP_HW_NOIRQ:
case ECP_HW_IRQ:
Pdx->IdxForwardProtocol = ECP_HW_FORWARD_NOIRQ;
break;
case ECP_SW:
Pdx->IdxForwardProtocol = ECP_SW_FORWARD;
break;
case EPP_HW:
Pdx->IdxForwardProtocol = EPP_HW_FORWARD;
break;
case EPP_SW:
Pdx->IdxForwardProtocol = EPP_SW_FORWARD;
break;
case IEEE_COMPATIBILITY:
Pdx->IdxForwardProtocol = IEEE_COMPAT_MODE;
break;
case CENTRONICS:
default:
Pdx->IdxForwardProtocol = CENTRONICS_MODE;
break;
}
switch (rev)
{
case BOUNDED_ECP:
Pdx->IdxReverseProtocol = BOUNDED_ECP_REVERSE;
break;
case ECP_HW_NOIRQ:
case ECP_HW_IRQ:
Pdx->IdxReverseProtocol = ECP_HW_REVERSE_NOIRQ;
break;
case ECP_SW:
Pdx->IdxReverseProtocol = ECP_SW_REVERSE;
break;
case EPP_HW:
Pdx->IdxReverseProtocol = EPP_HW_REVERSE;
break;
case EPP_SW:
Pdx->IdxReverseProtocol = EPP_SW_REVERSE;
break;
case BYTE_BIDIR:
Pdx->IdxReverseProtocol = BYTE_MODE;
break;
case CHANNEL_NIBBLE:
case NIBBLE:
default:
Pdx->IdxReverseProtocol = NIBBLE_MODE;
break;
}
}
else
{
Pdx->IdxReverseProtocol = NIBBLE_MODE;
Pdx->IdxForwardProtocol = CENTRONICS_MODE;
}
Pdx->bShadowBuffer = FALSE;
Pdx->ProtocolModesSupported = 0;
Pdx->BadProtocolModes = 0;
Pdx->fnRead = NULL;
Pdx->fnWrite = NULL;
Pdx->ForwardInterfaceAddress = DEFAULT_ECP_CHANNEL;
Pdx->ReverseInterfaceAddress = DEFAULT_ECP_CHANNEL;
Pdx->SetForwardAddress = FALSE;
Pdx->SetReverseAddress = FALSE;
Pdx->bIsHostRecoverSupported = FALSE;
Pdx->IsIeeeTerminateOk = FALSE;
for (i = FAMILY_NONE; i < FAMILY_MAX; i++) {
Pdx->ProtocolData[i] = 0;
}
}
NTSTATUS
ParBuildSendInternalIoctl(
IN ULONG IoControlCode,
IN PDEVICE_OBJECT TargetDeviceObject,
IN PVOID InputBuffer OPTIONAL,
IN ULONG InputBufferLength,
OUT PVOID OutputBuffer OPTIONAL,
IN ULONG OutputBufferLength,
IN PLARGE_INTEGER RequestedTimeout OPTIONAL
)
/*++dvdf
Routine Description:
This routine builds and sends an Internal IOCTL to the TargetDeviceObject, waits
for the IOCTL to complete, and returns status to the caller.
*** WORKWORK - dvdf 12Dec98: This function does not support Input and Output in the same IOCTL
Arguments:
IoControlCode - the IOCTL to send
TargetDeviceObject - who to send the IOCTL to
InputBuffer - pointer to input buffer, if any
InputBufferLength, - length of input buffer
OutputBuffer - pointer to output buffer, if any
OutputBufferLength, - length of output buffer
Timeout - how long to wait for request to complete, NULL==use driver global AcquirePortTimeout
Return Value:
Status
--*/
{
NTSTATUS status;
PIRP irp;
LARGE_INTEGER timeout;
KEVENT event;
PIO_STACK_LOCATION irpSp;
PAGED_CODE();
//
// Current limitation is that this function does not handle a request with
// both InputBufferLength and OutputBufferLength > 0
//
if( InputBufferLength != 0 && OutputBufferLength != 0 ) {
return STATUS_UNSUCCESSFUL;
}
//
// Allocate and initialize IRP
//
irp = IoAllocateIrp( (CCHAR)(TargetDeviceObject->StackSize + 1), FALSE );
if( !irp ) {
return STATUS_INSUFFICIENT_RESOURCES;
}
irpSp = IoGetNextIrpStackLocation( irp );
irpSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
irpSp->Parameters.DeviceIoControl.OutputBufferLength = OutputBufferLength;
irpSp->Parameters.DeviceIoControl.InputBufferLength = InputBufferLength;
irpSp->Parameters.DeviceIoControl.IoControlCode = IoControlCode;
if( InputBufferLength != 0 ) {
irp->AssociatedIrp.SystemBuffer = InputBuffer;
} else if( OutputBufferLength != 0 ) {
irp->AssociatedIrp.SystemBuffer = OutputBuffer;
}
//
// Set completion routine and send IRP
//
KeInitializeEvent( &event, NotificationEvent, FALSE );
IoSetCompletionRoutine( irp, ParSynchCompletionRoutine, &event, TRUE, TRUE, TRUE );
status = ParCallDriver(TargetDeviceObject, irp);
if( !NT_SUCCESS(status) ) {
DD(NULL,DDE,"ParBuildSendInternalIoctl - ParCallDriver FAILED w/status=%x\n",status);
IoFreeIrp( irp );
return status;
}
//
// Set timeout and wait
//
// user specified : default
timeout = (NULL != RequestedTimeout) ? *RequestedTimeout : AcquirePortTimeout;
status = KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, &timeout);
//
// Did we timeout or did the IRP complete?
//
if( status == STATUS_TIMEOUT ) {
// we timed out - cancel the IRP
IoCancelIrp( irp );
KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
}
//
// Irp is complete, grab the status and free the irp
//
status = irp->IoStatus.Status;
IoFreeIrp( irp );
return status;
}
UCHAR
ParInitializeDevice(
IN PPDO_EXTENSION Pdx
)
/*++
Routine Description:
This routine is invoked to initialize the parallel port drive.
It performs the following actions:
o Send INIT to the driver and if the device is online.
Arguments:
Context - Really the device extension.
Return Value:
The last value that we got from the status register.
--*/
{
KIRQL OldIrql;
UCHAR DeviceStatus = 0;
LARGE_INTEGER StartOfSpin = {0,0};
LARGE_INTEGER NextQuery = {0,0};
LARGE_INTEGER Difference = {0,0};
//
// Tim Wells (WestTek, L.L.C.)
//
// - Removed the deferred initialization code from DriverEntry, device creation
// code. This code will be better utilized in the Create/Open logic or from
// the calling application.
//
// - Changed this code to always reset when asked, and to return after a fixed
// interval reqardless of the response. Additional responses can be provided by
// read and write code.
//
//
// Clear the register.
//
if (GetControl(Pdx->Controller) & PAR_CONTROL_NOT_INIT) {
//
// We should stall for at least 60 microseconds after the init.
//
KeRaiseIrql( DISPATCH_LEVEL, &OldIrql );
StoreControl( Pdx->Controller, (UCHAR)(PAR_CONTROL_WR_CONTROL | PAR_CONTROL_SLIN) );
KeStallExecutionProcessor(60);
KeLowerIrql(OldIrql);
}
StoreControl( Pdx->Controller,
(UCHAR)(PAR_CONTROL_WR_CONTROL | PAR_CONTROL_NOT_INIT | PAR_CONTROL_SLIN) );
//
// Spin waiting for the device to initialize.
//
KeQueryTickCount(&StartOfSpin);
do {
KeQueryTickCount(&NextQuery);
Difference.QuadPart = NextQuery.QuadPart - StartOfSpin.QuadPart;
ASSERT(KeQueryTimeIncrement() <= MAXLONG);
if (Difference.QuadPart*KeQueryTimeIncrement() >= Pdx->AbsoluteOneSecond.QuadPart) {
//
// Give up on getting PAR_OK.
//
DD((PCE)Pdx,DDT,"Did spin of one second - StartOfSpin: %x NextQuery: %x\n", StartOfSpin.LowPart,NextQuery.LowPart);
break;
}
DeviceStatus = GetStatus(Pdx->Controller);
} while (!PAR_OK(DeviceStatus));
return (DeviceStatus);
}
VOID
ParNotInitError(
IN PPDO_EXTENSION Pdx,
IN UCHAR DeviceStatus
)
/*++
Routine Description:
Arguments:
Pdx - Supplies the device extension.
deviceStatus - Last read status.
Return Value:
None.
--*/
{
PIRP Irp = Pdx->CurrentOpIrp;
if (PAR_OFF_LINE(DeviceStatus)) {
Irp->IoStatus.Status = STATUS_DEVICE_OFF_LINE;
DD((PCE)Pdx,DDE,"Init Error - off line - STATUS/INFORMATON: %x/%x\n", Irp->IoStatus.Status, Irp->IoStatus.Information);
} else if (PAR_NO_CABLE(DeviceStatus)) {
Irp->IoStatus.Status = STATUS_DEVICE_NOT_CONNECTED;
DD((PCE)Pdx,DDE,"Init Error - no cable - not connected - STATUS/INFORMATON: %x/%x\n", Irp->IoStatus.Status, Irp->IoStatus.Information);
} else if (PAR_PAPER_EMPTY(DeviceStatus)) {
Irp->IoStatus.Status = STATUS_DEVICE_PAPER_EMPTY;
DD((PCE)Pdx,DDE,"Init Error - paper empty - STATUS/INFORMATON: %x/%x\n", Irp->IoStatus.Status, Irp->IoStatus.Information);
} else if (PAR_POWERED_OFF(DeviceStatus)) {
Irp->IoStatus.Status = STATUS_DEVICE_POWERED_OFF;
DD((PCE)Pdx,DDE,"Init Error - power off - STATUS/INFORMATON: %x/%x\n", Irp->IoStatus.Status, Irp->IoStatus.Information);
} else {
Irp->IoStatus.Status = STATUS_DEVICE_NOT_CONNECTED;
DD((PCE)Pdx,DDE,"Init Error - not conn - STATUS/INFORMATON: %x/%x\n", Irp->IoStatus.Status, Irp->IoStatus.Information);
}
}
VOID
ParCancelRequest(
PDEVICE_OBJECT DevObj,
PIRP Irp
)
/*++
Routine Description:
This routine is used to cancel any request in the parallel driver.
Arguments:
DevObj - Pointer to the device object for this device
Irp - Pointer to the IRP to be canceled.
Return Value:
None.
--*/
{
UNREFERENCED_PARAMETER( DevObj );
//
// The only reason that this irp can be on the queue is
// if it's not the current irp. Pull it off the queue
// and complete it as canceled.
//
ASSERT(!IsListEmpty(&Irp->Tail.Overlay.ListEntry));
RemoveEntryList(&Irp->Tail.Overlay.ListEntry);
IoReleaseCancelSpinLock(Irp->CancelIrql);
P4CompleteRequest( Irp, STATUS_CANCELLED, 0 );
}
#if PAR_NO_FAST_CALLS
// temp debug functions so params show up on stack trace
NTSTATUS
ParCallDriver(
IN PDEVICE_OBJECT DeviceObject,
IN OUT PIRP Irp
)
{
return IoCallDriver(DeviceObject, Irp);
}
#endif // PAR_NO_FAST_CALLS
NTSTATUS
ParSynchCompletionRoutine(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PKEVENT Event
)
/*++
Routine Description:
This routine is for use with synchronous IRP processing.
All it does is signal an event, so the driver knows it
can continue.
Arguments:
DriverObject - Pointer to driver object created by system.
Irp - Irp that just completed
Event - Event we'll signal to say Irp is done
Return Value:
None.
--*/
{
UNREFERENCED_PARAMETER( DeviceObject );
UNREFERENCED_PARAMETER( Irp );
KeSetEvent(Event, 0, FALSE);
return STATUS_MORE_PROCESSING_REQUIRED;
}
VOID
ParCheckParameters(
IN OUT PPDO_EXTENSION Pdx
)
/*++
Routine Description:
This routine reads the parameters section of the registry and modifies
the device extension as specified by the parameters.
Arguments:
RegistryPath - Supplies the registry path.
Pdx - Supplies the device extension.
Return Value:
None.
--*/
{
RTL_QUERY_REGISTRY_TABLE ParamTable[4];
ULONG UsePIWriteLoop;
ULONG UseNT35Priority;
ULONG Zero = 0;
NTSTATUS Status;
HANDLE hRegistry;
if (Pdx->PhysicalDeviceObject) {
Status = IoOpenDeviceRegistryKey (Pdx->PhysicalDeviceObject,
PLUGPLAY_REGKEY_DRIVER,
STANDARD_RIGHTS_ALL,
&hRegistry);
if (NT_SUCCESS(Status)) {
RtlZeroMemory(ParamTable, sizeof(ParamTable));
ParamTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
ParamTable[0].Name = (PWSTR)L"UsePIWriteLoop";
ParamTable[0].EntryContext = &UsePIWriteLoop;
ParamTable[0].DefaultType = REG_DWORD;
ParamTable[0].DefaultData = &Zero;
ParamTable[0].DefaultLength = sizeof(ULONG);
ParamTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT;
ParamTable[1].Name = (PWSTR)L"UseNT35Priority";
ParamTable[1].EntryContext = &UseNT35Priority;
ParamTable[1].DefaultType = REG_DWORD;
ParamTable[1].DefaultData = &Zero;
ParamTable[1].DefaultLength = sizeof(ULONG);
ParamTable[2].Flags = RTL_QUERY_REGISTRY_DIRECT;
ParamTable[2].Name = (PWSTR)L"InitializationTimeout";
ParamTable[2].EntryContext = &(Pdx->InitializationTimeout);
ParamTable[2].DefaultType = REG_DWORD;
ParamTable[2].DefaultData = &Zero;
ParamTable[2].DefaultLength = sizeof(ULONG);
Status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE | RTL_REGISTRY_OPTIONAL,
hRegistry, ParamTable, NULL, NULL);
if (NT_SUCCESS(Status)) {
if(UsePIWriteLoop) {
Pdx->UsePIWriteLoop = TRUE;
}
if(UseNT35Priority) {
Pdx->UseNT35Priority = TRUE;
}
if(Pdx->InitializationTimeout == 0) {
Pdx->InitializationTimeout = 15;
}
}
} else {
Pdx->InitializationTimeout = 15;
}
ZwClose (hRegistry);
} else {
Pdx->InitializationTimeout = 15;
}
}
BOOLEAN
String2Num(
IN OUT PCHAR *lpp_Str,
IN CHAR c,
OUT ULONG *num
)
{
int cc;
int cnt = 0;
DD(NULL,DDT,"String2Num. string [%s]\n", lpp_Str);
*num = 0;
if (!*lpp_Str) {
*num = 0;
return FALSE;
}
// At this point, we should have a string that is a
// positive hex value. I will not be checking for
// validity of the string. If peripheral handed me a
// bogus value then I'm gonna make their life
// miserable.
String2Num_Start:
cc = (int)(unsigned char)**lpp_Str;
if (cc >= '0' && cc <= '9') {
*num = 16 * *num + (cc - '0'); /* accumulate digit */
} else if (cc >= 'A' && cc <= 'F') {
*num = 16 * *num + (cc - 55); /* accumulate digit */
} else if (cc >= 'a' && cc <= 'f') {
*num = 16 * *num + (cc - 87); /* accumulate digit */
} else if (cc == c || cc == 0) {
*lpp_Str = 0;
return TRUE;
} else if (cc == 'y' || cc == 'Y') {
*lpp_Str = 0;
*num = (ULONG)~0; /* Special case */
return FALSE;
} else {
*lpp_Str = 0;
*num = 0; /* It's all messed up */
return FALSE;
}
DD(NULL,DDT,"String2Num. num [%x]\n", *num);
(*lpp_Str)++;
if (cnt++ > 100) {
// If our string is this large, then I'm gonna assume somethings wrong
DD(NULL,DDE,"String2Num. String too long\n");
goto String2Num_End;
}
goto String2Num_Start;
String2Num_End:
DD(NULL,DDE,"String2Num. Something's wrong with String\n");
*num = 0;
return FALSE;
}
UCHAR
StringCountValues(
IN PCHAR string,
IN CHAR delimeter
)
{
PUCHAR lpKey = (PUCHAR)string;
UCHAR cnt = 1;
if(!string) {
return 0;
}
while(*lpKey) {
if( *lpKey==delimeter ) {
++cnt;
}
lpKey++;
}
return cnt;
}
PCHAR
StringChr(
IN PCHAR string,
IN CHAR c
)
{
if(!string) {
return(NULL);
}
while(*string) {
if( *string==c ) {
return string;
}
string++;
}
return NULL;
}
VOID
StringSubst(
IN PCHAR lpS,
IN CHAR chTargetChar,
IN CHAR chReplacementChar,
IN USHORT cbS
)
{
USHORT iCnt = 0;
while ((lpS != '\0') && (iCnt++ < cbS))
if (*lpS == chTargetChar)
*lpS++ = chReplacementChar;
else
++lpS;
}
VOID
ParFixupDeviceId(
IN OUT PUCHAR DeviceId
)
/*++
Routine Description:
This routine parses the NULL terminated string and replaces any invalid
characters with an underscore character.
Invalid characters are:
c <= 0x20 (' ')
c > 0x7F
c == 0x2C (',')
Arguments:
DeviceId - specifies a device id string (or part of one), must be
null-terminated.
Return Value:
None.
--*/
{
PUCHAR p;
for( p = DeviceId; *p; ++p ) {
if( (*p <= ' ') || (*p > (UCHAR)0x7F) || (*p == ',') ) {
*p = '_';
}
}
}
VOID
ParDetectDot3DataLink(
IN PPDO_EXTENSION Pdx,
IN PCHAR DeviceId
)
{
PCHAR DOT3DL = NULL; // 1284.3 Data Link Channels
PCHAR DOT3C = NULL; // 1284.3 Data Link Services
PCHAR DOT4DL = NULL; // 1284.4 Data Link for peripherals that were implemented prior to 1284.3
PCHAR CMDField = NULL; // The command field for parsing legacy MLC
PCHAR DOT3M = NULL; // 1284 physical layer modes that will break this device
DD((PCE)Pdx,DDT,"ParDetectDot3DataLink: DeviceId [%s]\n", DeviceId);
ParDot3ParseDevId(&DOT3DL, &DOT3C, &CMDField, &DOT4DL, &DOT3M, DeviceId);
ParDot3ParseModes(Pdx,DOT3M);
if (DOT4DL) {
DD((PCE)Pdx,DDT,"ParDot3ParseModes - 1284.4 with MLC Data Link Detected. DOT4DL [%s]\n", DOT4DL);
ParDot4CreateObject(Pdx, DOT4DL);
} else if (DOT3DL) {
DD((PCE)Pdx,DDT,"ParDot4CreateObject - 1284.3 Data Link Detected DL:[%s] C:[%s]\n", DOT3DL, DOT3C);
ParDot3CreateObject(Pdx, DOT3DL, DOT3C);
} else if (CMDField) {
DD((PCE)Pdx,DDT,"ParDot3CreateObject - MLC Data Link Detected. MLC [%s]\n", CMDField);
ParMLCCreateObject(Pdx, CMDField);
} else {
DD((PCE)Pdx,DDT,"ParDot3CreateObject - No Data Link Detected\n");
}
}
VOID
ParDot3ParseDevId(
PCHAR *lpp_DL,
PCHAR *lpp_C,
PCHAR *lpp_CMD,
PCHAR *lpp_4DL,
PCHAR *lpp_M,
PCHAR lpDeviceID
)
{
PCHAR lpKey = lpDeviceID; // Pointer to the Key to look at
PCHAR lpValue; // Pointer to the Key's value
USHORT wKeyLength; // Length for the Key (for stringcmps)
// While there are still keys to look at.
while (lpKey != NULL) {
while (*lpKey == ' ')
++lpKey;
// Is there a terminating COLON character for the current key?
lpValue = StringChr((PCHAR)lpKey, ':');
if( NULL == lpValue ) {
// N: OOPS, somthing wrong with the Device ID
return;
}
// The actual start of the Key value is one past the COLON
++lpValue;
//
// Compute the Key length for Comparison, including the COLON
// which will serve as a terminator
//
wKeyLength = (USHORT)(lpValue - lpKey);
//
// Compare the Key to the Know quantities. To speed up the comparison
// a Check is made on the first character first, to reduce the number
// of strings to compare against.
// If a match is found, the appropriate lpp parameter is set to the
// key's value, and the terminating SEMICOLON is converted to a NULL
// In all cases lpKey is advanced to the next key if there is one.
//
switch (*lpKey) {
case '1':
// Look for DOT3 Datalink
if((RtlCompareMemory(lpKey, "1284.4DL:", wKeyLength)==9))
{
*lpp_4DL = lpValue;
if ((lpKey = StringChr((PCHAR)lpValue, ';'))!=NULL)
{
*lpKey = '\0';
++lpKey;
}
} else if((RtlCompareMemory(lpKey, "1284.3DL:", wKeyLength)==9))
{
*lpp_DL = lpValue;
if ((lpKey = StringChr((PCHAR)lpValue, ';'))!=NULL)
{
*lpKey = '\0';
++lpKey;
}
} else if((RtlCompareMemory(lpKey, "1284.3C:", wKeyLength)==8))
{
*lpp_C = lpValue;
if ((lpKey = StringChr((PCHAR)lpValue, ';'))!=0) {
*lpKey = '\0';
++lpKey;
}
} else if((RtlCompareMemory(lpKey, "1284.3M:", wKeyLength)==8))
{
*lpp_M = lpValue;
if ((lpKey = StringChr((PCHAR)lpValue, ';'))!=0) {
*lpKey = '\0';
++lpKey;
}
} else if((lpKey = StringChr((PCHAR)lpValue, ';'))!=0) {
*lpKey = '\0';
++lpKey;
}
break;
case '.':
// Look for for .3 extras
if ((RtlCompareMemory(lpKey, ".3C:", wKeyLength)==4) ) {
*lpp_C = lpValue;
if ((lpKey = StringChr((PCHAR)lpValue, ';'))!=0) {
*lpKey = '\0';
++lpKey;
}
} else if ((RtlCompareMemory(lpKey, ".3M:", wKeyLength)==4) ) {
*lpp_M = lpValue;
if ((lpKey = StringChr((PCHAR)lpValue, ';'))!=0) {
*lpKey = '\0';
++lpKey;
}
} else if((lpKey = StringChr((PCHAR)lpValue, ';'))!=0) {
*lpKey = '\0';
++lpKey;
}
break;
case 'C':
// Look for MLC Datalink
if( (RtlCompareMemory(lpKey, "CMD:", wKeyLength)==4 ) ||
(RtlCompareMemory(lpKey, "COMMAND SET:", wKeyLength)==12) ) {
*lpp_CMD = lpValue;
if ((lpKey = StringChr((PCHAR)lpValue, ';'))!=0) {
*lpKey = '\0';
++lpKey;
}
} else if((lpKey = StringChr((PCHAR)lpValue, ';'))!=0) {
*lpKey = '\0';
++lpKey;
}
break;
default:
// The key is uninteresting. Go to the next Key
if ((lpKey = StringChr((PCHAR)lpValue, ';'))!=0) {
*lpKey = '\0';
++lpKey;
}
break;
}
}
}
NTSTATUS
ParPnpGetId(
IN PCHAR DeviceIdString,
IN ULONG Type,
OUT PCHAR resultString,
OUT PCHAR descriptionString OPTIONAL
)
/*
Description:
Creates Id's from the device id retrieved from the printer
Parameters:
DeviceId - String with raw device id
Type - What of id we want as a result
Id - requested id
Return Value:
NTSTATUS
*/
{
NTSTATUS status = STATUS_SUCCESS;
USHORT checkSum = 0; // A 16 bit check sum
CHAR nodeName[16] = "LPTENUM\\";
// The following are used to generate sub-strings from the Device ID string
// to get the DevNode name, and to update the registry
PCHAR MFG = NULL; // Manufacturer name
PCHAR MDL = NULL; // Model name
PCHAR CLS = NULL; // Class name
PCHAR AID = NULL; // Hardare ID
PCHAR CID = NULL; // Compatible IDs
PCHAR DES = NULL; // Device Description
switch(Type) {
case BusQueryDeviceID:
// Extract the usefull fields from the DeviceID string. We want
// MANUFACTURE (MFG):
// MODEL (MDL):
// AUTOMATIC ID (AID):
// COMPATIBLE ID (CID):
// DESCRIPTION (DES):
// CLASS (CLS):
ParPnpFindDeviceIdKeys(&MFG, &MDL, &CLS, &DES, &AID, &CID, DeviceIdString);
// Check to make sure we got MFG and MDL as absolute minimum fields. If not
// we cannot continue.
if (!MFG || !MDL)
{
status = STATUS_NOT_FOUND;
goto ParPnpGetId_Cleanup;
}
//
// Concatenate the provided MFG and MDL P1284 fields
// Checksum the entire MFG+MDL string
//
sprintf(resultString, "%s%s\0",MFG,MDL);
if (descriptionString) {
sprintf((PCHAR)descriptionString, "%s %s\0",MFG,MDL);
}
break;
case BusQueryHardwareIDs:
GetCheckSum(DeviceIdString, (USHORT)strlen((const PCHAR)DeviceIdString), &checkSum);
sprintf(resultString,"%s%.20s%04X",nodeName,DeviceIdString,checkSum);
break;
case BusQueryCompatibleIDs:
//
// return only 1 id
//
GetCheckSum(DeviceIdString, (USHORT)strlen((const PCHAR)DeviceIdString), &checkSum);
sprintf(resultString,"%.20s%04X",DeviceIdString,checkSum);
break;
}
if (Type!=BusQueryDeviceID) {
//
// Convert and spaces in the Hardware ID to underscores
//
StringSubst (resultString, ' ', '_', (USHORT)strlen((const PCHAR)resultString));
}
ParPnpGetId_Cleanup:
return(status);
}
VOID
ParPnpFindDeviceIdKeys(
PCHAR *lppMFG,
PCHAR *lppMDL,
PCHAR *lppCLS,
PCHAR *lppDES,
PCHAR *lppAID,
PCHAR *lppCID,
PCHAR lpDeviceID
)
/*
Description:
This function will parse a P1284 Device ID string looking for keys
of interest to the LPT enumerator. Got it from win95 lptenum
Parameters:
lppMFG Pointer to MFG string pointer
lppMDL Pointer to MDL string pointer
lppMDL Pointer to CLS string pointer
lppDES Pointer to DES string pointer
lppCIC Pointer to CID string pointer
lppAID Pointer to AID string pointer
lpDeviceID Pointer to the Device ID string
Return Value:
no return VALUE.
If found the lpp parameters are set to the approprate portions
of the DeviceID string, and they are NULL terminated.
The actual DeviceID string is used, and the lpp Parameters just
reference sections, with appropriate null thrown in.
*/
{
PCHAR lpKey = lpDeviceID; // Pointer to the Key to look at
PCHAR lpValue; // Pointer to the Key's value
USHORT wKeyLength; // Length for the Key (for stringcmps)
// While there are still keys to look at.
DD(NULL,DDT,"ParPnpFindDeviceIdKeys - enter\n");
if( lppMFG ) { *lppMFG = NULL; }
if( lppMDL ) { *lppMDL = NULL; }
if( lppCLS ) { *lppCLS = NULL; }
if( lppDES ) { *lppDES = NULL; }
if( lppAID ) { *lppAID = NULL; }
if( lppCID ) { *lppCID = NULL; }
if( !lpDeviceID ) {
PptAssert(!"ParPnpFindDeviceIdKeys - NULL lpDeviceID");
return;
}
while (lpKey != NULL)
{
while (*lpKey == ' ')
++lpKey;
// Is there a terminating COLON character for the current key?
lpValue = StringChr(lpKey, ':');
if( NULL == lpValue ) {
// N: OOPS, somthing wrong with the Device ID
return;
}
// The actual start of the Key value is one past the COLON
++lpValue;
//
// Compute the Key length for Comparison, including the COLON
// which will serve as a terminator
//
wKeyLength = (USHORT)(lpValue - lpKey);
//
// Compare the Key to the Know quantities. To speed up the comparison
// a Check is made on the first character first, to reduce the number
// of strings to compare against.
// If a match is found, the appropriate lpp parameter is set to the
// key's value, and the terminating SEMICOLON is converted to a NULL
// In all cases lpKey is advanced to the next key if there is one.
//
switch (*lpKey) {
case 'M':
// Look for MANUFACTURE (MFG) or MODEL (MDL)
if((RtlCompareMemory(lpKey, "MANUFACTURER", wKeyLength)>5) ||
(RtlCompareMemory(lpKey, "MFG", wKeyLength)==3) ) {
*lppMFG = lpValue;
if ((lpKey = StringChr(lpValue, ';'))!=NULL) {
*lpKey = '\0';
++lpKey;
}
} else if((RtlCompareMemory(lpKey, "MODEL", wKeyLength)==5) ||
(RtlCompareMemory(lpKey, "MDL", wKeyLength)==3) ) {
*lppMDL = lpValue;
if ((lpKey = StringChr(lpValue, ';'))!=0) {
*lpKey = '\0';
++lpKey;
}
} else if((lpKey = StringChr(lpValue, ';'))!=0) {
*lpKey = '\0';
++lpKey;
}
break;
case 'C':
// Look for CLASS (CLS) or COMPATIBLEID (CID)
if ((RtlCompareMemory(lpKey, "CLASS", wKeyLength)==5) ||
(RtlCompareMemory(lpKey, "CLS", wKeyLength)==3) ) {
*lppCLS = lpValue;
if ((lpKey = StringChr(lpValue, ';'))!=0) {
*lpKey = '\0';
++lpKey;
}
} else if ((RtlCompareMemory(lpKey, "COMPATIBLEID", wKeyLength)>5) ||
(RtlCompareMemory(lpKey, "CID", wKeyLength)==3) ) {
*lppCID = lpValue;
if ((lpKey = StringChr(lpValue, ';'))!=0) {
*lpKey = '\0';
++lpKey;
}
} else if ((lpKey = StringChr(lpValue,';'))!=0) {
*lpKey = '\0';
++lpKey;
}
break;
case 'D':
// Look for DESCRIPTION (DES)
if(RtlCompareMemory(lpKey, "DESCRIPTION", wKeyLength) ||
RtlCompareMemory(lpKey, "DES", wKeyLength) ) {
*lppDES = lpValue;
if((lpKey = StringChr(lpValue, ';'))!=0) {
*lpKey = '\0';
++lpKey;
}
} else if ((lpKey = StringChr(lpValue, ';'))!=0) {
*lpKey = '\0';
++lpKey;
}
break;
case 'A':
// Look for AUTOMATIC ID (AID)
if (RtlCompareMemory(lpKey, "AUTOMATICID", wKeyLength) ||
RtlCompareMemory(lpKey, "AID", wKeyLength) ) {
*lppAID = lpValue;
if ((lpKey = StringChr(lpValue, ';'))!=0) {
*lpKey = '\0';
++lpKey;
}
} else if ((lpKey = StringChr(lpValue, ';'))!=0) {
*lpKey = '\0';
++lpKey;
}
break;
default:
// The key is uninteresting. Go to the next Key
if ((lpKey = StringChr(lpValue, ';'))!=0) {
*lpKey = '\0';
++lpKey;
}
break;
}
}
}
VOID
GetCheckSum(
PCHAR Block,
USHORT Len,
PUSHORT CheckSum
)
{
USHORT i;
// UCHAR lrc;
USHORT crc = 0;
unsigned short crc16a[] = {
0000000, 0140301, 0140601, 0000500,
0141401, 0001700, 0001200, 0141101,
0143001, 0003300, 0003600, 0143501,
0002400, 0142701, 0142201, 0002100,
};
unsigned short crc16b[] = {
0000000, 0146001, 0154001, 0012000,
0170001, 0036000, 0024000, 0162001,
0120001, 0066000, 0074000, 0132001,
0050000, 0116001, 0104001, 0043000,
};
//
// Calculate CRC using tables.
//
UCHAR tmp;
for ( i=0; i<Len; i++) {
tmp = (UCHAR)(Block[i] ^ (UCHAR)crc);
crc = (USHORT)((crc >> 8) ^ crc16a[tmp & 0x0f] ^ crc16b[tmp >> 4]);
}
*CheckSum = crc;
}
PCHAR
Par3QueryDeviceId(
IN PPDO_EXTENSION Pdx,
OUT PCHAR CallerDeviceIdBuffer, OPTIONAL
IN ULONG CallerBufferSize,
OUT PULONG DeviceIdSize,
IN BOOLEAN bReturnRawString, // TRUE == include the 2 size bytes in the returned string
// FALSE == discard the 2 size bytes
IN BOOLEAN bBuildStlDeviceId
)
/*++
This is the replacement function for SppQueryDeviceId.
This function uses the caller supplied buffer if the supplied buffer
is large enough to hold the device id. Otherwise, a buffer is
allocated from paged pool to hold the device ID and a pointer to
the allocated buffer is returned to the caller. The caller determines
whether a buffer was allocated by comparing the returned PCHAR with
the DeviceIdBuffer parameter passed to this function. A NULL return
value indicates that an error occurred.
*** this function assumes that the caller has already acquired
the port (and selected the device if needed in the case
of a 1284.3 daisy chain device).
*** If this function returns a pointer to a paged pool allocation then
the caller is responsible for freeing the buffer when it is no
longer needed.
--*/
{
PUCHAR Controller = Pdx->Controller;
NTSTATUS Status;
UCHAR idSizeBuffer[2];
ULONG bytesToRead;
ULONG bytesRead = 0;
USHORT deviceIdSize;
USHORT deviceIdBufferSize;
PCHAR deviceIdBuffer;
PCHAR readPtr;
BOOLEAN allocatedBuffer = FALSE;
DD((PCE)Pdx,DDT,"Enter pnp::Par3QueryDeviceId: Controller=%x\n", Controller);
if( TRUE == bBuildStlDeviceId ) {
// if this is a legacy stl, forward call to special handler
return ParStlQueryStlDeviceId(Pdx,
CallerDeviceIdBuffer, CallerBufferSize,
DeviceIdSize, bReturnRawString);
}
if( Pdx->Ieee1284_3DeviceId == DOT3_LEGACY_ZIP_ID ) {
// if this is a legacy Zip, forward call to special handler
return Par3QueryLegacyZipDeviceId(Pdx,
CallerDeviceIdBuffer, CallerBufferSize,
DeviceIdSize, bReturnRawString);
}
//
// Take a 40ms nap - there is at least one printer that can't handle
// back to back 1284 device ID queries without a minimum 20-30ms delay
// between the queries which breaks PnP'ing the printer
//
if( KeGetCurrentIrql() == PASSIVE_LEVEL ) {
LARGE_INTEGER delay;
delay.QuadPart = - 10 * 1000 * 40; // 40 ms
KeDelayExecutionThread( KernelMode, FALSE, &delay );
}
*DeviceIdSize = 0;
//
// If we are currently connected to the peripheral via any 1284 mode
// other than Compatibility/Spp mode (which does not require an IEEE
// negotiation), we must first terminate the current mode/connection.
//
ParTerminate( Pdx );
//
// Negotiate the peripheral into nibble device id mode.
//
Status = ParEnterNibbleMode(Pdx, REQUEST_DEVICE_ID);
if( !NT_SUCCESS(Status) ) {
DD((PCE)Pdx,DDT,"pnp::Par3QueryDeviceId: call to ParEnterNibbleMode FAILED\n");
ParTerminateNibbleMode(Pdx);
return NULL;
}
//
// Read first two bytes to get the total (including the 2 size bytes) size
// of the Device Id string.
//
bytesToRead = 2;
Status = ParNibbleModeRead(Pdx, idSizeBuffer, bytesToRead, &bytesRead);
if( !NT_SUCCESS( Status ) || ( bytesRead != bytesToRead ) ) {
DD((PCE)Pdx,DDT,"pnp::Par3QueryDeviceId: read of DeviceID size FAILED\n");
return NULL;
}
//
// Compute size of DeviceId string (including the 2 byte size prefix)
//
deviceIdSize = (USHORT)( idSizeBuffer[0]*0x100 + idSizeBuffer[1] );
DD((PCE)Pdx,DDT,"pnp::Par3QueryDeviceId: DeviceIdSize (including 2 size bytes) reported as %d\n", deviceIdSize);
//
// Allocate a buffer to hold the DeviceId string and read the DeviceId into it.
//
if( bReturnRawString ) {
//
// Caller wants the raw string including the 2 size bytes
//
*DeviceIdSize = deviceIdSize;
deviceIdBufferSize = (USHORT)(deviceIdSize + sizeof(CHAR)); // ID size + ID + terminating NULL
} else {
//
// Caller does not want the 2 byte size prefix
//
*DeviceIdSize = deviceIdSize - 2*sizeof(CHAR);
deviceIdBufferSize = (USHORT)(deviceIdSize - 2*sizeof(CHAR) + sizeof(CHAR)); // ID + terminating NULL
}
//
// If caller's buffer is large enough use it, otherwise allocate a buffer
// to hold the device ID
//
if( CallerDeviceIdBuffer && (CallerBufferSize >= deviceIdBufferSize) ) {
//
// Use caller's buffer - *** NOTE: we are creating an alias for the caller buffer
//
deviceIdBuffer = CallerDeviceIdBuffer;
DD((PCE)Pdx,DDT,"pnp::Par3QueryDeviceId: using Caller supplied buffer\n");
} else {
//
// Either caller did not supply a buffer or supplied a buffer that is not
// large enough to hold the device ID, so allocate a buffer.
//
DD((PCE)Pdx,DDT,"pnp::Par3QueryDeviceId: Caller's Buffer TOO_SMALL - CallerBufferSize= %d, deviceIdBufferSize= %d\n",
CallerBufferSize, deviceIdBufferSize);
DD((PCE)Pdx,DDT,"pnp::Par3QueryDeviceId: will allocate and return ptr to buffer\n");
deviceIdBuffer = (PCHAR)ExAllocatePool(PagedPool, deviceIdBufferSize);
if( !deviceIdBuffer ) {
DD((PCE)Pdx,DDT,"pnp::Par3QueryDeviceId: ExAllocatePool FAILED\n");
return NULL;
}
allocatedBuffer = TRUE; // note that we allocated our own buffer rather than using caller's buffer
}
//
// NULL out the ID buffer to be safe
//
RtlZeroMemory( deviceIdBuffer, deviceIdBufferSize );
//
// Does the caller want the 2 byte size prefix?
//
if( bReturnRawString ) {
//
// Yes, caller wants the size prefix. Copy prefix to buffer to return.
//
*(deviceIdBuffer+0) = idSizeBuffer[0];
*(deviceIdBuffer+1) = idSizeBuffer[1];
readPtr = deviceIdBuffer + 2;
} else {
//
// No, discard size prefix
//
readPtr = deviceIdBuffer;
}
//
// Read remainder of DeviceId from device
//
bytesToRead = deviceIdSize - 2; // already have the 2 size bytes
Status = ParNibbleModeRead(Pdx, readPtr, bytesToRead, &bytesRead);
ParTerminateNibbleMode( Pdx );
P5WritePortUchar(Controller + DCR_OFFSET, DCR_NEUTRAL);
if( !NT_SUCCESS(Status) || (bytesRead < 1) ) {
if( allocatedBuffer ) {
// we're using our own allocated buffer rather than a caller supplied buffer - free it
DD((PCE)Pdx,DDE,"Par3QueryDeviceId:: read of DeviceId FAILED - discarding buffer\n");
ExFreePool( deviceIdBuffer );
}
return NULL;
}
if ( bytesRead < bytesToRead ) {
//
// Device likely reported incorrect value for IEEE 1284 Device ID length
//
// This spew is on by default in checked builds to try to get
// a feel for how many types of devices are broken in this way
//
DD((PCE)Pdx,DDE,"pnp::Par3QueryDeviceId - ID shorter than expected\n");
}
return deviceIdBuffer;
}
VOID
ParReleasePortInfoToPortDevice(
IN PPDO_EXTENSION Pdx
)
/*++
Routine Description:
This routine will release the port information back to the port driver.
Arguments:
Extension - Supplies the device extension.
Return Value:
None.
--*/
{
//
// ParPort treats this as a NO-OP in Win2K, so don't bother sending the IOCTL.
//
// In follow-on to Win2K parport may use this to page the entire driver as
// it was originally intended, so we'll turn this back on then.
//
UNREFERENCED_PARAMETER( Pdx );
return;
}
VOID
ParFreePort(
IN PPDO_EXTENSION Pdx
)
/*++
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.
--*/
{
// Don't allow multiple releases
if( Pdx->bAllocated ) {
DD((PCE)Pdx,DDT,"ParFreePort - calling ParPort's FreePort function\n");
Pdx->FreePort( Pdx->PortContext );
} else {
DD((PCE)Pdx,DDT,"ParFreePort - we don't have the Port! (!Ext->bAllocated)\n");
}
Pdx->bAllocated = FALSE;
}
NTSTATUS
ParAllocPortCompletionRoutine(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PVOID Event
)
/*++
Routine Description:
This routine is the completion routine for a port allocate request.
Arguments:
DeviceObject - Supplies the device object.
Irp - Supplies the I/O request packet.
Context - Supplies the notification event.
Return Value:
STATUS_MORE_PROCESSING_REQUIRED - The Irp still requires processing.
--*/
{
UNREFERENCED_PARAMETER( Irp );
UNREFERENCED_PARAMETER( DeviceObject );
KeSetEvent((PKEVENT) Event, 0, FALSE);
return STATUS_MORE_PROCESSING_REQUIRED;
}
BOOLEAN
ParAllocPort(
IN PPDO_EXTENSION Pdx
)
/*++
Routine Description:
This routine takes the given Irp and sends it down as a port allocate
request. When this request completes, the Irp will be queued for
processing.
Arguments:
Pdx - Supplies the device extension.
Return Value:
FALSE - The port was not successfully allocated.
TRUE - The port was successfully allocated.
--*/
{
PIO_STACK_LOCATION NextSp;
KEVENT Event;
PIRP Irp;
BOOLEAN bAllocated;
NTSTATUS Status;
LARGE_INTEGER Timeout;
// Don't allow multiple allocations
if (Pdx->bAllocated) {
DD((PCE)Pdx,DDT,"ParAllocPort - controller=%x - port already allocated\n", Pdx->Controller);
return TRUE;
}
Irp = Pdx->CurrentOpIrp;
KeInitializeEvent(&Event, NotificationEvent, FALSE);
NextSp = IoGetNextIrpStackLocation(Irp);
NextSp->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
NextSp->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_PARALLEL_PORT_ALLOCATE;
IoSetCompletionRoutine( Irp, ParAllocPortCompletionRoutine, &Event, TRUE, TRUE, TRUE );
ParCallDriver(Pdx->PortDeviceObject, Irp);
Timeout.QuadPart = -((LONGLONG) Pdx->TimerStart*10*1000*1000);
Status = KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, &Timeout);
if (Status == STATUS_TIMEOUT) {
IoCancelIrp(Irp);
KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL);
}
bAllocated = (BOOLEAN)NT_SUCCESS(Irp->IoStatus.Status);
Pdx->bAllocated = bAllocated;
if (!bAllocated) {
Irp->IoStatus.Status = STATUS_DEVICE_BUSY;
DD((PCE)Pdx,DDE,"ParAllocPort - controller=%x - FAILED - DEVICE_BUSY timeout\n",Pdx->Controller);
}
return bAllocated;
}