mirror of https://github.com/tongzx/nt5src
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.
2187 lines
65 KiB
2187 lines
65 KiB
/*++
|
|
|
|
Copyright (C) Microsoft Corporation, 1993 - 1999
|
|
|
|
Module Name:
|
|
|
|
parpnp.c
|
|
|
|
Abstract:
|
|
|
|
This file contains the main PnP functions.
|
|
|
|
see:
|
|
- pnpfdo.c for AddDevice and FDO handling of PnP IRPs
|
|
- pnppdo.c for PDO handling of PnP IRPs
|
|
- pnpnotfy.c for PnP callback entry points
|
|
- pnputil.c for PnP utility functions
|
|
|
|
Author:
|
|
|
|
Timothy T. Wells
|
|
|
|
Environment:
|
|
|
|
Kernel mode
|
|
|
|
Revision History :
|
|
|
|
--*/
|
|
|
|
#include "pch.h"
|
|
|
|
// used to construct 1284.3 "Dot" name suffixes
|
|
// table lookup for integer to WCHAR conversion
|
|
WCHAR ParInt2Wchar[] = { L'0', L'1', L'2', L'3' };
|
|
|
|
//
|
|
// Keep track of the number of parallel port devices created...
|
|
//
|
|
ULONG g_NumPorts = 0;
|
|
|
|
LARGE_INTEGER AcquirePortTimeout;
|
|
|
|
UNICODE_STRING RegistryPath = {0,0,0};
|
|
|
|
NTSTATUS
|
|
ParRegisterForParportRemovalRelations(
|
|
IN PDEVICE_EXTENSION Extension
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
PARPORT_REMOVAL_RELATIONS pptRemovalRelations;
|
|
PDEVICE_OBJECT portDevObj = Extension->PortDeviceObject;
|
|
|
|
if( Extension->RegForPptRemovalRelations ) {
|
|
// already registered - don't do it again
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
pptRemovalRelations.DeviceObject = Extension->DeviceObject;
|
|
pptRemovalRelations.Flags = 0;
|
|
pptRemovalRelations.DeviceName = &Extension->ClassName;
|
|
|
|
status = ParBuildSendInternalIoctl(IOCTL_INTERNAL_REGISTER_FOR_REMOVAL_RELATIONS, portDevObj,
|
|
&pptRemovalRelations, sizeof(PARPORT_REMOVAL_RELATIONS),
|
|
NULL, 0, NULL);
|
|
if( NT_SUCCESS( status ) ) {
|
|
Extension->RegForPptRemovalRelations = TRUE;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
NTSTATUS
|
|
ParUnregisterForParportRemovalRelations(
|
|
IN PDEVICE_EXTENSION Extension
|
|
)
|
|
{
|
|
NTSTATUS status;
|
|
PARPORT_REMOVAL_RELATIONS pptRemovalRelations;
|
|
PDEVICE_OBJECT portDevObj = Extension->PortDeviceObject;
|
|
|
|
if( !Extension->RegForPptRemovalRelations ) {
|
|
// we're not registered - don't try to unregister
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
if( Extension->ParPortDeviceGone ) {
|
|
// ParPort device is already gone - probably surprise removed
|
|
// - don't try to send IOCTL or we will likely bugcheck
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
pptRemovalRelations.DeviceObject = Extension->DeviceObject;
|
|
pptRemovalRelations.Flags = 0;
|
|
pptRemovalRelations.DeviceName = &Extension->ClassName;
|
|
|
|
status = ParBuildSendInternalIoctl(IOCTL_INTERNAL_UNREGISTER_FOR_REMOVAL_RELATIONS, portDevObj,
|
|
&pptRemovalRelations, sizeof(PARPORT_REMOVAL_RELATIONS),
|
|
NULL, 0, NULL);
|
|
if( NT_SUCCESS( status ) ) {
|
|
Extension->RegForPptRemovalRelations = FALSE;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
PCHAR
|
|
Par3QueryDeviceId(
|
|
IN PDEVICE_EXTENSION Extension,
|
|
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 = Extension->Controller;
|
|
NTSTATUS Status;
|
|
UCHAR idSizeBuffer[2];
|
|
ULONG bytesToRead;
|
|
ULONG bytesRead = 0;
|
|
USHORT deviceIdSize;
|
|
USHORT deviceIdBufferSize;
|
|
PCHAR deviceIdBuffer;
|
|
PCHAR readPtr;
|
|
BOOLEAN allocatedBuffer = FALSE;
|
|
|
|
ParDumpV( ("Enter pnp::Par3QueryDeviceId: Controller=%x\n", Controller) );
|
|
|
|
if( TRUE == bBuildStlDeviceId ) {
|
|
// if this is a legacy stl, forward call to special handler
|
|
return ParStlQueryStlDeviceId(Extension,
|
|
CallerDeviceIdBuffer, CallerBufferSize,
|
|
DeviceIdSize, bReturnRawString);
|
|
}
|
|
|
|
if( Extension->Ieee1284_3DeviceId == DOT3_LEGACY_ZIP_ID ) {
|
|
// if this is a legacy Zip, forward call to special handler
|
|
return Par3QueryLegacyZipDeviceId(Extension,
|
|
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.
|
|
//
|
|
// dvdf - RMT - what if we are connected in a reverse mode?
|
|
//
|
|
if( (Extension->Connected) && (afpForward[Extension->IdxForwardProtocol].fnDisconnect) ) {
|
|
afpForward[Extension->IdxForwardProtocol].fnDisconnect (Extension);
|
|
}
|
|
|
|
|
|
//
|
|
// Negotiate the peripheral into nibble device id mode.
|
|
//
|
|
Status = ParEnterNibbleMode(Extension, REQUEST_DEVICE_ID);
|
|
if( !NT_SUCCESS(Status) ) {
|
|
ParDumpV( ("pnp::Par3QueryDeviceId: call to ParEnterNibbleMode FAILED\n") );
|
|
ParTerminateNibbleMode(Extension);
|
|
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(Extension, idSizeBuffer, bytesToRead, &bytesRead);
|
|
if( !NT_SUCCESS( Status ) || ( bytesRead != bytesToRead ) ) {
|
|
ParDumpV( ("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] );
|
|
ParDumpV( ("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;
|
|
ParDumpV( ("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.
|
|
//
|
|
ParDumpV( ("pnp::Par3QueryDeviceId: Caller's Buffer TOO_SMALL - CallerBufferSize= %d, deviceIdBufferSize= %d\n",
|
|
CallerBufferSize, deviceIdBufferSize) );
|
|
ParDumpV( ("pnp::Par3QueryDeviceId: will allocate and return ptr to buffer\n") );
|
|
deviceIdBuffer = (PCHAR)ExAllocatePool(PagedPool, deviceIdBufferSize);
|
|
if( !deviceIdBuffer ) {
|
|
ParDumpV( ("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(Extension, readPtr, bytesToRead, &bytesRead);
|
|
|
|
|
|
ParTerminateNibbleMode( Extension );
|
|
WRITE_PORT_UCHAR(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
|
|
ParDump2(PARERRORS, ("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
|
|
//
|
|
DbgPrint(("pnp::Par3QueryDeviceId - ID shorter than expected\n") );
|
|
}
|
|
|
|
return deviceIdBuffer;
|
|
}
|
|
|
|
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 PDEVICE_EXTENSION Extension
|
|
)
|
|
|
|
/*++
|
|
|
|
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.
|
|
|
|
Extension - Supplies the device extension.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
{
|
|
RTL_QUERY_REGISTRY_TABLE ParamTable[4];
|
|
ULONG UsePIWriteLoop;
|
|
ULONG UseNT35Priority;
|
|
ULONG Zero = 0;
|
|
NTSTATUS Status;
|
|
HANDLE hRegistry;
|
|
|
|
ParDump(PARDUMP_VERBOSE_MAX,
|
|
("PARALLEL: "
|
|
"Enter ParCheckParameters(...)\n") );
|
|
|
|
if (Extension->PhysicalDeviceObject) {
|
|
|
|
Status = IoOpenDeviceRegistryKey (Extension->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 = &(Extension->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) {
|
|
Extension->UsePIWriteLoop = TRUE;
|
|
}
|
|
|
|
if(UseNT35Priority) {
|
|
Extension->UseNT35Priority = TRUE;
|
|
}
|
|
|
|
if(Extension->InitializationTimeout == 0) {
|
|
Extension->InitializationTimeout = 15;
|
|
}
|
|
}
|
|
|
|
} else {
|
|
Extension->InitializationTimeout = 15;
|
|
}
|
|
|
|
ZwClose (hRegistry);
|
|
|
|
} else {
|
|
Extension->InitializationTimeout = 15;
|
|
}
|
|
}
|
|
|
|
BOOLEAN
|
|
String2Num(
|
|
IN OUT PUCHAR *lpp_Str,
|
|
IN CHAR c,
|
|
OUT ULONG *num
|
|
)
|
|
{
|
|
PUCHAR string = *lpp_Str;
|
|
int cc;
|
|
int cnt = 0;
|
|
|
|
ParDump2(PARINFO, ("String2Num. string [%s]\n", string) );
|
|
*num = 0;
|
|
if (!*lpp_Str)
|
|
{
|
|
ParDump2(PARINFO, ("String2Num. Null String\n") );
|
|
*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)
|
|
{
|
|
ParDump2(PARINFO, ("String2Num. Delimeter found num [%x]\n", *num));
|
|
*lpp_Str = 0;
|
|
return TRUE;
|
|
}
|
|
else if (cc == 'y' || cc == 'Y')
|
|
{
|
|
ParDump2(PARINFO, ("String2Num. Special case 'y' hit\n") );
|
|
*lpp_Str = 0;
|
|
*num = -1; /* Special case */
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
ParDump2(PARINFO, ("String2Num. Dunno\n") );
|
|
*lpp_Str = 0;
|
|
*num = 0; /* It's all messed up */
|
|
return FALSE;
|
|
}
|
|
ParDump2(PARINFO, ("String2Num. num [%x]\n", *num) );
|
|
(*lpp_Str)++;
|
|
if (cnt++ > 100)
|
|
{
|
|
// If our string is this large, then I'm gonna assume somethings
|
|
// wrong
|
|
ParDump2(PARINFO, ("String2Num. String too long\n") );
|
|
goto String2Num_End;
|
|
}
|
|
goto String2Num_Start;
|
|
|
|
String2Num_End:
|
|
ParDump2(PARINFO, ("String2Num. Somethings 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;
|
|
}
|
|
|
|
PUCHAR
|
|
StringChr(
|
|
IN PCHAR string,
|
|
IN CHAR c
|
|
)
|
|
{
|
|
if(!string) {
|
|
return(NULL);
|
|
}
|
|
|
|
while(*string) {
|
|
if( *string==c ) {
|
|
return (PUCHAR)string;
|
|
}
|
|
string++;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
VOID
|
|
StringSubst(
|
|
IN PUCHAR lpS,
|
|
IN UCHAR chTargetChar,
|
|
IN UCHAR 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 PDEVICE_EXTENSION Extension,
|
|
IN PUCHAR DeviceId
|
|
)
|
|
{
|
|
PUCHAR DOT3DL = NULL; // 1284.3 Data Link Channels
|
|
PUCHAR DOT3C = NULL; // 1284.3 Data Link Services
|
|
PUCHAR DOT4DL = NULL; // 1284.4 Data Link for peripherals that were
|
|
// implemented prior to 1284.3
|
|
PUCHAR CMDField = NULL; // The command field for parsing legacy MLC
|
|
PUCHAR DOT3M = NULL; // 1284 physical layer modes that will break this device
|
|
|
|
ParDump2(PARDUMP_PNP_DL, ("ParDetectDot3DataLink: DeviceId [%s]\n", DeviceId) );
|
|
ParDot3ParseDevId(&DOT3DL, &DOT3C, &CMDField, &DOT4DL, &DOT3M, DeviceId);
|
|
ParDot3ParseModes(Extension,DOT3M);
|
|
if (DOT4DL)
|
|
{
|
|
ParDump2(PARDUMP_PNP_DL, ("1284.4 with MLC Data Link Detected. DOT4DL [%s]\n", DOT4DL) );
|
|
ParDot4CreateObject(Extension, DOT4DL);
|
|
}
|
|
else if (DOT3DL)
|
|
{
|
|
ParDump2(PARDUMP_PNP_DL, ("1284.3 Data Link Detected DL:[%s] C:[%s]\n", DOT3DL, DOT3C) );
|
|
ParDot3CreateObject(Extension, DOT3DL, DOT3C);
|
|
}
|
|
else if (CMDField)
|
|
{
|
|
ParDump2(PARDUMP_PNP_DL, ("MLC Data Link Detected. MLC [%s]\n", CMDField) );
|
|
ParMLCCreateObject(Extension, CMDField);
|
|
}
|
|
#if DBG
|
|
else
|
|
{
|
|
ParDump2(PARDUMP_PNP_DL, ("No Data Link Detected\n") );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
VOID
|
|
ParDot3ParseDevId(
|
|
PUCHAR *lpp_DL,
|
|
PUCHAR *lpp_C,
|
|
PUCHAR *lpp_CMD,
|
|
PUCHAR *lpp_4DL,
|
|
PUCHAR *lpp_M,
|
|
PUCHAR lpDeviceID
|
|
)
|
|
{
|
|
PUCHAR lpKey = lpDeviceID; // Pointer to the Key to look at
|
|
PUCHAR lpValue; // Pointer to the Key's value
|
|
USHORT wKeyLength; // Length for the Key (for stringcmps)
|
|
|
|
// While there are still keys to look at.
|
|
|
|
ParDump(PARDUMP_PNP_DL,
|
|
("PARALLEL: "
|
|
"Enter ParDot3ParseDevId(...)\n") );
|
|
|
|
while (lpKey != NULL)
|
|
{
|
|
while (*lpKey == ' ')
|
|
++lpKey;
|
|
|
|
// Is there a terminating COLON character for the current key?
|
|
if (!(lpValue = StringChr((PCHAR)lpKey, ':')) ) {
|
|
// 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) ) {
|
|
|
|
*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;
|
|
}
|
|
}
|
|
}
|
|
|
|
PUCHAR
|
|
ParQueryDeviceId(
|
|
IN PDEVICE_EXTENSION Extension
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
try to read a device ID from a device
|
|
|
|
*** this function assumes that the caller has already acquired
|
|
the port (and selected the device if needed).
|
|
|
|
*** on success, this function returns a pointer to allocated pool
|
|
which the caller is responsible for freeing when it is no
|
|
longer needed
|
|
|
|
Arguments:
|
|
|
|
Extension - used to find the Controller
|
|
|
|
Return Value:
|
|
|
|
PUCHAR - points to DeviceIdString on success
|
|
NULL - if failure
|
|
|
|
--*/
|
|
{
|
|
PUCHAR Controller = Extension->Controller;
|
|
NTSTATUS Status;
|
|
UCHAR SizeBuf[2];
|
|
ULONG NumBytes = 0;
|
|
USHORT Size;
|
|
PUCHAR deviceId;
|
|
|
|
if ((Extension->Connected) &&
|
|
(afpForward[Extension->IdxForwardProtocol].fnDisconnect)) {
|
|
|
|
afpForward[Extension->IdxForwardProtocol].fnDisconnect (Extension);
|
|
}
|
|
|
|
//
|
|
// negotiate the peripheral into nibble mode/device id request.
|
|
//
|
|
Status = ParEnterNibbleMode(Extension, TRUE);
|
|
if (!NT_SUCCESS(Status)) {
|
|
ParDumpV( ("ParQueryDeviceId() - ParEnterNibbleMode FAILed\n") );
|
|
return NULL;
|
|
}
|
|
|
|
// read the Device Id string size (encoded in first 2 bytes)
|
|
// - reported size includes the 2 size bytes
|
|
Status = ParNibbleModeRead(Extension, SizeBuf, 2, &NumBytes);
|
|
if( !NT_SUCCESS(Status) || ( NT_SUCCESS(Status) && NumBytes != 2 ) ) {
|
|
// read of ID string size failed
|
|
ParDumpV( ("ParQueryDeviceId() - Read of ID string size FAILed\n") );
|
|
ParTerminateNibbleMode(Extension);
|
|
WRITE_PORT_UCHAR(Controller + DCR_OFFSET, DCR_NEUTRAL);
|
|
return NULL;
|
|
}
|
|
|
|
// we have the deviceId size
|
|
Size = (USHORT)( SizeBuf[0]*0x100 + SizeBuf[1] );
|
|
// *DeviceIdSize = Size - sizeof(USHORT);
|
|
ParDumpV( ("DeviceIdSize reported as %d, attempting to read DeviceId\n", Size - sizeof(USHORT)) );
|
|
|
|
// allocate a buffer to hold device ID
|
|
deviceId = ExAllocatePool(PagedPool, Size);
|
|
if( !deviceId ) {
|
|
ParDumpV( ("ParQueryDeviceId() - unable to allocate buffer to hold ID\n") );
|
|
ParTerminateNibbleMode(Extension);
|
|
WRITE_PORT_UCHAR(Controller + DCR_OFFSET, DCR_NEUTRAL);
|
|
ExFreePool(deviceId);
|
|
return NULL;
|
|
}
|
|
RtlZeroMemory(deviceId, Size);
|
|
|
|
// read the device ID
|
|
Status = ParNibbleModeRead(Extension, deviceId, Size - sizeof(USHORT), &NumBytes);
|
|
if ( !NT_SUCCESS(Status) || ( NT_SUCCESS(Status) && ( NumBytes != (Size - sizeof(USHORT)) ) ) ) {
|
|
ParDumpV( ("ParQueryDeviceId() - FAIL in read of DeviceID\n") );
|
|
ParTerminateNibbleMode(Extension);
|
|
WRITE_PORT_UCHAR(Controller + DCR_OFFSET, DCR_NEUTRAL);
|
|
ExFreePool(deviceId);
|
|
return NULL;
|
|
}
|
|
ParDumpV( ("ParQueryDeviceId() - ID=<%s>\n", deviceId) );
|
|
return deviceId;
|
|
}
|
|
|
|
VOID
|
|
ParKillDeviceObject(
|
|
PDEVICE_OBJECT DeviceObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Kill a ParClass ejected device object:
|
|
- set the device extension state to indicate that we are
|
|
in the process of going away so that we can fail
|
|
IRPs as appropriate
|
|
- remove symbolic link to the device object
|
|
- close handle to ParPort FILE object
|
|
- unregister PnP notification callbacks
|
|
- free pool allocations
|
|
- remove the device object from the ParClass FDO's list of
|
|
ParClass ejected device objects
|
|
- delete the device object
|
|
|
|
*** This routine assumes that the caller holds FdoExtension->DevObjListMutex
|
|
|
|
Arguments:
|
|
|
|
DeviceObject - The device object we want to kill
|
|
|
|
Return Value:
|
|
|
|
NONE
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION Extension;
|
|
|
|
if( !DeviceObject ) {
|
|
// insurance against receiving a NULL pointer
|
|
ParDumpV( ("ParKillDeviceObject(...): passed a NULL pointer, returning") );
|
|
return;
|
|
}
|
|
|
|
Extension = DeviceObject->DeviceExtension;
|
|
|
|
if( !Extension->IsPdo ) {
|
|
// we only handle ParClass ejected device objects (PDOs and PODOs)
|
|
ParDumpV( ("ParKillDeviceObject(...): DeviceObject passed is the FDO, bailing out") );
|
|
return;
|
|
}
|
|
|
|
ParDumpV( ("ParKillDeviceObject(...): Killing Device Object: %x %wZ\n",
|
|
DeviceObject, &Extension->SymbolicLinkName) );
|
|
|
|
|
|
//
|
|
// set the device extension state to indicate that death is
|
|
// imminent so that we can fail IRPs as appropriate
|
|
//
|
|
Extension->DeviceStateFlags |= PAR_DEVICE_DELETE_PENDING;
|
|
|
|
// Notify the data link so it can begin the cleanup process.
|
|
ParDot3DestroyObject(Extension);
|
|
|
|
//
|
|
// remove symbolic link to the device object
|
|
//
|
|
if( Extension->CreatedSymbolicLink ) {
|
|
NTSTATUS status;
|
|
status = IoDeleteSymbolicLink( &Extension->SymbolicLinkName );
|
|
if( !NT_SUCCESS(status) ) {
|
|
ParDumpV( ("IoDeleteSymbolicLink FAILED for %wZ\n", &Extension->SymbolicLinkName) );
|
|
}
|
|
status = RtlDeleteRegistryValue(RTL_REGISTRY_DEVICEMAP,
|
|
(PWSTR)L"PARALLEL PORTS",
|
|
Extension->ClassName.Buffer);
|
|
if( !NT_SUCCESS(status) ) {
|
|
ParDumpV( ("RtlDeleteRegistryValue FAILED for PARALLEL PORTS%wZ->%wZ\n",
|
|
&Extension->ClassName, &Extension->SymbolicLinkName) );
|
|
}
|
|
Extension->CreatedSymbolicLink = FALSE;
|
|
}
|
|
|
|
|
|
//
|
|
// close handle to ParPort FILE object
|
|
//
|
|
if( Extension->PortDeviceFileObject ) {
|
|
ObDereferenceObject( Extension->PortDeviceFileObject );
|
|
Extension->PortDeviceFileObject = NULL;
|
|
}
|
|
|
|
|
|
//
|
|
// unregister PnP notification callbacks
|
|
//
|
|
if( Extension->NotificationHandle ) {
|
|
IoUnregisterPlugPlayNotification (Extension->NotificationHandle);
|
|
Extension->NotificationHandle = NULL;
|
|
}
|
|
|
|
//
|
|
// If this is a PODO that is registered for WMI, unregister now
|
|
// (PDOs don't do this because they register/unregister during START/REMOVE)
|
|
//
|
|
if( ParIsPodo(DeviceObject) && Extension->PodoRegForWMI ) {
|
|
ParWMIRegistrationControl( DeviceObject, WMIREG_ACTION_DEREGISTER );
|
|
Extension->PodoRegForWMI = FALSE;
|
|
}
|
|
|
|
//
|
|
// free pool allocations that hold our name strings
|
|
//
|
|
RtlFreeUnicodeString(&Extension->PortSymbolicLinkName);
|
|
RtlFreeUnicodeString(&Extension->SymbolicLinkName);
|
|
RtlFreeUnicodeString(&Extension->ClassName);
|
|
|
|
|
|
//
|
|
// if the device object is in the list of ParClass ejected device objects,
|
|
// remove it from the list
|
|
//
|
|
{
|
|
//
|
|
// The head of the list is really "ParClassPdo" in the
|
|
// ParClass FDO's device extension
|
|
//
|
|
PDEVICE_EXTENSION FdoExtension = Extension->ParClassFdo->DeviceExtension;
|
|
PDEVICE_OBJECT currentDO = FdoExtension->ParClassPdo;
|
|
PDEVICE_EXTENSION currentExt;
|
|
if( !currentDO ) { // empty device object list
|
|
goto objectNotInList;
|
|
}
|
|
currentExt = currentDO->DeviceExtension;
|
|
|
|
if( currentDO == DeviceObject ) {
|
|
|
|
//
|
|
// device object that we're looking for is the
|
|
// first in the list, so remove it
|
|
//
|
|
FdoExtension->ParClassPdo = currentExt->Next;
|
|
currentExt->Next = NULL;
|
|
|
|
} else {
|
|
|
|
//
|
|
// walk the list to find the device object that we're looking for
|
|
//
|
|
|
|
PDEVICE_OBJECT nextDO = currentExt->Next;
|
|
PDEVICE_EXTENSION nextExt;
|
|
if( !nextDO ) { // object not in list
|
|
goto objectNotInList;
|
|
}
|
|
nextExt = nextDO->DeviceExtension;
|
|
|
|
while( nextDO != DeviceObject ) {
|
|
|
|
//
|
|
// we haven't found the device object that we're looking for yet,
|
|
// so advance our pointers
|
|
//
|
|
currentDO = nextDO;
|
|
currentExt = nextExt;
|
|
nextDO = currentExt->Next;
|
|
if( !nextDO ) { // object not in list
|
|
goto objectNotInList;
|
|
}
|
|
nextExt = nextDO->DeviceExtension;
|
|
|
|
}
|
|
|
|
//
|
|
// found device object - remove it from the list
|
|
//
|
|
currentExt->Next = nextExt->Next;
|
|
nextExt->Next = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
objectNotInList: // target for device object not in list
|
|
|
|
|
|
//
|
|
// delete the device object
|
|
//
|
|
if( !(Extension->DeviceStateFlags & PAR_DEVICE_DELETED) ) {
|
|
// mark extension so that we don't call IoDeleteDevice twice
|
|
Extension->DeviceStateFlags |= PAR_DEVICE_DELETED;
|
|
IoDeleteDevice(DeviceObject);
|
|
}
|
|
}
|
|
|
|
BOOLEAN
|
|
ParDeviceExists(
|
|
IN PDEVICE_EXTENSION Extension,
|
|
IN BOOLEAN HavePortKeepPort
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Is the hardware associated with this Device Object still there?
|
|
|
|
Query for the device's 1284 device ID string, extract the
|
|
relevent information from the raw ID string, and compare
|
|
this information with that stored in the device's extension.
|
|
|
|
Note: This function returns FALSE on any error.
|
|
|
|
Arguments:
|
|
|
|
Extension - The device extension of the Device Object to check
|
|
|
|
Return Value:
|
|
|
|
TRUE - if the device is still there
|
|
FALSE - otherwise
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PCHAR buffer = NULL;
|
|
ULONG bufferLength;
|
|
UCHAR resultString[MAX_ID_SIZE];
|
|
BOOLEAN boolResult;
|
|
|
|
ParDumpV( ("Enter ParDeviceExists(...): %wZ\n", &Extension->SymbolicLinkName) );
|
|
|
|
RtlZeroMemory(resultString, MAX_ID_SIZE);
|
|
|
|
//
|
|
// Select the 1284.3 daisy chain device
|
|
//
|
|
if( !ParSelectDevice(Extension, HavePortKeepPort) ) {
|
|
return FALSE;
|
|
};
|
|
|
|
//
|
|
// Query the DeviceId
|
|
//
|
|
if ( Extension->Ieee1284Flags & ( 1 << Extension->Ieee1284_3DeviceId ) ) {
|
|
buffer = Par3QueryDeviceId(Extension, NULL, 0, &bufferLength, FALSE, TRUE);
|
|
}
|
|
else {
|
|
buffer = Par3QueryDeviceId(Extension, NULL, 0, &bufferLength, FALSE, FALSE);
|
|
}
|
|
|
|
//
|
|
// We no longer need access to the hardware, Deselect the 1284.3 daisy chain device
|
|
//
|
|
boolResult = ParDeselectDevice(Extension, HavePortKeepPort);
|
|
ASSERT(TRUE == boolResult);
|
|
|
|
// check if we got a device ID
|
|
if( !buffer ) {
|
|
ParDumpV( ("pnp::ParDeviceExists: Device gone (Par3QueryDeviceId returned NULL)\n") );
|
|
return FALSE;
|
|
}
|
|
|
|
ParDumpP( ("pnp::ParDeviceExists: \"RAW\" ID string = <%s>\n", buffer) );
|
|
|
|
// extract the part of the ID that we want from the raw string
|
|
// returned by the hardware
|
|
status = ParPnpGetId((PUCHAR)buffer, BusQueryDeviceID, (PUCHAR)resultString, NULL);
|
|
|
|
// no longer needed
|
|
ExFreePool(buffer);
|
|
|
|
// were we able to extract the info that we want from the raw ID string?
|
|
if( !NT_SUCCESS(status) ) {
|
|
return FALSE;
|
|
}
|
|
|
|
// Does the ID that we just retrieved from the device match the one
|
|
// that we previously saved in the device extension?
|
|
if(0 != strcmp((const PCHAR)Extension->DeviceIdString, (const PCHAR)resultString)) {
|
|
ParDumpP( ("pnp::ParDeviceExists: device <%s> on %wZ GONE - strcmp failed\n",
|
|
resultString, &Extension->SymbolicLinkName) );
|
|
ParDumpP( ("pnp::ParDeviceExists: existing device was <%s>\n",
|
|
Extension->DeviceIdString) );
|
|
return FALSE;
|
|
}
|
|
|
|
ParDumpP( ("pnp::ParDeviceExists: device <%s> on %wZ is STILL THERE\n",
|
|
resultString, &Extension->SymbolicLinkName) );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
NTSTATUS
|
|
ParPnpGetId(
|
|
IN PUCHAR DeviceIdString,
|
|
IN ULONG Type,
|
|
OUT PUCHAR resultString,
|
|
OUT PUCHAR 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;
|
|
USHORT checkSum=0; // A 16 bit check sum
|
|
UCHAR 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
|
|
PUCHAR MFG = NULL; // Manufacturer name
|
|
PUCHAR MDL = NULL; // Model name
|
|
PUCHAR CLS = NULL; // Class name
|
|
PUCHAR AID = NULL; // Hardare ID
|
|
PUCHAR CID = NULL; // Compatible IDs
|
|
PUCHAR DES = NULL; // Device Description
|
|
|
|
ParDump(PARDUMP_VERBOSE_MAX,
|
|
("PARALLEL: "
|
|
"Enter ParPnpGetId(...)\n") );
|
|
|
|
status = STATUS_SUCCESS;
|
|
|
|
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((PCHAR)resultString, "%s%s\0",MFG,MDL);
|
|
|
|
if (descriptionString) {
|
|
sprintf((PCHAR)descriptionString, "%s %s\0",MFG,MDL);
|
|
}
|
|
|
|
break;
|
|
|
|
case BusQueryHardwareIDs:
|
|
|
|
GetCheckSum((PUCHAR)DeviceIdString, (USHORT)strlen((const PCHAR)DeviceIdString), &checkSum);
|
|
sprintf((PCHAR)resultString,"%s%.20s%04X",nodeName,DeviceIdString,checkSum);
|
|
break;
|
|
|
|
case BusQueryCompatibleIDs:
|
|
|
|
//
|
|
// return only 1 id
|
|
//
|
|
GetCheckSum(DeviceIdString, (USHORT)strlen((const PCHAR)DeviceIdString), &checkSum);
|
|
sprintf((PCHAR)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(
|
|
PUCHAR *lppMFG,
|
|
PUCHAR *lppMDL,
|
|
PUCHAR *lppCLS,
|
|
PUCHAR *lppDES,
|
|
PUCHAR *lppAID,
|
|
PUCHAR *lppCID,
|
|
PUCHAR 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.
|
|
|
|
*/
|
|
{
|
|
PUCHAR lpKey = lpDeviceID; // Pointer to the Key to look at
|
|
PUCHAR lpValue; // Pointer to the Key's value
|
|
USHORT wKeyLength; // Length for the Key (for stringcmps)
|
|
|
|
// While there are still keys to look at.
|
|
|
|
ParDump(PARDUMP_VERBOSE_MAX,
|
|
("PARALLEL: "
|
|
"Enter ParPnpFindDeviceIdKeys(...)\n") );
|
|
|
|
while (lpKey != NULL)
|
|
{
|
|
while (*lpKey == ' ')
|
|
++lpKey;
|
|
|
|
// Is there a terminating COLON character for the current key?
|
|
if (!(lpValue = StringChr((PCHAR)lpKey, ':')) ) {
|
|
// 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((PCHAR)lpValue, ';'))!=NULL) {
|
|
*lpKey = '\0';
|
|
++lpKey;
|
|
}
|
|
|
|
} else if((RtlCompareMemory(lpKey, "MODEL", wKeyLength)==5) ||
|
|
(RtlCompareMemory(lpKey, "MDL", wKeyLength)==3) ) {
|
|
|
|
*lppMDL = 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 CLASS (CLS)
|
|
if ((RtlCompareMemory(lpKey, "CLASS", wKeyLength)==5) ||
|
|
(RtlCompareMemory(lpKey, "CLS", wKeyLength)==3) ) {
|
|
|
|
*lppCLS = lpValue;
|
|
if ((lpKey = StringChr((PCHAR)lpValue, ';'))!=0) {
|
|
*lpKey = '\0';
|
|
++lpKey;
|
|
}
|
|
|
|
} else if ((RtlCompareMemory(lpKey, "COMPATIBLEID", wKeyLength)>5) ||
|
|
(RtlCompareMemory(lpKey, "CID", wKeyLength)==3) ) {
|
|
|
|
*lppCID = lpValue;
|
|
if ((lpKey = StringChr((PCHAR)lpValue, ';'))!=0) {
|
|
*lpKey = '\0';
|
|
++lpKey;
|
|
}
|
|
|
|
} else if ((lpKey = StringChr((PCHAR)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((PCHAR)lpValue, ';'))!=0) {
|
|
*lpKey = '\0';
|
|
++lpKey;
|
|
}
|
|
|
|
} else if ((lpKey = StringChr((PCHAR)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((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;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
GetCheckSum(
|
|
PUCHAR 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;
|
|
}
|
|
|
|
|
|
//
|
|
// old pnpdone.c follows
|
|
//
|
|
|
|
NTSTATUS
|
|
ParParallelPnp (
|
|
IN PDEVICE_OBJECT pDeviceObject,
|
|
IN PIRP pIrp
|
|
)
|
|
/*++dvdf
|
|
|
|
Routine Description:
|
|
|
|
This is the dispatch routine for all PNP IRPs. It forwards the request
|
|
to the appropriate routine based on whether the DO is a PDO or FDO.
|
|
|
|
Arguments:
|
|
|
|
pDeviceObject - represents a parallel device
|
|
|
|
pIrp - PNP Irp
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - if successful.
|
|
STATUS_UNSUCCESSFUL - otherwise.
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_EXTENSION extension = pDeviceObject->DeviceExtension;
|
|
|
|
if ( ((PDEVICE_EXTENSION)(pDeviceObject->DeviceExtension))->IsPdo ) {
|
|
ASSERT( extension->DeviceType && (PAR_DEVTYPE_PODO | PAR_DEVTYPE_PDO) );
|
|
return ParPdoParallelPnp (pDeviceObject, pIrp);
|
|
} else {
|
|
ASSERT( extension->DeviceType && PAR_DEVTYPE_FDO );
|
|
return ParFdoParallelPnp (pDeviceObject, pIrp);
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
ParAcquirePort(
|
|
IN PDEVICE_OBJECT PortDeviceObject,
|
|
IN PLARGE_INTEGER Timeout OPTIONAL
|
|
)
|
|
/*++dvdf
|
|
|
|
Routine Description:
|
|
|
|
This routine acquires the specified parallel port from the parallel
|
|
port arbiter ParPort via an IOCTL_INTERNAL_PARALLEL_PORT_ALLOCATE.
|
|
|
|
Arguments:
|
|
|
|
PortDeviceObject - points to the ParPort device to be acquired
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - if the port was successfully acquired
|
|
!STATUS_SUCCESS - otherwise
|
|
|
|
--*/
|
|
{
|
|
LARGE_INTEGER localTimeout;
|
|
|
|
if( Timeout ) {
|
|
localTimeout = *Timeout; // caller specified
|
|
} else {
|
|
localTimeout = AcquirePortTimeout; // driver global variable default
|
|
}
|
|
|
|
return ParBuildSendInternalIoctl(IOCTL_INTERNAL_PARALLEL_PORT_ALLOCATE,
|
|
PortDeviceObject, NULL, 0, NULL, 0, &localTimeout);
|
|
}
|
|
|
|
NTSTATUS
|
|
ParReleasePort(
|
|
IN PDEVICE_OBJECT PortDeviceObject
|
|
)
|
|
/*++dvdf
|
|
|
|
Routine Description:
|
|
|
|
This routine releases the specified parallel port back to the the parallel
|
|
port arbiter ParPort via an IOCTL_INTERNAL_PARALLEL_PORT_FREE.
|
|
|
|
Arguments:
|
|
|
|
PortDeviceObject - points to the ParPort device to be released
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - if the port was successfully released
|
|
!STATUS_SUCCESS - otherwise
|
|
|
|
--*/
|
|
{
|
|
return ParBuildSendInternalIoctl(IOCTL_INTERNAL_PARALLEL_PORT_FREE,
|
|
PortDeviceObject, NULL, 0, NULL, 0, NULL);
|
|
}
|
|
|
|
NTSTATUS
|
|
ParInit1284_3Bus(
|
|
IN PDEVICE_OBJECT PortDeviceObject
|
|
)
|
|
/*++dvdf
|
|
|
|
Routine Description:
|
|
|
|
This routine reinitializes the 1284.3 daisy chain "bus" via an
|
|
IOCTL_INTERNAL_INIT_1284_3_BUS sent to the ParPort device to
|
|
reinitialize.
|
|
|
|
Reinitializing the 1284.3 bus assigns addresses [0..3] to the
|
|
daisy chain devices based on their position in the 1284.3 daisy chain.
|
|
Address 0 is closest to the host port and address 3 is closest to the
|
|
end of the chain.
|
|
|
|
New devices must be assigned an address before they will respond to
|
|
1284.3 SELECT and DESELECT commands.
|
|
|
|
A 1284.3 daisy chain device whose position in the 1284.3 daisy chain
|
|
has changed due to the addition or removal of another 1284.3 daisy
|
|
chain device between the existing device and the host port will be
|
|
assigned a new addresses based on its new position in the chain.
|
|
|
|
Preconditions:
|
|
|
|
Caller must have already Acquired the Port via ParAcquirePort()prior
|
|
to calling this function.
|
|
|
|
Preconditions:
|
|
|
|
Caller still owns the port after calling this function and is responsible
|
|
for freeing the port when it is no longer required via ParReleasePort().
|
|
|
|
Arguments:
|
|
|
|
PortDeviceObject - points to the ParPort that the device is connected to.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - if the initialization was successful
|
|
|
|
!STATUS_SUCCESS - otherwise
|
|
|
|
--*/
|
|
{
|
|
return ParBuildSendInternalIoctl(IOCTL_INTERNAL_INIT_1284_3_BUS,
|
|
PortDeviceObject, NULL, 0, NULL, 0, NULL);
|
|
}
|
|
|
|
|
|
VOID
|
|
ParMarkPdoHardwareGone(
|
|
IN PDEVICE_EXTENSION Extension
|
|
)
|
|
/*++dvdf
|
|
|
|
Routine Description:
|
|
|
|
This routine is called to mark a device as "Hardware Gone", i.e., the
|
|
hardware associated with this device is no longer there.
|
|
|
|
- set DeviceState flag so that we know the device is no longer present
|
|
- mark extension so FDO no longer reports the device to PnP during BusRelation query
|
|
- delete symbolic link
|
|
- delete registry Parallelx -> LPTy mapping
|
|
|
|
Arguments:
|
|
|
|
Extension - points to the device extension of the device that has gone away
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Mark our extension so that we know our hardware is gone.
|
|
//
|
|
Extension->DeviceStateFlags = PAR_DEVICE_HARDWARE_GONE;
|
|
|
|
//
|
|
// Mark our extension so that the ParClass FDO no longer reports us to PnP
|
|
// in response to QUERY_DEVICE_RELATIONS/BusRelations.
|
|
//
|
|
Extension->DeviceIdString[0] = 0;
|
|
|
|
//
|
|
// Cleanup Symbolic Link and Registry
|
|
//
|
|
if( Extension->CreatedSymbolicLink ) {
|
|
NTSTATUS status;
|
|
|
|
// Remove symbolic link NOW so that the name can be reused by another device
|
|
status = IoDeleteSymbolicLink( &Extension->SymbolicLinkName );
|
|
if( !NT_SUCCESS(status) ) {
|
|
ParDumpV( ("IoDeleteSymbolicLink FAILED for %wZ\n", &Extension->SymbolicLinkName) );
|
|
}
|
|
|
|
status = RtlDeleteRegistryValue(RTL_REGISTRY_DEVICEMAP, (PWSTR)L"PARALLEL PORTS", Extension->ClassName.Buffer);
|
|
|
|
// Remove our Parallelx -> LPTy mapping from HKLM\HARDWARE\DEVICEMAP\PARALLEL PORTS
|
|
if( !NT_SUCCESS(status) ) {
|
|
ParDumpV( ("RtlDeleteRegistryValue FAILED for PARALLEL PORTS %wZ->%wZ\n",
|
|
&Extension->ClassName, &Extension->SymbolicLinkName) );
|
|
}
|
|
|
|
Extension->CreatedSymbolicLink = FALSE;
|
|
}
|
|
}
|
|
|
|
NTSTATUS
|
|
ParPnpNotifyTargetDeviceChange(
|
|
IN PDEVICE_INTERFACE_CHANGE_NOTIFICATION pDeviceInterfaceChangeNotification,
|
|
IN PDEVICE_OBJECT pDeviceObject
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine is the PlugPlay notification callback routine that
|
|
gets called when our ParPort gets QUERY_REMOVE, REMOVE, or
|
|
REMOVE_CANCELLED.
|
|
|
|
Arguments:
|
|
|
|
pDeviceInterfaceChangeNotification - Structure defining the change.
|
|
|
|
pDeviceObject - The ParClass ejected device object
|
|
receiving the notification
|
|
(context passed when we registered
|
|
for notification)
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - always
|
|
|
|
--*/
|
|
|
|
{
|
|
PDEVICE_EXTENSION Extension = (PDEVICE_EXTENSION)pDeviceObject->DeviceExtension;
|
|
|
|
ParDump2(PARDUMP_PNP_PARPORT,
|
|
("ParPnpNotifyTargetDeviceChange(...): "
|
|
"%x %wZ received PLUGPLAY Notification for ParPort Device\n",
|
|
pDeviceObject, &Extension->SymbolicLinkName) );
|
|
|
|
if(IsEqualGUID( (LPGUID)&(pDeviceInterfaceChangeNotification->Event),
|
|
(LPGUID)&GUID_TARGET_DEVICE_QUERY_REMOVE)) {
|
|
|
|
//
|
|
// Our ParPort is going to receive a QUERY_REMOVE
|
|
//
|
|
|
|
|
|
ParDump2(PARDUMP_PNP_PARPORT,
|
|
("ParPnpNotifyTargetDeviceChange(...): Our ParPort will receive QUERY_REMOVE\n") );
|
|
|
|
ExAcquireFastMutex(&Extension->OpenCloseMutex);
|
|
|
|
if (Extension->OpenCloseRefCount > 0) {
|
|
//
|
|
// someone has an open handle to us, do nothing,
|
|
// Our ParPort should fail QUERY_REMOVE because we
|
|
// still have a handle to it
|
|
//
|
|
DDPnP1(("## TargetQueryRemoveNotification - %wZ - keep handle open\n",&Extension->SymbolicLinkName));
|
|
|
|
ParDump2(PARDUMP_PNP_PARPORT,
|
|
("ParPnpNotifyTargetDeviceChange(...): Someone has an open handle to us, "
|
|
"KEEP our handle to ParPort\n") );
|
|
|
|
} else if(Extension->PortDeviceFileObject) {
|
|
//
|
|
// close our handle to ParPort to prevent us
|
|
// from blocking our ParPort from succeeding
|
|
// its QUERY_REMOVE
|
|
//
|
|
DDPnP1(("## TargetQueryRemoveNotification - %wZ - close handle\n",&Extension->SymbolicLinkName));
|
|
|
|
ParDump2(PARDUMP_PNP_PARPORT,
|
|
("ParPnpNotifyTargetDeviceChange(...): no one has an open handle to us, "
|
|
"CLOSE our handle to ParPort\n") );
|
|
ObDereferenceObject(Extension->PortDeviceFileObject);
|
|
Extension->PortDeviceFileObject = NULL;
|
|
|
|
//
|
|
// Set DeviceStateFlags accordingly so we handle
|
|
// IRPs properly while waiting to see if our ParPort gets
|
|
// REMOVE or REMOVE_CANCELLED
|
|
//
|
|
|
|
// We expect to be deleted, our parport is in a remove pending state
|
|
// Extension->DeviceStateFlags |= PAR_DEVICE_DELETE_PENDING;
|
|
Extension->DeviceStateFlags |= PAR_DEVICE_PORT_REMOVE_PENDING;
|
|
}
|
|
|
|
ExReleaseFastMutex(&Extension->OpenCloseMutex);
|
|
|
|
} else if(IsEqualGUID( (LPGUID)&(pDeviceInterfaceChangeNotification->Event),
|
|
(LPGUID)&GUID_TARGET_DEVICE_REMOVE_COMPLETE)) {
|
|
|
|
DDPnP1(("## TargetRemoveCompleteNotification - %wZ\n",&Extension->SymbolicLinkName));
|
|
|
|
//
|
|
// Our ParPort is gone, clean up
|
|
//
|
|
Extension->ParPortDeviceGone = TRUE;
|
|
|
|
//
|
|
// First, clean up any worker thread
|
|
//
|
|
if(Extension->ThreadObjectPointer) {
|
|
|
|
// set the flag for the worker thread to kill itself
|
|
Extension->TimeToTerminateThread = TRUE;
|
|
|
|
// wake up the thread so it can kill itself
|
|
KeReleaseSemaphore(&Extension->RequestSemaphore, 0, 1, FALSE);
|
|
|
|
// allow thread to get past PauseEvent so it can kill self
|
|
KeSetEvent(&Extension->PauseEvent, 0, TRUE);
|
|
|
|
// wait for the thread to die
|
|
KeWaitForSingleObject(Extension->ThreadObjectPointer, UserRequest, KernelMode, FALSE, NULL);
|
|
|
|
// allow the system to release the thread object
|
|
ObDereferenceObject(Extension->ThreadObjectPointer);
|
|
|
|
// note that we no longer have a worker thread
|
|
Extension->ThreadObjectPointer = NULL;
|
|
}
|
|
|
|
if( Extension->DeviceIdString[0] == 0 ) {
|
|
// this is a PODO, PnP doesn't know about us, so kill self now
|
|
PDEVICE_EXTENSION FdoExtension = Extension->ParClassFdo->DeviceExtension;
|
|
ExAcquireFastMutex(&FdoExtension->DevObjListMutex);
|
|
ParKillDeviceObject(pDeviceObject);
|
|
ExReleaseFastMutex(&FdoExtension->DevObjListMutex);
|
|
} else {
|
|
// this is a PDO, note that our hardware is gone and wait for PnP system
|
|
// to send us a REMOVE
|
|
PDEVICE_EXTENSION FdoExtension;
|
|
Extension->DeviceStateFlags = PAR_DEVICE_HARDWARE_GONE;
|
|
Extension->DeviceIdString[0] = 0;
|
|
|
|
//
|
|
// remove symbolic link NOW in case the interface returns before PnP gets
|
|
// around to sending us a QUERY_DEVICE_RELATIONS/BusRelations followed by
|
|
// a REMOVE_DEVICE
|
|
//
|
|
if( Extension->CreatedSymbolicLink ) {
|
|
NTSTATUS status;
|
|
status = IoDeleteSymbolicLink( &Extension->SymbolicLinkName );
|
|
if( !NT_SUCCESS(status) ) {
|
|
ParDump2(PARDUMP_PNP_PARPORT,
|
|
("IoDeleteSymbolicLink FAILED for %wZ\n", &Extension->SymbolicLinkName) );
|
|
}
|
|
status = RtlDeleteRegistryValue(RTL_REGISTRY_DEVICEMAP, (PWSTR)L"PARALLEL PORTS", Extension->ClassName.Buffer);
|
|
if( !NT_SUCCESS(status) ) {
|
|
ParDump2(PARDUMP_PNP_PARPORT,
|
|
("RtlDeleteRegistryValue FAILED for PARALLEL PORTS %wZ->%wZ\n",
|
|
&Extension->ClassName, &Extension->SymbolicLinkName) );
|
|
}
|
|
Extension->CreatedSymbolicLink = FALSE;
|
|
}
|
|
|
|
// tell PnP that the set of ParClass enumerated PDOs has changed
|
|
FdoExtension = (PDEVICE_EXTENSION)(Extension->ParClassFdo->DeviceExtension);
|
|
IoInvalidateDeviceRelations(FdoExtension->PhysicalDeviceObject, BusRelations);
|
|
}
|
|
|
|
} else if(IsEqualGUID( (LPGUID)&(pDeviceInterfaceChangeNotification->Event),
|
|
(LPGUID)&GUID_TARGET_DEVICE_REMOVE_CANCELLED)) {
|
|
|
|
//
|
|
// Our ParPort is back online (REMOVE_CANCELLED)
|
|
//
|
|
|
|
DDPnP1(("## TargetRemoveCancelledNotification - %wZ\n",&Extension->SymbolicLinkName));
|
|
|
|
ParDump2(PARDUMP_PNP_PARPORT,
|
|
("ParPnpNotifyTargetDeviceChange(...): Our ParPort completed a REMOVE_CANCELLED\n") );
|
|
|
|
ExAcquireFastMutex(&Extension->OpenCloseMutex);
|
|
|
|
if( !Extension->PortDeviceFileObject ) {
|
|
|
|
//
|
|
// we dropped our connection to our ParPort prior to
|
|
// our ParPort receiving QUERY_REMOVE, reestablish
|
|
// a FILE connection and resume operation
|
|
//
|
|
|
|
NTSTATUS status;
|
|
PFILE_OBJECT portDeviceFileObject;
|
|
PDEVICE_OBJECT portDeviceObject;
|
|
|
|
ParDump2(PARDUMP_PNP_PARPORT,
|
|
("ParPnpNotifyTargetDeviceChange(...): reopening file against our ParPort\n") );
|
|
|
|
status = IoGetDeviceObjectPointer(&Extension->PortSymbolicLinkName,
|
|
STANDARD_RIGHTS_ALL,
|
|
&portDeviceFileObject,
|
|
&portDeviceObject);
|
|
|
|
if(NT_SUCCESS(status) && portDeviceFileObject && portDeviceObject) {
|
|
|
|
// save REFERENCED PFILE_OBJECT in our device extension
|
|
Extension->PortDeviceFileObject = portDeviceFileObject;
|
|
// our ParPort device object should not have changed
|
|
ASSERT(Extension->PortDeviceObject == portDeviceObject);
|
|
|
|
} else {
|
|
|
|
ParDump2(PARDUMP_PNP_PARPORT,
|
|
("In ParPnpNotifyTargetDeviceChange(...): Unable to reopen FILE against our ParPort\n") );
|
|
|
|
//
|
|
// Unable to reestablish connection? Inconceivable!
|
|
//
|
|
ASSERT(FALSE);
|
|
}
|
|
}
|
|
|
|
//
|
|
// set DeviceStateFlags accordingly to resume processing IRPs
|
|
//
|
|
Extension->DeviceStateFlags &= ~PAR_DEVICE_PORT_REMOVE_PENDING;
|
|
|
|
ExReleaseFastMutex(&Extension->OpenCloseMutex);
|
|
|
|
} else {
|
|
ParDump2(PARDUMP_PNP_PARPORT,
|
|
("In ParPnpNotifyTargetDeviceChange(...): Unrecognized GUID_TARGET_DEVICE type\n") );
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// dvdf - former devobj.c follows
|
|
//
|
|
|
|
VOID
|
|
ParMakeClassNameFromNumber(
|
|
IN ULONG Number,
|
|
OUT PUNICODE_STRING ClassName
|
|
)
|
|
/*++dvdf
|
|
|
|
Routine Description:
|
|
|
|
This routine creates a ClassName for a ParClass PODO of the form:
|
|
L"\Device\ParallelN" where N is the wide string representation
|
|
of the 'Number' parameter.
|
|
|
|
Note: On success ClassName->Buffer points to allocated pool.
|
|
The caller is responsible for freeing this allocation when
|
|
it is no longer required.
|
|
|
|
Note: The returned ClassName->Buffer is UNICODE_NULL terminated.
|
|
|
|
Arguments:
|
|
|
|
Number - Supplies the number.
|
|
|
|
ClassName - returns classname for device object on success,
|
|
(ClassName->Buffer == NULL) indicates failure
|
|
|
|
Return Value:
|
|
|
|
None - caller determines success or failure by examining ClassName->Buffer.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
UNICODE_STRING digits;
|
|
WCHAR digitsBuffer[10];
|
|
UNICODE_STRING prefix;
|
|
|
|
PAGED_CODE();
|
|
|
|
// initialize ClassName to failure state
|
|
RtlInitUnicodeString(ClassName, NULL);
|
|
|
|
|
|
// create prefix
|
|
RtlInitUnicodeString(&prefix, (PWSTR)L"\\Device\\Parallel");
|
|
|
|
|
|
// create suffix
|
|
digits.Length = 0;
|
|
digits.MaximumLength = sizeof(digitsBuffer);
|
|
digits.Buffer = digitsBuffer;
|
|
status = RtlIntegerToUnicodeString(Number, 10, &digits);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
return;
|
|
}
|
|
|
|
|
|
// calculate required space, allocate paged pool, and zero buffer
|
|
ClassName->MaximumLength = (USHORT)(prefix.Length + digits.Length + sizeof(WCHAR));
|
|
ClassName->Buffer = ExAllocatePool(PagedPool, ClassName->MaximumLength);
|
|
if( !ClassName->Buffer ) {
|
|
// unable to allocate pool, set ClassName to failure state and return
|
|
RtlInitUnicodeString(ClassName, NULL);
|
|
return;
|
|
}
|
|
RtlZeroMemory(ClassName->Buffer, ClassName->MaximumLength);
|
|
|
|
|
|
// try to catenate prefix and suffix in buffer to form ClassName
|
|
status = RtlAppendUnicodeStringToString(ClassName, &prefix);
|
|
if( !NT_SUCCESS(status) ) {
|
|
// error on prefix, release buffer, set ClassName to error state
|
|
RtlFreeUnicodeString(ClassName);
|
|
} else {
|
|
// prefix ok, try appending suffix
|
|
status = RtlAppendUnicodeStringToString(ClassName, &digits);
|
|
if( !NT_SUCCESS(status) ) {
|
|
// error on suffix, release buffer, set ClassName to failure state
|
|
RtlFreeUnicodeString(ClassName);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
ParMakeDotClassNameFromBaseClassName(
|
|
IN PUNICODE_STRING BaseClassName,
|
|
IN ULONG Number,
|
|
OUT PUNICODE_STRING DotClassName
|
|
)
|
|
/*++dvdf - code complete - compiles clean - not tested
|
|
|
|
Routine Description:
|
|
|
|
|
|
This routine creates a ClassName for a ParClass PDO of the form:
|
|
L"\Device\ParallelN.M" where L"\Device\ParallelN" is the
|
|
BaseClassName and M is the wide string representation of
|
|
the 'Number' parameter. The returned value is intended to be
|
|
used as the ClassName of a ParClass 1284.3 Daisy Chain device
|
|
or a ParClass End-Of-Chain PnP device. BaseClassName is not
|
|
modified.
|
|
|
|
Note: On success DotClassName->Buffer points to allocated pool.
|
|
The caller is responsible for freeing this allocation when
|
|
it is no longer required.
|
|
|
|
Note: The returned DotClassName->Buffer is UNICODE_NULL terminated.
|
|
|
|
Arguments:
|
|
|
|
BaseClassName - points to the ClassName of the PODO for the Raw
|
|
host port to which this device is connected
|
|
|
|
Number - supplies the 1284.3 daisy chain device ID [0..3] for
|
|
a 1284.3 daisy chain device or 4 for an
|
|
End-Of-Chain PnP device.
|
|
|
|
DotClassName - returns the ClassName to be used for the PDO on success,
|
|
(DotClassName->Buffer == NULL) indicates failure
|
|
|
|
Return Value:
|
|
|
|
None - caller determines success or failure by examining DotClassName->Buffer.
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
UNICODE_STRING digits;
|
|
UNICODE_STRING dot;
|
|
WCHAR digitsBuffer[10];
|
|
|
|
PAGED_CODE();
|
|
|
|
if( Number > DOT3_LEGACY_ZIP_ID ) {
|
|
// 0..3 are Daisy Chain devices, 4 is End-Of-Chain device, 5 is Legacy Zip
|
|
RtlInitUnicodeString(DotClassName, NULL);
|
|
return;
|
|
}
|
|
|
|
RtlInitUnicodeString(&dot, (PWSTR)L".");
|
|
|
|
digits.Length = 0;
|
|
digits.MaximumLength = sizeof(digitsBuffer);
|
|
digits.Buffer = digitsBuffer;
|
|
status = RtlIntegerToUnicodeString(Number, 10, &digits);
|
|
if ( !NT_SUCCESS(status) ) {
|
|
RtlInitUnicodeString(DotClassName, NULL);
|
|
return;
|
|
}
|
|
|
|
DotClassName->MaximumLength = (USHORT)(BaseClassName->Length + digits.Length + dot.Length + 2*sizeof(UNICODE_NULL));
|
|
DotClassName->Buffer = ExAllocatePool(PagedPool, DotClassName->MaximumLength);
|
|
if (!DotClassName->Buffer) {
|
|
RtlInitUnicodeString(DotClassName, NULL);
|
|
return;
|
|
}
|
|
|
|
RtlZeroMemory(DotClassName->Buffer, DotClassName->MaximumLength);
|
|
|
|
RtlAppendUnicodeStringToString(DotClassName, BaseClassName);
|
|
RtlAppendUnicodeStringToString(DotClassName, &dot);
|
|
RtlAppendUnicodeStringToString(DotClassName, &digits);
|
|
|
|
return;
|
|
}
|
|
|
|
VOID
|
|
ParAcquireListMutexAndKillDeviceObject(
|
|
IN PDEVICE_OBJECT Fdo,
|
|
IN PDEVICE_OBJECT DevObj
|
|
)
|
|
/*++dvdf - code complete - compiles clean - not tested
|
|
|
|
Routine Description:
|
|
|
|
This function provides a wrapper around ParKillDeviceObject() that
|
|
handles acquiring and releasing the Mutex that protects the list
|
|
of ParClass created PDOs and PODOs.
|
|
|
|
ParKillDeviceObject() requires that its caller hold the FDO ListMutex.
|
|
|
|
Arguments:
|
|
|
|
Fdo - points to the ParClass FDO (The Mutex resides in the FDO extension)
|
|
|
|
DevObj - points to the DeviceObject to be killed
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
|
|
|
|
PAGED_CODE();
|
|
|
|
ExAcquireFastMutex(&fdoExt->DevObjListMutex);
|
|
ParKillDeviceObject(DevObj);
|
|
ExReleaseFastMutex(&fdoExt->DevObjListMutex);
|
|
}
|
|
|
|
VOID
|
|
ParAddDevObjToFdoList(
|
|
IN PDEVICE_OBJECT DevObj
|
|
)
|
|
/*++dvdf - code complete - compiles clean - not tested
|
|
|
|
Routine Description:
|
|
|
|
This function adds a PDO or PODO to the list of ParClass
|
|
created PODOs and PDOs. The DevObj is added to the front
|
|
of the list.
|
|
|
|
Arguments:
|
|
|
|
DevObj - points to the DeviceObject (PDO or PODO) to be added to the list
|
|
|
|
Return Value:
|
|
|
|
None - This function can not fail.
|
|
|
|
--*/
|
|
{
|
|
PDEVICE_EXTENSION devObjExt = DevObj->DeviceExtension;
|
|
PDEVICE_EXTENSION fdoExt = devObjExt->ParClassFdo->DeviceExtension;
|
|
|
|
PAGED_CODE();
|
|
|
|
ExAcquireFastMutex(&fdoExt->DevObjListMutex);
|
|
devObjExt->Next = fdoExt->ParClassPdo;
|
|
fdoExt->ParClassPdo = DevObj;
|
|
ExReleaseFastMutex(&fdoExt->DevObjListMutex);
|
|
}
|
|
|