1411 lines
47 KiB
C
1411 lines
47 KiB
C
//+-------------------------------------------------------------------------
|
||
//
|
||
// Microsoft Windows
|
||
//
|
||
// Copyright (C) Microsoft Corporation, 1998 - 1999
|
||
//
|
||
// File: devobj.c
|
||
//
|
||
//--------------------------------------------------------------------------
|
||
|
||
// this file contains functions to create, initialize, manage, and destroy ParClass device objects
|
||
|
||
#include "pch.h"
|
||
|
||
extern WCHAR ParInt2Wchar[];
|
||
|
||
VOID
|
||
ParMakeClassNameFromPortLptName(
|
||
IN PUNICODE_STRING PortSymbolicLinkName,
|
||
OUT PUNICODE_STRING ClassName
|
||
)
|
||
/*
|
||
|
||
Get LPTx name from ParPort device and use this name to construct the \Device\Parallely name.
|
||
|
||
y = x-1 (i.e., LPT3 => \Device\Parallel2)
|
||
|
||
*/
|
||
{
|
||
NTSTATUS status;
|
||
PDEVICE_OBJECT portDeviceObject;
|
||
PFILE_OBJECT portDeviceFileObject;
|
||
PWSTR portName;
|
||
ULONG portNumber;
|
||
LONG count;
|
||
UNICODE_STRING str;
|
||
|
||
// Get a pointer to the ParPort device
|
||
status = IoGetDeviceObjectPointer(PortSymbolicLinkName, STANDARD_RIGHTS_ALL,
|
||
&portDeviceFileObject, &portDeviceObject);
|
||
if( !NT_SUCCESS(status) ) {
|
||
ParDump2(PARPNP1, ("devobj::ParMakeClassNameFromPortLptName - unable to get handle to parport device\n"));
|
||
return;
|
||
}
|
||
|
||
// Get LPTx portName from ParPort device
|
||
portName = ParGetPortLptName( portDeviceObject );
|
||
|
||
// Done with handle
|
||
ObDereferenceObject( portDeviceFileObject );
|
||
|
||
// Did we get a portName?
|
||
if( 0 == portName ) {
|
||
ParDump2(PARPNP1, ("devobj::ParMakeClassNameFromPortLptName - unable to get portName from parport device\n"));
|
||
return;
|
||
}
|
||
|
||
// Verify that the portname looks like LPTx where x is a number
|
||
ParDump2(PARPNP1, ("devobj::ParMakeClassNameFromPortLptName - portName = <%S>\n", portName));
|
||
|
||
if( portName[0] != L'L' || portName[1] != L'P' || portName[2] != L'T' ) {
|
||
ParDump2(PARPNP1, ("devobj::ParMakeClassNameFromPortLptName - name prefix doesn't look like LPT\n"));
|
||
return;
|
||
}
|
||
|
||
// prefix is LPT, check for integer suffix with value > 0
|
||
RtlInitUnicodeString( &str, (PWSTR)&portName[3] );
|
||
|
||
status = RtlUnicodeStringToInteger( &str, 10, &portNumber );
|
||
if( !NT_SUCCESS( status ) ) {
|
||
ParDump2(PARPNP1, ("devobj::ParMakeClassNameFromPortLptName - name suffix doesn't look like an integer\n"));
|
||
return;
|
||
}
|
||
|
||
if( portNumber == 0 ) {
|
||
ParDump2(PARPNP1, ("devobj::ParMakeClassNameFromPortLptName - name suffix == 0 - FAIL - Invalid value\n"));
|
||
return;
|
||
}
|
||
|
||
ParDump2(PARPNP1, ("devobj::ParMakeClassNameFromPortLptName - LPT name suffix= %d\n", portNumber));
|
||
|
||
// Build \Device\Parallely name from LPTx name
|
||
ParMakeClassNameFromNumber( portNumber-1, ClassName );
|
||
|
||
ParDump2(PARPNP1, ("devobj::ParMakeClassNameFromPortLptName - portName=<%S> className=<%wZ>\n",
|
||
portName, ClassName));
|
||
|
||
}
|
||
|
||
// return TRUE if given a pointer to a ParClass PODO, FALSE otherwise
|
||
BOOLEAN
|
||
ParIsPodo(PDEVICE_OBJECT DevObj) {
|
||
PDEVICE_EXTENSION devExt = DevObj->DeviceExtension;
|
||
|
||
if( !devExt->IsPdo ) {
|
||
// this is an FDO
|
||
return FALSE;
|
||
}
|
||
|
||
// still here? - this is either a PODO or a PDO
|
||
|
||
if( devExt->DeviceIdString[0] != 0 ) {
|
||
// this device object has a device ID string - It's a PDO
|
||
return FALSE;
|
||
}
|
||
|
||
// still here? - this is either a PODO, or a PDO marked "hardware gone" that
|
||
// is simply waiting for PnP to send it a REMOVE.
|
||
|
||
if( devExt->DeviceStateFlags & PAR_DEVICE_HARDWARE_GONE) {
|
||
// this is a PDO marked "hardware gone"
|
||
return FALSE;
|
||
}
|
||
|
||
// still here? - this is a PODO
|
||
return TRUE;
|
||
}
|
||
|
||
#if USE_TEMP_FIX_FOR_MULTIPLE_INTERFACE_ARRIVAL
|
||
//
|
||
// Temp fix for PnP calling us multiple times for the same interface arrival
|
||
//
|
||
//
|
||
// Return Value: Did we already process an interface arrival for this interface?
|
||
//
|
||
BOOLEAN
|
||
ParIsDuplicateInterfaceArrival(
|
||
IN PDEVICE_INTERFACE_CHANGE_NOTIFICATION NotificationStruct,
|
||
IN PDEVICE_OBJECT Fdo
|
||
)
|
||
{
|
||
PUNICODE_STRING portSymbolicLinkName = NotificationStruct->SymbolicLinkName;
|
||
PDEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
|
||
PDEVICE_OBJECT curDevObj;
|
||
PDEVICE_EXTENSION curDevExt;
|
||
|
||
PAGED_CODE();
|
||
|
||
ParDump2(PARPNP1, ("Enter ParIsDuplicateInterfaceArrival()\n"));
|
||
|
||
//
|
||
// Find the ParClass PODOs and compare the PortSymbolicLinkName in the
|
||
// device extension with the one in the interface arrival notification.
|
||
//
|
||
curDevObj = fdoExt->ParClassPdo;
|
||
while( curDevObj ) {
|
||
curDevExt = curDevObj->DeviceExtension;
|
||
if( ParIsPodo(curDevObj) ) {
|
||
ParDump2(PARPNP1, ("DevObj= %x IS PODO\n",curDevObj) );
|
||
ParDump2(PARPNP1, ("Comparing port symbolic link names\n"));
|
||
if( RtlEqualUnicodeString(portSymbolicLinkName,&curDevExt->PortSymbolicLinkName, FALSE) ) {
|
||
ParDump2(PARPNP1, ("MATCH! - Second Arrival of this Interface\n"));
|
||
return TRUE;
|
||
} else {
|
||
ParDump2(PARPNP1, ("NO match on port symbolic link name for interface arrival - keep searching\n"));
|
||
}
|
||
} else {
|
||
ParDump2(PARPNP1, ("DevObj= %x NOT a PODO\n",curDevObj) );
|
||
}
|
||
curDevObj = curDevExt->Next;
|
||
}
|
||
|
||
return FALSE;
|
||
}
|
||
#endif
|
||
|
||
NTSTATUS
|
||
ParPnpNotifyInterfaceChange(
|
||
IN PDEVICE_INTERFACE_CHANGE_NOTIFICATION NotificationStruct,
|
||
IN PDEVICE_OBJECT Fdo
|
||
)
|
||
/*++dvdf
|
||
|
||
Routine Description:
|
||
|
||
This routine is the PnP "interface change notification" callback routine.
|
||
|
||
This gets called on a ParPort triggered device interface arrival or removal.
|
||
- Interface arrival corresponds to a ParPort device being STARTed
|
||
- Interface removal corresponds to a ParPort device being REMOVEd
|
||
|
||
On arrival:
|
||
- Create the LPTx PODO (PlainOldDeviceObject) for legacy/raw port access.
|
||
- Query for an EOC (End-Of-Chain) PnP Device, create PDO if device found.
|
||
- Enumerate all 1284.3 DC (Daisy Chain) devices attached to the port.
|
||
|
||
This callback is a NO-OP for interface removal because all ParClass created
|
||
P[O]DOs register for PnP EventCategoryTargetDeviceChange callbacks and
|
||
use that callback to clean up when their associated ParPort device goes
|
||
away.
|
||
|
||
Arguments:
|
||
|
||
NotificationStruct - Structure defining the change.
|
||
|
||
Fdo - pointer to ParClass FDO
|
||
(supplied as the "context" when we
|
||
registered for this callback)
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - always, even if something goes wrong
|
||
|
||
--*/
|
||
{
|
||
PUNICODE_STRING portSymbolicLinkName = NotificationStruct->SymbolicLinkName;
|
||
PDEVICE_OBJECT legacyPodo;
|
||
PDEVICE_EXTENSION legacyExt;
|
||
// PDEVICE_OBJECT endOfChainPdo;
|
||
PDEVICE_OBJECT portDeviceObject;
|
||
PFILE_OBJECT portDeviceFileObject;
|
||
NTSTATUS status;
|
||
BOOLEAN foundNewDevice = FALSE;
|
||
// UCHAR dot3DeviceCount;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Verify that interface class is a ParPort device interface.
|
||
//
|
||
// Any other InterfaceClassGuid is an error, but let it go since
|
||
// it is not fatal to the machine.
|
||
//
|
||
if( !IsEqualGUID( (LPGUID)&(NotificationStruct->InterfaceClassGuid),
|
||
(LPGUID)&GUID_PARALLEL_DEVICE) ) {
|
||
ParDump2(PARERRORS, ("ParPnpNotifyInterfaceChange() - ERROR - Bad InterfaceClassGuid\n") );
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
//
|
||
// This callback is a NO-OP for interface removal.
|
||
//
|
||
// All ParClass DO's that depend on this interface register for target
|
||
// device change PnP notification on the ParPort device associated
|
||
// with this interface.
|
||
//
|
||
// Thus, all such ParClass DO's that depend on this interface will
|
||
// have already received target device change notification callbacks
|
||
// that the ParPort device associated with this interface is being
|
||
// removed prior to the arrival of this interface removal
|
||
// notification callback.
|
||
//
|
||
if(!IsEqualGUID( (LPGUID)&(NotificationStruct->Event),
|
||
(LPGUID)&GUID_DEVICE_INTERFACE_ARRIVAL )) {
|
||
ParDump2(PARPNP1, ("Interface Removal Notification\n") );
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
//
|
||
// A ParPort device has STARTed and we are the bus driver for the port.
|
||
// Continue...
|
||
//
|
||
|
||
#if USE_TEMP_FIX_FOR_MULTIPLE_INTERFACE_ARRIVAL
|
||
//
|
||
// Temp fix for PnP calling us multiple times for the same interface arrival
|
||
//
|
||
if( ParIsDuplicateInterfaceArrival(NotificationStruct, Fdo) ) {
|
||
ParDump2(PARERRORS, ("Duplicate Interface Arrival Notification - Returning/Ignoring\n") );
|
||
return STATUS_SUCCESS;
|
||
}
|
||
#endif
|
||
|
||
//
|
||
// Get a pointer to and create a FILE against the ParPort device
|
||
//
|
||
status = IoGetDeviceObjectPointer(portSymbolicLinkName, STANDARD_RIGHTS_ALL,
|
||
&portDeviceFileObject, &portDeviceObject);
|
||
if( !NT_SUCCESS(status) ) {
|
||
ParDump2(PARERRORS, ("ParPnpNotifyInterfaceChange() - ERROR - unable to get device object port to ParPort device\n") );
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
//
|
||
// Acquire the ParPort device
|
||
//
|
||
status = ParAcquirePort(portDeviceObject, NULL);
|
||
if( !NT_SUCCESS(status) ) {
|
||
ParDump2(PARERRORS, ("ParPnpNotifyInterfaceChange() - ERROR - unable to acquire port\n") );
|
||
ObDereferenceObject(portDeviceFileObject);
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
//
|
||
// Create the Legacy LPTx PODO (PlainOldDeviceObject) for raw port access.
|
||
//
|
||
legacyPodo = ParCreateLegacyPodo(Fdo, portSymbolicLinkName);
|
||
if( !legacyPodo ) {
|
||
// If we can't create the legacyPodo, then nothing following will
|
||
// succeed, so bail out.
|
||
ParDump2(PARERRORS, ("ParPnpNotifyInterfaceChange() - ERROR - unable to create legacyPodo\n") );
|
||
ParReleasePort(portDeviceObject);
|
||
ObDereferenceObject(portDeviceFileObject);
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
|
||
//
|
||
// SUCCESS - Legacy PODO created
|
||
//
|
||
legacyExt = legacyPodo->DeviceExtension;
|
||
ParDump2(PARPNP1, ("devobj::ParPnpNotifyInterfaceChange - CREATED legacyPODO - <%wZ> <%wZ>\n",
|
||
&legacyExt->ClassName, &legacyExt->SymbolicLinkName) );
|
||
|
||
|
||
//
|
||
// Legacy PODO - add to list of ParClass created device objects
|
||
//
|
||
ParAddDevObjToFdoList(legacyPodo);
|
||
|
||
|
||
//
|
||
// release port and close our FILE to Parport device (our PODO and PDOs
|
||
// have their own FILEs open against parport)
|
||
//
|
||
ParReleasePort(portDeviceObject);
|
||
ObDereferenceObject(portDeviceFileObject);
|
||
|
||
|
||
//
|
||
// Tell PnP that we might have some new children
|
||
//
|
||
{
|
||
PDEVICE_EXTENSION fdoExt = Fdo->DeviceExtension;
|
||
ParDump2(PARPNP1, ("devobj::ParPnpNotifyInterfaceChange - calling IoInvalidateDeviceRelations(,BusRelations)\n") );
|
||
IoInvalidateDeviceRelations(fdoExt->PhysicalDeviceObject, BusRelations);
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
PDEVICE_OBJECT
|
||
ParCreateLegacyPodo(
|
||
IN PDEVICE_OBJECT Fdo,
|
||
IN PUNICODE_STRING PortSymbolicLinkName
|
||
)
|
||
/*++dvdf
|
||
|
||
Routine Description:
|
||
|
||
This routine creates the LPTx PODO (PlainOldDeviceObject) that is
|
||
used for legacy/raw port access.
|
||
|
||
- create a classname of the form "\Device\ParallelN"
|
||
- create device object
|
||
- initialize device object and extension
|
||
- create symbolic link
|
||
- register for PnP TargetDeviceChange notification
|
||
|
||
Arguments:
|
||
|
||
Fdo - pointer to ParClass FDO
|
||
|
||
PortSymbolicLinkName - symbolic link name of the ParPort device
|
||
|
||
Return Value:
|
||
|
||
PDEVICE_OBJECT - on success
|
||
NULL - otherwise
|
||
|
||
--*/
|
||
|
||
{
|
||
NTSTATUS status;
|
||
UNICODE_STRING className = {0,0,0};
|
||
PDEVICE_OBJECT legacyPodo;
|
||
PDEVICE_EXTENSION legacyExt;
|
||
PDRIVER_OBJECT driverObject = Fdo->DriverObject;
|
||
|
||
#define PAR_CLASSNAME_OFFSET 8
|
||
|
||
PAGED_CODE();
|
||
|
||
|
||
//
|
||
// Legacy PODO - try to build a \Device\Parallely classname based on the LPTx name
|
||
// retrieved from the ParPort device
|
||
//
|
||
ParMakeClassNameFromPortLptName(PortSymbolicLinkName, &className);
|
||
|
||
if( !className.Buffer ) {
|
||
//
|
||
// We failed to construct a ClassName from the Port's
|
||
// LPTx name - just make up a name
|
||
//
|
||
// Use an offset so that the name we make up doesn't collide
|
||
// with other ports
|
||
//
|
||
ParMakeClassNameFromNumber( PAR_CLASSNAME_OFFSET + g_NumPorts++, &className );
|
||
if( !className.Buffer ) {
|
||
// unable to create class name, bail out
|
||
return NULL;
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// Legacy PODO - create device object
|
||
//
|
||
status = ParCreateDevice(driverObject, sizeof(DEVICE_EXTENSION), &className,
|
||
FILE_DEVICE_PARALLEL_PORT, 0, TRUE, &legacyPodo);
|
||
|
||
if( !NT_SUCCESS( status ) ) {
|
||
//
|
||
// We failed to create a device, if failure was due to a name collision, then
|
||
// make up a new classname and try again
|
||
//
|
||
if( status == STATUS_OBJECT_NAME_COLLISION || status == STATUS_OBJECT_NAME_EXISTS ) {
|
||
// name collision - make up a new classname and try again
|
||
RtlFreeUnicodeString( &className );
|
||
ParMakeClassNameFromNumber( PAR_CLASSNAME_OFFSET + g_NumPorts++, &className );
|
||
if( !className.Buffer ) {
|
||
// unable to create class name, bail out
|
||
return NULL;
|
||
}
|
||
status = ParCreateDevice(driverObject, sizeof(DEVICE_EXTENSION), &className,
|
||
FILE_DEVICE_PARALLEL_PORT, 0, TRUE, &legacyPodo);
|
||
}
|
||
}
|
||
|
||
|
||
if( !NT_SUCCESS( status ) ) {
|
||
// unable to create device object, bail out
|
||
RtlFreeUnicodeString(&className);
|
||
ParLogError(driverObject, NULL, PhysicalZero, PhysicalZero,
|
||
0, 0, 0, 9, STATUS_SUCCESS, PAR_INSUFFICIENT_RESOURCES);
|
||
return NULL;
|
||
}
|
||
|
||
legacyExt = legacyPodo->DeviceExtension;
|
||
|
||
//
|
||
// Legacy PODO - initialize device object and extension
|
||
//
|
||
ParInitCommonDOPre(legacyPodo, Fdo, &className);
|
||
status = ParInitLegacyPodo(legacyPodo, PortSymbolicLinkName);
|
||
if( !NT_SUCCESS( status ) ) {
|
||
// initialization failed, clean up and bail out
|
||
ParAcquireListMutexAndKillDeviceObject(Fdo, legacyPodo);
|
||
return NULL;
|
||
}
|
||
|
||
//
|
||
// Note that we are a PODO in our extension
|
||
//
|
||
legacyExt->DeviceType = PAR_DEVTYPE_PODO;
|
||
|
||
|
||
ParInitCommonDOPost(legacyPodo);
|
||
|
||
//
|
||
// Legacy PODO - create symbolic link
|
||
//
|
||
if( legacyExt->SymbolicLinkName.Buffer ) {
|
||
|
||
status = IoCreateUnprotectedSymbolicLink(&legacyExt->SymbolicLinkName,
|
||
&legacyExt->ClassName);
|
||
|
||
if ( NT_SUCCESS(status) ) {
|
||
// note this in our extension for later cleanup
|
||
legacyExt->CreatedSymbolicLink = TRUE;
|
||
|
||
// Write symbolic link map info to the registry.
|
||
status = RtlWriteRegistryValue(RTL_REGISTRY_DEVICEMAP,
|
||
(PWSTR)L"PARALLEL PORTS",
|
||
legacyExt->ClassName.Buffer,
|
||
REG_SZ,
|
||
legacyExt->SymbolicLinkName.Buffer,
|
||
legacyExt->SymbolicLinkName.Length +
|
||
sizeof(WCHAR));
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
// unable to write map info to registry - continue anyway
|
||
ParLogError(legacyPodo->DriverObject, legacyPodo,
|
||
legacyExt->OriginalController, PhysicalZero,
|
||
0, 0, 0, 6, status, PAR_NO_DEVICE_MAP_CREATED);
|
||
}
|
||
|
||
} else {
|
||
|
||
// unable to create the symbolic link.
|
||
legacyExt->CreatedSymbolicLink = FALSE;
|
||
RtlFreeUnicodeString(&legacyExt->SymbolicLinkName);
|
||
ParLogError(legacyPodo->DriverObject, legacyPodo,
|
||
legacyExt->OriginalController, PhysicalZero,
|
||
0, 0, 0, 5, status, PAR_NO_SYMLINK_CREATED);
|
||
}
|
||
|
||
} else {
|
||
// extension does not contain a symbolic link name for us to use
|
||
legacyExt->CreatedSymbolicLink = FALSE;
|
||
}
|
||
|
||
if( FALSE == legacyExt->CreatedSymbolicLink ) {
|
||
// Couldn't create a symbolic Link
|
||
// clean up and bail out
|
||
ParAcquireListMutexAndKillDeviceObject(Fdo, legacyPodo);
|
||
return NULL;
|
||
}
|
||
|
||
//
|
||
// Legacy PODO - register for PnP TargetDeviceChange notification
|
||
//
|
||
status = IoRegisterPlugPlayNotification(EventCategoryTargetDeviceChange,
|
||
0,
|
||
(PVOID)legacyExt->PortDeviceFileObject,
|
||
driverObject,
|
||
(PDRIVER_NOTIFICATION_CALLBACK_ROUTINE) ParPnpNotifyTargetDeviceChange,
|
||
(PVOID)legacyPodo,
|
||
&legacyExt->NotificationHandle);
|
||
|
||
if( !NT_SUCCESS(status) ) {
|
||
// PnP registration for TargetDeviceChange notification failed,
|
||
// clean up and bail out
|
||
ParAcquireListMutexAndKillDeviceObject(Fdo, legacyPodo);
|
||
return NULL;
|
||
}
|
||
|
||
//
|
||
// Register for WMI
|
||
//
|
||
status = ParWmiPdoInitWmi( legacyPodo );
|
||
if( !NT_SUCCESS( status ) ) {
|
||
// WMI registration failed, clean up and bail out
|
||
ParAcquireListMutexAndKillDeviceObject(Fdo, legacyPodo);
|
||
return NULL;
|
||
} else {
|
||
//
|
||
// Note in our extension that we have registered for WMI
|
||
// so we can clean up later
|
||
//
|
||
legacyExt->PodoRegForWMI = TRUE;
|
||
}
|
||
|
||
return legacyPodo;
|
||
}
|
||
|
||
VOID
|
||
ParInitCommonDOPre(
|
||
IN PDEVICE_OBJECT DevObj,
|
||
IN PDEVICE_OBJECT Fdo,
|
||
IN PUNICODE_STRING ClassName
|
||
)
|
||
/*++dvdf - code complete - compiles clean - not tested
|
||
|
||
Routine Description:
|
||
|
||
This routine contains common initialization code for ParClass
|
||
created PDOs and PODOs that should be called before (Pre) the
|
||
device object type specific (PDO/PODO) intialization function
|
||
is called.
|
||
|
||
Arguments:
|
||
|
||
DevObj - points to the DeviceObject to be initialized
|
||
|
||
Fdo - points to the ParClass FDO
|
||
|
||
ClassName - points to the ClassName for the PDO/PODO
|
||
|
||
Return Value:
|
||
|
||
None - This function can not fail.
|
||
|
||
--*/
|
||
{
|
||
PDEVICE_EXTENSION extension;
|
||
|
||
PAGED_CODE();
|
||
|
||
// - we use buffered IO
|
||
// - force power dispatch at PASSIVE_LEVEL IRQL so we can page driver
|
||
// DevObj->Flags |= (DO_BUFFERED_IO | DO_POWER_PAGABLE);
|
||
DevObj->Flags |= DO_BUFFERED_IO; // RMT - should also set POWER_PAGABLE
|
||
|
||
// initialize extension to all zeros
|
||
extension = DevObj->DeviceExtension;
|
||
RtlZeroMemory(extension, sizeof(DEVICE_EXTENSION));
|
||
|
||
// used by debugger extension
|
||
extension->ExtensionSignature = PARCLASS_EXTENSION_SIGNATURE;
|
||
extension->ExtensionSignatureEnd = PARCLASS_EXTENSION_SIGNATURE;
|
||
|
||
// initialize synchronization and list mechanisms
|
||
ExInitializeFastMutex(&extension->OpenCloseMutex);
|
||
InitializeListHead(&extension->WorkQueue);
|
||
KeInitializeSemaphore(&extension->RequestSemaphore, 0, MAXLONG);
|
||
KeInitializeEvent(&extension->PauseEvent, NotificationEvent, TRUE);
|
||
|
||
|
||
// general info
|
||
extension->ClassName = *ClassName; // copy the struct
|
||
extension->DeviceObject = DevObj;
|
||
|
||
extension->EndOfChain = TRUE; // override later if this is a
|
||
extension->Ieee1284_3DeviceId = DOT3_END_OF_CHAIN_ID; // 1284.3 Daisy Chain device
|
||
|
||
extension->IsPdo = TRUE; // really means !FDO
|
||
extension->ParClassFdo = Fdo;
|
||
extension->BusyDelay = 0;
|
||
extension->BusyDelayDetermined = FALSE;
|
||
|
||
// timing constants
|
||
extension->TimerStart = PAR_WRITE_TIMEOUT_VALUE;
|
||
extension->IdleTimeout.QuadPart -= 250*10*1000; // 250 ms
|
||
extension->AbsoluteOneSecond.QuadPart = 10*1000*1000;
|
||
extension->OneSecond.QuadPart = -(extension->AbsoluteOneSecond.QuadPart);
|
||
|
||
// init IEEE 1284 protocol settings
|
||
ParInitializeExtension1284Info( extension );
|
||
}
|
||
|
||
NTSTATUS
|
||
ParInitLegacyPodo(
|
||
IN PDEVICE_OBJECT LegacyPodo,
|
||
IN PUNICODE_STRING PortSymbolicLinkName
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This function performs ParClass DeviceObject and DeviceExtension
|
||
initialization specific to ParClass Legacy PODOs (Plain Old Device
|
||
Objects). A Legacy PODO represents the "raw" parallel port and is
|
||
used by legacy drivers to communicate with parallel port connected
|
||
devices.
|
||
|
||
Precondition:
|
||
- ParInitCommonPre(...) must be called with this DeviceObject before
|
||
this function is called.
|
||
|
||
Postcondition:
|
||
- ParInitCommonPost(...) must be called with this DeviceObject after
|
||
this function is called.
|
||
|
||
On error, this routine defers cleaning up the LegacyPodo DeviceObject
|
||
and any pool allocations to the caller.
|
||
|
||
Arguments:
|
||
|
||
LegacyPodo - pointer to the legacy device object to initialize
|
||
|
||
PortSymbolicLinkName - symbolic link name of ParPort device
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - on success
|
||
STATUS_UNSUCCESSFUL - otherwise
|
||
|
||
--*/
|
||
{
|
||
NTSTATUS status = STATUS_SUCCESS;
|
||
PDEVICE_EXTENSION legacyExt = LegacyPodo->DeviceExtension;
|
||
PWSTR buffer = NULL;
|
||
PFILE_OBJECT portDeviceFileObject = NULL;
|
||
PDEVICE_OBJECT portDeviceObject = NULL;
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Get a pointer to and create a FILE against the ParPort device
|
||
//
|
||
// - We need a pointer to the ParPort DeviceObject so that we can send
|
||
// it IRPs.
|
||
//
|
||
// - We need a FILE against the DeviceObject to keep the ParPort
|
||
// DeviceObject from "going away" while we are using it.
|
||
//
|
||
// - Having an open FILE against the ParPort DeviceObject will also prevent
|
||
// PnP from doing a resource rebalance on the ParPort DeviceObject and
|
||
// changing its resources while we are holding pointers to the ParPort
|
||
// device's registers. This works because a ParPort DeviceObject fails
|
||
// PnP QUERY_STOP IRPs if anyone has an open FILE against it.
|
||
//
|
||
status = IoGetDeviceObjectPointer(PortSymbolicLinkName,
|
||
STANDARD_RIGHTS_ALL,
|
||
&portDeviceFileObject,
|
||
&portDeviceObject);
|
||
if( !NT_SUCCESS(status) ) {
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
//
|
||
// success - save pointer to ParPort DeviceObject and a copy of the
|
||
// REFERENCED pointer to the FILE created against the ParPort
|
||
// DeviceObject in our extension
|
||
//
|
||
legacyExt->PortDeviceObject = portDeviceObject;
|
||
legacyExt->PortDeviceFileObject = portDeviceFileObject;
|
||
|
||
//
|
||
// Save a copy of the ParPort SymbolicLinkName in our device extension.
|
||
//
|
||
buffer = ParCreateWideStringFromUnicodeString(PortSymbolicLinkName);
|
||
if( !buffer ) {
|
||
// unable to copy PortSymbolicLinkName, bail out
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
// copy ParPort SymbolicLinkName to our device extension
|
||
RtlInitUnicodeString(&legacyExt->PortSymbolicLinkName, buffer);
|
||
|
||
//
|
||
// make sure that IRPs sent to us have enough stack locations so that we
|
||
// can forward them to ParPort if needed
|
||
//
|
||
legacyExt->DeviceObject->StackSize = (CHAR)( legacyExt->PortDeviceObject->StackSize + 1 );
|
||
|
||
//
|
||
// Obtain PARALLEL_PORT_INFORMATION and PARALLEL_PNP_INFORMATION from
|
||
// the ParPort device and save it in our device extension
|
||
//
|
||
status = ParGetPortInfoFromPortDevice(legacyExt);
|
||
if (!NT_SUCCESS(status)) {
|
||
ParLogError(LegacyPodo->DriverObject, LegacyPodo, PhysicalZero, PhysicalZero,
|
||
0, 0, 0, 4, status, PAR_CANT_FIND_PORT_DRIVER);
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
|
||
if (legacyExt->OriginalController.HighPart == 0 &&
|
||
legacyExt->OriginalController.LowPart == (ULONG_PTR) legacyExt->Controller) {
|
||
|
||
legacyExt->UsePIWriteLoop = FALSE;
|
||
} else {
|
||
legacyExt->UsePIWriteLoop = TRUE;
|
||
}
|
||
|
||
return STATUS_SUCCESS;
|
||
}
|
||
|
||
VOID
|
||
ParInitCommonDOPost(
|
||
IN PDEVICE_OBJECT DevObj
|
||
)
|
||
/*++dvdf - code complete - compiles clean - not tested
|
||
|
||
Routine Description:
|
||
|
||
This routine contains common initialization code for ParClass
|
||
created PDOs and PODOs that should be called after (Post) the
|
||
device object type specific (PDO/PODO) intialization function
|
||
is called.
|
||
|
||
Arguments:
|
||
|
||
DevObj - points to the DeviceObject to be initialized
|
||
|
||
Return Value:
|
||
|
||
None - This function can not fail.
|
||
|
||
--*/
|
||
{
|
||
PAGED_CODE();
|
||
|
||
// Check the registry for parameter overrides
|
||
ParCheckParameters(DevObj->DeviceExtension);
|
||
|
||
// Tell the IO system that we are ready to receive IRPs
|
||
DevObj->Flags &= ~DO_DEVICE_INITIALIZING;
|
||
}
|
||
|
||
PDEVICE_OBJECT
|
||
ParDetectCreateEndOfChainPdo(
|
||
IN PDEVICE_OBJECT LegacyPodo
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Detect if an EndOfChain device is connected. If so, create a PDO for the
|
||
device and add the PDO to the list of ParClass children.
|
||
|
||
Arguments:
|
||
|
||
LegacyPodo - pointer to the legacy PODO for the port.
|
||
|
||
Return Value:
|
||
|
||
Pointer to the new PDO if device found
|
||
|
||
NULL otherwise
|
||
|
||
--*/
|
||
{
|
||
PDEVICE_OBJECT newPdo = ParDetectCreatePdo( LegacyPodo, DOT3_END_OF_CHAIN_ID, FALSE );
|
||
|
||
if( newPdo ) {
|
||
ParAddDevObjToFdoList( newPdo );
|
||
}
|
||
|
||
return newPdo;
|
||
}
|
||
|
||
PDEVICE_OBJECT
|
||
ParDetectCreatePdo(
|
||
IN PDEVICE_OBJECT LegacyPodo,
|
||
IN UCHAR Dot3Id,
|
||
IN BOOLEAN bStlDot3Id
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Detect if there is a 1284 device attached.
|
||
If a device is detected then create a PDO to represent the device.
|
||
|
||
Preconditions:
|
||
Caller has acquired the ParPort device
|
||
Caller has SELECTed the device
|
||
|
||
Postconditions:
|
||
ParPort device is still acquired
|
||
Device is still SELECTed
|
||
|
||
Arguments:
|
||
|
||
LegacyPodo - points to the Legacy PODO for the port
|
||
|
||
Dot3Id - 1284.3 daisy chain id
|
||
0..3 is daisy chain device
|
||
4 is end of chain device
|
||
|
||
Return Value:
|
||
|
||
PDEVICE_OBJECT - on success, points to the PDO we create
|
||
NULL - otherwise
|
||
|
||
--*/
|
||
{
|
||
PDEVICE_EXTENSION legacyExt = LegacyPodo->DeviceExtension;
|
||
PDRIVER_OBJECT driverObject = LegacyPodo->DriverObject;
|
||
PDEVICE_OBJECT fdo = legacyExt->ParClassFdo;
|
||
UNICODE_STRING className = {0,0,NULL};
|
||
NTSTATUS status;
|
||
PCHAR deviceIdString = NULL;
|
||
ULONG deviceIdLength;
|
||
PDEVICE_OBJECT newDevObj = NULL;
|
||
PDEVICE_EXTENSION newDevExt;
|
||
ULONG idTry = 1;
|
||
ULONG maxIdTries = 3;
|
||
|
||
BOOLEAN useModifiedClassName = FALSE;
|
||
UNICODE_STRING modifiedClassName;
|
||
UNICODE_STRING suffix;
|
||
UNICODE_STRING dash;
|
||
WCHAR suffixBuffer[10];
|
||
ULONG number = 0;
|
||
ULONG maxNumber = 256;
|
||
ULONG newLength;
|
||
|
||
|
||
PAGED_CODE();
|
||
|
||
//
|
||
// Query for PnP device
|
||
//
|
||
while( (NULL == deviceIdString) && (idTry <= maxIdTries) ) {
|
||
deviceIdString = Par3QueryDeviceId(legacyExt, NULL, 0, &deviceIdLength, FALSE, bStlDot3Id);
|
||
if( NULL == deviceIdString ) {
|
||
ParDump2(PARPNP1, ("devobj::ParDetectCreatePdo - no 1284 ID on try %d\n", idTry) );
|
||
KeStallExecutionProcessor(1);
|
||
++idTry;
|
||
} else {
|
||
ParDump2(PARPNP1, ("devobj::ParDetectCreatePdo - devIdString=<%s> on try %d\n", deviceIdString, idTry) );
|
||
}
|
||
}
|
||
|
||
if( !deviceIdString ) {
|
||
ParDump2(PARPNP1, ("devobj::ParDetectCreatePdo - no 1284 ID, bail out\n") );
|
||
return NULL;
|
||
}
|
||
|
||
|
||
//
|
||
// Found PnP Device, create a PDO to represent the device
|
||
// - create classname
|
||
// - create device object
|
||
// - initialize device object and extension
|
||
// - create symbolic link
|
||
// - register for PnP TargetDeviceChange notification
|
||
//
|
||
|
||
|
||
//
|
||
// Create a class name of the form \Device\ParallelN,
|
||
//
|
||
ParMakeDotClassNameFromBaseClassName(&legacyExt->ClassName, Dot3Id, &className);
|
||
if( !className.Buffer ) {
|
||
// unable to construct ClassName
|
||
ParDump2(PARPNP1, ("devobj::ParDetectCreatePdo - unable to construct ClassName for device\n") );
|
||
ExFreePool(deviceIdString);
|
||
return NULL;
|
||
}
|
||
|
||
|
||
//
|
||
// create device object
|
||
//
|
||
status = ParCreateDevice(driverObject, sizeof(DEVICE_EXTENSION), &className,
|
||
FILE_DEVICE_PARALLEL_PORT, 0, TRUE, &newDevObj);
|
||
|
||
///
|
||
if( status == STATUS_OBJECT_NAME_COLLISION ) {
|
||
//
|
||
// old name is still in use, appending a suffix and try again
|
||
//
|
||
|
||
ParDump2(PARPNP1, ("devobj::ParDetectCreatePdo - ParCreateDevice FAILED due to name Collision on <%wZ> - retry\n", &className));
|
||
|
||
useModifiedClassName = TRUE;
|
||
|
||
suffix.Length = 0;
|
||
suffix.MaximumLength = sizeof(suffixBuffer);
|
||
suffix.Buffer = suffixBuffer;
|
||
|
||
RtlInitUnicodeString( &dash, (PWSTR)L"-" );
|
||
|
||
newLength = className.MaximumLength + 5*sizeof(WCHAR); // L"-XXX" suffix
|
||
modifiedClassName.Length = 0;
|
||
modifiedClassName.MaximumLength = (USHORT)newLength;
|
||
modifiedClassName.Buffer = ExAllocatePool(PagedPool, newLength);
|
||
if( NULL == modifiedClassName.Buffer ) {
|
||
ParDump2(PARPNP1, ("devobj::ParDetectCreatePdo - ParCreateDevice FAILED - no PagedPool avail\n"));
|
||
ExFreePool(deviceIdString);
|
||
RtlFreeUnicodeString( &className );
|
||
return NULL;
|
||
}
|
||
|
||
while( ( status == STATUS_OBJECT_NAME_COLLISION ) && ( number <= maxNumber ) ) {
|
||
|
||
status = RtlIntegerToUnicodeString(number, 10, &suffix);
|
||
if ( !NT_SUCCESS(status) ) {
|
||
ExFreePool(deviceIdString);
|
||
RtlFreeUnicodeString( &className );
|
||
RtlFreeUnicodeString( &modifiedClassName );
|
||
return NULL;
|
||
}
|
||
|
||
RtlCopyUnicodeString( &modifiedClassName, &className );
|
||
RtlAppendUnicodeStringToString( &modifiedClassName, &dash );
|
||
RtlAppendUnicodeStringToString( &modifiedClassName, &suffix );
|
||
|
||
ParDump2(PARPNP1, ("devobj::ParDetectCreatePdo - trying ParCreateDevice with className <%wZ>\n", &modifiedClassName));
|
||
status = ParCreateDevice(driverObject, sizeof(DEVICE_EXTENSION), &modifiedClassName,
|
||
FILE_DEVICE_PARALLEL_PORT, 0, TRUE, &newDevObj);
|
||
if( NT_SUCCESS( status ) ) {
|
||
ParDump2(PARPNP1, ("devobj::ParDetectCreatePdo - ParCreateDevice returned SUCCESS with className <%wZ>\n", &modifiedClassName));
|
||
} else {
|
||
++number;
|
||
}
|
||
}
|
||
}
|
||
///
|
||
|
||
if( useModifiedClassName ) {
|
||
// copy modifiedClassName to className
|
||
RtlFreeUnicodeString( &className );
|
||
className = modifiedClassName;
|
||
ParDump2(PARPNP1, ("devobj::ParDetectCreatePdo - copy useModifiedClassName to className - className=<%wZ>\n", &className));
|
||
}
|
||
|
||
|
||
if( !NT_SUCCESS(status) ) {
|
||
// unable to create device object, bail out
|
||
ParDump2(PARPNP1, ("devobj::ParDetectCreatePdo - unable to create device object "
|
||
"className=<%wZ>, bail out - status=%x\n", &className, status) );
|
||
ExFreePool(deviceIdString);
|
||
RtlFreeUnicodeString(&className);
|
||
ParLogError(fdo->DriverObject, NULL, PhysicalZero, PhysicalZero,
|
||
0, 0, 0, 9, STATUS_SUCCESS, PAR_INSUFFICIENT_RESOURCES);
|
||
return NULL;
|
||
} else {
|
||
ParDump2(PARPNP1, ("devobj::ParDetectCreatePdo - device created <%wZ>\n", &className));
|
||
}
|
||
//
|
||
// device object created
|
||
//
|
||
newDevExt = newDevObj->DeviceExtension;
|
||
|
||
//
|
||
// initialize device object and extension
|
||
//
|
||
ParInitCommonDOPre(newDevObj, fdo, &className);
|
||
status = ParInitPdo(newDevObj, (PUCHAR)deviceIdString, deviceIdLength, LegacyPodo, Dot3Id);
|
||
if( !NT_SUCCESS( status ) ) {
|
||
// initialization failed, clean up and bail out
|
||
ParDump2(PARPNP1, ("devobj::ParDetectCreatePdo - call to ParInitPdo failed, bail out\n") );
|
||
ParAcquireListMutexAndKillDeviceObject(fdo, newDevObj);
|
||
return NULL;
|
||
}
|
||
ParInitCommonDOPost(newDevObj);
|
||
|
||
//
|
||
// create symbolic link
|
||
//
|
||
if( newDevExt->SymbolicLinkName.Buffer ) {
|
||
|
||
ParDump2(PARPNP1, ("devobj::ParDetectCreatePdo - ready to create symlink - SymbolicLinkName <%wZ>, ClassName <%wZ>\n",
|
||
&newDevExt->SymbolicLinkName, &newDevExt->ClassName) );
|
||
ParDump2(PARPNP1, (" - Length=%hd, MaximumLength=%hd\n", newDevExt->ClassName.Length, newDevExt->ClassName.MaximumLength) );
|
||
|
||
// doug
|
||
ASSERT(newDevExt->ClassName.Length < 100);
|
||
|
||
PAGED_CODE();
|
||
status = IoCreateUnprotectedSymbolicLink(&newDevExt->SymbolicLinkName, &newDevExt->ClassName);
|
||
|
||
if ( NT_SUCCESS(status) ) {
|
||
// note this in our extension for later cleanup
|
||
ParDump2(PARPNP1, ("devobj::ParDetectCreatePdo - SymbolicLinkName -> ClassName = <%wZ> -> <%wZ>\n",
|
||
&newDevExt->SymbolicLinkName, &newDevExt->ClassName) );
|
||
newDevExt->CreatedSymbolicLink = TRUE;
|
||
|
||
// Write symbolic link map info to the registry.
|
||
status = RtlWriteRegistryValue(RTL_REGISTRY_DEVICEMAP,
|
||
(PWSTR)L"PARALLEL PORTS",
|
||
newDevExt->ClassName.Buffer,
|
||
REG_SZ,
|
||
newDevExt->SymbolicLinkName.Buffer,
|
||
newDevExt->SymbolicLinkName.Length +
|
||
sizeof(WCHAR));
|
||
|
||
if (!NT_SUCCESS(status)) {
|
||
// unable to write map info to registry - continue anyway
|
||
ParLogError(newDevObj->DriverObject, newDevObj,
|
||
newDevExt->OriginalController, PhysicalZero,
|
||
0, 0, 0, 6, status, PAR_NO_DEVICE_MAP_CREATED);
|
||
}
|
||
|
||
} else {
|
||
|
||
// unable to create the symbolic link.
|
||
ParDump2(PARPNP1, ("devobj::ParDetectCreatePdo - unable to create SymbolicLink - status = %x\n",status));
|
||
newDevExt->CreatedSymbolicLink = FALSE;
|
||
RtlFreeUnicodeString(&newDevExt->SymbolicLinkName);
|
||
ParLogError(newDevObj->DriverObject, newDevObj,
|
||
newDevExt->OriginalController, PhysicalZero,
|
||
0, 0, 0, 5, status, PAR_NO_SYMLINK_CREATED);
|
||
}
|
||
|
||
} else {
|
||
// extension does not contain a symbolic link name for us to use
|
||
ParDump2(PARPNP1, ("devobj::ParDetectCreatePdo - extension does not contain a symbolic link for us to use\n"));
|
||
newDevExt->CreatedSymbolicLink = FALSE;
|
||
}
|
||
|
||
// End-Of-Chain PDO - register for PnP TargetDeviceChange notification
|
||
status = IoRegisterPlugPlayNotification(EventCategoryTargetDeviceChange,
|
||
0,
|
||
(PVOID)newDevExt->PortDeviceFileObject,
|
||
driverObject,
|
||
(PDRIVER_NOTIFICATION_CALLBACK_ROUTINE) ParPnpNotifyTargetDeviceChange,
|
||
(PVOID)newDevObj,
|
||
&newDevExt->NotificationHandle);
|
||
|
||
if( !NT_SUCCESS(status) ) {
|
||
// PnP registration for TargetDeviceChange notification failed,
|
||
// clean up and bail out
|
||
ParDump2(PARPNP1, ("devobj::ParDetectCreatePdo - PnP registration failed, killing PDO\n") );
|
||
ParAcquireListMutexAndKillDeviceObject(fdo, newDevObj);
|
||
return NULL;
|
||
}
|
||
|
||
return newDevObj;
|
||
}
|
||
|
||
NTSTATUS
|
||
ParInitPdo(
|
||
IN PDEVICE_OBJECT NewPdo,
|
||
IN PUCHAR DeviceIdString,
|
||
IN ULONG DeviceIdLength,
|
||
IN PDEVICE_OBJECT LegacyPodo,
|
||
IN UCHAR Dot3Id
|
||
)
|
||
{
|
||
static WCHAR ParInt2Wchar[] = { L'0', L'1', L'2', L'3', L'4', L'5' };
|
||
NTSTATUS status;
|
||
PDEVICE_EXTENSION legacyExt = LegacyPodo->DeviceExtension;
|
||
PDEVICE_EXTENSION newExt = NewPdo->DeviceExtension;
|
||
|
||
//
|
||
// start with identical initialization as done for Legacy PODO
|
||
//
|
||
status = ParInitLegacyPodo(NewPdo, &legacyExt->PortSymbolicLinkName);
|
||
if( !NT_SUCCESS(status) ) {
|
||
return status;
|
||
}
|
||
|
||
//
|
||
// Fixup extension if we are a daisy chain device or legacy Zip
|
||
//
|
||
newExt->Ieee1284Flags = legacyExt->Ieee1284Flags;
|
||
if( Dot3Id != DOT3_END_OF_CHAIN_ID ) {
|
||
newExt->EndOfChain = FALSE;
|
||
newExt->Ieee1284_3DeviceId = Dot3Id;
|
||
// NewPdo->Flags &= ~DO_POWER_PAGABLE; // RMT - temp clear bit until ppa, disk, partmgr set it
|
||
}
|
||
|
||
//
|
||
// Note that we are a PDO in our extension
|
||
//
|
||
newExt->DeviceType = PAR_DEVTYPE_PDO;
|
||
|
||
|
||
//
|
||
// use PODO's SymbolicLinkName + a ".N" suffix as the PDO's SymbolicLinkName
|
||
//
|
||
{
|
||
UNICODE_STRING newSymLinkName;
|
||
USHORT index;
|
||
USHORT length = (USHORT)( newExt->SymbolicLinkName.Length + ( 3 * sizeof(WCHAR) ) );
|
||
ParDump2(PARPNP1, ("devobj::ParInitPdo - old SymLinkName=%wZ\n", &newExt->SymbolicLinkName) );
|
||
RtlInitUnicodeString(&newSymLinkName, NULL);
|
||
newSymLinkName.Buffer = ExAllocatePool(PagedPool, length);
|
||
if( !newSymLinkName.Buffer ) {
|
||
return STATUS_UNSUCCESSFUL;
|
||
}
|
||
newSymLinkName.Length=0;
|
||
newSymLinkName.MaximumLength=length;
|
||
RtlCopyUnicodeString(&newSymLinkName, &newExt->SymbolicLinkName);
|
||
index = (USHORT) ( (newExt->SymbolicLinkName.Length)/sizeof(WCHAR) );
|
||
newSymLinkName.Buffer[index+0] = L'.';
|
||
newSymLinkName.Buffer[index+1] = ParInt2Wchar[Dot3Id];
|
||
newSymLinkName.Buffer[index+2] = L'\0';
|
||
newSymLinkName.Length += (2 * sizeof(WCHAR));
|
||
RtlFreeUnicodeString(&newExt->SymbolicLinkName);
|
||
newExt->SymbolicLinkName = newSymLinkName;
|
||
ParDump2(PARPNP1, ("devobj::ParInitPdo - new SymLinkName=%wZ\n", &newExt->SymbolicLinkName) );
|
||
ParDump2(PARPNP1, (" - Length=%hd, MaximumLength=%hd\n", newExt->SymbolicLinkName.Length, newExt->SymbolicLinkName.MaximumLength) );
|
||
}
|
||
|
||
// initialize PnP fields of device extension
|
||
{
|
||
UCHAR RawString[128];
|
||
UCHAR DescriptionString[128];
|
||
PUCHAR deviceIdString;
|
||
|
||
deviceIdString = ExAllocatePool(PagedPool, DeviceIdLength + sizeof(UCHAR) );
|
||
if( deviceIdString ) {
|
||
RtlCopyMemory(deviceIdString, DeviceIdString, DeviceIdLength);
|
||
*(deviceIdString+DeviceIdLength) = 0; // NULL terminate
|
||
}
|
||
|
||
RtlZeroMemory( RawString, sizeof(RawString) );
|
||
RtlZeroMemory( DescriptionString, sizeof(DescriptionString) );
|
||
status = ParPnpGetId(DeviceIdString, BusQueryDeviceID, RawString, DescriptionString);
|
||
|
||
if (NT_SUCCESS(status)) {
|
||
RtlCopyMemory(newExt->DeviceIdString, RawString, strlen((const PCHAR)RawString));
|
||
RtlCopyMemory(newExt->DeviceDescription, DescriptionString, strlen((const PCHAR)DescriptionString));
|
||
if( deviceIdString ) {
|
||
ParDetectDot3DataLink(newExt, deviceIdString);
|
||
}
|
||
}
|
||
|
||
if( deviceIdString ) {
|
||
ExFreePool(deviceIdString);
|
||
}
|
||
|
||
}
|
||
return status;
|
||
}
|
||
|
||
PWSTR
|
||
ParGetPortLptName(
|
||
IN PDEVICE_OBJECT PortDeviceObject
|
||
)
|
||
// return 0 on any error
|
||
{
|
||
NTSTATUS status;
|
||
PARALLEL_PNP_INFORMATION pnpInfo;
|
||
|
||
// Get Parallel Pnp Info from ParPort device, return PortName
|
||
|
||
status = ParBuildSendInternalIoctl(IOCTL_INTERNAL_GET_PARALLEL_PNP_INFO,
|
||
PortDeviceObject, NULL, 0,
|
||
&pnpInfo, sizeof(PARALLEL_PNP_INFORMATION), NULL);
|
||
if( NT_SUCCESS(status) ) {
|
||
return (PWSTR)(pnpInfo.PortName);
|
||
} else {
|
||
ParDump2(PARERRORS, ("devobj::ParGetPortLptName - FAILED - returning 0\n"));
|
||
return 0;
|
||
}
|
||
}
|
||
|
||
UCHAR
|
||
ParGet1284_3DeviceCount(
|
||
IN PDEVICE_OBJECT PortDeviceObject
|
||
)
|
||
// return 0 on any error
|
||
|
||
{
|
||
NTSTATUS status;
|
||
PARALLEL_PNP_INFORMATION pnpInfo;
|
||
|
||
// Get Parallel Pnp Info from ParPort device, return Dot3 device Id count returned
|
||
|
||
status = ParBuildSendInternalIoctl(IOCTL_INTERNAL_GET_PARALLEL_PNP_INFO,
|
||
PortDeviceObject, NULL, 0,
|
||
&pnpInfo, sizeof(PARALLEL_PNP_INFORMATION), NULL);
|
||
if( NT_SUCCESS(status) ) {
|
||
return (UCHAR)(pnpInfo.Ieee1284_3DeviceCount);
|
||
} else {
|
||
ParDump2(PARERRORS, ("devobj::ParGet1284_3DeviceCount - FAILED - returning 0\n"));
|
||
return 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;
|
||
BOOLEAN needToCopyOutputBuffer = FALSE;
|
||
|
||
PAGED_CODE();
|
||
|
||
ParDump2(PARIOCTL1,("devobj::ParBuildSendInternalIoctl: Enter\n"));
|
||
|
||
//
|
||
// Current limitation is that this function does not handle a request with
|
||
// both InputBufferLength and OutputBufferLength > 0
|
||
//
|
||
if( InputBufferLength != 0 && OutputBufferLength != 0 ) {
|
||
// ASSERTMSG("ParBuildSendInternalIoctl does not support input and output in the same IOCTL \n", FALSE);
|
||
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) ) {
|
||
ParDump2(PARIOCTL1,("devobj::ParBuildSendInternalIoctl: ParCallDriver FAILED - 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;
|
||
}
|
||
|
||
NTSTATUS
|
||
ParSelect1284_3Device(
|
||
IN PDEVICE_OBJECT PortDeviceObject,
|
||
IN UCHAR Dot3DeviceId
|
||
)
|
||
/*++dvdf
|
||
|
||
Routine Description:
|
||
|
||
This routine selects a 1284.3 daisy chain device via an
|
||
IOCTL_INTERNAL_SELECT_DEVICE sent to the ParPort to which
|
||
our device is connected.
|
||
|
||
Note: Caller must have already Acquired the Port prior to calling this function.
|
||
|
||
Arguments:
|
||
|
||
PortDeviceObject - points to the ParPort that the device is connected to.
|
||
|
||
Dot3DeviceId - IEEE 1284.3 daisy chain id (in the range [0..3]) to be selected.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - if the device was selected
|
||
!STATUS_SUCCESS - otherwise
|
||
|
||
--*/
|
||
{
|
||
PARALLEL_1284_COMMAND par1284Command;
|
||
|
||
par1284Command.ID = Dot3DeviceId;
|
||
par1284Command.Port = 0;
|
||
par1284Command.CommandFlags = PAR_HAVE_PORT_KEEP_PORT; // we have already Acquired the port
|
||
|
||
return ParBuildSendInternalIoctl(IOCTL_INTERNAL_SELECT_DEVICE,
|
||
PortDeviceObject,
|
||
&par1284Command, sizeof(PARALLEL_1284_COMMAND),
|
||
NULL, 0, NULL);
|
||
}
|
||
|
||
NTSTATUS
|
||
ParDeselect1284_3Device(
|
||
IN PDEVICE_OBJECT PortDeviceObject,
|
||
IN UCHAR Dot3DeviceId
|
||
)
|
||
/*++dvdf
|
||
|
||
Routine Description:
|
||
|
||
This routine selects a 1284.3 daisy chain device via an
|
||
IOCTL_INTERNAL_SELECT_DEVICE sent to the ParPort to which
|
||
our device is connected.
|
||
|
||
Note: This function does not Release the port so the Caller still has
|
||
the port after this function returns.
|
||
|
||
Arguments:
|
||
|
||
PortDeviceObject - points to the ParPort that the device is connected to.
|
||
|
||
Dot3DeviceId - IEEE 1284.3 daisy chain id (in the range [0..3]) to be selected.
|
||
|
||
Return Value:
|
||
|
||
STATUS_SUCCESS - if the device was selected
|
||
!STATUS_SUCCESS - otherwise
|
||
|
||
--*/
|
||
{
|
||
PARALLEL_1284_COMMAND par1284Command;
|
||
|
||
par1284Command.ID = Dot3DeviceId;
|
||
par1284Command.Port = 0;
|
||
par1284Command.CommandFlags = PAR_HAVE_PORT_KEEP_PORT; // just Deselect device, don't Release port
|
||
|
||
return ParBuildSendInternalIoctl(IOCTL_INTERNAL_DESELECT_DEVICE,
|
||
PortDeviceObject,
|
||
&par1284Command, sizeof(PARALLEL_1284_COMMAND),
|
||
NULL, 0, NULL);
|
||
}
|