/*++ Copyright (c) 1993 Microsoft Corporation Module Name: parport.c Abstract: This module contains the code for the parallel port driver. This driver creates a device for each parallel port on the system. It increments 'IoGetConfigurationInformation()->ParallelCount' once for each parallel port. Each device created (\Device\ParallelPort0, \DeviceParallelPort1,...) supports a number of internal ioctls that allow parallel class drivers to get information about the parallel port and to share the parallel port. IOCTL_INTERNAL_GET_PARALLEL_PORT_INFO returns the location and span of the register set for the parallel port. This ioctl also returns callback functions for 'FreePort', 'TryAllocatePort', and 'QueryNumWaiters' (more on these below). This ioctl should be called by a class driver during 'DriverEntry' and the information added to the class driver's device extension. A parallel class driver should never touch the parallel port registers returned by IOCTL_INTERNAL_GET_PARALLEL_PORT_INFO without first allocating the port from the parallel port driver. The port can be allocated from IRQL <= DISPATCH_LEVEL by calling IOCTL_INTERNAL_PARALLEL_PORT_ALLOCATE, or 'TryAllocatePort'. The first call is the simplest: the IRP will be queued in the parallel port driver until the port is free and then will return with a successful status. The class driver may cancel this IRP at any time which serves as a mechanism to timeout an allocate request. It is often convenient to use an incomming read or write IRP and pass it down to the port driver as an ALLOCATE. That way the class driver may avoid having to do its own queueing. The 'TryAllocatePort' call returns immediately from the port driver with a TRUE status if the port was allocated or an FALSE status if the port was busy. Once the port is allocated, it is owned by the allocating class driver until a 'FreePort' call is made. This releases the port and wakes up the next caller. The 'QueryNumWaiters' call which can be made at IRQL <= DISPATCH_LEVEL is useful to check and see if there are other class drivers waiting for the port. In this way, a class driver that needs to hold on to the port for an extended period of time (e.g. tape backup) can let go of the port if it detects another class driver needing service. If a class driver wishes to use the parallel port's interrupt then it should connect to this interrupt by calling IOCTL_INTERNAL_PARALLEL_CONNECT_INTERRUPT during its DriverEntry routine. Besides giving the port driver an interrupt service routine the class driver may optionally give the port driver a deferred port check routine. Such a routine would be called whenever the port is left free. This would allow the class driver to make sure that interrupts were turned on whenever the port was left idle. If the driver using the interrupt has an Unload routine then it should call IOCTL_INTERNAL_PARALLEL_DISCONNECT_INTERRUPT in its Unload routine. If a class driver's interrupt service routine is called when this class driver does not own the port, the class driver may attempt to grap the port quickly if it is free by calling the 'TryAllocatePortAtInterruptLevel' function. This function is returned when the class driver connects to the interrupt. The class driver may also use the 'FreePortFromInterruptLevel' function to free the port. Please refer to the PARSIMP driver code for a template of a simple parallel class driver. This template implements simple allocation and freeing of the parallel port on an IRP by IRP basis. It also has optional code for timing out allocation requests and for using the parallel port interrupt. Author: Anthony V. Ercolano 1-Aug-1992 Norbert P. Kusters 18-Oct-1993 Environment: Kernel mode Revision History : --*/ #include "ntddk.h" #include "parallel.h" #include "parport.h" #include "parlog.h" #ifdef POOL_TAGGING #ifdef ExAllocatePool #undef ExAllocatePool #endif #define ExAllocatePool(a,b) ExAllocatePoolWithTag(a,b,'PraP') #endif // // This is the actual definition of ParDebugLevel. // Note that it is only defined if this is a "debug" // build. // #if DBG extern ULONG PptDebugLevel = 0; #endif static const PHYSICAL_ADDRESS PhysicalZero = {0}; typedef struct _PARALLEL_FIRMWARE_DATA { PDRIVER_OBJECT DriverObject; ULONG ControllersFound; LIST_ENTRY ConfigList; } PARALLEL_FIRMWARE_DATA, *PPARALLEL_FIRMWARE_DATA; typedef enum _PPT_MEM_COMPARES { AddressesAreEqual, AddressesOverlap, AddressesAreDisjoint } PPT_MEM_COMPARES, *PPPT_MEM_COMPARES; typedef struct _SYNCHRONIZED_COUNT_CONTEXT { PLONG Count; LONG NewCount; } SYNCHRONIZED_COUNT_CONTEXT, *PSYNCHRONIZED_COUNT_CONTEXT; typedef struct _SYNCHRONIZED_LIST_CONTEXT { PLIST_ENTRY List; PLIST_ENTRY NewEntry; } SYNCHRONIZED_LIST_CONTEXT, *PSYNCHRONIZED_LIST_CONTEXT; typedef struct _SYNCHRONIZED_DISCONNECT_CONTEXT { PDEVICE_EXTENSION Extension; PPARALLEL_INTERRUPT_SERVICE_ROUTINE IsrInfo; } SYNCHRONIZED_DISCONNECT_CONTEXT, *PSYNCHRONIZED_DISCONNECT_CONTEXT; NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ); VOID PptLogError( IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT DeviceObject OPTIONAL, IN PHYSICAL_ADDRESS P1, IN PHYSICAL_ADDRESS P2, IN ULONG SequenceNumber, IN UCHAR MajorFunctionCode, IN UCHAR RetryCount, IN ULONG UniqueErrorValue, IN NTSTATUS FinalStatus, IN NTSTATUS SpecificIOStatus ); PPT_MEM_COMPARES PptMemCompare( IN PHYSICAL_ADDRESS A, IN ULONG SpanOfA, IN PHYSICAL_ADDRESS B, IN ULONG SpanOfB ); VOID PptGetConfigInfo( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath, OUT PLIST_ENTRY ConfigList ); NTSTATUS PptConfigCallBack( IN PVOID Context, IN PUNICODE_STRING PathName, IN INTERFACE_TYPE BusType, IN ULONG BusNumber, IN PKEY_VALUE_FULL_INFORMATION* BusInformation, IN CONFIGURATION_TYPE ControllerType, IN ULONG ControllerNumber, IN PKEY_VALUE_FULL_INFORMATION* ControllerInformation, IN CONFIGURATION_TYPE PeripheralType, IN ULONG PeripheralNumber, IN PKEY_VALUE_FULL_INFORMATION* PeripheralInformation ); NTSTATUS PptItemCallBack( IN PVOID Context, IN PUNICODE_STRING PathName, IN INTERFACE_TYPE BusType, IN ULONG BusNumber, IN PKEY_VALUE_FULL_INFORMATION* BusInformation, IN CONFIGURATION_TYPE ControllerType, IN ULONG ControllerNumber, IN PKEY_VALUE_FULL_INFORMATION* ControllerInformation, IN CONFIGURATION_TYPE PeripheralType, IN ULONG PeripheralNumber, IN PKEY_VALUE_FULL_INFORMATION* PeripheralInformation ); NTSTATUS PptInitializeController( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath, IN PCONFIG_DATA ConfigData ); PVOID PptGetMappedAddress( IN INTERFACE_TYPE BusType, IN ULONG BusNumber, IN PHYSICAL_ADDRESS IoAddress, IN ULONG NumberOfBytes, IN ULONG AddressSpace, OUT PBOOLEAN MappedAddress ); BOOLEAN PptPutInConfigList( IN PDRIVER_OBJECT DriverObject, IN OUT PLIST_ENTRY ConfigList, IN PCONFIG_DATA New ); VOID PptFreePortDpc( IN PKDPC Dpc, IN OUT PVOID Extension, IN PVOID SystemArgument1, IN PVOID SystemArgument2 ); VOID PptReportResourcesDevice( IN PDEVICE_EXTENSION Extension, IN BOOLEAN ClaimInterrupt, OUT PBOOLEAN ConflictDetected ); VOID PptUnReportResourcesDevice( IN OUT PDEVICE_EXTENSION Extension ); BOOLEAN PptInterruptService( IN PKINTERRUPT Interrupt, IN PVOID Extension ); BOOLEAN PptTryAllocatePort( IN PVOID Extension ); VOID PptFreePort( IN PVOID Extension ); ULONG PptQueryNumWaiters( IN PVOID Extension ); BOOLEAN PptIsNecR98Machine( void ); #ifdef ALLOC_PRAGMA #pragma alloc_text(INIT,DriverEntry) #pragma alloc_text(INIT,PptGetConfigInfo) #pragma alloc_text(INIT,PptConfigCallBack) #pragma alloc_text(INIT,PptItemCallBack) #pragma alloc_text(INIT,PptInitializeController) #pragma alloc_text(INIT,PptGetMappedAddress) #pragma alloc_text(INIT,PptPutInConfigList) #endif // // Keep track of GET and RELEASE port info. // ULONG PortInfoReferenceCount = 0; PFAST_MUTEX PortInfoMutex = NULL; NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) /*++ Routine Description: This routine is called at system initialization time to initialize this driver. Arguments: DriverObject - Supplies the driver object. RegistryPath - Supplies the registry path for this driver. Return Value: STATUS_SUCCESS - We could initialize at least one device. STATUS_NO_SUCH_DEVICE - We could not initialize even one device. --*/ { LIST_ENTRY configList; PCONFIG_DATA currentConfig; PLIST_ENTRY head; PptGetConfigInfo(DriverObject, RegistryPath, &configList); // // Initialize each item in the list of configuration records. // while (!IsListEmpty(&configList)) { head = RemoveHeadList(&configList); currentConfig = CONTAINING_RECORD(head, CONFIG_DATA, ConfigList); PptInitializeController(DriverObject, RegistryPath, currentConfig); } if (!DriverObject->DeviceObject) { return STATUS_NO_SUCH_DEVICE; } // // Initialize the Driver Object with driver's entry points // DriverObject->MajorFunction[IRP_MJ_CREATE] = PptDispatchCreateClose; DriverObject->MajorFunction[IRP_MJ_CLOSE] = PptDispatchCreateClose; DriverObject->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] = PptDispatchDeviceControl; DriverObject->MajorFunction[IRP_MJ_CLEANUP] = PptDispatchCleanup; DriverObject->DriverUnload = PptUnload; // // Let this driver be paged until someone requests PORT_INFO. // PortInfoMutex = ExAllocatePool(NonPagedPool, sizeof(FAST_MUTEX)); if (!PortInfoMutex) { return STATUS_INSUFFICIENT_RESOURCES; } ExInitializeFastMutex(PortInfoMutex); MmPageEntireDriver(DriverEntry); return STATUS_SUCCESS; } VOID PptLogError( IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT DeviceObject OPTIONAL, IN PHYSICAL_ADDRESS P1, IN PHYSICAL_ADDRESS P2, IN ULONG SequenceNumber, IN UCHAR MajorFunctionCode, IN UCHAR RetryCount, IN ULONG UniqueErrorValue, IN NTSTATUS FinalStatus, IN NTSTATUS SpecificIOStatus ) /*++ Routine Description: This routine allocates an error log entry, copies the supplied data to it, and requests that it be written to the error log file. Arguments: DriverObject - Supplies a pointer to the driver object for the device. DeviceObject - Supplies a pointer to the device object associated with the device that had the error, early in initialization, one may not yet exist. P1,P2 - Supplies the physical addresses for the controller ports involved with the error if they are available and puts them through as dump data. SequenceNumber - Supplies a ulong value that is unique to an IRP over the life of the irp in this driver - 0 generally means an error not associated with an irp. MajorFunctionCode - Supplies the major function code of the irp if there is an error associated with it. RetryCount - Supplies the number of times a particular operation has been retried. UniqueErrorValue - Supplies a unique long word that identifies the particular call to this function. FinalStatus - Supplies the final status given to the irp that was associated with this error. If this log entry is being made during one of the retries this value will be STATUS_SUCCESS. SpecificIOStatus - Supplies the IO status for this particular error. Return Value: None. --*/ { PIO_ERROR_LOG_PACKET errorLogEntry; PVOID objectToUse; SHORT dumpToAllocate; if (ARGUMENT_PRESENT(DeviceObject)) { objectToUse = DeviceObject; } else { objectToUse = DriverObject; } dumpToAllocate = 0; if (PptMemCompare(P1, 1, PhysicalZero, 1) != AddressesAreEqual) { dumpToAllocate = (SHORT) sizeof(PHYSICAL_ADDRESS); } if (PptMemCompare(P2, 1, PhysicalZero, 1) != AddressesAreEqual) { dumpToAllocate += (SHORT) sizeof(PHYSICAL_ADDRESS); } errorLogEntry = IoAllocateErrorLogEntry(objectToUse, (UCHAR) (sizeof(IO_ERROR_LOG_PACKET) + dumpToAllocate)); if (!errorLogEntry) { return; } errorLogEntry->ErrorCode = SpecificIOStatus; errorLogEntry->SequenceNumber = SequenceNumber; errorLogEntry->MajorFunctionCode = MajorFunctionCode; errorLogEntry->RetryCount = RetryCount; errorLogEntry->UniqueErrorValue = UniqueErrorValue; errorLogEntry->FinalStatus = FinalStatus; errorLogEntry->DumpDataSize = dumpToAllocate; if (dumpToAllocate) { RtlCopyMemory(errorLogEntry->DumpData, &P1, sizeof(PHYSICAL_ADDRESS)); if (dumpToAllocate > sizeof(PHYSICAL_ADDRESS)) { RtlCopyMemory(((PUCHAR) errorLogEntry->DumpData) + sizeof(PHYSICAL_ADDRESS), &P2, sizeof(PHYSICAL_ADDRESS)); } } IoWriteErrorLogEntry(errorLogEntry); } PPT_MEM_COMPARES PptMemCompare( IN PHYSICAL_ADDRESS A, IN ULONG SpanOfA, IN PHYSICAL_ADDRESS B, IN ULONG SpanOfB ) /*++ Routine Description: This routine compares two phsical address. Arguments: A - Supplies one half of the comparison. SpanOfA - Supplies the span of A in units of bytes. B - Supplies the other half of the comparison. SpanOfB - Supplies the span of B in units of bytes. Return Value: The result of the comparison. --*/ { LARGE_INTEGER a, b; LARGE_INTEGER lower, higher; ULONG lowerSpan; a.LowPart = A.LowPart; a.HighPart = A.HighPart; b.LowPart = B.LowPart; b.HighPart = B.HighPart; if (a.QuadPart == b.QuadPart) { return AddressesAreEqual; } if (a.QuadPart > b.QuadPart) { higher = a; lower = b; lowerSpan = SpanOfB; } else { higher = b; lower = a; lowerSpan = SpanOfA; } if (higher.QuadPart - lower.QuadPart >= lowerSpan) { return AddressesAreDisjoint; } return AddressesOverlap; } VOID PptGetConfigInfo( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath, OUT PLIST_ENTRY ConfigList ) /*++ Routine Description: This routine will "return" a list of configuration records for the parallel ports to initialize. It will first query the firmware data. It will then look for "user" specified parallel ports in the registry. It will place the user specified parallel ports in the passed in list. After it finds all of the user specified ports, it will attempt to add the firmware parallel ports into the passed in lists. The insert in the list code detects conflicts and rejects a new port. In this way we can prevent firmware found ports from overiding information specified by the "user". Arguments: DriverObject - Supplies the driver object. RegistryPath - Supplies the registry path for this driver. ConfigList - Returns a list of configuration records for the parallel ports to initialize. Return Value: None. --*/ { PARALLEL_FIRMWARE_DATA firmware; CONFIGURATION_TYPE sc; INTERFACE_TYPE iType; PLIST_ENTRY head; PCONFIG_DATA firmwareData; InitializeListHead(ConfigList); RtlZeroMemory(&firmware, sizeof(PARALLEL_FIRMWARE_DATA)); firmware.DriverObject = DriverObject; firmware.ControllersFound = IoGetConfigurationInformation()->ParallelCount; InitializeListHead(&firmware.ConfigList); // // First we query the hardware registry for all of // the firmware defined ports. We loop over all of // the busses. // sc = ParallelController; for (iType = 0; iType < MaximumInterfaceType; iType++) { IoQueryDeviceDescription(&iType, NULL, &sc, NULL, NULL, NULL, PptConfigCallBack, &firmware); } while (!IsListEmpty(&firmware.ConfigList)) { head = RemoveHeadList(&firmware.ConfigList); firmwareData = CONTAINING_RECORD(head, CONFIG_DATA, ConfigList); if (!PptPutInConfigList(DriverObject, ConfigList, firmwareData)) { PptLogError(DriverObject, NULL, PhysicalZero, PhysicalZero, 0, 0, 0, 1, STATUS_SUCCESS, PAR_USER_OVERRIDE); ParDump(PARERRORS, ("PARPORT: Conflict detected with user data for firmware port %wZ\n" "-------- User data will overides firmware data\n", &firmwareData->NtNameForPort)); ExFreePool(firmwareData->NtNameForPort.Buffer); ExFreePool(firmwareData); } } } NTSTATUS PptConfigCallBack( IN PVOID Context, IN PUNICODE_STRING PathName, IN INTERFACE_TYPE BusType, IN ULONG BusNumber, IN PKEY_VALUE_FULL_INFORMATION* BusInformation, IN CONFIGURATION_TYPE ControllerType, IN ULONG ControllerNumber, IN PKEY_VALUE_FULL_INFORMATION* ControllerInformation, IN CONFIGURATION_TYPE PeripheralType, IN ULONG PeripheralNumber, IN PKEY_VALUE_FULL_INFORMATION* PeripheralInformation ) /*++ Routine Description: This routine is used to acquire all of the configuration information for each parallel controller found by the firmware Arguments: Context - Supplies the pointer to the list head of the list of configuration records that we are building up. PathName - Not Used. BusType - Supplies the bus type. Internal, Isa, ... BusNumber - Supplies which bus number if we are on a multibus system. BusInformation - Not Used. ControllerType - Supplies the controller type. Should always be ParallelController. ControllerNumber - Supplies which controller number if there is more than one controller in the system. ControllerInformation - Supplies an array of pointers to the three pieces of registry information. PeripheralType - Undefined for this call. PeripheralNumber - Undefined for this call. PeripheralInformation - Undefined for this call. Return Value: STATUS_SUCCESS - Success. STATUS_INSUFFICIENT_RESOURCES - Not all of the resource information could be acquired. --*/ { PPARALLEL_FIRMWARE_DATA config = Context; PCM_FULL_RESOURCE_DESCRIPTOR controllerData; PCONFIG_DATA controller; ULONG i; PCM_PARTIAL_RESOURCE_DESCRIPTOR partial; BOOLEAN foundPort, foundInterrupt; WCHAR ntNumberBuffer[100]; UNICODE_STRING ntNumberString; UNICODE_STRING temp; ASSERT(ControllerType == ParallelController); // // Bail if some fool wrote a loader. // if (!ControllerInformation[IoQueryDeviceConfigurationData]->DataLength) { return STATUS_SUCCESS; } controllerData = (PCM_FULL_RESOURCE_DESCRIPTOR) (((PUCHAR)ControllerInformation[IoQueryDeviceConfigurationData]) + ControllerInformation[IoQueryDeviceConfigurationData]->DataOffset); controller = ExAllocatePool(PagedPool, sizeof(CONFIG_DATA)); if (!controller) { PptLogError(config->DriverObject, NULL, PhysicalZero, PhysicalZero, 0, 0, 0, 2, STATUS_SUCCESS, PAR_INSUFFICIENT_RESOURCES); ParDump( PARERRORS, ("PARPORT: Couldn't allocate memory for the configuration data\n" "-------- for firmware data\n") ); return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory(controller, sizeof(CONFIG_DATA)); InitializeListHead(&controller->ConfigList); controller->InterfaceType = BusType; controller->BusNumber = BusNumber; // // We need to get the following information out of the partial // resource descriptors. // // The base address and span covered by the parallel controllers // registers. // // It is not defined how these appear in the partial resource // lists, so we will just loop over all of them. If we find // something we don't recognize, we drop that information on // the floor. When we have finished going through all the // partial information, we validate that we got the above // information. // foundInterrupt = foundPort = FALSE; for (i = 0; i < controllerData->PartialResourceList.Count; i++) { partial = &controllerData->PartialResourceList.PartialDescriptors[i]; if (partial->Type == CmResourceTypePort) { foundPort = TRUE; controller->SpanOfController = partial->u.Port.Length; controller->Controller = partial->u.Port.Start; controller->AddressSpace = partial->Flags; if((controller->SpanOfController == 0x1000) && (PptIsNecR98Machine())) { ParDump(0, ("parport!PptConfigCallBack - " "found R98 machine with firmware bug. " "Truncating SpanOfController to 8\n")); controller->SpanOfController = 8; } } else if (partial->Type == CmResourceTypeInterrupt) { foundInterrupt = TRUE; controller->InterruptLevel = partial->u.Interrupt.Level; controller->InterruptVector = partial->u.Interrupt.Vector; controller->InterruptAffinity = partial->u.Interrupt.Affinity; if (partial->Flags & CM_RESOURCE_INTERRUPT_LATCHED) { controller->InterruptMode = Latched; } else { controller->InterruptMode = LevelSensitive; } } } if (!foundPort || !foundInterrupt) { PptLogError(config->DriverObject, NULL, PhysicalZero, PhysicalZero, 0, 0, 0, 3, STATUS_SUCCESS, PAR_NOT_ENOUGH_CONFIG_INFO); ExFreePool(controller); return STATUS_SUCCESS; } // // The following are so the we can form the // name following the \Device // and the default name that will be symbolic // linked to the device and the object directory // that link will go in. // ntNumberString.Length = 0; ntNumberString.MaximumLength = 100; ntNumberString.Buffer = ntNumberBuffer; if (!NT_SUCCESS(RtlIntegerToUnicodeString(config->ControllersFound, 10, &ntNumberString))) { PptLogError(config->DriverObject, NULL, controller->Controller, PhysicalZero, 0, 0, 0, 4, STATUS_SUCCESS, PAR_INSUFFICIENT_RESOURCES); ParDump( PARERRORS, ("PARPORT: Couldn't convert NT controller number\n" "-------- to unicode for firmware data: %d\n", config->ControllersFound) ); ExFreePool(controller); return STATUS_SUCCESS; } RtlInitUnicodeString(&controller->NtNameForPort, NULL); RtlInitUnicodeString(&temp, DD_PARALLEL_PORT_BASE_NAME_U); controller->NtNameForPort.MaximumLength = temp.Length + ntNumberString.Length + sizeof(WCHAR); controller->NtNameForPort.Buffer = ExAllocatePool(PagedPool, controller->NtNameForPort.MaximumLength); if (!controller->NtNameForPort.Buffer) { PptLogError(config->DriverObject, NULL, controller->Controller, PhysicalZero, 0, 0, 0, 5, STATUS_SUCCESS, PAR_INSUFFICIENT_RESOURCES); ParDump( PARERRORS, ("PARPORT: Couldn't allocate memory for NT\n" "-------- name for NT firmware data: %d\n", config->ControllersFound) ); ExFreePool(controller); return STATUS_SUCCESS; } RtlZeroMemory(controller->NtNameForPort.Buffer, controller->NtNameForPort.MaximumLength); RtlAppendUnicodeStringToString(&controller->NtNameForPort, &temp); RtlAppendUnicodeStringToString(&controller->NtNameForPort, &ntNumberString); InsertTailList(&config->ConfigList, &controller->ConfigList); config->ControllersFound++; return STATUS_SUCCESS; } NTSTATUS PptItemCallBack( IN PVOID Context, IN PUNICODE_STRING PathName, IN INTERFACE_TYPE BusType, IN ULONG BusNumber, IN PKEY_VALUE_FULL_INFORMATION* BusInformation, IN CONFIGURATION_TYPE ControllerType, IN ULONG ControllerNumber, IN PKEY_VALUE_FULL_INFORMATION* ControllerInformation, IN CONFIGURATION_TYPE PeripheralType, IN ULONG PeripheralNumber, IN PKEY_VALUE_FULL_INFORMATION* PeripheralInformation ) /*++ Routine Description: This IoQueryDeviceDescription callback merely sets the context argument to TRUE. Arguments: Context - Supplies a boolean to set to TRUE. Return Value: STATUS_SUCCESS - Success. --*/ { *((BOOLEAN *)Context) = TRUE; return STATUS_SUCCESS; } BOOLEAN PptPutInConfigList( IN PDRIVER_OBJECT DriverObject, IN OUT PLIST_ENTRY ConfigList, IN PCONFIG_DATA New ) /*++ Routine Description: Given a new config record and a config list, this routine will perform a check to make sure that the new record doesn't conflict with old records. If everything checks out it will insert the new config record into the config list. Arguments: DriverObject - Supplies the driver we are attempting to get configuration information for. ConfigList - Supplies the listhead for a list of configuration records for ports to control. New - Supplies a pointer to new configuration record to add. Return Value: FALSE - The new record was not added to the config list. TRUE - The new record was added to the config list. --*/ { PHYSICAL_ADDRESS PhysicalMax; PLIST_ENTRY current; PCONFIG_DATA oldConfig; ParDump( PARCONFIG, ("PARPORT: Attempting to add %wZ\n" "-------- to the config list\n" "-------- PortAddress is %x\n" "-------- BusNumber is %d\n" "-------- BusType is %d\n" "-------- AddressSpace is %d\n", &New->NtNameForPort, New->Controller.LowPart, New->BusNumber, New->InterfaceType, New->AddressSpace ) ); // // We don't support any boards whose memory wraps around // the physical address space. // PhysicalMax.LowPart = (ULONG)~0; PhysicalMax.HighPart = ~0; if (PptMemCompare(New->Controller, New->SpanOfController, PhysicalMax, 0) != AddressesAreDisjoint) { PptLogError(DriverObject, NULL, New->Controller, PhysicalZero, 0, 0, 0, 6, STATUS_SUCCESS, PAR_DEVICE_TOO_HIGH); ParDump( PARERRORS, ("PARPORT: Error in config record for %wZ\n" "-------- registers rap around physical memory\n", &New->NtNameForPort) ); return FALSE; } // // Go through the list looking for previous devices // with the same address. // for (current = ConfigList->Flink; current != ConfigList; current = current->Flink) { oldConfig = CONTAINING_RECORD(current, CONFIG_DATA, ConfigList); // // We only care about ports that are on the same bus. // if (oldConfig->InterfaceType == New->InterfaceType && oldConfig->BusNumber == New->BusNumber) { ParDump( PARCONFIG, ("PARPORT: Comparing it to %wZ\n" "-------- already in the config list\n" "-------- PortAddress is %x\n" "-------- BusNumber is %d\n" "-------- BusType is %d\n" "-------- AddressSpace is %d\n", &oldConfig->NtNameForPort, oldConfig->Controller.LowPart, oldConfig->BusNumber, oldConfig->InterfaceType, oldConfig->AddressSpace ) ); if (PptMemCompare(New->Controller, New->SpanOfController, oldConfig->Controller, oldConfig->SpanOfController) != AddressesAreDisjoint) { PptLogError(DriverObject, NULL, New->Controller, oldConfig->Controller, 0, 0, 0, 7, STATUS_SUCCESS, PAR_CONTROL_OVERLAP); return FALSE; } } } InsertTailList(ConfigList, &New->ConfigList); return TRUE; } VOID PptCleanupDevice( IN PDEVICE_EXTENSION Extension ) /*++ Routine Description: This routine will deallocate all of the memory used for a particular device. It will also disconnect any resources if need be. Arguments: Extension - Pointer to the device extension which is getting rid of all it's resources. Return Value: None. --*/ { ParDump( PARUNLOAD, ("PARPORT: in PptCleanupDevice for extension: %x\n",Extension) ); if (Extension && Extension->UnMapRegisters) { MmUnmapIoSpace(Extension->PortInfo.Controller, Extension->PortInfo.SpanOfController); } } NTSTATUS PptInitializeController( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath, IN PCONFIG_DATA ConfigData ) /*++ Routine Description: Really too many things to mention here. In general, it forms and sets up names, creates the device, translates bus relative items... Arguments: DriverObject - Supplies the driver object to be used to create the device object. RegistryPath - Supplies the registry path for this port driver. ConfigData - Supplies the configuration record for this port. Return Value: STATUS_SUCCCESS if everything went ok. A !NT_SUCCESS status otherwise. Notes: This routine will deallocate the config data. --*/ { UNICODE_STRING uniNameString, uniDeviceString; NTSTATUS status; PDEVICE_EXTENSION extension; BOOLEAN conflict; PDEVICE_OBJECT deviceObject; ParDump( PARCONFIG, ("PARPORT: Initializing for configuration record of %wZ\n", &ConfigData->NtNameForPort) ); // // Form a name like \Device\ParallelPort0. // // First we allocate space for the name. // RtlInitUnicodeString(&uniNameString, NULL); RtlInitUnicodeString(&uniDeviceString, L"\\Device\\"); uniNameString.MaximumLength = uniDeviceString.Length + ConfigData->NtNameForPort.Length + sizeof(WCHAR); uniNameString.Buffer = ExAllocatePool(PagedPool, uniNameString.MaximumLength); if (!uniNameString.Buffer) { PptLogError(DriverObject, NULL, ConfigData->Controller, PhysicalZero, 0, 0, 0, 8, STATUS_SUCCESS, PAR_INSUFFICIENT_RESOURCES); ParDump( PARERRORS, ("PARPORT: Could not form Unicode name string for %wZ\n", &ConfigData->NtNameForPort) ); ExFreePool(ConfigData->NtNameForPort.Buffer); ExFreePool(ConfigData); return STATUS_INSUFFICIENT_RESOURCES; } RtlZeroMemory(uniNameString.Buffer, uniNameString.MaximumLength); RtlAppendUnicodeStringToString(&uniNameString, &uniDeviceString); RtlAppendUnicodeStringToString(&uniNameString, &ConfigData->NtNameForPort); // // Create the device object for this device. // status = IoCreateDevice(DriverObject, sizeof(DEVICE_EXTENSION), &uniNameString, FILE_DEVICE_PARALLEL_PORT, 0, FALSE, &deviceObject); if (!NT_SUCCESS(status)) { PptLogError(DriverObject, NULL, ConfigData->Controller, PhysicalZero, 0, 0, 0, 9, STATUS_SUCCESS, PAR_INSUFFICIENT_RESOURCES); ParDump( PARERRORS, ("PARPORT: Could not create a device for %wZ\n", &ConfigData->NtNameForPort) ); ExFreePool(uniNameString.Buffer); ExFreePool(ConfigData->NtNameForPort.Buffer); ExFreePool(ConfigData); return status; } // // We have created the device, increment the counter in the // IO system that keep track. Anyplace that we do an IoDeleteDevice // we need to decrement. // IoGetConfigurationInformation()->ParallelCount++; // // The device object has a pointer to an area of non-paged // pool allocated for this device. This will be the device // extension. // extension = deviceObject->DeviceExtension; // // Zero all of the memory associated with the device // extension. // RtlZeroMemory(extension, sizeof(DEVICE_EXTENSION)); // // Get a "back pointer" to the device object. // extension->DeviceObject = deviceObject; // // Initialize 'WorkQueue' in extension. // InitializeListHead(&extension->WorkQueue); extension->WorkQueueCount = -1; // // Map the memory for the control registers for the parallel device // into virtual memory. // extension->PortInfo.Controller = PptGetMappedAddress( ConfigData->InterfaceType, ConfigData->BusNumber, ConfigData->Controller, ConfigData->SpanOfController, (BOOLEAN) ConfigData->AddressSpace, &extension->UnMapRegisters); if (!extension->PortInfo.Controller) { PptLogError(deviceObject->DriverObject, deviceObject, ConfigData->Controller, PhysicalZero, 0, 0, 0, 10, STATUS_SUCCESS, PAR_REGISTERS_NOT_MAPPED); ParDump( PARERRORS, ("PARPORT: Could not map memory for device registers for %wZ\n", &ConfigData->NtNameForPort) ); extension->UnMapRegisters = FALSE; status = STATUS_NONE_MAPPED; goto ExtensionCleanup; } extension->PortInfo.OriginalController = ConfigData->Controller; extension->PortInfo.SpanOfController = ConfigData->SpanOfController; extension->PortInfo.FreePort = PptFreePort; extension->PortInfo.TryAllocatePort = PptTryAllocatePort; extension->PortInfo.QueryNumWaiters = PptQueryNumWaiters; extension->PortInfo.Context = extension; // // Save the configuration information about the interrupt. // extension->AddressSpace = ConfigData->AddressSpace; extension->InterfaceType = ConfigData->InterfaceType; extension->BusNumber = ConfigData->BusNumber; extension->InterruptLevel = ConfigData->InterruptLevel; extension->InterruptVector = ConfigData->InterruptVector; extension->InterruptAffinity = ConfigData->InterruptAffinity; extension->InterruptMode = ConfigData->InterruptMode; // // Start off with an empty list of interrupt service routines. // Also, start off without the interrupt connected. // InitializeListHead(&extension->IsrList); extension->InterruptObject = NULL; extension->InterruptRefCount = 0; // // Initialize the free port DPC. // KeInitializeDpc(&extension->FreePortDpc, PptFreePortDpc, extension); // // Now the extension is all set up. Just report the resources. // PptReportResourcesDevice(extension, FALSE, &conflict); if (conflict) { status = STATUS_NO_SUCH_DEVICE; PptLogError(deviceObject->DriverObject, deviceObject, ConfigData->Controller, PhysicalZero, 0, 0, 0, 11, STATUS_SUCCESS, PAR_ADDRESS_CONFLICT); ParDump( PARERRORS, ("PARPORT: Could not claim the device registers for %wZ\n", &ConfigData->NtNameForPort) ); goto ExtensionCleanup; } // // If it was requested that the port be disabled, now is the // time to do it. // if (ConfigData->DisablePort) { status = STATUS_NO_SUCH_DEVICE; PptLogError(deviceObject->DriverObject, deviceObject, ConfigData->Controller, PhysicalZero, 0, 0, 0, 12, STATUS_SUCCESS, PAR_DISABLED_PORT); ParDump( PARERRORS, ("PARPORT: Port %wZ disabled as requested\n", &ConfigData->NtNameForPort) ); goto ExtensionCleanup; } // // Common error path cleanup. If the status is // bad, get rid of the device extension, device object // and any memory associated with it. // ExtensionCleanup: ExFreePool(uniNameString.Buffer); ExFreePool(ConfigData->NtNameForPort.Buffer); ExFreePool(ConfigData); if (NT_ERROR(status)) { PptCleanupDevice(extension); IoDeleteDevice(deviceObject); IoGetConfigurationInformation()->ParallelCount--; } return status; } PVOID PptGetMappedAddress( IN INTERFACE_TYPE BusType, IN ULONG BusNumber, IN PHYSICAL_ADDRESS IoAddress, IN ULONG NumberOfBytes, IN ULONG AddressSpace, OUT PBOOLEAN MappedAddress ) /*++ Routine Description: This routine maps an IO address to system address space. Arguments: BusType - Supplies the type of bus - eisa, mca, isa. IoBusNumber - Supplies the bus number. IoAddress - Supplies the base device address to be mapped. NumberOfBytes - Supplies the number of bytes for which the address is valid. AddressSpace - Supplies whether the address is in io space or memory. MappedAddress - Supplies whether the address was mapped. This only has meaning if the address returned is non-null. Return Value: The mapped address. --*/ { PHYSICAL_ADDRESS cardAddress; PVOID address; if (!HalTranslateBusAddress(BusType, BusNumber, IoAddress, &AddressSpace, &cardAddress)) { AddressSpace = 1; cardAddress.QuadPart = 0; } // // Map the device base address into the virtual address space // if the address is in memory space. // if (!AddressSpace) { address = MmMapIoSpace(cardAddress, NumberOfBytes, FALSE); *MappedAddress = (address ? TRUE : FALSE); } else { address = (PVOID) cardAddress.LowPart; *MappedAddress = FALSE; } return address; } VOID PptReportResourcesDevice( IN PDEVICE_EXTENSION Extension, IN BOOLEAN ClaimInterrupt, OUT PBOOLEAN ConflictDetected ) /*++ Routine Description: This routine reports the resources used for a device that is "ready" to run. If some conflict was detected, it doesn't matter, the reources are reported. Arguments: Extension - Supplies the device extension of the device we are reporting resources for. ClaimInterrupt - Supplies whether or not to claim the interrupt. ConflictDetected - Returns whether or not a conflict was detected. Return Value: None. --*/ { PCM_RESOURCE_LIST resourceList; PCM_PARTIAL_RESOURCE_DESCRIPTOR partial; UNICODE_STRING className; ULONG sizeofResourceList; ParDump( PARCONFIG, ("PARPORT: In PptReportResourcesDevice\n" "-------- for extension %x\n", Extension) ); // // The resource list for a device will consist of // // The resource list record itself with a count // of one for the single "built in" full resource // descriptor. // // The built-in full resource descriptor will contain // the bus type and busnumber and the built in partial // resource list. // // The built in partial resource list will have a count of 2: // // 1) The base register physical address and it's span. // // 2) The interrupt. // sizeofResourceList = sizeof(CM_RESOURCE_LIST); if (ClaimInterrupt) { sizeofResourceList += sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR); } resourceList = ExAllocatePool(PagedPool, sizeofResourceList); if (!resourceList) { // // Oh well, can't allocate the memory. Act as though // we succeeded. // PptLogError(Extension->DeviceObject->DriverObject, Extension->DeviceObject, Extension->PortInfo.OriginalController, PhysicalZero, 0, 0, 0, 13, STATUS_SUCCESS, PAR_INSUFFICIENT_RESOURCES); return; } RtlZeroMemory(resourceList, sizeofResourceList); resourceList->Count = 1; resourceList->List[0].InterfaceType = Extension->InterfaceType; resourceList->List[0].BusNumber = Extension->BusNumber; resourceList->List[0].PartialResourceList.Count = 1; partial = &resourceList->List[0].PartialResourceList.PartialDescriptors[0]; // // Account for the space used by the controller. // partial->Type = CmResourceTypePort; partial->ShareDisposition = CmResourceShareDeviceExclusive; partial->Flags = (USHORT) Extension->AddressSpace; partial->u.Port.Start = Extension->PortInfo.OriginalController; partial->u.Port.Length = Extension->PortInfo.SpanOfController; if (ClaimInterrupt) { partial++; resourceList->List[0].PartialResourceList.Count += 1; partial->Type = CmResourceTypeInterrupt; partial->ShareDisposition = CmResourceShareShared; if (Extension->InterruptMode == Latched) { partial->Flags = CM_RESOURCE_INTERRUPT_LATCHED; } else { partial->Flags = CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE; } partial->u.Interrupt.Vector = Extension->InterruptVector; partial->u.Interrupt.Level = Extension->InterruptLevel; partial->u.Interrupt.Affinity = Extension->InterruptAffinity; } RtlInitUnicodeString(&className, L"LOADED PARALLEL DRIVER RESOURCES"); IoReportResourceUsage(&className, Extension->DeviceObject->DriverObject, NULL, 0, Extension->DeviceObject, resourceList, sizeofResourceList, FALSE, ConflictDetected); ExFreePool(resourceList); } VOID PptUnReportResourcesDevice( IN OUT PDEVICE_EXTENSION Extension ) /*++ Routine Description: Purge the resources for this particular device. Arguments: Extension - The device extension of the device we are *un*reporting resources for. Return Value: None. --*/ { CM_RESOURCE_LIST resourceList; UNICODE_STRING className; BOOLEAN junkBoolean; ParDump( PARUNLOAD, ("PARPORT: In PptUnreportResourcesDevice\n" "-------- for extension %x of port %x\n", Extension, Extension->PortInfo.OriginalController.LowPart) ); RtlZeroMemory(&resourceList, sizeof(CM_RESOURCE_LIST)); resourceList.Count = 0; RtlInitUnicodeString(&className, L"LOADED PARALLEL DRIVER RESOURCES"); IoReportResourceUsage(&className, Extension->DeviceObject->DriverObject, NULL, 0, Extension->DeviceObject, &resourceList, sizeof(CM_RESOURCE_LIST), FALSE, &junkBoolean); } NTSTATUS PptConnectInterrupt( IN PDEVICE_EXTENSION Extension ) /*++ Routine Description: This routine connects the port interrupt service routine to the interrupt. Arguments: Extension - Supplies the device extension. Return Value: NTSTATUS code. --*/ { BOOLEAN conflict; ULONG interruptVector; KIRQL irql; KAFFINITY affinity; NTSTATUS status = STATUS_SUCCESS; PptReportResourcesDevice(Extension, TRUE, &conflict); if (conflict) { status = STATUS_NO_SUCH_DEVICE; } if (NT_SUCCESS(status)) { // // Connect the interrupt. // interruptVector = HalGetInterruptVector(Extension->InterfaceType, Extension->BusNumber, Extension->InterruptLevel, Extension->InterruptVector, &irql, &affinity); status = IoConnectInterrupt(&Extension->InterruptObject, PptInterruptService, Extension, NULL, interruptVector, irql, irql, Extension->InterruptMode, TRUE, affinity, FALSE); } if (!NT_SUCCESS(status)) { PptReportResourcesDevice(Extension, FALSE, &conflict); PptLogError(Extension->DeviceObject->DriverObject, Extension->DeviceObject, Extension->PortInfo.OriginalController, PhysicalZero, 0, 0, 0, 14, status, PAR_INTERRUPT_CONFLICT); ParDump( PARERRORS, ("PARPORT: Could not connect to interrupt for %x\n", Extension->PortInfo.OriginalController.LowPart) ); } return status; } VOID PptDisconnectInterrupt( IN PDEVICE_EXTENSION Extension ) /*++ Routine Description: This routine disconnects the port interrupt service routine from the interrupt. Arguments: Extension - Supplies the device extension. Return Value: None. --*/ { BOOLEAN conflict; IoDisconnectInterrupt(Extension->InterruptObject); PptReportResourcesDevice(Extension, FALSE, &conflict); } NTSTATUS PptDispatchCreateClose( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine is the dispatch for create and close requests. This request completes successfully. Arguments: DeviceObject - Supplies the device object. Irp - Supplies the I/O request packet. Return Value: STATUS_SUCCESS - Success. --*/ { ParDump( PARIRPPATH, ("PARPORT: In create or close with IRP: %x\n", Irp) ); Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } BOOLEAN PptSynchronizedIncrement( IN OUT PVOID SyncContext ) /*++ Routine Description: This routine increments the 'Count' variable in the context and returns its new value in the 'NewCount' variable. Arguments: SyncContext - Supplies the synchronize count context. Return Value: TRUE --*/ { ((PSYNCHRONIZED_COUNT_CONTEXT) SyncContext)->NewCount = ++(*(((PSYNCHRONIZED_COUNT_CONTEXT) SyncContext)->Count)); return(TRUE); } BOOLEAN PptSynchronizedDecrement( IN OUT PVOID SyncContext ) /*++ Routine Description: This routine decrements the 'Count' variable in the context and returns its new value in the 'NewCount' variable. Arguments: SyncContext - Supplies the synchronize count context. Return Value: TRUE --*/ { ((PSYNCHRONIZED_COUNT_CONTEXT) SyncContext)->NewCount = --(*(((PSYNCHRONIZED_COUNT_CONTEXT) SyncContext)->Count)); return(TRUE); } BOOLEAN PptSynchronizedRead( IN OUT PVOID SyncContext ) /*++ Routine Description: This routine reads the 'Count' variable in the context and returns its value in the 'NewCount' variable. Arguments: SyncContext - Supplies the synchronize count context. Return Value: None. --*/ { ((PSYNCHRONIZED_COUNT_CONTEXT) SyncContext)->NewCount = *(((PSYNCHRONIZED_COUNT_CONTEXT) SyncContext)->Count); return(TRUE); } BOOLEAN PptSynchronizedQueue( IN PVOID Context ) /*++ Routine Description: This routine adds the given list entry to the given list. Arguments: Context - Supplies the synchronized list context. Return Value: TRUE --*/ { PSYNCHRONIZED_LIST_CONTEXT listContext; listContext = Context; InsertTailList(listContext->List, listContext->NewEntry); return(TRUE); } BOOLEAN PptSynchronizedDisconnect( IN PVOID Context ) /*++ Routine Description: This routine removes the given list entry from the ISR list. Arguments: Context - Supplies the synchronized disconnect context. Return Value: FALSE - The given list entry was not removed from the list. TRUE - The given list entry was removed from the list. --*/ { PSYNCHRONIZED_DISCONNECT_CONTEXT disconnectContext; PKSERVICE_ROUTINE ServiceRoutine; PVOID ServiceContext; PLIST_ENTRY current; PISR_LIST_ENTRY listEntry; disconnectContext = Context; ServiceRoutine = disconnectContext->IsrInfo->InterruptServiceRoutine; ServiceContext = disconnectContext->IsrInfo->InterruptServiceContext; for (current = disconnectContext->Extension->IsrList.Flink; current != &(disconnectContext->Extension->IsrList); current = current->Flink) { listEntry = CONTAINING_RECORD(current, ISR_LIST_ENTRY, ListEntry); if (listEntry->ServiceRoutine == ServiceRoutine && listEntry->ServiceContext == ServiceContext) { RemoveEntryList(current); return TRUE; } } return FALSE; } VOID PptCancelRoutine( IN OUT PDEVICE_OBJECT DeviceObject, IN OUT PIRP Irp ) /*++ Routine Description: This routine is called on when the given IRP is cancelled. It will dequeue this IRP off the work queue and complete the request as CANCELLED. If it can't get if off the queue then this routine will ignore the CANCEL request since the IRP is about to complete anyway. Arguments: DeviceObject - Supplies the device object. Irp - Supplies the IRP. Return Value: None. --*/ { PDEVICE_EXTENSION extension; SYNCHRONIZED_COUNT_CONTEXT syncContext; extension = DeviceObject->DeviceExtension; syncContext.Count = &extension->WorkQueueCount; if (extension->InterruptRefCount) { KeSynchronizeExecution(extension->InterruptObject, PptSynchronizedDecrement, &syncContext); } else { PptSynchronizedDecrement(&syncContext); } RemoveEntryList(&Irp->Tail.Overlay.ListEntry); IoReleaseCancelSpinLock(Irp->CancelIrql); Irp->IoStatus.Information = 0; Irp->IoStatus.Status = STATUS_CANCELLED; ParDump( PARIRPPATH, ("PARPORT: About to complete IRP in cancel routine\n" "Irp: %x status: %x Information: %x\n", Irp, Irp->IoStatus.Status, Irp->IoStatus.Information) ); IoCompleteRequest(Irp, IO_NO_INCREMENT); } VOID PptFreePortDpc( IN PKDPC Dpc, IN OUT PVOID Extension, IN PVOID SystemArgument1, IN PVOID SystemArgument2 ) /*++ Routine Description: This routine is a DPC that will free the port and if necessary complete an alloc request that is waiting. Arguments: Dpc - Not used. Extension - Supplies the device extension. SystemArgument1 - Not used. SystemArgument2 - Not used. Return Value: None. --*/ { PptFreePort(Extension); } BOOLEAN PptTryAllocatePortAtInterruptLevel( IN PVOID Context ) /*++ Routine Description: This routine is called at interrupt level to quickly allocate the parallel port if it is available. This call will fail if the port is not available. Arguments: Context - Supplies the device extension. Return Value: FALSE - The port was not allocated. TRUE - The port was successfully allocated. --*/ { if (((PDEVICE_EXTENSION) Context)->WorkQueueCount == -1) { ((PDEVICE_EXTENSION) Context)->WorkQueueCount = 0; return(TRUE); } else { return(FALSE); } } VOID PptFreePortFromInterruptLevel( IN PVOID Context ) /*++ Routine Description: This routine frees the port that was allocated at interrupt level. Arguments: Context - Supplies the device extension. Return Value: None. --*/ { // If no one is waiting for the port then this is simple operation, // otherwise queue a DPC to free the port later on. if (((PDEVICE_EXTENSION) Context)->WorkQueueCount == 0) { ((PDEVICE_EXTENSION) Context)->WorkQueueCount = -1; } else { KeInsertQueueDpc(&((PDEVICE_EXTENSION) Context)->FreePortDpc, NULL, NULL); } } BOOLEAN PptInterruptService( IN PKINTERRUPT Interrupt, IN PVOID Extension ) /*++ Routine Description: This routine services the interrupt for the parallel port. This routine will call out to all of the interrupt routines that connected with this device via IOCTL_INTERNAL_PARALLEL_CONNECT_INTERRUPT in order until one of them returns TRUE. Arguments: Interrupt - Supplies the interrupt object. Extension - Supplies the device extension. Return Value: FALSE - The interrupt was not handled. TRUE - The interrupt was handled. --*/ { PDEVICE_EXTENSION extension; PLIST_ENTRY current; PISR_LIST_ENTRY isrListEntry; extension = Extension; for (current = extension->IsrList.Flink; current != &extension->IsrList; current = current->Flink) { isrListEntry = CONTAINING_RECORD(current, ISR_LIST_ENTRY, ListEntry); if (isrListEntry->ServiceRoutine(Interrupt, isrListEntry->ServiceContext)) { return(TRUE); } } return(FALSE); } BOOLEAN PptTryAllocatePort( IN PVOID Extension ) /*++ Routine Description: This routine attempts to allocate the port. If the port is available then the call will succeed with the port allocated. If the port is not available the then call will fail immediately. Arguments: Extension - Supplies the device extension. Return Value: FALSE - The port was not allocated. TRUE - The port was allocated. --*/ { PDEVICE_EXTENSION extension = Extension; KIRQL cancelIrql; BOOLEAN b; if (extension->InterruptRefCount) { b = KeSynchronizeExecution(extension->InterruptObject, PptTryAllocatePortAtInterruptLevel, extension); } else { IoAcquireCancelSpinLock(&cancelIrql); b = PptTryAllocatePortAtInterruptLevel(extension); IoReleaseCancelSpinLock(cancelIrql); } return(b); } BOOLEAN PptTraversePortCheckList( IN PVOID Extension ) /*++ Routine Description: This routine traverses the deferred port check routines. This call must be synchronized at interrupt level so that real interrupts are blocked until these routines are completed. Arguments: Extension - Supplies the device extension. Return Value: FALSE - The port is in use so no action taken by this routine. TRUE - All of the deferred interrupt routines were called. --*/ { PDEVICE_EXTENSION extension = Extension; PLIST_ENTRY current; PISR_LIST_ENTRY checkEntry; // First check to make sure that the port is still free. if (extension->WorkQueueCount >= 0) { return FALSE; } for (current = extension->IsrList.Flink; current != &extension->IsrList; current = current->Flink) { checkEntry = CONTAINING_RECORD(current, ISR_LIST_ENTRY, ListEntry); if (checkEntry->DeferredPortCheckRoutine) { checkEntry->DeferredPortCheckRoutine(checkEntry->CheckContext); } } return TRUE; } VOID PptFreePort( IN PVOID Extension ) /*++ Routine Description: This routine frees the port. Arguments: Extension - Supplies the device extension. Return Value: None. --*/ { PDEVICE_EXTENSION extension = Extension; SYNCHRONIZED_COUNT_CONTEXT syncContext; KIRQL cancelIrql; PLIST_ENTRY head; PIRP irp; ULONG interruptRefCount; syncContext.Count = &extension->WorkQueueCount; IoAcquireCancelSpinLock(&cancelIrql); if (extension->InterruptRefCount) { KeSynchronizeExecution(extension->InterruptObject, PptSynchronizedDecrement, &syncContext); } else { PptSynchronizedDecrement(&syncContext); } if (syncContext.NewCount >= 0) { head = RemoveHeadList(&extension->WorkQueue); irp = CONTAINING_RECORD(head, IRP, Tail.Overlay.ListEntry); IoSetCancelRoutine(irp, NULL); IoReleaseCancelSpinLock(cancelIrql); irp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest(irp, IO_PARALLEL_INCREMENT); } else { interruptRefCount = extension->InterruptRefCount; IoReleaseCancelSpinLock(cancelIrql); if (interruptRefCount) { KeSynchronizeExecution(extension->InterruptObject, PptTraversePortCheckList, extension); } } } ULONG PptQueryNumWaiters( IN PVOID Extension ) /*++ Routine Description: This routine returns the number of irps queued waiting for the parallel port. Arguments: Extension - Supplies the device extension. Return Value: The number of irps queued waiting for the port. --*/ { PDEVICE_EXTENSION extension = Extension; KIRQL cancelIrql; SYNCHRONIZED_COUNT_CONTEXT syncContext; syncContext.Count = &extension->WorkQueueCount; if (extension->InterruptRefCount) { KeSynchronizeExecution(extension->InterruptObject, PptSynchronizedRead, &syncContext); } else { IoAcquireCancelSpinLock(&cancelIrql); PptSynchronizedRead(&syncContext); IoReleaseCancelSpinLock(cancelIrql); } return((syncContext.NewCount >= 0) ? ((ULONG) syncContext.NewCount) : 0); } NTSTATUS PptDispatchDeviceControl( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine is the dispatch for INTERNAL_DEVICE_CONTROLs. Arguments: DeviceObject - Supplies the device object. Irp - Supplies the I/O request packet. Return Value: STATUS_SUCCESS - Success. STATUS_PENDING - The request is pending. STATUS_INVALID_PARAMETER - Invalid parameter. STATUS_CANCELLED - The request was cancelled. STATUS_BUFFER_TOO_SMALL - The supplied buffer is too small. --*/ { PIO_STACK_LOCATION irpSp; PDEVICE_EXTENSION extension; NTSTATUS status; PPARALLEL_PORT_INFORMATION portInfo; PMORE_PARALLEL_PORT_INFORMATION morePortInfo; KIRQL cancelIrql; SYNCHRONIZED_COUNT_CONTEXT syncContext; PPARALLEL_INTERRUPT_SERVICE_ROUTINE isrInfo; PPARALLEL_INTERRUPT_INFORMATION interruptInfo; PISR_LIST_ENTRY isrListEntry; SYNCHRONIZED_LIST_CONTEXT listContext; SYNCHRONIZED_DISCONNECT_CONTEXT disconnectContext; BOOLEAN disconnectInterrupt; BOOLEAN deferredRoutinePresent; irpSp = IoGetCurrentIrpStackLocation(Irp); ParDump( PARIRPPATH, ("PARPORT: In internal device control dispatch with IRP: %x\n" "Io control code: %d\n", Irp, irpSp->Parameters.DeviceIoControl.IoControlCode) ); extension = DeviceObject->DeviceExtension; Irp->IoStatus.Information = 0; switch (irpSp->Parameters.DeviceIoControl.IoControlCode) { case IOCTL_INTERNAL_PARALLEL_PORT_ALLOCATE: IoAcquireCancelSpinLock(&cancelIrql); if (Irp->Cancel) { status = STATUS_CANCELLED; } else { syncContext.Count = &extension->WorkQueueCount; if (extension->InterruptRefCount) { KeSynchronizeExecution(extension->InterruptObject, PptSynchronizedIncrement, &syncContext); } else { PptSynchronizedIncrement(&syncContext); } if (syncContext.NewCount) { IoSetCancelRoutine(Irp, PptCancelRoutine); IoMarkIrpPending(Irp); InsertTailList(&extension->WorkQueue, &Irp->Tail.Overlay.ListEntry); status = STATUS_PENDING; } else { ParDump( PARIRPPATH, ("PARPORT: Completing ALLOCATE request in dispatch\n") ); status = STATUS_SUCCESS; } } IoReleaseCancelSpinLock(cancelIrql); break; case IOCTL_INTERNAL_GET_PARALLEL_PORT_INFO: if (irpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(PARALLEL_PORT_INFORMATION)) { status = STATUS_BUFFER_TOO_SMALL; } else { Irp->IoStatus.Information = sizeof(PARALLEL_PORT_INFORMATION); portInfo = Irp->AssociatedIrp.SystemBuffer; *portInfo = extension->PortInfo; status = STATUS_SUCCESS; ExAcquireFastMutex(PortInfoMutex); if (++PortInfoReferenceCount == 1) { MmResetDriverPaging(DriverEntry); } ExReleaseFastMutex(PortInfoMutex); } break; case IOCTL_INTERNAL_RELEASE_PARALLEL_PORT_INFO: ExAcquireFastMutex(PortInfoMutex); if (--PortInfoReferenceCount == 0) { MmPageEntireDriver(DriverEntry); } ExReleaseFastMutex(PortInfoMutex); status = STATUS_SUCCESS; break; case IOCTL_INTERNAL_GET_MORE_PARALLEL_PORT_INFO: if (irpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(MORE_PARALLEL_PORT_INFORMATION)) { status = STATUS_BUFFER_TOO_SMALL; } else { Irp->IoStatus.Information = sizeof(MORE_PARALLEL_PORT_INFORMATION); morePortInfo = Irp->AssociatedIrp.SystemBuffer; morePortInfo->InterfaceType = extension->InterfaceType; morePortInfo->BusNumber = extension->BusNumber; morePortInfo->InterruptLevel = extension->InterruptLevel; morePortInfo->InterruptVector = extension->InterruptVector; morePortInfo->InterruptAffinity = extension->InterruptAffinity; morePortInfo->InterruptMode = extension->InterruptMode; status = STATUS_SUCCESS; } break; case IOCTL_INTERNAL_PARALLEL_CONNECT_INTERRUPT: if (irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(PARALLEL_INTERRUPT_SERVICE_ROUTINE) || irpSp->Parameters.DeviceIoControl.OutputBufferLength < sizeof(PARALLEL_INTERRUPT_INFORMATION)) { status = STATUS_BUFFER_TOO_SMALL; } else { isrInfo = Irp->AssociatedIrp.SystemBuffer; interruptInfo = Irp->AssociatedIrp.SystemBuffer; IoAcquireCancelSpinLock(&cancelIrql); if (extension->InterruptRefCount) { ++extension->InterruptRefCount; IoReleaseCancelSpinLock(cancelIrql); status = STATUS_SUCCESS; } else { IoReleaseCancelSpinLock(cancelIrql); status = PptConnectInterrupt(extension); if (NT_SUCCESS(status)) { IoAcquireCancelSpinLock(&cancelIrql); ++extension->InterruptRefCount; IoReleaseCancelSpinLock(cancelIrql); } } if (NT_SUCCESS(status)) { isrListEntry = ExAllocatePool(NonPagedPool, sizeof(ISR_LIST_ENTRY)); if (isrListEntry) { isrListEntry->ServiceRoutine = isrInfo->InterruptServiceRoutine; isrListEntry->ServiceContext = isrInfo->InterruptServiceContext; isrListEntry->DeferredPortCheckRoutine = isrInfo->DeferredPortCheckRoutine; isrListEntry->CheckContext = isrInfo->DeferredPortCheckContext; // Put the ISR_LIST_ENTRY onto the ISR list. listContext.List = &extension->IsrList; listContext.NewEntry = &isrListEntry->ListEntry; KeSynchronizeExecution(extension->InterruptObject, PptSynchronizedQueue, &listContext); interruptInfo->InterruptObject = extension->InterruptObject; interruptInfo->TryAllocatePortAtInterruptLevel = PptTryAllocatePortAtInterruptLevel; interruptInfo->FreePortFromInterruptLevel = PptFreePortFromInterruptLevel; interruptInfo->Context = extension; Irp->IoStatus.Information = sizeof(PARALLEL_INTERRUPT_INFORMATION); status = STATUS_SUCCESS; } else { status = STATUS_INSUFFICIENT_RESOURCES; } } } break; case IOCTL_INTERNAL_PARALLEL_DISCONNECT_INTERRUPT: if (irpSp->Parameters.DeviceIoControl.InputBufferLength < sizeof(PARALLEL_INTERRUPT_SERVICE_ROUTINE)) { status = STATUS_BUFFER_TOO_SMALL; } else { isrInfo = Irp->AssociatedIrp.SystemBuffer; // Take the ISR out of the ISR list. IoAcquireCancelSpinLock(&cancelIrql); if (extension->InterruptRefCount) { IoReleaseCancelSpinLock(cancelIrql); disconnectContext.Extension = extension; disconnectContext.IsrInfo = isrInfo; if (KeSynchronizeExecution(extension->InterruptObject, PptSynchronizedDisconnect, &disconnectContext)) { status = STATUS_SUCCESS; IoAcquireCancelSpinLock(&cancelIrql); if (--extension->InterruptRefCount == 0) { disconnectInterrupt = TRUE; } else { disconnectInterrupt = FALSE; } IoReleaseCancelSpinLock(cancelIrql); } else { status = STATUS_INVALID_PARAMETER; disconnectInterrupt = FALSE; } } else { IoReleaseCancelSpinLock(cancelIrql); disconnectInterrupt = FALSE; status = STATUS_INVALID_PARAMETER; } // Disconnect the interrupt if appropriate. if (disconnectInterrupt) { PptDisconnectInterrupt(extension); } } break; default: status = STATUS_INVALID_PARAMETER; break; } if (status != STATUS_PENDING) { Irp->IoStatus.Status = status; IoCompleteRequest(Irp, IO_NO_INCREMENT); } return status; } NTSTATUS PptDispatchCleanup( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine cancels all of the IRPs currently queued on the given device. Arguments: DeviceObject - Supplies the device object. Irp - Supplies the cleanup IRP. Return Value: STATUS_SUCCESS - Success. --*/ { PDEVICE_EXTENSION extension; PIRP irp; KIRQL cancelIrql; ParDump( PARIRPPATH, ("PARPORT: In cleanup with IRP: %x\n", Irp) ); extension = DeviceObject->DeviceExtension; IoAcquireCancelSpinLock(&cancelIrql); while (!IsListEmpty(&extension->WorkQueue)) { irp = CONTAINING_RECORD(extension->WorkQueue.Blink, IRP, Tail.Overlay.ListEntry); irp->Cancel = TRUE; irp->CancelIrql = cancelIrql; irp->CancelRoutine = NULL; PptCancelRoutine(DeviceObject, irp); IoAcquireCancelSpinLock(&cancelIrql); } IoReleaseCancelSpinLock(cancelIrql); Irp->IoStatus.Status = STATUS_SUCCESS; Irp->IoStatus.Information = 0; ParDump( PARIRPPATH, ("PARPORT: About to complete IRP in cleanup\n" "Irp: %x status: %x Information: %x\n", Irp, Irp->IoStatus.Status, Irp->IoStatus.Information) ); IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } VOID PptUnload( IN PDRIVER_OBJECT DriverObject ) /*++ Routine Description: This routine cleans up all of the memory associated with any of the devices belonging to the driver. It will loop through the device list. Arguments: DriverObject - Supplies the driver object controling all of the devices. Return Value: None. --*/ { PDEVICE_OBJECT currentDevice; PDEVICE_EXTENSION extension; PLIST_ENTRY head; PISR_LIST_ENTRY entry; ParDump( PARUNLOAD, ("PARPORT: In ParUnload\n") ); while (currentDevice = DriverObject->DeviceObject) { extension = currentDevice->DeviceExtension; if (extension->InterruptRefCount) { PptDisconnectInterrupt(extension); } PptCleanupDevice(extension); while (!IsListEmpty(&extension->IsrList)) { head = RemoveHeadList(&extension->IsrList); entry = CONTAINING_RECORD(head, ISR_LIST_ENTRY, ListEntry); ExFreePool(entry); } PptUnReportResourcesDevice(extension); IoDeleteDevice(currentDevice); IoGetConfigurationInformation()->ParallelCount--; } ExFreePool(PortInfoMutex); } BOOLEAN PptIsNecR98Machine( void ) /*++ Routine Description: This routine checks the machine type in the registry to determine if this is an Nec R98 machine. Arguments: None. Return Value: TRUE - this machine is an R98 FALSE - this machine is not --*/ { UNICODE_STRING path; RTL_QUERY_REGISTRY_TABLE paramTable[2]; NTSTATUS status; UNICODE_STRING identifierString; UNICODE_STRING necR98Identifier; UNICODE_STRING necR98JIdentifier; RtlInitUnicodeString(&path, L"\\Registry\\Machine\\HARDWARE\\DESCRIPTION\\System"); RtlInitUnicodeString(&necR98Identifier, L"NEC-R98"); RtlInitUnicodeString(&necR98JIdentifier, L"NEC-J98"); identifierString.Length = 0; identifierString.MaximumLength = 32; identifierString.Buffer = ExAllocatePool(PagedPool, identifierString.MaximumLength); if(!identifierString.Buffer) return FALSE; RtlZeroMemory(paramTable, sizeof(paramTable)); paramTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT | RTL_QUERY_REGISTRY_REQUIRED; paramTable[0].Name = L"Identifier"; paramTable[0].EntryContext = &identifierString; paramTable[0].DefaultType = REG_SZ; paramTable[0].DefaultData = &path; paramTable[0].DefaultLength = 0; status = RtlQueryRegistryValues(RTL_REGISTRY_ABSOLUTE, path.Buffer, paramTable, NULL, NULL); if(NT_SUCCESS(status)) { if((RtlCompareUnicodeString(&identifierString, &necR98Identifier, FALSE) == 0) || (RtlCompareUnicodeString(&identifierString, &necR98JIdentifier, FALSE) == 0)) { ParDump(0, ("parport!PptIsNecR98Machine - this an R98 machine\n")); ExFreePool(identifierString.Buffer); return TRUE; } } else { ParDump(0, ("parport!PptIsNecR98Machine - " "RtlQueryRegistryValues failed [status 0x%x]\n", status)); ExFreePool(identifierString.Buffer); return FALSE; } ParDump(0, ("parport!PptIsNecR98Machine - " "this is not an R98 machine\n")); ExFreePool(identifierString.Buffer); return FALSE; }