/*++ Copyright (C) Microsoft Corporation, 1990 - 1999 Module Name: pnp.c Abstract: This is the NT SCSI port driver. This file contains the self-contained plug and play code. Authors: Peter Wieland Environment: kernel mode only Notes: This module is a driver dll for scsi miniports. Revision History: --*/ #include "port.h" #include #define __FILE_ID__ 'pnp ' #if DBG static const char *__file__ = __FILE__; #endif #define NUM_DEVICE_TYPE_INFO_ENTRIES 18 extern SCSIPORT_DEVICE_TYPE DeviceTypeInfo[]; ULONG SpAdapterStopRemoveSupported = TRUE; NTSTATUS SpQueryCapabilities( IN PADAPTER_EXTENSION Adapter ); PWCHAR ScsiPortAddGenericControllerId( IN PDRIVER_OBJECT DriverObject, IN PWCHAR IdList ); VOID CopyField( IN PUCHAR Destination, IN PUCHAR Source, IN ULONG Count, IN UCHAR Change ); NTSTATUS ScsiPortInitPnpAdapter( IN PDEVICE_OBJECT Fdo ); NTSTATUS SpStartLowerDevice( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ); VOID SpGetSlotNumber( IN PDEVICE_OBJECT Fdo, IN PPORT_CONFIGURATION_INFORMATION ConfigInfo, IN PCM_RESOURCE_LIST ResourceList ); BOOLEAN SpGetInterrupt( IN PCM_RESOURCE_LIST FullResourceList, OUT ULONG *Irql, OUT ULONG *Vector, OUT KAFFINITY *Affinity ); VOID SpQueryDeviceRelationsCompletion( IN PADAPTER_EXTENSION Adapter, IN PSP_ENUMERATION_REQUEST Request, IN NTSTATUS Status ); NTSTATUS ScsiPortDetermineGenId( IN PDRIVER_OBJECT DriverObject, IN PINQUIRYDATA InquiryData, OUT PUCHAR GenericId ); // // Routines start // #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, ScsiPortAddDevice) #pragma alloc_text(PAGE, ScsiPortUnload) #pragma alloc_text(PAGE, ScsiPortFdoPnp) #pragma alloc_text(PAGE, ScsiPortStartAdapter) #pragma alloc_text(PAGE, ScsiPortGetDeviceId) #pragma alloc_text(PAGE, ScsiPortGetInstanceId) #pragma alloc_text(PAGE, ScsiPortGetHardwareIds) #pragma alloc_text(PAGE, ScsiPortGetCompatibleIds) #pragma alloc_text(PAGE, ScsiPortDetermineGenId) #pragma alloc_text(PAGE, CopyField) #pragma alloc_text(PAGE, SpFindInitData) #pragma alloc_text(PAGE, SpStartLowerDevice) #pragma alloc_text(PAGE, SpGetSlotNumber) #pragma alloc_text(PAGE, SpGetDeviceTypeInfo) #pragma alloc_text(PAGE, ScsiPortAddGenericControllerId) #pragma alloc_text(PAGE, SpQueryCapabilities) #pragma alloc_text(PAGE, SpGetInterrupt) #pragma alloc_text(PAGE, SpQueryDeviceRelationsCompletion) #pragma alloc_text(PAGELOCK, ScsiPortInitPnpAdapter) #endif #ifdef ALLOC_PRAGMA #pragma code_seg("PAGE") #endif SCSIPORT_DEVICE_TYPE DeviceTypeInfo[NUM_DEVICE_TYPE_INFO_ENTRIES] = { {"Disk", "GenDisk", L"DiskPeripheral", TRUE}, {"Sequential", "", L"TapePeripheral", TRUE}, {"Printer", "GenPrinter", L"PrinterPeripheral", FALSE}, {"Processor", "", L"OtherPeripheral", FALSE}, {"Worm", "GenWorm", L"WormPeripheral", TRUE}, {"CdRom", "GenCdRom", L"CdRomPeripheral", TRUE}, {"Scanner", "GenScanner", L"ScannerPeripheral", FALSE}, {"Optical", "GenOptical", L"OpticalDiskPeripheral", TRUE}, {"Changer", "ScsiChanger", L"MediumChangerPeripheral", TRUE}, {"Net", "ScsiNet", L"CommunicationsPeripheral", FALSE}, {"ASCIT8", "ScsiASCIT8", L"ASCPrePressGraphicsPeripheral", FALSE}, {"ASCIT8", "ScsiASCIT8", L"ASCPrePressGraphicsPeripheral", FALSE}, {"Array", "ScsiArray", L"ArrayPeripheral", FALSE}, {"Enclosure", "ScsiEnclosure", L"EnclosurePeripheral", FALSE}, {"RBC", "ScsiRBC", L"RBCPeripheral", TRUE}, {"CardReader", "ScsiCardReader", L"CardReaderPeripheral", FALSE}, {"Bridge", "ScsiBridge", L"BridgePeripheral", FALSE}, {"Other", "ScsiOther", L"OtherPeripheral", FALSE} }; #ifdef ALLOC_PRAGMA #pragma code_seg() #endif NTSTATUS ScsiPortAddDevice( IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT PhysicalDeviceObject ) /*++ Routine Description: This routine handles add-device requests for the scsi port driver Arguments: DriverObject - a pointer to the driver object for this device PhysicalDeviceObject - a pointer to the PDO we are being added to Return Value: STATUS_SUCCESS --*/ { PSCSIPORT_DRIVER_EXTENSION driverExtension; PDEVICE_OBJECT newFdo; NTSTATUS status; PAGED_CODE(); status = SpCreateAdapter(DriverObject, &newFdo); if(newFdo != NULL) { PADAPTER_EXTENSION adapter; PCOMMON_EXTENSION commonExtension; PDEVICE_OBJECT newStack; adapter = newFdo->DeviceExtension; commonExtension = &(adapter->CommonExtension); adapter->IsMiniportDetected = FALSE; adapter->IsPnp = TRUE; driverExtension = IoGetDriverObjectExtension(DriverObject, ScsiPortInitialize); switch(driverExtension->BusType) { #if 0 case BusTypeFibre: { adapter->DisablePower = TRUE; adapter->DisableStop = TRUE; break; } #endif default: { adapter->DisablePower = FALSE; adapter->DisableStop = FALSE; break; } } newStack = IoAttachDeviceToDeviceStack(newFdo, PhysicalDeviceObject); adapter->CommonExtension.LowerDeviceObject = newStack; adapter->LowerPdo = PhysicalDeviceObject; if(newStack == NULL) { status = STATUS_UNSUCCESSFUL; } else { status = STATUS_SUCCESS; } } return status; } VOID ScsiPortUnload( IN PDRIVER_OBJECT DriverObject ) /*++ Routine Description: This routine will shut down all device objects for this miniport and clean up all allocated resources Arguments: DriverObject - the driver being unloaded Return Value: none --*/ { PVOID Packet; PSCSIPORT_DRIVER_EXTENSION DriverExtension; PVOID CurrentValue; PVOID InvalidPage; PAGED_CODE(); // // See if there is a driver extension for this driver. It is possible // that one has not been created yet, so this may fail, in which case // we give up and return. // DriverExtension = IoGetDriverObjectExtension( DriverObject, ScsiPortInitialize ); if (DriverExtension == NULL) { return; } // // Get the reserve event in the driver extension. The reserve event // may not have already been used, so it's possible that it is NULL. If // this is the case, we give up and return. // Packet = DriverExtension->ReserveAllocFailureLogEntry; if (Packet != NULL) { // // We have to ensure that we are the only instance to use this // event. To do so, we attempt to NULL the event in the driver // extension. If somebody else beats us to it, they own the // event and we have to give up. // CurrentValue = InterlockedCompareExchangePointer( &(DriverExtension->ReserveAllocFailureLogEntry), NULL, Packet); if (Packet == CurrentValue) { IoFreeErrorLogEntry(Packet); } } // // Free the invalid page we created to catch misbehaving miniports. // InvalidPage = DriverExtension->InvalidPage; if (InvalidPage != NULL) { CurrentValue = InterlockedCompareExchangePointer( &(DriverExtension->InvalidPage), NULL, InvalidPage); if (InvalidPage == CurrentValue) { MmProtectMdlSystemAddress(DriverExtension->UnusedPageMdl, PAGE_READWRITE); MmUnlockPages(DriverExtension->UnusedPageMdl); IoFreeMdl(DriverExtension->UnusedPageMdl); ExFreePool(DriverExtension->UnusedPage); } } #ifdef ALLOC_PRAGMA if (VerifierApiCodeSectionHandle != NULL) { PVOID Handle = VerifierApiCodeSectionHandle; CurrentValue = InterlockedCompareExchangePointer( &VerifierApiCodeSectionHandle, NULL, Handle); if (CurrentValue == Handle) { MmUnlockPagableImageSection(Handle); } } #endif return; } NTSTATUS ScsiPortFdoPnp( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) { PCOMMON_EXTENSION commonExtension = DeviceObject->DeviceExtension; PADAPTER_EXTENSION adapter = DeviceObject->DeviceExtension; PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp); NTSTATUS status = STATUS_INVALID_DEVICE_REQUEST; ULONG isRemoved; BOOLEAN sendDown = TRUE; PAGED_CODE(); isRemoved = SpAcquireRemoveLock(DeviceObject, Irp); DebugPrint((2, "ScsiPortFdoPnp: FDO %p IRP %p MinorFunction %x isRemoved %d\n", DeviceObject, Irp, irpStack->MinorFunction, isRemoved)); switch (irpStack->MinorFunction) { case IRP_MN_QUERY_PNP_DEVICE_STATE: { // // Extract the address of the device state buffer from the IRP. // PPNP_DEVICE_STATE deviceState; deviceState = (PPNP_DEVICE_STATE) &(Irp->IoStatus.Information); // // Copy our saved device state into the supplied address. // *deviceState = adapter->DeviceState; // // If the device is in the paging path, mark it as not-disableable. // if (commonExtension->PagingPathCount != 0) { SET_FLAG((*deviceState), PNP_DEVICE_NOT_DISABLEABLE); } // // Leave sendDown set to TRUE so request will be forwarded to // lower driver below. // break; } case IRP_MN_START_DEVICE: { PSCSIPORT_DRIVER_EXTENSION driverExtension = IoGetDriverObjectExtension(DeviceObject->DriverObject, ScsiPortInitialize); PCM_RESOURCE_LIST allocatedResources = irpStack->Parameters.StartDevice.AllocatedResources; PCM_RESOURCE_LIST translatedResources = irpStack->Parameters.StartDevice.AllocatedResourcesTranslated; ULONG interfaceFlags; irpStack = IoGetCurrentIrpStackLocation(Irp); // // Override sendDown so we complete this request below instead of // forwarding it to the lower driver. // sendDown = FALSE; // // Make sure this device was created by an add rather than the // one that was found by port or miniport. // if(adapter->IsPnp == FALSE) { DebugPrint((1, "ScsiPortFdoPnp - asked to start non-pnp " "adapter\n")); status = STATUS_UNSUCCESSFUL; break; } if(commonExtension->CurrentPnpState == IRP_MN_START_DEVICE) { DebugPrint((1, "ScsiPortFdoPnp - already started - nothing " "to do\n")); status = STATUS_SUCCESS; break; } // // Now make sure that pnp handed us some resources. It may not // if this is a phantom of a PCI device which we reported on a // previous boot. In that case pnp thinks we'll allocate the // resources ourselves. // if(allocatedResources == NULL) { // // This happens with reported devices when PCI moves them from // boot to boot. // Irp->IoStatus.Status = STATUS_UNSUCCESSFUL; break; } ASSERT(allocatedResources->Count); // // Make sure that adapters with this interface type can be // initialized as pnp drivers. // interfaceFlags = SpQueryPnpInterfaceFlags( driverExtension, allocatedResources->List[0].InterfaceType); if(interfaceFlags == SP_PNP_NOT_SAFE) { // // Nope - not safe. We cannot start this device so return // failure. // DebugPrint((1, "ScsiPortFdoPnp - Miniport cannot be run in " "pnp mode for interface type %#08lx\n", allocatedResources->List[0].InterfaceType)); // // Mark the device as not being pnp - that way we won't get // removed. // adapter->IsPnp = FALSE; status = STATUS_UNSUCCESSFUL; break; } // // Check to see if this interface requires slot/function numbers. // If not then zero out the virtual slot number. // if(!TEST_FLAG(interfaceFlags, SP_PNP_NEEDS_LOCATION)) { adapter->VirtualSlotNumber.u.AsULONG = 0; } // // Determine if we should have found this device during // our detection scan. We do this by checking to see if the pdo // has a pnp bus type. If not and the detection flag is set then // assume duplicate detection has failed and don't start this // device. // { status = SpGetBusTypeGuid(adapter); if((status == STATUS_OBJECT_NAME_NOT_FOUND) && ((driverExtension->LegacyAdapterDetection == TRUE) && (interfaceFlags & SP_PNP_NON_ENUMERABLE))) { DbgPrint("ScsiPortFdoPnp: device has no pnp bus type but " "was not found as a duplicate during " "detection\n"); status = STATUS_UNSUCCESSFUL; // // make sure this one doesn't get removed though - if it's // removed then the resources may be disabled for the // ghost. // adapter->IsPnp = FALSE; break; } } // // Finally, if this is a PCI adapter make sure we were given // an interrupt. The current assumption is that there aren't // any polled-mode PCI SCSI adapters in the market. // if(TEST_FLAG(interfaceFlags, SP_PNP_INTERRUPT_REQUIRED)) { ULONG irql, vector; KAFFINITY affinity; if(SpGetInterrupt(allocatedResources, &irql, &vector, &affinity) == FALSE) { PIO_ERROR_LOG_PACKET error = IoAllocateErrorLogEntry(DeviceObject, sizeof(IO_ERROR_LOG_PACKET)); status = STATUS_DEVICE_CONFIGURATION_ERROR; if(error != NULL) { error->MajorFunctionCode = IRP_MJ_PNP; error->UniqueErrorValue = 0x418; error->ErrorCode = IO_ERR_INCORRECT_IRQL; IoWriteErrorLogEntry(error); } break; } } status = SpStartLowerDevice(DeviceObject, Irp); if(NT_SUCCESS(status)) { // // If we haven't allocated a HwDeviceExtension for this thing // yet then we'll need to set it up // if(commonExtension->IsInitialized == FALSE) { DebugPrint((1, "ScsiPortFdoPnp - find and init adapter %#p\n", DeviceObject)); if(allocatedResources == NULL) { status = STATUS_INVALID_PARAMETER; } else { adapter->AllocatedResources = RtlDuplicateCmResourceList( DeviceObject->DriverObject, NonPagedPool, allocatedResources, SCSIPORT_TAG_RESOURCE_LIST); adapter->TranslatedResources = RtlDuplicateCmResourceList( DeviceObject->DriverObject, NonPagedPool, translatedResources, SCSIPORT_TAG_RESOURCE_LIST); commonExtension->IsInitialized = TRUE; status = ScsiPortInitPnpAdapter(DeviceObject); } if(!NT_SUCCESS(status)) { DebugPrint((1, "ScsiPortInitializeAdapter failed " "%#08lx\n", status)); break; } } // // Start up the adapter. // status = ScsiPortStartAdapter(DeviceObject); if(NT_SUCCESS(status)) { commonExtension->PreviousPnpState = 0xff; commonExtension->CurrentPnpState = IRP_MN_START_DEVICE; } } break; } case IRP_MN_FILTER_RESOURCE_REQUIREMENTS: { PIO_RESOURCE_REQUIREMENTS_LIST requirements; // // Grab the bus and slot numbers out of the resource requirements // list. // requirements = irpStack->Parameters.FilterResourceRequirements. IoResourceRequirementList; if (requirements != NULL) { adapter->RealBusNumber = requirements->BusNumber; adapter->RealSlotNumber = requirements->SlotNumber; } // // Leave sendDown in its default TRUE state so we will forward // this request to the lower driver. // break; } case IRP_MN_CANCEL_STOP_DEVICE: { sendDown = TRUE; Irp->IoStatus.Status = STATUS_SUCCESS; if(commonExtension->CurrentPnpState == IRP_MN_QUERY_STOP_DEVICE) { commonExtension->CurrentPnpState = commonExtension->PreviousPnpState; commonExtension->PreviousPnpState = 0xff; } break; } case IRP_MN_CANCEL_REMOVE_DEVICE: { sendDown = TRUE; Irp->IoStatus.Status = STATUS_SUCCESS; if(commonExtension->CurrentPnpState == IRP_MN_QUERY_REMOVE_DEVICE) { commonExtension->CurrentPnpState = commonExtension->PreviousPnpState; commonExtension->PreviousPnpState = 0xff; } break; } case IRP_MN_QUERY_STOP_DEVICE: { // // If stop has been disabled, fail the IRP and override sendDown // so we complete the request instead of forwarding it. // if (adapter->DisableStop) { status = STATUS_NOT_SUPPORTED; sendDown = FALSE; break; } // // Fall through. // } case IRP_MN_QUERY_REMOVE_DEVICE: { // // No problem with this request on our part. Just send it down // to the next driver. // if (SpAdapterStopRemoveSupported) { if((adapter->IsPnp) && SpIsAdapterControlTypeSupported(adapter, ScsiStopAdapter)) { Irp->IoStatus.Status = STATUS_SUCCESS; sendDown = TRUE; } else { status = STATUS_UNSUCCESSFUL; sendDown = FALSE; } if(NT_SUCCESS(status)) { commonExtension->PreviousPnpState = commonExtension->CurrentPnpState; commonExtension->CurrentPnpState = irpStack->MinorFunction; } } else { Irp->IoStatus.Status = STATUS_UNSUCCESSFUL; status = STATUS_UNSUCCESSFUL; sendDown = FALSE; } break; } case IRP_MN_SURPRISE_REMOVAL: { PDEVICE_OBJECT lowerDevice = commonExtension->LowerDeviceObject; // // First mark the device as REMOVE_PENDING - this should keep // us from starting up any new i/o requests. // commonExtension->IsRemoved = REMOVE_PENDING; // // Terminate the device. This shuts down the miniport as quickly // as possible and aborts all i/o requests. // if(commonExtension->CurrentPnpState == IRP_MN_START_DEVICE) { SpTerminateAdapter(adapter); } // // Release the remove lock and wait for any in-flight requests to // complete. // SpReleaseRemoveLock(DeviceObject, Irp); SpWaitForRemoveLock(DeviceObject, DeviceObject); // // Go do the surprise remove portion of removing the adapter. // ScsiPortRemoveAdapter(DeviceObject, TRUE); // // Save the new state of this device. // commonExtension->PreviousPnpState = commonExtension->CurrentPnpState; commonExtension->CurrentPnpState = IRP_MN_SURPRISE_REMOVAL; // // Since we have already released the adapter remove lock, we // the request here instead of doing it at the bottom of the // function. // IoCopyCurrentIrpStackLocationToNext(Irp); return IoCallDriver(commonExtension->LowerDeviceObject, Irp); } case IRP_MN_REMOVE_DEVICE: // // Asked to remove the adapter. We'll ask the port driver to // stop it's adapter and release it's resources. We can // detach and delete our device object as the lower driver // completes the remove request. // ASSERT(isRemoved != REMOVE_COMPLETE); // // If the device has been started then make sure we've got the // necessary code to disable it. If it isn't currently started // then either it's got the code we need or it's never been // started - in either case we can just tear it down. // if((adapter->IsPnp == FALSE) || ((commonExtension->CurrentPnpState == IRP_MN_START_DEVICE) && (!SpIsAdapterControlTypeSupported(adapter, ScsiStopAdapter)))) { // // the miniport needs to be stopped but we cannot do it. // Fail the request. // status = STATUS_UNSUCCESSFUL; sendDown = FALSE; break; } // // Clear the interface if it exists. // if(adapter->InterfaceName.Buffer != NULL) { IoSetDeviceInterfaceState( &(adapter->InterfaceName), FALSE); RtlFreeUnicodeString(&(adapter->InterfaceName)); RtlInitUnicodeString(&(adapter->InterfaceName), NULL); } SpReleaseRemoveLock(DeviceObject, Irp); ScsiPortRemoveAdapter(DeviceObject, FALSE); // // The adapter's been removed. Set the new state now. // commonExtension->CurrentPnpState = IRP_MN_REMOVE_DEVICE; commonExtension->PreviousPnpState = 0xff; // // Forward the request down and wait for it to complete. // status = SpSendIrpSynchronous( commonExtension->LowerDeviceObject, Irp); // // Indicate the the adapter is fully removed. // commonExtension->IsRemoved = REMOVE_COMPLETE; // // Complete the IRP. // IoCompleteRequest(Irp, IO_NO_INCREMENT); // // Detach and delete the FDO. // IoDetachDevice(commonExtension->LowerDeviceObject); IoDeleteDevice(DeviceObject); return status; case IRP_MN_STOP_DEVICE: { sendDown = TRUE; ASSERT(adapter->IsPnp); ASSERT(adapter->HwAdapterControl != NULL); status = ScsiPortStopAdapter(DeviceObject, Irp); Irp->IoStatus.Status = status; Irp->IoStatus.Information = 0L; if(!NT_SUCCESS(status)) { sendDown = FALSE; } else { commonExtension->PreviousPnpState = commonExtension->CurrentPnpState; commonExtension->CurrentPnpState = IRP_MN_STOP_DEVICE; } break; } case IRP_MN_QUERY_DEVICE_RELATIONS: { DEVICE_RELATION_TYPE type = irpStack->Parameters.QueryDeviceRelations.Type; DebugPrint((1, "ScsiPortFdoPnp - got " "IRP_MJ_QUERY_DEVICE_RELATIONS\n")); DebugPrint((1, "\ttype is %d\n", type)); if (type == BusRelations) { PSP_ENUMERATION_REQUEST request; request = InterlockedCompareExchangePointer( &adapter->PnpEnumRequestPtr, NULL, &(adapter->PnpEnumerationRequest)); if (request != NULL) { RtlZeroMemory(request, sizeof(SP_ENUMERATION_REQUEST)); request->CompletionRoutine = SpQueryDeviceRelationsCompletion; request->Context = Irp; request->CompletionStatus = &(Irp->IoStatus.Status); IoMarkIrpPending(Irp); SpEnumerateAdapterAsynchronous(adapter, request, FALSE); return STATUS_PENDING; } else { ASSERT(FALSE && "Unexpected!! Concurrent QDR requests"); Irp->IoStatus.Status = STATUS_DEVICE_BUSY; Irp->IoStatus.Information = 0L; sendDown = FALSE; } } break; } case IRP_MN_DEVICE_USAGE_NOTIFICATION: // // Send the irp down to the device below us. // Since there's a remove lock outstanding on the PDO we can release // the lock on the FDO before sending this down. // Irp->IoStatus.Status = STATUS_SUCCESS; break; case IRP_MN_QUERY_ID: { PWCHAR newIdList; // // We add the id GEN_SCSIADAPTER to the compatible ID's for any // adapters controlled by scsiport. // DebugPrint((2, "ScsiPortFdoPnp: got IRP_MN_QUERY_ID\n")); if(irpStack->Parameters.QueryId.IdType != BusQueryCompatibleIDs) { sendDown = TRUE; break; } status = SpSendIrpSynchronous(commonExtension->LowerDeviceObject, Irp); newIdList = ScsiPortAddGenericControllerId( DeviceObject->DriverObject, (PWCHAR) (Irp->IoStatus.Information)); if(newIdList == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; } else { status = STATUS_SUCCESS; if(Irp->IoStatus.Information != 0L) { ExFreePool((PVOID) Irp->IoStatus.Information); } Irp->IoStatus.Information = (ULONG_PTR) newIdList; } sendDown = FALSE; break; } default: { PIO_STACK_LOCATION nextIrpStack = IoGetNextIrpStackLocation(Irp); DebugPrint((1, "ScsiPortFdoPnp: Unimplemented PNP/POWER minor " "code %d\n", irpStack->MinorFunction)); break; } } if (sendDown) { IoCopyCurrentIrpStackLocationToNext(Irp); SpReleaseRemoveLock(DeviceObject, Irp); status = IoCallDriver(commonExtension->LowerDeviceObject, Irp); } else { SpReleaseRemoveLock(DeviceObject, Irp); Irp->IoStatus.Status = status; SpCompleteRequest(DeviceObject, Irp, NULL, IO_NO_INCREMENT); } return status; } NTSTATUS ScsiPortStartAdapter( IN PDEVICE_OBJECT Adapter ) /*++ Routine Descripion: This routine will start an adapter. It is illegal to start the device if it has already been started. Arguments: Adapter - a pointer to the functional device object (adapter) being started Return Value: STATUS_SUCCESS if the device was properly started and enumeration was attempted - or if the device had previously been started. error value indicating the cause of the failure otherwise --*/ { PSCSIPORT_DRIVER_EXTENSION driverExtension = IoGetDriverObjectExtension(Adapter->DriverObject, ScsiPortInitialize); PADAPTER_EXTENSION adapterExtension = Adapter->DeviceExtension; PCOMMON_EXTENSION commonExtension = Adapter->DeviceExtension; UCHAR pathId; PAGED_CODE(); ASSERT(driverExtension != NULL); ASSERT_FDO(Adapter); ASSERT(commonExtension->CurrentPnpState != IRP_MN_START_DEVICE); ASSERT(commonExtension->IsInitialized); ASSERT(((Adapter->Flags & DO_DEVICE_INITIALIZING) == 0)); DebugPrint((1, "ScsiPortStartAdapter - starting adapter %#p\n", Adapter)); // // Start timer. Request timeout counters // in the logical units have already been // initialized. // adapterExtension->TickCount = 0; IoStartTimer(Adapter); // // Initialize WMI support. // if (adapterExtension->CommonExtension.WmiInitialized == FALSE) { // // Build the SCSIPORT WMI registration information buffer for this FDO. // SpWmiInitializeSpRegInfo(Adapter); // // Register this device object only if the miniport supports WMI and/or // SCSIPORT will support certain WMI GUIDs on behalf of the miniport. // if (adapterExtension->CommonExtension.WmiScsiPortRegInfoBuf != NULL) { // // Register this functional device object as a WMI data provider, // instructing WMI that it is ready to receive WMI IRPs. // DebugPrint((1, "ScsiPortStartAdapter: REGISTER FDO:%p\n", Adapter)); IoWMIRegistrationControl(Adapter, WMIREG_ACTION_REGISTER); adapterExtension->CommonExtension.WmiInitialized = TRUE; } // // Allocate several WMI_MINIPORT_REQUEST_ITEM blocks to satisfy a // potential onslaught of WMIEvent notifications by the miniport. // if (adapterExtension->CommonExtension.WmiMiniPortSupport) { // // Currently, we only allocate two per new adapter (FDO). // SpWmiInitializeFreeRequestList(Adapter, 2); } } // // Create a well known name for this device object by making a symbolic // link to the PDO. Even if this fails, the start should still succeed. // if(adapterExtension->PortNumber == -1) { NTSTATUS status; UNICODE_STRING unicodePdoName; ULONG number; UNICODE_STRING interfaceName; RtlInitUnicodeString(&unicodePdoName, adapterExtension->DeviceName); // // Start at zero and keep going through all the possible numbers // until we find a hole. This is an unfortunate requirement for // legacy support since most old class drivers will give up if // they find a hole in the scsiport numbers. // number = 0; do { WCHAR wideLinkName[64]; UNICODE_STRING unicodeLinkName; // // Create the well known name string first. // swprintf(wideLinkName, L"\\Device\\ScsiPort%d", number); RtlInitUnicodeString(&unicodeLinkName, wideLinkName); status = IoCreateSymbolicLink(&unicodeLinkName, &unicodePdoName); if(NT_SUCCESS(status)) { // // Found a spot - mark this one as named so we don't go // through this trouble again and save the port number // adapterExtension->PortNumber = number; // // Create the dos port driver name. If there's a collision // then just forget it. // swprintf(wideLinkName, L"\\DosDevices\\Scsi%d:", number); RtlInitUnicodeString(&unicodeLinkName, wideLinkName); IoCreateSymbolicLink(&unicodeLinkName, &unicodePdoName); } else { number++; } } while (status == STATUS_OBJECT_NAME_COLLISION); // // Increment the count of scsiport device // IoGetConfigurationInformation()->ScsiPortCount++; // // Create a device map entry for this adapter. // SpBuildDeviceMapEntry(commonExtension); // // Register our device interface. // status = IoRegisterDeviceInterface(adapterExtension->LowerPdo, &StoragePortClassGuid, NULL, &interfaceName); if(NT_SUCCESS(status)) { adapterExtension->InterfaceName = interfaceName; status = IoSetDeviceInterfaceState(&interfaceName, TRUE); if(!NT_SUCCESS(status)) { RtlFreeUnicodeString(&interfaceName); RtlInitUnicodeString(&(adapterExtension->InterfaceName), NULL); } } } // // Set the force next bus scan bit. // adapterExtension->ForceNextBusScan = TRUE; return STATUS_SUCCESS; } NTSTATUS ScsiPortGetDeviceId( IN PDEVICE_OBJECT Pdo, OUT PUNICODE_STRING UnicodeString ) /*++ Routine Description: This routine will allocate space for and fill in a device id string for the specified Pdo. This string is generated from the bus type (scsi) and the type of the device. Arguments: Pdo - a pointer to the physical device object being queried UnicodeString - a pointer to an already allocated unicode string structure. This routine will allocate and fill in the buffer of this structure Returns: status --*/ { PLOGICAL_UNIT_EXTENSION physicalExtension = Pdo->DeviceExtension; PINQUIRYDATA inquiryData = &(physicalExtension->InquiryData); UCHAR buffer[256]; PUCHAR rawIdString = buffer; ANSI_STRING ansiIdString; ULONG whichString; PAGED_CODE(); ASSERT(UnicodeString != NULL); RtlZeroMemory(buffer, sizeof(buffer)); sprintf(rawIdString, "SCSI\\%s", SpGetDeviceTypeInfo(inquiryData->DeviceType)->DeviceTypeString); rawIdString += strlen(rawIdString); ASSERT(*rawIdString == '\0'); for(whichString = 0; whichString < 3; whichString++) { PUCHAR headerString; PUCHAR sourceString; ULONG sourceStringLength; ULONG i; switch(whichString) { // // Vendor Id // case 0: { sourceString = inquiryData->VendorId; sourceStringLength = sizeof(inquiryData->VendorId); headerString = "Ven"; break; } // // Product Id // case 1: { sourceString = inquiryData->ProductId; sourceStringLength = sizeof(inquiryData->ProductId); headerString = "Prod"; break; } // // Product Revision Level // case 2: { sourceString = inquiryData->ProductRevisionLevel; sourceStringLength = sizeof(inquiryData->ProductRevisionLevel); headerString = "Rev"; break; } } // // Start at the end of the source string and back up until we find a // non-space, non-null character. // for(; sourceStringLength > 0; sourceStringLength--) { if((sourceString[sourceStringLength - 1] != ' ') && (sourceString[sourceStringLength - 1] != '\0')) { break; } } // // Throw the header string into the block // sprintf(rawIdString, "&%s_", headerString); rawIdString += strlen(headerString) + 2; // // Spew the string into the device id // for(i = 0; i < sourceStringLength; i++) { *rawIdString = (sourceString[i] != ' ') ? (sourceString[i]) : ('_'); rawIdString++; } ASSERT(*rawIdString == '\0'); } RtlInitAnsiString(&ansiIdString, buffer); DebugPrint((1, "DeviceId for logical unit %#p is %Z\n", Pdo, &ansiIdString)); return RtlAnsiStringToUnicodeString(UnicodeString, &ansiIdString, TRUE); } NTSTATUS ScsiPortGetInstanceId( IN PDEVICE_OBJECT Pdo, OUT PUNICODE_STRING UnicodeString ) /*++ Routine Description: This routine will allocate space for and fill in an instance id string for the specified Pdo. This string will be generated either from the device type + serial number of the device (if it has a serial number) or from the address of the device. Arguments: Pdo - a pointer to the physical device object being queried UnicodeString - a pointer to an already allocated unicode string structure. This routine will allocate and fill in the buffer of this structure Returns: status --*/ { PLOGICAL_UNIT_EXTENSION physicalExtension = Pdo->DeviceExtension; PDRIVER_OBJECT driverObject = Pdo->DriverObject; PSCSIPORT_DEVICE_TYPE deviceTypeInfo; UCHAR idStringBuffer[64]; ANSI_STRING ansiIdString; PAGED_CODE(); ASSERT(UnicodeString != NULL); // // can't use serial number even if it exists since a device which is // multiply connected to the same bus (dual-ported device) will have // the same serial number at each connection and would confuse the PNP. // sprintf(idStringBuffer, "%x%x%x", physicalExtension->PathId, physicalExtension->TargetId, physicalExtension->Lun ); RtlInitAnsiString(&ansiIdString, idStringBuffer); return RtlAnsiStringToUnicodeString(UnicodeString, &ansiIdString, TRUE); } NTSTATUS ScsiPortGetCompatibleIds( IN PDRIVER_OBJECT DriverObject, IN PINQUIRYDATA InquiryData, OUT PUNICODE_STRING UnicodeString ) /*++ Routine Description: This routine will allocate space for and fill in a compatible id multi string for the specified Pdo. This string is generated using the bus and device types for the device Arguments: InquiryData - the inquiry data to generate compatible ids from. UnicodeString - a pointer to an already allocated unicode string structure. This routine will allocate and fill in the buffer of this structure Returns: status --*/ { UCHAR s[sizeof("SCSI\\DEVICE_TYPE_GOES_HERE")]; PSTR stringBuffer[] = { s, "SCSI\\RAW", NULL}; // // Fill in the scsi specific string // sprintf(stringBuffer[0], "SCSI\\%s", SpGetDeviceTypeInfo(InquiryData->DeviceType)->DeviceTypeString); // // Set up the first id string // return ScsiPortStringArrayToMultiString( DriverObject, UnicodeString, stringBuffer); } #define MPIO_DEVICE_LIST L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Control\\MPDEV" NTSTATUS ScsiPortDetermineGenId( IN PDRIVER_OBJECT DriverObject, IN PINQUIRYDATA InquiryData, OUT PUCHAR GenericId ) /*++ Routine Description: This routine will return the correct Compatible ID for the device in the caller supplied 'CompatibleId'. The value will either be a GenXXX or MPIODisk, depending upon results from routines exported by portlib.lib Arguments: DriverObject - The port-driver's DriverObject. InquiryData - The inquiry data of the device for which a compatID is being built. GenericId - Storage for the NULL-terminated buffer that will contain the correct ID. Returns: SUCCESS or some error status from the library routines - (INSUFFICIENT_RESOURCES or some registry access error). --*/ { PSCSIPORT_DEVICE_TYPE devTypeInfo; PSCSIPORT_DRIVER_EXTENSION driverExtension; UNICODE_STRING mpioRegistryPath; UCHAR vendorId[9]; UCHAR productId[17]; UCHAR genericId[40]; BOOLEAN mpio = FALSE; NTSTATUS status = STATUS_SUCCESS; PAGED_CODE(); // // Init compatibleId, as it will always contain something. // RtlZeroMemory(genericId, 40); // // Get the type info for this device. // devTypeInfo = SpGetDeviceTypeInfo(InquiryData->DeviceType); // // Default to the GenXXX of the device. If it's not a disk, errors occur // in this routine, or it's a non-mpio supported disk, then this value // will be returned. // RtlCopyMemory(genericId, devTypeInfo->GenericTypeString, strlen(devTypeInfo->GenericTypeString)); // // Check whether this is a disk or not. If not, the generic id // built earlier will be used. // if (InquiryData->DeviceType == DIRECT_ACCESS_DEVICE) { NTSTATUS status2; // // Get the driverObject extension. This contains storage for the // supported device list. // driverExtension = IoGetDriverObjectExtension( DriverObject, ScsiPortInitialize); ASSERT(driverExtension != NULL); // // Determine whether this contains the list, or is uninitialised. // if (driverExtension->MPIOSupportedDeviceList.Buffer == NULL) { // // Build the registry path string. // RtlInitUnicodeString(&mpioRegistryPath, MPIO_DEVICE_LIST); // // Call into the port-driver library to get the list. // Any failures here will be dealt with below. // status2 = PortGetMPIODeviceList( &mpioRegistryPath, &driverExtension->MPIOSupportedDeviceList); } else { // // Buffer is there, so load up 'success'. // status2 = STATUS_SUCCESS; } if (NT_SUCCESS(status2)) { // // Get NULL terminated copies of Vendor and Product ID from the // passed-in inquiry data. // RtlZeroMemory(vendorId, 9); RtlZeroMemory(productId, 17); RtlCopyMemory(vendorId, InquiryData->VendorId, 8); RtlCopyMemory(productId, InquiryData->ProductId, 16); // // Call into the port-driver library to determine whether this // device is supported under multipath. // mpio = PortIsDeviceMPIOSupported( &driverExtension->MPIOSupportedDeviceList, vendorId, productId); if (mpio) { // // Build MPIODisk instead of GenDisk. Mpdev.sys loads against // this. // RtlCopyMemory(genericId, "MPIODisk", 8); } } } if (NT_SUCCESS(status)) { // // Copy the built ID into the caller's buffer. // RtlCopyMemory(GenericId, genericId, strlen(genericId)); } return status; } NTSTATUS ScsiPortGetHardwareIds( IN PDRIVER_OBJECT DriverObject, IN PINQUIRYDATA InquiryData, OUT PUNICODE_STRING UnicodeString ) /*++ Routine Description: This routine will allocate space for and fill in a hardware id multi string for the specified Pdo. This string is generated using the device type and the inquiry data. Arguments: InquiryData - the inquiry data to be converted into id strings. UnicodeString - a pointer to an already allocated unicode string structure. This routine will allocate and fill in the buffer of this structure Returns: status --*/ #define NUMBER_HARDWARE_STRINGS 6 { PSCSIPORT_DEVICE_TYPE devTypeInfo = SpGetDeviceTypeInfo(InquiryData->DeviceType); ULONG i; PSTR strings[NUMBER_HARDWARE_STRINGS + 1]; UCHAR scratch[64]; UCHAR genericId[40]; NTSTATUS status; SIZE_T len; PAGED_CODE(); // // Zero out the string buffer // RtlZeroMemory(strings, sizeof(strings)); RtlZeroMemory(genericId, 40); // // Build the compatible ID for this device. // status = ScsiPortDetermineGenId(DriverObject, InquiryData, genericId); if (!NT_SUCCESS(status)) { return status; } try { for(i = 0; i < NUMBER_HARDWARE_STRINGS; i++) { RtlZeroMemory(scratch, sizeof(scratch)); // // Build each of the hardware id's // switch(i) { // // Bus + Dev Type + Vendor + Product + Revision // case 0: { sprintf(scratch, "SCSI\\%s", devTypeInfo->DeviceTypeString); CopyField(scratch + strlen(scratch), InquiryData->VendorId, 8, '_'); CopyField(scratch + strlen(scratch), InquiryData->ProductId, 16, '_'); CopyField(scratch + strlen(scratch), InquiryData->ProductRevisionLevel, 4, '_'); break; } // // bus + device + vendor + product // case 1: { sprintf(scratch, "SCSI\\%s", devTypeInfo->DeviceTypeString); CopyField(scratch + strlen(scratch), InquiryData->VendorId, 8, '_'); CopyField(scratch + strlen(scratch), InquiryData->ProductId, 16, '_'); break; } // // bus + device + vendor // case 2: { sprintf(scratch, "SCSI\\%s", devTypeInfo->DeviceTypeString); CopyField(scratch + strlen(scratch), InquiryData->VendorId, 8, '_'); break; } // // bus \ vendor + product + revision[0] // case 3: { sprintf(scratch, "SCSI\\"); // // Fall through to the next set. // } // // vendor + product + revision[0] (win9x) // case 4: { CopyField(scratch + strlen(scratch), InquiryData->VendorId, 8, '_'); CopyField(scratch + strlen(scratch), InquiryData->ProductId, 16, '_'); CopyField(scratch + strlen(scratch), InquiryData->ProductRevisionLevel, 1, '_'); break; } case 5: { // // Copy over the previously built generic id. // RtlCopyMemory(scratch, genericId, strlen(genericId)); break; } default: { ASSERT(FALSE); break; } } len = strlen(scratch); if(len != 0) { strings[i] = SpAllocatePool(PagedPool, strlen(scratch) + sizeof(UCHAR), SCSIPORT_TAG_PNP_ID, DriverObject); if(strings[i] == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; leave; } RtlCopyMemory(strings[i], scratch, len+1); } else { break; } } status = ScsiPortStringArrayToMultiString(DriverObject, UnicodeString, strings); leave; } finally { for(i = 0; i < NUMBER_HARDWARE_STRINGS; i++) { if(strings[i]) { ExFreePool(strings[i]); } } } return status; } #undef NUMBER_HARDWARE_STRINGS VOID CopyField( IN PUCHAR Destination, IN PUCHAR Source, IN ULONG Count, IN UCHAR Change ) /*++ Routine Description: This routine will copy Count string bytes from Source to Destination. If it finds a nul byte in the Source it will translate that and any subsequent bytes into Change. It will also replace spaces with the specified character. Arguments: Destination - the location to copy bytes Source - the location to copy bytes from Count - the number of bytes to be copied Return Value: none --*/ { ULONG i = 0; BOOLEAN pastEnd = FALSE; PAGED_CODE(); for(i = 0; i < Count; i++) { if(!pastEnd) { if(Source[i] == 0) { pastEnd = TRUE; Destination[i] = Change; } else if(Source[i] == ' ') { Destination[i] = Change; } else { Destination[i] = Source[i]; } } else { Destination[i] = Change; } } return; } NTSTATUS ScsiPortInitPnpAdapter( IN PDEVICE_OBJECT Fdo ) /*++ Routine Description: This routine will find and (if found) initialize a specific adapter. The adapter is specified by the ResourceList passed in. This routine will initialize a port configuration structure using the information provided in the resource list and call the miniport's find adapter routine to locate the adapter. If that completes successfully, the miniport's initialize routine will be called. This will connect the interrupts and initialize the timers and DPCs as well as allocating common buffers and request data structures. Arguments: Fdo - the device object for the adapter being initialized Return Value: status --*/ { PADAPTER_EXTENSION adapter = Fdo->DeviceExtension; PSCSIPORT_DRIVER_EXTENSION driverExtension = IoGetDriverObjectExtension(Fdo->DriverObject, ScsiPortInitialize); INTERFACE_TYPE interfaceType; ULONG resultLength; PHW_INITIALIZATION_DATA hwInitializationData = NULL; CONFIGURATION_CONTEXT configurationContext; PPORT_CONFIGURATION_INFORMATION configInfo = NULL; BOOLEAN callAgain; OBJECT_ATTRIBUTES objectAttributes; ULONG uniqueId; PHW_DEVICE_EXTENSION hwDeviceExtension; ULONG hwDeviceExtensionSize; PUNICODE_STRING registryPath = &(driverExtension->RegistryPath); NTSTATUS status; PAGED_CODE(); // // Find the init data for this interface type // interfaceType = SpGetPdoInterfaceType(adapter->LowerPdo); hwInitializationData = SpFindInitData(driverExtension, interfaceType); if(hwInitializationData == NULL) { // // Hmmm. The miniport never reported this adapter type. We can't // start the device since we don't know what the correct entry points // are. Pretend it doesn't exist // return STATUS_NO_SUCH_DEVICE; } hwDeviceExtensionSize = hwInitializationData->DeviceExtensionSize + sizeof(HW_DEVICE_EXTENSION); RtlZeroMemory(&configurationContext, sizeof(configurationContext)); if(hwInitializationData->NumberOfAccessRanges != 0) { configurationContext.AccessRanges = SpAllocatePool(PagedPool, (hwInitializationData->NumberOfAccessRanges * sizeof(ACCESS_RANGE)), SCSIPORT_TAG_ACCESS_RANGE, Fdo->DriverObject); if(configurationContext.AccessRanges == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } } try { ULONG portConfigSize; // // Allocate the HwDeviceExtension first - it's easier to deallocate :) // hwDeviceExtension = SpAllocatePool(NonPagedPool, hwDeviceExtensionSize, SCSIPORT_TAG_DEV_EXT, Fdo->DriverObject); if(hwDeviceExtension == NULL) { DebugPrint((1, "ScsiPortInitialize: Could not allocate " "HwDeviceExtension\n")); status = STATUS_INSUFFICIENT_RESOURCES; uniqueId = __LINE__; leave; } RtlZeroMemory(hwDeviceExtension, hwDeviceExtensionSize); // // Setup device extension pointers // SpInitializeAdapterExtension(adapter, hwInitializationData, hwDeviceExtension); // // initialize the miniport config info buffer // status = SpInitializeConfiguration( adapter, registryPath, hwInitializationData, &configurationContext); if(!NT_SUCCESS(status)) { uniqueId = __LINE__; leave; } // // Allocate a config-info structure and access ranges for the // miniport drivers to use // portConfigSize = sizeof(PORT_CONFIGURATION_INFORMATION); portConfigSize += hwInitializationData->NumberOfAccessRanges * sizeof(ACCESS_RANGE); portConfigSize += 7; portConfigSize &= ~7; configInfo = SpAllocatePool(NonPagedPool, portConfigSize, SCSIPORT_TAG_PORT_CONFIG, Fdo->DriverObject); if(configInfo == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; uniqueId = __LINE__; leave; } adapter->PortConfig = configInfo; // // Copy the current structure to the writable copy // RtlCopyMemory(configInfo, &configurationContext.PortConfig, sizeof(PORT_CONFIGURATION_INFORMATION)); // // Copy the SrbExtensionSize from device extension to ConfigInfo. // A check will be made later to determine if the miniport updated // this value // configInfo->SrbExtensionSize = adapter->SrbExtensionSize; configInfo->SpecificLuExtensionSize = adapter->HwLogicalUnitExtensionSize; // // initialize the access range array // if(hwInitializationData->NumberOfAccessRanges != 0) { configInfo->AccessRanges = (PVOID) (configInfo + 1); // // Quadword align this // (ULONG_PTR) (configInfo->AccessRanges) += 7; (ULONG_PTR) (configInfo->AccessRanges) &= ~7; RtlCopyMemory(configInfo->AccessRanges, configurationContext.AccessRanges, (hwInitializationData->NumberOfAccessRanges * sizeof(ACCESS_RANGE))); } // // Set the adapter interface type. // configInfo->AdapterInterfaceType = interfaceType; // // Since we've been handed resources we need to build a config info // structure before we can call the find adapter routine // SpBuildConfiguration(adapter, hwInitializationData, configInfo); SpGetSlotNumber(Fdo, configInfo, adapter->AllocatedResources); // // Get the miniport configuration inofmraiton // status = SpCallHwFindAdapter(Fdo, hwInitializationData, NULL, &configurationContext, configInfo, &callAgain); if(status == STATUS_DEVICE_DOES_NOT_EXIST) { adapter->PortConfig = NULL; ExFreePool(configInfo); } else if(NT_SUCCESS(status)) { status = SpAllocateAdapterResources(Fdo); if(NT_SUCCESS(status)) { PCOMMON_EXTENSION commonExtension = Fdo->DeviceExtension; BOOLEAN stopped; // // If the device's previous state is IRP_MN_STOP_DEVICE then // it should have a disable count of 1. Clear the disabled // state. // stopped = ((commonExtension->CurrentPnpState == IRP_MN_STOP_DEVICE) ? TRUE : FALSE); if(stopped) { ASSERT(adapter->CommonExtension.PreviousPnpState == IRP_MN_START_DEVICE); ASSERT(adapter->DisableCount == 1); adapter->DisableCount = 0; CLEAR_FLAG(adapter->InterruptData.InterruptFlags, PD_DISABLE_INTERRUPTS); } status = SpCallHwInitialize(Fdo); if(stopped) { KIRQL oldIrql; PVOID sectionHandle; // // Restart i/o processing. // sectionHandle = MmLockPagableCodeSection(ScsiPortInitPnpAdapter); KeRaiseIrql(DISPATCH_LEVEL, &oldIrql); IoStartNextPacket(Fdo, FALSE); KeLowerIrql(oldIrql); MmUnlockPagableImageSection(sectionHandle); } } } } finally { if(!NT_SUCCESS(status)) { PIO_ERROR_LOG_PACKET errorLogEntry; // // An error occured - log it. // errorLogEntry = (PIO_ERROR_LOG_PACKET) IoAllocateErrorLogEntry( Fdo, sizeof(IO_ERROR_LOG_PACKET)); if(errorLogEntry != NULL) { errorLogEntry->ErrorCode = IO_ERR_DRIVER_ERROR; errorLogEntry->UniqueErrorValue = uniqueId; errorLogEntry->FinalStatus = status; errorLogEntry->DumpDataSize = 0; IoWriteErrorLogEntry(errorLogEntry); } // // Clean up the last device object which is not used. // SpDestroyAdapter(adapter, FALSE); if (configurationContext.AccessRanges != NULL) { ExFreePool(configurationContext.AccessRanges); } if (configurationContext.Parameter != NULL) { ExFreePool(configurationContext.Parameter); } } else { // // Determine which adapter control functions this miniport will // support for the adapter. // SpGetSupportedAdapterControlFunctions(adapter); } } return status; } PHW_INITIALIZATION_DATA SpFindInitData( IN PSCSIPORT_DRIVER_EXTENSION DriverExtension, IN INTERFACE_TYPE InterfaceType ) /*++ Routine Description: This routine will search the list of chained init structures looking for the first one that matches the interface type in the resource list. Arguments: DriverExtension - The driver extension to be searched ResourceList - this resource list describes the (interface) type of the adapter we are looking for Return Value: a pointer to the HW_INITIALIZATION_DATA structure for this interface type NULL if none was found --*/ { PSP_INIT_CHAIN_ENTRY chainEntry = DriverExtension->InitChain; PAGED_CODE(); while(chainEntry != NULL) { if(chainEntry->InitData.AdapterInterfaceType == InterfaceType) { return &(chainEntry->InitData); } chainEntry = chainEntry->NextEntry; } return NULL; } NTSTATUS SpStartLowerDevice( IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp ) /*++ Routine Description: This routine will forward the start request to the next lower device and block until it's completion. Arguments: DeviceObject - the device to which the start request was issued. Irp - the start request Return Value: status --*/ { PADAPTER_EXTENSION adapter = DeviceObject->DeviceExtension; PCOMMON_EXTENSION commonExtension = DeviceObject->DeviceExtension; PKEVENT event; NTSTATUS status; PAGED_CODE(); event = SpAllocatePool(NonPagedPool, sizeof(KEVENT), SCSIPORT_TAG_EVENT, DeviceObject->DriverObject); if(event == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } KeInitializeEvent(event, SynchronizationEvent, FALSE); IoCopyCurrentIrpStackLocationToNext(Irp); IoSetCompletionRoutine(Irp, SpSignalCompletion, event, TRUE, TRUE, TRUE); status = IoCallDriver(commonExtension->LowerDeviceObject, Irp); if(status == STATUS_PENDING) { KeWaitForSingleObject(event, Executive, KernelMode, FALSE, NULL); status = Irp->IoStatus.Status; } if(NT_SUCCESS(status)) { PIO_STACK_LOCATION irpStack; // // Now go and retrieve any interfaces we need from the lower device. // Irp->IoStatus.Status = STATUS_NOT_SUPPORTED; irpStack = IoGetNextIrpStackLocation(Irp); irpStack->Parameters.QueryInterface.InterfaceType = (LPGUID) &GUID_BUS_INTERFACE_STANDARD; irpStack->Parameters.QueryInterface.Size = sizeof(BUS_INTERFACE_STANDARD); irpStack->Parameters.QueryInterface.Version = 1; irpStack->Parameters.QueryInterface.Interface = (PINTERFACE) &(adapter->LowerBusInterfaceStandard); irpStack->MajorFunction = IRP_MJ_PNP; irpStack->MinorFunction = IRP_MN_QUERY_INTERFACE; KeResetEvent(event); IoSetCompletionRoutine(Irp, SpSignalCompletion, event, TRUE, TRUE, TRUE); IoCallDriver(commonExtension->LowerDeviceObject, Irp); KeWaitForSingleObject(event, Executive, KernelMode, FALSE, NULL); if(NT_SUCCESS(Irp->IoStatus.Status)) { adapter->LowerBusInterfaceStandardRetrieved = TRUE; } else { DebugPrint((1, "LowerBusInterfaceStandard request returned " "%#08lx\n", Irp->IoStatus.Status)); adapter->LowerBusInterfaceStandardRetrieved = FALSE; } Irp->IoStatus.Status = status; } ExFreePool(event); return status; } VOID SpGetSlotNumber( IN PDEVICE_OBJECT Fdo, IN PPORT_CONFIGURATION_INFORMATION ConfigInfo, IN PCM_RESOURCE_LIST ResourceList ) /*++ Routine Description: This routine will open the registry key for the associated Pdo and try to retrieve the bus, slot and function numbers that will be stored there if this was a device we detected or one that the user has configured by hand. These values will be stored in the ConfigInfo structure for the adapter. If this information does not exist then the values will be filled with zeros and the IsVirtualSlot flag will be set in the Fdo for use by other routines. Arguments: Fdo - a pointer to the functional device object for this adapter ConfigInfo - the ConfigInfo structure to be changed Return Value: None --*/ { PADAPTER_EXTENSION adapter = Fdo->DeviceExtension; HANDLE instanceHandle = NULL; HANDLE parametersHandle = NULL; NTSTATUS status; PAGED_CODE(); adapter->IsInVirtualSlot = FALSE; try { OBJECT_ATTRIBUTES objectAttributes; UNICODE_STRING unicodeKeyName; status = IoOpenDeviceRegistryKey(adapter->LowerPdo, PLUGPLAY_REGKEY_DEVICE, KEY_READ, &instanceHandle); if(!NT_SUCCESS(status)) { leave; } RtlInitUnicodeString(&unicodeKeyName, L"Scsiport"); InitializeObjectAttributes( &objectAttributes, &unicodeKeyName, OBJ_CASE_INSENSITIVE, instanceHandle, NULL); status = ZwOpenKey(¶metersHandle, KEY_READ, &objectAttributes); if(!NT_SUCCESS(status)) { leave; } else { RTL_QUERY_REGISTRY_TABLE queryTable[3]; ULONG busNumber; ULONG slotNumber; ULONG negativeOne = 0xffffffff; RtlZeroMemory(queryTable, sizeof(queryTable)); queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT; queryTable[0].Name = L"SlotNumber"; queryTable[0].EntryContext = &slotNumber; queryTable[0].DefaultType = REG_DWORD; queryTable[0].DefaultData = &negativeOne; queryTable[0].DefaultLength = sizeof(ULONG); queryTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT; queryTable[1].Name = L"BusNumber"; queryTable[1].EntryContext = &busNumber; queryTable[1].DefaultType = REG_DWORD; queryTable[1].DefaultData = &negativeOne; queryTable[1].DefaultLength = sizeof(ULONG); status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE, (PWSTR) parametersHandle, queryTable, NULL, NULL); if(!NT_SUCCESS(status)) { leave; } if((busNumber == negativeOne) || (slotNumber == negativeOne)) { ConfigInfo->SystemIoBusNumber = ResourceList->List[0].BusNumber; ConfigInfo->SlotNumber = 0; adapter->IsInVirtualSlot = TRUE; } else { ConfigInfo->SystemIoBusNumber = busNumber; ConfigInfo->SlotNumber = slotNumber; adapter->IsInVirtualSlot = FALSE; } } } finally { // // If an error occurred then we'll need to try virtualizing this // adapter // if(!NT_SUCCESS(status)) { // // Send ourself a query capabilities IRP so that we can retrieve // slot and function numbers from PCI. // status = SpQueryCapabilities(adapter); if(NT_SUCCESS(status)) { ConfigInfo->SystemIoBusNumber = ResourceList->List[0].BusNumber; ConfigInfo->SlotNumber = adapter->VirtualSlotNumber.u.AsULONG; adapter->IsInVirtualSlot = TRUE; } } if(instanceHandle != NULL) { ZwClose(instanceHandle); } if(parametersHandle != NULL) { ZwClose(parametersHandle); } } return; } PSCSIPORT_DEVICE_TYPE SpGetDeviceTypeInfo( IN UCHAR DeviceType ) { PAGED_CODE(); if(DeviceType >= NUM_DEVICE_TYPE_INFO_ENTRIES) { return &(DeviceTypeInfo[NUM_DEVICE_TYPE_INFO_ENTRIES - 1]); } else { return &(DeviceTypeInfo[DeviceType]); } }; PWCHAR ScsiPortAddGenericControllerId( IN PDRIVER_OBJECT DriverObject, IN PWCHAR IdList ) /*++ Routine Description: This routine will attempt to add the id GEN_SCSIADAPTER to the provided list of compatible id's. Arguments: Pdo - a pointer to the physical device object being queried UnicodeString - a pointer to an already allocated unicode string structure. This routine will allocate and fill in the buffer of this structure Returns: status --*/ { ULONG stringLength = 0; ULONG i = 0; PWCHAR addedString = L"GEN_SCSIADAPTER"; PWCHAR newList; PWCHAR p; PAGED_CODE(); // // If strings were provided then count them to determine a length for the // new id list. // if(IdList != NULL) { i = 0; while((IdList[i] != UNICODE_NULL) || (IdList[i+1] != UNICODE_NULL)) { i++; } // // Compensate for the fact that we stopped counting just before the // first byte of the double-null. // i += 2; stringLength = i; } stringLength += wcslen(L"GEN_SCSIADAPTER"); // // We'll need to add in yet another NULL to terminate the current ending // string. // stringLength += 2; // // Allocate a new string list to replace the existing one with. // newList = SpAllocatePool(PagedPool, (stringLength * sizeof(WCHAR)), SCSIPORT_TAG_PNP_ID, DriverObject); if(newList == NULL) { return NULL; } RtlFillMemory(newList, (stringLength * sizeof(WCHAR)), '@'); // // If we were provided with a string then copy it into the buffer we just // allocated. // if(ARGUMENT_PRESENT(IdList)) { i = 0; while((IdList[i] != UNICODE_NULL) || (IdList[i+1] != UNICODE_NULL)) { newList[i] = IdList[i]; i++; } // // Terminate the string we just wrote. // newList[i] = UNICODE_NULL; p = &(newList[i+1]); } else { p = newList; } // // Copy the new id string into the buffer. // for(i = 0; addedString[i] != UNICODE_NULL; i++) { *p = addedString[i]; p++; } // // Write two unicode nulls to the string to terminate it. // *p = UNICODE_NULL; p++; *p = UNICODE_NULL; // // Set up the first id string // return newList; } NTSTATUS SpQueryCapabilities( IN PADAPTER_EXTENSION Adapter ) { DEVICE_CAPABILITIES capabilities; PIRP irp; PIO_STACK_LOCATION irpStack; KEVENT event; NTSTATUS status; PAGED_CODE(); // // Initialize the capabilities structure. // RtlZeroMemory(&capabilities, sizeof(DEVICE_CAPABILITIES)); capabilities.Size = sizeof(DEVICE_CAPABILITIES); capabilities.Version = 1; capabilities.Address = capabilities.UINumber = (ULONG)-1; // // Initialize the event we're going to wait on. // KeInitializeEvent(&event, SynchronizationEvent, FALSE); // // Allocate a new irp. // irp = SpAllocateIrp((CCHAR) (Adapter->DeviceObject->StackSize + 1), FALSE, Adapter->DeviceObject->DriverObject); if(irp == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } irpStack = IoGetNextIrpStackLocation(irp); irpStack->MajorFunction = IRP_MJ_PNP; irpStack->MinorFunction = IRP_MN_QUERY_CAPABILITIES; irpStack->Parameters.DeviceCapabilities.Capabilities = &capabilities; irp->IoStatus.Status = STATUS_NOT_SUPPORTED; IoSetCompletionRoutine(irp, SpSignalCompletion, &event, TRUE, TRUE, TRUE); // // Send the irp to ourself ... just in case we ever start modifying // the contents of the capabilities in our PNP dispatch routine. // IoCallDriver(Adapter->DeviceObject, irp); KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL); status = irp->IoStatus.Status; if(NT_SUCCESS(status)) { USHORT device; USHORT function; device = (USHORT) (capabilities.Address >> 0x10); function = (USHORT) (capabilities.Address & 0x0000ffff); Adapter->VirtualSlotNumber.u.bits.DeviceNumber = device; Adapter->VirtualSlotNumber.u.bits.FunctionNumber = function; } else { Adapter->VirtualSlotNumber.u.AsULONG = 0; } IoFreeIrp(irp); return status; } BOOLEAN SpGetInterrupt( IN PCM_RESOURCE_LIST FullResourceList, OUT ULONG *Irql, OUT ULONG *Vector, OUT KAFFINITY *Affinity ) /*++ Routine Description: Given a full resource list returns the interrupt. Arguments: FullResourceList - the resource list. Irql - returns the irql for the interrupt. Vector - returns the vector for the interrupt. Affinity - returns the affinity for the interrupt. Return Value: TRUE if an interrupt is found. FALSE if none was found (in which case the output parameters are not valid. --*/ { ULONG rangeNumber; ULONG index; PCM_FULL_RESOURCE_DESCRIPTOR resourceList; PCM_PARTIAL_RESOURCE_DESCRIPTOR partialData; PAGED_CODE(); rangeNumber = 0; resourceList = FullResourceList->List; for (index = 0; index < resourceList->PartialResourceList.Count; index++) { partialData = &resourceList->PartialResourceList.PartialDescriptors[index]; if(partialData->Type == CmResourceTypeInterrupt) { *Irql = partialData->u.Interrupt.Level; *Vector = partialData->u.Interrupt.Vector; *Affinity = partialData->u.Interrupt.Affinity; return TRUE; } } return FALSE; } VOID SpQueryDeviceRelationsCompletion( IN PADAPTER_EXTENSION Adapter, IN PSP_ENUMERATION_REQUEST Request, IN NTSTATUS Unused ) { PIRP irp = (PIRP) Request->Context; PDEVICE_RELATIONS deviceRelations; PDEVICE_OBJECT lowerDevice = Adapter->CommonExtension.LowerDeviceObject; NTSTATUS status; PAGED_CODE(); ASSERT_FDO(Adapter->DeviceObject); // // Pnp is done in a system thread - we shouldn't get a user-mode APC to this // thread. // ASSERT(Unused != STATUS_USER_APC); // // Return the list of devices on the bus // status = SpExtractDeviceRelations(Adapter, BusRelations, &deviceRelations); if(NT_SUCCESS(status)) { ULONG i; DebugPrint((2, "SpQueryDeviceRelationsCompletion: Found %d devices " "on adapter %#p\n", deviceRelations->Count, Adapter)); for(i = 0; i < deviceRelations->Count; i++) { DebugPrint((2, "/t#%2d: device %#p\n", i, deviceRelations->Objects[i])); } } // // Put the pointer to the enumeration request object back. // Request = InterlockedCompareExchangePointer( &Adapter->PnpEnumRequestPtr, &(Adapter->PnpEnumerationRequest), NULL); ASSERT(Request == NULL); // // Store the status and the return information in the IRP. // irp->IoStatus.Status = status; if(NT_SUCCESS(status)) { irp->IoStatus.Information = (ULONG_PTR) deviceRelations; } else { irp->IoStatus.Information = (ULONG_PTR) NULL; } return; }