/* * ReactOS kernel * Copyright (C) 2001, 2002 ReactOS Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ /* * COPYRIGHT: See COPYING in the top level directory * PROJECT: ReactOS Storage Stack * FILE: drivers/storage/scsiport/scsiport.c * PURPOSE: SCSI port driver * PROGRAMMER: Eric Kohl * Aleksey Bragin (aleksey reactos org) */ /* INCLUDES *****************************************************************/ #include "precomp.h" #include #include #include #include #include #include #define NDEBUG #include #include "scsiport_int.h" ULONG InternalDebugLevel = 0x00; #undef ScsiPortMoveMemory /* GLOBALS *******************************************************************/ static BOOLEAN SpiGetPciConfigData(IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT DeviceObject, IN struct _HW_INITIALIZATION_DATA *HwInitializationData, IN OUT PPORT_CONFIGURATION_INFORMATION PortConfig, IN PUNICODE_STRING RegistryPath, IN ULONG BusNumber, IN OUT PPCI_SLOT_NUMBER NextSlotNumber); static NTSTATUS NTAPI ScsiPortCreateClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp); static DRIVER_DISPATCH ScsiPortDispatchScsi; static NTSTATUS NTAPI ScsiPortDispatchScsi(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp); static NTSTATUS NTAPI ScsiPortDeviceControl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp); static DRIVER_STARTIO ScsiPortStartIo; static VOID NTAPI ScsiPortStartIo(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp); static BOOLEAN NTAPI ScsiPortStartPacket(IN OUT PVOID Context); IO_ALLOCATION_ACTION NTAPI SpiAdapterControl(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID MapRegisterBase, PVOID Context); static PSCSI_PORT_LUN_EXTENSION SpiAllocateLunExtension(IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension); static PSCSI_PORT_LUN_EXTENSION SpiGetLunExtension (IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, IN UCHAR PathId, IN UCHAR TargetId, IN UCHAR Lun); static PSCSI_REQUEST_BLOCK_INFO SpiAllocateSrbStructures(PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, PSCSI_PORT_LUN_EXTENSION LunExtension, PSCSI_REQUEST_BLOCK Srb); static NTSTATUS SpiSendInquiry(IN PDEVICE_OBJECT DeviceObject, IN OUT PSCSI_LUN_INFO LunInfo); static VOID SpiScanAdapter(IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension); static NTSTATUS SpiGetInquiryData (IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, IN PIRP Irp); static PSCSI_REQUEST_BLOCK_INFO SpiGetSrbData(IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, IN UCHAR PathId, IN UCHAR TargetId, IN UCHAR Lun, IN UCHAR QueueTag); static BOOLEAN NTAPI ScsiPortIsr(IN PKINTERRUPT Interrupt, IN PVOID ServiceContext); static VOID NTAPI ScsiPortDpcForIsr(IN PKDPC Dpc, IN PDEVICE_OBJECT DpcDeviceObject, IN PIRP DpcIrp, IN PVOID DpcContext); static VOID NTAPI ScsiPortIoTimer(PDEVICE_OBJECT DeviceObject, PVOID Context); IO_ALLOCATION_ACTION NTAPI ScsiPortAllocateAdapterChannel(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID MapRegisterBase, IN PVOID Context); static NTSTATUS SpiBuildDeviceMap(IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, IN PUNICODE_STRING RegistryPath); static NTSTATUS SpiStatusSrbToNt(UCHAR SrbStatus); static VOID SpiSendRequestSense(IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, IN PSCSI_REQUEST_BLOCK Srb); static IO_COMPLETION_ROUTINE SpiCompletionRoutine; NTSTATUS NTAPI SpiCompletionRoutine(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context); static VOID NTAPI SpiProcessCompletedRequest(IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, IN PSCSI_REQUEST_BLOCK_INFO SrbInfo, OUT PBOOLEAN NeedToCallStartIo); VOID NTAPI SpiGetNextRequestFromLun(IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, IN PSCSI_PORT_LUN_EXTENSION LunExtension); VOID NTAPI SpiMiniportTimerDpc(IN struct _KDPC *Dpc, IN PVOID DeviceObject, IN PVOID SystemArgument1, IN PVOID SystemArgument2); static NTSTATUS SpiCreatePortConfig(PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, PHW_INITIALIZATION_DATA HwInitData, PCONFIGURATION_INFO InternalConfigInfo, PPORT_CONFIGURATION_INFORMATION ConfigInfo, BOOLEAN FirstCall); NTSTATUS NTAPI SpQueryDeviceCallout(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); static VOID SpiParseDeviceInfo(IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, IN HANDLE Key, IN PPORT_CONFIGURATION_INFORMATION ConfigInfo, IN PCONFIGURATION_INFO InternalConfigInfo, IN PUCHAR Buffer); static VOID SpiResourceToConfig(IN PHW_INITIALIZATION_DATA HwInitializationData, IN PCM_FULL_RESOURCE_DESCRIPTOR ResourceDescriptor, IN PPORT_CONFIGURATION_INFORMATION PortConfig); static PCM_RESOURCE_LIST SpiConfigToResource(PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, PPORT_CONFIGURATION_INFORMATION PortConfig); static VOID SpiCleanupAfterInit(PSCSI_PORT_DEVICE_EXTENSION DeviceExtension); static NTSTATUS SpiHandleAttachRelease(PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, PIRP Irp); static NTSTATUS SpiAllocateCommonBuffer(PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, ULONG NonCachedSize); NTHALAPI ULONG NTAPI HalGetBusData(BUS_DATA_TYPE, ULONG, ULONG, PVOID, ULONG); NTHALAPI ULONG NTAPI HalGetInterruptVector(INTERFACE_TYPE, ULONG, ULONG, ULONG, PKIRQL, PKAFFINITY); NTHALAPI NTSTATUS NTAPI HalAssignSlotResources(PUNICODE_STRING, PUNICODE_STRING, PDRIVER_OBJECT, PDEVICE_OBJECT, INTERFACE_TYPE, ULONG, ULONG, PCM_RESOURCE_LIST *); /* FUNCTIONS *****************************************************************/ /********************************************************************** * NAME EXPORTED * DriverEntry * * DESCRIPTION * This function initializes the driver. * * RUN LEVEL * PASSIVE_LEVEL * * ARGUMENTS * DriverObject * System allocated Driver Object for this driver. * * RegistryPath * Name of registry driver service key. * * RETURN VALUE * Status. */ NTSTATUS NTAPI DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath) { DPRINT("ScsiPort Driver %s\n", VERSION); return(STATUS_SUCCESS); } /********************************************************************** * NAME EXPORTED * ScsiDebugPrint * * DESCRIPTION * Prints debugging messages. * * RUN LEVEL * PASSIVE_LEVEL * * ARGUMENTS * DebugPrintLevel * Debug level of the given message. * * DebugMessage * Pointer to printf()-compatible format string. * * ... Additional output data (see printf()). * * RETURN VALUE * None. * * @implemented */ VOID ScsiDebugPrint(IN ULONG DebugPrintLevel, IN PCHAR DebugMessage, ...) { char Buffer[256]; va_list ap; if (DebugPrintLevel > InternalDebugLevel) return; va_start(ap, DebugMessage); vsprintf(Buffer, DebugMessage, ap); va_end(ap); DbgPrint(Buffer); } /* An internal helper function for ScsiPortCompleteRequest */ VOID NTAPI SpiCompleteRequest(IN PVOID HwDeviceExtension, IN PSCSI_REQUEST_BLOCK_INFO SrbInfo, IN UCHAR SrbStatus) { PSCSI_REQUEST_BLOCK Srb; /* Get current SRB */ Srb = SrbInfo->Srb; /* Return if there is no SRB or it is not active */ if (!Srb || !(Srb->SrbFlags & SRB_FLAGS_IS_ACTIVE)) return; /* Set status */ Srb->SrbStatus = SrbStatus; /* Set data transfered to 0 */ Srb->DataTransferLength = 0; /* Notify */ ScsiPortNotification(RequestComplete, HwDeviceExtension, Srb); } /* * @unimplemented */ VOID NTAPI ScsiPortCompleteRequest(IN PVOID HwDeviceExtension, IN UCHAR PathId, IN UCHAR TargetId, IN UCHAR Lun, IN UCHAR SrbStatus) { PSCSI_PORT_DEVICE_EXTENSION DeviceExtension; PSCSI_PORT_LUN_EXTENSION LunExtension; PSCSI_REQUEST_BLOCK_INFO SrbInfo; PLIST_ENTRY ListEntry; ULONG BusNumber; ULONG Target; DPRINT("ScsiPortCompleteRequest() called\n"); DeviceExtension = CONTAINING_RECORD(HwDeviceExtension, SCSI_PORT_DEVICE_EXTENSION, MiniPortDeviceExtension); /* Go through all buses */ for (BusNumber = 0; BusNumber < 8; BusNumber++) { /* Go through all targets */ for (Target = 0; Target < DeviceExtension->MaxTargedIds; Target++) { /* Get logical unit list head */ LunExtension = DeviceExtension->LunExtensionList[Target % 8]; /* Go through all logical units */ while (LunExtension) { /* Now match what caller asked with what we are at now */ if ((PathId == SP_UNTAGGED || PathId == LunExtension->PathId) && (TargetId == SP_UNTAGGED || TargetId == LunExtension->TargetId) && (Lun == SP_UNTAGGED || Lun == LunExtension->Lun)) { /* Yes, that's what caller asked for. Complete abort requests */ if (LunExtension->CompletedAbortRequests) { /* TODO: Save SrbStatus in this request */ DPRINT1("Completing abort request without setting SrbStatus!\n"); /* Issue a notification request */ ScsiPortNotification(RequestComplete, HwDeviceExtension, LunExtension->CompletedAbortRequests); } /* Complete the request using our helper */ SpiCompleteRequest(HwDeviceExtension, &LunExtension->SrbInfo, SrbStatus); /* Go through the queue and complete everything there too */ ListEntry = LunExtension->SrbInfo.Requests.Flink; while (ListEntry != &LunExtension->SrbInfo.Requests) { /* Get the actual SRB info entry */ SrbInfo = CONTAINING_RECORD(ListEntry, SCSI_REQUEST_BLOCK_INFO, Requests); /* Complete it */ SpiCompleteRequest(HwDeviceExtension, SrbInfo, SrbStatus); /* Advance to the next request in queue */ ListEntry = SrbInfo->Requests.Flink; } } /* Advance to the next one */ LunExtension = LunExtension->Next; } } } } /* * @unimplemented */ VOID NTAPI ScsiPortFlushDma(IN PVOID HwDeviceExtension) { DPRINT("ScsiPortFlushDma()\n"); UNIMPLEMENTED; } /* * @implemented */ VOID NTAPI ScsiPortFreeDeviceBase(IN PVOID HwDeviceExtension, IN PVOID MappedAddress) { PSCSI_PORT_DEVICE_EXTENSION DeviceExtension; PMAPPED_ADDRESS NextMa, LastMa; //DPRINT("ScsiPortFreeDeviceBase() called\n"); DeviceExtension = CONTAINING_RECORD(HwDeviceExtension, SCSI_PORT_DEVICE_EXTENSION, MiniPortDeviceExtension); /* Initialize our pointers */ NextMa = DeviceExtension->MappedAddressList; LastMa = NextMa; while (NextMa) { if (NextMa->MappedAddress == MappedAddress) { /* Unmap it first */ MmUnmapIoSpace(MappedAddress, NextMa->NumberOfBytes); /* Remove it from the list */ if (NextMa == DeviceExtension->MappedAddressList) { /* Remove the first entry */ DeviceExtension->MappedAddressList = NextMa->NextMappedAddress; } else { LastMa->NextMappedAddress = NextMa->NextMappedAddress; } /* Free the resources and quit */ ExFreePool(NextMa); return; } else { LastMa = NextMa; NextMa = NextMa->NextMappedAddress; } } } /* * @implemented */ ULONG NTAPI ScsiPortGetBusData(IN PVOID DeviceExtension, IN ULONG BusDataType, IN ULONG SystemIoBusNumber, IN ULONG SlotNumber, IN PVOID Buffer, IN ULONG Length) { DPRINT("ScsiPortGetBusData()\n"); if (Length) { /* If Length is non-zero, just forward the call to HalGetBusData() function */ return HalGetBusData(BusDataType, SystemIoBusNumber, SlotNumber, Buffer, Length); } /* We have a more complex case here */ UNIMPLEMENTED; return 0; } /* * @implemented */ ULONG NTAPI ScsiPortSetBusDataByOffset(IN PVOID DeviceExtension, IN ULONG BusDataType, IN ULONG SystemIoBusNumber, IN ULONG SlotNumber, IN PVOID Buffer, IN ULONG Offset, IN ULONG Length) { DPRINT("ScsiPortSetBusDataByOffset()\n"); return HalSetBusDataByOffset(BusDataType, SystemIoBusNumber, SlotNumber, Buffer, Offset, Length); } /* * @implemented */ PVOID NTAPI ScsiPortGetDeviceBase(IN PVOID HwDeviceExtension, IN INTERFACE_TYPE BusType, IN ULONG SystemIoBusNumber, IN SCSI_PHYSICAL_ADDRESS IoAddress, IN ULONG NumberOfBytes, IN BOOLEAN InIoSpace) { PSCSI_PORT_DEVICE_EXTENSION DeviceExtension; PHYSICAL_ADDRESS TranslatedAddress; PMAPPED_ADDRESS DeviceBase; ULONG AddressSpace; PVOID MappedAddress; //DPRINT ("ScsiPortGetDeviceBase() called\n"); DeviceExtension = CONTAINING_RECORD(HwDeviceExtension, SCSI_PORT_DEVICE_EXTENSION, MiniPortDeviceExtension); AddressSpace = (ULONG)InIoSpace; if (HalTranslateBusAddress(BusType, SystemIoBusNumber, IoAddress, &AddressSpace, &TranslatedAddress) == FALSE) { return NULL; } /* i/o space */ if (AddressSpace != 0) return((PVOID)(ULONG_PTR)TranslatedAddress.QuadPart); MappedAddress = MmMapIoSpace(TranslatedAddress, NumberOfBytes, FALSE); DeviceBase = ExAllocatePoolWithTag(NonPagedPool, sizeof(MAPPED_ADDRESS), TAG_SCSIPORT); if (DeviceBase == NULL) return MappedAddress; DeviceBase->MappedAddress = MappedAddress; DeviceBase->NumberOfBytes = NumberOfBytes; DeviceBase->IoAddress = IoAddress; DeviceBase->BusNumber = SystemIoBusNumber; /* Link it to the Device Extension list */ DeviceBase->NextMappedAddress = DeviceExtension->MappedAddressList; DeviceExtension->MappedAddressList = DeviceBase; return MappedAddress; } /* * @unimplemented */ PVOID NTAPI ScsiPortGetLogicalUnit(IN PVOID HwDeviceExtension, IN UCHAR PathId, IN UCHAR TargetId, IN UCHAR Lun) { PSCSI_PORT_DEVICE_EXTENSION DeviceExtension; PSCSI_PORT_LUN_EXTENSION LunExtension; DPRINT("ScsiPortGetLogicalUnit() called\n"); DeviceExtension = CONTAINING_RECORD(HwDeviceExtension, SCSI_PORT_DEVICE_EXTENSION, MiniPortDeviceExtension); /* Check the extension size */ if (!DeviceExtension->LunExtensionSize) { /* They didn't want one */ return NULL; } LunExtension = SpiGetLunExtension(DeviceExtension, PathId, TargetId, Lun); /* Check that the logical unit exists */ if (!LunExtension) { /* Nope, return NULL */ return NULL; } /* Return the logical unit miniport extension */ return (LunExtension + 1); } /* * @implemented */ SCSI_PHYSICAL_ADDRESS NTAPI ScsiPortGetPhysicalAddress(IN PVOID HwDeviceExtension, IN PSCSI_REQUEST_BLOCK Srb OPTIONAL, IN PVOID VirtualAddress, OUT ULONG *Length) { PSCSI_PORT_DEVICE_EXTENSION DeviceExtension; SCSI_PHYSICAL_ADDRESS PhysicalAddress; SIZE_T BufferLength = 0; ULONG_PTR Offset; PSCSI_SG_ADDRESS SGList; PSCSI_REQUEST_BLOCK_INFO SrbInfo; DPRINT("ScsiPortGetPhysicalAddress(%p %p %p %p)\n", HwDeviceExtension, Srb, VirtualAddress, Length); DeviceExtension = CONTAINING_RECORD(HwDeviceExtension, SCSI_PORT_DEVICE_EXTENSION, MiniPortDeviceExtension); if (Srb == NULL || Srb->SenseInfoBuffer == VirtualAddress) { /* Simply look it up in the allocated common buffer */ Offset = (PUCHAR)VirtualAddress - (PUCHAR)DeviceExtension->SrbExtensionBuffer; BufferLength = DeviceExtension->CommonBufferLength - Offset; PhysicalAddress.QuadPart = DeviceExtension->PhysicalAddress.QuadPart + Offset; } else if (DeviceExtension->MapRegisters) { /* Scatter-gather list must be used */ SrbInfo = SpiGetSrbData(DeviceExtension, Srb->PathId, Srb->TargetId, Srb->Lun, Srb->QueueTag); SGList = SrbInfo->ScatterGather; /* Find needed item in the SG list */ Offset = (PCHAR)VirtualAddress - (PCHAR)Srb->DataBuffer; while (Offset >= SGList->Length) { Offset -= SGList->Length; SGList++; } /* We're done, store length and physical address */ BufferLength = SGList->Length - Offset; PhysicalAddress.QuadPart = SGList->PhysicalAddress.QuadPart + Offset; } else { /* Nothing */ PhysicalAddress.QuadPart = (LONGLONG)(SP_UNINITIALIZED_VALUE); } *Length = (ULONG)BufferLength; return PhysicalAddress; } /* * @unimplemented */ PSCSI_REQUEST_BLOCK NTAPI ScsiPortGetSrb(IN PVOID DeviceExtension, IN UCHAR PathId, IN UCHAR TargetId, IN UCHAR Lun, IN LONG QueueTag) { DPRINT1("ScsiPortGetSrb() unimplemented\n"); UNIMPLEMENTED; return NULL; } /* * @implemented */ PVOID NTAPI ScsiPortGetUncachedExtension(IN PVOID HwDeviceExtension, IN PPORT_CONFIGURATION_INFORMATION ConfigInfo, IN ULONG NumberOfBytes) { PSCSI_PORT_DEVICE_EXTENSION DeviceExtension; DEVICE_DESCRIPTION DeviceDescription; ULONG MapRegistersCount; NTSTATUS Status; DPRINT("ScsiPortGetUncachedExtension(%p %p %lu)\n", HwDeviceExtension, ConfigInfo, NumberOfBytes); DeviceExtension = CONTAINING_RECORD(HwDeviceExtension, SCSI_PORT_DEVICE_EXTENSION, MiniPortDeviceExtension); /* Check for allocated common DMA buffer */ if (DeviceExtension->SrbExtensionBuffer != NULL) { DPRINT1("The HBA has already got a common DMA buffer!\n"); return NULL; } /* Check for DMA adapter object */ if (DeviceExtension->AdapterObject == NULL) { /* Initialize DMA adapter description */ RtlZeroMemory(&DeviceDescription, sizeof(DEVICE_DESCRIPTION)); DeviceDescription.Version = DEVICE_DESCRIPTION_VERSION; DeviceDescription.Master = ConfigInfo->Master; DeviceDescription.ScatterGather = ConfigInfo->ScatterGather; DeviceDescription.DemandMode = ConfigInfo->DemandMode; DeviceDescription.Dma32BitAddresses = ConfigInfo->Dma32BitAddresses; DeviceDescription.BusNumber = ConfigInfo->SystemIoBusNumber; DeviceDescription.DmaChannel = ConfigInfo->DmaChannel; DeviceDescription.InterfaceType = ConfigInfo->AdapterInterfaceType; DeviceDescription.DmaWidth = ConfigInfo->DmaWidth; DeviceDescription.DmaSpeed = ConfigInfo->DmaSpeed; DeviceDescription.MaximumLength = ConfigInfo->MaximumTransferLength; DeviceDescription.DmaPort = ConfigInfo->DmaPort; /* Get a DMA adapter object */ DeviceExtension->AdapterObject = HalGetAdapter(&DeviceDescription, &MapRegistersCount); /* Fail in case of error */ if (DeviceExtension->AdapterObject == NULL) { DPRINT1("HalGetAdapter() failed\n"); return NULL; } /* Set number of physical breaks */ if (ConfigInfo->NumberOfPhysicalBreaks != 0 && MapRegistersCount > ConfigInfo->NumberOfPhysicalBreaks) { DeviceExtension->PortCapabilities.MaximumPhysicalPages = ConfigInfo->NumberOfPhysicalBreaks; } else { DeviceExtension->PortCapabilities.MaximumPhysicalPages = MapRegistersCount; } } /* Update auto request sense feature */ DeviceExtension->SupportsAutoSense = ConfigInfo->AutoRequestSense; /* Update Srb extension size */ if (DeviceExtension->SrbExtensionSize != ConfigInfo->SrbExtensionSize) DeviceExtension->SrbExtensionSize = ConfigInfo->SrbExtensionSize; /* Update Srb extension alloc flag */ if (ConfigInfo->AutoRequestSense || DeviceExtension->SrbExtensionSize) DeviceExtension->NeedSrbExtensionAlloc = TRUE; /* Allocate a common DMA buffer */ Status = SpiAllocateCommonBuffer(DeviceExtension, NumberOfBytes); if (!NT_SUCCESS(Status)) { DPRINT1("SpiAllocateCommonBuffer() failed with Status = 0x%08X!\n", Status); return NULL; } return DeviceExtension->NonCachedExtension; } static NTSTATUS SpiAllocateCommonBuffer(PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, ULONG NonCachedSize) { PVOID *SrbExtension, CommonBuffer; ULONG CommonBufferLength, BufSize; /* If size is 0, set it to 16 */ if (!DeviceExtension->SrbExtensionSize) DeviceExtension->SrbExtensionSize = 16; /* Calculate size */ BufSize = DeviceExtension->SrbExtensionSize; /* Add autosense data size if needed */ if (DeviceExtension->SupportsAutoSense) BufSize += sizeof(SENSE_DATA); /* Round it */ BufSize = (BufSize + sizeof(LONGLONG) - 1) & ~(sizeof(LONGLONG) - 1); /* Sum up into the total common buffer length, and round it to page size */ CommonBufferLength = ROUND_TO_PAGES(NonCachedSize + BufSize * DeviceExtension->RequestsNumber); /* Allocate it */ if (!DeviceExtension->AdapterObject) { /* From nonpaged pool if there is no DMA */ CommonBuffer = ExAllocatePoolWithTag(NonPagedPool, CommonBufferLength, TAG_SCSIPORT); } else { /* Perform a full request since we have a DMA adapter*/ CommonBuffer = HalAllocateCommonBuffer(DeviceExtension->AdapterObject, CommonBufferLength, &DeviceExtension->PhysicalAddress, FALSE ); } /* Fail in case of error */ if (!CommonBuffer) return STATUS_INSUFFICIENT_RESOURCES; /* Zero it */ RtlZeroMemory(CommonBuffer, CommonBufferLength); /* Store its size in Device Extension */ DeviceExtension->CommonBufferLength = CommonBufferLength; /* SrbExtension buffer is located at the beginning of the buffer */ DeviceExtension->SrbExtensionBuffer = CommonBuffer; /* Non-cached extension buffer is located at the end of the common buffer */ if (NonCachedSize) { CommonBufferLength -= NonCachedSize; DeviceExtension->NonCachedExtension = (PUCHAR)CommonBuffer + CommonBufferLength; } else { DeviceExtension->NonCachedExtension = NULL; } if (DeviceExtension->NeedSrbExtensionAlloc) { /* Look up how many SRB data structures we need */ DeviceExtension->SrbDataCount = CommonBufferLength / BufSize; /* Initialize the free SRB extensions list */ SrbExtension = (PVOID *)CommonBuffer; DeviceExtension->FreeSrbExtensions = SrbExtension; /* Fill the remaining pointers (if we have more than 1 SRB) */ while (CommonBufferLength >= 2 * BufSize) { *SrbExtension = (PVOID*)((PCHAR)SrbExtension + BufSize); SrbExtension = *SrbExtension; CommonBufferLength -= BufSize; } } return STATUS_SUCCESS; } /* * @implemented */ PVOID NTAPI ScsiPortGetVirtualAddress(IN PVOID HwDeviceExtension, IN SCSI_PHYSICAL_ADDRESS PhysicalAddress) { PSCSI_PORT_DEVICE_EXTENSION DeviceExtension; ULONG Offset; DPRINT("ScsiPortGetVirtualAddress(%p %I64x)\n", HwDeviceExtension, PhysicalAddress.QuadPart); DeviceExtension = CONTAINING_RECORD(HwDeviceExtension, SCSI_PORT_DEVICE_EXTENSION, MiniPortDeviceExtension); if (DeviceExtension->PhysicalAddress.QuadPart > PhysicalAddress.QuadPart) return NULL; Offset = (ULONG)(PhysicalAddress.QuadPart - DeviceExtension->PhysicalAddress.QuadPart); if (Offset >= DeviceExtension->CommonBufferLength) return NULL; return (PVOID)((ULONG_PTR)DeviceExtension->SrbExtensionBuffer + Offset); } static VOID SpiInitOpenKeys(PCONFIGURATION_INFO ConfigInfo, PUNICODE_STRING RegistryPath) { OBJECT_ATTRIBUTES ObjectAttributes; UNICODE_STRING KeyName; NTSTATUS Status; /* Open the service key */ InitializeObjectAttributes(&ObjectAttributes, RegistryPath, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = ZwOpenKey(&ConfigInfo->ServiceKey, KEY_READ, &ObjectAttributes); if (!NT_SUCCESS(Status)) { DPRINT("Unable to open driver's registry key %wZ, status 0x%08x\n", RegistryPath, Status); ConfigInfo->ServiceKey = NULL; } /* If we could open driver's service key, then proceed to the Parameters key */ if (ConfigInfo->ServiceKey != NULL) { RtlInitUnicodeString(&KeyName, L"Parameters"); InitializeObjectAttributes(&ObjectAttributes, &KeyName, OBJ_CASE_INSENSITIVE, ConfigInfo->ServiceKey, (PSECURITY_DESCRIPTOR) NULL); /* Try to open it */ Status = ZwOpenKey(&ConfigInfo->DeviceKey, KEY_READ, &ObjectAttributes); if (NT_SUCCESS(Status)) { /* Yes, Parameters key exist, and it must be used instead of the Service key */ ZwClose(ConfigInfo->ServiceKey); ConfigInfo->ServiceKey = ConfigInfo->DeviceKey; ConfigInfo->DeviceKey = NULL; } } if (ConfigInfo->ServiceKey != NULL) { /* Open the Device key */ RtlInitUnicodeString(&KeyName, L"Device"); InitializeObjectAttributes(&ObjectAttributes, &KeyName, OBJ_CASE_INSENSITIVE, ConfigInfo->ServiceKey, NULL); /* We don't check for failure here - not needed */ ZwOpenKey(&ConfigInfo->DeviceKey, KEY_READ, &ObjectAttributes); } } /********************************************************************** * NAME EXPORTED * ScsiPortInitialize * * DESCRIPTION * Initializes SCSI port driver specific data. * * RUN LEVEL * PASSIVE_LEVEL * * ARGUMENTS * Argument1 * Pointer to the miniport driver's driver object. * * Argument2 * Pointer to the miniport driver's registry path. * * HwInitializationData * Pointer to port driver specific configuration data. * * HwContext Miniport driver specific context. * * RETURN VALUE * Status. * * @implemented */ ULONG NTAPI ScsiPortInitialize(IN PVOID Argument1, IN PVOID Argument2, IN struct _HW_INITIALIZATION_DATA *HwInitializationData, IN PVOID HwContext) { PDRIVER_OBJECT DriverObject = (PDRIVER_OBJECT)Argument1; PUNICODE_STRING RegistryPath = (PUNICODE_STRING)Argument2; PSCSI_PORT_DEVICE_EXTENSION DeviceExtension = NULL; PCONFIGURATION_INFORMATION SystemConfig; PPORT_CONFIGURATION_INFORMATION PortConfig; PORT_CONFIGURATION_INFORMATION InitialPortConfig; CONFIGURATION_INFO ConfigInfo; ULONG DeviceExtensionSize; ULONG PortConfigSize; BOOLEAN Again; BOOLEAN DeviceFound = FALSE; BOOLEAN FirstConfigCall = TRUE; ULONG Result; NTSTATUS Status; ULONG MaxBus; PCI_SLOT_NUMBER SlotNumber; PDEVICE_OBJECT PortDeviceObject; WCHAR NameBuffer[80]; UNICODE_STRING DeviceName; WCHAR DosNameBuffer[80]; UNICODE_STRING DosDeviceName; PIO_SCSI_CAPABILITIES PortCapabilities; KIRQL OldIrql; PCM_RESOURCE_LIST ResourceList; BOOLEAN Conflict; SIZE_T BusConfigSize; DPRINT ("ScsiPortInitialize() called!\n"); /* Check params for validity */ if ((HwInitializationData->HwInitialize == NULL) || (HwInitializationData->HwStartIo == NULL) || (HwInitializationData->HwInterrupt == NULL) || (HwInitializationData->HwFindAdapter == NULL) || (HwInitializationData->HwResetBus == NULL)) { return STATUS_INVALID_PARAMETER; } /* Set handlers */ DriverObject->DriverStartIo = ScsiPortStartIo; DriverObject->MajorFunction[IRP_MJ_CREATE] = ScsiPortCreateClose; DriverObject->MajorFunction[IRP_MJ_CLOSE] = ScsiPortCreateClose; DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = ScsiPortDeviceControl; DriverObject->MajorFunction[IRP_MJ_SCSI] = ScsiPortDispatchScsi; /* Obtain configuration information */ SystemConfig = IoGetConfigurationInformation(); /* Zero the internal configuration info structure */ RtlZeroMemory(&ConfigInfo, sizeof(CONFIGURATION_INFO)); /* Zero starting slot number */ SlotNumber.u.AsULONG = 0; /* Allocate space for access ranges */ if (HwInitializationData->NumberOfAccessRanges) { ConfigInfo.AccessRanges = ExAllocatePoolWithTag(PagedPool, HwInitializationData->NumberOfAccessRanges * sizeof(ACCESS_RANGE), TAG_SCSIPORT); /* Fail if failed */ if (ConfigInfo.AccessRanges == NULL) return STATUS_INSUFFICIENT_RESOURCES; } /* Open registry keys */ SpiInitOpenKeys(&ConfigInfo, (PUNICODE_STRING)Argument2); /* Last adapter number = not known */ ConfigInfo.LastAdapterNumber = SP_UNINITIALIZED_VALUE; /* Calculate sizes of DeviceExtension and PortConfig */ DeviceExtensionSize = sizeof(SCSI_PORT_DEVICE_EXTENSION) + HwInitializationData->DeviceExtensionSize; MaxBus = (HwInitializationData->AdapterInterfaceType == PCIBus) ? 8 : 1; DPRINT("MaxBus: %lu\n", MaxBus); while (TRUE) { /* Create a unicode device name */ swprintf(NameBuffer, L"\\Device\\ScsiPort%lu", SystemConfig->ScsiPortCount); RtlInitUnicodeString(&DeviceName, NameBuffer); DPRINT("Creating device: %wZ\n", &DeviceName); /* Create the port device */ Status = IoCreateDevice(DriverObject, DeviceExtensionSize, &DeviceName, FILE_DEVICE_CONTROLLER, 0, FALSE, &PortDeviceObject); if (!NT_SUCCESS(Status)) { DPRINT1("IoCreateDevice call failed! (Status 0x%lX)\n", Status); PortDeviceObject = NULL; break; } DPRINT ("Created device: %wZ (%p)\n", &DeviceName, PortDeviceObject); /* Set the buffering strategy here... */ PortDeviceObject->Flags |= DO_DIRECT_IO; PortDeviceObject->AlignmentRequirement = FILE_WORD_ALIGNMENT; /* FIXME: Is this really needed? */ /* Fill Device Extension */ DeviceExtension = PortDeviceObject->DeviceExtension; RtlZeroMemory(DeviceExtension, DeviceExtensionSize); DeviceExtension->Length = DeviceExtensionSize; DeviceExtension->DeviceObject = PortDeviceObject; DeviceExtension->PortNumber = SystemConfig->ScsiPortCount; /* Driver's routines... */ DeviceExtension->HwInitialize = HwInitializationData->HwInitialize; DeviceExtension->HwStartIo = HwInitializationData->HwStartIo; DeviceExtension->HwInterrupt = HwInitializationData->HwInterrupt; DeviceExtension->HwResetBus = HwInitializationData->HwResetBus; DeviceExtension->HwDmaStarted = HwInitializationData->HwDmaStarted; /* Extensions sizes */ DeviceExtension->MiniPortExtensionSize = HwInitializationData->DeviceExtensionSize; DeviceExtension->LunExtensionSize = HwInitializationData->SpecificLuExtensionSize; DeviceExtension->SrbExtensionSize = HwInitializationData->SrbExtensionSize; /* Round Srb extension size to the quadword */ DeviceExtension->SrbExtensionSize = ~(sizeof(LONGLONG) - 1) & (DeviceExtension->SrbExtensionSize + sizeof(LONGLONG) - 1); /* Fill some numbers (bus count, lun count, etc) */ DeviceExtension->MaxLunCount = SCSI_MAXIMUM_LOGICAL_UNITS; DeviceExtension->RequestsNumber = 16; /* Initialize the spin lock in the controller extension */ KeInitializeSpinLock(&DeviceExtension->IrqLock); KeInitializeSpinLock(&DeviceExtension->SpinLock); /* Initialize the DPC object */ IoInitializeDpcRequest(PortDeviceObject, ScsiPortDpcForIsr); /* Initialize the device timer */ DeviceExtension->TimerCount = -1; IoInitializeTimer(PortDeviceObject, ScsiPortIoTimer, DeviceExtension); /* Initialize miniport timer */ KeInitializeTimer(&DeviceExtension->MiniportTimer); KeInitializeDpc(&DeviceExtension->MiniportTimerDpc, SpiMiniportTimerDpc, PortDeviceObject); CreatePortConfig: Status = SpiCreatePortConfig(DeviceExtension, HwInitializationData, &ConfigInfo, &InitialPortConfig, FirstConfigCall); if (!NT_SUCCESS(Status)) { DPRINT("SpiCreatePortConfig() failed with Status 0x%08X\n", Status); break; } /* Allocate and initialize port configuration info */ PortConfigSize = (sizeof(PORT_CONFIGURATION_INFORMATION) + HwInitializationData->NumberOfAccessRanges * sizeof(ACCESS_RANGE) + 7) & ~7; DeviceExtension->PortConfig = ExAllocatePoolWithTag(NonPagedPool, PortConfigSize, TAG_SCSIPORT); /* Fail if failed */ if (DeviceExtension->PortConfig == NULL) { Status = STATUS_INSUFFICIENT_RESOURCES; break; } PortConfig = DeviceExtension->PortConfig; /* Copy information here */ RtlCopyMemory(PortConfig, &InitialPortConfig, sizeof(PORT_CONFIGURATION_INFORMATION)); /* Copy extension sizes into the PortConfig */ PortConfig->SpecificLuExtensionSize = DeviceExtension->LunExtensionSize; PortConfig->SrbExtensionSize = DeviceExtension->SrbExtensionSize; /* Initialize Access ranges */ if (HwInitializationData->NumberOfAccessRanges != 0) { PortConfig->AccessRanges = (PVOID)(PortConfig+1); /* Align to LONGLONG */ PortConfig->AccessRanges = (PVOID)((ULONG_PTR)(PortConfig->AccessRanges) + 7); PortConfig->AccessRanges = (PVOID)((ULONG_PTR)(PortConfig->AccessRanges) & ~7); /* Copy the data */ RtlCopyMemory(PortConfig->AccessRanges, ConfigInfo.AccessRanges, HwInitializationData->NumberOfAccessRanges * sizeof(ACCESS_RANGE)); } /* Search for matching PCI device */ if ((HwInitializationData->AdapterInterfaceType == PCIBus) && (HwInitializationData->VendorIdLength > 0) && (HwInitializationData->VendorId != NULL) && (HwInitializationData->DeviceIdLength > 0) && (HwInitializationData->DeviceId != NULL)) { PortConfig->BusInterruptLevel = 0; /* Get PCI device data */ DPRINT("VendorId '%.*s' DeviceId '%.*s'\n", HwInitializationData->VendorIdLength, HwInitializationData->VendorId, HwInitializationData->DeviceIdLength, HwInitializationData->DeviceId); if (!SpiGetPciConfigData(DriverObject, PortDeviceObject, HwInitializationData, PortConfig, RegistryPath, ConfigInfo.BusNumber, &SlotNumber)) { /* Continue to the next bus, nothing here */ ConfigInfo.BusNumber++; DeviceExtension->PortConfig = NULL; ExFreePool(PortConfig); Again = FALSE; goto CreatePortConfig; } if (!PortConfig->BusInterruptLevel) { /* Bypass this slot, because no interrupt was assigned */ DeviceExtension->PortConfig = NULL; ExFreePool(PortConfig); goto CreatePortConfig; } } else { DPRINT("Non-pci bus\n"); } /* Note: HwFindAdapter is called once for each bus */ Again = FALSE; DPRINT("Calling HwFindAdapter() for Bus %lu\n", PortConfig->SystemIoBusNumber); Result = (HwInitializationData->HwFindAdapter)(&DeviceExtension->MiniPortDeviceExtension, HwContext, 0, /* BusInformation */ ConfigInfo.Parameter, /* ArgumentString */ PortConfig, &Again); DPRINT("HwFindAdapter() Result: %lu Again: %s\n", Result, (Again) ? "True" : "False"); /* Free MapRegisterBase, it's not needed anymore */ if (DeviceExtension->MapRegisterBase != NULL) { ExFreePool(DeviceExtension->MapRegisterBase); DeviceExtension->MapRegisterBase = NULL; } /* If result is nothing good... */ if (Result != SP_RETURN_FOUND) { DPRINT("HwFindAdapter() Result: %lu\n", Result); if (Result == SP_RETURN_NOT_FOUND) { /* We can continue on the next bus */ ConfigInfo.BusNumber++; Again = FALSE; DeviceExtension->PortConfig = NULL; ExFreePool(PortConfig); goto CreatePortConfig; } /* Otherwise, break */ Status = STATUS_INTERNAL_ERROR; break; } DPRINT("ScsiPortInitialize(): Found HBA! (%x), adapter Id %d\n", PortConfig->BusInterruptVector, PortConfig->InitiatorBusId[0]); /* If the SRB extension size was updated */ if (!DeviceExtension->NonCachedExtension && (PortConfig->SrbExtensionSize != DeviceExtension->SrbExtensionSize)) { /* Set it (rounding to LONGLONG again) */ DeviceExtension->SrbExtensionSize = (PortConfig->SrbExtensionSize + sizeof(LONGLONG)) & ~(sizeof(LONGLONG) - 1); } /* The same with LUN extension size */ if (PortConfig->SpecificLuExtensionSize != DeviceExtension->LunExtensionSize) DeviceExtension->LunExtensionSize = PortConfig->SpecificLuExtensionSize; if (!((HwInitializationData->AdapterInterfaceType == PCIBus) && (HwInitializationData->VendorIdLength > 0) && (HwInitializationData->VendorId != NULL) && (HwInitializationData->DeviceIdLength > 0) && (HwInitializationData->DeviceId != NULL))) { /* Construct a resource list */ ResourceList = SpiConfigToResource(DeviceExtension, PortConfig); if (ResourceList) { UNICODE_STRING UnicodeString; RtlInitUnicodeString(&UnicodeString, L"ScsiAdapter"); DPRINT("Reporting resources\n"); Status = IoReportResourceUsage(&UnicodeString, DriverObject, NULL, 0, PortDeviceObject, ResourceList, FIELD_OFFSET(CM_RESOURCE_LIST, List[0].PartialResourceList.PartialDescriptors) + ResourceList->List[0].PartialResourceList.Count * sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR), FALSE, &Conflict); ExFreePool(ResourceList); /* In case of a failure or a conflict, break */ if (Conflict || (!NT_SUCCESS(Status))) { if (Conflict) Status = STATUS_CONFLICTING_ADDRESSES; break; } } } /* Reset the Conflict var */ Conflict = FALSE; /* Copy all stuff which we ever need from PortConfig to the DeviceExtension */ if (PortConfig->MaximumNumberOfTargets > SCSI_MAXIMUM_TARGETS_PER_BUS) DeviceExtension->MaxTargedIds = SCSI_MAXIMUM_TARGETS_PER_BUS; else DeviceExtension->MaxTargedIds = PortConfig->MaximumNumberOfTargets; DeviceExtension->BusNum = PortConfig->NumberOfBuses; DeviceExtension->CachesData = PortConfig->CachesData; DeviceExtension->ReceiveEvent = PortConfig->ReceiveEvent; DeviceExtension->SupportsTaggedQueuing = PortConfig->TaggedQueuing; DeviceExtension->MultipleReqsPerLun = PortConfig->MultipleRequestPerLu; /* If something was disabled via registry - apply it */ if (ConfigInfo.DisableMultipleLun) DeviceExtension->MultipleReqsPerLun = PortConfig->MultipleRequestPerLu = FALSE; if (ConfigInfo.DisableTaggedQueueing) DeviceExtension->SupportsTaggedQueuing = PortConfig->MultipleRequestPerLu = FALSE; /* Check if we need to alloc SRB data */ if (DeviceExtension->SupportsTaggedQueuing || DeviceExtension->MultipleReqsPerLun) { DeviceExtension->NeedSrbDataAlloc = TRUE; } else { DeviceExtension->NeedSrbDataAlloc = FALSE; } /* Get a pointer to the port capabilities */ PortCapabilities = &DeviceExtension->PortCapabilities; /* Copy one field there */ DeviceExtension->MapBuffers = PortConfig->MapBuffers; PortCapabilities->AdapterUsesPio = PortConfig->MapBuffers; if (DeviceExtension->AdapterObject == NULL && (PortConfig->DmaChannel != SP_UNINITIALIZED_VALUE || PortConfig->Master)) { DPRINT1("DMA is not supported yet\n"); ASSERT(FALSE); } if (DeviceExtension->SrbExtensionBuffer == NULL && (DeviceExtension->SrbExtensionSize != 0 || PortConfig->AutoRequestSense)) { DeviceExtension->SupportsAutoSense = PortConfig->AutoRequestSense; DeviceExtension->NeedSrbExtensionAlloc = TRUE; /* Allocate common buffer */ Status = SpiAllocateCommonBuffer(DeviceExtension, 0); /* Check for failure */ if (!NT_SUCCESS(Status)) break; } /* Allocate SrbData, if needed */ if (DeviceExtension->NeedSrbDataAlloc) { ULONG Count; PSCSI_REQUEST_BLOCK_INFO SrbData; if (DeviceExtension->SrbDataCount != 0) Count = DeviceExtension->SrbDataCount; else Count = DeviceExtension->RequestsNumber * 2; /* Allocate the data */ SrbData = ExAllocatePoolWithTag(NonPagedPool, Count * sizeof(SCSI_REQUEST_BLOCK_INFO), TAG_SCSIPORT); if (SrbData == NULL) return STATUS_INSUFFICIENT_RESOURCES; RtlZeroMemory(SrbData, Count * sizeof(SCSI_REQUEST_BLOCK_INFO)); DeviceExtension->SrbInfo = SrbData; DeviceExtension->FreeSrbInfo = SrbData; DeviceExtension->SrbDataCount = Count; /* Link it to the list */ while (Count > 0) { SrbData->Requests.Flink = (PLIST_ENTRY)(SrbData + 1); SrbData++; Count--; } /* Mark the last entry of the list */ SrbData--; SrbData->Requests.Flink = NULL; } /* Initialize port capabilities */ PortCapabilities = &DeviceExtension->PortCapabilities; PortCapabilities->Length = sizeof(IO_SCSI_CAPABILITIES); PortCapabilities->MaximumTransferLength = PortConfig->MaximumTransferLength; if (PortConfig->ReceiveEvent) PortCapabilities->SupportedAsynchronousEvents |= SRBEV_SCSI_ASYNC_NOTIFICATION; PortCapabilities->TaggedQueuing = DeviceExtension->SupportsTaggedQueuing; PortCapabilities->AdapterScansDown = PortConfig->AdapterScansDown; if (PortConfig->AlignmentMask > PortDeviceObject->AlignmentRequirement) PortDeviceObject->AlignmentRequirement = PortConfig->AlignmentMask; PortCapabilities->AlignmentMask = PortDeviceObject->AlignmentRequirement; if (PortCapabilities->MaximumPhysicalPages == 0) { PortCapabilities->MaximumPhysicalPages = BYTES_TO_PAGES(PortCapabilities->MaximumTransferLength); /* Apply miniport's limits */ if (PortConfig->NumberOfPhysicalBreaks < PortCapabilities->MaximumPhysicalPages) { PortCapabilities->MaximumPhysicalPages = PortConfig->NumberOfPhysicalBreaks; } } /* Deal with interrupts */ if (DeviceExtension->HwInterrupt == NULL || (PortConfig->BusInterruptLevel == 0 && PortConfig->BusInterruptVector == 0)) { /* No interrupts */ DeviceExtension->InterruptCount = 0; DPRINT1("Interrupt Count: 0\n"); UNIMPLEMENTED; /* This code path will ALWAYS crash so stop it now */ while(TRUE); } else { BOOLEAN InterruptShareable; KINTERRUPT_MODE InterruptMode[2]; ULONG InterruptVector[2], i, MappedIrq[2]; KIRQL Dirql[2], MaxDirql; KAFFINITY Affinity[2]; DeviceExtension->InterruptLevel[0] = PortConfig->BusInterruptLevel; DeviceExtension->InterruptLevel[1] = PortConfig->BusInterruptLevel2; InterruptVector[0] = PortConfig->BusInterruptVector; InterruptVector[1] = PortConfig->BusInterruptVector2; InterruptMode[0] = PortConfig->InterruptMode; InterruptMode[1] = PortConfig->InterruptMode2; DeviceExtension->InterruptCount = (PortConfig->BusInterruptLevel2 != 0 || PortConfig->BusInterruptVector2 != 0) ? 2 : 1; for (i = 0; i < DeviceExtension->InterruptCount; i++) { /* Register an interrupt handler for this device */ MappedIrq[i] = HalGetInterruptVector(PortConfig->AdapterInterfaceType, PortConfig->SystemIoBusNumber, DeviceExtension->InterruptLevel[i], InterruptVector[i], &Dirql[i], &Affinity[i]); } if (DeviceExtension->InterruptCount == 1 || Dirql[0] > Dirql[1]) MaxDirql = Dirql[0]; else MaxDirql = Dirql[1]; for (i = 0; i < DeviceExtension->InterruptCount; i++) { /* Determine IRQ sharability as usual */ if (PortConfig->AdapterInterfaceType == MicroChannel || InterruptMode[i] == LevelSensitive) { InterruptShareable = TRUE; } else { InterruptShareable = FALSE; } Status = IoConnectInterrupt(&DeviceExtension->Interrupt[i], (PKSERVICE_ROUTINE)ScsiPortIsr, DeviceExtension, &DeviceExtension->IrqLock, MappedIrq[i], Dirql[i], MaxDirql, InterruptMode[i], InterruptShareable, Affinity[i], FALSE); if (!(NT_SUCCESS(Status))) { DPRINT1("Could not connect interrupt %d\n", InterruptVector[i]); DeviceExtension->Interrupt[i] = NULL; break; } } if (!NT_SUCCESS(Status)) break; } /* Save IoAddress (from access ranges) */ if (HwInitializationData->NumberOfAccessRanges != 0) { DeviceExtension->IoAddress = ((*(PortConfig->AccessRanges))[0]).RangeStart.LowPart; DPRINT("Io Address %x\n", DeviceExtension->IoAddress); } /* Set flag that it's allowed to disconnect during this command */ DeviceExtension->Flags |= SCSI_PORT_DISCONNECT_ALLOWED; /* Initialize counter of active requests (-1 means there are none) */ DeviceExtension->ActiveRequestCounter = -1; /* Analyze what we have about DMA */ if (DeviceExtension->AdapterObject != NULL && PortConfig->Master && PortConfig->NeedPhysicalAddresses) { DeviceExtension->MapRegisters = TRUE; } else { DeviceExtension->MapRegisters = FALSE; } /* Call HwInitialize at DISPATCH_LEVEL */ KeRaiseIrql(DISPATCH_LEVEL, &OldIrql); if (!KeSynchronizeExecution(DeviceExtension->Interrupt[0], DeviceExtension->HwInitialize, DeviceExtension->MiniPortDeviceExtension)) { DPRINT1("HwInitialize() failed!\n"); KeLowerIrql(OldIrql); Status = STATUS_ADAPTER_HARDWARE_ERROR; break; } /* Check if a notification is needed */ if (DeviceExtension->InterruptData.Flags & SCSI_PORT_NOTIFICATION_NEEDED) { /* Call DPC right away, because we're already at DISPATCH_LEVEL */ ScsiPortDpcForIsr(NULL, DeviceExtension->DeviceObject, NULL, NULL); } /* Lower irql back to what it was */ KeLowerIrql(OldIrql); /* Start our timer */ IoStartTimer(PortDeviceObject); /* Initialize bus scanning information */ BusConfigSize = FIELD_OFFSET(BUSES_CONFIGURATION_INFORMATION, BusScanInfo[DeviceExtension->PortConfig->NumberOfBuses]); DeviceExtension->BusesConfig = ExAllocatePoolWithTag(PagedPool, BusConfigSize, TAG_SCSIPORT); if (!DeviceExtension->BusesConfig) { DPRINT1("Out of resources!\n"); Status = STATUS_INSUFFICIENT_RESOURCES; break; } /* Zero it */ RtlZeroMemory(DeviceExtension->BusesConfig, BusConfigSize); /* Store number of buses there */ DeviceExtension->BusesConfig->NumberOfBuses = (UCHAR)DeviceExtension->BusNum; /* Scan the adapter for devices */ SpiScanAdapter(DeviceExtension); /* Build the registry device map */ SpiBuildDeviceMap(DeviceExtension, (PUNICODE_STRING)Argument2); /* Create the dos device link */ swprintf(DosNameBuffer, L"\\??\\Scsi%lu:", SystemConfig->ScsiPortCount); RtlInitUnicodeString(&DosDeviceName, DosNameBuffer); IoCreateSymbolicLink(&DosDeviceName, &DeviceName); /* Increase the port count */ SystemConfig->ScsiPortCount++; FirstConfigCall = FALSE; /* Increase adapter number and bus number respectively */ ConfigInfo.AdapterNumber++; if (!Again) ConfigInfo.BusNumber++; DPRINT("Bus: %lu MaxBus: %lu\n", ConfigInfo.BusNumber, MaxBus); DeviceFound = TRUE; } /* Clean up the mess */ SpiCleanupAfterInit(DeviceExtension); /* Close registry keys */ if (ConfigInfo.ServiceKey != NULL) ZwClose(ConfigInfo.ServiceKey); if (ConfigInfo.DeviceKey != NULL) ZwClose(ConfigInfo.DeviceKey); if (ConfigInfo.BusKey != NULL) ZwClose(ConfigInfo.BusKey); if (ConfigInfo.AccessRanges != NULL) ExFreePool(ConfigInfo.AccessRanges); if (ConfigInfo.Parameter != NULL) ExFreePool(ConfigInfo.Parameter); DPRINT("ScsiPortInitialize() done, Status = 0x%08X, DeviceFound = %d!\n", Status, DeviceFound); return (DeviceFound == FALSE) ? Status : STATUS_SUCCESS; } static VOID SpiCleanupAfterInit(PSCSI_PORT_DEVICE_EXTENSION DeviceExtension) { PSCSI_LUN_INFO LunInfo; PVOID Ptr; ULONG Bus, Lun; /* Check if we have something to clean up */ if (DeviceExtension == NULL) return; /* Stop the timer */ IoStopTimer(DeviceExtension->DeviceObject); /* Disconnect the interrupts */ while (DeviceExtension->InterruptCount) { if (DeviceExtension->Interrupt[--DeviceExtension->InterruptCount]) IoDisconnectInterrupt(DeviceExtension->Interrupt[DeviceExtension->InterruptCount]); } /* Delete ConfigInfo */ if (DeviceExtension->BusesConfig) { for (Bus = 0; Bus < DeviceExtension->BusNum; Bus++) { if (!DeviceExtension->BusesConfig->BusScanInfo[Bus]) continue; LunInfo = DeviceExtension->BusesConfig->BusScanInfo[Bus]->LunInfo; while (LunInfo) { /* Free current, but save pointer to the next one */ Ptr = LunInfo->Next; ExFreePool(LunInfo); LunInfo = Ptr; } ExFreePool(DeviceExtension->BusesConfig->BusScanInfo[Bus]); } ExFreePool(DeviceExtension->BusesConfig); } /* Free PortConfig */ if (DeviceExtension->PortConfig) ExFreePool(DeviceExtension->PortConfig); /* Free LUNs*/ for(Lun = 0; Lun < LUS_NUMBER; Lun++) { while (DeviceExtension->LunExtensionList[Lun]) { Ptr = DeviceExtension->LunExtensionList[Lun]; DeviceExtension->LunExtensionList[Lun] = DeviceExtension->LunExtensionList[Lun]->Next; ExFreePool(Ptr); } } /* Free common buffer (if it exists) */ if (DeviceExtension->SrbExtensionBuffer != NULL && DeviceExtension->CommonBufferLength != 0) { if (!DeviceExtension->AdapterObject) { ExFreePool(DeviceExtension->SrbExtensionBuffer); } else { HalFreeCommonBuffer(DeviceExtension->AdapterObject, DeviceExtension->CommonBufferLength, DeviceExtension->PhysicalAddress, DeviceExtension->SrbExtensionBuffer, FALSE); } } /* Free SRB info */ if (DeviceExtension->SrbInfo != NULL) ExFreePool(DeviceExtension->SrbInfo); /* Unmap mapped addresses */ while (DeviceExtension->MappedAddressList != NULL) { MmUnmapIoSpace(DeviceExtension->MappedAddressList->MappedAddress, DeviceExtension->MappedAddressList->NumberOfBytes); Ptr = DeviceExtension->MappedAddressList; DeviceExtension->MappedAddressList = DeviceExtension->MappedAddressList->NextMappedAddress; ExFreePool(Ptr); } /* Finally delete the device object */ DPRINT("Deleting device %p\n", DeviceExtension->DeviceObject); IoDeleteDevice(DeviceExtension->DeviceObject); } /* * @unimplemented */ VOID NTAPI ScsiPortIoMapTransfer(IN PVOID HwDeviceExtension, IN PSCSI_REQUEST_BLOCK Srb, IN PVOID LogicalAddress, IN ULONG Length) { DPRINT1("ScsiPortIoMapTransfer()\n"); UNIMPLEMENTED; } /* * @unimplemented */ VOID NTAPI ScsiPortLogError(IN PVOID HwDeviceExtension, IN PSCSI_REQUEST_BLOCK Srb OPTIONAL, IN UCHAR PathId, IN UCHAR TargetId, IN UCHAR Lun, IN ULONG ErrorCode, IN ULONG UniqueId) { //PSCSI_PORT_DEVICE_EXTENSION DeviceExtension; DPRINT1("ScsiPortLogError() called\n"); DPRINT1("PathId: 0x%02x TargetId: 0x%02x Lun: 0x%02x ErrorCode: 0x%08lx UniqueId: 0x%08lx\n", PathId, TargetId, Lun, ErrorCode, UniqueId); //DeviceExtension = CONTAINING_RECORD(HwDeviceExtension, SCSI_PORT_DEVICE_EXTENSION, MiniPortDeviceExtension); DPRINT("ScsiPortLogError() done\n"); } /* * @implemented */ VOID NTAPI ScsiPortMoveMemory(OUT PVOID Destination, IN PVOID Source, IN ULONG Length) { RtlMoveMemory(Destination, Source, Length); } /* * @implemented */ VOID ScsiPortNotification(IN SCSI_NOTIFICATION_TYPE NotificationType, IN PVOID HwDeviceExtension, ...) { PSCSI_PORT_DEVICE_EXTENSION DeviceExtension; va_list ap; DPRINT("ScsiPortNotification() called\n"); DeviceExtension = CONTAINING_RECORD(HwDeviceExtension, SCSI_PORT_DEVICE_EXTENSION, MiniPortDeviceExtension); DPRINT("DeviceExtension %p\n", DeviceExtension); va_start(ap, HwDeviceExtension); switch (NotificationType) { case RequestComplete: { PSCSI_REQUEST_BLOCK Srb; PSCSI_REQUEST_BLOCK_INFO SrbData; Srb = (PSCSI_REQUEST_BLOCK) va_arg (ap, PSCSI_REQUEST_BLOCK); DPRINT("Notify: RequestComplete (Srb %p)\n", Srb); /* Make sure Srb is alright */ ASSERT(Srb->SrbStatus != SRB_STATUS_PENDING); ASSERT(Srb->Function != SRB_FUNCTION_EXECUTE_SCSI || Srb->SrbStatus != SRB_STATUS_SUCCESS || Srb->ScsiStatus == SCSISTAT_GOOD); if (!(Srb->SrbFlags & SRB_FLAGS_IS_ACTIVE)) { /* It's been already completed */ va_end(ap); return; } /* It's not active anymore */ Srb->SrbFlags &= ~SRB_FLAGS_IS_ACTIVE; if (Srb->Function == SRB_FUNCTION_ABORT_COMMAND) { /* TODO: Treat it specially */ ASSERT(FALSE); } else { /* Get the SRB data */ SrbData = SpiGetSrbData(DeviceExtension, Srb->PathId, Srb->TargetId, Srb->Lun, Srb->QueueTag); /* Make sure there are no CompletedRequests and there is a Srb */ ASSERT(SrbData->CompletedRequests == NULL && SrbData->Srb != NULL); /* If it's a read/write request, make sure it has data inside it */ if ((Srb->SrbStatus == SRB_STATUS_SUCCESS) && ((Srb->Cdb[0] == SCSIOP_READ) || (Srb->Cdb[0] == SCSIOP_WRITE))) { ASSERT(Srb->DataTransferLength); } SrbData->CompletedRequests = DeviceExtension->InterruptData.CompletedRequests; DeviceExtension->InterruptData.CompletedRequests = SrbData; } } break; case NextRequest: DPRINT("Notify: NextRequest\n"); DeviceExtension->InterruptData.Flags |= SCSI_PORT_NEXT_REQUEST_READY; break; case NextLuRequest: { UCHAR PathId; UCHAR TargetId; UCHAR Lun; PSCSI_PORT_LUN_EXTENSION LunExtension; PathId = (UCHAR) va_arg (ap, int); TargetId = (UCHAR) va_arg (ap, int); Lun = (UCHAR) va_arg (ap, int); DPRINT("Notify: NextLuRequest(PathId %u TargetId %u Lun %u)\n", PathId, TargetId, Lun); /* Mark it in the flags field */ DeviceExtension->InterruptData.Flags |= SCSI_PORT_NEXT_REQUEST_READY; /* Get the LUN extension */ LunExtension = SpiGetLunExtension(DeviceExtension, PathId, TargetId, Lun); /* If returned LunExtension is NULL, break out */ if (!LunExtension) break; /* This request should not be processed if */ if ((LunExtension->ReadyLun) || (LunExtension->SrbInfo.Srb)) { /* Nothing to do here */ break; } /* Add this LUN to the list */ LunExtension->ReadyLun = DeviceExtension->InterruptData.ReadyLun; DeviceExtension->InterruptData.ReadyLun = LunExtension; } break; case ResetDetected: DPRINT("Notify: ResetDetected\n"); /* Add RESET flags */ DeviceExtension->InterruptData.Flags |= SCSI_PORT_RESET | SCSI_PORT_RESET_REPORTED; break; case CallDisableInterrupts: DPRINT1("UNIMPLEMENTED SCSI Notification called: CallDisableInterrupts!\n"); break; case CallEnableInterrupts: DPRINT1("UNIMPLEMENTED SCSI Notification called: CallEnableInterrupts!\n"); break; case RequestTimerCall: DPRINT("Notify: RequestTimerCall\n"); DeviceExtension->InterruptData.Flags |= SCSI_PORT_TIMER_NEEDED; DeviceExtension->InterruptData.HwScsiTimer = (PHW_TIMER)va_arg(ap, PHW_TIMER); DeviceExtension->InterruptData.MiniportTimerValue = (ULONG)va_arg(ap, ULONG); break; case BusChangeDetected: DPRINT1("UNIMPLEMENTED SCSI Notification called: BusChangeDetected!\n"); break; default: DPRINT1 ("Unsupported notification from WMI: %lu\n", NotificationType); break; } va_end(ap); /* Request a DPC after we're done with the interrupt */ DeviceExtension->InterruptData.Flags |= SCSI_PORT_NOTIFICATION_NEEDED; } /* * @implemented */ BOOLEAN NTAPI ScsiPortValidateRange(IN PVOID HwDeviceExtension, IN INTERFACE_TYPE BusType, IN ULONG SystemIoBusNumber, IN SCSI_PHYSICAL_ADDRESS IoAddress, IN ULONG NumberOfBytes, IN BOOLEAN InIoSpace) { DPRINT("ScsiPortValidateRange()\n"); return(TRUE); } /* INTERNAL FUNCTIONS ********************************************************/ static VOID SpiResourceToConfig(IN PHW_INITIALIZATION_DATA HwInitializationData, IN PCM_FULL_RESOURCE_DESCRIPTOR ResourceDescriptor, IN PPORT_CONFIGURATION_INFORMATION PortConfig) { PACCESS_RANGE AccessRange; PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialData; ULONG RangeNumber; ULONG Index; ULONG Interrupt = 0; ULONG Dma = 0; RangeNumber = 0; /* Loop through all entries */ for (Index = 0; Index < ResourceDescriptor->PartialResourceList.Count; Index++) { PartialData = &ResourceDescriptor->PartialResourceList.PartialDescriptors[Index]; switch (PartialData->Type) { case CmResourceTypePort: /* Copy access ranges */ if (RangeNumber < HwInitializationData->NumberOfAccessRanges) { AccessRange = &((*(PortConfig->AccessRanges))[RangeNumber]); AccessRange->RangeStart = PartialData->u.Port.Start; AccessRange->RangeLength = PartialData->u.Port.Length; AccessRange->RangeInMemory = FALSE; RangeNumber++; } break; case CmResourceTypeMemory: /* Copy access ranges */ if (RangeNumber < HwInitializationData->NumberOfAccessRanges) { AccessRange = &((*(PortConfig->AccessRanges))[RangeNumber]); AccessRange->RangeStart = PartialData->u.Memory.Start; AccessRange->RangeLength = PartialData->u.Memory.Length; AccessRange->RangeInMemory = TRUE; RangeNumber++; } break; case CmResourceTypeInterrupt: if (Interrupt == 0) { /* Copy interrupt data */ PortConfig->BusInterruptLevel = PartialData->u.Interrupt.Level; PortConfig->BusInterruptVector = PartialData->u.Interrupt.Vector; /* Set interrupt mode accordingly to the resource */ if (PartialData->Flags == CM_RESOURCE_INTERRUPT_LATCHED) { PortConfig->InterruptMode = Latched; } else if (PartialData->Flags == CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE) { PortConfig->InterruptMode = LevelSensitive; } } else if (Interrupt == 1) { /* Copy interrupt data */ PortConfig->BusInterruptLevel2 = PartialData->u.Interrupt.Level; PortConfig->BusInterruptVector2 = PartialData->u.Interrupt.Vector; /* Set interrupt mode accordingly to the resource */ if (PartialData->Flags == CM_RESOURCE_INTERRUPT_LATCHED) { PortConfig->InterruptMode2 = Latched; } else if (PartialData->Flags == CM_RESOURCE_INTERRUPT_LEVEL_SENSITIVE) { PortConfig->InterruptMode2 = LevelSensitive; } } Interrupt++; break; case CmResourceTypeDma: if (Dma == 0) { PortConfig->DmaChannel = PartialData->u.Dma.Channel; PortConfig->DmaPort = PartialData->u.Dma.Port; if (PartialData->Flags & CM_RESOURCE_DMA_8) PortConfig->DmaWidth = Width8Bits; else if ((PartialData->Flags & CM_RESOURCE_DMA_16) || (PartialData->Flags & CM_RESOURCE_DMA_8_AND_16)) //??? PortConfig->DmaWidth = Width16Bits; else if (PartialData->Flags & CM_RESOURCE_DMA_32) PortConfig->DmaWidth = Width32Bits; } else if (Dma == 1) { PortConfig->DmaChannel2 = PartialData->u.Dma.Channel; PortConfig->DmaPort2 = PartialData->u.Dma.Port; if (PartialData->Flags & CM_RESOURCE_DMA_8) PortConfig->DmaWidth2 = Width8Bits; else if ((PartialData->Flags & CM_RESOURCE_DMA_16) || (PartialData->Flags & CM_RESOURCE_DMA_8_AND_16)) //??? PortConfig->DmaWidth2 = Width16Bits; else if (PartialData->Flags & CM_RESOURCE_DMA_32) PortConfig->DmaWidth2 = Width32Bits; } break; } } } static PCM_RESOURCE_LIST SpiConfigToResource(PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, PPORT_CONFIGURATION_INFORMATION PortConfig) { PCONFIGURATION_INFORMATION ConfigInfo; PCM_RESOURCE_LIST ResourceList; PCM_PARTIAL_RESOURCE_DESCRIPTOR ResourceDescriptor; PACCESS_RANGE AccessRange; ULONG ListLength = 0, i, FullSize; ULONG Interrupt, Dma; /* Get current Atdisk usage from the system */ ConfigInfo = IoGetConfigurationInformation(); if (PortConfig->AtdiskPrimaryClaimed) ConfigInfo->AtDiskPrimaryAddressClaimed = TRUE; if (PortConfig->AtdiskSecondaryClaimed) ConfigInfo->AtDiskSecondaryAddressClaimed = TRUE; /* Do we use DMA? */ if (PortConfig->DmaChannel != SP_UNINITIALIZED_VALUE || PortConfig->DmaPort != SP_UNINITIALIZED_VALUE) { Dma = 1; if (PortConfig->DmaChannel2 != SP_UNINITIALIZED_VALUE || PortConfig->DmaPort2 != SP_UNINITIALIZED_VALUE) Dma++; } else { Dma = 0; } ListLength += Dma; /* How many interrupts to we have? */ Interrupt = DeviceExtension->InterruptCount; ListLength += Interrupt; /* How many access ranges do we use? */ AccessRange = &((*(PortConfig->AccessRanges))[0]); for (i = 0; i < PortConfig->NumberOfAccessRanges; i++) { if (AccessRange->RangeLength != 0) ListLength++; AccessRange++; } /* Allocate the resource list, since we know its size now */ FullSize = sizeof(CM_RESOURCE_LIST) + (ListLength - 1) * sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR); ResourceList = (PCM_RESOURCE_LIST)ExAllocatePoolWithTag(PagedPool, FullSize, TAG_SCSIPORT); if (!ResourceList) return NULL; /* Zero it */ RtlZeroMemory(ResourceList, FullSize); /* Initialize it */ ResourceList->Count = 1; ResourceList->List[0].InterfaceType = PortConfig->AdapterInterfaceType; ResourceList->List[0].BusNumber = PortConfig->SystemIoBusNumber; ResourceList->List[0].PartialResourceList.Count = ListLength; ResourceDescriptor = ResourceList->List[0].PartialResourceList.PartialDescriptors; /* Copy access ranges array over */ for (i = 0; i < PortConfig->NumberOfAccessRanges; i++) { AccessRange = &((*(PortConfig->AccessRanges))[i]); /* If the range is empty - skip it */ if (AccessRange->RangeLength == 0) continue; if (AccessRange->RangeInMemory) { ResourceDescriptor->Type = CmResourceTypeMemory; ResourceDescriptor->Flags = CM_RESOURCE_MEMORY_READ_WRITE; } else { ResourceDescriptor->Type = CmResourceTypePort; ResourceDescriptor->Flags = CM_RESOURCE_PORT_IO; } ResourceDescriptor->ShareDisposition = CmResourceShareDeviceExclusive; ResourceDescriptor->u.Memory.Start = AccessRange->RangeStart; ResourceDescriptor->u.Memory.Length = AccessRange->RangeLength; ResourceDescriptor++; } /* If we use interrupt(s), copy them */ while (Interrupt) { ResourceDescriptor->Type = CmResourceTypeInterrupt; if (PortConfig->AdapterInterfaceType == MicroChannel || ((Interrupt == 2) ? PortConfig->InterruptMode2 : PortConfig->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 = (Interrupt == 2) ? PortConfig->BusInterruptLevel2 : PortConfig->BusInterruptLevel; ResourceDescriptor->u.Interrupt.Vector = (Interrupt == 2) ? PortConfig->BusInterruptVector2 : PortConfig->BusInterruptVector; ResourceDescriptor->u.Interrupt.Affinity = 0; ResourceDescriptor++; Interrupt--; } /* Copy DMA data */ while (Dma) { ResourceDescriptor->Type = CmResourceTypeDma; ResourceDescriptor->ShareDisposition = CmResourceShareDeviceExclusive; ResourceDescriptor->u.Dma.Channel = (Dma == 2) ? PortConfig->DmaChannel2 : PortConfig->DmaChannel; ResourceDescriptor->u.Dma.Port = (Dma == 2) ? PortConfig->DmaPort2 : PortConfig->DmaPort; ResourceDescriptor->Flags = 0; if (((Dma == 2) ? PortConfig->DmaWidth2 : PortConfig->DmaWidth) == Width8Bits) ResourceDescriptor->Flags |= CM_RESOURCE_DMA_8; else if (((Dma == 2) ? PortConfig->DmaWidth2 : PortConfig->DmaWidth) == Width16Bits) ResourceDescriptor->Flags |= CM_RESOURCE_DMA_16; else ResourceDescriptor->Flags |= CM_RESOURCE_DMA_32; if (((Dma == 2) ? PortConfig->DmaChannel2 : PortConfig->DmaChannel) == SP_UNINITIALIZED_VALUE) ResourceDescriptor->u.Dma.Channel = 0; if (((Dma == 2) ? PortConfig->DmaPort2 : PortConfig->DmaPort) == SP_UNINITIALIZED_VALUE) ResourceDescriptor->u.Dma.Port = 0; ResourceDescriptor++; Dma--; } return ResourceList; } static BOOLEAN SpiGetPciConfigData(IN PDRIVER_OBJECT DriverObject, IN PDEVICE_OBJECT DeviceObject, IN struct _HW_INITIALIZATION_DATA *HwInitializationData, IN OUT PPORT_CONFIGURATION_INFORMATION PortConfig, IN PUNICODE_STRING RegistryPath, IN ULONG BusNumber, IN OUT PPCI_SLOT_NUMBER NextSlotNumber) { PCI_COMMON_CONFIG PciConfig; PCI_SLOT_NUMBER SlotNumber; ULONG DataSize; ULONG DeviceNumber; ULONG FunctionNumber; CHAR VendorIdString[8]; CHAR DeviceIdString[8]; UNICODE_STRING UnicodeStr; PCM_RESOURCE_LIST ResourceList = NULL; NTSTATUS Status; DPRINT ("SpiGetPciConfiguration() called\n"); SlotNumber.u.AsULONG = 0; /* Loop through all devices */ for (DeviceNumber = NextSlotNumber->u.bits.DeviceNumber; DeviceNumber < PCI_MAX_DEVICES; DeviceNumber++) { SlotNumber.u.bits.DeviceNumber = DeviceNumber; /* Loop through all functions */ for (FunctionNumber = NextSlotNumber->u.bits.FunctionNumber; FunctionNumber < PCI_MAX_FUNCTION; FunctionNumber++) { SlotNumber.u.bits.FunctionNumber = FunctionNumber; /* Get PCI config bytes */ DataSize = HalGetBusData(PCIConfiguration, BusNumber, SlotNumber.u.AsULONG, &PciConfig, sizeof(ULONG)); /* If result of HalGetBusData is 0, then the bus is wrong */ if (DataSize == 0) return FALSE; /* Check if result is PCI_INVALID_VENDORID or too small */ if ((DataSize < sizeof(ULONG)) || (PciConfig.VendorID == PCI_INVALID_VENDORID)) { /* Continue to try the next function */ continue; } sprintf (VendorIdString, "%04hx", PciConfig.VendorID); sprintf (DeviceIdString, "%04hx", PciConfig.DeviceID); if (_strnicmp(VendorIdString, HwInitializationData->VendorId, HwInitializationData->VendorIdLength) || _strnicmp(DeviceIdString, HwInitializationData->DeviceId, HwInitializationData->DeviceIdLength)) { /* It is not our device */ continue; } DPRINT("Found device 0x%04hx 0x%04hx at %1lu %2lu %1lu\n", PciConfig.VendorID, PciConfig.DeviceID, BusNumber, SlotNumber.u.bits.DeviceNumber, SlotNumber.u.bits.FunctionNumber); RtlInitUnicodeString(&UnicodeStr, L"ScsiAdapter"); Status = HalAssignSlotResources(RegistryPath, &UnicodeStr, DriverObject, DeviceObject, PCIBus, BusNumber, SlotNumber.u.AsULONG, &ResourceList); if (!NT_SUCCESS(Status)) break; /* Create configuration information */ SpiResourceToConfig(HwInitializationData, ResourceList->List, PortConfig); /* Free the resource list */ ExFreePool(ResourceList); /* Set dev & fn numbers */ NextSlotNumber->u.bits.DeviceNumber = DeviceNumber; NextSlotNumber->u.bits.FunctionNumber = FunctionNumber + 1; /* Save the slot number */ PortConfig->SlotNumber = SlotNumber.u.AsULONG; return TRUE; } NextSlotNumber->u.bits.FunctionNumber = 0; } NextSlotNumber->u.bits.DeviceNumber = 0; DPRINT ("No device found\n"); return FALSE; } /********************************************************************** * NAME INTERNAL * ScsiPortCreateClose * * DESCRIPTION * Answer requests for Create/Close calls: a null operation. * * RUN LEVEL * PASSIVE_LEVEL * * ARGUMENTS * DeviceObject * Pointer to a device object. * * Irp * Pointer to an IRP. * * RETURN VALUE * Status. */ static NTSTATUS NTAPI ScsiPortCreateClose(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { DPRINT("ScsiPortCreateClose()\n"); Irp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } static NTSTATUS SpiHandleAttachRelease(PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, PIRP Irp) { PSCSI_LUN_INFO LunInfo; PIO_STACK_LOCATION IrpStack; PDEVICE_OBJECT DeviceObject; PSCSI_REQUEST_BLOCK Srb; KIRQL Irql; /* Get pointer to the SRB */ IrpStack = IoGetCurrentIrpStackLocation(Irp); Srb = (PSCSI_REQUEST_BLOCK)IrpStack->Parameters.Others.Argument1; /* Check if PathId matches number of buses */ if (DeviceExtension->BusesConfig == NULL || DeviceExtension->BusesConfig->NumberOfBuses <= Srb->PathId) { Srb->SrbStatus = SRB_STATUS_NO_DEVICE; return STATUS_DEVICE_DOES_NOT_EXIST; } /* Get pointer to LunInfo */ LunInfo = DeviceExtension->BusesConfig->BusScanInfo[Srb->PathId]->LunInfo; /* Find matching LunInfo */ while (LunInfo) { if (LunInfo->PathId == Srb->PathId && LunInfo->TargetId == Srb->TargetId && LunInfo->Lun == Srb->Lun) { break; } LunInfo = LunInfo->Next; } /* If we couldn't find it - exit */ if (LunInfo == NULL) return STATUS_DEVICE_DOES_NOT_EXIST; /* Get spinlock */ KeAcquireSpinLock(&DeviceExtension->SpinLock, &Irql); /* Release, if asked */ if (Srb->Function == SRB_FUNCTION_RELEASE_DEVICE) { LunInfo->DeviceClaimed = FALSE; KeReleaseSpinLock(&DeviceExtension->SpinLock, Irql); Srb->SrbStatus = SRB_STATUS_SUCCESS; return STATUS_SUCCESS; } /* Attach, if not already claimed */ if (LunInfo->DeviceClaimed) { KeReleaseSpinLock(&DeviceExtension->SpinLock, Irql); Srb->SrbStatus = SRB_STATUS_BUSY; return STATUS_DEVICE_BUSY; } /* Save the device object */ DeviceObject = LunInfo->DeviceObject; if (Srb->Function == SRB_FUNCTION_CLAIM_DEVICE) LunInfo->DeviceClaimed = TRUE; if (Srb->Function == SRB_FUNCTION_ATTACH_DEVICE) LunInfo->DeviceObject = Srb->DataBuffer; Srb->DataBuffer = DeviceObject; KeReleaseSpinLock(&DeviceExtension->SpinLock, Irql); Srb->SrbStatus = SRB_STATUS_SUCCESS; return STATUS_SUCCESS; } /********************************************************************** * NAME INTERNAL * ScsiPortDispatchScsi * * DESCRIPTION * Answer requests for SCSI calls * * RUN LEVEL * PASSIVE_LEVEL * * ARGUMENTS * Standard dispatch arguments * * RETURNS * NTSTATUS */ static NTSTATUS NTAPI ScsiPortDispatchScsi(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { PSCSI_PORT_DEVICE_EXTENSION DeviceExtension; PSCSI_PORT_LUN_EXTENSION LunExtension; PIO_STACK_LOCATION Stack; PSCSI_REQUEST_BLOCK Srb; KIRQL Irql; NTSTATUS Status = STATUS_SUCCESS; PIRP NextIrp, IrpList; PKDEVICE_QUEUE_ENTRY Entry; DPRINT("ScsiPortDispatchScsi(DeviceObject %p Irp %p)\n", DeviceObject, Irp); DeviceExtension = DeviceObject->DeviceExtension; Stack = IoGetCurrentIrpStackLocation(Irp); Srb = Stack->Parameters.Scsi.Srb; if (Srb == NULL) { DPRINT1("ScsiPortDispatchScsi() called with Srb = NULL!\n"); Status = STATUS_UNSUCCESSFUL; Irp->IoStatus.Status = Status; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return(Status); } DPRINT("Srb: %p\n", Srb); DPRINT("Srb->Function: %lu\n", Srb->Function); DPRINT("PathId: %lu TargetId: %lu Lun: %lu\n", Srb->PathId, Srb->TargetId, Srb->Lun); LunExtension = SpiGetLunExtension(DeviceExtension, Srb->PathId, Srb->TargetId, Srb->Lun); if (LunExtension == NULL) { DPRINT("ScsiPortDispatchScsi() called with an invalid LUN\n"); Status = STATUS_NO_SUCH_DEVICE; Srb->SrbStatus = SRB_STATUS_NO_DEVICE; Irp->IoStatus.Status = Status; Irp->IoStatus.Information = 0; IoCompleteRequest(Irp, IO_NO_INCREMENT); return(Status); } switch (Srb->Function) { case SRB_FUNCTION_SHUTDOWN: case SRB_FUNCTION_FLUSH: DPRINT (" SRB_FUNCTION_SHUTDOWN or FLUSH\n"); if (DeviceExtension->CachesData == FALSE) { /* All success here */ Srb->SrbStatus = SRB_STATUS_SUCCESS; Irp->IoStatus.Status = STATUS_SUCCESS; IoCompleteRequest(Irp, IO_NO_INCREMENT); return STATUS_SUCCESS; } /* Fall through to a usual execute operation */ case SRB_FUNCTION_EXECUTE_SCSI: case SRB_FUNCTION_IO_CONTROL: DPRINT(" SRB_FUNCTION_EXECUTE_SCSI or SRB_FUNCTION_IO_CONTROL\n"); /* Mark IRP as pending in all cases */ IoMarkIrpPending(Irp); if (Srb->SrbFlags & SRB_FLAGS_BYPASS_FROZEN_QUEUE) { /* Start IO directly */ IoStartPacket(DeviceObject, Irp, NULL, NULL); } else { KIRQL oldIrql; /* We need to be at DISPATCH_LEVEL */ KeRaiseIrql (DISPATCH_LEVEL, &oldIrql); /* Insert IRP into the queue */ if (!KeInsertByKeyDeviceQueue(&LunExtension->DeviceQueue, &Irp->Tail.Overlay.DeviceQueueEntry, Srb->QueueSortKey)) { /* It means the queue is empty, and we just start this request */ IoStartPacket(DeviceObject, Irp, NULL, NULL); } /* Back to the old IRQL */ KeLowerIrql (oldIrql); } return STATUS_PENDING; case SRB_FUNCTION_CLAIM_DEVICE: case SRB_FUNCTION_ATTACH_DEVICE: DPRINT (" SRB_FUNCTION_CLAIM_DEVICE or ATTACH\n"); /* Reference device object and keep the device object */ Status = SpiHandleAttachRelease(DeviceExtension, Irp); break; case SRB_FUNCTION_RELEASE_DEVICE: DPRINT (" SRB_FUNCTION_RELEASE_DEVICE\n"); /* Dereference device object and clear the device object */ Status = SpiHandleAttachRelease(DeviceExtension, Irp); break; case SRB_FUNCTION_RELEASE_QUEUE: DPRINT(" SRB_FUNCTION_RELEASE_QUEUE\n"); /* Guard with the spinlock */ KeAcquireSpinLock(&DeviceExtension->SpinLock, &Irql); if (!(LunExtension->Flags & LUNEX_FROZEN_QUEUE)) { DPRINT("Queue is not frozen really\n"); KeReleaseSpinLock(&DeviceExtension->SpinLock, Irql); Srb->SrbStatus = SRB_STATUS_SUCCESS; Status = STATUS_SUCCESS; break; } /* Unfreeze the queue */ LunExtension->Flags &= ~LUNEX_FROZEN_QUEUE; if (LunExtension->SrbInfo.Srb == NULL) { /* Get next logical unit request */ SpiGetNextRequestFromLun(DeviceExtension, LunExtension); /* SpiGetNextRequestFromLun() releases the spinlock */ KeLowerIrql(Irql); } else { DPRINT("The queue has active request\n"); KeReleaseSpinLock(&DeviceExtension->SpinLock, Irql); } Srb->SrbStatus = SRB_STATUS_SUCCESS; Status = STATUS_SUCCESS; break; case SRB_FUNCTION_FLUSH_QUEUE: DPRINT(" SRB_FUNCTION_FLUSH_QUEUE\n"); /* Guard with the spinlock */ KeAcquireSpinLock(&DeviceExtension->SpinLock, &Irql); if (!(LunExtension->Flags & LUNEX_FROZEN_QUEUE)) { DPRINT("Queue is not frozen really\n"); KeReleaseSpinLock(&DeviceExtension->SpinLock, Irql); Status = STATUS_INVALID_DEVICE_REQUEST; break; } /* Make sure there is no active request */ ASSERT(LunExtension->SrbInfo.Srb == NULL); /* Compile a list from the device queue */ IrpList = NULL; while ((Entry = KeRemoveDeviceQueue(&LunExtension->DeviceQueue)) != NULL) { NextIrp = CONTAINING_RECORD(Entry, IRP, Tail.Overlay.DeviceQueueEntry); /* Get the Srb */ Stack = IoGetCurrentIrpStackLocation(NextIrp); Srb = Stack->Parameters.Scsi.Srb; /* Set statuse */ Srb->SrbStatus = SRB_STATUS_REQUEST_FLUSHED; NextIrp->IoStatus.Status = STATUS_UNSUCCESSFUL; /* Add then to the list */ NextIrp->Tail.Overlay.ListEntry.Flink = (PLIST_ENTRY)IrpList; IrpList = NextIrp; } /* Unfreeze the queue */ LunExtension->Flags &= ~LUNEX_FROZEN_QUEUE; /* Release the spinlock */ KeReleaseSpinLock(&DeviceExtension->SpinLock, Irql); /* Complete those requests */ while (IrpList) { NextIrp = IrpList; IrpList = (PIRP)NextIrp->Tail.Overlay.ListEntry.Flink; IoCompleteRequest(NextIrp, 0); } Status = STATUS_SUCCESS; break; default: DPRINT1("SRB function not implemented (Function %lu)\n", Srb->Function); Status = STATUS_NOT_IMPLEMENTED; break; } Irp->IoStatus.Status = Status; IoCompleteRequest(Irp, IO_NO_INCREMENT); return(Status); } /********************************************************************** * NAME INTERNAL * ScsiPortDeviceControl * * DESCRIPTION * Answer requests for device control calls * * RUN LEVEL * PASSIVE_LEVEL * * ARGUMENTS * Standard dispatch arguments * * RETURNS * NTSTATUS */ static NTSTATUS NTAPI ScsiPortDeviceControl(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { PIO_STACK_LOCATION Stack; PSCSI_PORT_DEVICE_EXTENSION DeviceExtension; PDUMP_POINTERS DumpPointers; NTSTATUS Status; DPRINT("ScsiPortDeviceControl()\n"); Irp->IoStatus.Information = 0; Stack = IoGetCurrentIrpStackLocation(Irp); DeviceExtension = DeviceObject->DeviceExtension; switch (Stack->Parameters.DeviceIoControl.IoControlCode) { case IOCTL_SCSI_GET_DUMP_POINTERS: DPRINT(" IOCTL_SCSI_GET_DUMP_POINTERS\n"); if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(DUMP_POINTERS)) { Status = STATUS_BUFFER_OVERFLOW; Irp->IoStatus.Information = sizeof(DUMP_POINTERS); break; } DumpPointers = Irp->AssociatedIrp.SystemBuffer; DumpPointers->DeviceObject = DeviceObject; /* More data.. ? */ Status = STATUS_SUCCESS; Irp->IoStatus.Information = sizeof(DUMP_POINTERS); break; case IOCTL_SCSI_GET_CAPABILITIES: DPRINT(" IOCTL_SCSI_GET_CAPABILITIES\n"); if (Stack->Parameters.DeviceIoControl.OutputBufferLength == sizeof(PVOID)) { *((PVOID *)Irp->AssociatedIrp.SystemBuffer) = &DeviceExtension->PortCapabilities; Irp->IoStatus.Information = sizeof(PVOID); Status = STATUS_SUCCESS; break; } if (Stack->Parameters.DeviceIoControl.OutputBufferLength < sizeof(IO_SCSI_CAPABILITIES)) { Status = STATUS_BUFFER_TOO_SMALL; break; } RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer, &DeviceExtension->PortCapabilities, sizeof(IO_SCSI_CAPABILITIES)); Irp->IoStatus.Information = sizeof(IO_SCSI_CAPABILITIES); Status = STATUS_SUCCESS; break; case IOCTL_SCSI_GET_INQUIRY_DATA: DPRINT(" IOCTL_SCSI_GET_INQUIRY_DATA\n"); /* Copy inquiry data to the port device extension */ Status = SpiGetInquiryData(DeviceExtension, Irp); break; case IOCTL_SCSI_MINIPORT: DPRINT1("IOCTL_SCSI_MINIPORT unimplemented!\n"); Status = STATUS_NOT_IMPLEMENTED; break; case IOCTL_SCSI_PASS_THROUGH: DPRINT1("IOCTL_SCSI_PASS_THROUGH unimplemented!\n"); Status = STATUS_NOT_IMPLEMENTED; break; default: if (DEVICE_TYPE_FROM_CTL_CODE(Stack->Parameters.DeviceIoControl.IoControlCode) == MOUNTDEVCONTROLTYPE) { switch (Stack->Parameters.DeviceIoControl.IoControlCode) { case IOCTL_MOUNTDEV_QUERY_DEVICE_NAME: DPRINT1("Got unexpected IOCTL_MOUNTDEV_QUERY_DEVICE_NAME\n"); break; case IOCTL_MOUNTDEV_QUERY_UNIQUE_ID: DPRINT1("Got unexpected IOCTL_MOUNTDEV_QUERY_UNIQUE_ID\n"); break; default: DPRINT1(" got ioctl intended for the mount manager: 0x%lX\n", Stack->Parameters.DeviceIoControl.IoControlCode); break; } } else { DPRINT1(" unknown ioctl code: 0x%lX\n", Stack->Parameters.DeviceIoControl.IoControlCode); } Status = STATUS_NOT_IMPLEMENTED; break; } /* Complete the request with the given status */ Irp->IoStatus.Status = Status; IoCompleteRequest(Irp, IO_NO_INCREMENT); return Status; } static VOID NTAPI ScsiPortStartIo(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp) { PSCSI_PORT_DEVICE_EXTENSION DeviceExtension; PSCSI_PORT_LUN_EXTENSION LunExtension; PIO_STACK_LOCATION IrpStack; PSCSI_REQUEST_BLOCK Srb; PSCSI_REQUEST_BLOCK_INFO SrbInfo; LONG CounterResult; NTSTATUS Status; DPRINT("ScsiPortStartIo() called!\n"); DeviceExtension = DeviceObject->DeviceExtension; IrpStack = IoGetCurrentIrpStackLocation(Irp); DPRINT("DeviceExtension %p\n", DeviceExtension); Srb = IrpStack->Parameters.Scsi.Srb; /* Apply "default" flags */ Srb->SrbFlags |= DeviceExtension->SrbFlags; /* Get LUN extension */ LunExtension = SpiGetLunExtension(DeviceExtension, Srb->PathId, Srb->TargetId, Srb->Lun); if (DeviceExtension->NeedSrbDataAlloc || DeviceExtension->NeedSrbExtensionAlloc) { /* Allocate them */ SrbInfo = SpiAllocateSrbStructures(DeviceExtension, LunExtension, Srb); /* Couldn't alloc one or both data structures, return */ if (SrbInfo == NULL) { /* We have to call IoStartNextPacket, because this request was not started */ if (LunExtension->Flags & LUNEX_REQUEST_PENDING) IoStartNextPacket(DeviceObject, FALSE); return; } } else { /* No allocations are needed */ SrbInfo = &LunExtension->SrbInfo; Srb->SrbExtension = NULL; Srb->QueueTag = SP_UNTAGGED; } /* Increase sequence number of SRB */ if (!SrbInfo->SequenceNumber) { /* Increase global sequence number */ DeviceExtension->SequenceNumber++; /* Assign it */ SrbInfo->SequenceNumber = DeviceExtension->SequenceNumber; } /* Check some special SRBs */ if (Srb->Function == SRB_FUNCTION_ABORT_COMMAND) { /* Some special handling */ DPRINT1("Abort command! Unimplemented now\n"); } else { SrbInfo->Srb = Srb; } if (Srb->SrbFlags & SRB_FLAGS_UNSPECIFIED_DIRECTION) { // Store the MDL virtual address in SrbInfo structure SrbInfo->DataOffset = MmGetMdlVirtualAddress(Irp->MdlAddress); if (DeviceExtension->MapBuffers) { /* Calculate offset within DataBuffer */ SrbInfo->DataOffset = MmGetSystemAddressForMdl(Irp->MdlAddress); Srb->DataBuffer = SrbInfo->DataOffset + (ULONG)((PUCHAR)Srb->DataBuffer - (PUCHAR)MmGetMdlVirtualAddress(Irp->MdlAddress)); } if (DeviceExtension->AdapterObject) { /* Flush buffers */ KeFlushIoBuffers(Irp->MdlAddress, Srb->SrbFlags & SRB_FLAGS_DATA_IN ? TRUE : FALSE, TRUE); } if (DeviceExtension->MapRegisters) { /* Calculate number of needed map registers */ SrbInfo->NumberOfMapRegisters = ADDRESS_AND_SIZE_TO_SPAN_PAGES( Srb->DataBuffer, Srb->DataTransferLength); /* Allocate adapter channel */ Status = IoAllocateAdapterChannel(DeviceExtension->AdapterObject, DeviceExtension->DeviceObject, SrbInfo->NumberOfMapRegisters, SpiAdapterControl, SrbInfo); if (!NT_SUCCESS(Status)) { DPRINT1("IoAllocateAdapterChannel() failed!\n"); Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST; ScsiPortNotification(RequestComplete, DeviceExtension + 1, Srb); ScsiPortNotification(NextRequest, DeviceExtension + 1); /* Request DPC for that work */ IoRequestDpc(DeviceExtension->DeviceObject, NULL, NULL); } /* Control goes to SpiAdapterControl */ return; } } /* Increase active request counter */ CounterResult = InterlockedIncrement(&DeviceExtension->ActiveRequestCounter); if (CounterResult == 0 && DeviceExtension->AdapterObject != NULL && !DeviceExtension->MapRegisters) { IoAllocateAdapterChannel( DeviceExtension->AdapterObject, DeviceObject, DeviceExtension->PortCapabilities.MaximumPhysicalPages, ScsiPortAllocateAdapterChannel, LunExtension ); return; } KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock); if (!KeSynchronizeExecution(DeviceExtension->Interrupt[0], ScsiPortStartPacket, DeviceObject)) { DPRINT("Synchronization failed!\n"); Irp->IoStatus.Status = STATUS_UNSUCCESSFUL; Irp->IoStatus.Information = 0; KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock); IoCompleteRequest(Irp, IO_NO_INCREMENT); } else { /* Release the spinlock only */ KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock); } DPRINT("ScsiPortStartIo() done\n"); } static BOOLEAN NTAPI ScsiPortStartPacket(IN OUT PVOID Context) { PSCSI_PORT_DEVICE_EXTENSION DeviceExtension; PIO_STACK_LOCATION IrpStack; PSCSI_REQUEST_BLOCK Srb; PDEVICE_OBJECT DeviceObject = (PDEVICE_OBJECT)Context; PSCSI_PORT_LUN_EXTENSION LunExtension; PSCSI_REQUEST_BLOCK_INFO SrbInfo; BOOLEAN Result; BOOLEAN StartTimer; DPRINT("ScsiPortStartPacket() called\n"); DeviceExtension = (PSCSI_PORT_DEVICE_EXTENSION)DeviceObject->DeviceExtension; IrpStack = IoGetCurrentIrpStackLocation(DeviceObject->CurrentIrp); Srb = IrpStack->Parameters.Scsi.Srb; /* Get LUN extension */ LunExtension = SpiGetLunExtension(DeviceExtension, Srb->PathId, Srb->TargetId, Srb->Lun); /* Check if we are in a reset state */ if (DeviceExtension->InterruptData.Flags & SCSI_PORT_RESET) { /* Mark the we've got requests while being in the reset state */ DeviceExtension->InterruptData.Flags |= SCSI_PORT_RESET_REQUEST; return TRUE; } /* Set the time out value */ DeviceExtension->TimerCount = Srb->TimeOutValue; /* We are busy */ DeviceExtension->Flags |= SCSI_PORT_DEVICE_BUSY; if (LunExtension->RequestTimeout != -1) { /* Timer already active */ StartTimer = FALSE; } else { /* It hasn't been initialized yet */ LunExtension->RequestTimeout = Srb->TimeOutValue; StartTimer = TRUE; } if (Srb->SrbFlags & SRB_FLAGS_BYPASS_FROZEN_QUEUE) { /* Handle bypass-requests */ /* Is this an abort request? */ if (Srb->Function == SRB_FUNCTION_ABORT_COMMAND) { /* Get pointer to SRB info structure */ SrbInfo = SpiGetSrbData(DeviceExtension, Srb->PathId, Srb->TargetId, Srb->Lun, Srb->QueueTag); /* Check if the request is still "active" */ if (SrbInfo == NULL || SrbInfo->Srb == NULL || !(SrbInfo->Srb->SrbFlags & SRB_FLAGS_IS_ACTIVE)) { /* It's not, mark it as active then */ Srb->SrbFlags |= SRB_FLAGS_IS_ACTIVE; if (StartTimer) LunExtension->RequestTimeout = -1; DPRINT("Request has been already completed, but abort request came\n"); Srb->SrbStatus = SRB_STATUS_ABORT_FAILED; /* Notify about request complete */ ScsiPortNotification(RequestComplete, DeviceExtension->MiniPortDeviceExtension, Srb); /* and about readiness for the next request */ ScsiPortNotification(NextRequest, DeviceExtension->MiniPortDeviceExtension); /* They might ask for some work, so queue the DPC for them */ IoRequestDpc(DeviceExtension->DeviceObject, NULL, NULL); /* We're done in this branch */ return TRUE; } } else { /* Add number of queued requests */ LunExtension->QueueCount++; } /* Bypass requests don't need request sense */ LunExtension->Flags &= ~LUNEX_NEED_REQUEST_SENSE; /* Is disconnect disabled for this request? */ if (Srb->SrbFlags & SRB_FLAGS_DISABLE_DISCONNECT) { /* Set the corresponding flag */ DeviceExtension->Flags &= ~SCSI_PORT_DISCONNECT_ALLOWED; } /* Transfer timeout value from Srb to Lun */ LunExtension->RequestTimeout = Srb->TimeOutValue; } else { if (Srb->SrbFlags & SRB_FLAGS_DISABLE_DISCONNECT) { /* It's a disconnect, so no more requests can go */ DeviceExtension->Flags &= ~SCSI_PORT_DISCONNECT_ALLOWED; } LunExtension->Flags |= SCSI_PORT_LU_ACTIVE; /* Increment queue count */ LunExtension->QueueCount++; /* If it's tagged - special thing */ if (Srb->QueueTag != SP_UNTAGGED) { SrbInfo = &DeviceExtension->SrbInfo[Srb->QueueTag - 1]; /* Chek for consistency */ ASSERT(SrbInfo->Requests.Blink == NULL); /* Insert it into the list of requests */ InsertTailList(&LunExtension->SrbInfo.Requests, &SrbInfo->Requests); } } /* Mark this Srb active */ Srb->SrbFlags |= SRB_FLAGS_IS_ACTIVE; /* Call HwStartIo routine */ Result = DeviceExtension->HwStartIo(&DeviceExtension->MiniPortDeviceExtension, Srb); /* If notification is needed, then request a DPC */ if (DeviceExtension->InterruptData.Flags & SCSI_PORT_NOTIFICATION_NEEDED) IoRequestDpc(DeviceExtension->DeviceObject, NULL, NULL); return Result; } IO_ALLOCATION_ACTION NTAPI SpiAdapterControl(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID MapRegisterBase, PVOID Context) { PSCSI_REQUEST_BLOCK Srb; PSCSI_SG_ADDRESS ScatterGatherList; KIRQL CurrentIrql; PIO_STACK_LOCATION IrpStack; ULONG TotalLength = 0; PSCSI_REQUEST_BLOCK_INFO SrbInfo; PSCSI_PORT_DEVICE_EXTENSION DeviceExtension; PUCHAR DataVA; BOOLEAN WriteToDevice; /* Get pointers to SrbInfo and DeviceExtension */ SrbInfo = (PSCSI_REQUEST_BLOCK_INFO)Context; DeviceExtension = DeviceObject->DeviceExtension; /* Get pointer to SRB */ IrpStack = IoGetCurrentIrpStackLocation(Irp); Srb = (PSCSI_REQUEST_BLOCK)IrpStack->Parameters.Others.Argument1; /* Depending on the map registers number, we allocate either from NonPagedPool, or from our static list */ if (SrbInfo->NumberOfMapRegisters > MAX_SG_LIST) { SrbInfo->ScatterGather = ExAllocatePoolWithTag( NonPagedPool, SrbInfo->NumberOfMapRegisters * sizeof(SCSI_SG_ADDRESS), TAG_SCSIPORT); if (SrbInfo->ScatterGather == NULL) ASSERT(FALSE); Srb->SrbFlags |= SRB_FLAGS_SGLIST_FROM_POOL; } else { SrbInfo->ScatterGather = SrbInfo->ScatterGatherList; } /* Use chosen SG list source */ ScatterGatherList = SrbInfo->ScatterGather; /* Save map registers base */ SrbInfo->BaseOfMapRegister = MapRegisterBase; /* Determine WriteToDevice flag */ WriteToDevice = Srb->SrbFlags & SRB_FLAGS_DATA_OUT ? TRUE : FALSE; /* Get virtual address of the data buffer */ DataVA = (PUCHAR)MmGetMdlVirtualAddress(Irp->MdlAddress) + ((PCHAR)Srb->DataBuffer - SrbInfo->DataOffset); /* Build the actual SG list */ while (TotalLength < Srb->DataTransferLength) { if (!ScatterGatherList) break; ScatterGatherList->Length = Srb->DataTransferLength - TotalLength; ScatterGatherList->PhysicalAddress = IoMapTransfer(DeviceExtension->AdapterObject, Irp->MdlAddress, MapRegisterBase, DataVA + TotalLength, &ScatterGatherList->Length, WriteToDevice); TotalLength += ScatterGatherList->Length; ScatterGatherList++; } /* Schedule an active request */ InterlockedIncrement(&DeviceExtension->ActiveRequestCounter ); KeAcquireSpinLock(&DeviceExtension->SpinLock, &CurrentIrql); KeSynchronizeExecution(DeviceExtension->Interrupt[0], ScsiPortStartPacket, DeviceObject); KeReleaseSpinLock(&DeviceExtension->SpinLock, CurrentIrql); return DeallocateObjectKeepRegisters; } static PSCSI_PORT_LUN_EXTENSION SpiAllocateLunExtension(IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension) { PSCSI_PORT_LUN_EXTENSION LunExtension; ULONG LunExtensionSize; DPRINT("SpiAllocateLunExtension(%p)\n", DeviceExtension); /* Round LunExtensionSize first to the sizeof LONGLONG */ LunExtensionSize = (DeviceExtension->LunExtensionSize + sizeof(LONGLONG) - 1) & ~(sizeof(LONGLONG) - 1); LunExtensionSize += sizeof(SCSI_PORT_LUN_EXTENSION); DPRINT("LunExtensionSize %lu\n", LunExtensionSize); LunExtension = ExAllocatePoolWithTag(NonPagedPool, LunExtensionSize, TAG_SCSIPORT); if (LunExtension == NULL) { DPRINT1("Out of resources!\n"); return NULL; } /* Zero everything */ RtlZeroMemory(LunExtension, LunExtensionSize); /* Initialize a list of requests */ InitializeListHead(&LunExtension->SrbInfo.Requests); /* Initialize timeout counter */ LunExtension->RequestTimeout = -1; /* Set maximum queue size */ LunExtension->MaxQueueCount = 256; /* Initialize request queue */ KeInitializeDeviceQueue(&LunExtension->DeviceQueue); return LunExtension; } static PSCSI_PORT_LUN_EXTENSION SpiGetLunExtension (IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, IN UCHAR PathId, IN UCHAR TargetId, IN UCHAR Lun) { PSCSI_PORT_LUN_EXTENSION LunExtension; DPRINT("SpiGetLunExtension(%p %u %u %u) called\n", DeviceExtension, PathId, TargetId, Lun); /* Get appropriate list */ LunExtension = DeviceExtension->LunExtensionList[(TargetId + Lun) % LUS_NUMBER]; /* Iterate it until we find what we need */ while (LunExtension) { if (LunExtension->TargetId == TargetId && LunExtension->Lun == Lun && LunExtension->PathId == PathId) { /* All matches, return */ return LunExtension; } /* Advance to the next item */ LunExtension = LunExtension->Next; } /* We did not find anything */ DPRINT("Nothing found\n"); return NULL; } static PSCSI_REQUEST_BLOCK_INFO SpiAllocateSrbStructures(PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, PSCSI_PORT_LUN_EXTENSION LunExtension, PSCSI_REQUEST_BLOCK Srb) { PCHAR SrbExtension; PSCSI_REQUEST_BLOCK_INFO SrbInfo; /* Spinlock must be held while this function executes */ KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock); /* Allocate SRB data structure */ if (DeviceExtension->NeedSrbDataAlloc) { /* Treat the abort request in a special way */ if (Srb->Function == SRB_FUNCTION_ABORT_COMMAND) { SrbInfo = SpiGetSrbData(DeviceExtension, Srb->PathId, Srb->TargetId, Srb->Lun, Srb->QueueTag); } else if (Srb->SrbFlags & (SRB_FLAGS_QUEUE_ACTION_ENABLE | SRB_FLAGS_NO_QUEUE_FREEZE) && !(Srb->SrbFlags & SRB_FLAGS_DISABLE_DISCONNECT) ) { /* Do not process tagged commands if need request sense is set */ if (LunExtension->Flags & LUNEX_NEED_REQUEST_SENSE) { ASSERT(!(LunExtension->Flags & LUNEX_REQUEST_PENDING)); LunExtension->PendingRequest = Srb->OriginalRequest; LunExtension->Flags |= LUNEX_REQUEST_PENDING | SCSI_PORT_LU_ACTIVE; /* Release the spinlock and return */ KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock); return NULL; } ASSERT(LunExtension->SrbInfo.Srb == NULL); SrbInfo = DeviceExtension->FreeSrbInfo; if (SrbInfo == NULL) { /* No SRB structures left in the list. We have to leave and wait while we are called again */ DeviceExtension->Flags |= SCSI_PORT_REQUEST_PENDING; KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock); return NULL; } DeviceExtension->FreeSrbInfo = (PSCSI_REQUEST_BLOCK_INFO)SrbInfo->Requests.Flink; /* QueueTag must never be 0, so +1 to it */ Srb->QueueTag = (UCHAR)(SrbInfo - DeviceExtension->SrbInfo) + 1; } else { /* Usual untagged command */ if ( (!IsListEmpty(&LunExtension->SrbInfo.Requests) || LunExtension->Flags & LUNEX_NEED_REQUEST_SENSE) && !(Srb->SrbFlags & SRB_FLAGS_BYPASS_FROZEN_QUEUE) ) { /* Mark it as pending and leave */ ASSERT(!(LunExtension->Flags & LUNEX_REQUEST_PENDING)); LunExtension->Flags |= LUNEX_REQUEST_PENDING | SCSI_PORT_LU_ACTIVE; LunExtension->PendingRequest = Srb->OriginalRequest; KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock); return(NULL); } Srb->QueueTag = SP_UNTAGGED; SrbInfo = &LunExtension->SrbInfo; } } else { Srb->QueueTag = SP_UNTAGGED; SrbInfo = &LunExtension->SrbInfo; } /* Allocate SRB extension structure */ if (DeviceExtension->NeedSrbExtensionAlloc) { /* Check the list of free extensions */ SrbExtension = DeviceExtension->FreeSrbExtensions; /* If no free extensions... */ if (SrbExtension == NULL) { /* Free SRB data */ if (Srb->Function != SRB_FUNCTION_ABORT_COMMAND && Srb->QueueTag != SP_UNTAGGED) { SrbInfo->Requests.Blink = NULL; SrbInfo->Requests.Flink = (PLIST_ENTRY)DeviceExtension->FreeSrbInfo; DeviceExtension->FreeSrbInfo = SrbInfo; } /* Return, in order to be called again later */ DeviceExtension->Flags |= SCSI_PORT_REQUEST_PENDING; KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock); return NULL; } /* Remove that free SRB extension from the list (since we're going to use it) */ DeviceExtension->FreeSrbExtensions = *((PVOID *)SrbExtension); /* Spinlock can be released now */ KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock); Srb->SrbExtension = SrbExtension; if (Srb->SenseInfoBuffer != NULL && DeviceExtension->SupportsAutoSense) { /* Store pointer to the SenseInfo buffer */ SrbInfo->SaveSenseRequest = Srb->SenseInfoBuffer; /* Does data fit the buffer? */ if (Srb->SenseInfoBufferLength > sizeof(SENSE_DATA)) { /* No, disabling autosense at all */ Srb->SrbFlags |= SRB_FLAGS_DISABLE_AUTOSENSE; } else { /* Yes, update the buffer pointer */ Srb->SenseInfoBuffer = SrbExtension + DeviceExtension->SrbExtensionSize; } } } else { /* Cleanup... */ Srb->SrbExtension = NULL; KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock); } return SrbInfo; } static NTSTATUS SpiSendInquiry(IN PDEVICE_OBJECT DeviceObject, IN OUT PSCSI_LUN_INFO LunInfo) { IO_STATUS_BLOCK IoStatusBlock; PIO_STACK_LOCATION IrpStack; KEVENT Event; KIRQL Irql; PIRP Irp; NTSTATUS Status; PINQUIRYDATA InquiryBuffer; PSENSE_DATA SenseBuffer; BOOLEAN KeepTrying = TRUE; ULONG RetryCount = 0; SCSI_REQUEST_BLOCK Srb; PCDB Cdb; PSCSI_PORT_LUN_EXTENSION LunExtension; PSCSI_PORT_DEVICE_EXTENSION DeviceExtension; DPRINT("SpiSendInquiry() called\n"); DeviceExtension = (PSCSI_PORT_DEVICE_EXTENSION)DeviceObject->DeviceExtension; InquiryBuffer = ExAllocatePoolWithTag(NonPagedPool, INQUIRYDATABUFFERSIZE, TAG_SCSIPORT); if (InquiryBuffer == NULL) return STATUS_INSUFFICIENT_RESOURCES; SenseBuffer = ExAllocatePoolWithTag(NonPagedPool, SENSE_BUFFER_SIZE, TAG_SCSIPORT); if (SenseBuffer == NULL) { ExFreePoolWithTag(InquiryBuffer, TAG_SCSIPORT); return STATUS_INSUFFICIENT_RESOURCES; } while (KeepTrying) { /* Initialize event for waiting */ KeInitializeEvent(&Event, NotificationEvent, FALSE); /* Create an IRP */ Irp = IoBuildDeviceIoControlRequest(IOCTL_SCSI_EXECUTE_IN, DeviceObject, NULL, 0, InquiryBuffer, INQUIRYDATABUFFERSIZE, TRUE, &Event, &IoStatusBlock); if (Irp == NULL) { DPRINT("IoBuildDeviceIoControlRequest() failed\n"); /* Quit the loop */ Status = STATUS_INSUFFICIENT_RESOURCES; KeepTrying = FALSE; continue; } /* Prepare SRB */ RtlZeroMemory(&Srb, sizeof(SCSI_REQUEST_BLOCK)); Srb.Length = sizeof(SCSI_REQUEST_BLOCK); Srb.OriginalRequest = Irp; Srb.PathId = LunInfo->PathId; Srb.TargetId = LunInfo->TargetId; Srb.Lun = LunInfo->Lun; Srb.Function = SRB_FUNCTION_EXECUTE_SCSI; Srb.SrbFlags = SRB_FLAGS_DATA_IN | SRB_FLAGS_DISABLE_SYNCH_TRANSFER; Srb.TimeOutValue = 4; Srb.CdbLength = 6; Srb.SenseInfoBuffer = SenseBuffer; Srb.SenseInfoBufferLength = SENSE_BUFFER_SIZE; Srb.DataBuffer = InquiryBuffer; Srb.DataTransferLength = INQUIRYDATABUFFERSIZE; /* Attach Srb to the Irp */ IrpStack = IoGetNextIrpStackLocation (Irp); IrpStack->Parameters.Scsi.Srb = &Srb; /* Fill in CDB */ Cdb = (PCDB)Srb.Cdb; Cdb->CDB6INQUIRY.OperationCode = SCSIOP_INQUIRY; Cdb->CDB6INQUIRY.LogicalUnitNumber = LunInfo->Lun; Cdb->CDB6INQUIRY.AllocationLength = INQUIRYDATABUFFERSIZE; /* Call the driver */ Status = IoCallDriver(DeviceObject, Irp); /* Wait for it to complete */ if (Status == STATUS_PENDING) { DPRINT("SpiSendInquiry(): Waiting for the driver to process request...\n"); KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); Status = IoStatusBlock.Status; } DPRINT("SpiSendInquiry(): Request processed by driver, status = 0x%08X\n", Status); if (SRB_STATUS(Srb.SrbStatus) == SRB_STATUS_SUCCESS) { /* All fine, copy data over */ RtlCopyMemory(LunInfo->InquiryData, InquiryBuffer, INQUIRYDATABUFFERSIZE); /* Quit the loop */ Status = STATUS_SUCCESS; KeepTrying = FALSE; continue; } DPRINT("Inquiry SRB failed with SrbStatus 0x%08X\n", Srb.SrbStatus); /* Check if the queue is frozen */ if (Srb.SrbStatus & SRB_STATUS_QUEUE_FROZEN) { /* Something weird happened, deal with it (unfreeze the queue) */ KeepTrying = FALSE; DPRINT("SpiSendInquiry(): the queue is frozen at TargetId %d\n", Srb.TargetId); LunExtension = SpiGetLunExtension(DeviceExtension, LunInfo->PathId, LunInfo->TargetId, LunInfo->Lun); /* Clear frozen flag */ LunExtension->Flags &= ~LUNEX_FROZEN_QUEUE; /* Acquire the spinlock */ KeAcquireSpinLock(&DeviceExtension->SpinLock, &Irql); /* Process the request */ SpiGetNextRequestFromLun(DeviceObject->DeviceExtension, LunExtension); /* SpiGetNextRequestFromLun() releases the spinlock, so we just lower irql back to what it was before */ KeLowerIrql(Irql); } /* Check if data overrun happened */ if (SRB_STATUS(Srb.SrbStatus) == SRB_STATUS_DATA_OVERRUN) { DPRINT("Data overrun at TargetId %d\n", LunInfo->TargetId); /* Nothing dramatic, just copy data, but limiting the size */ RtlCopyMemory(LunInfo->InquiryData, InquiryBuffer, (Srb.DataTransferLength > INQUIRYDATABUFFERSIZE) ? INQUIRYDATABUFFERSIZE : Srb.DataTransferLength); /* Quit the loop */ Status = STATUS_SUCCESS; KeepTrying = FALSE; } else if ((Srb.SrbStatus & SRB_STATUS_AUTOSENSE_VALID) && SenseBuffer->SenseKey == SCSI_SENSE_ILLEGAL_REQUEST) { /* LUN is not valid, but some device responds there. Mark it as invalid anyway */ /* Quit the loop */ Status = STATUS_INVALID_DEVICE_REQUEST; KeepTrying = FALSE; } else { /* Retry a couple of times if no timeout happened */ if ((RetryCount < 2) && (SRB_STATUS(Srb.SrbStatus) != SRB_STATUS_NO_DEVICE) && (SRB_STATUS(Srb.SrbStatus) != SRB_STATUS_SELECTION_TIMEOUT)) { RetryCount++; KeepTrying = TRUE; } else { /* That's all, quit the loop */ KeepTrying = FALSE; /* Set status according to SRB status */ if (SRB_STATUS(Srb.SrbStatus) == SRB_STATUS_BAD_FUNCTION || SRB_STATUS(Srb.SrbStatus) == SRB_STATUS_BAD_SRB_BLOCK_LENGTH) { Status = STATUS_INVALID_DEVICE_REQUEST; } else { Status = STATUS_IO_DEVICE_ERROR; } } } } /* Free buffers */ ExFreePoolWithTag(InquiryBuffer, TAG_SCSIPORT); ExFreePoolWithTag(SenseBuffer, TAG_SCSIPORT); DPRINT("SpiSendInquiry() done with Status 0x%08X\n", Status); return Status; } /* Scans all SCSI buses */ static VOID SpiScanAdapter(IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension) { PSCSI_PORT_LUN_EXTENSION LunExtension; ULONG Bus; ULONG Target; ULONG Lun; PSCSI_BUS_SCAN_INFO BusScanInfo; PSCSI_LUN_INFO LastLunInfo, LunInfo, LunInfoExists; BOOLEAN DeviceExists; ULONG Hint; NTSTATUS Status; ULONG DevicesFound; DPRINT("SpiScanAdapter() called\n"); /* Scan all buses */ for (Bus = 0; Bus < DeviceExtension->BusNum; Bus++) { DPRINT(" Scanning bus %d\n", Bus); DevicesFound = 0; /* Get pointer to the scan information */ BusScanInfo = DeviceExtension->BusesConfig->BusScanInfo[Bus]; if (BusScanInfo) { /* Find the last LUN info in the list */ LunInfo = DeviceExtension->BusesConfig->BusScanInfo[Bus]->LunInfo; LastLunInfo = LunInfo; while (LunInfo != NULL) { LastLunInfo = LunInfo; LunInfo = LunInfo->Next; } } else { /* We need to allocate this buffer */ BusScanInfo = ExAllocatePoolWithTag(NonPagedPool, sizeof(SCSI_BUS_SCAN_INFO), TAG_SCSIPORT); if (!BusScanInfo) { DPRINT1("Out of resources!\n"); return; } /* Store the pointer in the BusScanInfo array */ DeviceExtension->BusesConfig->BusScanInfo[Bus] = BusScanInfo; /* Fill this struct (length and bus ids for now) */ BusScanInfo->Length = sizeof(SCSI_BUS_SCAN_INFO); BusScanInfo->LogicalUnitsCount = 0; BusScanInfo->BusIdentifier = DeviceExtension->PortConfig->InitiatorBusId[Bus]; BusScanInfo->LunInfo = NULL; /* Set pointer to the last LUN info to NULL */ LastLunInfo = NULL; } /* Create LUN information structure */ LunInfo = ExAllocatePoolWithTag(PagedPool, sizeof(SCSI_LUN_INFO), TAG_SCSIPORT); if (!LunInfo) { DPRINT1("Out of resources!\n"); return; } RtlZeroMemory(LunInfo, sizeof(SCSI_LUN_INFO)); /* Create LunExtension */ LunExtension = SpiAllocateLunExtension(DeviceExtension); /* And send INQUIRY to every target */ for (Target = 0; Target < DeviceExtension->PortConfig->MaximumNumberOfTargets; Target++) { /* TODO: Support scan bottom-up */ /* Skip if it's the same address */ if (Target == BusScanInfo->BusIdentifier) continue; /* Try to find an existing device here */ DeviceExists = FALSE; LunInfoExists = BusScanInfo->LunInfo; /* Find matching address on this bus */ while (LunInfoExists) { if (LunInfoExists->TargetId == Target) { DeviceExists = TRUE; break; } /* Advance to the next one */ LunInfoExists = LunInfoExists->Next; } /* No need to bother rescanning, since we already did that before */ if (DeviceExists) continue; /* Scan all logical units */ for (Lun = 0; Lun < SCSI_MAXIMUM_LOGICAL_UNITS; Lun++) { if ((!LunExtension) || (!LunInfo)) break; /* Add extension to the list */ Hint = (Target + Lun) % LUS_NUMBER; LunExtension->Next = DeviceExtension->LunExtensionList[Hint]; DeviceExtension->LunExtensionList[Hint] = LunExtension; /* Fill Path, Target, Lun fields */ LunExtension->PathId = LunInfo->PathId = (UCHAR)Bus; LunExtension->TargetId = LunInfo->TargetId = (UCHAR)Target; LunExtension->Lun = LunInfo->Lun = (UCHAR)Lun; /* Set flag to prevent race conditions */ LunExtension->Flags |= SCSI_PORT_SCAN_IN_PROGRESS; /* Zero LU extension contents */ if (DeviceExtension->LunExtensionSize) { RtlZeroMemory(LunExtension + 1, DeviceExtension->LunExtensionSize); } /* Finally send the inquiry command */ Status = SpiSendInquiry(DeviceExtension->DeviceObject, LunInfo); if (NT_SUCCESS(Status)) { /* Let's see if we really found a device */ PINQUIRYDATA InquiryData = (PINQUIRYDATA)LunInfo->InquiryData; /* Check if this device is unsupported */ if (InquiryData->DeviceTypeQualifier == DEVICE_QUALIFIER_NOT_SUPPORTED) { DeviceExtension->LunExtensionList[Hint] = DeviceExtension->LunExtensionList[Hint]->Next; continue; } /* Clear the "in scan" flag */ LunExtension->Flags &= ~SCSI_PORT_SCAN_IN_PROGRESS; DPRINT("SpiScanAdapter(): Found device of type %d at bus %d tid %d lun %d\n", InquiryData->DeviceType, Bus, Target, Lun); /* * Cache the inquiry data into the LUN extension (or alternatively * we could save a pointer to LunInfo within the LunExtension?) */ RtlCopyMemory(&LunExtension->InquiryData, InquiryData, INQUIRYDATABUFFERSIZE); /* Add this info to the linked list */ LunInfo->Next = NULL; if (LastLunInfo) LastLunInfo->Next = LunInfo; else BusScanInfo->LunInfo = LunInfo; /* Store the last LUN info */ LastLunInfo = LunInfo; /* Store DeviceObject */ LunInfo->DeviceObject = DeviceExtension->DeviceObject; /* Allocate another buffer */ LunInfo = ExAllocatePoolWithTag(PagedPool, sizeof(SCSI_LUN_INFO), TAG_SCSIPORT); if (!LunInfo) { DPRINT1("Out of resources!\n"); break; } RtlZeroMemory(LunInfo, sizeof(SCSI_LUN_INFO)); /* Create a new LU extension */ LunExtension = SpiAllocateLunExtension(DeviceExtension); DevicesFound++; } else { /* Remove this LUN from the list */ DeviceExtension->LunExtensionList[Hint] = DeviceExtension->LunExtensionList[Hint]->Next; /* Decide whether we are continuing or not */ if (Status == STATUS_INVALID_DEVICE_REQUEST) continue; else break; } } } /* Free allocated buffers */ if (LunExtension) ExFreePoolWithTag(LunExtension, TAG_SCSIPORT); if (LunInfo) ExFreePoolWithTag(LunInfo, TAG_SCSIPORT); /* Sum what we found */ BusScanInfo->LogicalUnitsCount += (UCHAR)DevicesFound; DPRINT(" Found %d devices on bus %d\n", DevicesFound, Bus); } DPRINT("SpiScanAdapter() done\n"); } static NTSTATUS SpiGetInquiryData(IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, IN PIRP Irp) { ULONG InquiryDataSize; PSCSI_LUN_INFO LunInfo; ULONG BusCount, LunCount, Length; PIO_STACK_LOCATION IrpStack; PSCSI_ADAPTER_BUS_INFO AdapterBusInfo; PSCSI_INQUIRY_DATA InquiryData; PSCSI_BUS_DATA BusData; ULONG Bus; PUCHAR Buffer; DPRINT("SpiGetInquiryData() called\n"); /* Get pointer to the buffer */ IrpStack = IoGetCurrentIrpStackLocation(Irp); Buffer = Irp->AssociatedIrp.SystemBuffer; /* Initialize bus and LUN counters */ BusCount = DeviceExtension->BusesConfig->NumberOfBuses; LunCount = 0; /* Calculate total number of LUNs */ for (Bus = 0; Bus < BusCount; Bus++) LunCount += DeviceExtension->BusesConfig->BusScanInfo[Bus]->LogicalUnitsCount; /* Calculate size of inquiry data, rounding up to sizeof(ULONG) */ InquiryDataSize = ((sizeof(SCSI_INQUIRY_DATA) - 1 + INQUIRYDATABUFFERSIZE + sizeof(ULONG) - 1) & ~(sizeof(ULONG) - 1)); /* Calculate data size */ Length = sizeof(SCSI_ADAPTER_BUS_INFO) + (BusCount - 1) * sizeof(SCSI_BUS_DATA); Length += InquiryDataSize * LunCount; /* Check, if all data is going to fit into provided buffer */ if (IrpStack->Parameters.DeviceIoControl.OutputBufferLength < Length) { Irp->IoStatus.Status = STATUS_BUFFER_TOO_SMALL; return STATUS_BUFFER_TOO_SMALL; } /* Store data size in the IRP */ Irp->IoStatus.Information = Length; DPRINT("Data size: %lu\n", Length); AdapterBusInfo = (PSCSI_ADAPTER_BUS_INFO)Buffer; AdapterBusInfo->NumberOfBuses = (UCHAR)BusCount; /* Point InquiryData to the corresponding place inside Buffer */ InquiryData = (PSCSI_INQUIRY_DATA)(Buffer + sizeof(SCSI_ADAPTER_BUS_INFO) + (BusCount - 1) * sizeof(SCSI_BUS_DATA)); /* Loop each bus */ for (Bus = 0; Bus < BusCount; Bus++) { BusData = &AdapterBusInfo->BusData[Bus]; /* Calculate and save an offset of the inquiry data */ BusData->InquiryDataOffset = (ULONG)((PUCHAR)InquiryData - Buffer); /* Get a pointer to the LUN information structure */ LunInfo = DeviceExtension->BusesConfig->BusScanInfo[Bus]->LunInfo; /* Store Initiator Bus Id */ BusData->InitiatorBusId = DeviceExtension->BusesConfig->BusScanInfo[Bus]->BusIdentifier; /* Store LUN count */ BusData->NumberOfLogicalUnits = DeviceExtension->BusesConfig->BusScanInfo[Bus]->LogicalUnitsCount; /* Loop all LUNs */ while (LunInfo != NULL) { DPRINT("(Bus %lu Target %lu Lun %lu)\n", Bus, LunInfo->TargetId, LunInfo->Lun); /* Fill InquiryData with values */ InquiryData->PathId = LunInfo->PathId; InquiryData->TargetId = LunInfo->TargetId; InquiryData->Lun = LunInfo->Lun; InquiryData->InquiryDataLength = INQUIRYDATABUFFERSIZE; InquiryData->DeviceClaimed = LunInfo->DeviceClaimed; InquiryData->NextInquiryDataOffset = (ULONG)((PUCHAR)InquiryData + InquiryDataSize - Buffer); /* Copy data in it */ RtlCopyMemory(InquiryData->InquiryData, LunInfo->InquiryData, INQUIRYDATABUFFERSIZE); /* Move to the next LUN */ LunInfo = LunInfo->Next; InquiryData = (PSCSI_INQUIRY_DATA) ((PCHAR)InquiryData + InquiryDataSize); } /* Either mark the end, or set offset to 0 */ if (BusData->NumberOfLogicalUnits != 0) ((PSCSI_INQUIRY_DATA) ((PCHAR)InquiryData - InquiryDataSize))->NextInquiryDataOffset = 0; else BusData->InquiryDataOffset = 0; } /* Finish with success */ Irp->IoStatus.Status = STATUS_SUCCESS; return STATUS_SUCCESS; } static PSCSI_REQUEST_BLOCK_INFO SpiGetSrbData(IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, IN UCHAR PathId, IN UCHAR TargetId, IN UCHAR Lun, IN UCHAR QueueTag) { PSCSI_PORT_LUN_EXTENSION LunExtension; if (QueueTag == SP_UNTAGGED) { /* Untagged request, get LU and return pointer to SrbInfo */ LunExtension = SpiGetLunExtension(DeviceExtension, PathId, TargetId, Lun); /* Return NULL in case of error */ if (!LunExtension) return(NULL); /* Return the pointer to SrbInfo */ return &LunExtension->SrbInfo; } else { /* Make sure the tag is valid, if it is - return the data */ if (QueueTag > DeviceExtension->SrbDataCount || QueueTag < 1) return NULL; else return &DeviceExtension->SrbInfo[QueueTag -1]; } } static VOID SpiSendRequestSense(IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, IN PSCSI_REQUEST_BLOCK InitialSrb) { PSCSI_REQUEST_BLOCK Srb; PCDB Cdb; PIRP Irp; PIO_STACK_LOCATION IrpStack; LARGE_INTEGER LargeInt; PVOID *Ptr; DPRINT("SpiSendRequestSense() entered, InitialSrb %p\n", InitialSrb); /* Allocate Srb */ Srb = ExAllocatePoolWithTag(NonPagedPool, sizeof(SCSI_REQUEST_BLOCK) + sizeof(PVOID), TAG_SCSIPORT); RtlZeroMemory(Srb, sizeof(SCSI_REQUEST_BLOCK)); /* Allocate IRP */ LargeInt.QuadPart = (LONGLONG) 1; Irp = IoBuildAsynchronousFsdRequest(IRP_MJ_READ, DeviceExtension->DeviceObject, InitialSrb->SenseInfoBuffer, InitialSrb->SenseInfoBufferLength, &LargeInt, NULL); IoSetCompletionRoutine(Irp, (PIO_COMPLETION_ROUTINE)SpiCompletionRoutine, Srb, TRUE, TRUE, TRUE); if (!Srb) { DPRINT("SpiSendRequestSense() failed, Srb %p\n", Srb); return; } IrpStack = IoGetNextIrpStackLocation(Irp); IrpStack->MajorFunction = IRP_MJ_SCSI; /* Put Srb address into Irp... */ IrpStack->Parameters.Others.Argument1 = (PVOID)Srb; /* ...and vice versa */ Srb->OriginalRequest = Irp; /* Save Srb */ Ptr = (PVOID *)(Srb+1); *Ptr = InitialSrb; /* Build CDB for REQUEST SENSE */ Srb->CdbLength = 6; Cdb = (PCDB)Srb->Cdb; Cdb->CDB6INQUIRY.OperationCode = SCSIOP_REQUEST_SENSE; Cdb->CDB6INQUIRY.LogicalUnitNumber = 0; Cdb->CDB6INQUIRY.Reserved1 = 0; Cdb->CDB6INQUIRY.PageCode = 0; Cdb->CDB6INQUIRY.IReserved = 0; Cdb->CDB6INQUIRY.AllocationLength = (UCHAR)InitialSrb->SenseInfoBufferLength; Cdb->CDB6INQUIRY.Control = 0; /* Set address */ Srb->TargetId = InitialSrb->TargetId; Srb->Lun = InitialSrb->Lun; Srb->PathId = InitialSrb->PathId; Srb->Function = SRB_FUNCTION_EXECUTE_SCSI; Srb->Length = sizeof(SCSI_REQUEST_BLOCK); /* Timeout will be 2 seconds */ Srb->TimeOutValue = 2; /* No auto request sense */ Srb->SenseInfoBufferLength = 0; Srb->SenseInfoBuffer = NULL; /* Set necessary flags */ Srb->SrbFlags = SRB_FLAGS_DATA_IN | SRB_FLAGS_BYPASS_FROZEN_QUEUE | SRB_FLAGS_DISABLE_DISCONNECT; /* Transfer disable synch transfer flag */ if (InitialSrb->SrbFlags & SRB_FLAGS_DISABLE_SYNCH_TRANSFER) Srb->SrbFlags |= SRB_FLAGS_DISABLE_SYNCH_TRANSFER; Srb->DataBuffer = InitialSrb->SenseInfoBuffer; /* Fill the transfer length */ Srb->DataTransferLength = InitialSrb->SenseInfoBufferLength; /* Clear statuses */ Srb->ScsiStatus = Srb->SrbStatus = 0; Srb->NextSrb = 0; /* Call the driver */ (VOID)IoCallDriver(DeviceExtension->DeviceObject, Irp); DPRINT("SpiSendRequestSense() done\n"); } static VOID NTAPI SpiProcessCompletedRequest(IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, IN PSCSI_REQUEST_BLOCK_INFO SrbInfo, OUT PBOOLEAN NeedToCallStartIo) { PSCSI_REQUEST_BLOCK Srb; PSCSI_PORT_LUN_EXTENSION LunExtension; LONG Result; PIRP Irp; //ULONG SequenceNumber; Srb = SrbInfo->Srb; Irp = Srb->OriginalRequest; /* Get Lun extension */ LunExtension = SpiGetLunExtension(DeviceExtension, Srb->PathId, Srb->TargetId, Srb->Lun); if (Srb->SrbFlags & SRB_FLAGS_UNSPECIFIED_DIRECTION && DeviceExtension->MapBuffers && Irp->MdlAddress) { /* MDL is shared if transfer is broken into smaller parts */ Srb->DataBuffer = (PCCHAR)MmGetMdlVirtualAddress(Irp->MdlAddress) + ((PCCHAR)Srb->DataBuffer - SrbInfo->DataOffset); /* In case of data going in, flush the buffers */ if (Srb->SrbFlags & SRB_FLAGS_DATA_IN) { KeFlushIoBuffers(Irp->MdlAddress, TRUE, FALSE); } } /* Flush adapter if needed */ if (SrbInfo->BaseOfMapRegister) { /* TODO: Implement */ ASSERT(FALSE); } /* Clear the request */ SrbInfo->Srb = NULL; /* If disconnect is disabled... */ if (Srb->SrbFlags & SRB_FLAGS_DISABLE_DISCONNECT) { /* Acquire the spinlock since we mess with flags */ KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock); /* Set corresponding flag */ DeviceExtension->Flags |= SCSI_PORT_DISCONNECT_ALLOWED; /* Clear the timer if needed */ if (!(DeviceExtension->InterruptData.Flags & SCSI_PORT_RESET)) DeviceExtension->TimerCount = -1; /* Spinlock is not needed anymore */ KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock); if (!(DeviceExtension->Flags & SCSI_PORT_REQUEST_PENDING) && !(DeviceExtension->Flags & SCSI_PORT_DEVICE_BUSY) && !(*NeedToCallStartIo)) { /* We're not busy, but we have a request pending */ IoStartNextPacket(DeviceExtension->DeviceObject, FALSE); } } /* Scatter/gather */ if (Srb->SrbFlags & SRB_FLAGS_SGLIST_FROM_POOL) { /* TODO: Implement */ ASSERT(FALSE); } /* Acquire spinlock (we're freeing SrbExtension) */ KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock); /* Free it (if needed) */ if (Srb->SrbExtension) { if (Srb->SenseInfoBuffer != NULL && DeviceExtension->SupportsAutoSense) { ASSERT(Srb->SenseInfoBuffer == NULL || SrbInfo->SaveSenseRequest != NULL); if (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) { /* Copy sense data to the buffer */ RtlCopyMemory(SrbInfo->SaveSenseRequest, Srb->SenseInfoBuffer, Srb->SenseInfoBufferLength); } /* And restore the pointer */ Srb->SenseInfoBuffer = SrbInfo->SaveSenseRequest; } /* Put it into the free srb extensions list */ *((PVOID *)Srb->SrbExtension) = DeviceExtension->FreeSrbExtensions; DeviceExtension->FreeSrbExtensions = Srb->SrbExtension; } /* Save transfer length in the IRP */ Irp->IoStatus.Information = Srb->DataTransferLength; //SequenceNumber = SrbInfo->SequenceNumber; SrbInfo->SequenceNumber = 0; /* Decrement the queue count */ LunExtension->QueueCount--; /* Free Srb, if needed*/ if (Srb->QueueTag != SP_UNTAGGED) { /* Put it into the free list */ SrbInfo->Requests.Blink = NULL; SrbInfo->Requests.Flink = (PLIST_ENTRY)DeviceExtension->FreeSrbInfo; DeviceExtension->FreeSrbInfo = SrbInfo; } /* SrbInfo is not used anymore */ SrbInfo = NULL; if (DeviceExtension->Flags & SCSI_PORT_REQUEST_PENDING) { /* Clear the flag */ DeviceExtension->Flags &= ~SCSI_PORT_REQUEST_PENDING; /* Note the caller about StartIo */ *NeedToCallStartIo = TRUE; } if (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SUCCESS) { /* Start the packet */ Irp->IoStatus.Status = STATUS_SUCCESS; if (!(Srb->SrbFlags & SRB_FLAGS_BYPASS_FROZEN_QUEUE) && LunExtension->RequestTimeout == -1) { /* Start the next packet */ SpiGetNextRequestFromLun(DeviceExtension, LunExtension); } else { /* Release the spinlock */ KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock); } DPRINT("IoCompleting request IRP 0x%p\n", Irp); IoCompleteRequest(Irp, IO_DISK_INCREMENT); /* Decrement number of active requests, and analyze the result */ Result = InterlockedDecrement(&DeviceExtension->ActiveRequestCounter); if (Result < 0 && !DeviceExtension->MapRegisters && DeviceExtension->AdapterObject != NULL) { /* Nullify map registers */ DeviceExtension->MapRegisterBase = NULL; IoFreeAdapterChannel(DeviceExtension->AdapterObject); } /* Exit, we're done */ return; } /* Decrement number of active requests, and analyze the result */ Result = InterlockedDecrement(&DeviceExtension->ActiveRequestCounter); if (Result < 0 && !DeviceExtension->MapRegisters && DeviceExtension->AdapterObject != NULL) { /* Result is negative, so this is a slave, free map registers */ DeviceExtension->MapRegisterBase = NULL; IoFreeAdapterChannel(DeviceExtension->AdapterObject); } /* Convert status */ Irp->IoStatus.Status = SpiStatusSrbToNt(Srb->SrbStatus); /* It's not a bypass, it's busy or the queue is full? */ if ((Srb->ScsiStatus == SCSISTAT_BUSY || Srb->SrbStatus == SRB_STATUS_BUSY || Srb->ScsiStatus == SCSISTAT_QUEUE_FULL) && !(Srb->SrbFlags & SRB_FLAGS_BYPASS_FROZEN_QUEUE)) { DPRINT("Busy SRB status %x\n", Srb->SrbStatus); /* Requeue, if needed */ if (LunExtension->Flags & (LUNEX_FROZEN_QUEUE | LUNEX_BUSY)) { DPRINT("it's being requeued\n"); Srb->SrbStatus = SRB_STATUS_PENDING; Srb->ScsiStatus = 0; if (!KeInsertByKeyDeviceQueue(&LunExtension->DeviceQueue, &Irp->Tail.Overlay.DeviceQueueEntry, Srb->QueueSortKey)) { /* It's a big f.ck up if we got here */ Srb->SrbStatus = SRB_STATUS_ERROR; Srb->ScsiStatus = SCSISTAT_BUSY; ASSERT(FALSE); goto Error; } /* Release the spinlock */ KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock); } else if (LunExtension->AttemptCount++ < 20) { /* LUN is still busy */ Srb->ScsiStatus = 0; Srb->SrbStatus = SRB_STATUS_PENDING; LunExtension->BusyRequest = Irp; LunExtension->Flags |= LUNEX_BUSY; /* Release the spinlock */ KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock); } else { Error: /* Freeze the queue*/ Srb->SrbStatus |= SRB_STATUS_QUEUE_FROZEN; LunExtension->Flags |= LUNEX_FROZEN_QUEUE; /* "Unfull" the queue */ LunExtension->Flags &= ~LUNEX_FULL_QUEUE; /* Release the spinlock */ KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock); /* Return status that the device is not ready */ Irp->IoStatus.Status = STATUS_DEVICE_NOT_READY; IoCompleteRequest(Irp, IO_DISK_INCREMENT); } return; } /* Start the next request, if LUN is idle, and this is sense request */ if (((Srb->ScsiStatus != SCSISTAT_CHECK_CONDITION) || (Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) || !Srb->SenseInfoBuffer || !Srb->SenseInfoBufferLength) && (Srb->SrbFlags & SRB_FLAGS_NO_QUEUE_FREEZE)) { if (LunExtension->RequestTimeout == -1) SpiGetNextRequestFromLun(DeviceExtension, LunExtension); else KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock); } else { /* Freeze the queue */ Srb->SrbStatus |= SRB_STATUS_QUEUE_FROZEN; LunExtension->Flags |= LUNEX_FROZEN_QUEUE; /* Do we need a request sense? */ if (Srb->ScsiStatus == SCSISTAT_CHECK_CONDITION && !(Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID) && Srb->SenseInfoBuffer && Srb->SenseInfoBufferLength) { /* If LUN is busy, we have to requeue it in order to allow request sense */ if (LunExtension->Flags & LUNEX_BUSY) { DPRINT("Requeuing busy request to allow request sense\n"); if (!KeInsertByKeyDeviceQueue(&LunExtension->DeviceQueue, &LunExtension->BusyRequest->Tail.Overlay.DeviceQueueEntry, Srb->QueueSortKey)) { /* We should never get here */ ASSERT(FALSE); KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock); IoCompleteRequest(Irp, IO_DISK_INCREMENT); return; } /* Clear busy flags */ LunExtension->Flags &= ~(LUNEX_FULL_QUEUE | LUNEX_BUSY); } /* Release the spinlock */ KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock); /* Send RequestSense */ SpiSendRequestSense(DeviceExtension, Srb); /* Exit */ return; } /* Release the spinlock */ KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock); } /* Complete the request */ IoCompleteRequest(Irp, IO_DISK_INCREMENT); } NTSTATUS NTAPI SpiCompletionRoutine(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID Context) { PSCSI_REQUEST_BLOCK Srb = (PSCSI_REQUEST_BLOCK)Context; PSCSI_REQUEST_BLOCK InitialSrb; PIRP InitialIrp; DPRINT("SpiCompletionRoutine() entered, IRP %p \n", Irp); if ((Srb->Function == SRB_FUNCTION_RESET_BUS) || (Srb->Function == SRB_FUNCTION_ABORT_COMMAND)) { /* Deallocate SRB and IRP and exit */ ExFreePool(Srb); IoFreeIrp(Irp); return STATUS_MORE_PROCESSING_REQUIRED; } /* Get a pointer to the SRB and IRP which were initially sent */ InitialSrb = *((PVOID *)(Srb+1)); InitialIrp = InitialSrb->OriginalRequest; if ((SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_SUCCESS) || (SRB_STATUS(Srb->SrbStatus) == SRB_STATUS_DATA_OVERRUN)) { /* Sense data is OK */ InitialSrb->SrbStatus |= SRB_STATUS_AUTOSENSE_VALID; /* Set length to be the same */ InitialSrb->SenseInfoBufferLength = (UCHAR)Srb->DataTransferLength; } /* Make sure initial SRB's queue is frozen */ ASSERT(InitialSrb->SrbStatus & SRB_STATUS_QUEUE_FROZEN); /* Complete this request */ IoCompleteRequest(InitialIrp, IO_DISK_INCREMENT); /* Deallocate everything (internal) */ ExFreePool(Srb); if (Irp->MdlAddress != NULL) { MmUnlockPages(Irp->MdlAddress); IoFreeMdl(Irp->MdlAddress); Irp->MdlAddress = NULL; } IoFreeIrp(Irp); return STATUS_MORE_PROCESSING_REQUIRED; } static BOOLEAN NTAPI ScsiPortIsr(IN PKINTERRUPT Interrupt, IN PVOID ServiceContext) { PSCSI_PORT_DEVICE_EXTENSION DeviceExtension; DPRINT("ScsiPortIsr() called!\n"); DeviceExtension = (PSCSI_PORT_DEVICE_EXTENSION)ServiceContext; /* If interrupts are disabled - we don't expect any */ if (DeviceExtension->InterruptData.Flags & SCSI_PORT_DISABLE_INTERRUPTS) return FALSE; /* Call miniport's HwInterrupt routine */ if (DeviceExtension->HwInterrupt(&DeviceExtension->MiniPortDeviceExtension) == FALSE) { /* This interrupt doesn't belong to us */ return FALSE; } /* If flag of notification is set - queue a DPC */ if (DeviceExtension->InterruptData.Flags & SCSI_PORT_NOTIFICATION_NEEDED) { IoRequestDpc(DeviceExtension->DeviceObject, DeviceExtension->CurrentIrp, DeviceExtension); } return TRUE; } BOOLEAN NTAPI SpiSaveInterruptData(IN PVOID Context) { PSCSI_PORT_SAVE_INTERRUPT InterruptContext = Context; PSCSI_PORT_LUN_EXTENSION LunExtension; PSCSI_REQUEST_BLOCK Srb; PSCSI_REQUEST_BLOCK_INFO SrbInfo, NextSrbInfo; PSCSI_PORT_DEVICE_EXTENSION DeviceExtension; BOOLEAN IsTimed; /* Get pointer to the device extension */ DeviceExtension = InterruptContext->DeviceExtension; /* If we don't have anything pending - return */ if (!(DeviceExtension->InterruptData.Flags & SCSI_PORT_NOTIFICATION_NEEDED)) return FALSE; /* Actually save the interrupt data */ *InterruptContext->InterruptData = DeviceExtension->InterruptData; /* Clear the data stored in the device extension */ DeviceExtension->InterruptData.Flags &= (SCSI_PORT_RESET | SCSI_PORT_RESET_REQUEST | SCSI_PORT_DISABLE_INTERRUPTS); DeviceExtension->InterruptData.CompletedAbort = NULL; DeviceExtension->InterruptData.ReadyLun = NULL; DeviceExtension->InterruptData.CompletedRequests = NULL; /* Loop through the list of completed requests */ SrbInfo = InterruptContext->InterruptData->CompletedRequests; while (SrbInfo) { /* Make sure we have SRV */ ASSERT(SrbInfo->Srb); /* Get SRB and LunExtension */ Srb = SrbInfo->Srb; LunExtension = SpiGetLunExtension(DeviceExtension, Srb->PathId, Srb->TargetId, Srb->Lun); /* We have to check special cases if request is unsuccessful*/ if (Srb->SrbStatus != SRB_STATUS_SUCCESS) { /* Check if we need request sense by a few conditions */ if (Srb->SenseInfoBuffer && Srb->SenseInfoBufferLength && Srb->ScsiStatus == SCSISTAT_CHECK_CONDITION && !(Srb->SrbStatus & SRB_STATUS_AUTOSENSE_VALID)) { if (LunExtension->Flags & LUNEX_NEED_REQUEST_SENSE) { /* It means: we tried to send REQUEST SENSE, but failed */ Srb->ScsiStatus = 0; Srb->SrbStatus = SRB_STATUS_REQUEST_SENSE_FAILED; } else { /* Set the corresponding flag, so that REQUEST SENSE will be sent */ LunExtension->Flags |= LUNEX_NEED_REQUEST_SENSE; } } /* Check for a full queue */ if (Srb->ScsiStatus == SCSISTAT_QUEUE_FULL) { /* TODO: Implement when it's encountered */ ASSERT(FALSE); } } /* Let's decide if we need to watch timeout or not */ if (Srb->QueueTag == SP_UNTAGGED) { IsTimed = TRUE; } else { if (LunExtension->SrbInfo.Requests.Flink == &SrbInfo->Requests) IsTimed = TRUE; else IsTimed = FALSE; /* Remove it from the queue */ RemoveEntryList(&SrbInfo->Requests); } if (IsTimed) { /* We have to maintain timeout counter */ if (IsListEmpty(&LunExtension->SrbInfo.Requests)) { LunExtension->RequestTimeout = -1; } else { NextSrbInfo = CONTAINING_RECORD(LunExtension->SrbInfo.Requests.Flink, SCSI_REQUEST_BLOCK_INFO, Requests); Srb = NextSrbInfo->Srb; /* Update timeout counter */ LunExtension->RequestTimeout = Srb->TimeOutValue; } } SrbInfo = SrbInfo->CompletedRequests; } return TRUE; } VOID NTAPI SpiGetNextRequestFromLun(IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, IN PSCSI_PORT_LUN_EXTENSION LunExtension) { PIO_STACK_LOCATION IrpStack; PIRP NextIrp; PKDEVICE_QUEUE_ENTRY Entry; PSCSI_REQUEST_BLOCK Srb; /* If LUN is not active or queue is more than maximum allowed */ if (LunExtension->QueueCount >= LunExtension->MaxQueueCount || !(LunExtension->Flags & SCSI_PORT_LU_ACTIVE)) { /* Release the spinlock and exit */ KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock); return; } /* Check if we can get a next request */ if (LunExtension->Flags & (LUNEX_NEED_REQUEST_SENSE | LUNEX_BUSY | LUNEX_FULL_QUEUE | LUNEX_FROZEN_QUEUE | LUNEX_REQUEST_PENDING)) { /* Pending requests can only be started if the queue is empty */ if (IsListEmpty(&LunExtension->SrbInfo.Requests) && !(LunExtension->Flags & (LUNEX_BUSY | LUNEX_FROZEN_QUEUE | LUNEX_FULL_QUEUE | LUNEX_NEED_REQUEST_SENSE))) { /* Make sure we have SRB */ ASSERT(LunExtension->SrbInfo.Srb == NULL); /* Clear active and pending flags */ LunExtension->Flags &= ~(LUNEX_REQUEST_PENDING | SCSI_PORT_LU_ACTIVE); /* Get next Irp, and clear pending requests list */ NextIrp = LunExtension->PendingRequest; LunExtension->PendingRequest = NULL; /* Set attempt counter to zero */ LunExtension->AttemptCount = 0; /* Release the spinlock */ KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock); /* Start the next pending request */ IoStartPacket(DeviceExtension->DeviceObject, NextIrp, (PULONG)NULL, NULL); return; } else { /* Release the spinlock, without clearing any flags and exit */ KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock); return; } } /* Reset active flag */ LunExtension->Flags &= ~SCSI_PORT_LU_ACTIVE; /* Set attempt counter to zero */ LunExtension->AttemptCount = 0; /* Remove packet from the device queue */ Entry = KeRemoveByKeyDeviceQueue(&LunExtension->DeviceQueue, LunExtension->SortKey); if (Entry != NULL) { /* Get pointer to the next irp */ NextIrp = CONTAINING_RECORD(Entry, IRP, Tail.Overlay.DeviceQueueEntry); /* Get point to the SRB */ IrpStack = IoGetCurrentIrpStackLocation(NextIrp); Srb = (PSCSI_REQUEST_BLOCK)IrpStack->Parameters.Others.Argument1; /* Set new key*/ LunExtension->SortKey = Srb->QueueSortKey; LunExtension->SortKey++; /* Release the spinlock */ KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock); /* Start the next pending request */ IoStartPacket(DeviceExtension->DeviceObject, NextIrp, (PULONG)NULL, NULL); } else { /* Release the spinlock */ KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock); } } // ScsiPortDpcForIsr // DESCRIPTION: // // RUN LEVEL: // // ARGUMENTS: // IN PKDPC Dpc // IN PDEVICE_OBJECT DpcDeviceObject // IN PIRP DpcIrp // IN PVOID DpcContext // static VOID NTAPI ScsiPortDpcForIsr(IN PKDPC Dpc, IN PDEVICE_OBJECT DpcDeviceObject, IN PIRP DpcIrp, IN PVOID DpcContext) { PSCSI_PORT_DEVICE_EXTENSION DeviceExtension = DpcDeviceObject->DeviceExtension; SCSI_PORT_INTERRUPT_DATA InterruptData; SCSI_PORT_SAVE_INTERRUPT Context; PSCSI_PORT_LUN_EXTENSION LunExtension; BOOLEAN NeedToStartIo; PSCSI_REQUEST_BLOCK_INFO SrbInfo; LARGE_INTEGER TimerValue; DPRINT("ScsiPortDpcForIsr(Dpc %p DpcDeviceObject %p DpcIrp %p DpcContext %p)\n", Dpc, DpcDeviceObject, DpcIrp, DpcContext); /* We need to acquire spinlock */ KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock); RtlZeroMemory(&InterruptData, sizeof(SCSI_PORT_INTERRUPT_DATA)); TryAgain: /* Interrupt structure must be snapshotted, and only then analyzed */ Context.InterruptData = &InterruptData; Context.DeviceExtension = DeviceExtension; if (!KeSynchronizeExecution(DeviceExtension->Interrupt[0], SpiSaveInterruptData, &Context)) { /* Nothing - just return (don't forget to release the spinlock */ KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock); DPRINT("ScsiPortDpcForIsr() done\n"); return; } /* If flush of adapters is needed - do it */ if (InterruptData.Flags & SCSI_PORT_FLUSH_ADAPTERS) { /* TODO: Implement */ ASSERT(FALSE); } /* Check for IoMapTransfer */ if (InterruptData.Flags & SCSI_PORT_MAP_TRANSFER) { /* TODO: Implement */ ASSERT(FALSE); } /* Check if timer is needed */ if (InterruptData.Flags & SCSI_PORT_TIMER_NEEDED) { /* Save the timer routine */ DeviceExtension->HwScsiTimer = InterruptData.HwScsiTimer; if (InterruptData.MiniportTimerValue == 0) { /* Cancel the timer */ KeCancelTimer(&DeviceExtension->MiniportTimer); } else { /* Convert timer value */ TimerValue.QuadPart = Int32x32To64(InterruptData.MiniportTimerValue, -10); /* Set the timer */ KeSetTimer(&DeviceExtension->MiniportTimer, TimerValue, &DeviceExtension->MiniportTimerDpc); } } /* If it's ready for the next request */ if (InterruptData.Flags & SCSI_PORT_NEXT_REQUEST_READY) { /* Check for a duplicate request (NextRequest+NextLuRequest) */ if ((DeviceExtension->Flags & (SCSI_PORT_DEVICE_BUSY | SCSI_PORT_DISCONNECT_ALLOWED)) == (SCSI_PORT_DEVICE_BUSY | SCSI_PORT_DISCONNECT_ALLOWED)) { /* Clear busy flag set by ScsiPortStartPacket() */ DeviceExtension->Flags &= ~SCSI_PORT_DEVICE_BUSY; if (!(InterruptData.Flags & SCSI_PORT_RESET)) { /* Ready for next, and no reset is happening */ DeviceExtension->TimerCount = -1; } } else { /* Not busy, but not ready for the next request */ DeviceExtension->Flags &= ~SCSI_PORT_DEVICE_BUSY; InterruptData.Flags &= ~SCSI_PORT_NEXT_REQUEST_READY; } } /* Any resets? */ if (InterruptData.Flags & SCSI_PORT_RESET_REPORTED) { /* Hold for a bit */ DeviceExtension->TimerCount = 4; } /* Any ready LUN? */ if (InterruptData.ReadyLun != NULL) { /* Process all LUNs from the list*/ while (TRUE) { /* Remove it from the list first (as processed) */ LunExtension = InterruptData.ReadyLun; InterruptData.ReadyLun = LunExtension->ReadyLun; LunExtension->ReadyLun = NULL; /* Get next request for this LUN */ SpiGetNextRequestFromLun(DeviceExtension, LunExtension); /* Still ready requests exist? If yes - get spinlock, if no - stop here */ if (InterruptData.ReadyLun != NULL) KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock); else break; } } else { /* Release the spinlock */ KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock); } /* If we ready for next packet, start it */ if (InterruptData.Flags & SCSI_PORT_NEXT_REQUEST_READY) IoStartNextPacket(DeviceExtension->DeviceObject, FALSE); NeedToStartIo = FALSE; /* Loop the completed request list */ while (InterruptData.CompletedRequests) { /* Remove the request */ SrbInfo = InterruptData.CompletedRequests; InterruptData.CompletedRequests = SrbInfo->CompletedRequests; SrbInfo->CompletedRequests = NULL; /* Process it */ SpiProcessCompletedRequest(DeviceExtension, SrbInfo, &NeedToStartIo); } /* Loop abort request list */ while (InterruptData.CompletedAbort) { LunExtension = InterruptData.CompletedAbort; /* Remove the request */ InterruptData.CompletedAbort = LunExtension->CompletedAbortRequests; /* Get spinlock since we're going to change flags */ KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock); /* TODO: Put SrbExtension to the list of free extensions */ ASSERT(FALSE); } /* If we need - call StartIo routine */ if (NeedToStartIo) { /* Make sure CurrentIrp is not null! */ ASSERT(DpcDeviceObject->CurrentIrp != NULL); ScsiPortStartIo(DpcDeviceObject, DpcDeviceObject->CurrentIrp); } /* Everything has been done, check */ if (InterruptData.Flags & SCSI_PORT_ENABLE_INT_REQUEST) { /* Synchronize using spinlock */ KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock); /* Request an interrupt */ DeviceExtension->HwInterrupt(DeviceExtension->MiniPortDeviceExtension); ASSERT(DeviceExtension->Flags & SCSI_PORT_DISABLE_INT_REQUESET); /* Should interrupts be enabled again? */ if (DeviceExtension->Flags & SCSI_PORT_DISABLE_INT_REQUESET) { /* Clear this flag */ DeviceExtension->Flags &= ~SCSI_PORT_DISABLE_INT_REQUESET; /* Call a special routine to do this */ ASSERT(FALSE); #if 0 KeSynchronizeExecution(DeviceExtension->Interrupt, SpiEnableInterrupts, DeviceExtension); #endif } /* If we need a notification again - loop */ if (DeviceExtension->InterruptData.Flags & SCSI_PORT_NOTIFICATION_NEEDED) goto TryAgain; /* Release the spinlock */ KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock); } DPRINT("ScsiPortDpcForIsr() done\n"); } BOOLEAN NTAPI SpiProcessTimeout(PVOID ServiceContext) { PDEVICE_OBJECT DeviceObject = (PDEVICE_OBJECT)ServiceContext; PSCSI_PORT_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension; ULONG Bus; DPRINT("SpiProcessTimeout() entered\n"); DeviceExtension->TimerCount = -1; if (DeviceExtension->InterruptData.Flags & SCSI_PORT_RESET) { DeviceExtension->InterruptData.Flags &= ~SCSI_PORT_RESET; if (DeviceExtension->InterruptData.Flags & SCSI_PORT_RESET_REQUEST) { DeviceExtension->InterruptData.Flags &= ~SCSI_PORT_RESET_REQUEST; ScsiPortStartPacket(ServiceContext); } return FALSE; } else { DPRINT("Resetting the bus\n"); for (Bus = 0; Bus < DeviceExtension->BusNum; Bus++) { DeviceExtension->HwResetBus(DeviceExtension->MiniPortDeviceExtension, Bus); /* Reset flags and set reset timeout to 4 seconds */ DeviceExtension->InterruptData.Flags |= SCSI_PORT_RESET; DeviceExtension->TimerCount = 4; } /* If miniport requested - request a dpc for it */ if (DeviceExtension->InterruptData.Flags & SCSI_PORT_NOTIFICATION_NEEDED) IoRequestDpc(DeviceExtension->DeviceObject, NULL, NULL); } return TRUE; } BOOLEAN NTAPI SpiResetBus(PVOID ServiceContext) { PRESETBUS_PARAMS ResetParams = (PRESETBUS_PARAMS)ServiceContext; PSCSI_PORT_DEVICE_EXTENSION DeviceExtension; /* Perform the bus reset */ DeviceExtension = (PSCSI_PORT_DEVICE_EXTENSION)ResetParams->DeviceExtension; DeviceExtension->HwResetBus(DeviceExtension->MiniPortDeviceExtension, ResetParams->PathId); /* Set flags and start the timer */ DeviceExtension->InterruptData.Flags |= SCSI_PORT_RESET; DeviceExtension->TimerCount = 4; /* If miniport requested - give him a DPC */ if (DeviceExtension->InterruptData.Flags & SCSI_PORT_NOTIFICATION_NEEDED) IoRequestDpc(DeviceExtension->DeviceObject, NULL, NULL); return TRUE; } // ScsiPortIoTimer // DESCRIPTION: // This function handles timeouts and other time delayed processing // // RUN LEVEL: // // ARGUMENTS: // IN PDEVICE_OBJECT DeviceObject Device object registered with timer // IN PVOID Context the Controller extension for the // controller the device is on // static VOID NTAPI ScsiPortIoTimer(PDEVICE_OBJECT DeviceObject, PVOID Context) { PSCSI_PORT_DEVICE_EXTENSION DeviceExtension; PSCSI_PORT_LUN_EXTENSION LunExtension; ULONG Lun; PIRP Irp; DPRINT("ScsiPortIoTimer()\n"); DeviceExtension = (PSCSI_PORT_DEVICE_EXTENSION)DeviceObject->DeviceExtension; /* Protect with the spinlock */ KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock); /* Check timeouts */ if (DeviceExtension->TimerCount > 0) { /* Decrease the timeout counter */ DeviceExtension->TimerCount--; if (DeviceExtension->TimerCount == 0) { /* Timeout, process it */ if (KeSynchronizeExecution(DeviceExtension->Interrupt[0], SpiProcessTimeout, DeviceExtension->DeviceObject)) { DPRINT("Error happened during processing timeout, but nothing critical\n"); } } KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock); /* We should exit now, since timeout is processed */ return; } /* Per-Lun scanning of timeouts is needed... */ for (Lun = 0; Lun < LUS_NUMBER; Lun++) { LunExtension = DeviceExtension->LunExtensionList[Lun]; while (LunExtension) { if (LunExtension->Flags & LUNEX_BUSY) { if (!(LunExtension->Flags & (LUNEX_NEED_REQUEST_SENSE | LUNEX_FROZEN_QUEUE))) { DPRINT("Retrying busy request\n"); /* Clear flags, and retry busy request */ LunExtension->Flags &= ~(LUNEX_BUSY | LUNEX_FULL_QUEUE); Irp = LunExtension->BusyRequest; /* Clearing busy request */ LunExtension->BusyRequest = NULL; KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock); IoStartPacket(DeviceObject, Irp, (PULONG)NULL, NULL); KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock); } } else if (LunExtension->RequestTimeout == 0) { RESETBUS_PARAMS ResetParams; LunExtension->RequestTimeout = -1; DPRINT("Request timed out, resetting bus\n"); /* Pass params to the bus reset routine */ ResetParams.PathId = LunExtension->PathId; ResetParams.DeviceExtension = DeviceExtension; if (!KeSynchronizeExecution(DeviceExtension->Interrupt[0], SpiResetBus, &ResetParams)) { DPRINT1("Reset failed\n"); } } else if (LunExtension->RequestTimeout > 0) { /* Decrement the timeout counter */ LunExtension->RequestTimeout--; } LunExtension = LunExtension->Next; } } /* Release the spinlock */ KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock); } /********************************************************************** * NAME INTERNAL * SpiBuildDeviceMap * * DESCRIPTION * Builds the registry device map of all device which are attached * to the given SCSI HBA port. The device map is located at: * \Registry\Machine\DeviceMap\Scsi * * RUN LEVEL * PASSIVE_LEVEL * * ARGUMENTS * DeviceExtension * ... * * RegistryPath * Name of registry driver service key. * * RETURNS * NTSTATUS */ static NTSTATUS SpiBuildDeviceMap(IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, IN PUNICODE_STRING RegistryPath) { PSCSI_PORT_LUN_EXTENSION LunExtension; OBJECT_ATTRIBUTES ObjectAttributes; UNICODE_STRING KeyName; UNICODE_STRING ValueName; WCHAR NameBuffer[64]; ULONG Disposition; HANDLE ScsiKey; HANDLE ScsiPortKey = NULL; HANDLE ScsiBusKey = NULL; HANDLE ScsiInitiatorKey = NULL; HANDLE ScsiTargetKey = NULL; HANDLE ScsiLunKey = NULL; ULONG BusNumber; ULONG Target; ULONG CurrentTarget; ULONG Lun; PWCHAR DriverName; ULONG UlongData; PWCHAR TypeName; NTSTATUS Status; DPRINT("SpiBuildDeviceMap() called\n"); if (DeviceExtension == NULL || RegistryPath == NULL) { DPRINT1("Invalid parameter\n"); return STATUS_INVALID_PARAMETER; } /* Open or create the 'Scsi' subkey */ RtlInitUnicodeString(&KeyName, L"\\Registry\\Machine\\Hardware\\DeviceMap\\Scsi"); InitializeObjectAttributes(&ObjectAttributes, &KeyName, OBJ_CASE_INSENSITIVE | OBJ_OPENIF | OBJ_KERNEL_HANDLE, 0, NULL); Status = ZwCreateKey(&ScsiKey, KEY_ALL_ACCESS, &ObjectAttributes, 0, NULL, REG_OPTION_VOLATILE, &Disposition); if (!NT_SUCCESS(Status)) { DPRINT("ZwCreateKey() failed (Status %lx)\n", Status); return Status; } /* Create new 'Scsi Port X' subkey */ DPRINT("Scsi Port %lu\n", DeviceExtension->PortNumber); swprintf(NameBuffer, L"Scsi Port %lu", DeviceExtension->PortNumber); RtlInitUnicodeString(&KeyName, NameBuffer); InitializeObjectAttributes(&ObjectAttributes, &KeyName, OBJ_KERNEL_HANDLE, ScsiKey, NULL); Status = ZwCreateKey(&ScsiPortKey, KEY_ALL_ACCESS, &ObjectAttributes, 0, NULL, REG_OPTION_VOLATILE, &Disposition); ZwClose(ScsiKey); if (!NT_SUCCESS(Status)) { DPRINT("ZwCreateKey() failed (Status %lx)\n", Status); return Status; } /* * Create port-specific values */ /* Set 'DMA Enabled' (REG_DWORD) value */ UlongData = (ULONG)!DeviceExtension->PortCapabilities.AdapterUsesPio; DPRINT(" DMA Enabled = %s\n", UlongData ? "TRUE" : "FALSE"); RtlInitUnicodeString(&ValueName, L"DMA Enabled"); Status = ZwSetValueKey(ScsiPortKey, &ValueName, 0, REG_DWORD, &UlongData, sizeof(UlongData)); if (!NT_SUCCESS(Status)) { DPRINT("ZwSetValueKey('DMA Enabled') failed (Status %lx)\n", Status); ZwClose(ScsiPortKey); return Status; } /* Set 'Driver' (REG_SZ) value */ DriverName = wcsrchr(RegistryPath->Buffer, L'\\') + 1; RtlInitUnicodeString(&ValueName, L"Driver"); Status = ZwSetValueKey(ScsiPortKey, &ValueName, 0, REG_SZ, DriverName, (ULONG)((wcslen(DriverName) + 1) * sizeof(WCHAR))); if (!NT_SUCCESS(Status)) { DPRINT("ZwSetValueKey('Driver') failed (Status %lx)\n", Status); ZwClose(ScsiPortKey); return Status; } /* Set 'Interrupt' (REG_DWORD) value (NT4 only) */ UlongData = (ULONG)DeviceExtension->PortConfig->BusInterruptLevel; DPRINT(" Interrupt = %lu\n", UlongData); RtlInitUnicodeString(&ValueName, L"Interrupt"); Status = ZwSetValueKey(ScsiPortKey, &ValueName, 0, REG_DWORD, &UlongData, sizeof(UlongData)); if (!NT_SUCCESS(Status)) { DPRINT("ZwSetValueKey('Interrupt') failed (Status %lx)\n", Status); ZwClose(ScsiPortKey); return Status; } /* Set 'IOAddress' (REG_DWORD) value (NT4 only) */ UlongData = ScsiPortConvertPhysicalAddressToUlong((*DeviceExtension->PortConfig->AccessRanges)[0].RangeStart); DPRINT(" IOAddress = %lx\n", UlongData); RtlInitUnicodeString(&ValueName, L"IOAddress"); Status = ZwSetValueKey(ScsiPortKey, &ValueName, 0, REG_DWORD, &UlongData, sizeof(UlongData)); if (!NT_SUCCESS(Status)) { DPRINT("ZwSetValueKey('IOAddress') failed (Status %lx)\n", Status); ZwClose(ScsiPortKey); return Status; } /* Enumerate buses */ for (BusNumber = 0; BusNumber < DeviceExtension->PortConfig->NumberOfBuses; BusNumber++) { /* Create 'Scsi Bus X' key */ DPRINT(" Scsi Bus %lu\n", BusNumber); swprintf(NameBuffer, L"Scsi Bus %lu", BusNumber); RtlInitUnicodeString(&KeyName, NameBuffer); InitializeObjectAttributes(&ObjectAttributes, &KeyName, 0, ScsiPortKey, NULL); Status = ZwCreateKey(&ScsiBusKey, KEY_ALL_ACCESS, &ObjectAttributes, 0, NULL, REG_OPTION_VOLATILE, &Disposition); if (!NT_SUCCESS(Status)) { DPRINT("ZwCreateKey() failed (Status %lx)\n", Status); goto ByeBye; } /* Create 'Initiator Id X' key */ DPRINT(" Initiator Id %lu\n", DeviceExtension->PortConfig->InitiatorBusId[BusNumber]); swprintf(NameBuffer, L"Initiator Id %lu", (ULONG)(UCHAR)DeviceExtension->PortConfig->InitiatorBusId[BusNumber]); RtlInitUnicodeString(&KeyName, NameBuffer); InitializeObjectAttributes(&ObjectAttributes, &KeyName, 0, ScsiBusKey, NULL); Status = ZwCreateKey(&ScsiInitiatorKey, KEY_ALL_ACCESS, &ObjectAttributes, 0, NULL, REG_OPTION_VOLATILE, &Disposition); if (!NT_SUCCESS(Status)) { DPRINT("ZwCreateKey() failed (Status %lx)\n", Status); goto ByeBye; } /* FIXME: Are there any initiator values (??) */ ZwClose(ScsiInitiatorKey); ScsiInitiatorKey = NULL; /* Enumerate targets */ CurrentTarget = (ULONG)-1; ScsiTargetKey = NULL; for (Target = 0; Target < DeviceExtension->PortConfig->MaximumNumberOfTargets; Target++) { for (Lun = 0; Lun < SCSI_MAXIMUM_LOGICAL_UNITS; Lun++) { LunExtension = SpiGetLunExtension(DeviceExtension, (UCHAR)BusNumber, (UCHAR)Target, (UCHAR)Lun); if (LunExtension == NULL) continue; if (Target != CurrentTarget) { /* Close old target key */ if (ScsiTargetKey != NULL) { ZwClose(ScsiTargetKey); ScsiTargetKey = NULL; } /* Create 'Target Id X' key */ DPRINT(" Target Id %lu\n", Target); swprintf(NameBuffer, L"Target Id %lu", Target); RtlInitUnicodeString(&KeyName, NameBuffer); InitializeObjectAttributes(&ObjectAttributes, &KeyName, 0, ScsiBusKey, NULL); Status = ZwCreateKey(&ScsiTargetKey, KEY_ALL_ACCESS, &ObjectAttributes, 0, NULL, REG_OPTION_VOLATILE, &Disposition); if (!NT_SUCCESS(Status)) { DPRINT("ZwCreateKey() failed (Status %lx)\n", Status); goto ByeBye; } CurrentTarget = Target; } /* Create 'Logical Unit Id X' key */ DPRINT(" Logical Unit Id %lu\n", Lun); swprintf(NameBuffer, L"Logical Unit Id %lu", Lun); RtlInitUnicodeString(&KeyName, NameBuffer); InitializeObjectAttributes(&ObjectAttributes, &KeyName, 0, ScsiTargetKey, NULL); Status = ZwCreateKey(&ScsiLunKey, KEY_ALL_ACCESS, &ObjectAttributes, 0, NULL, REG_OPTION_VOLATILE, &Disposition); if (!NT_SUCCESS(Status)) { DPRINT("ZwCreateKey() failed (Status %lx)\n", Status); goto ByeBye; } /* Set 'Identifier' (REG_SZ) value */ swprintf(NameBuffer, L"%.8S%.16S%.4S", LunExtension->InquiryData.VendorId, LunExtension->InquiryData.ProductId, LunExtension->InquiryData.ProductRevisionLevel); DPRINT(" Identifier = '%S'\n", NameBuffer); RtlInitUnicodeString(&ValueName, L"Identifier"); Status = ZwSetValueKey(ScsiLunKey, &ValueName, 0, REG_SZ, NameBuffer, (ULONG)((wcslen(NameBuffer) + 1) * sizeof(WCHAR))); if (!NT_SUCCESS(Status)) { DPRINT("ZwSetValueKey('Identifier') failed (Status %lx)\n", Status); goto ByeBye; } /* Set 'Type' (REG_SZ) value */ /* * See https://docs.microsoft.com/en-us/windows-hardware/drivers/install/identifiers-for-ide-devices * and https://docs.microsoft.com/en-us/windows-hardware/drivers/install/identifiers-for-scsi-devices * for a list of types with their human-readable forms. */ switch (LunExtension->InquiryData.DeviceType) { case 0: TypeName = L"DiskPeripheral"; break; case 1: TypeName = L"TapePeripheral"; break; case 2: TypeName = L"PrinterPeripheral"; break; // case 3: "ProcessorPeripheral", classified as 'other': fall back to default case. case 4: TypeName = L"WormPeripheral"; break; case 5: TypeName = L"CdRomPeripheral"; break; case 6: TypeName = L"ScannerPeripheral"; break; case 7: TypeName = L"OpticalDiskPeripheral"; break; case 8: TypeName = L"MediumChangerPeripheral"; break; case 9: TypeName = L"CommunicationsPeripheral"; break; /* New peripheral types (SCSI only) */ case 10: case 11: TypeName = L"ASCPrePressGraphicsPeripheral"; break; case 12: TypeName = L"ArrayPeripheral"; break; case 13: TypeName = L"EnclosurePeripheral"; break; case 14: TypeName = L"RBCPeripheral"; break; case 15: TypeName = L"CardReaderPeripheral"; break; case 16: TypeName = L"BridgePeripheral"; break; default: TypeName = L"OtherPeripheral"; break; } DPRINT(" Type = '%S'\n", TypeName); RtlInitUnicodeString(&ValueName, L"Type"); Status = ZwSetValueKey(ScsiLunKey, &ValueName, 0, REG_SZ, TypeName, (ULONG)((wcslen(TypeName) + 1) * sizeof(WCHAR))); if (!NT_SUCCESS(Status)) { DPRINT("ZwSetValueKey('Type') failed (Status %lx)\n", Status); goto ByeBye; } ZwClose(ScsiLunKey); ScsiLunKey = NULL; } /* Close old target key */ if (ScsiTargetKey != NULL) { ZwClose(ScsiTargetKey); ScsiTargetKey = NULL; } } ZwClose(ScsiBusKey); ScsiBusKey = NULL; } ByeBye: if (ScsiLunKey != NULL) ZwClose(ScsiLunKey); if (ScsiInitiatorKey != NULL) ZwClose(ScsiInitiatorKey); if (ScsiTargetKey != NULL) ZwClose(ScsiTargetKey); if (ScsiBusKey != NULL) ZwClose(ScsiBusKey); if (ScsiPortKey != NULL) ZwClose(ScsiPortKey); DPRINT("SpiBuildDeviceMap() done\n"); return Status; } VOID NTAPI SpiMiniportTimerDpc(IN struct _KDPC *Dpc, IN PVOID DeviceObject, IN PVOID SystemArgument1, IN PVOID SystemArgument2) { PSCSI_PORT_DEVICE_EXTENSION DeviceExtension; DPRINT("Miniport timer DPC\n"); DeviceExtension = ((PDEVICE_OBJECT)DeviceObject)->DeviceExtension; /* Acquire the spinlock */ KeAcquireSpinLockAtDpcLevel(&DeviceExtension->SpinLock); /* Call the timer routine */ if (DeviceExtension->HwScsiTimer != NULL) { DeviceExtension->HwScsiTimer(&DeviceExtension->MiniPortDeviceExtension); } /* Release the spinlock */ KeReleaseSpinLockFromDpcLevel(&DeviceExtension->SpinLock); if (DeviceExtension->InterruptData.Flags & SCSI_PORT_NOTIFICATION_NEEDED) { ScsiPortDpcForIsr(NULL, DeviceExtension->DeviceObject, NULL, NULL); } } static NTSTATUS SpiCreatePortConfig(PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, PHW_INITIALIZATION_DATA HwInitData, PCONFIGURATION_INFO InternalConfigInfo, PPORT_CONFIGURATION_INFORMATION ConfigInfo, BOOLEAN ZeroStruct) { UNICODE_STRING UnicodeString; OBJECT_ATTRIBUTES ObjectAttributes; PCONFIGURATION_INFORMATION DdkConfigInformation; HANDLE RootKey, Key; BOOLEAN Found; WCHAR DeviceBuffer[16]; WCHAR StrBuffer[512]; ULONG Bus; NTSTATUS Status; /* Zero out the struct if told so */ if (ZeroStruct) { /* First zero the portconfig */ RtlZeroMemory(ConfigInfo, sizeof(PORT_CONFIGURATION_INFORMATION)); /* Then access ranges */ RtlZeroMemory(InternalConfigInfo->AccessRanges, HwInitData->NumberOfAccessRanges * sizeof(ACCESS_RANGE)); /* Initialize the struct */ ConfigInfo->Length = sizeof(PORT_CONFIGURATION_INFORMATION); ConfigInfo->AdapterInterfaceType = HwInitData->AdapterInterfaceType; ConfigInfo->InterruptMode = Latched; ConfigInfo->DmaChannel = SP_UNINITIALIZED_VALUE; ConfigInfo->DmaPort = SP_UNINITIALIZED_VALUE; ConfigInfo->DmaChannel2 = SP_UNINITIALIZED_VALUE; ConfigInfo->DmaPort2 = SP_UNINITIALIZED_VALUE; ConfigInfo->MaximumTransferLength = SP_UNINITIALIZED_VALUE; ConfigInfo->NumberOfAccessRanges = HwInitData->NumberOfAccessRanges; ConfigInfo->MaximumNumberOfTargets = 8; /* Store parameters */ ConfigInfo->NeedPhysicalAddresses = HwInitData->NeedPhysicalAddresses; ConfigInfo->MapBuffers = HwInitData->MapBuffers; ConfigInfo->AutoRequestSense = HwInitData->AutoRequestSense; ConfigInfo->ReceiveEvent = HwInitData->ReceiveEvent; ConfigInfo->TaggedQueuing = HwInitData->TaggedQueuing; ConfigInfo->MultipleRequestPerLu = HwInitData->MultipleRequestPerLu; /* Get the disk usage */ DdkConfigInformation = IoGetConfigurationInformation(); ConfigInfo->AtdiskPrimaryClaimed = DdkConfigInformation->AtDiskPrimaryAddressClaimed; ConfigInfo->AtdiskSecondaryClaimed = DdkConfigInformation->AtDiskSecondaryAddressClaimed; /* Initiator bus id is not set */ for (Bus = 0; Bus < 8; Bus++) ConfigInfo->InitiatorBusId[Bus] = (CCHAR)SP_UNINITIALIZED_VALUE; } ConfigInfo->NumberOfPhysicalBreaks = 17; /* Clear this information */ InternalConfigInfo->DisableTaggedQueueing = FALSE; InternalConfigInfo->DisableMultipleLun = FALSE; /* Store Bus Number */ ConfigInfo->SystemIoBusNumber = InternalConfigInfo->BusNumber; TryNextAd: if (ConfigInfo->AdapterInterfaceType == Internal) { /* Open registry key for HW database */ InitializeObjectAttributes(&ObjectAttributes, DeviceExtension->DeviceObject->DriverObject->HardwareDatabase, OBJ_CASE_INSENSITIVE, NULL, NULL); Status = ZwOpenKey(&RootKey, KEY_READ, &ObjectAttributes); if (NT_SUCCESS(Status)) { /* Create name for it */ swprintf(StrBuffer, L"ScsiAdapter\\%lu", InternalConfigInfo->AdapterNumber); RtlInitUnicodeString(&UnicodeString, StrBuffer); /* Open device key */ InitializeObjectAttributes(&ObjectAttributes, &UnicodeString, OBJ_CASE_INSENSITIVE, RootKey, NULL); Status = ZwOpenKey(&Key, KEY_READ, &ObjectAttributes); ZwClose(RootKey); if (NT_SUCCESS(Status)) { if (InternalConfigInfo->LastAdapterNumber != InternalConfigInfo->AdapterNumber) { DPRINT("Hardware info found at %S\n", StrBuffer); /* Parse it */ SpiParseDeviceInfo(DeviceExtension, Key, ConfigInfo, InternalConfigInfo, (PUCHAR)StrBuffer); InternalConfigInfo->BusNumber = 0; } else { /* Try the next adapter */ InternalConfigInfo->AdapterNumber++; goto TryNextAd; } } else { /* Info was not found, exit */ DPRINT1("ZwOpenKey() failed with Status=0x%08X\n", Status); return STATUS_DEVICE_DOES_NOT_EXIST; } } else { DPRINT1("ZwOpenKey() failed with Status=0x%08X\n", Status); } } /* Look at device params */ Key = NULL; if (InternalConfigInfo->Parameter) { ExFreePool(InternalConfigInfo->Parameter); InternalConfigInfo->Parameter = NULL; } if (InternalConfigInfo->ServiceKey != NULL) { swprintf(DeviceBuffer, L"Device%lu", InternalConfigInfo->AdapterNumber); RtlInitUnicodeString(&UnicodeString, DeviceBuffer); /* Open the service key */ InitializeObjectAttributes(&ObjectAttributes, &UnicodeString, OBJ_CASE_INSENSITIVE, InternalConfigInfo->ServiceKey, NULL); Status = ZwOpenKey(&Key, KEY_READ, &ObjectAttributes); } /* Parse device key */ if (InternalConfigInfo->DeviceKey != NULL) { SpiParseDeviceInfo(DeviceExtension, InternalConfigInfo->DeviceKey, ConfigInfo, InternalConfigInfo, (PUCHAR)StrBuffer); } /* Then parse hw info */ if (Key != NULL) { if (InternalConfigInfo->LastAdapterNumber != InternalConfigInfo->AdapterNumber) { SpiParseDeviceInfo(DeviceExtension, Key, ConfigInfo, InternalConfigInfo, (PUCHAR)StrBuffer); /* Close the key */ ZwClose(Key); } else { /* Adapter not found, go try the next one */ InternalConfigInfo->AdapterNumber++; /* Close the key */ ZwClose(Key); goto TryNextAd; } } /* Update the last adapter number */ InternalConfigInfo->LastAdapterNumber = InternalConfigInfo->AdapterNumber; /* Do we have this kind of bus at all? */ Found = FALSE; Status = IoQueryDeviceDescription(&HwInitData->AdapterInterfaceType, &InternalConfigInfo->BusNumber, NULL, NULL, NULL, NULL, SpQueryDeviceCallout, &Found); /* This bus was not found */ if (!Found) { INTERFACE_TYPE InterfaceType = Eisa; /* Check for EISA */ if (HwInitData->AdapterInterfaceType == Isa) { Status = IoQueryDeviceDescription(&InterfaceType, &InternalConfigInfo->BusNumber, NULL, NULL, NULL, NULL, SpQueryDeviceCallout, &Found); /* Return respectively */ if (Found) return STATUS_SUCCESS; else return STATUS_DEVICE_DOES_NOT_EXIST; } else { return STATUS_DEVICE_DOES_NOT_EXIST; } } else { return STATUS_SUCCESS; } } static VOID SpiParseDeviceInfo(IN PSCSI_PORT_DEVICE_EXTENSION DeviceExtension, IN HANDLE Key, IN PPORT_CONFIGURATION_INFORMATION ConfigInfo, IN PCONFIGURATION_INFO InternalConfigInfo, IN PUCHAR Buffer) { PKEY_VALUE_FULL_INFORMATION KeyValueInformation; PCM_FULL_RESOURCE_DESCRIPTOR FullResource; PCM_PARTIAL_RESOURCE_DESCRIPTOR PartialDescriptor; PCM_SCSI_DEVICE_DATA ScsiDeviceData; ULONG Length, Count, Dma = 0, Interrupt = 0; ULONG Index = 0, RangeCount = 0; UNICODE_STRING UnicodeString; ANSI_STRING AnsiString; NTSTATUS Status = STATUS_SUCCESS; KeyValueInformation = (PKEY_VALUE_FULL_INFORMATION) Buffer; /* Loop through all values in the device node */ while(TRUE) { Status = ZwEnumerateValueKey(Key, Index, KeyValueFullInformation, Buffer, 512, &Length); if (!NT_SUCCESS(Status)) return; Index++; /* Length for DWORD is ok? */ if (KeyValueInformation->Type == REG_DWORD && KeyValueInformation->DataLength != sizeof(ULONG)) { continue; } /* Get MaximumLogicalUnit */ if (_wcsnicmp(KeyValueInformation->Name, L"MaximumLogicalUnit", KeyValueInformation->NameLength/2) == 0) { if (KeyValueInformation->Type != REG_DWORD) { DPRINT("Bad data type for MaximumLogicalUnit\n"); continue; } DeviceExtension->MaxLunCount = *((PUCHAR) (Buffer + KeyValueInformation->DataOffset)); /* Check / reset if needed */ if (DeviceExtension->MaxLunCount > SCSI_MAXIMUM_LOGICAL_UNITS) DeviceExtension->MaxLunCount = SCSI_MAXIMUM_LOGICAL_UNITS; DPRINT("MaximumLogicalUnit = %d\n", DeviceExtension->MaxLunCount); } /* Get InitiatorTargetId */ if (_wcsnicmp(KeyValueInformation->Name, L"InitiatorTargetId", KeyValueInformation->NameLength / 2) == 0) { if (KeyValueInformation->Type != REG_DWORD) { DPRINT("Bad data type for InitiatorTargetId\n"); continue; } ConfigInfo->InitiatorBusId[0] = *((PUCHAR) (Buffer + KeyValueInformation->DataOffset)); /* Check / reset if needed */ if (ConfigInfo->InitiatorBusId[0] > ConfigInfo->MaximumNumberOfTargets - 1) ConfigInfo->InitiatorBusId[0] = (CCHAR)-1; DPRINT("InitiatorTargetId = %d\n", ConfigInfo->InitiatorBusId[0]); } /* Get ScsiDebug */ if (_wcsnicmp(KeyValueInformation->Name, L"ScsiDebug", KeyValueInformation->NameLength/2) == 0) { DPRINT("ScsiDebug key not supported\n"); } /* Check for a breakpoint */ if (_wcsnicmp(KeyValueInformation->Name, L"BreakPointOnEntry", KeyValueInformation->NameLength/2) == 0) { DPRINT1("Breakpoint on entry requested!\n"); DbgBreakPoint(); } /* Get DisableSynchronousTransfers */ if (_wcsnicmp(KeyValueInformation->Name, L"DisableSynchronousTransfers", KeyValueInformation->NameLength/2) == 0) { DeviceExtension->SrbFlags |= SRB_FLAGS_DISABLE_SYNCH_TRANSFER; DPRINT("Synch transfers disabled\n"); } /* Get DisableDisconnects */ if (_wcsnicmp(KeyValueInformation->Name, L"DisableDisconnects", KeyValueInformation->NameLength/2) == 0) { DeviceExtension->SrbFlags |= SRB_FLAGS_DISABLE_DISCONNECT; DPRINT("Disconnects disabled\n"); } /* Get DisableTaggedQueuing */ if (_wcsnicmp(KeyValueInformation->Name, L"DisableTaggedQueuing", KeyValueInformation->NameLength/2) == 0) { InternalConfigInfo->DisableTaggedQueueing = TRUE; DPRINT("Tagged queueing disabled\n"); } /* Get DisableMultipleRequests */ if (_wcsnicmp(KeyValueInformation->Name, L"DisableMultipleRequests", KeyValueInformation->NameLength/2) == 0) { InternalConfigInfo->DisableMultipleLun = TRUE; DPRINT("Multiple requests disabled\n"); } /* Get DriverParameters */ if (_wcsnicmp(KeyValueInformation->Name, L"DriverParameters", KeyValueInformation->NameLength/2) == 0) { /* Skip if nothing */ if (KeyValueInformation->DataLength == 0) continue; /* If there was something previously allocated - free it */ if (InternalConfigInfo->Parameter != NULL) ExFreePool(InternalConfigInfo->Parameter); /* Allocate it */ InternalConfigInfo->Parameter = ExAllocatePoolWithTag(NonPagedPool, KeyValueInformation->DataLength, TAG_SCSIPORT); if (InternalConfigInfo->Parameter != NULL) { if (KeyValueInformation->Type != REG_SZ) { /* Just copy */ RtlCopyMemory( InternalConfigInfo->Parameter, (PCCHAR)KeyValueInformation + KeyValueInformation->DataOffset, KeyValueInformation->DataLength); } else { /* If it's a unicode string, convert it to ansi */ UnicodeString.Length = (USHORT)KeyValueInformation->DataLength; UnicodeString.MaximumLength = (USHORT)KeyValueInformation->DataLength; UnicodeString.Buffer = (PWSTR)((PCCHAR)KeyValueInformation + KeyValueInformation->DataOffset); AnsiString.Length = 0; AnsiString.MaximumLength = (USHORT)KeyValueInformation->DataLength; AnsiString.Buffer = (PCHAR)InternalConfigInfo->Parameter; Status = RtlUnicodeStringToAnsiString(&AnsiString, &UnicodeString, FALSE); /* In case of error, free the allocated space */ if (!NT_SUCCESS(Status)) { ExFreePool(InternalConfigInfo->Parameter); InternalConfigInfo->Parameter = NULL; } } } DPRINT("Found driver parameter\n"); } /* Get MaximumSGList */ if (_wcsnicmp(KeyValueInformation->Name, L"MaximumSGList", KeyValueInformation->NameLength/2) == 0) { if (KeyValueInformation->Type != REG_DWORD) { DPRINT("Bad data type for MaximumSGList\n"); continue; } ConfigInfo->NumberOfPhysicalBreaks = *((PUCHAR)(Buffer + KeyValueInformation->DataOffset)); /* Check / fix */ if (ConfigInfo->NumberOfPhysicalBreaks > SCSI_MAXIMUM_PHYSICAL_BREAKS) { ConfigInfo->NumberOfPhysicalBreaks = SCSI_MAXIMUM_PHYSICAL_BREAKS; } else if (ConfigInfo->NumberOfPhysicalBreaks < SCSI_MINIMUM_PHYSICAL_BREAKS) { ConfigInfo->NumberOfPhysicalBreaks = SCSI_MINIMUM_PHYSICAL_BREAKS; } DPRINT("MaximumSGList = %d\n", ConfigInfo->NumberOfPhysicalBreaks); } /* Get NumberOfRequests */ if (_wcsnicmp(KeyValueInformation->Name, L"NumberOfRequests", KeyValueInformation->NameLength/2) == 0) { if (KeyValueInformation->Type != REG_DWORD) { DPRINT("NumberOfRequests has wrong data type\n"); continue; } DeviceExtension->RequestsNumber = *((PUCHAR)(Buffer + KeyValueInformation->DataOffset)); /* Check / fix */ if (DeviceExtension->RequestsNumber < 16) { DeviceExtension->RequestsNumber = 16; } else if (DeviceExtension->RequestsNumber > 512) { DeviceExtension->RequestsNumber = 512; } DPRINT("Number Of Requests = %d\n", DeviceExtension->RequestsNumber); } /* Get 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)) { DPRINT("Bad data type for ResourceList\n"); continue; } else { DPRINT("Found ResourceList\n"); } FullResource = (PCM_FULL_RESOURCE_DESCRIPTOR)(Buffer + KeyValueInformation->DataOffset); /* Copy some info from it */ InternalConfigInfo->BusNumber = FullResource->BusNumber; ConfigInfo->SystemIoBusNumber = FullResource->BusNumber; /* Loop through it */ for (Count = 0; Count < FullResource->PartialResourceList.Count; Count++) { /* Get partial descriptor */ PartialDescriptor = &FullResource->PartialResourceList.PartialDescriptors[Count]; /* Check datalength */ if ((ULONG)((PCHAR)(PartialDescriptor + 1) - (PCHAR)FullResource) > KeyValueInformation->DataLength) { DPRINT("Resource data is of incorrect size\n"); break; } switch (PartialDescriptor->Type) { case CmResourceTypePort: if (RangeCount >= ConfigInfo->NumberOfAccessRanges) { DPRINT("Too many access ranges\n"); continue; } InternalConfigInfo->AccessRanges[RangeCount].RangeInMemory = FALSE; InternalConfigInfo->AccessRanges[RangeCount].RangeStart = PartialDescriptor->u.Port.Start; InternalConfigInfo->AccessRanges[RangeCount].RangeLength = PartialDescriptor->u.Port.Length; RangeCount++; break; case CmResourceTypeMemory: if (RangeCount >= ConfigInfo->NumberOfAccessRanges) { DPRINT("Too many access ranges\n"); continue; } InternalConfigInfo->AccessRanges[RangeCount].RangeInMemory = TRUE; InternalConfigInfo->AccessRanges[RangeCount].RangeStart = PartialDescriptor->u.Memory.Start; InternalConfigInfo->AccessRanges[RangeCount].RangeLength = PartialDescriptor->u.Memory.Length; RangeCount++; break; case CmResourceTypeInterrupt: if (Interrupt == 0) { ConfigInfo->BusInterruptLevel = PartialDescriptor->u.Interrupt.Level; ConfigInfo->BusInterruptVector = PartialDescriptor->u.Interrupt.Vector; ConfigInfo->InterruptMode = (PartialDescriptor->Flags & CM_RESOURCE_INTERRUPT_LATCHED) ? Latched : LevelSensitive; } else if (Interrupt == 1) { ConfigInfo->BusInterruptLevel2 = PartialDescriptor->u.Interrupt.Level; ConfigInfo->BusInterruptVector2 = PartialDescriptor->u.Interrupt.Vector; ConfigInfo->InterruptMode2 = (PartialDescriptor->Flags & CM_RESOURCE_INTERRUPT_LATCHED) ? Latched : LevelSensitive; } Interrupt++; break; case CmResourceTypeDma: if (Dma == 0) { ConfigInfo->DmaChannel = PartialDescriptor->u.Dma.Channel; ConfigInfo->DmaPort = PartialDescriptor->u.Dma.Port; if (PartialDescriptor->Flags & CM_RESOURCE_DMA_8) ConfigInfo->DmaWidth = Width8Bits; else if ((PartialDescriptor->Flags & CM_RESOURCE_DMA_16) || (PartialDescriptor->Flags & CM_RESOURCE_DMA_8_AND_16)) //??? ConfigInfo->DmaWidth = Width16Bits; else if (PartialDescriptor->Flags & CM_RESOURCE_DMA_32) ConfigInfo->DmaWidth = Width32Bits; } else if (Dma == 1) { ConfigInfo->DmaChannel2 = PartialDescriptor->u.Dma.Channel; ConfigInfo->DmaPort2 = PartialDescriptor->u.Dma.Port; if (PartialDescriptor->Flags & CM_RESOURCE_DMA_8) ConfigInfo->DmaWidth2 = Width8Bits; else if ((PartialDescriptor->Flags & CM_RESOURCE_DMA_16) || (PartialDescriptor->Flags & CM_RESOURCE_DMA_8_AND_16)) //??? ConfigInfo->DmaWidth2 = Width16Bits; else if (PartialDescriptor->Flags & CM_RESOURCE_DMA_32) ConfigInfo->DmaWidth2 = Width32Bits; } Dma++; break; case CmResourceTypeDeviceSpecific: if (PartialDescriptor->u.DeviceSpecificData.DataSize < sizeof(CM_SCSI_DEVICE_DATA) || (PCHAR) (PartialDescriptor + 1) - (PCHAR)FullResource + PartialDescriptor->u.DeviceSpecificData.DataSize > KeyValueInformation->DataLength) { DPRINT("Resource data length is incorrect"); break; } /* Set only one field from it */ ScsiDeviceData = (PCM_SCSI_DEVICE_DATA) (PartialDescriptor+1); ConfigInfo->InitiatorBusId[0] = ScsiDeviceData->HostIdentifier; break; } } } } } NTSTATUS NTAPI SpQueryDeviceCallout(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) { PBOOLEAN Found = (PBOOLEAN)Context; /* We just set our Found variable to TRUE */ *Found = TRUE; return STATUS_SUCCESS; } IO_ALLOCATION_ACTION NTAPI ScsiPortAllocateAdapterChannel(IN PDEVICE_OBJECT DeviceObject, IN PIRP Irp, IN PVOID MapRegisterBase, IN PVOID Context) { KIRQL Irql; PSCSI_PORT_DEVICE_EXTENSION DeviceExtension = DeviceObject->DeviceExtension; /* Guard access with the spinlock */ KeAcquireSpinLock(&DeviceExtension->SpinLock, &Irql); /* Save MapRegisterBase we've got here */ DeviceExtension->MapRegisterBase = MapRegisterBase; /* Start pending request */ KeSynchronizeExecution(DeviceExtension->Interrupt[0], ScsiPortStartPacket, DeviceObject); /* Release spinlock we took */ KeReleaseSpinLock(&DeviceExtension->SpinLock, Irql); return KeepObject; } static NTSTATUS SpiStatusSrbToNt(UCHAR SrbStatus) { switch (SRB_STATUS(SrbStatus)) { case SRB_STATUS_TIMEOUT: case SRB_STATUS_COMMAND_TIMEOUT: return STATUS_IO_TIMEOUT; case SRB_STATUS_BAD_SRB_BLOCK_LENGTH: case SRB_STATUS_BAD_FUNCTION: return STATUS_INVALID_DEVICE_REQUEST; case SRB_STATUS_NO_DEVICE: case SRB_STATUS_INVALID_LUN: case SRB_STATUS_INVALID_TARGET_ID: case SRB_STATUS_NO_HBA: return STATUS_DEVICE_DOES_NOT_EXIST; case SRB_STATUS_DATA_OVERRUN: return STATUS_BUFFER_OVERFLOW; case SRB_STATUS_SELECTION_TIMEOUT: return STATUS_DEVICE_NOT_CONNECTED; default: return STATUS_IO_DEVICE_ERROR; } return STATUS_IO_DEVICE_ERROR; } #undef ScsiPortConvertPhysicalAddressToUlong /* * @implemented */ ULONG NTAPI ScsiPortConvertPhysicalAddressToUlong(IN SCSI_PHYSICAL_ADDRESS Address) { DPRINT("ScsiPortConvertPhysicalAddressToUlong()\n"); return(Address.u.LowPart); } /* EOF */