//+------------------------------------------------------------------------- // // 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); }