/*++ Copyright (C) Microsoft Corporation, 1990 - 1999 Module Name: port.c Abstract: This is the NT SCSI port driver. This file contains the initialization code. Authors: Mike Glass Jeff Havens Environment: kernel mode only Notes: This module is a driver dll for scsi miniports. Revision History: --*/ #include "port.h" #define __FILE_ID__ 'init' #if DBG static const char *__file__ = __FILE__; #endif // // Instantiate GUIDs for this module // #include #include #include #include ULONG ScsiPortLegacyAdapterDetection = FALSE; PVOID ScsiDirectory = NULL; // // Global list of adapter device objects. This is used to maintain a tag // value for all the adapters. This tag is used as a lookup key by the // lookaside list allocators in order to find the device object. // KSPIN_LOCK ScsiGlobalAdapterListSpinLock; PDEVICE_OBJECT *ScsiGlobalAdapterList = (PVOID) -1; ULONG ScsiGlobalAdapterListElements = 0; // // Indicates that the system can handle 64 bit physical addresses. // ULONG Sp64BitPhysicalAddresses = FALSE; // // Debugging switches. // ULONG SpRemapBuffersByDefault = FALSE; #if defined(NEWQUEUE) // // Default values that dictate the number of requests we will handle per zone // before advancing to the next zone and the number of consecutive requests we // will handle to a given sector before advancing off that sector. These // values may be modified through through registry settings. // ULONG SpPerZoneLimit = 1000; ULONG SpPerBlockLimit = 5; #endif // NEWQUEUE VOID SpCreateScsiDirectory( VOID ); NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ); ULONG SpGetBusData( IN PADAPTER_EXTENSION Adapter, IN PDEVICE_OBJECT Pdo OPTIONAL, IN BUS_DATA_TYPE BusDataType, IN ULONG BusNumber, IN ULONG SlotNumber, IN PVOID Buffer, IN ULONG Length ); NTSTATUS SpAllocateDriverExtension( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath, OUT PSCSIPORT_DRIVER_EXTENSION *DriverExtension ); ULONG SpQueryPnpInterfaceFlags( IN PSCSIPORT_DRIVER_EXTENSION DriverExtension, IN INTERFACE_TYPE InterfaceType ); NTSTATUS SpInitializeSrbDataLookasideList( IN PDEVICE_OBJECT AdapterObject ); VOID SpInitializeRequestSenseParams( IN PADAPTER_EXTENSION AdapterExtension ); VOID SpInitializePerformanceParams( IN PADAPTER_EXTENSION AdapterExtension ); VOID SpInitializePowerParams( IN PADAPTER_EXTENSION AdapterExtension ); #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, ScsiPortInitialize) #pragma alloc_text(PAGE, SpAllocateDriverExtension) #pragma alloc_text(PAGE, SpGetCommonBuffer) #pragma alloc_text(PAGE, SpInitializeConfiguration) #pragma alloc_text(PAGE, SpBuildResourceList) #pragma alloc_text(PAGE, SpParseDevice) #pragma alloc_text(PAGE, GetPciConfiguration) #pragma alloc_text(PAGE, SpBuildConfiguration) #pragma alloc_text(PAGE, SpQueryPnpInterfaceFlags) #pragma alloc_text(PAGE, SpConfigurationCallout) #pragma alloc_text(PAGE, SpReportNewAdapter) #pragma alloc_text(PAGE, SpCreateAdapter) #pragma alloc_text(PAGE, SpInitializeAdapterExtension) #pragma alloc_text(PAGE, ScsiPortInitLegacyAdapter) #pragma alloc_text(PAGE, SpAllocateAdapterResources) #pragma alloc_text(PAGE, SpOpenDeviceKey) #pragma alloc_text(PAGE, SpOpenParametersKey) #pragma alloc_text(PAGE, SpInitializeRequestSenseParams) #pragma alloc_text(PAGE, SpInitializePerformanceParams) #pragma alloc_text(PAGE, SpInitializePowerParams) #pragma alloc_text(PAGE, SpGetRegistryValue) #pragma alloc_text(PAGELOCK, SpInitializeSrbDataLookasideList) #pragma alloc_text(INIT, DriverEntry) #pragma alloc_text(PAGE, SpCreateScsiDirectory) #endif ULONG ScsiPortInitialize( IN PVOID Argument1, IN PVOID Argument2, IN PHW_INITIALIZATION_DATA HwInitializationData, IN PVOID HwContext OPTIONAL ) /*++ Routine Description: This routine initializes the port driver. Arguments: Argument1 - Pointer to driver object created by system HwInitializationData - Miniport initialization structure HwContext - Value passed to miniport driver's config routine Return Value: The function value is the final status from the initialization operation. --*/ { PDRIVER_OBJECT driverObject = Argument1; PSCSIPORT_DRIVER_EXTENSION driverExtension; PUNICODE_STRING registryPath = (PUNICODE_STRING) Argument2; ULONG pnpInterfaceFlags; NTSTATUS status; PAGED_CODE(); // // If the global adapter list pointer is negative one then we need to do // our global initialization. This includes creating the scsi directory // and initializing the spinlock for protecting the global adapter list. // if(((LONG_PTR)ScsiGlobalAdapterList) == -1) { ScsiGlobalAdapterList = NULL; ScsiGlobalAdapterListElements = 0; KeInitializeSpinLock(&ScsiGlobalAdapterListSpinLock); ScsiPortInitializeDispatchTables(); SpCreateScsiDirectory(); status = SpInitializeGuidInterfaceMapping(driverObject); if(!NT_SUCCESS(status)) { return status; } // // Create the SCSI device map in the registry. // SpInitDeviceMap(); // // Determine if the system can do 64-bit physical addresses. // Sp64BitPhysicalAddresses = SpDetermine64BitSupport(); } // // Check that the length of this structure is equal to or less than // what the port driver expects it to be. This is effectively a // version check. // if (HwInitializationData->HwInitializationDataSize > sizeof(HW_INITIALIZATION_DATA)) { DebugPrint((0,"ScsiPortInitialize: Miniport driver wrong version\n")); return (ULONG) STATUS_REVISION_MISMATCH; } // // Check that each required entry is not NULL. // if ((!HwInitializationData->HwInitialize) || (!HwInitializationData->HwFindAdapter) || (!HwInitializationData->HwStartIo) || (!HwInitializationData->HwResetBus)) { DebugPrint((0, "ScsiPortInitialize: Miniport driver missing required entry\n")); return (ULONG) STATUS_REVISION_MISMATCH; } // // Try to allocate a driver extension // driverExtension = IoGetDriverObjectExtension(driverObject, ScsiPortInitialize); if (driverExtension == NULL) { // // None exists for this key so we need to initialize the new one // status = SpAllocateDriverExtension(driverObject, registryPath, &driverExtension); if(!NT_SUCCESS(status)) { // // Something else went wrong - we cannot continue. // DebugPrint((0, "ScsiPortInitialize: Error %#08lx allocating driver " "extension - cannot continue\n", status)); return status; } } // // Set up the device driver entry points. // driverObject->DriverStartIo = ScsiPortStartIo; driverObject->MajorFunction[IRP_MJ_SCSI] = ScsiPortGlobalDispatch; driverObject->MajorFunction[IRP_MJ_CREATE] = ScsiPortGlobalDispatch; driverObject->MajorFunction[IRP_MJ_CLOSE] = ScsiPortGlobalDispatch; driverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ScsiPortGlobalDispatch; driverObject->MajorFunction[IRP_MJ_PNP] = ScsiPortGlobalDispatch; driverObject->MajorFunction[IRP_MJ_SYSTEM_CONTROL] = ScsiPortGlobalDispatch; driverObject->MajorFunction[IRP_MJ_POWER] = ScsiPortGlobalDispatch; // // Set up the device driver's pnp-power routine, add routine and unload // routine // driverObject->DriverExtension->AddDevice = ScsiPortAddDevice; driverObject->DriverUnload = ScsiPortUnload; // // Find out if this interface type is safe for this adapter // pnpInterfaceFlags = SpQueryPnpInterfaceFlags( driverExtension, HwInitializationData->AdapterInterfaceType); // // Special handling for the "Internal" interface type // if(HwInitializationData->AdapterInterfaceType == Internal) { if (TEST_FLAG(pnpInterfaceFlags, SP_PNP_IS_SAFE) == SP_PNP_NOT_SAFE) { return STATUS_NO_SUCH_DEVICE; } } // // If there's a chance this interface can handle pnp then store away // the interface information. // if(TEST_FLAG(pnpInterfaceFlags, SP_PNP_IS_SAFE)) { PSP_INIT_CHAIN_ENTRY entry = NULL; PSP_INIT_CHAIN_ENTRY *nextEntry = &(driverExtension->InitChain); // // Run to the end of the chain and make sure we don't have any information // about this interface type already // while(*nextEntry != NULL) { if((*nextEntry)->InitData.AdapterInterfaceType == HwInitializationData->AdapterInterfaceType) { // // We already have enough information about this interface type // return STATUS_SUCCESS; } nextEntry = &((*nextEntry)->NextEntry); } // // Allocate an init chain entry to store the config information away in // entry = SpAllocatePool(NonPagedPool, sizeof(SP_INIT_CHAIN_ENTRY), SCSIPORT_TAG_INIT_CHAIN, driverObject); if(entry == NULL) { DebugPrint((1, "ScsiPortInitialize: couldn't allocate chain entry\n")); return (ULONG) STATUS_INSUFFICIENT_RESOURCES; } RtlCopyMemory(&(entry->InitData), HwInitializationData, sizeof(HW_INITIALIZATION_DATA)); // // Stick this entry onto the end of the chain // entry->NextEntry = NULL; *nextEntry = entry; } // // There are two possible reasons we might be doing this in legacy // mode. If it's an internal bus type we always detect. Otherwise, if // the interface isn't safe for pnp we'll use the legacy path. Or if // the registry indicates we should do detection for this miniport AND // the pnp interface flags indicate that this bus may not be enumerable // we'll hit the legacy path. // #if !defined(NO_LEGACY_DRIVERS) if((TEST_FLAG(pnpInterfaceFlags, SP_PNP_IS_SAFE) == FALSE) || (driverExtension->LegacyAdapterDetection && TEST_FLAG(pnpInterfaceFlags, SP_PNP_NON_ENUMERABLE))) { // // If we're supposed to detect this device then just call directly into // SpInitLegacyAdapter to find what we can find // DebugPrint((1, "ScsiPortInitialize: flags = %#08lx & LegacyAdapterDetection = %d\n", pnpInterfaceFlags, driverExtension->LegacyAdapterDetection)); DebugPrint((1, "ScsiPortInitialize: Doing Legacy Adapter detection\n")); status = ScsiPortInitLegacyAdapter(driverExtension, HwInitializationData, HwContext); } #endif // NO_LEGACY_DRIVERS // // Always return success if there's an interface which can handle pnp, // even if the detection fails. // if(driverExtension->SafeInterfaceCount != 0) { status = STATUS_SUCCESS; } return status; } PVOID ScsiPortGetDeviceBase( IN PVOID HwDeviceExtension, IN INTERFACE_TYPE BusType, IN ULONG SystemIoBusNumber, SCSI_PHYSICAL_ADDRESS IoAddress, ULONG NumberOfBytes, BOOLEAN InIoSpace ) /*++ Routine Description: This routine maps an IO address to system address space. Use ScsiPortFreeDeviceBase to unmap address. Arguments: HwDeviceExtension - used to find port device extension. BusType - what type of bus - eisa, mca, isa SystemIoBusNumber - which IO bus (for machines with multiple buses). IoAddress - base device address to be mapped. NumberOfBytes - number of bytes for which address is valid. InIoSpace - indicates an IO address. Return Value: Mapped address. --*/ { PADAPTER_EXTENSION adapter = GET_FDO_EXTENSION(HwDeviceExtension); BOOLEAN isReinit; PHYSICAL_ADDRESS cardAddress; ULONG addressSpace = InIoSpace; PVOID mappedAddress = NULL; PMAPPED_ADDRESS newMappedAddress; BOOLEAN b = FALSE; isReinit = (TEST_FLAG(adapter->Flags, PD_MINIPORT_REINITIALIZING) == PD_MINIPORT_REINITIALIZING); // // If a set of resources was provided to the miniport for this adapter then // get the translation out of the resource lists provided. // if(!adapter->IsMiniportDetected) { CM_PARTIAL_RESOURCE_DESCRIPTOR translation; b = SpFindAddressTranslation(adapter, BusType, SystemIoBusNumber, IoAddress, NumberOfBytes, InIoSpace, &translation); if(b) { cardAddress = translation.u.Generic.Start; addressSpace = (translation.Type == CmResourceTypePort) ? 1 : 0; } else { DebugPrint((1, "ScsiPortGetDeviceBase: SpFindAddressTranslation failed. %s Address = %lx\n", InIoSpace ? "I/O" : "Memory", IoAddress.LowPart)); } } if((isReinit == FALSE) && (b == FALSE)) { // // This isn't a reinitialization. Either the miniport is not pnp // or it asked for something that it wasn't assigned. Unfortunately // we need to deal with both cases for the time being. // b = HalTranslateBusAddress( BusType, SystemIoBusNumber, IoAddress, &addressSpace, &cardAddress ); } if (b == FALSE) { // // Still no translated address. Error // DebugPrint((1, "ScsiPortGetDeviceBase: Translate bus address " "failed. %s Address = %lx\n", InIoSpace ? "I/O" : "Memory", IoAddress.LowPart)); return NULL; } // // Map the device base address into the virtual address space // if the address is in memory space. // if ((isReinit == FALSE) && (addressSpace == FALSE)) { // // We're not reinitializing and we need to map the address space. // Use MM to do the mapping. // newMappedAddress = SpAllocateAddressMapping(adapter); if(newMappedAddress == NULL) { DebugPrint((0, "ScsiPortGetDeviceBase: could not find free block " "to track address mapping - returning NULL\n")); return NULL; } mappedAddress = MmMapIoSpace(cardAddress, NumberOfBytes, FALSE); // // Store mapped address, bytes count, etc. // newMappedAddress->MappedAddress = mappedAddress; newMappedAddress->NumberOfBytes = NumberOfBytes; newMappedAddress->IoAddress = IoAddress; newMappedAddress->BusNumber = SystemIoBusNumber; } else if ((isReinit == TRUE) && (addressSpace == FALSE)) { ULONG i; // // This is a reinitialization - we should already have the mapping // for the address saved away in our list. // newMappedAddress = SpFindMappedAddress(adapter, IoAddress, NumberOfBytes, SystemIoBusNumber); if(newMappedAddress != NULL) { mappedAddress = newMappedAddress->MappedAddress; return mappedAddress; } // // We should always find the mapped address here if the miniport // is behaving itself. // KeBugCheckEx(PORT_DRIVER_INTERNAL, 0, 0, 0, 0); } else { mappedAddress = (PVOID)(ULONG_PTR)cardAddress.QuadPart; } return mappedAddress; } // end ScsiPortGetDeviceBase() VOID ScsiPortFreeDeviceBase( IN PVOID HwDeviceExtension, IN PVOID MappedAddress ) /*++ Routine Description: This routine unmaps an IO address that has been previously mapped to system address space using ScsiPortGetDeviceBase(). Arguments: HwDeviceExtension - used to find port device extension. MappedAddress - address to unmap. NumberOfBytes - number of bytes mapped. InIoSpace - address is in IO space. Return Value: None --*/ { PADAPTER_EXTENSION adapter; ULONG i; PMAPPED_ADDRESS nextMappedAddress; PMAPPED_ADDRESS lastMappedAddress; adapter = GET_FDO_EXTENSION(HwDeviceExtension); SpFreeMappedAddress(adapter, MappedAddress); return; } // end ScsiPortFreeDeviceBase() PVOID ScsiPortGetUncachedExtension( IN PVOID HwDeviceExtension, IN PPORT_CONFIGURATION_INFORMATION ConfigInfo, IN ULONG NumberOfBytes ) /*++ Routine Description: This function allocates a common buffer to be used as the uncached device extension for the miniport driver. This function will also allocate any required SRB extensions. The DmaAdapter is allocated if it has not been allocated previously. Arguments: DeviceExtension - Supplies a pointer to the miniports device extension. ConfigInfo - Supplies a pointer to the partially initialized configuraiton information. This is used to get an DMA adapter object. NumberOfBytes - Supplies the size of the extension which needs to be allocated Return Value: A pointer to the uncached device extension or NULL if the extension could not be allocated or was previously allocated. --*/ { PADAPTER_EXTENSION adapter = GET_FDO_EXTENSION(HwDeviceExtension); DEVICE_DESCRIPTION deviceDescription; ULONG numberOfMapRegisters; NTSTATUS status; PVOID SrbExtensionBuffer; // // If the miniport is being reinitialized then just return the current // uncached extension (if any). // if (TEST_FLAG(adapter->Flags, PD_MINIPORT_REINITIALIZING)) { DebugPrint((1, "ScsiPortGetUncachedExtension - miniport is " "reinitializing returning %#p\n", adapter->NonCachedExtension)); if(TEST_FLAG(adapter->Flags, PD_UNCACHED_EXTENSION_RETURNED)) { // // The miniport asked for it's uncached extension once during // reinitialization - simulate the behavior on the original second // call and return NULL. // return NULL; } else { // // The miniport only gets one non-cached extension - keep track // of the fact that we returned it and don't give them a pointer // to it again. This flag is cleared once the initialization // is complete. // SET_FLAG(adapter->Flags, PD_UNCACHED_EXTENSION_RETURNED); return(adapter->NonCachedExtension); } } // // Make sure that a common buffer has not already been allocated. // SrbExtensionBuffer = SpGetSrbExtensionBuffer(adapter); if (SrbExtensionBuffer != NULL) { return(NULL); } // // If there no adapter object then try and get one. // if (adapter->DmaAdapterObject == NULL) { RtlZeroMemory(&deviceDescription, sizeof(DEVICE_DESCRIPTION)); deviceDescription.Version = DEVICE_DESCRIPTION_VERSION; deviceDescription.DmaChannel = ConfigInfo->DmaChannel; deviceDescription.InterfaceType = ConfigInfo->AdapterInterfaceType; deviceDescription.DmaWidth = ConfigInfo->DmaWidth; deviceDescription.DmaSpeed = ConfigInfo->DmaSpeed; deviceDescription.ScatterGather = ConfigInfo->ScatterGather; deviceDescription.Master = ConfigInfo->Master; deviceDescription.DmaPort = ConfigInfo->DmaPort; deviceDescription.Dma32BitAddresses = ConfigInfo->Dma32BitAddresses; adapter->Dma32BitAddresses = ConfigInfo->Dma32BitAddresses; // // If the miniport puts anything in here other than 0x80 then we // assume it wants to support 64-bit addresses. // DebugPrint((1, "ScsiPortGetUncachedExtension: Dma64BitAddresses = " "%#0x\n", ConfigInfo->Dma64BitAddresses)); adapter->RemapBuffers = (BOOLEAN) (SpRemapBuffersByDefault != 0); if((ConfigInfo->Dma64BitAddresses & ~SCSI_DMA64_SYSTEM_SUPPORTED) != 0){ DebugPrint((1, "ScsiPortGetUncachedExtension: will request " "64-bit PA's\n")); deviceDescription.Dma64BitAddresses = TRUE; adapter->Dma64BitAddresses = TRUE; } else if(Sp64BitPhysicalAddresses == TRUE) { DebugPrint((1, "ScsiPortGetUncachedExtension: Will remap buffers for adapter %#p\n", adapter)); adapter->RemapBuffers = TRUE; } deviceDescription.BusNumber = ConfigInfo->SystemIoBusNumber; deviceDescription.AutoInitialize = FALSE; // // If we get here then it's unlikely that the adapter is doing // slave mode DMA - if it were it wouldn't need a shared memory segment // to share with it's controller (because it's unlikely it could use it) // deviceDescription.DemandMode = FALSE; deviceDescription.MaximumLength = ConfigInfo->MaximumTransferLength; adapter->DmaAdapterObject = IoGetDmaAdapter(adapter->LowerPdo, &deviceDescription, &numberOfMapRegisters ); // // If an adapter could not be allocated then return NULL. // if (adapter->DmaAdapterObject == NULL) { return(NULL); } // // Determine the number of page breaks allowed. // if (numberOfMapRegisters > ConfigInfo->NumberOfPhysicalBreaks && ConfigInfo->NumberOfPhysicalBreaks != 0) { adapter->Capabilities.MaximumPhysicalPages = ConfigInfo->NumberOfPhysicalBreaks; } else { adapter->Capabilities.MaximumPhysicalPages = numberOfMapRegisters; } } // // Set auto request sense in device extension. // adapter->AutoRequestSense = ConfigInfo->AutoRequestSense; // // Initialize power parameters. // SpInitializePowerParams(adapter); // // Initialize configurable performance parameters. // SpInitializePerformanceParams(adapter); // // Initialize configurable request sense parameters. // SpInitializeRequestSenseParams(adapter); // // Update SrbExtensionSize, if necessary. The miniport's FindAdapter routine // has the opportunity to adjust it after being called, depending upon // it's Scatter/Gather List requirements. // if (adapter->SrbExtensionSize != ConfigInfo->SrbExtensionSize) { adapter->SrbExtensionSize = ConfigInfo->SrbExtensionSize; } // // If the adapter supports AutoRequestSense or needs SRB extensions // then an SRB list needs to be allocated. // if (adapter->SrbExtensionSize != 0 || ConfigInfo->AutoRequestSense) { adapter->AllocateSrbExtension = TRUE; } // // Allocate the common buffer. // status = SpGetCommonBuffer( adapter, NumberOfBytes); if (!NT_SUCCESS(status)) { return(NULL); } return(adapter->NonCachedExtension); } NTSTATUS SpGetCommonBuffer( PADAPTER_EXTENSION DeviceExtension, ULONG NonCachedExtensionSize ) /*++ Routine Description: This routine determines the required size of the common buffer. Allocates the common buffer and finally sets up the srb extension list. This routine expects that the adapter object has already been allocated. Arguments: DeviceExtension - Supplies a pointer to the device extension. NonCachedExtensionSize - Supplies the size of the noncached device extension for the miniport driver. Return Value: Returns the status of the allocate operation. --*/ { PVOID buffer; ULONG length; ULONG blockSize; PVOID *srbExtension; ULONG uncachedExtAlignment = 0; PAGED_CODE(); // // Round the uncached extension up to a page boundary so the srb // extensions following it begin page aligned. // if (NonCachedExtensionSize != 0) { uncachedExtAlignment = DeviceExtension->UncachedExtAlignment; NonCachedExtensionSize = ROUND_UP_COUNT(NonCachedExtensionSize, PAGE_SIZE); DeviceExtension->NonCachedExtensionSize = NonCachedExtensionSize; } // // If verifier is enabled and configured to allocate common buffer space in // separate blocks, call out to the verifier routine to do the allocation. // if (SpVerifyingCommonBuffer(DeviceExtension)) { return SpGetCommonBufferVrfy(DeviceExtension,NonCachedExtensionSize); } // // Calculate the size of the entire common buffer block. // length = SpGetCommonBufferSize(DeviceExtension, NonCachedExtensionSize, &blockSize); // // If the adapter has an alignment requirement for its uncached extension, // round the size of the entire common buffer up to the required boundary. // if (uncachedExtAlignment != 0 && NonCachedExtensionSize != 0) { length = ROUND_UP_COUNT(length, uncachedExtAlignment); } // // Allocate the common buffer. // if (DeviceExtension->DmaAdapterObject == NULL) { // // Since there is no adapter just allocate from non-paged pool. // buffer = SpAllocatePool(NonPagedPool, length, SCSIPORT_TAG_COMMON_BUFFER, DeviceExtension->DeviceObject->DriverObject); } else { // // If the controller can do 64-bit addresses or if the adapter has // alignment requirements for its uncached extension, then we need to // specifically force the uncached extension area below the 4GB mark // and force it to be aligned on the appropriate boundary. // if( ((Sp64BitPhysicalAddresses) && (DeviceExtension->Dma64BitAddresses == TRUE)) || (uncachedExtAlignment != 0)) { PHYSICAL_ADDRESS boundary; if (uncachedExtAlignment != 0) { boundary.QuadPart = length; } else { boundary.HighPart = 1; boundary.LowPart = 0; } // // We'll get page aligned memory out of this which is probably // better than the requirements of the adapter. // buffer = MmAllocateContiguousMemorySpecifyCache( length, (DeviceExtension->MinimumCommonBufferBase), (DeviceExtension->MaximumCommonBufferBase), boundary, MmCached); if(buffer != NULL) { DeviceExtension->PhysicalCommonBuffer = MmGetPhysicalAddress(buffer); } DeviceExtension->UncachedExtensionIsCommonBuffer = FALSE; } else { buffer = AllocateCommonBuffer( DeviceExtension->DmaAdapterObject, length, &DeviceExtension->PhysicalCommonBuffer, FALSE ); DeviceExtension->UncachedExtensionIsCommonBuffer = TRUE; } } DebugPrint((1, "SpGetCommonBuffer: buffer:%p PhysicalCommonBuffer:%p\n", buffer, DeviceExtension->PhysicalCommonBuffer)); if (buffer == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } // // Clear the common buffer. // RtlZeroMemory(buffer, length); // // Save the size of the common buffer. // DeviceExtension->CommonBufferSize = length; // // Set the Srb Extension to the start of the buffer. This address // is used to deallocate the common buffer, so it must be // set whether the device is using an Srb Extension or not. // DeviceExtension->SrbExtensionBuffer = buffer; // // Initialize the noncached extension. // if (NonCachedExtensionSize != 0) { DeviceExtension->NonCachedExtension = buffer; } else { DeviceExtension->NonCachedExtension = NULL; } // // Initialize the SRB extension list. // if (DeviceExtension->AllocateSrbExtension) { ULONG i = 0; // // Subtract the length of the non-cached extension from the common // buffer block. // length -= DeviceExtension->NonCachedExtensionSize; // // Initialize the SRB extension list. // srbExtension = (PVOID*)((PUCHAR)buffer + DeviceExtension->NonCachedExtensionSize); DeviceExtension->SrbExtensionListHeader = srbExtension; while (length >= blockSize * 2) { *srbExtension = (PVOID *)((PCHAR) srbExtension + blockSize); srbExtension = *srbExtension; length -= blockSize; i++; } DebugPrint((1, "SpGetCommonBuffer: %d entries put onto " "SrbExtension list\n", i)); DeviceExtension->NumberOfRequests = i; } return(STATUS_SUCCESS); } NTSTATUS DriverEntry( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath ) /*++ Routine Description: Temporary entry point needed to initialize the scsi port driver. Arguments: DriverObject - Pointer to the driver object created by the system. Return Value: STATUS_SUCCESS --*/ { // // NOTE: This routine should not be needed ! DriverEntry is defined // in the miniport driver. // UNREFERENCED_PARAMETER(DriverObject); return STATUS_SUCCESS; } // end DriverEntry() NTSTATUS SpInitializeConfiguration( IN PADAPTER_EXTENSION DeviceExtension, IN PUNICODE_STRING RegistryPath, IN PHW_INITIALIZATION_DATA HwInitData, IN PCONFIGURATION_CONTEXT Context ) /*++ Routine Description: This routine initializes the port configuration information structure. Any necessary information is extracted from the registery. Arguments: DeviceExtension - Supplies the device extension. HwInitData - Supplies the initial miniport data. Context - Supplies the context data used access calls. Return Value: NTSTATUS - Success if requested bus type exists and additional configuration information is available. --*/ { ULONG j; NTSTATUS status; UNICODE_STRING unicodeString; OBJECT_ATTRIBUTES objectAttributes; PCONFIGURATION_INFORMATION configurationInformation; HANDLE deviceKey; HANDLE generalKey; BOOLEAN found; ANSI_STRING ansiString; CCHAR deviceBuffer[16]; CCHAR nodeBuffer[SP_REG_BUFFER_SIZE]; // // If this is the initial call then zero the information and set // the structure to the uninitialized values. // RtlZeroMemory(&Context->PortConfig, sizeof(PORT_CONFIGURATION_INFORMATION)); ASSERT(Context->AccessRanges != NULL); RtlZeroMemory( Context->AccessRanges, HwInitData->NumberOfAccessRanges * sizeof(ACCESS_RANGE) ); Context->PortConfig.Length = sizeof(PORT_CONFIGURATION_INFORMATION); Context->PortConfig.AdapterInterfaceType = HwInitData->AdapterInterfaceType; Context->PortConfig.InterruptMode = Latched; Context->PortConfig.MaximumTransferLength = SP_UNINITIALIZED_VALUE; Context->PortConfig.DmaChannel = SP_UNINITIALIZED_VALUE; Context->PortConfig.DmaPort = SP_UNINITIALIZED_VALUE; Context->PortConfig.NumberOfAccessRanges = HwInitData->NumberOfAccessRanges; Context->PortConfig.MaximumNumberOfTargets = 8; Context->PortConfig.MaximumNumberOfLogicalUnits = SCSI_MAXIMUM_LOGICAL_UNITS; Context->PortConfig.WmiDataProvider = FALSE; // // If the system indicates it can do 64-bit physical addressing then tell // the miniport it's an option. // if(Sp64BitPhysicalAddresses == TRUE) { Context->PortConfig.Dma64BitAddresses = SCSI_DMA64_SYSTEM_SUPPORTED; } else { Context->PortConfig.Dma64BitAddresses = 0; } // // Save away the some of the attributes. // Context->PortConfig.NeedPhysicalAddresses = HwInitData->NeedPhysicalAddresses; Context->PortConfig.MapBuffers = HwInitData->MapBuffers; Context->PortConfig.AutoRequestSense = HwInitData->AutoRequestSense; Context->PortConfig.ReceiveEvent = HwInitData->ReceiveEvent; Context->PortConfig.TaggedQueuing = HwInitData->TaggedQueuing; Context->PortConfig.MultipleRequestPerLu = HwInitData->MultipleRequestPerLu; // // Indicate the current AT disk usage. // configurationInformation = IoGetConfigurationInformation(); Context->PortConfig.AtdiskPrimaryClaimed = configurationInformation->AtDiskPrimaryAddressClaimed; Context->PortConfig.AtdiskSecondaryClaimed = configurationInformation->AtDiskSecondaryAddressClaimed; for (j = 0; j < 8; j++) { Context->PortConfig.InitiatorBusId[j] = (UCHAR)SP_UNINITIALIZED_VALUE; } Context->PortConfig.NumberOfPhysicalBreaks = SP_DEFAULT_PHYSICAL_BREAK_VALUE; // // Clear some of the context information. // Context->DisableTaggedQueueing = FALSE; Context->DisableMultipleLu = FALSE; // // Record the system bus number. // Context->PortConfig.SystemIoBusNumber = Context->BusNumber; // // Initialize the adapter number on the context. // Context->AdapterNumber = DeviceExtension->AdapterNumber - 1; ASSERT((LONG)Context->AdapterNumber > -1); // // Check for device parameters. // if (Context->Parameter) { ExFreePool(Context->Parameter); Context->Parameter = NULL; } generalKey = SpOpenDeviceKey(RegistryPath, -1); // // First parse the device information. // if (generalKey != NULL) { SpParseDevice(DeviceExtension, generalKey, Context, nodeBuffer); ZwClose(generalKey); } // // Next parse the specific device information so that it can override the // general device information. This node is not used if the last adapter // was not found. // deviceKey = SpOpenDeviceKey(RegistryPath, Context->AdapterNumber); if (deviceKey != NULL) { SpParseDevice(DeviceExtension, deviceKey, Context, nodeBuffer); ZwClose(deviceKey); } DeviceExtension->SrbTimeout = SRB_DEFAULT_TIMEOUT; PortGetDiskTimeoutValue(&DeviceExtension->SrbTimeout); // // Determine if the requested bus type is on this system. // if(HwInitData->AdapterInterfaceType != PNPBus) { found = FALSE; if(HwInitData->AdapterInterfaceType != MicroChannel) { status = IoQueryDeviceDescription(&HwInitData->AdapterInterfaceType, &Context->BusNumber, NULL, NULL, NULL, NULL, SpConfigurationCallout, &found); } // // If the request failed, then assume this type of bus is not here. // if (!found) { INTERFACE_TYPE interfaceType = Eisa; if (HwInitData->AdapterInterfaceType == Isa) { // // Check for an Eisa bus. // status = IoQueryDeviceDescription(&interfaceType, &Context->BusNumber, NULL, NULL, NULL, NULL, SpConfigurationCallout, &found); // // If the request failed, then assume this type of bus is not here. // if (found) { return(STATUS_SUCCESS); } else { return(STATUS_DEVICE_DOES_NOT_EXIST); } } else { return(STATUS_DEVICE_DOES_NOT_EXIST); } } else { return(STATUS_SUCCESS); } } else { return STATUS_SUCCESS; } } PCM_RESOURCE_LIST SpBuildResourceList( PADAPTER_EXTENSION DeviceExtension, PPORT_CONFIGURATION_INFORMATION ConfigInfo ) /*++ Routine Description: Creates a resource list which is used to query or report resource usage in the system Arguments: DeviceExtension - Pointer to the port's deviceExtension. ConfigInfo - Pointer to the information structure filled out by the miniport findAdapter routine. Return Value: Returns a pointer to a filled up resource list, or 0 if the call failed. Note: Memory is allocated by the routine for the resourcelist. It must be freed up by the caller by calling ExFreePool(); --*/ { PCM_RESOURCE_LIST resourceList; PCM_PARTIAL_RESOURCE_DESCRIPTOR resourceDescriptor; PCONFIGURATION_INFORMATION configurationInformation; PACCESS_RANGE accessRange; ULONG listLength = 0; ULONG hasInterrupt; ULONG i; BOOLEAN hasDma; PAGED_CODE(); // // Indicate the current AT disk usage. // configurationInformation = IoGetConfigurationInformation(); if (ConfigInfo->AtdiskPrimaryClaimed) { configurationInformation->AtDiskPrimaryAddressClaimed = TRUE; } if (ConfigInfo->AtdiskSecondaryClaimed) { configurationInformation->AtDiskSecondaryAddressClaimed = TRUE; } // // Determine if adapter uses DMA. Only report the DMA channel if a // channel number is used. // if (ConfigInfo->DmaChannel != SP_UNINITIALIZED_VALUE || ConfigInfo->DmaPort != SP_UNINITIALIZED_VALUE) { hasDma = TRUE; listLength++; } else { hasDma = FALSE; } DeviceExtension->HasInterrupt = FALSE; if (DeviceExtension->HwInterrupt == NULL || (ConfigInfo->BusInterruptLevel == 0 && ConfigInfo->BusInterruptVector == 0)) { hasInterrupt = 0; } else { hasInterrupt = 1; listLength++; } // // Detemine whether the second interrupt is used. // if (DeviceExtension->HwInterrupt != NULL && (ConfigInfo->BusInterruptLevel2 != 0 || ConfigInfo->BusInterruptVector2 != 0)) { hasInterrupt++; listLength++; } if(hasInterrupt) { DeviceExtension->HasInterrupt = TRUE; } // // Determine the number of access ranges used. // accessRange = &((*(ConfigInfo->AccessRanges))[0]); for (i = 0; i < ConfigInfo->NumberOfAccessRanges; i++) { if (accessRange->RangeLength != 0) { listLength++; } accessRange++; } resourceList = (PCM_RESOURCE_LIST) SpAllocatePool(PagedPool, (sizeof(CM_RESOURCE_LIST) + ((listLength - 1) * sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR))), SCSIPORT_TAG_RESOURCE_LIST, DeviceExtension->DeviceObject->DriverObject); // // Return NULL if the structure could not be allocated. // Otherwise, fill it out. // if (!resourceList) { return NULL; } else { // // Clear the resource list. // RtlZeroMemory( resourceList, sizeof(CM_RESOURCE_LIST) + (listLength - 1) * sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR) ); // // Initialize the various fields. // resourceList->Count = 1; resourceList->List[0].InterfaceType = ConfigInfo->AdapterInterfaceType; resourceList->List[0].BusNumber = ConfigInfo->SystemIoBusNumber; resourceList->List[0].PartialResourceList.Count = listLength; resourceDescriptor = resourceList->List[0].PartialResourceList.PartialDescriptors; // // For each entry in the access range, fill in an entry in the // resource list // for (i = 0; i < ConfigInfo->NumberOfAccessRanges; i++) { accessRange = &((*(ConfigInfo->AccessRanges))[i]); if (accessRange->RangeLength == 0) { // // Skip the empty ranges. // continue; } if (accessRange->RangeInMemory) { resourceDescriptor->Type = CmResourceTypeMemory; resourceDescriptor->Flags = CM_RESOURCE_MEMORY_READ_WRITE; } else { resourceDescriptor->Type = CmResourceTypePort; resourceDescriptor->Flags = CM_RESOURCE_PORT_IO; if(ConfigInfo->AdapterInterfaceType == Eisa) { resourceDescriptor->Flags = CM_RESOURCE_PORT_16_BIT_DECODE; } } resourceDescriptor->ShareDisposition = CmResourceShareDeviceExclusive; resourceDescriptor->u.Memory.Start = accessRange->RangeStart; resourceDescriptor->u.Memory.Length = accessRange->RangeLength; resourceDescriptor++; } // // Fill in the entry for the interrupt if it was present. // if (hasInterrupt) { resourceDescriptor->Type = CmResourceTypeInterrupt; if (ConfigInfo->AdapterInterfaceType == MicroChannel || ConfigInfo->InterruptMode == LevelSensitive) { resourceDescriptor->ShareDisposition = CmResourceShareShared; resourceDescriptor->Flags = CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE; } else { resourceDescriptor->ShareDisposition = CmResourceShareDeviceExclusive; resourceDescriptor->Flags = CM_RESOURCE_INTERRUPT_LATCHED; } resourceDescriptor->u.Interrupt.Level = ConfigInfo->BusInterruptLevel; resourceDescriptor->u.Interrupt.Vector = ConfigInfo->BusInterruptVector; resourceDescriptor->u.Interrupt.Affinity = 0; resourceDescriptor++; --hasInterrupt; } if (hasInterrupt) { resourceDescriptor->Type = CmResourceTypeInterrupt; if (ConfigInfo->AdapterInterfaceType == MicroChannel || ConfigInfo->InterruptMode2 == LevelSensitive) { resourceDescriptor->ShareDisposition = CmResourceShareShared; resourceDescriptor->Flags = CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE; } else { resourceDescriptor->ShareDisposition = CmResourceShareDeviceExclusive; resourceDescriptor->Flags = CM_RESOURCE_INTERRUPT_LATCHED; } resourceDescriptor->u.Interrupt.Level = ConfigInfo->BusInterruptLevel2; resourceDescriptor->u.Interrupt.Vector = ConfigInfo->BusInterruptVector2; resourceDescriptor->u.Interrupt.Affinity = 0; resourceDescriptor++; } if (hasDma) { // // Fill out DMA information; // resourceDescriptor->Type = CmResourceTypeDma; resourceDescriptor->ShareDisposition = CmResourceShareDeviceExclusive; resourceDescriptor->u.Dma.Channel = ConfigInfo->DmaChannel; resourceDescriptor->u.Dma.Port = ConfigInfo->DmaPort; resourceDescriptor->Flags = 0; // // Set the initialized values to zero. // if (ConfigInfo->DmaChannel == SP_UNINITIALIZED_VALUE) { resourceDescriptor->u.Dma.Channel = 0; } if (ConfigInfo->DmaPort == SP_UNINITIALIZED_VALUE) { resourceDescriptor->u.Dma.Port = 0; } } return resourceList; } } // end SpBuildResourceList() VOID SpParseDevice( IN PADAPTER_EXTENSION DeviceExtension, IN HANDLE Key, IN PCONFIGURATION_CONTEXT Context, IN PUCHAR Buffer ) /*++ Routine Description: This routine parses a device key node and updates the configuration information. Arguments: DeviceExtension - Supplies the device extension. Key - Supplies an open key to the device node. ConfigInfo - Supplies the configuration information to be initialized. Context - Supplies the configuration context. Buffer - Supplies a scratch buffer for temporary data storage. Return Value: None --*/ { PKEY_VALUE_FULL_INFORMATION keyValueInformation; NTSTATUS status = STATUS_SUCCESS; PCM_FULL_RESOURCE_DESCRIPTOR resource; PCM_PARTIAL_RESOURCE_DESCRIPTOR descriptor; PCM_SCSI_DEVICE_DATA scsiData; UNICODE_STRING unicodeString; ANSI_STRING ansiString; ULONG length; ULONG index = 0; ULONG rangeCount = 0; ULONG count; PAGED_CODE(); keyValueInformation = (PKEY_VALUE_FULL_INFORMATION) Buffer; // // Look at each of the values in the device node. // while(TRUE){ status = ZwEnumerateValueKey( Key, index, KeyValueFullInformation, Buffer, SP_REG_BUFFER_SIZE, &length ); if (!NT_SUCCESS(status)) { #if DBG if (status != STATUS_NO_MORE_ENTRIES) { DebugPrint((1, "SpParseDevice: ZwEnumerateValueKey failed. Status: %lx", status)); } #endif return; } // // Update the index for the next time around the loop. // index++; // // Check that the length is reasonable. // if (keyValueInformation->Type == REG_DWORD && keyValueInformation->DataLength != sizeof(ULONG)) { continue; } // // Check for a maximum lu number. // if (_wcsnicmp(keyValueInformation->Name, L"MaximumLogicalUnit", keyValueInformation->NameLength/2) == 0) { if (keyValueInformation->Type != REG_DWORD) { DebugPrint((1, "SpParseDevice: Bad data type for MaximumLogicalUnit.\n")); continue; } DeviceExtension->MaxLuCount = *((PUCHAR) (Buffer + keyValueInformation->DataOffset)); DebugPrint((1, "SpParseDevice: MaximumLogicalUnit = %d found.\n", DeviceExtension->MaxLuCount)); // // If the value is out of bounds, then reset it. // if (DeviceExtension->MaxLuCount > SCSI_MAXIMUM_LOGICAL_UNITS) { DeviceExtension->MaxLuCount = SCSI_MAXIMUM_LOGICAL_UNITS; } } if (_wcsnicmp(keyValueInformation->Name, L"InitiatorTargetId", keyValueInformation->NameLength/2) == 0) { if (keyValueInformation->Type != REG_DWORD) { DebugPrint((1, "SpParseDevice: Bad data type for InitiatorTargetId.\n")); continue; } Context->PortConfig.InitiatorBusId[0] = *((PUCHAR) (Buffer + keyValueInformation->DataOffset)); DebugPrint((1, "SpParseDevice: InitiatorTargetId = %d found.\n", Context->PortConfig.InitiatorBusId[0])); // // If the value is out of bounds, then reset it. // if (Context->PortConfig.InitiatorBusId[0] > Context->PortConfig.MaximumNumberOfTargets - 1) { Context->PortConfig.InitiatorBusId[0] = (UCHAR)SP_UNINITIALIZED_VALUE; } } if (_wcsnicmp(keyValueInformation->Name, L"ScsiDebug", keyValueInformation->NameLength/2) == 0) { if (keyValueInformation->Type != REG_DWORD) { DebugPrint((1, "SpParseDevice: Bad data type for ScsiDebug.\n")); continue; } #if DBG ScsiDebug = *((PULONG) (Buffer + keyValueInformation->DataOffset)); #endif } if (_wcsnicmp(keyValueInformation->Name, L"BreakPointOnEntry", keyValueInformation->NameLength/2) == 0) { DebugPrint((0, "SpParseDevice: Break point requested on entry.\n")); DbgBreakPoint(); } // // Check for disabled synchonous tranfers. // if (_wcsnicmp(keyValueInformation->Name, L"DisableSynchronousTransfers", keyValueInformation->NameLength/2) == 0) { DeviceExtension->CommonExtension.SrbFlags |= SRB_FLAGS_DISABLE_SYNCH_TRANSFER; DebugPrint((1, "SpParseDevice: Disabling synchonous transfers\n")); } // // Check for disabled disconnects. // if (_wcsnicmp(keyValueInformation->Name, L"DisableDisconnects", keyValueInformation->NameLength/2) == 0) { DeviceExtension->CommonExtension.SrbFlags |= SRB_FLAGS_DISABLE_DISCONNECT; DebugPrint((1, "SpParseDevice: Disabling disconnects\n")); } // // Check for disabled tagged queuing. // if (_wcsnicmp(keyValueInformation->Name, L"DisableTaggedQueuing", keyValueInformation->NameLength/2) == 0) { Context->DisableTaggedQueueing = TRUE; DebugPrint((1, "SpParseDevice: Disabling tagged queueing\n")); } // // Check for disabled multiple requests per logical unit. // if (_wcsnicmp(keyValueInformation->Name, L"DisableMultipleRequests", keyValueInformation->NameLength/2) == 0) { Context->DisableMultipleLu = TRUE; DebugPrint((1, "SpParseDevice: Disabling multiple requests\n")); } // // Check for the minimum & maximum physical addresses that this // controller can use for it's uncached extension. If none is provided // assume it must be in the first 4GB of memory. // if(_wcsnicmp(keyValueInformation->Name, L"MinimumUCXAddress", keyValueInformation->NameLength/2) == 0) { if (keyValueInformation->Type == REG_BINARY) { DeviceExtension->MinimumCommonBufferBase.QuadPart = *((PULONGLONG) (Buffer + keyValueInformation->DataOffset)); } } if(_wcsnicmp(keyValueInformation->Name, L"MaximumUCXAddress", keyValueInformation->NameLength/2) == 0) { if (keyValueInformation->Type == REG_BINARY) { DeviceExtension->MaximumCommonBufferBase.QuadPart = *((PULONGLONG) (Buffer + keyValueInformation->DataOffset)); } } if(DeviceExtension->MaximumCommonBufferBase.QuadPart == 0) { DeviceExtension->MaximumCommonBufferBase.LowPart = 0xffffffff; DeviceExtension->MaximumCommonBufferBase.HighPart = 0x0; } // // Make sure that the minimum and maximum parameters are valid. // If there's not at least one valid page between them then reset // the minimum to zero. // if(DeviceExtension->MinimumCommonBufferBase.QuadPart >= (DeviceExtension->MaximumCommonBufferBase.QuadPart - PAGE_SIZE)) { DebugPrint((0, "SpParseDevice: MinimumUCXAddress %I64x is invalid\n", DeviceExtension->MinimumCommonBufferBase.QuadPart)); DeviceExtension->MinimumCommonBufferBase.QuadPart = 0; } // // Check for driver parameters tranfers. // if (_wcsnicmp(keyValueInformation->Name, L"DriverParameters", keyValueInformation->NameLength/2) == 0) { if (keyValueInformation->DataLength == 0) { continue; } // // Free any previous driver parameters. // if (Context->Parameter != NULL) { ExFreePool(Context->Parameter); } Context->Parameter = SpAllocatePool(NonPagedPool, keyValueInformation->DataLength, SCSIPORT_TAG_MINIPORT_PARAM, DeviceExtension->DeviceObject->DriverObject); if (Context->Parameter != NULL) { if (keyValueInformation->Type != REG_SZ) { // // This is some random information just copy it. // RtlCopyMemory( Context->Parameter, (PCCHAR) keyValueInformation + keyValueInformation->DataOffset, keyValueInformation->DataLength ); } else { // // This is a unicode string. Convert it to a ANSI string. // Initialize the strings. // unicodeString.Buffer = (PWSTR) ((PCCHAR) keyValueInformation + keyValueInformation->DataOffset); unicodeString.Length = (USHORT) keyValueInformation->DataLength; unicodeString.MaximumLength = (USHORT) keyValueInformation->DataLength; ansiString.Buffer = (PCHAR) Context->Parameter; ansiString.Length = 0; ansiString.MaximumLength = (USHORT) keyValueInformation->DataLength; status = RtlUnicodeStringToAnsiString( &ansiString, &unicodeString, FALSE ); if (!NT_SUCCESS(status)) { // // Free the context. // ExFreePool(Context->Parameter); Context->Parameter = NULL; } } } DebugPrint((1, "SpParseDevice: Found driver parameter.\n")); } // // See if an entry for Maximum Scatter-Gather List has been // set. // if (_wcsnicmp(keyValueInformation->Name, L"MaximumSGList", keyValueInformation->NameLength/2) == 0) { ULONG maxBreaks, minBreaks; if (keyValueInformation->Type != REG_DWORD) { DebugPrint((1, "SpParseDevice: Bad data type for MaximumSGList.\n")); continue; } Context->PortConfig.NumberOfPhysicalBreaks = *((PUCHAR)(Buffer + keyValueInformation->DataOffset)); DebugPrint((1, "SpParseDevice: MaximumSGList = %d found.\n", Context->PortConfig.NumberOfPhysicalBreaks)); // // If the value is out of bounds, then reset it. // if ((Context->PortConfig.MapBuffers) && (!Context->PortConfig.Master)) { maxBreaks = SP_UNINITIALIZED_VALUE; minBreaks = SCSI_MINIMUM_PHYSICAL_BREAKS; } else { maxBreaks = SCSI_MAXIMUM_PHYSICAL_BREAKS; minBreaks = SCSI_MINIMUM_PHYSICAL_BREAKS; } if (Context->PortConfig.NumberOfPhysicalBreaks > maxBreaks) { Context->PortConfig.NumberOfPhysicalBreaks = maxBreaks; } else if (Context->PortConfig.NumberOfPhysicalBreaks < minBreaks) { Context->PortConfig.NumberOfPhysicalBreaks = minBreaks; } } // // See if an entry for Number of request has been set. // if (_wcsnicmp(keyValueInformation->Name, L"NumberOfRequests", keyValueInformation->NameLength/2) == 0) { ULONG value; if (keyValueInformation->Type != REG_DWORD) { DebugPrint((1, "SpParseDevice: Bad data type for NumberOfRequests.\n")); continue; } value = *((PULONG)(Buffer + keyValueInformation->DataOffset)); // // If the value is out of bounds, then reset it. // if (value < MINIMUM_SRB_EXTENSIONS) { DeviceExtension->NumberOfRequests = MINIMUM_SRB_EXTENSIONS; } else if (value > MAXIMUM_SRB_EXTENSIONS) { DeviceExtension->NumberOfRequests = MAXIMUM_SRB_EXTENSIONS; } else { DeviceExtension->NumberOfRequests = value; } DebugPrint((1, "SpParseDevice: Number Of Requests = %d found.\n", DeviceExtension->NumberOfRequests)); } // // Check for resource list. // if (_wcsnicmp(keyValueInformation->Name, L"ResourceList", keyValueInformation->NameLength/2) == 0 || _wcsnicmp(keyValueInformation->Name, L"Configuration Data", keyValueInformation->NameLength/2) == 0 ) { if (keyValueInformation->Type != REG_FULL_RESOURCE_DESCRIPTOR || keyValueInformation->DataLength < sizeof(REG_FULL_RESOURCE_DESCRIPTOR)) { DebugPrint((1, "SpParseDevice: Bad data type for ResourceList.\n")); continue; } else { DebugPrint((1, "SpParseDevice: ResourceList found!\n")); } resource = (PCM_FULL_RESOURCE_DESCRIPTOR) (Buffer + keyValueInformation->DataOffset); // // Set the bus number equal to the bus number for the // resouce. Note the context value is also set to the // new bus number. // Context->BusNumber = resource->BusNumber; Context->PortConfig.SystemIoBusNumber = resource->BusNumber; // // Walk the resource list and update the configuration. // for (count = 0; count < resource->PartialResourceList.Count; count++) { descriptor = &resource->PartialResourceList.PartialDescriptors[count]; // // Verify size is ok. // if ((ULONG)((PCHAR) (descriptor + 1) - (PCHAR) resource) > keyValueInformation->DataLength) { DebugPrint((1, "SpParseDevice: Resource data too small.\n")); break; } // // Switch on descriptor type; // switch (descriptor->Type) { case CmResourceTypePort: if (rangeCount >= Context->PortConfig.NumberOfAccessRanges) { DebugPrint((1, "SpParseDevice: Too many access ranges.\n")); continue; } Context->AccessRanges[rangeCount].RangeStart = descriptor->u.Port.Start; Context->AccessRanges[rangeCount].RangeLength = descriptor->u.Port.Length; Context->AccessRanges[rangeCount].RangeInMemory = FALSE; rangeCount++; break; case CmResourceTypeMemory: if (rangeCount >= Context->PortConfig.NumberOfAccessRanges) { DebugPrint((1, "SpParseDevice: Too many access ranges.\n")); continue; } Context->AccessRanges[rangeCount].RangeStart = descriptor->u.Memory.Start; Context->AccessRanges[rangeCount].RangeLength = descriptor->u.Memory.Length; Context->AccessRanges[rangeCount].RangeInMemory = TRUE; rangeCount++; break; case CmResourceTypeInterrupt: Context->PortConfig.BusInterruptVector = descriptor->u.Interrupt.Vector; Context->PortConfig.BusInterruptLevel = descriptor->u.Interrupt.Level; break; case CmResourceTypeDma: Context->PortConfig.DmaChannel = descriptor->u.Dma.Channel; Context->PortConfig.DmaPort = descriptor->u.Dma.Port; break; case CmResourceTypeDeviceSpecific: if (descriptor->u.DeviceSpecificData.DataSize < sizeof(CM_SCSI_DEVICE_DATA) || (PCHAR) (descriptor + 1) - (PCHAR) resource + descriptor->u.DeviceSpecificData.DataSize > keyValueInformation->DataLength) { DebugPrint((1, "SpParseDevice: Device specific resource data too small.\n")); break; } // // The actual data follows the descriptor. // scsiData = (PCM_SCSI_DEVICE_DATA) (descriptor+1); Context->PortConfig.InitiatorBusId[0] = scsiData->HostIdentifier; break; } } } // // See if an entry for uncached extension alignment has been set. // if (_wcsnicmp(keyValueInformation->Name, L"UncachedExtAlignment", keyValueInformation->NameLength/2) == 0) { ULONG value; if (keyValueInformation->Type != REG_DWORD) { DebugPrint((1, "SpParseDevice: Bad data type for " "UncachedExtAlignment.\n")); continue; } value = *((PULONG)(Buffer + keyValueInformation->DataOffset)); // // Specified alignment must be 3 to 16, which equates to 8-byte and // 64k-byte alignment, respectively. // if (value > 16) { value = 16; } else if (value < 3) { value = 3; } DeviceExtension->UncachedExtAlignment = 1 << value; DebugPrint((1, "SpParseDevice: Uncached ext alignment = %d.\n", DeviceExtension->UncachedExtAlignment)); } // UncachedExtAlignment // // Look for an override to the default reset hold period. // if (_wcsnicmp(keyValueInformation->Name, L"ResetHoldTime", keyValueInformation->NameLength/2) == 0) { ULONG value; if (keyValueInformation->Type != REG_DWORD) { DebugPrint((1, "SpParseDevice: Bad data type for " "ResetHoldTime.\n")); continue; } value = *((PULONG)(Buffer + keyValueInformation->DataOffset)); DeviceExtension->ResetHoldTime = (value <= 60) ? value : 60; } // ResetHoldPeriod // // Look for setting instructing us to create an initiator LU. // if (_wcsnicmp(keyValueInformation->Name, L"CreateInitiatorLU", keyValueInformation->NameLength/2) == 0) { ULONG value; if (keyValueInformation->Type != REG_DWORD) { DebugPrint((1, "SpParseDevice: Bad data type for " "CreateInitiatorLU.\n")); continue; } value = *((PULONG)(Buffer + keyValueInformation->DataOffset)); DeviceExtension->CreateInitiatorLU = (value == 0) ? FALSE : TRUE; } // CreateInitiatorLU } } NTSTATUS SpConfigurationCallout( 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 indicate that the requested perpherial data was found. Arguments: Context - Supplies a pointer to boolean which is set to TURE when this routine is call. The remaining arguments are unsed. Return Value: Returns success. --*/ { PAGED_CODE(); *(PBOOLEAN) Context = TRUE; return(STATUS_SUCCESS); } NTSTATUS SpGetRegistryValue( IN PDRIVER_OBJECT DriverObject, IN HANDLE Handle, IN PWSTR KeyString, OUT PKEY_VALUE_FULL_INFORMATION *KeyInformation ) /*++ Routine Description: This routine retrieve's any data associated with a registry key. The key is queried with a zero-length buffer to get it's actual size then a buffer is allocated and the actual query takes place. It is the responsibility of the caller to free the buffer. Arguments: Handle - Supplies the key handle whose value is to be queried KeyString - Supplies the null-terminated Unicode name of the value. KeyInformation - Returns a pointer to the allocated data buffer. Return Value: The function value is the final status of the query operation. --*/ { UNICODE_STRING unicodeString; NTSTATUS status; PKEY_VALUE_FULL_INFORMATION infoBuffer; ULONG keyValueLength; PAGED_CODE(); RtlInitUnicodeString(&unicodeString, KeyString); // // Query with a zero-length buffer, to get the size needed. // status = ZwQueryValueKey( Handle, &unicodeString, KeyValueFullInformation, (PVOID) NULL, 0, &keyValueLength); if (status != STATUS_BUFFER_OVERFLOW && status != STATUS_BUFFER_TOO_SMALL) { *KeyInformation = NULL; return status; } // // Allocate a buffer large enough to contain the entire key data value. // infoBuffer = SpAllocatePool(NonPagedPool, keyValueLength, SCSIPORT_TAG_REGISTRY, DriverObject); if(!infoBuffer) { *KeyInformation = NULL; return STATUS_INSUFFICIENT_RESOURCES; } // // Query the data for the key value. // status = ZwQueryValueKey( Handle, &unicodeString, KeyValueFullInformation, infoBuffer, keyValueLength, &keyValueLength); if(!NT_SUCCESS(status)) { ExFreePool(infoBuffer); *KeyInformation = NULL; return status; } *KeyInformation = infoBuffer; return STATUS_SUCCESS; } VOID SpBuildConfiguration( IN PADAPTER_EXTENSION AdapterExtension, IN PHW_INITIALIZATION_DATA HwInitializationData, IN PPORT_CONFIGURATION_INFORMATION ConfigInformation ) /*++ Routine Description: Given a full resource description, fill in the port configuration information. Arguments: HwInitializationData - to know maximum resources for device. ControllerData - the CM_FULL_RESOURCE list for this configuration ConfigInformation - the config info structure to be filled in Return Value: None --*/ { ULONG rangeNumber; ULONG index; PACCESS_RANGE accessRange; PCM_FULL_RESOURCE_DESCRIPTOR resourceList; PCM_PARTIAL_RESOURCE_DESCRIPTOR partialData; PAGED_CODE(); rangeNumber = 0; ASSERT(!AdapterExtension->IsMiniportDetected); ASSERT(AdapterExtension->AllocatedResources); resourceList = AdapterExtension->AllocatedResources->List; for (index = 0; index < resourceList->PartialResourceList.Count; index++) { partialData = &resourceList->PartialResourceList.PartialDescriptors[index]; switch (partialData->Type) { case CmResourceTypePort: // // Verify range count does not exceed what the // miniport indicated. // if (HwInitializationData->NumberOfAccessRanges > rangeNumber) { // // Get next access range. // accessRange = &((*(ConfigInformation->AccessRanges))[rangeNumber]); accessRange->RangeStart = partialData->u.Port.Start; accessRange->RangeLength = partialData->u.Port.Length; accessRange->RangeInMemory = FALSE; rangeNumber++; } break; case CmResourceTypeInterrupt: ConfigInformation->BusInterruptLevel = partialData->u.Interrupt.Level; ConfigInformation->BusInterruptVector = partialData->u.Interrupt.Vector; // // Check interrupt mode. // if (partialData->Flags == CM_RESOURCE_INTERRUPT_LATCHED) { ConfigInformation->InterruptMode = Latched; } else if (partialData->Flags == CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE) { ConfigInformation->InterruptMode = LevelSensitive; } AdapterExtension->HasInterrupt = TRUE; break; case CmResourceTypeMemory: // // Verify range count does not exceed what the // miniport indicated. // if (HwInitializationData->NumberOfAccessRanges > rangeNumber) { // // Get next access range. // accessRange = &((*(ConfigInformation->AccessRanges))[rangeNumber]); accessRange->RangeStart = partialData->u.Memory.Start; accessRange->RangeLength = partialData->u.Memory.Length; accessRange->RangeInMemory = TRUE; rangeNumber++; } break; case CmResourceTypeDma: ConfigInformation->DmaChannel = partialData->u.Dma.Channel; ConfigInformation->DmaPort = partialData->u.Dma.Port; break; } } } #if !defined(NO_LEGACY_DRIVERS) BOOLEAN GetPciConfiguration( IN PDRIVER_OBJECT DriverObject, IN OUT PDEVICE_OBJECT DeviceObject, IN PHW_INITIALIZATION_DATA HwInitializationData, IN PVOID RegistryPath, IN ULONG BusNumber, IN OUT PPCI_SLOT_NUMBER SlotNumber ) /*++ Routine Description: Walk PCI slot information looking for Vendor and Product ID matches. Get slot information for matches and register with hal for the resources. Arguments: DriverObject - Miniport driver object. DeviceObject - Represents this adapter. HwInitializationData - Miniport description. RegistryPath - Service key path. BusNumber - PCI bus number for this search. SlotNumber - Starting slot number for this search. Return Value: TRUE if card found. Slot and function numbers will return values that should be used to continue the search for additional cards, when a card is found. --*/ { PADAPTER_EXTENSION fdoExtension = DeviceObject->DeviceExtension; ULONG rangeNumber = 0; ULONG pciBuffer; ULONG slotNumber; ULONG functionNumber; ULONG status; PCI_SLOT_NUMBER slotData; PPCI_COMMON_CONFIG pciData; UNICODE_STRING unicodeString; UCHAR vendorString[5]; UCHAR deviceString[5]; PAGED_CODE(); pciData = (PPCI_COMMON_CONFIG)&pciBuffer; // // // typedef struct _PCI_SLOT_NUMBER { // union { // struct { // ULONG DeviceNumber:5; // ULONG FunctionNumber:3; // ULONG Reserved:24; // } bits; // ULONG AsULONG; // } u; // } PCI_SLOT_NUMBER, *PPCI_SLOT_NUMBER; // slotData.u.AsULONG = 0; // // Look at each device. // for (slotNumber = (*SlotNumber).u.bits.DeviceNumber; slotNumber < 32; slotNumber++) { slotData.u.bits.DeviceNumber = slotNumber; // // Look at each function. // for (functionNumber= (*SlotNumber).u.bits.FunctionNumber; functionNumber < 8; functionNumber++) { slotData.u.bits.FunctionNumber = functionNumber; // // Make sure that the function number loop restarts at function // zero, not what was passed in. If we find an adapter we'll // reset this value to contain the next function number to // be tested. // (*SlotNumber).u.bits.FunctionNumber = 0; if (!HalGetBusData(PCIConfiguration, BusNumber, slotData.u.AsULONG, pciData, sizeof(ULONG))) { // // Out of PCI data. // return FALSE; } if (pciData->VendorID == PCI_INVALID_VENDORID) { // // No PCI device, or no more functions on device // move to next PCI device. // break; } // // Translate hex ids to strings. // sprintf(vendorString, "%04x", pciData->VendorID); sprintf(deviceString, "%04x", pciData->DeviceID); DebugPrint((1, "GetPciConfiguration: Bus %x Slot %x Function %x Vendor %s Product %s\n", BusNumber, slotNumber, functionNumber, vendorString, deviceString)); // // Compare strings. // if (_strnicmp(vendorString, HwInitializationData->VendorId, HwInitializationData->VendorIdLength) || _strnicmp(deviceString, HwInitializationData->DeviceId, HwInitializationData->DeviceIdLength)) { // // Not our PCI device. Try next device/function // continue; } // // This is the miniport drivers slot. Allocate the // resources. // RtlInitUnicodeString(&unicodeString, L"ScsiAdapter"); status = HalAssignSlotResources( RegistryPath, &unicodeString, DriverObject, DeviceObject, PCIBus, BusNumber, slotData.u.AsULONG, &(fdoExtension->AllocatedResources)); if (!NT_SUCCESS(status)) { // // ToDo: Log this error. // DebugPrint((0, "SCSIPORT - GetPciConfiguration: Resources for " "bus %d slot %d could not be retrieved [%#08lx]\n", BusNumber, slotData.u.AsULONG, status)); break; } // // Record PCI slot number for miniport. // slotData.u.bits.FunctionNumber++; *SlotNumber = slotData; // // Translate the resources // status = SpTranslateResources(DriverObject, fdoExtension->AllocatedResources, &(fdoExtension->TranslatedResources)); return TRUE; } // next PCI function } // next PCI slot return FALSE; } // GetPciConfiguration() #endif // NO_LEGACY_DRIVERS ULONG ScsiPortSetBusDataByOffset( IN PVOID DeviceExtension, IN ULONG BusDataType, IN ULONG SystemIoBusNumber, IN ULONG SlotNumber, IN PVOID Buffer, IN ULONG Offset, IN ULONG Length ) /*++ Routine Description: The function returns writes bus data to a specific offset within a slot. Arguments: DeviceExtension - State information for a particular adapter. BusDataType - Supplies the type of bus. SystemIoBusNumber - Indicates which system IO bus. SlotNumber - Indicates which slot. Buffer - Supplies the data to write. Offset - Byte offset to begin the write. Length - Supplies a count in bytes of the maximum amount to return. Return Value: Number of bytes written. --*/ { PADAPTER_EXTENSION fdoExtension = GET_FDO_EXTENSION(DeviceExtension); if(!fdoExtension->IsInVirtualSlot) { #if defined(NO_LEGACY_DRIVERS) DebugPrint((1,"ScsiPortSetBusDataByOffset: !fdoExtension->" "IsInVirtualSlot, not supported for 64-bits.\n")); return STATUS_INVALID_PARAMETER; #else return(HalSetBusDataByOffset(BusDataType, SystemIoBusNumber, SlotNumber, Buffer, Offset, Length)); #endif // NO_LEGACY_DRIVERS } else { // // ThePCI bus interface SetBusData routine only accepts read requests // from PCIConfiguration space. We do not support anything else. // if (BusDataType != PCIConfiguration) { ASSERT(FALSE && "Invalid PCI_WHICHSPACE_ parameter"); return 0; } ASSERT(fdoExtension->LowerBusInterfaceStandardRetrieved == TRUE); return fdoExtension->LowerBusInterfaceStandard.SetBusData( fdoExtension->LowerBusInterfaceStandard.Context, PCI_WHICHSPACE_CONFIG, Buffer, Offset, Length); } } // end ScsiPortSetBusDataByOffset() VOID SpCreateScsiDirectory( VOID ) { UNICODE_STRING unicodeDirectoryName; OBJECT_ATTRIBUTES objectAttributes; HANDLE directory; NTSTATUS status; PAGED_CODE(); RtlInitUnicodeString( &unicodeDirectoryName, L"\\Device\\Scsi"); InitializeObjectAttributes( &objectAttributes, &unicodeDirectoryName, OBJ_CASE_INSENSITIVE | OBJ_PERMANENT, NULL, NULL); status = ZwCreateDirectoryObject(&directory, DIRECTORY_ALL_ACCESS, &objectAttributes); if(NT_SUCCESS(status)) { ObReferenceObjectByHandle(directory, FILE_READ_ATTRIBUTES, NULL, KernelMode, &ScsiDirectory, NULL); ZwClose(directory); } return; } NTSTATUS SpReportNewAdapter( IN PDEVICE_OBJECT DeviceObject ) /*++ Routine Description: This routine will report an adapter discovered while sniffing through the system to plug and play in order to get an add device call for it This is done by: * calling IoReportDetectedDevice to inform PnP about the new device * storing the returned PDO pointer into the device extension as the lower device object so we can match PDO to FDO when the add device rolls around Arguments: DeviceObject - a pointer to the device object that was "found" Return Value: status --*/ { PDRIVER_OBJECT driverObject = DeviceObject->DriverObject; PADAPTER_EXTENSION functionalExtension = DeviceObject->DeviceExtension; PPORT_CONFIGURATION_INFORMATION configInfo = functionalExtension->PortConfig; PDEVICE_OBJECT pdo = NULL; BOOLEAN resourceAssigned; NTSTATUS status; PAGED_CODE(); ASSERT(functionalExtension->AllocatedResources != NULL); ASSERT(functionalExtension->IsPnp == FALSE); if(functionalExtension->IsMiniportDetected) { // // We haven't claimed the resources yet and we need pnp to give them // to us the next time around. // resourceAssigned = FALSE; } else { // // The port driver located this device using the HAL to scan all // appropriate bus slots. It's already claimed those resources and // on the next boot we'll hopefully have a duplicate PDO to use // for the device. Don't let pnp grab the resources on our behalf. // resourceAssigned = TRUE; } status = IoReportDetectedDevice(driverObject, configInfo->AdapterInterfaceType, configInfo->SystemIoBusNumber, configInfo->SlotNumber, functionalExtension->AllocatedResources, NULL, resourceAssigned, &pdo); // // If we got a PDO then setup information about slot and bus numbers in // the devnode in the registry. These may not be valid but we assume that // if the miniport asks for slot info then it's on a bus that supports it. // if(NT_SUCCESS(status)) { HANDLE instanceHandle; NTSTATUS writeStatus; writeStatus = SpWriteNumericInstanceValue( pdo, L"BusNumber", configInfo->SystemIoBusNumber); status = min(writeStatus, status); writeStatus = SpWriteNumericInstanceValue( pdo, L"SlotNumber", configInfo->SlotNumber); status = min(writeStatus, status); writeStatus = SpWriteNumericInstanceValue( pdo, L"LegacyInterfaceType", configInfo->AdapterInterfaceType); status = min(writeStatus, status); } if(NT_SUCCESS(status)) { PDEVICE_OBJECT newStack; newStack = IoAttachDeviceToDeviceStack( DeviceObject, pdo); functionalExtension->CommonExtension.LowerDeviceObject = newStack; functionalExtension->LowerPdo = pdo; if(newStack == NULL) { status = STATUS_UNSUCCESSFUL; } else { status = STATUS_SUCCESS; } } return status; } NTSTATUS SpCreateAdapter( IN PDRIVER_OBJECT DriverObject, OUT PDEVICE_OBJECT *Fdo ) /*++ Routine Description: This routine will allocate a new functional device object for an adapter. It will allocate the device and fill in the common and functional device extension fields which can be setup without any information about the adapter this device object is for. This routine will increment the global ScsiPortCount Arguments: DriverObject - a pointer to the driver object for this device Fdo - a location to store the FDO pointer if the routine is successful Return Value: status --*/ { PSCSIPORT_DRIVER_EXTENSION driverExtension; LONG adapterNumber; ULONG i, j; PUNICODE_STRING registryPath; WCHAR wideBuffer[128]; ULONG serviceNameIndex = 0; ULONG serviceNameChars; WCHAR wideDeviceName[64]; UNICODE_STRING unicodeDeviceName; PWCHAR savedDeviceName = NULL; PADAPTER_EXTENSION fdoExtension; PCOMMON_EXTENSION commonExtension; NTSTATUS status; PAGED_CODE(); driverExtension = IoGetDriverObjectExtension(DriverObject, ScsiPortInitialize); adapterNumber = InterlockedIncrement(&driverExtension->AdapterCount); RtlZeroMemory(wideBuffer, sizeof(wideBuffer)); registryPath = &(driverExtension->RegistryPath); for(i = 0; i < (registryPath->Length / sizeof(WCHAR)); i++) { if(registryPath->Buffer[i] == UNICODE_NULL) { i--; break; } if((registryPath->Buffer[i] == L'\\') || (registryPath->Buffer[i] == L'/')) { serviceNameIndex = i+1; } } serviceNameChars = (i - serviceNameIndex) + 1; DebugPrint((2, "SpCreateAdapter: Registry buffer %#p\n", registryPath)); DebugPrint((2, "SpCreateAdapter: Starting offset %d chars\n", serviceNameIndex)); DebugPrint((2, "SpCreateAdapter: Ending offset %d chars\n", i)); DebugPrint((2, "SpCreateAdapter: %d chars or %d bytes will be copied\n", serviceNameChars, (serviceNameChars * sizeof(WCHAR)))); DebugPrint((2, "SpCreateAdapter: Name is \"")); for(j = 0; j < serviceNameChars; j++) { DebugPrint((2, "%wc", registryPath->Buffer[serviceNameIndex + j])); } DebugPrint((2, "\"\n")); RtlCopyMemory(wideBuffer, &(registryPath->Buffer[serviceNameIndex]), serviceNameChars * sizeof(WCHAR)); swprintf(wideDeviceName, L"\\Device\\Scsi\\%ws%d", wideBuffer, adapterNumber); RtlInitUnicodeString(&unicodeDeviceName, wideDeviceName); DebugPrint((1, "SpCreateFdo: Device object name is %wZ\n", &unicodeDeviceName)); status = IoCreateDevice( DriverObject, ADAPTER_EXTENSION_SIZE + unicodeDeviceName.MaximumLength, &unicodeDeviceName, FILE_DEVICE_CONTROLLER, FILE_DEVICE_SECURE_OPEN, FALSE, Fdo); ASSERTMSG("Name isn't unique: ", status != STATUS_OBJECT_NAME_COLLISION); if(!NT_SUCCESS(status)) { DebugPrint((1, "ScsiPortAddDevice: couldn't allocate new FDO " "[%#08lx]\n", status)); return status; } fdoExtension = (*Fdo)->DeviceExtension; commonExtension = &(fdoExtension->CommonExtension); RtlZeroMemory(fdoExtension, ADAPTER_EXTENSION_SIZE); commonExtension->DeviceObject = *Fdo; commonExtension->IsPdo = FALSE; commonExtension->MajorFunction = AdapterMajorFunctionTable; commonExtension->WmiInitialized = FALSE; commonExtension->WmiMiniPortSupport = FALSE; commonExtension->WmiScsiPortRegInfoBuf = NULL; commonExtension->WmiScsiPortRegInfoBufSize = 0; commonExtension->CurrentPnpState = 0xff; commonExtension->PreviousPnpState = 0xff; commonExtension->CurrentDeviceState = PowerDeviceD0; commonExtension->DesiredDeviceState = PowerDeviceUnspecified; commonExtension->CurrentSystemState = PowerSystemWorking; KeInitializeEvent(&commonExtension->RemoveEvent, SynchronizationEvent, FALSE); // // Initialize remove lock to zero. It will be incremented once pnp is aware // of its existance. // commonExtension->RemoveLock = 0; #if DBG KeInitializeSpinLock(&commonExtension->RemoveTrackingSpinlock); commonExtension->RemoveTrackingList = NULL; ExInitializeNPagedLookasideList( &(commonExtension->RemoveTrackingLookasideList), NULL, NULL, 0, sizeof(REMOVE_TRACKING_BLOCK), SCSIPORT_TAG_LOCK_TRACKING, 64); commonExtension->RemoveTrackingLookasideListInitialized = TRUE; #else commonExtension->RemoveTrackingSpinlock = (ULONG) -1L; commonExtension->RemoveTrackingList = (PVOID) -1L; #endif SpAcquireRemoveLock(*Fdo, *Fdo); // // Initialize the logical unit list locks. // for(i = 0; i < NUMBER_LOGICAL_UNIT_BINS; i++) { KeInitializeSpinLock(&fdoExtension->LogicalUnitList[i].Lock); } // // Don't set port number until the device has been started. // fdoExtension->PortNumber = (ULONG) -1; fdoExtension->AdapterNumber = adapterNumber; // // Copy the device name for later use. // fdoExtension->DeviceName = (PWSTR) (fdoExtension + 1); RtlCopyMemory(fdoExtension->DeviceName, unicodeDeviceName.Buffer, unicodeDeviceName.MaximumLength); // // Initialize the enumeration synchronization event. // KeInitializeMutex(&(fdoExtension->EnumerationDeviceMutex), 0); ExInitializeFastMutex(&(fdoExtension->EnumerationWorklistMutex)); ExInitializeWorkItem(&(fdoExtension->EnumerationWorkItem), SpEnumerationWorker, fdoExtension); // // Initialize the power up mutex. // ExInitializeFastMutex(&(fdoExtension->PowerMutex)); // // Set uncached extension limits to valid values. // fdoExtension->MaximumCommonBufferBase.HighPart = 0; fdoExtension->MaximumCommonBufferBase.LowPart = 0xffffffff; // // Initialize the adapter BlockedLogicalUnit to point to itself // fdoExtension->BlockedLogicalUnit = (PLOGICAL_UNIT_EXTENSION) &fdoExtension->BlockedLogicalUnit; (*Fdo)->Flags |= DO_DIRECT_IO; (*Fdo)->Flags &= ~DO_DEVICE_INITIALIZING; // fdoExtension->CommonExtension.IsInitialized = TRUE; return status; } VOID SpInitializeAdapterExtension( IN PADAPTER_EXTENSION FdoExtension, IN PHW_INITIALIZATION_DATA HwInitializationData, IN OUT PHW_DEVICE_EXTENSION HwDeviceExtension OPTIONAL ) /*++ Routine Description: This routine will setup the miniport entry points and initialize values in the port driver device extension. It will also setup the pointers to the HwDeviceExtension if supplied Arguments: FdoExtension - the fdo extension being initialized HwInitializationData - the init data we are using to initalize the fdo extension HwDeviceExtension - the miniport's private extension Return Value: none --*/ { PSCSIPORT_DRIVER_EXTENSION DrvExt; PAGED_CODE(); FdoExtension->HwFindAdapter = HwInitializationData->HwFindAdapter; FdoExtension->HwInitialize = HwInitializationData->HwInitialize; FdoExtension->HwStartIo = HwInitializationData->HwStartIo; FdoExtension->HwInterrupt = HwInitializationData->HwInterrupt; FdoExtension->HwResetBus = HwInitializationData->HwResetBus; FdoExtension->HwDmaStarted = HwInitializationData->HwDmaStarted; FdoExtension->HwLogicalUnitExtensionSize = HwInitializationData->SpecificLuExtensionSize; FdoExtension->HwAdapterControl = NULL; if(HwInitializationData->HwInitializationDataSize >= (FIELD_OFFSET(HW_INITIALIZATION_DATA, HwAdapterControl) + sizeof(PHW_ADAPTER_CONTROL))) { // // This miniport knows about the stop adapter routine. Store the // pointer away. // FdoExtension->HwAdapterControl = HwInitializationData->HwAdapterControl; } // // If scsiport's verifier is configured, initialize the verifier extension. // DrvExt = IoGetDriverObjectExtension( FdoExtension->DeviceObject->DriverObject, ScsiPortInitialize); if (DrvExt != NULL && DrvExt->Verifying == 1) { SpDoVerifierInit(FdoExtension, HwInitializationData); } // // Check if the miniport driver requires any noncached memory. // SRB extensions will come from this memory. Round the size // a multiple of quadwords // FdoExtension->SrbExtensionSize = (HwInitializationData->SrbExtensionSize + sizeof(LONGLONG) - 1) & ~(sizeof(LONGLONG) - 1); // // Initialize the maximum lu count // FdoExtension->MaxLuCount = SCSI_MAXIMUM_LOGICAL_UNITS; FdoExtension->NumberOfRequests = MINIMUM_SRB_EXTENSIONS; if(ARGUMENT_PRESENT(HwDeviceExtension)) { HwDeviceExtension->FdoExtension = FdoExtension; FdoExtension->HwDeviceExtension = HwDeviceExtension->HwDeviceExtension; } #if defined(FORWARD_PROGRESS) // // Initialize the reserved pages which we use to ensure that forward progress // can be made in low-memory conditions. // FdoExtension->ReservedPages = MmAllocateMappingAddress( SP_RESERVED_PAGES * PAGE_SIZE, SCSIPORT_TAG_MAPPING_LIST); // // Allocate a spare MDL for use in low memory conditions. Note that we // pass NULL as the VirtualAddress. We do this because we're reinitialize // the MDL everytime we use it with the appropriate VA and size. // FdoExtension->ReservedMdl = IoAllocateMdl(NULL, SP_RESERVED_PAGES * PAGE_SIZE, FALSE, FALSE, NULL); #endif FdoExtension->SrbTimeout = SRB_DEFAULT_TIMEOUT; // // Initialize the reset hold period to default 4 seconds. // FdoExtension->ResetHoldTime = 4; return; } #if !defined(NO_LEGACY_DRIVERS) NTSTATUS ScsiPortInitLegacyAdapter( IN PSCSIPORT_DRIVER_EXTENSION DriverExtension, IN PHW_INITIALIZATION_DATA HwInitializationData, IN PVOID HwContext ) /*++ Routine Description: This routine will locate the adapters attached to a given bus type and then report them (and their necessary resources) to the pnp system to be initialized later. If adapters are found, this routine will pre-initialize their device extensions and place them into one of the init chains for use during Add/Start device routines. Arguments: DriverExtension - a pointer to the driver extension for this miniport HwInitializationData - the init data that the miniport handed to ScsiPortInitialize Return Value: status --*/ { CONFIGURATION_CONTEXT configurationContext; PPORT_CONFIGURATION_INFORMATION configInfo = NULL; PUNICODE_STRING registryPath = &(DriverExtension->RegistryPath); PHW_DEVICE_EXTENSION hwDeviceExtension = NULL; PDEVICE_OBJECT fdo; PADAPTER_EXTENSION fdoExtension; BOOLEAN callAgain = FALSE; BOOLEAN isPci = FALSE; PCI_SLOT_NUMBER slotNumber; OBJECT_ATTRIBUTES objectAttributes; PCM_RESOURCE_LIST resourceList; ULONG uniqueId; BOOLEAN attached = FALSE; NTSTATUS returnStatus = STATUS_DEVICE_DOES_NOT_EXIST; NTSTATUS status; PAGED_CODE(); slotNumber.u.AsULONG = 0; RtlZeroMemory(&configurationContext, sizeof(configurationContext)); if(HwInitializationData->NumberOfAccessRanges != 0) { configurationContext.AccessRanges = SpAllocatePool(PagedPool, (HwInitializationData->NumberOfAccessRanges * sizeof(ACCESS_RANGE)), SCSIPORT_TAG_ACCESS_RANGE, DriverExtension->DriverObject); if(configurationContext.AccessRanges == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } } // // Keep calling the miniport's find adapter routine until the miniport // indicates it is doen and there is no more configuration information. // The loop is terminated when the SpInitializeConfiguration routine // inidcates ther eis no more configuration information or an error occurs. // do { ULONG hwDeviceExtensionSize = HwInitializationData->DeviceExtensionSize + sizeof(HW_DEVICE_EXTENSION); attached = FALSE; fdo = NULL; fdoExtension = NULL; // // Allocate the HwDeviceExtension first - it's easier to deallocate :) // hwDeviceExtension = SpAllocatePool(NonPagedPool, hwDeviceExtensionSize, SCSIPORT_TAG_DEV_EXT, DriverExtension->DriverObject); if(hwDeviceExtension == NULL) { DebugPrint((1, "SpInitLegacyAdapter: Could not allocate " "HwDeviceExtension\n")); fdoExtension = NULL; uniqueId = __LINE__; status = STATUS_INSUFFICIENT_RESOURCES; break; } RtlZeroMemory(hwDeviceExtension, hwDeviceExtensionSize); status = SpCreateAdapter(DriverExtension->DriverObject, &fdo); if(!NT_SUCCESS(status)) { DebugPrint((1, "SpInitLegacyAdapter: Could not allocate " "fdo [%#08lx]\n", status)); ExFreePool(hwDeviceExtension); uniqueId = __LINE__; break; } fdoExtension = fdo->DeviceExtension; fdoExtension->IsMiniportDetected = TRUE; // // Setup device extension pointers // SpInitializeAdapterExtension(fdoExtension, HwInitializationData, hwDeviceExtension); hwDeviceExtension = NULL; fdoExtension->CommonExtension.IsInitialized = TRUE; NewConfiguration: // // initialize the miniport config info buffer // status = SpInitializeConfiguration( fdoExtension, &DriverExtension->RegistryPath, HwInitializationData, &configurationContext); if(!NT_SUCCESS(status)) { uniqueId = __LINE__; break; } // // Allocate a config-info structure and access ranges for the // miniport drivers to use // configInfo = SpAllocatePool( NonPagedPool, ((sizeof(PORT_CONFIGURATION_INFORMATION) + (HwInitializationData->NumberOfAccessRanges * sizeof(ACCESS_RANGE)) + 7) & ~7), SCSIPORT_TAG_ACCESS_RANGE, DriverExtension->DriverObject); if(configInfo == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; uniqueId = __LINE__; break; } fdoExtension->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 = fdoExtension->SrbExtensionSize; configInfo->SpecificLuExtensionSize = fdoExtension->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))); } ASSERT(HwInitializationData->AdapterInterfaceType != Internal); // // If PCI bus initialize configuration information with // slot information. // if(HwInitializationData->AdapterInterfaceType == PCIBus && HwInitializationData->VendorIdLength > 0 && HwInitializationData->DeviceIdLength > 0 && HwInitializationData->DeviceId && HwInitializationData->VendorId) { PCI_SLOT_NUMBER tmp; isPci = TRUE; configInfo->BusInterruptLevel = 0; if(!GetPciConfiguration(DriverExtension->DriverObject, fdo, HwInitializationData, registryPath, configurationContext.BusNumber, &slotNumber)) { // // Adapter not found. Continue search with next bus // configurationContext.BusNumber++; slotNumber.u.AsULONG = 0; fdoExtension->PortConfig = NULL; ExFreePool(configInfo); callAgain = FALSE; goto NewConfiguration; } fdoExtension->IsMiniportDetected = FALSE; // // GetPciConfiguration increments the function number when it // finds something. We need to be looking at the previous // function number. // tmp.u.AsULONG = slotNumber.u.AsULONG; tmp.u.bits.FunctionNumber--; configInfo->SlotNumber = tmp.u.AsULONG; SpBuildConfiguration(fdoExtension, HwInitializationData, configInfo); if(!configInfo->BusInterruptLevel) { // // No interrupt was assigned - skip this slot and call // again // fdoExtension->PortConfig = NULL; ExFreePool(configInfo); goto NewConfiguration; } } // // Get the miniport configuration inofmraiton // callAgain = FALSE; status = SpCallHwFindAdapter(fdo, HwInitializationData, HwContext, &configurationContext, configInfo, &callAgain); if(NT_SUCCESS(status)) { status = SpAllocateAdapterResources(fdo); if(NT_SUCCESS(status)) { status = SpCallHwInitialize(fdo); } attached = TRUE; } else if (status == STATUS_DEVICE_DOES_NOT_EXIST) { PCM_RESOURCE_LIST emptyResources = NULL; configurationContext.BusNumber++; fdoExtension->PortConfig = NULL; ExFreePool(configInfo); callAgain = FALSE; // // Release the resources we've allocated for this device object // if it's a PCI system. // IoAssignResources(registryPath, NULL, DriverExtension->DriverObject, fdo, NULL, &emptyResources); if(emptyResources != NULL) { ExFreePool(emptyResources); } goto NewConfiguration; } if(NT_SUCCESS(status)) { // // Try to start the adapter // status = ScsiPortStartAdapter(fdo); if(NT_SUCCESS(status)) { fdoExtension->CommonExtension.CurrentPnpState = IRP_MN_START_DEVICE; } } if(!NT_SUCCESS(returnStatus)) { // // if no devices were found then just return the current status // returnStatus = status; } if(!NT_SUCCESS(status)) { break; } SpEnumerateAdapterSynchronous(fdoExtension, TRUE); // // update the local adapter count // configurationContext.AdapterNumber++; // // Bump the bus number if miniport inidicated that it should not be // called again on this bus. // if(!callAgain) { configurationContext.BusNumber++; } // // Set the return status to STATUS_SUCCESS to indicate that one HBA // was found. // returnStatus = STATUS_SUCCESS; } while(TRUE); if(!NT_SUCCESS(status)) { // // If the device existed but some other error occurred then log it. // if(status != STATUS_DEVICE_DOES_NOT_EXIST) { 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); } } if(attached) { // // Tell PNP that this device should be destroyed. // fdoExtension->DeviceState = PNP_DEVICE_DISABLED | PNP_DEVICE_FAILED; fdoExtension->CommonExtension.CurrentPnpState = IRP_MN_REMOVE_DEVICE; IoInvalidateDeviceState(fdoExtension->LowerPdo); } else { // // If the HwDeviceExtension hasn't been deleted or assigned to the // adapter yet then delete it. // if(hwDeviceExtension != NULL) { ExFreePool(hwDeviceExtension); } // // Clean up the last device object which is not used. // if (fdoExtension != NULL) { fdoExtension->CommonExtension.IsRemoved = REMOVE_PENDING; fdoExtension->CommonExtension.CurrentPnpState = IRP_MN_REMOVE_DEVICE; SpReleaseRemoveLock(fdoExtension->DeviceObject, fdoExtension->DeviceObject); SpDestroyAdapter(fdoExtension, FALSE); } // // Delete it. // IoDeleteDevice(fdo); } if (configurationContext.AccessRanges != NULL) { ExFreePool(configurationContext.AccessRanges); } if (configurationContext.Parameter != NULL) { ExFreePool(configurationContext.Parameter); } } return returnStatus; } #endif // NO_LEGACY_DRIVERS NTSTATUS SpCallHwFindAdapter( IN PDEVICE_OBJECT Fdo, IN PHW_INITIALIZATION_DATA HwInitData, IN PVOID HwContext OPTIONAL, IN OUT PCONFIGURATION_CONTEXT ConfigurationContext, IN OUT PPORT_CONFIGURATION_INFORMATION ConfigInfo, OUT PBOOLEAN CallAgain ) /*++ Routine Description: This routine will issue a call to the miniport's find adapter routine Arguments: Fdo - the fdo for the adapter being found. This fdo must have already had it's device extension initialized and a HwDeviceExtension allocated HwInitData - a pointer to the HwINitializationData block passed in by the miniport HwContext - the context information passed into ScsiPortInitialize by the miniport if it's still available ConfigurationContext - A configuration context structure which contains state information during a device detection ConfigInfo - the config info structure for the miniport's resources CallAgain - a boolean flag indicating whether the miniport said to call it again for this interface type Return Value: status --*/ { PADAPTER_EXTENSION adapter = Fdo->DeviceExtension; PSCSIPORT_DRIVER_EXTENSION driverExtension = IoGetDriverObjectExtension(Fdo->DriverObject, ScsiPortInitialize); NTSTATUS status; PCM_RESOURCE_LIST resourceList; *CallAgain = FALSE; // // Preallocate space for 20 address mappings. This should be enough // to handle any miniport. We'll shrink down the allocation and // setup the appropriate "next" pointers once the adapter has been // initialized. // SpPreallocateAddressMapping(adapter, 20); status = adapter->HwFindAdapter(adapter->HwDeviceExtension, HwContext, NULL, ConfigurationContext->Parameter, ConfigInfo, CallAgain); if(adapter->InterruptData.InterruptFlags & PD_LOG_ERROR) { adapter->InterruptData.InterruptFlags &= ~(PD_LOG_ERROR | PD_NOTIFICATION_REQUIRED); LogErrorEntry(adapter, &(adapter->InterruptData.LogEntry)); } // // Free the pointer to the bus data at map register base. This was // allocated by ScsiPortGetBusData // if(adapter->MapRegisterBase) { ExFreePool(adapter->MapRegisterBase); adapter->MapRegisterBase = NULL; } // // If the device/driver doesn't support bus mastering then it cannot run // on a system with 64-bit addresses. // if((status == SP_RETURN_FOUND) && (ConfigInfo->Master == FALSE) && (Sp64BitPhysicalAddresses == TRUE)) { DebugPrint((0, "SpCallHwFindAdapter: Driver does not support bus " "mastering for adapter %#08lx - this type of adapter is " "not supported on systems with 64-bit physical " "addresses\n", adapter)); return STATUS_NOT_SUPPORTED; } // // If no device was found then return an error // if(status != SP_RETURN_FOUND) { DebugPrint((1, "SpFindAdapter: miniport find adapter routine reported " "an error %d\n", status)); switch(status) { case SP_RETURN_NOT_FOUND: { // // The driver could not find any devices on this bus. // Try the next bus. // *CallAgain = FALSE; return STATUS_DEVICE_DOES_NOT_EXIST; } case SP_RETURN_BAD_CONFIG: { return STATUS_INVALID_PARAMETER; } case SP_RETURN_ERROR: { return STATUS_ADAPTER_HARDWARE_ERROR; } default: { return STATUS_INTERNAL_ERROR; } } return status; } else { status = STATUS_SUCCESS; } // // Cleanup the mapped address list. // SpPurgeFreeMappedAddressList(adapter); DebugPrint((1, "SpFindAdapter: SCSI Adapter ID is %d\n", ConfigInfo->InitiatorBusId[0])); // // Check the resource requirements against the registry. This will // check for conflicts and store the information if none were found. // if(!adapter->IsPnp) { UNICODE_STRING unicodeString; BOOLEAN conflict; RtlInitUnicodeString(&unicodeString, L"ScsiAdapter"); adapter->AllocatedResources = SpBuildResourceList(adapter, ConfigInfo); status = SpReportNewAdapter(Fdo); if(!NT_SUCCESS(status)) { return status; } } // // Update SrbExtensionSize and SpecificLuExtensionSize, if necessary. // If the common buffer has already been allocated, this has already // been done // if(!adapter->NonCachedExtension && (ConfigInfo->SrbExtensionSize != adapter->SrbExtensionSize)) { adapter->SrbExtensionSize = (ConfigInfo->SrbExtensionSize + sizeof(LONGLONG)) & ~(sizeof(LONGLONG) - 1); } if(ConfigInfo->SpecificLuExtensionSize != adapter->HwLogicalUnitExtensionSize) { adapter->HwLogicalUnitExtensionSize = ConfigInfo->SpecificLuExtensionSize; } // // Get maximum target IDs. // if(ConfigInfo->MaximumNumberOfTargets > SCSI_MAXIMUM_TARGETS_PER_BUS) { adapter->MaximumTargetIds = SCSI_MAXIMUM_TARGETS_PER_BUS; } else { adapter->MaximumTargetIds = ConfigInfo->MaximumNumberOfTargets; } // // Get number of SCSI buses. // adapter->NumberOfBuses = ConfigInfo->NumberOfBuses; // // Remember if the adapter caches data. // adapter->CachesData = ConfigInfo->CachesData; // // Save away some of the attributes. // adapter->ReceiveEvent = ConfigInfo->ReceiveEvent; adapter->TaggedQueuing = ConfigInfo->TaggedQueuing; adapter->MultipleRequestPerLu = ConfigInfo->MultipleRequestPerLu; adapter->CommonExtension.WmiMiniPortSupport = ConfigInfo->WmiDataProvider; // // Clear those options which have been disabled in the registry. // if(ConfigurationContext->DisableMultipleLu) { adapter->MultipleRequestPerLu = ConfigInfo->MultipleRequestPerLu = FALSE; } if(ConfigurationContext->DisableTaggedQueueing) { adapter->TaggedQueuing = ConfigInfo->TaggedQueuing = ConfigInfo->MultipleRequestPerLu = FALSE; } // // If the adapter supports tagged queuing or multiple requests per logical // unit, SRB data needs to be allocated. // if (adapter->TaggedQueuing || adapter->MultipleRequestPerLu) { adapter->SupportsMultipleRequests = TRUE; } else { adapter->SupportsMultipleRequests = FALSE; } return status; } NTSTATUS SpAllocateAdapterResources( IN PDEVICE_OBJECT Fdo ) /*++ Routine Description: This routine will allocate and initialize any necessary resources for the adapter. It handles one time initialization of the srb data blocks, srb extensions, etc... Arguments: Fdo - a pointer to the functional device object being initialized Return Value: status --*/ { PADAPTER_EXTENSION fdoExtension = Fdo->DeviceExtension; PIO_SCSI_CAPABILITIES capabilities; PPORT_CONFIGURATION_INFORMATION configInfo = fdoExtension->PortConfig; NTSTATUS status = STATUS_SUCCESS; PVOID SrbExtensionBuffer; PAGED_CODE(); // // Initialize the capabilities pointer // capabilities = &fdoExtension->Capabilities; // // Set indicator as to whether adapter needs kernel mapped buffers // fdoExtension->MapBuffers = configInfo->MapBuffers; capabilities->AdapterUsesPio = configInfo->MapBuffers; // // Determine if a DMA Adapter must be allocated // if((fdoExtension->DmaAdapterObject == NULL) && (configInfo->Master || configInfo->DmaChannel != SP_UNINITIALIZED_VALUE)) { DEVICE_DESCRIPTION deviceDescription; ULONG numberOfMapRegisters; // // Get the adapter object for this card // RtlZeroMemory(&deviceDescription, sizeof(deviceDescription)); deviceDescription.Version = DEVICE_DESCRIPTION_VERSION; deviceDescription.DmaChannel = configInfo->DmaChannel; deviceDescription.InterfaceType = configInfo->AdapterInterfaceType; deviceDescription.BusNumber = configInfo->SystemIoBusNumber; deviceDescription.DmaWidth = configInfo->DmaWidth; deviceDescription.DmaSpeed = configInfo->DmaSpeed; deviceDescription.ScatterGather = configInfo->ScatterGather; deviceDescription.Master = configInfo->Master; deviceDescription.DmaPort = configInfo->DmaPort; deviceDescription.Dma32BitAddresses = configInfo->Dma32BitAddresses; deviceDescription.AutoInitialize = FALSE; deviceDescription.DemandMode = configInfo->DemandMode; deviceDescription.MaximumLength = configInfo->MaximumTransferLength; fdoExtension->Dma32BitAddresses = configInfo->Dma32BitAddresses; // // If the miniport puts anything in here other than 0x80 then we // assume it wants to support 64-bit addresses. // DebugPrint((1, "SpAllocateAdapterResources: Dma64BitAddresses = " "%#0x\n", configInfo->Dma64BitAddresses)); fdoExtension->RemapBuffers = (BOOLEAN) (SpRemapBuffersByDefault != 0); if((configInfo->Dma64BitAddresses & ~SCSI_DMA64_SYSTEM_SUPPORTED) != 0){ DebugPrint((1, "SpAllocateAdapterResources: will request " "64-bit PA's\n")); deviceDescription.Dma64BitAddresses = TRUE; fdoExtension->Dma64BitAddresses = TRUE; } else if(Sp64BitPhysicalAddresses == TRUE) { DebugPrint((1, "SpAllocateAdapterResources: Will remap buffers for adapter %#p\n", fdoExtension)); fdoExtension->RemapBuffers = TRUE; } fdoExtension->DmaAdapterObject = IoGetDmaAdapter(fdoExtension->LowerPdo, &deviceDescription, &numberOfMapRegisters); ASSERT(fdoExtension->DmaAdapterObject); // // Set maximum number of page breaks // if(numberOfMapRegisters > configInfo->NumberOfPhysicalBreaks) { capabilities->MaximumPhysicalPages = configInfo->NumberOfPhysicalBreaks; } else { capabilities->MaximumPhysicalPages = numberOfMapRegisters; } } status = SpAllocateTagBitMap(fdoExtension); if(!NT_SUCCESS(status)) { return status; } // // Initialize power parameters. // SpInitializePowerParams(fdoExtension); // // Initialize tunable per-adapter performance parameters. // SpInitializePerformanceParams(fdoExtension); // // Allocate memory for the noncached extension if it has not already // been allocated. If the adapter supports AutoRequestSense or // needs SRB extensions then an SRB list needs to be allocated. // SrbExtensionBuffer = SpGetSrbExtensionBuffer(fdoExtension); if(((fdoExtension->SrbExtensionSize != 0) || (configInfo->AutoRequestSense)) && (SrbExtensionBuffer == NULL)) { // // Initialize configurable request sense parameters. // SpInitializeRequestSenseParams(fdoExtension); // // Capture the auto request sense flag when the common buffer is // allocated. // fdoExtension->AutoRequestSense = configInfo->AutoRequestSense; fdoExtension->AllocateSrbExtension = TRUE; status = SpGetCommonBuffer(fdoExtension, 0); if(!NT_SUCCESS(status)) { return status; } } status = SpInitializeSrbDataLookasideList(Fdo); if(!NT_SUCCESS(status)) { return status; } // // Initialize the emergency SRB_DATA structures. // fdoExtension->EmergencySrbData = SpAllocateSrbData(fdoExtension, NULL, NULL); if(fdoExtension->EmergencySrbData == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } // // If we are re-initializing a stopped adapter, we must not wipe out any // existing blocked requests. // if (fdoExtension->SrbDataBlockedRequests.Flink == NULL && fdoExtension->SrbDataBlockedRequests.Blink == NULL) { InitializeListHead(&fdoExtension->SrbDataBlockedRequests); } KeInitializeSpinLock(&fdoExtension->EmergencySrbDataSpinLock); // // Initialize the pointer to the enumeration request block. // fdoExtension->PnpEnumRequestPtr = &(fdoExtension->PnpEnumerationRequest); // // Allocate buffers needed for bus scans. // fdoExtension->InquiryBuffer = SpAllocatePool( NonPagedPoolCacheAligned, SP_INQUIRY_BUFFER_SIZE, SCSIPORT_TAG_INQUIRY, Fdo->DriverObject); if(fdoExtension->InquiryBuffer == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } fdoExtension->InquirySenseBuffer = SpAllocatePool( NonPagedPoolCacheAligned, SENSE_BUFFER_SIZE + fdoExtension->AdditionalSenseBytes, SCSIPORT_TAG_INQUIRY, Fdo->DriverObject); if(fdoExtension->InquirySenseBuffer == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } // // Preallocate an irp for inquiries. Since this is only used for scsi // operations we should only need one stack location. // fdoExtension->InquiryIrp = SpAllocateIrp(INQUIRY_STACK_LOCATIONS, FALSE, Fdo->DriverObject); if(fdoExtension->InquiryIrp == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } // // Build an MDL for the inquiry buffer. // fdoExtension->InquiryMdl = SpAllocateMdl(fdoExtension->InquiryBuffer, INQUIRYDATABUFFERSIZE, FALSE, FALSE, NULL, Fdo->DriverObject); if(fdoExtension->InquiryMdl == NULL) { return STATUS_INSUFFICIENT_RESOURCES; } MmBuildMdlForNonPagedPool(fdoExtension->InquiryMdl); // // Initialize the capabilities structure. // capabilities->Length = sizeof(IO_SCSI_CAPABILITIES); capabilities->MaximumTransferLength = configInfo->MaximumTransferLength; if(configInfo->ReceiveEvent) { capabilities->SupportedAsynchronousEvents |= SRBEV_SCSI_ASYNC_NOTIFICATION; } capabilities->TaggedQueuing = fdoExtension->TaggedQueuing; capabilities->AdapterScansDown = configInfo->AdapterScansDown; // // Update the device object alignment if necessary. // if(configInfo->AlignmentMask > Fdo->AlignmentRequirement) { Fdo->AlignmentRequirement = configInfo->AlignmentMask; } capabilities->AlignmentMask = Fdo->AlignmentRequirement; // // Make sure maximum number of pages is set to a reasonable value. // This occurs for miniports with no Dma adapter. // if(capabilities->MaximumPhysicalPages == 0) { capabilities->MaximumPhysicalPages = BYTES_TO_PAGES(capabilities->MaximumTransferLength); // // Honor any limit requested by the miniport // if(configInfo->NumberOfPhysicalBreaks < capabilities->MaximumPhysicalPages) { capabilities->MaximumPhysicalPages = configInfo->NumberOfPhysicalBreaks; } } return status; } NTSTATUS SpCallHwInitialize( IN PDEVICE_OBJECT Fdo ) /*++ Routine Description: This routine will initialize the specified adapter, connect the interrupts, and initialize any necessary resources Arguments: Fdo - a pointer to the functional device object being initialized Return Value: status --*/ { PADAPTER_EXTENSION fdoExtension = Fdo->DeviceExtension; PPORT_CONFIGURATION_INFORMATION configInfo = fdoExtension->PortConfig; KIRQL irql; NTSTATUS status; // // Allocate spin lock for critical sections. // KeInitializeSpinLock(&fdoExtension->SpinLock); // // Initialize DPC routine. // IoInitializeDpcRequest(fdoExtension->CommonExtension.DeviceObject, ScsiPortCompletionDpc); // // Initialize the port timeout counter. // fdoExtension->PortTimeoutCounter = PD_TIMER_STOPPED; // // Initialize the device object timer only if it doesn't already exist // (there's no way to delete a timer without deleting the device so if // we are stopped and restarted then the timer stays around. Reinitializing // it could cause the timer list to go circular) // if(Fdo->Timer == NULL) { IoInitializeTimer(Fdo, ScsiPortTickHandler, NULL); } // // Initialize miniport timer and timer DPC // KeInitializeTimer(&fdoExtension->MiniPortTimer); KeInitializeDpc(&fdoExtension->MiniPortTimerDpc, SpMiniPortTimerDpc, Fdo); KeInitializeSpinLock(&fdoExtension->InterruptSpinLock); if((fdoExtension->HwInterrupt == NULL) || (fdoExtension->HasInterrupt == FALSE)) { // // There is no interrupt so use the dummy routine. // fdoExtension->SynchronizeExecution = SpSynchronizeExecution; fdoExtension->InterruptObject = (PVOID) fdoExtension; DebugPrint((1, "ScsiPortInitialize: Adapter has no interrupt.\n")); } else { KIRQL syncIrql = 0; KIRQL irql2 = 0; ULONG vector = 0, vector2 = 0; KAFFINITY affinity = 0, affinity2 = 0; BOOLEAN interruptSharable = FALSE; BOOLEAN secondInterrupt = FALSE; DebugPrint((1, "ScsiPortInitialize: Interrupt Info for adapter %#p\n", Fdo)); DebugPrint((1, "ScsiPortInitialize: AdapterInterfaceType = %d\n", configInfo->AdapterInterfaceType)); DebugPrint((1, "ScsiPortInitialize: BusInterruptLevel = %d\n", configInfo->BusInterruptLevel)); DebugPrint((1, "ScsiPortInitialize: BusInterruptVector = %d\n", configInfo->BusInterruptVector)); DebugPrint((1, "ScsiPortInitialize: BusInterruptLevel2 = %d\n", configInfo->BusInterruptLevel2)); DebugPrint((1, "ScsiPortInitialize: BusInterruptVector2 = %d\n", configInfo->BusInterruptVector2)); irql = 0; // // Determine if 2 interrupt sync. is needed. // if(fdoExtension->HwInterrupt != NULL && (configInfo->BusInterruptLevel != 0 || configInfo->BusInterruptVector != 0) && (configInfo->BusInterruptLevel2 != 0 || configInfo->BusInterruptVector2 != 0)) { secondInterrupt = TRUE; } // // Save the interrupt level. // fdoExtension->InterruptLevel = configInfo->BusInterruptLevel; // // Set up for a real interrupt. // fdoExtension->SynchronizeExecution = KeSynchronizeExecution; // // Call HAL to get system interrupt parameters for the first // interrupt. // if(fdoExtension->IsMiniportDetected) { #if defined(NO_LEGACY_DRIVERS) DbgPrint("SpCallHwInitialize: fdoExtension->IsMiniportDetected " "not supported for 64 bits!\n"); #else vector = HalGetInterruptVector( configInfo->AdapterInterfaceType, configInfo->SystemIoBusNumber, configInfo->BusInterruptLevel, configInfo->BusInterruptVector, &irql, &affinity); if(secondInterrupt) { // // Spin lock to sync. multiple IRQ's (PCI IDE). // KeInitializeSpinLock(&fdoExtension->MultipleIrqSpinLock); // // Call HAL to get system interrupt parameters for the // second interrupt. // vector2 = HalGetInterruptVector( configInfo->AdapterInterfaceType, configInfo->SystemIoBusNumber, configInfo->BusInterruptLevel2, configInfo->BusInterruptVector2, &irql2, &affinity2); } ASSERT(affinity != 0); if(configInfo->AdapterInterfaceType == MicroChannel || configInfo->InterruptMode == LevelSensitive) { interruptSharable = TRUE; } #endif // NO_LEGACY_DRIVERS } else { ULONG i, j; ASSERT(secondInterrupt == FALSE); for(i = 0; i < fdoExtension->TranslatedResources->Count; i++) { for(j = 0; j < fdoExtension->TranslatedResources->List[i].PartialResourceList.Count; j++) { PCM_PARTIAL_RESOURCE_DESCRIPTOR descriptor = &fdoExtension->TranslatedResources->List[i].PartialResourceList.PartialDescriptors[j]; if(descriptor->Type == CmResourceTypeInterrupt) { vector = descriptor->u.Interrupt.Vector; affinity = descriptor->u.Interrupt.Affinity; irql = (KIRQL) descriptor->u.Interrupt.Level; if(descriptor->ShareDisposition == CmResourceShareShared) { interruptSharable = TRUE; } break; } } } } syncIrql = (irql > irql2) ? irql : irql2; DebugPrint((1, "SpInitializeAdapter: vector = %d\n", vector)); DebugPrint((1, "SpInitializeAdapter: irql = %d\n", irql)); DebugPrint((1, "SpInitializeAdapter: affinity = %#08lx\n", affinity)); status = IoConnectInterrupt( &fdoExtension->InterruptObject, (PKSERVICE_ROUTINE) ScsiPortInterrupt, Fdo, (secondInterrupt ? (&fdoExtension->MultipleIrqSpinLock) : NULL), vector, irql, syncIrql, configInfo->InterruptMode, interruptSharable, affinity, FALSE); if(!NT_SUCCESS(status)) { DebugPrint((1, "SpInitializeAdapter: Can't connect " "interrupt %d\n", vector)); fdoExtension->InterruptObject = NULL; return status; } if(secondInterrupt) { DebugPrint((1, "SpInitializeAdapter: SCSI adapter second IRQ is %d\n", configInfo->BusInterruptLevel2)); DebugPrint((1, "SpInitializeAdapter: vector = %d\n", vector)); DebugPrint((1, "SpInitializeAdapter: irql = %d\n", irql)); DebugPrint((1, "SpInitializeAdapter: affinity = %#08lx\n", affinity)); status = IoConnectInterrupt( &fdoExtension->InterruptObject2, (PKSERVICE_ROUTINE) ScsiPortInterrupt, Fdo, &fdoExtension->MultipleIrqSpinLock, vector2, irql2, syncIrql, configInfo->InterruptMode2, interruptSharable, affinity2, FALSE); if(!NT_SUCCESS(status)) { // // If we needed both interrupts, we will continue but not // claim any of the resources for the second one // DebugPrint((1, "SpInitializeAdapter: Can't connect " "second interrupt %d\n", vector2)); fdoExtension->InterruptObject2 = NULL; configInfo->BusInterruptVector2 = 0; configInfo->BusInterruptLevel2 = 0; } } } // // Record first access range if it exists. // if(configInfo->NumberOfAccessRanges != 0) { fdoExtension->IoAddress = ((*(configInfo->AccessRanges))[0]).RangeStart.LowPart; DebugPrint((1, "SpInitializeAdapter: IO Base address %x\n", fdoExtension->IoAddress)); } // // Indicate that a disconnect allowed command running. This bit is // normally on. // fdoExtension->Flags |= PD_DISCONNECT_RUNNING; // // Initialize the request count to -1. This count is biased by -1 so // that a value of zero indicates the adapter must be allocated // fdoExtension->ActiveRequestCount = -1; // // Indiciate if a scatter/gather list needs to be built. // if(fdoExtension->DmaAdapterObject != NULL && configInfo->Master && configInfo->NeedPhysicalAddresses) { fdoExtension->MasterWithAdapter = TRUE; } else { fdoExtension->MasterWithAdapter = FALSE; } // // Call the hardware dependant driver to do it's initialization. // This routine must be called at DISPATCH_LEVEL. // KeRaiseIrql(DISPATCH_LEVEL, &irql); if(!fdoExtension->SynchronizeExecution(fdoExtension->InterruptObject, fdoExtension->HwInitialize, fdoExtension->HwDeviceExtension)) { DebugPrint((1, "SpInitializeAdapter: initialization failed\n")); KeLowerIrql(irql); return STATUS_ADAPTER_HARDWARE_ERROR; } // // Check for miniport work requests. Note this is an unsynchronized // test on the bit that can be set by the interrupt routine; However, // the worst that can happen is that the completion DPC checks for work // twice. // if(fdoExtension->InterruptData.InterruptFlags & PD_NOTIFICATION_REQUIRED) { // // Call the completion DPC directly. It must be called at dispatch // level. // SpRequestCompletionDpc(Fdo); } KeLowerIrql(irql); return STATUS_SUCCESS; } HANDLE SpOpenDeviceKey( IN PUNICODE_STRING RegistryPath, IN ULONG DeviceNumber ) /*++ Routine Description: This routine will open the services keys for the miniport and put handles to them into the configuration context structure. Arguments: RegistryPath - a pointer to the service key name for this miniport DeviceNumber - which device too search for under the service key. -1 indicates that the default device key should be opened. Return Value: status --*/ { OBJECT_ATTRIBUTES objectAttributes; WCHAR buffer[64]; UNICODE_STRING unicodeString; HANDLE serviceKey; HANDLE deviceKey = NULL; NTSTATUS status; PAGED_CODE(); serviceKey = SpOpenParametersKey(RegistryPath); if(serviceKey != NULL) { // // Check for a Device Node. The device node applies to every device // if(DeviceNumber == (ULONG) -1) { swprintf(buffer, L"Device"); } else { swprintf(buffer, L"Device%d", DeviceNumber); } RtlInitUnicodeString(&unicodeString, buffer); InitializeObjectAttributes(&objectAttributes, &unicodeString, OBJ_CASE_INSENSITIVE, serviceKey, (PSECURITY_DESCRIPTOR) NULL); // // It doesn't matter if this call fails or not. If it fails, then there // is no default device node. If it works then the handle will be set. // ZwOpenKey(&deviceKey, KEY_READ, &objectAttributes); ZwClose(serviceKey); } return deviceKey; } HANDLE SpOpenParametersKey( IN PUNICODE_STRING RegistryPath ) /*++ Routine Description: This routine will open the services keys for the miniport and put handles to them into the configuration context structure. Arguments: RegistryPath - a pointer to the service key name for this miniport Return Value: status --*/ { OBJECT_ATTRIBUTES objectAttributes; UNICODE_STRING unicodeString; HANDLE serviceKey; NTSTATUS status; PAGED_CODE(); // // Open the service node // InitializeObjectAttributes(&objectAttributes, RegistryPath, OBJ_CASE_INSENSITIVE, NULL, (PSECURITY_DESCRIPTOR) NULL); status = ZwOpenKey(&serviceKey, KEY_READ, &objectAttributes); if(!NT_SUCCESS(status)) { DebugPrint((1, "SpOpenParameterKey: cannot open service key node for " "driver. Name: %wZ Status: %08lx\n", RegistryPath, status)); } // // Try to open the parameters key. If it exists then replace the service // key with the new key. This allows the device nodes to be placed // under DriverName\Parameters\Device or DriverName\Device // if(serviceKey != NULL) { HANDLE parametersKey; // // Check for a device node. The device node applies to every device // RtlInitUnicodeString(&unicodeString, L"Parameters"); InitializeObjectAttributes(&objectAttributes, &unicodeString, OBJ_CASE_INSENSITIVE, serviceKey, (PSECURITY_DESCRIPTOR) NULL); // // Attempt to open the parameters key // status = ZwOpenKey(¶metersKey, KEY_READ, &objectAttributes); if(NT_SUCCESS(status)) { // // There is a Parameters key. Use that instead of the service // node key. Close the service node and set the new value // ZwClose(serviceKey); serviceKey = parametersKey; } } return serviceKey; } ULONG SpQueryPnpInterfaceFlags( IN PSCSIPORT_DRIVER_EXTENSION DriverExtension, IN INTERFACE_TYPE InterfaceType ) /*++ Routine Description: This routine will look up the interface type in the PnpInterface value in the service's parameters key. If the interface is found in this binary value the routine will return TRUE. If the interface type is not there or if any errors occur reading the data, this routine will return FALSE. Arguments: ConfigurationContext - a pointer to the configuration context for this miniport InterfaceType - the interface type we are searching for Return Value: TRUE if the interface type is in the safe list FALSE if the interface type is not in the safe list or if the value cannot be found --*/ { ULONG i; PAGED_CODE(); for(i = 0; i < DriverExtension->PnpInterfaceCount; i++) { if(DriverExtension->PnpInterface[i].InterfaceType == InterfaceType) { DebugPrint((2, "SpQueryPnpInterfaceFlags: interface %d has flags " "%#08lx\n", InterfaceType, DriverExtension->PnpInterface[i].Flags)); return DriverExtension->PnpInterface[i].Flags; } } DebugPrint((2, "SpQueryPnpInterfaceFlags: No interface flags for %d\n", InterfaceType)); return SP_PNP_NOT_SAFE; } ULONG ScsiPortGetBusData( IN PVOID DeviceExtension, IN ULONG BusDataType, IN ULONG SystemIoBusNumber, IN ULONG SlotNumber, IN PVOID Buffer, IN ULONG Length ) /*++ Routine Description: The function returns the bus data for an adapter slot or CMOS address. Arguments: BusDataType - Supplies the type of bus. BusNumber - Indicates which bus. Buffer - Supplies the space to store the data. Length - Supplies a count in bytes of the maximum amount to return. Return Value: Returns the amount of data stored into the buffer. --*/ { PADAPTER_EXTENSION fdoExtension = GET_FDO_EXTENSION(DeviceExtension); PDEVICE_OBJECT lowerDevice = NULL; CM_EISA_SLOT_INFORMATION slotInformation; // // If this is in a virtualized slot then setup the lower device object // pointer to go to the PDO // if(fdoExtension->IsInVirtualSlot) { // // Make sure the bus and slot number are correct // if(SlotNumber != fdoExtension->VirtualSlotNumber.u.AsULONG) { ASSERT(BusDataType == PCIConfiguration); return 2; } lowerDevice = fdoExtension->CommonExtension.LowerDeviceObject; } // // If the length is nonzero, retrieve the requested data. // if (Length != 0) { return SpGetBusData(fdoExtension, lowerDevice, BusDataType, SystemIoBusNumber, SlotNumber, Buffer, Length); } // // Free any previously allocated data. // if (fdoExtension->MapRegisterBase != NULL) { ExFreePool(fdoExtension->MapRegisterBase); fdoExtension->MapRegisterBase = NULL; } if (BusDataType == EisaConfiguration) { // // Determine the length to allocate based on the number of functions // for the slot. // Length = SpGetBusData( fdoExtension, lowerDevice, BusDataType, SystemIoBusNumber, SlotNumber, &slotInformation, sizeof(CM_EISA_SLOT_INFORMATION)); if (Length < sizeof(CM_EISA_SLOT_INFORMATION)) { // // The data is messed up since this should never occur // return 0; } // // Calculate the required length based on the number of functions. // Length = sizeof(CM_EISA_SLOT_INFORMATION) + (sizeof(CM_EISA_FUNCTION_INFORMATION) * slotInformation.NumberFunctions); } else if (BusDataType == PCIConfiguration) { // // Read only the header. // Length = PCI_COMMON_HDR_LENGTH; } else { Length = PAGE_SIZE; } fdoExtension->MapRegisterBase = SpAllocatePool(NonPagedPool, Length, SCSIPORT_TAG_BUS_DATA, fdoExtension->DeviceObject->DriverObject); ASSERT_FDO(fdoExtension->DeviceObject); if (fdoExtension->MapRegisterBase == NULL) { return 0; } // // Return the pointer to the miniport driver. // *((PVOID *)Buffer) = fdoExtension->MapRegisterBase; return SpGetBusData(fdoExtension, lowerDevice, BusDataType, SystemIoBusNumber, SlotNumber, fdoExtension->MapRegisterBase, Length); } ULONG SpGetBusData( IN PADAPTER_EXTENSION Adapter, IN PDEVICE_OBJECT Pdo OPTIONAL, IN BUS_DATA_TYPE BusDataType, IN ULONG BusNumber, IN ULONG SlotNumber, IN PVOID Buffer, IN ULONG Length ) /*++ Routine Description: This routine will retrieve bus data from the specified slot and bus number or from the supplied physical device object. If bus and slot number are supplied it will tranlate into a call to HalGetBusData. If a PDO is supplied instead this will issue an IRP_MN_READ_CONFIG to the lower level driver. This routine allocates memory and waits for irp completion - it should not be called above passive level. Arguments: Pdo - if this is non-NULL then it should be a pointer to the top of the device object stack for the PDO representing this adapter BusNumber - if PDO is NULL then this should be the bus number the adapter sits on - zero otherwise SlotNumber - if PDO is NULL then this is the number of the slot the adapter is installed into - zero otherwise Buffer - location to store the returned data Length - size of above Return Value: status --*/ { // // if the user didn't specify a PDO to query then just throw this request // to the HAL // if(Pdo == NULL) { #if defined(NO_LEGACY_DRIVERS) DebugPrint((1,"SpGetBusData: NULL PDO, not supported for 64-bits.\n")); return STATUS_INVALID_PARAMETER; #else return HalGetBusData(BusDataType, BusNumber, SlotNumber, Buffer, Length); #endif // NO_LEGACY_DRIVERS } else { // // ThePCI bus interface GetBusData routine only accepts read requests // from PCIConfiguration space. We do not support anything else. // if (BusDataType != PCIConfiguration) { ASSERT(FALSE && "Invalid PCI_WHICHSPACE_ parameter"); return 0; } ASSERT(Adapter->LowerBusInterfaceStandardRetrieved == TRUE); return Adapter->LowerBusInterfaceStandard.GetBusData( Adapter->LowerBusInterfaceStandard.Context, PCI_WHICHSPACE_CONFIG, Buffer, 0L, Length); } } NTSTATUS SpInitializeSrbDataLookasideList( IN PDEVICE_OBJECT AdapterObject ) { KIRQL oldIrql; ULONG adapterTag; PDEVICE_OBJECT *newAdapterList; PDEVICE_OBJECT *oldAdapterList = NULL; NTSTATUS status = STATUS_SUCCESS; #ifdef ALLOC_PRAGMA PVOID sectionHandle = MmLockPagableCodeSection( SpInitializeSrbDataLookasideList); InterlockedIncrement(&SpPAGELOCKLockCount); #endif // // Add our device object to the global adapter list. This will require // increasing the size of the list. // KeAcquireSpinLock(&ScsiGlobalAdapterListSpinLock, &oldIrql); try { adapterTag = ScsiGlobalAdapterListElements; newAdapterList = SpAllocatePool( NonPagedPool, (sizeof(PDEVICE_OBJECT) * (adapterTag + 1)), SCSIPORT_TAG_GLOBAL, AdapterObject->DriverObject); if(newAdapterList == NULL) { status = STATUS_INSUFFICIENT_RESOURCES; leave; } ScsiGlobalAdapterListElements += 1; if(ScsiGlobalAdapterList != NULL) { RtlCopyMemory(newAdapterList, ScsiGlobalAdapterList, (sizeof(PDEVICE_OBJECT) * adapterTag)); } newAdapterList[adapterTag] = AdapterObject; oldAdapterList = ScsiGlobalAdapterList; ScsiGlobalAdapterList = newAdapterList; if(oldAdapterList != NULL) { ExFreePool(oldAdapterList); } } finally { KeReleaseSpinLock(&ScsiGlobalAdapterListSpinLock, oldIrql); } #ifdef ALLOC_PRAGMA MmUnlockPagableImageSection(sectionHandle); InterlockedDecrement(&SpPAGELOCKLockCount); #endif if(NT_SUCCESS(status)) { PADAPTER_EXTENSION adapterExtension = AdapterObject->DeviceExtension; // // Create the lookaside list for SRB_DATA blobs. Make sure there's // enough space for a small scatter gather list allocated in the // structure as well. // ExInitializeNPagedLookasideList( &adapterExtension->SrbDataLookasideList, (PALLOCATE_FUNCTION) SpAllocateSrbDataBackend, (PFREE_FUNCTION) SpFreeSrbDataBackend, 0L, sizeof(SRB_DATA), adapterTag, SRB_LIST_DEPTH); adapterExtension->SrbDataListInitialized = TRUE; } return status; } #define SP_KEY_VALUE_BUFFER_SIZE 255 NTSTATUS SpAllocateDriverExtension( IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath, OUT PSCSIPORT_DRIVER_EXTENSION *DriverExtension ) /*++ Routine Description: This routine will determine the proper size for the scsiport driver extension (based on the number of PnpInterface flags recorded in the services key) --*/ { PSCSIPORT_DRIVER_EXTENSION driverExtension = NULL; OBJECT_ATTRIBUTES objectAttributes; UNICODE_STRING unicodeString; HANDLE serviceKey = NULL; HANDLE parametersKey = NULL; HANDLE interfaceKey = NULL; STORAGE_BUS_TYPE busType; ULONG passes; NTSTATUS status; PAGED_CODE(); *DriverExtension = NULL; DebugPrint((1, "SpAllocateDriverExtension: Allocating extension for " "driver %wZ\n", &DriverObject->DriverName)); try { // // Try to open the services key first // InitializeObjectAttributes( &objectAttributes, RegistryPath, OBJ_CASE_INSENSITIVE, NULL, NULL); status = ZwOpenKey(&serviceKey, KEY_READ, &objectAttributes); if(!NT_SUCCESS(status)) { DebugPrint((1, "SpAllocateDriverExtension: Unable to open registry " "key %wZ [%#08lx]\n", RegistryPath, status)); leave; } // // Open the parameters key // RtlInitUnicodeString(&unicodeString, L"Parameters"); InitializeObjectAttributes(&objectAttributes, &unicodeString, OBJ_CASE_INSENSITIVE, serviceKey, NULL); status = ZwOpenKey(¶metersKey, KEY_READ, &objectAttributes); if(!NT_SUCCESS(status)) { DebugPrint((1, "SpAllocateDriverExtension: Unable to open " "parameters key of %wZ [%#08lx]\n", RegistryPath, status)); leave; } // // Try to determine the bus type for this driver. // RtlInitUnicodeString(&(unicodeString), L"BusType"); { ULONG tmp; status = SpReadNumericValue(parametersKey, NULL, &unicodeString, &tmp); busType = (STORAGE_BUS_TYPE) tmp; } if(NT_SUCCESS(status)) { switch(busType) { case BusTypeScsi: case BusTypeAtapi: case BusTypeAta: case BusTypeSsa: case BusTypeFibre: case BusTypeRAID: { DebugPrint((1, "SpAllocateDriverExtension: Bus type set to %d\n", busType)); break; } default: { busType = BusTypeScsi; break; } } } else { busType = BusTypeScsi; } // // got that one - now open the pnpinterface key. // RtlInitUnicodeString(&unicodeString, L"PnpInterface"); InitializeObjectAttributes(&objectAttributes, &unicodeString, OBJ_CASE_INSENSITIVE, parametersKey, NULL); status = ZwOpenKey(&interfaceKey, KEY_READ, &objectAttributes); if(!NT_SUCCESS(status)) { DebugPrint((1, "SpAllocateDriverExtension: Unable to open " "PnpInterface key of %wZ [%#08lx]\n", &RegistryPath, status)); leave; } // // Now that we have the pnpinterface key open we enumerate the entries in // two steps. The first is to count up the number of entries. We then // allocate an appropriately sized driver object extension, zero it out, // and copy the values into the PnpInterface section at the end. // for(passes = 0; passes < 2; passes++) { ULONG count; status = STATUS_SUCCESS; for(count = 0; TRUE; count++) { UCHAR buffer[SP_KEY_VALUE_BUFFER_SIZE]; PKEY_VALUE_FULL_INFORMATION keyValue = (PKEY_VALUE_FULL_INFORMATION) buffer; ULONG resultLength; ASSERTMSG("ScsiPort configuration error - possibly too many " "count entries: ", count != MaximumInterfaceType); RtlZeroMemory(buffer, sizeof(UCHAR) * SP_KEY_VALUE_BUFFER_SIZE); status = ZwEnumerateValueKey( interfaceKey, count, (passes == 0) ? KeyValueBasicInformation : KeyValueFullInformation, keyValue, sizeof(buffer), &resultLength); if(status == STATUS_NO_MORE_ENTRIES) { status = STATUS_SUCCESS; break; } else if(!NT_SUCCESS(status)) { DebugPrint((1, "SpAllocateDriverExtension: Fatal error %#08lx " "enumerating PnpInterface key under %wZ.", status, RegistryPath)); leave; } if(passes == 1) { PSCSIPORT_INTERFACE_TYPE_DATA interface = &(driverExtension->PnpInterface[count]); ULONG t; ASSERTMSG("ScsiPort internal error - too many pnpinterface " "entries on second pass: ", count <= driverExtension->PnpInterfaceCount); // // First turn the name of the entry into a numerical value // so we can match it to an interface type. // RtlInitUnicodeString(&unicodeString, keyValue->Name); if((keyValue->Type != REG_DWORD) && (keyValue->Type != REG_NONE)) { DbgPrint("SpAllocateDriverExtension: Fatal error parsing " "PnpInterface under %wZ - entry %wZ is not " "a REG_DWORD or REG_NONE entry (%d instead)\n", status, RegistryPath, &unicodeString); status = STATUS_DEVICE_CONFIGURATION_ERROR; leave; } status = RtlUnicodeStringToInteger( &unicodeString, 0L, &t); if(!NT_SUCCESS(status)) { DbgPrint("SpAllocateDriverExtension: Fatal error %#08lx " "parsing PnpInterface under %wZ - entry %wZ is " "not a valid interface type name\n", status, RegistryPath, &unicodeString); leave; } if(t > MaximumInterfaceType) { DbgPrint("SpAllocateDriverExtension: Fatal error " "parsing PnpInterface under %wZ - entry %wZ is " "> MaximumInterfaceType (%d)\n", status, RegistryPath, &unicodeString); interface->InterfaceType = InterfaceTypeUndefined; status = STATUS_DEVICE_CONFIGURATION_ERROR; leave; } interface->InterfaceType = (INTERFACE_TYPE) t; if(keyValue->Type == REG_NONE) { interface->Flags = 0L; } else { interface->Flags = *(((PUCHAR) keyValue) + keyValue->DataOffset); if(interface->Flags & SP_PNP_IS_SAFE) { ASSERT(driverExtension != NULL); driverExtension->SafeInterfaceCount++; } switch(interface->InterfaceType) { case PCIBus: { SET_FLAG(interface->Flags, SP_PNP_NEEDS_LOCATION); SET_FLAG(interface->Flags, SP_PNP_INTERRUPT_REQUIRED); CLEAR_FLAG(interface->Flags, SP_PNP_NON_ENUMERABLE); break; } case Internal: case PNPISABus: case PNPBus: case PCMCIABus: { // // These buses don't ever do detection. // CLEAR_FLAG(interface->Flags, SP_PNP_NON_ENUMERABLE); break; } default: { // // The other bus types will always do detection // if given the chance. // if(!TEST_FLAG(interface->Flags, SP_PNP_NO_LEGACY_DETECTION)) { SET_FLAG(interface->Flags, SP_PNP_NON_ENUMERABLE); } break; } } } DebugPrint((1, "SpAllocateDriverExtension: Interface %d has " "flags %#08lx\n", interface->InterfaceType, interface->Flags)); } } if(passes == 0) { ULONG extensionSize; // // We know how much extra space we need so go ahead and allocate // the extension. // DebugPrint((2, "SpAllocateDriverExtension: Driver has %d interface " "entries\n", count)); extensionSize = sizeof(SCSIPORT_DRIVER_EXTENSION) + (sizeof(SCSIPORT_INTERFACE_TYPE_DATA) * count); DebugPrint((2, "SpAllocateDriverExtension: Driver extension will " "be %d bytes\n", extensionSize)); status = IoAllocateDriverObjectExtension(DriverObject, ScsiPortInitialize, extensionSize, &driverExtension); if(!NT_SUCCESS(status)) { DebugPrint((1, "SpAllocateDriverExtension: Fatal error %#08lx " "allocating driver extension\n", status)); leave; } RtlZeroMemory(driverExtension, extensionSize); driverExtension->PnpInterfaceCount = count; } } ASSERTMSG("ScsiPortAllocateDriverExtension internal error: left first " "section with non-success status: ", NT_SUCCESS(status)); } finally { // // If the driver extension has not been allocated then go ahead and // do that here. // if(driverExtension == NULL) { DebugPrint((1, "SpAllocateDriverExtension: Driver has 0 interface " "entries\n")); DebugPrint((2, "SpAllocateDriverExtension: Driver extension will " "be %d bytes\n", sizeof(SCSIPORT_DRIVER_EXTENSION))); status = IoAllocateDriverObjectExtension(DriverObject, ScsiPortInitialize, sizeof(SCSIPORT_DRIVER_EXTENSION), &driverExtension); if(!NT_SUCCESS(status)) { DebugPrint((1, "SpAllocateDriverExtension: Fatal error %#08lx " "allocating driver extension\n", status)); goto Finally_Cleanup; } RtlZeroMemory(driverExtension, sizeof(SCSIPORT_DRIVER_EXTENSION)); } else { driverExtension->BusType = busType; } status = STATUS_SUCCESS; Finally_Cleanup:; } if (status != STATUS_SUCCESS) goto Cleanup; // // initialize the remaining fields in the driver object extension. // driverExtension->ReserveAllocFailureLogEntry = SpAllocateErrorLogEntry(DriverObject); driverExtension->UnusedPage = NULL; driverExtension->UnusedPageMdl = NULL; driverExtension->InvalidPage = NULL; driverExtension->DriverObject = DriverObject; driverExtension->RegistryPath = *RegistryPath; driverExtension->RegistryPath.MaximumLength += sizeof(WCHAR); driverExtension->RegistryPath.Buffer = SpAllocatePool(PagedPool, driverExtension->RegistryPath.MaximumLength, SCSIPORT_TAG_REGISTRY, DriverObject); if(driverExtension->RegistryPath.Buffer == NULL) { DebugPrint((1, "SpAllocateDriverExtension: Fatal error " "allocating copy of registry path\n")); status = STATUS_INSUFFICIENT_RESOURCES; goto Cleanup; } RtlCopyUnicodeString(&(driverExtension->RegistryPath), RegistryPath); // // Now get the values of the LegacyAdapterDetection flags. // // // Set it to a good default value in case we error out getting the flags // if(ScsiPortLegacyAdapterDetection) { // // Global flag breaks scissors // driverExtension->LegacyAdapterDetection = TRUE; } else { if(parametersKey != NULL) { UNICODE_STRING valueName; UCHAR buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(ULONG)]; PKEY_VALUE_PARTIAL_INFORMATION keyValueInformation = (PKEY_VALUE_PARTIAL_INFORMATION) buffer; ULONG length; RtlInitUnicodeString(&valueName, L"LegacyAdapterDetection"); status = ZwQueryValueKey(parametersKey, &valueName, KeyValuePartialInformation, keyValueInformation, sizeof(buffer), &length); if(NT_SUCCESS(status) && (length >= sizeof(KEY_VALUE_PARTIAL_INFORMATION)) && (keyValueInformation->Type == REG_DWORD)) { ULONG data = *((PULONG) keyValueInformation->Data); driverExtension->LegacyAdapterDetection = (data == 1); // // Rewrite a zero in to the value. // data = 0; status = ZwSetValueKey(parametersKey, &valueName, keyValueInformation->TitleIndex, REG_DWORD, &data, sizeof(data)); if(!NT_SUCCESS(status)) { DebugPrint((1, "SpAllocateDriverExtension: Error %#08lx " "setting LegacyAdapterDetection value to " "zero\n", status)); status = STATUS_SUCCESS; } } else { driverExtension->LegacyAdapterDetection = FALSE; } } if(driverExtension->LegacyAdapterDetection == FALSE) { UNICODE_STRING unicodeKeyName; UNICODE_STRING unicodeClassGuid; HANDLE controlClassKey = NULL; HANDLE scsiAdapterKey = NULL; RtlInitUnicodeString(&unicodeClassGuid, NULL); // // Miniport doesn't want to do detection. Check to see if the // global port driver flag has been switched on. // RtlInitUnicodeString( &unicodeString, L"\\REGISTRY\\MACHINE\\SYSTEM\\CurrentControlSet\\Control\\Class"); RtlZeroMemory(&objectAttributes, sizeof(OBJECT_ATTRIBUTES)); InitializeObjectAttributes( &objectAttributes, &unicodeString, OBJ_CASE_INSENSITIVE, NULL, NULL); try { status = ZwOpenKey(&controlClassKey, KEY_READ, &objectAttributes); if(!NT_SUCCESS(status)) { DebugPrint((1, "SpAllocateDriverExtension: Error %#08lx " "opening key %wZ\n", status, &unicodeString)); leave; } // // Now open up the GUID key for our device. // status = RtlStringFromGUID(&GUID_DEVCLASS_SCSIADAPTER, &unicodeClassGuid); if(!NT_SUCCESS(status)) { DebugPrint((1, "SpAllocateDriverExtension: Error %#08lx " "converting GUID to unicode string\n", status)); leave; } RtlZeroMemory(&objectAttributes, sizeof(OBJECT_ATTRIBUTES)); InitializeObjectAttributes(&objectAttributes, &unicodeClassGuid, OBJ_CASE_INSENSITIVE, controlClassKey, NULL); status = ZwOpenKey(&scsiAdapterKey, KEY_READ, &objectAttributes); if(!NT_SUCCESS(status)) { DebugPrint((1, "SpAllocateDriverExtension: Error %#08lx " "opening class key %wZ\n", status, &unicodeClassGuid)); leave; } else { UCHAR buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(ULONG)]; PKEY_VALUE_PARTIAL_INFORMATION keyInfo = (PKEY_VALUE_PARTIAL_INFORMATION) buffer; ULONG infoLength; RtlInitUnicodeString(&unicodeString, L"LegacyAdapterDetection"); status = ZwQueryValueKey(scsiAdapterKey, &unicodeString, KeyValuePartialInformation, keyInfo, sizeof(buffer), &infoLength); if(!NT_SUCCESS(status)) { DebugPrint((2, "SpAllocateDriverExtension: Error " "%#08lx reading key %wZ\n", status, &unicodeString)); status = STATUS_SUCCESS; leave; } if(*((PULONG) keyInfo->Data) == 0) { driverExtension->LegacyAdapterDetection = FALSE; } else { driverExtension->LegacyAdapterDetection = TRUE; } } } finally { if(controlClassKey != NULL) { ZwClose(controlClassKey); } if(scsiAdapterKey != NULL) { ZwClose(scsiAdapterKey); } RtlFreeUnicodeString(&unicodeClassGuid); } } status = STATUS_SUCCESS; } Cleanup: // // If we got out of everything above and didn't allocate a driver // extension then if(serviceKey) { ZwClose(serviceKey); } if(parametersKey) { ZwClose(parametersKey); } if(interfaceKey) { ZwClose(interfaceKey); } if(NT_SUCCESS(status)) { *DriverExtension = driverExtension; } return status; } extern ULONG ScsiPortVerifierInitialized; NTSTATUS DllInitialize( IN PUNICODE_STRING RegistryPath ) { HANDLE VerifierKey; UNICODE_STRING Name; OBJECT_ATTRIBUTES ObjectAttributes; NTSTATUS Status; ULONG ResultLength; UCHAR buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(ULONG)]; PKEY_VALUE_PARTIAL_INFORMATION ValueInfo = (PKEY_VALUE_PARTIAL_INFORMATION)buffer; // // Check the verification level first; someone may have poked the value // from the debugger to prevent us from doing any verifier initialization. // if (SpVrfyLevel == SP_VRFY_NONE) { return STATUS_SUCCESS; } // // Read the global verification level from the registry. If the value is // not present or if the value indicates 'no verification', we don't want // to do any verifier initialization at all. // RtlInitUnicodeString(&Name, SCSIPORT_CONTROL_KEY SCSIPORT_VERIFIER_KEY); InitializeObjectAttributes(&ObjectAttributes, &Name, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = ZwOpenKey(&VerifierKey, KEY_READ, &ObjectAttributes); if (NT_SUCCESS(Status)) { RtlInitUnicodeString(&Name, L"VerifyLevel"); Status = ZwQueryValueKey(VerifierKey, &Name, KeyValuePartialInformation, ValueInfo, sizeof(buffer), &ResultLength); if (NT_SUCCESS(Status)) { if (ValueInfo->Type == REG_DWORD) { if (ResultLength >= sizeof(ULONG)) { SpVrfyLevel |= ((PULONG)(ValueInfo->Data))[0]; if (SpVrfyLevel != SP_VRFY_NONE && ScsiPortVerifierInitialized == 0) { // // Ok, we found a verifier level and it did not tell us // not to verify. Go ahead and initialize scsiport's // verifier. // if (SpVerifierInitialization()) { ScsiPortVerifierInitialized = 1; } } } } } ZwClose(VerifierKey); } #if defined(NEWQUEUE) // // Read the global queue parameters. These values override the default // settings for the number of requests we handle per zone and the number // of consecutive requests we handle to a particular sector. // RtlInitUnicodeString(&Name, SCSIPORT_CONTROL_KEY L"QueueParams"); InitializeObjectAttributes(&ObjectAttributes, &Name, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = ZwOpenKey(&VerifierKey, KEY_READ, &ObjectAttributes); if (NT_SUCCESS(Status)) { RtlInitUnicodeString(&Name, L"PerZoneLimit"); Status = ZwQueryValueKey(VerifierKey, &Name, KeyValuePartialInformation, ValueInfo, sizeof(buffer), &ResultLength); if (NT_SUCCESS(Status)) { if (ValueInfo->Type == REG_DWORD) { if (ResultLength >= sizeof(ULONG)) { SpPerZoneLimit = ((PULONG)(ValueInfo->Data))[0]; } } } RtlInitUnicodeString(&Name, L"PerBlockLimit"); Status = ZwQueryValueKey(VerifierKey, &Name, KeyValuePartialInformation, ValueInfo, sizeof(buffer), &ResultLength); if (NT_SUCCESS(Status)) { if (ValueInfo->Type == REG_DWORD) { if (ResultLength >= sizeof(ULONG)) { SpPerBlockLimit = ((PULONG)(ValueInfo->Data))[0]; } } } ZwClose(VerifierKey); } DebugPrint((1, "ScsiPort: SpPerZoneLimit:%x SpPerBlockLimit:%x\n", SpPerZoneLimit, SpPerBlockLimit)); #endif // NEWQUEUE return STATUS_SUCCESS; } VOID SpInitializePowerParams( IN PADAPTER_EXTENSION AdapterExtension ) /*++ Routine Description: This routine initializes per-adapter power parameters. Arguments: Adapter - Points to an adapter extension. Return Value: Notes: --*/ { NTSTATUS status; ULONG needsShutdown; PAGED_CODE(); // // If this is not a pnp device, don't attempt to read registry info. // if (AdapterExtension->IsPnp == FALSE) { AdapterExtension->NeedsShutdown = FALSE; return; } status = SpReadNumericInstanceValue(AdapterExtension->LowerPdo, L"NeedsSystemShutdownNotification", &needsShutdown); if (!NT_SUCCESS(status)) { AdapterExtension->NeedsShutdown = 0; } else { AdapterExtension->NeedsShutdown = (needsShutdown == 0) ? FALSE : TRUE; } } VOID SpInitializePerformanceParams( IN PADAPTER_EXTENSION AdapterExtension ) /*++ Routine Description: This routine initializes per-adapter tunable performance parameters. Arguments: Adapter - Points to an adapter extension. Return Value: Notes: --*/ { NTSTATUS status; ULONG remainInReducedMaxQueueState; PAGED_CODE(); // // If this isn't a pnp device, don't attempt to get parameters. // if (AdapterExtension->IsPnp == FALSE) { AdapterExtension->RemainInReducedMaxQueueState = 0xffffffff; return; } status = SpReadNumericInstanceValue(AdapterExtension->LowerPdo, L"RemainInReducedMaxQueueState", &remainInReducedMaxQueueState); if (!NT_SUCCESS(status)) { AdapterExtension->RemainInReducedMaxQueueState = 0xffffffff; } else { AdapterExtension->RemainInReducedMaxQueueState = remainInReducedMaxQueueState; } } VOID SpInitializeRequestSenseParams( IN PADAPTER_EXTENSION AdapterExtension ) /*++ Routine Description: This routine returns the number of additonal sense bytes supported by the specified adapter. By default, an adapter will support zero additional sense bytes. The default is overridden by specifying an alternative via the registry. Arguments: Adapter - Points to an adapter extension. Return Value: Notes: --*/ { NTSTATUS status; ULONG TotalSenseDataBytes; PAGED_CODE(); // // If this isn't a pnp device, don't attempt to determine // if it supports additional sense data. // if (AdapterExtension->IsPnp == FALSE) { AdapterExtension->AdditionalSenseBytes = 0; return; } status = SpReadNumericInstanceValue(AdapterExtension->LowerPdo, L"TotalSenseDataBytes", &TotalSenseDataBytes); if (!NT_SUCCESS(status)) { // // Value is absent. No additional sense bytes. // AdapterExtension->AdditionalSenseBytes = 0; } else { // // The acceptable range of values is [18..255]. // if (TotalSenseDataBytes <= SENSE_BUFFER_SIZE) { AdapterExtension->AdditionalSenseBytes = 0; } else if (TotalSenseDataBytes >= MAX_SENSE_BUFFER_SIZE) { AdapterExtension->AdditionalSenseBytes = MAX_ADDITIONAL_SENSE_BYTES; } else { // // The value in the registry is valid. The number of additional // sense bytes is TotalSize - StandardSize. // AdapterExtension->AdditionalSenseBytes = (UCHAR)(TotalSenseDataBytes - SENSE_BUFFER_SIZE); } } }