mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3136 lines
78 KiB
3136 lines
78 KiB
/*++
|
|
|
|
Copyright (C) Microsoft Corporation, 1990 - 1999
|
|
|
|
Module Name:
|
|
|
|
port.c
|
|
|
|
Abstract:
|
|
|
|
This is the NT SCSI port driver.
|
|
|
|
Authors:
|
|
|
|
Mike Glass
|
|
Jeff Havens
|
|
|
|
Environment:
|
|
|
|
kernel mode only
|
|
|
|
Notes:
|
|
|
|
This module is a dll for the kernel.
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "port.h"
|
|
|
|
#if DBG
|
|
static const char *__file__ = __FILE__;
|
|
#endif
|
|
|
|
#define __FILE_ID__ 'util'
|
|
|
|
typedef struct SP_GUID_INTERFACE_MAPPING {
|
|
GUID Guid;
|
|
INTERFACE_TYPE InterfaceType;
|
|
} SP_GUID_INTERFACE_MAPPING, *PSP_GUID_INTERFACE_MAPPING;
|
|
|
|
PSP_GUID_INTERFACE_MAPPING SpGuidInterfaceMappingList = NULL;
|
|
|
|
VOID
|
|
SpProcessSpecialControllerList(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PINQUIRYDATA InquiryData,
|
|
IN HANDLE ListKey,
|
|
OUT PSP_SPECIAL_CONTROLLER_FLAGS Flags
|
|
);
|
|
|
|
VOID
|
|
SpProcessSpecialControllerFlags(
|
|
IN HANDLE FlagsKey,
|
|
OUT PSP_SPECIAL_CONTROLLER_FLAGS Flags
|
|
);
|
|
|
|
|
|
NTSTATUS
|
|
ScsiPortBuildMultiString(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
PUNICODE_STRING MultiString,
|
|
...
|
|
);
|
|
|
|
NTSTATUS
|
|
SpMultiStringToStringArray(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PUNICODE_STRING MultiString,
|
|
OUT PWSTR *StringArray[],
|
|
BOOLEAN Forward
|
|
);
|
|
|
|
VOID
|
|
FASTCALL
|
|
SpFreeSrbData(
|
|
IN PADAPTER_EXTENSION Adapter,
|
|
IN PSRB_DATA SrbData
|
|
);
|
|
|
|
VOID
|
|
FASTCALL
|
|
SpFreeBypassSrbData(
|
|
IN PADAPTER_EXTENSION Adapter,
|
|
IN PSRB_DATA SrbData
|
|
);
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, ScsiPortBuildMultiString)
|
|
#pragma alloc_text(PAGE, ScsiPortStringArrayToMultiString)
|
|
#pragma alloc_text(PAGE, SpMultiStringToStringArray)
|
|
#pragma alloc_text(PAGE, RtlDuplicateCmResourceList)
|
|
#pragma alloc_text(PAGE, RtlSizeOfCmResourceList)
|
|
#pragma alloc_text(PAGE, SpTranslateResources)
|
|
#pragma alloc_text(PAGE, SpCheckSpecialDeviceFlags)
|
|
#pragma alloc_text(PAGE, SpProcessSpecialControllerList)
|
|
#pragma alloc_text(PAGE, SpProcessSpecialControllerFlags)
|
|
#pragma alloc_text(PAGE, SpAllocateTagBitMap)
|
|
#pragma alloc_text(PAGE, SpGetPdoInterfaceType)
|
|
#pragma alloc_text(PAGE, SpReadNumericInstanceValue)
|
|
#pragma alloc_text(PAGE, SpWriteNumericInstanceValue)
|
|
#pragma alloc_text(PAGE, SpReleaseMappedAddresses)
|
|
#pragma alloc_text(PAGE, SpInitializeGuidInterfaceMapping)
|
|
#pragma alloc_text(PAGE, SpSendIrpSynchronous)
|
|
#pragma alloc_text(PAGE, SpGetBusTypeGuid)
|
|
#pragma alloc_text(PAGE, SpDetermine64BitSupport)
|
|
#pragma alloc_text(PAGE, SpReadNumericValue)
|
|
#pragma alloc_text(PAGE, SpAllocateAddressMapping)
|
|
#pragma alloc_text(PAGE, SpPreallocateAddressMapping)
|
|
#pragma alloc_text(PAGE, SpPurgeFreeMappedAddressList)
|
|
#pragma alloc_text(PAGE, SpFreeMappedAddress)
|
|
#endif
|
|
|
|
|
|
NTSTATUS
|
|
SpInitializeGuidInterfaceMapping(
|
|
IN PDRIVER_OBJECT DriverObject
|
|
)
|
|
{
|
|
ULONG size;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(SpGuidInterfaceMappingList == NULL);
|
|
|
|
size = sizeof(SP_GUID_INTERFACE_MAPPING) * 5;
|
|
|
|
SpGuidInterfaceMappingList = SpAllocatePool(PagedPool,
|
|
size,
|
|
SCSIPORT_TAG_INTERFACE_MAPPING,
|
|
DriverObject);
|
|
|
|
if(SpGuidInterfaceMappingList == NULL) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlZeroMemory(SpGuidInterfaceMappingList, size);
|
|
|
|
SpGuidInterfaceMappingList[0].Guid = GUID_BUS_TYPE_PCMCIA;
|
|
SpGuidInterfaceMappingList[0].InterfaceType = Isa;
|
|
|
|
SpGuidInterfaceMappingList[1].Guid = GUID_BUS_TYPE_PCI;
|
|
SpGuidInterfaceMappingList[1].InterfaceType = PCIBus;
|
|
|
|
SpGuidInterfaceMappingList[2].Guid = GUID_BUS_TYPE_ISAPNP;
|
|
SpGuidInterfaceMappingList[2].InterfaceType = Isa;
|
|
|
|
SpGuidInterfaceMappingList[3].Guid = GUID_BUS_TYPE_EISA;
|
|
SpGuidInterfaceMappingList[3].InterfaceType = Eisa;
|
|
|
|
SpGuidInterfaceMappingList[4].InterfaceType = InterfaceTypeUndefined;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
ScsiPortBuildMultiString(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PUNICODE_STRING MultiString,
|
|
...
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will take a null terminated list of ascii strings and combine
|
|
them together into a unicode multi-string block.
|
|
|
|
This routine allocates memory for the string buffer - is the caller's
|
|
responsibility to free it.
|
|
|
|
Arguments:
|
|
|
|
MultiString - a UNICODE_STRING structure into which the multi string will
|
|
be built.
|
|
|
|
... - a NULL terminated list of narrow strings which will be combined
|
|
together. This list may not be empty.
|
|
|
|
Return Value:
|
|
|
|
status
|
|
|
|
--*/
|
|
|
|
{
|
|
PCSTR rawEntry;
|
|
ANSI_STRING ansiEntry;
|
|
|
|
UNICODE_STRING unicodeEntry;
|
|
PWSTR unicodeLocation;
|
|
|
|
ULONG multiLength = 0;
|
|
|
|
NTSTATUS status;
|
|
|
|
va_list ap;
|
|
|
|
va_start(ap, MultiString);
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Make sure we aren't going to leak any memory
|
|
//
|
|
|
|
ASSERT(MultiString->Buffer == NULL);
|
|
|
|
rawEntry = va_arg(ap, PCSTR);
|
|
|
|
while(rawEntry != NULL) {
|
|
|
|
RtlInitAnsiString(&ansiEntry, rawEntry);
|
|
|
|
multiLength += RtlAnsiStringToUnicodeSize(&ansiEntry);
|
|
|
|
rawEntry = va_arg(ap, PCSTR);
|
|
|
|
}
|
|
|
|
ASSERT(multiLength != 0);
|
|
|
|
multiLength += sizeof(WCHAR);
|
|
|
|
MultiString->Buffer = SpAllocatePool(PagedPool,
|
|
multiLength,
|
|
SCSIPORT_TAG_PNP_ID,
|
|
DriverObject);
|
|
|
|
if(MultiString->Buffer == NULL) {
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
}
|
|
|
|
unicodeEntry.Buffer = MultiString->Buffer;
|
|
unicodeEntry.MaximumLength = (USHORT) multiLength;
|
|
|
|
va_start(ap, MultiString);
|
|
|
|
rawEntry = va_arg(ap, PCSTR);
|
|
|
|
while(rawEntry != NULL) {
|
|
|
|
|
|
RtlInitAnsiString(&ansiEntry, rawEntry);
|
|
|
|
status = RtlAnsiStringToUnicodeString(
|
|
&unicodeEntry,
|
|
&ansiEntry,
|
|
FALSE);
|
|
|
|
//
|
|
// Since we're not allocating any memory the only failure possible
|
|
// is if this function is bad
|
|
//
|
|
|
|
ASSERT(NT_SUCCESS(status));
|
|
|
|
//
|
|
// Push the buffer location up and reduce the maximum count
|
|
//
|
|
|
|
unicodeEntry.Buffer += unicodeEntry.Length + sizeof(WCHAR);
|
|
unicodeEntry.MaximumLength -= unicodeEntry.Length + sizeof(WCHAR);
|
|
|
|
rawEntry = va_arg(ap, PCSTR);
|
|
|
|
};
|
|
|
|
ASSERT(unicodeEntry.MaximumLength == sizeof(WCHAR));
|
|
|
|
//
|
|
// Stick the final NUL on the end of the multisz
|
|
//
|
|
|
|
RtlZeroMemory(unicodeEntry.Buffer, unicodeEntry.MaximumLength);
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
NTSTATUS
|
|
ScsiPortStringArrayToMultiString(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
PUNICODE_STRING MultiString,
|
|
PCSTR StringArray[]
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will take a null terminated array of ascii strings and merge
|
|
them together into a unicode multi-string block.
|
|
|
|
This routine allocates memory for the string buffer - is the caller's
|
|
responsibility to free it.
|
|
|
|
Arguments:
|
|
|
|
MultiString - a UNICODE_STRING structure into which the multi string will
|
|
be built.
|
|
|
|
StringArray - a NULL terminated list of narrow strings which will be combined
|
|
together. This list may not be empty.
|
|
|
|
Return Value:
|
|
|
|
status
|
|
|
|
--*/
|
|
|
|
{
|
|
ANSI_STRING ansiEntry;
|
|
|
|
UNICODE_STRING unicodeEntry;
|
|
PWSTR unicodeLocation;
|
|
|
|
UCHAR i;
|
|
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Make sure we aren't going to leak any memory
|
|
//
|
|
|
|
ASSERT(MultiString->Buffer == NULL);
|
|
|
|
RtlInitUnicodeString(MultiString, NULL);
|
|
|
|
for(i = 0; StringArray[i] != NULL; i++) {
|
|
|
|
RtlInitAnsiString(&ansiEntry, StringArray[i]);
|
|
|
|
MultiString->Length += (USHORT) RtlAnsiStringToUnicodeSize(&ansiEntry);
|
|
}
|
|
|
|
ASSERT(MultiString->Length != 0);
|
|
|
|
MultiString->MaximumLength = MultiString->Length + sizeof(UNICODE_NULL);
|
|
|
|
MultiString->Buffer = SpAllocatePool(PagedPool,
|
|
MultiString->MaximumLength,
|
|
SCSIPORT_TAG_PNP_ID,
|
|
DriverObject);
|
|
|
|
if(MultiString->Buffer == NULL) {
|
|
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
|
|
}
|
|
|
|
RtlZeroMemory(MultiString->Buffer, MultiString->MaximumLength);
|
|
|
|
unicodeEntry = *MultiString;
|
|
|
|
for(i = 0; StringArray[i] != NULL; i++) {
|
|
|
|
RtlInitAnsiString(&ansiEntry, StringArray[i]);
|
|
|
|
status = RtlAnsiStringToUnicodeString(
|
|
&unicodeEntry,
|
|
&ansiEntry,
|
|
FALSE);
|
|
|
|
//
|
|
// Since we're not allocating any memory the only failure possible
|
|
// is if this function is bad
|
|
//
|
|
|
|
ASSERT(NT_SUCCESS(status));
|
|
|
|
//
|
|
// Push the buffer location up and reduce the maximum count
|
|
//
|
|
|
|
((PSTR) unicodeEntry.Buffer) += unicodeEntry.Length + sizeof(WCHAR);
|
|
unicodeEntry.MaximumLength -= unicodeEntry.Length + sizeof(WCHAR);
|
|
|
|
};
|
|
|
|
//
|
|
// Stick the final NUL on the end of the multisz
|
|
//
|
|
|
|
// RtlZeroMemory(unicodeEntry.Buffer, unicodeEntry.MaximumLength);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SpMultiStringToStringArray(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PUNICODE_STRING MultiString,
|
|
OUT PWSTR *StringArray[],
|
|
BOOLEAN Forward
|
|
)
|
|
|
|
{
|
|
ULONG stringCount = 0;
|
|
ULONG stringNumber;
|
|
ULONG i;
|
|
PWSTR *stringArray;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Pass one: count the number of string elements.
|
|
//
|
|
|
|
for(i = 0; i < (MultiString->MaximumLength / sizeof(WCHAR)); i++) {
|
|
if(MultiString->Buffer[i] == UNICODE_NULL) {
|
|
stringCount++;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Allocate the memory for a NULL-terminated string array.
|
|
//
|
|
|
|
stringArray = SpAllocatePool(PagedPool,
|
|
(stringCount + 1) * sizeof(PWSTR),
|
|
SCSIPORT_TAG_PNP_ID,
|
|
DriverObject);
|
|
|
|
if(stringArray == NULL) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlZeroMemory(stringArray, (stringCount + 1) * sizeof(PWSTR));
|
|
|
|
//
|
|
// Pass two : Put the string pointers in place.
|
|
//
|
|
|
|
i = 0;
|
|
|
|
for(stringNumber = 0; stringNumber < stringCount; stringNumber++) {
|
|
|
|
ULONG arrayNumber;
|
|
|
|
if(Forward) {
|
|
arrayNumber = stringNumber;
|
|
} else {
|
|
arrayNumber = stringCount - stringNumber - 1;
|
|
}
|
|
|
|
//
|
|
// Put a pointer to the head of the string into the array.
|
|
//
|
|
|
|
stringArray[arrayNumber] = &MultiString->Buffer[i];
|
|
|
|
//
|
|
// Scan for the end of the string.
|
|
//
|
|
|
|
while((i < (MultiString->MaximumLength / sizeof(WCHAR))) &&
|
|
(MultiString->Buffer[i] != UNICODE_NULL)) {
|
|
i++;
|
|
}
|
|
|
|
//
|
|
// Jump past the NULL.
|
|
//
|
|
|
|
i++;
|
|
}
|
|
|
|
*StringArray = stringArray;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
PCM_RESOURCE_LIST
|
|
RtlDuplicateCmResourceList(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
POOL_TYPE PoolType,
|
|
PCM_RESOURCE_LIST ResourceList,
|
|
ULONG Tag
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will attempt to allocate memory to copy the supplied
|
|
resource list. If sufficient memory cannot be allocated then the routine
|
|
will return NULL.
|
|
|
|
Arguments:
|
|
|
|
PoolType - the type of pool to allocate the duplicate from
|
|
|
|
ResourceList - the resource list to be copied
|
|
|
|
Tag - a value to tag the memory allocation with. If 0 then untagged
|
|
memory will be allocated.
|
|
|
|
Return Value:
|
|
|
|
an allocated copy of the resource list (caller must free) or
|
|
NULL if memory could not be allocated.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG size = sizeof(CM_RESOURCE_LIST);
|
|
PVOID buffer;
|
|
|
|
PAGED_CODE();
|
|
|
|
size = RtlSizeOfCmResourceList(ResourceList);
|
|
|
|
buffer = SpAllocatePool(PoolType,
|
|
size,
|
|
Tag,
|
|
DriverObject);
|
|
|
|
if (buffer != NULL) {
|
|
RtlCopyMemory(buffer,
|
|
ResourceList,
|
|
size);
|
|
}
|
|
|
|
return buffer;
|
|
}
|
|
|
|
ULONG
|
|
RtlSizeOfCmResourceList(
|
|
IN PCM_RESOURCE_LIST ResourceList
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine returns the size of a CM_RESOURCE_LIST.
|
|
|
|
Arguments:
|
|
|
|
ResourceList - the resource list to be copied
|
|
|
|
Return Value:
|
|
|
|
an allocated copy of the resource list (caller must free) or
|
|
NULL if memory could not be allocated.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG size = sizeof(CM_RESOURCE_LIST);
|
|
ULONG i;
|
|
|
|
PAGED_CODE();
|
|
|
|
for(i = 0; i < ResourceList->Count; i++) {
|
|
|
|
PCM_FULL_RESOURCE_DESCRIPTOR fullDescriptor = &(ResourceList->List[i]);
|
|
ULONG j;
|
|
|
|
//
|
|
// First descriptor is included in the size of the resource list.
|
|
//
|
|
|
|
if(i != 0) {
|
|
size += sizeof(CM_FULL_RESOURCE_DESCRIPTOR);
|
|
}
|
|
|
|
for(j = 0; j < fullDescriptor->PartialResourceList.Count; j++) {
|
|
|
|
//
|
|
// First descriptor is included in the size of the partial list.
|
|
//
|
|
|
|
if(j != 0) {
|
|
size += sizeof(CM_PARTIAL_RESOURCE_DESCRIPTOR);
|
|
}
|
|
}
|
|
}
|
|
|
|
return size;
|
|
}
|
|
|
|
#if !defined(NO_LEGACY_DRIVERS)
|
|
|
|
BOOLEAN
|
|
SpTranslateResources(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PCM_RESOURCE_LIST AllocatedResources,
|
|
OUT PCM_RESOURCE_LIST *TranslatedResources
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will call into the Hal to translate any recognizable resources
|
|
in the AllocatedResources list. This routine allocates the space for the
|
|
translated list - the caller is responsible for freeing this buffer.
|
|
|
|
If any errors occur the TranslatedResources will be NULL and the routine
|
|
will return FALSE.
|
|
|
|
Arguments:
|
|
|
|
AllocatedResources - The list of resources to be translated.
|
|
|
|
TranslatedResources - A location to store the translated resources. There
|
|
will be a one to one mapping between translated and
|
|
untranslated. Any non-standard resource types will
|
|
be blindly copied.
|
|
|
|
Return Value:
|
|
|
|
TRUE if all resources were translated properly.
|
|
|
|
FALSE otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
PCM_RESOURCE_LIST list;
|
|
|
|
ULONG listNumber;
|
|
|
|
PAGED_CODE();
|
|
|
|
(*TranslatedResources) = NULL;
|
|
|
|
list = RtlDuplicateCmResourceList(DriverObject,
|
|
NonPagedPool,
|
|
AllocatedResources,
|
|
SCSIPORT_TAG_RESOURCE_LIST);
|
|
|
|
if(list == NULL) {
|
|
return FALSE;
|
|
}
|
|
|
|
for(listNumber = 0; listNumber < list->Count; listNumber++) {
|
|
|
|
PCM_FULL_RESOURCE_DESCRIPTOR fullDescriptor;
|
|
ULONG resourceNumber;
|
|
|
|
fullDescriptor = &(list->List[listNumber]);
|
|
|
|
for(resourceNumber = 0;
|
|
resourceNumber < fullDescriptor->PartialResourceList.Count;
|
|
resourceNumber++) {
|
|
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR partialDescriptor;
|
|
CM_PARTIAL_RESOURCE_DESCRIPTOR tmp;
|
|
|
|
partialDescriptor =
|
|
&(fullDescriptor->PartialResourceList.PartialDescriptors[resourceNumber]);
|
|
|
|
switch(partialDescriptor->Type) {
|
|
|
|
case CmResourceTypePort:
|
|
case CmResourceTypeMemory: {
|
|
|
|
ULONG addressSpace;
|
|
|
|
if(partialDescriptor->Type == CmResourceTypePort) {
|
|
addressSpace = 1;
|
|
} else {
|
|
addressSpace = 0;
|
|
}
|
|
|
|
tmp = *partialDescriptor;
|
|
|
|
if(HalTranslateBusAddress(
|
|
fullDescriptor->InterfaceType,
|
|
fullDescriptor->BusNumber,
|
|
partialDescriptor->u.Generic.Start,
|
|
&addressSpace,
|
|
&(tmp.u.Generic.Start))) {
|
|
|
|
tmp.Type = (addressSpace == 0) ? CmResourceTypeMemory :
|
|
CmResourceTypePort;
|
|
|
|
} else {
|
|
|
|
ExFreePool(list);
|
|
return FALSE;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case CmResourceTypeInterrupt: {
|
|
|
|
tmp = *partialDescriptor;
|
|
|
|
tmp.u.Interrupt.Vector =
|
|
HalGetInterruptVector(
|
|
fullDescriptor->InterfaceType,
|
|
fullDescriptor->BusNumber,
|
|
partialDescriptor->u.Interrupt.Level,
|
|
partialDescriptor->u.Interrupt.Vector,
|
|
&((UCHAR) tmp.u.Interrupt.Level),
|
|
&(tmp.u.Interrupt.Affinity));
|
|
|
|
if(tmp.u.Interrupt.Affinity == 0) {
|
|
|
|
//
|
|
// Translation failed.
|
|
//
|
|
|
|
ExFreePool(list);
|
|
return FALSE;
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
};
|
|
|
|
*partialDescriptor = tmp;
|
|
}
|
|
}
|
|
|
|
*TranslatedResources = list;
|
|
return TRUE;
|
|
}
|
|
#endif // NO_LEGACY_DRIVERS
|
|
|
|
|
|
BOOLEAN
|
|
SpFindAddressTranslation(
|
|
IN PADAPTER_EXTENSION AdapterExtension,
|
|
IN INTERFACE_TYPE BusType,
|
|
IN ULONG BusNumber,
|
|
IN PHYSICAL_ADDRESS RangeStart,
|
|
IN ULONG RangeLength,
|
|
IN BOOLEAN InIoSpace,
|
|
IN OUT PCM_PARTIAL_RESOURCE_DESCRIPTOR Translation
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will search the resource lists in the AdapterExtension to
|
|
translate the given memory or i/o range using the resources provided by
|
|
pnp or the Hal.
|
|
|
|
Arguments:
|
|
|
|
AdapterExtesnion - the device extension for the adapter making the request
|
|
|
|
RangeStart - the starting address of the memory range
|
|
|
|
RangeLength - the number of bytes in the memory range
|
|
|
|
InIoSpace - whether the untranslated range is in io or memory space.
|
|
|
|
Return Value:
|
|
|
|
a pointer to a partial resource descriptor describing the proper range to
|
|
be used or NULL if no matching range of sufficient length can be found.
|
|
|
|
--*/
|
|
|
|
{
|
|
PCM_RESOURCE_LIST list;
|
|
|
|
ULONG listNumber;
|
|
|
|
list = AdapterExtension->AllocatedResources;
|
|
|
|
ASSERT(!AdapterExtension->IsMiniportDetected);
|
|
ASSERT(AdapterExtension->AllocatedResources);
|
|
ASSERT(AdapterExtension->TranslatedResources);
|
|
|
|
for(listNumber = 0; listNumber < list->Count; listNumber++) {
|
|
|
|
PCM_FULL_RESOURCE_DESCRIPTOR fullDescriptor;
|
|
ULONG resourceNumber;
|
|
|
|
fullDescriptor = &(list->List[listNumber]);
|
|
|
|
if((fullDescriptor->InterfaceType != BusType) ||
|
|
(fullDescriptor->BusNumber != BusNumber)) {
|
|
continue;
|
|
}
|
|
|
|
for(resourceNumber = 0;
|
|
resourceNumber < fullDescriptor->PartialResourceList.Count;
|
|
resourceNumber++) {
|
|
|
|
PCM_PARTIAL_RESOURCE_DESCRIPTOR partialDescriptor;
|
|
|
|
UCHAR desiredType =
|
|
InIoSpace ? CmResourceTypePort : CmResourceTypeMemory;
|
|
|
|
partialDescriptor =
|
|
&(fullDescriptor->PartialResourceList.PartialDescriptors[resourceNumber]);
|
|
|
|
if(partialDescriptor->Type == desiredType) {
|
|
|
|
ULONGLONG requestedStart = (ULONGLONG) RangeStart.QuadPart;
|
|
ULONGLONG requestedEnd =
|
|
((ULONGLONG) RangeStart.QuadPart) + RangeLength;
|
|
|
|
ULONGLONG testStart =
|
|
(ULONGLONG) partialDescriptor->u.Generic.Start.QuadPart;
|
|
ULONGLONG testEnd =
|
|
testStart + partialDescriptor->u.Generic.Length;
|
|
|
|
ULONGLONG requestedOffset = requestedStart - testStart;
|
|
|
|
ULONG rangeOffset;
|
|
|
|
//
|
|
// Make sure the base address is within the current range.
|
|
//
|
|
|
|
if((requestedStart < testStart) ||
|
|
(requestedStart >= testEnd)) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Make sure the end of the requested range is within this
|
|
// descriptor.
|
|
//
|
|
|
|
if(requestedEnd > testEnd) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// We seem to have found a match. Copy the equivalent resource
|
|
// in the translated resource list.
|
|
//
|
|
|
|
*Translation =
|
|
AdapterExtension->TranslatedResources->List[listNumber].
|
|
PartialResourceList.PartialDescriptors[resourceNumber];
|
|
|
|
//
|
|
// Return an offset into the translated range equivalent to the
|
|
// offset in the untranslated range.
|
|
//
|
|
|
|
requestedStart = Translation->u.Generic.Start.QuadPart;
|
|
requestedStart += requestedOffset;
|
|
|
|
Translation->u.Generic.Start.QuadPart = requestedStart;
|
|
|
|
return TRUE;
|
|
|
|
};
|
|
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SpLockUnlockQueue(
|
|
IN PDEVICE_OBJECT LogicalUnit,
|
|
IN BOOLEAN LockQueue,
|
|
IN BOOLEAN BypassLockedQueue
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will lock or unlock the logical unit queue.
|
|
|
|
This routine is synchronous.
|
|
|
|
Arguments:
|
|
|
|
LogicalUnit - the logical unit to be locked or unlocked
|
|
|
|
LockQueue - whether the queue should be locked or unlocked
|
|
|
|
BypassLockedQueue - whether the operation should bypass other locks or
|
|
whether it should sit in the queue. Must be true for
|
|
unlock requests.
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if the operation was successful
|
|
|
|
error status otherwise.
|
|
|
|
--*/
|
|
|
|
{
|
|
PLOGICAL_UNIT_EXTENSION luExtension = LogicalUnit->DeviceExtension;
|
|
PIRP irp;
|
|
PIO_STACK_LOCATION irpStack;
|
|
PSCSI_REQUEST_BLOCK srb;
|
|
PKEVENT event = NULL;
|
|
|
|
NTSTATUS status;
|
|
|
|
ASSERTMSG("Must bypass locked queue when unlocking: ",
|
|
(LockQueue || BypassLockedQueue));
|
|
|
|
DebugPrint((1, "SpLockUnlockQueue: %sing queue for logical unit extension "
|
|
"%#p\n",
|
|
LockQueue ? "Lock" : "Unlock",
|
|
luExtension));
|
|
|
|
//
|
|
// Build an IRP to send to the logical unit. We need one stack
|
|
// location for our completion routine and one for the irp to be
|
|
// processed with. This irp should never be dispatched to the
|
|
//
|
|
|
|
irp = SpAllocateIrp((CCHAR) (LogicalUnit->StackSize + 1),
|
|
FALSE,
|
|
LogicalUnit->DriverObject);
|
|
|
|
if(irp == NULL) {
|
|
DebugPrint((1, "SpLockUnlockQueue: Couldn't allocate IRP\n"));
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
try {
|
|
|
|
srb = SpAllocatePool(NonPagedPool,
|
|
sizeof(SCSI_REQUEST_BLOCK),
|
|
SCSIPORT_TAG_ENABLE,
|
|
LogicalUnit->DriverObject);
|
|
|
|
if(srb == NULL) {
|
|
|
|
DebugPrint((1, "SpLockUnlockQueue: Couldn't allocate SRB\n"));
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
leave;
|
|
}
|
|
|
|
event = SpAllocatePool(NonPagedPool,
|
|
sizeof(KEVENT),
|
|
SCSIPORT_TAG_EVENT,
|
|
LogicalUnit->DriverObject);
|
|
|
|
if(event == NULL) {
|
|
|
|
DebugPrint((1, "SpLockUnlockQueue: Couldn't allocate Context\n"));
|
|
status = STATUS_INSUFFICIENT_RESOURCES;
|
|
leave;
|
|
}
|
|
|
|
RtlZeroMemory(srb, sizeof(SCSI_REQUEST_BLOCK));
|
|
|
|
KeInitializeEvent(event, NotificationEvent, FALSE);
|
|
|
|
srb->Length = sizeof(SCSI_REQUEST_BLOCK);
|
|
|
|
srb->Function = LockQueue ? SRB_FUNCTION_LOCK_QUEUE :
|
|
SRB_FUNCTION_UNLOCK_QUEUE;
|
|
|
|
srb->OriginalRequest = irp;
|
|
srb->DataBuffer = NULL;
|
|
|
|
srb->QueueTag = SP_UNTAGGED;
|
|
|
|
if(BypassLockedQueue) {
|
|
srb->SrbFlags |= SRB_FLAGS_BYPASS_LOCKED_QUEUE;
|
|
}
|
|
|
|
IoSetCompletionRoutine(irp,
|
|
SpSignalCompletion,
|
|
event,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE);
|
|
|
|
irpStack = IoGetNextIrpStackLocation(irp);
|
|
|
|
irpStack->Parameters.Scsi.Srb = srb;
|
|
irpStack->MajorFunction = IRP_MJ_SCSI;
|
|
|
|
status = IoCallDriver(LogicalUnit, irp);
|
|
|
|
if(status == STATUS_PENDING) {
|
|
|
|
KeWaitForSingleObject(event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
|
|
status = irp->IoStatus.Status;
|
|
}
|
|
|
|
} finally {
|
|
|
|
if(irp != NULL) {
|
|
IoFreeIrp(irp);
|
|
}
|
|
|
|
if(srb != NULL) {
|
|
ExFreePool(srb);
|
|
}
|
|
|
|
if(event != NULL) {
|
|
ExFreePool(event);
|
|
}
|
|
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SpCheckSpecialDeviceFlags(
|
|
IN PLOGICAL_UNIT_EXTENSION LogicalUnit,
|
|
IN PINQUIRYDATA InquiryData
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will check the registry to determine what sort of special
|
|
handling a target device requires. If there is a device-node then the
|
|
routine will check under the device parameters for this particular logical
|
|
unit.
|
|
|
|
If there is no device node for the logical unit the hardware id will be
|
|
looked up (first) under the devnode for the adapter or (if not found) the
|
|
bad controller list stored under the scsiport Control key.
|
|
|
|
The flags tested for include (this list should be updated as more are
|
|
added):
|
|
|
|
* OneLun - used to keep from enumerating past LUN 0 on a particular
|
|
device.
|
|
* SparseLun - used to indicate the device may have holes in the LUN
|
|
numbers.
|
|
* NonStandardVPD - used to indicate that a target does not support VPD 0x00
|
|
but does support VPD 0x80 and 0x83.
|
|
* BinarySN - used to indicate that the target supplied a binary
|
|
serial number and that we need to convert it to ascii.
|
|
|
|
|
|
|
|
These values are REG_DWORD's. REG_NULL can be used to return a value
|
|
to it's default.
|
|
|
|
Arguments:
|
|
|
|
LogicalUnit - the logical unit
|
|
|
|
InquiryData - the inquiry data retreived for the lun
|
|
|
|
Return Value:
|
|
|
|
status
|
|
|
|
--*/
|
|
|
|
{
|
|
HANDLE baseKey = NULL;
|
|
HANDLE listKey = NULL;
|
|
HANDLE entryKey = NULL;
|
|
|
|
UNICODE_STRING keyName;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
|
|
SP_SPECIAL_CONTROLLER_FLAGS flags = {
|
|
0, // SparseLun
|
|
0, // OneLun
|
|
0, // LargeLuns
|
|
0, // SetLunInCdb
|
|
0, // NonStandardVPD
|
|
0 // BinarySN
|
|
};
|
|
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
DebugPrint((1, "SpCheckSpecialDeviceFlags - checking flags for %#p\n",
|
|
LogicalUnit));
|
|
|
|
//
|
|
// Check the bad controller list in the scsiport control key
|
|
//
|
|
|
|
try {
|
|
|
|
DebugPrint((2, "SpCheckSpecialDeviceFlags - trying control list\n"));
|
|
|
|
RtlInitUnicodeString(&keyName,
|
|
SCSIPORT_CONTROL_KEY SCSIPORT_SPECIAL_TARGET_KEY);
|
|
|
|
InitializeObjectAttributes(&objectAttributes,
|
|
&keyName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
NULL,
|
|
NULL);
|
|
|
|
status = ZwOpenKey(&listKey,
|
|
KEY_READ,
|
|
&objectAttributes);
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
DebugPrint((2, "SpCheckSpecialDeviceFlags - error %#08lx opening "
|
|
"key %wZ\n",
|
|
status,
|
|
&keyName));
|
|
leave;
|
|
}
|
|
|
|
SpProcessSpecialControllerList(
|
|
LogicalUnit->DeviceObject->DriverObject,
|
|
InquiryData,
|
|
listKey,
|
|
&flags);
|
|
|
|
} finally {
|
|
|
|
if(listKey != NULL) {
|
|
ZwClose(listKey);
|
|
listKey = NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Next check the special list in the adapter's devnode.
|
|
//
|
|
|
|
try {
|
|
|
|
PDEVICE_OBJECT adapterPdo = LogicalUnit->AdapterExtension->LowerPdo;
|
|
|
|
DebugPrint((2, "SpCheckSpecialDeviceFlags - trying adapter list\n"));
|
|
|
|
status = IoOpenDeviceRegistryKey(adapterPdo,
|
|
PLUGPLAY_REGKEY_DEVICE,
|
|
KEY_READ,
|
|
&baseKey);
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
|
|
DebugPrint((2, "SpCheckSpecialDeviceFlags - error %#08lx opening "
|
|
"adapter devnode key\n", status));
|
|
leave;
|
|
}
|
|
|
|
RtlInitUnicodeString(&keyName,
|
|
L"ScsiPort\\" SCSIPORT_SPECIAL_TARGET_KEY);
|
|
|
|
InitializeObjectAttributes(&objectAttributes,
|
|
&keyName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
baseKey,
|
|
NULL);
|
|
|
|
status = ZwOpenKey(&listKey,
|
|
KEY_READ,
|
|
&objectAttributes);
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
DebugPrint((2, "SpCheckSpecialDeviceFlags - error %#08lx opening "
|
|
"adapter devnode key %wZ\n", status, &keyName));
|
|
leave;
|
|
}
|
|
|
|
SpProcessSpecialControllerList(
|
|
LogicalUnit->DeviceObject->DriverObject,
|
|
InquiryData,
|
|
listKey,
|
|
&flags);
|
|
|
|
} finally {
|
|
|
|
if(baseKey != NULL) {
|
|
ZwClose(baseKey);
|
|
baseKey = NULL;
|
|
|
|
if(listKey != NULL) {
|
|
ZwClose(listKey);
|
|
listKey = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Finally check the devnode (if any) for the logical unit. This one is
|
|
// special - the hardware id already matchs so the key just contains the
|
|
// values to be used, not a database of values.
|
|
//
|
|
|
|
try {
|
|
|
|
status = IoOpenDeviceRegistryKey(LogicalUnit->CommonExtension.DeviceObject,
|
|
PLUGPLAY_REGKEY_DEVICE,
|
|
KEY_READ,
|
|
&baseKey);
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
DebugPrint((2, "SpCheckSpecialDeviceFlags - error %#08lx opening "
|
|
"device devnode key\n", status));
|
|
leave;
|
|
}
|
|
|
|
RtlInitUnicodeString(&keyName,
|
|
L"ScsiPort\\" SCSIPORT_SPECIAL_TARGET_KEY);
|
|
|
|
InitializeObjectAttributes(&objectAttributes,
|
|
&keyName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
baseKey,
|
|
NULL);
|
|
|
|
status = ZwOpenKey(&listKey,
|
|
KEY_READ,
|
|
&objectAttributes);
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
DebugPrint((2, "SpCheckSpecialDeviceFlags - error %#08lx opening "
|
|
"device devnode key %wZ\n", status, &keyName));
|
|
leave;
|
|
}
|
|
|
|
SpProcessSpecialControllerFlags(listKey, &flags);
|
|
|
|
} finally {
|
|
if(baseKey != NULL) {
|
|
ZwClose(baseKey);
|
|
baseKey = NULL;
|
|
|
|
if(listKey != NULL) {
|
|
ZwClose(listKey);
|
|
listKey = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
LogicalUnit->SpecialFlags = flags;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
VOID
|
|
SpProcessSpecialControllerList(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN PINQUIRYDATA InquiryData,
|
|
IN HANDLE ListKey,
|
|
OUT PSP_SPECIAL_CONTROLLER_FLAGS Flags
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will match the specified logical unit to a set of special
|
|
controller flags stored in the registry key ListKey. These flags will
|
|
be written into the Flags structure, overwriting any flags which already
|
|
exist.
|
|
|
|
If no logical unit is provided then the ListKey handle is assumed to point
|
|
at the appropriate list entry and the values stored there will be copied
|
|
into the Flags structure.
|
|
|
|
Arguments:
|
|
|
|
InquiryData - The inquiry data for the logical unit. This is used to
|
|
match strings in the special target list.
|
|
|
|
ListKey - a handle to the special controller list to locate the logical
|
|
unit in, or a handle to a list of flags if the LogicalUnit value
|
|
is not present.
|
|
|
|
Flags - a location to store the flags.
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
|
|
{
|
|
UNICODE_STRING hardwareIds;
|
|
PWSTR *hardwareIdList;
|
|
|
|
ULONG idNumber;
|
|
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
RtlInitUnicodeString(&hardwareIds, NULL);
|
|
|
|
status = ScsiPortGetHardwareIds(DriverObject, InquiryData, &hardwareIds);
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
DebugPrint((2, "SpProcessSpecialControllerList: Error %#08lx getting "
|
|
"hardware id's\n", status));
|
|
return;
|
|
}
|
|
|
|
status = SpMultiStringToStringArray(DriverObject,
|
|
&hardwareIds,
|
|
&hardwareIdList,
|
|
FALSE);
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
RtlFreeUnicodeString(&hardwareIds);
|
|
return;
|
|
}
|
|
|
|
for(idNumber = 0; hardwareIdList[idNumber] != NULL; idNumber++) {
|
|
|
|
PWSTR hardwareId = hardwareIdList[idNumber];
|
|
ULONG j;
|
|
UNICODE_STRING keyName;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
HANDLE flagsKey;
|
|
|
|
DebugPrint((2, "SpProcessSpecialControllerList: processing id %ws\n",
|
|
hardwareId));
|
|
|
|
//
|
|
// Remove the leading slash from the name.
|
|
//
|
|
|
|
for(j = 0; hardwareId[j] != UNICODE_NULL; j++) {
|
|
if(hardwareId[j] == L'\\') {
|
|
hardwareId = &(hardwareId[j+1]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Process the hardware id that we just found the end of.
|
|
//
|
|
|
|
RtlInitUnicodeString(&keyName, hardwareId);
|
|
|
|
DebugPrint((2, "SpProcessSpecialControllerList: Finding match for "
|
|
"%wZ - id %d\n", &keyName, idNumber));
|
|
|
|
InitializeObjectAttributes(&objectAttributes,
|
|
&keyName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
ListKey,
|
|
NULL);
|
|
|
|
status = ZwOpenKey(&flagsKey,
|
|
KEY_READ,
|
|
&objectAttributes);
|
|
|
|
if(NT_SUCCESS(status)) {
|
|
SpProcessSpecialControllerFlags(flagsKey, Flags);
|
|
ZwClose(flagsKey);
|
|
} else {
|
|
DebugPrint((2, "SpProcessSpecialControllerList: Error %#08lx "
|
|
"opening key\n", status));
|
|
}
|
|
|
|
}
|
|
|
|
ExFreePool(hardwareIdList);
|
|
RtlFreeUnicodeString(&hardwareIds);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
VOID
|
|
SpProcessSpecialControllerFlags(
|
|
IN HANDLE FlagsKey,
|
|
OUT PSP_SPECIAL_CONTROLLER_FLAGS Flags
|
|
)
|
|
{
|
|
RTL_QUERY_REGISTRY_TABLE queryTable[7];
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
RtlZeroMemory(queryTable, sizeof(queryTable));
|
|
|
|
queryTable[0].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
queryTable[0].Name = L"SparseLUN";
|
|
queryTable[0].EntryContext = &(Flags->SparseLun);
|
|
|
|
queryTable[1].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
queryTable[1].Name = L"OneLUN";
|
|
queryTable[1].EntryContext = &(Flags->OneLun);
|
|
|
|
queryTable[2].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
queryTable[2].Name = L"LargeLuns";
|
|
queryTable[2].EntryContext = &(Flags->LargeLuns);
|
|
|
|
queryTable[3].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
queryTable[3].Name = L"SetLunInCdb";
|
|
queryTable[3].EntryContext = &(Flags->SetLunInCdb);
|
|
|
|
queryTable[4].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
queryTable[4].Name = L"NonStandardVPD";
|
|
queryTable[4].EntryContext = &(Flags->NonStandardVPD);
|
|
|
|
queryTable[5].Flags = RTL_QUERY_REGISTRY_DIRECT;
|
|
queryTable[5].Name = L"BinarySN";
|
|
queryTable[5].EntryContext = &(Flags->BinarySN);
|
|
|
|
status = RtlQueryRegistryValues(RTL_REGISTRY_HANDLE,
|
|
FlagsKey,
|
|
queryTable,
|
|
NULL,
|
|
NULL);
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
DebugPrint((2, "SpProcssSpecialControllerFlags: Error %#08lx reading "
|
|
"values\n", status));
|
|
} else {
|
|
DebugPrint((2, "SpProcessSpecialControllerFlags: %s%s%s%s%s\n",
|
|
((Flags->SparseLun ||
|
|
Flags->OneLun ||
|
|
Flags->LargeLuns ||
|
|
Flags->SetLunInCdb ||
|
|
Flags->NonStandardVPD ||
|
|
Flags->BinarySN) ? "" : "none"),
|
|
(Flags->SparseLun ? "SparseLun " : ""),
|
|
(Flags->OneLun ? "OneLun " : ""),
|
|
(Flags->LargeLuns ? "LargeLuns " : ""),
|
|
(Flags->SetLunInCdb ? "SetLunInCdb" : ""),
|
|
(Flags->NonStandardVPD ? "NonStandardVPD" : ""),
|
|
(Flags->BinarySN ? "BinarySN" : "")));
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
PSRB_DATA
|
|
FASTCALL
|
|
SpAllocateSrbData(
|
|
IN PADAPTER_EXTENSION Adapter,
|
|
IN OPTIONAL PIRP Request
|
|
)
|
|
|
|
{
|
|
PSRB_DATA srbData;
|
|
|
|
srbData = ExAllocateFromNPagedLookasideList(
|
|
&Adapter->SrbDataLookasideList);
|
|
|
|
#if TEST_LISTS
|
|
if(srbData != NULL) {
|
|
InterlockedIncrement64(&Adapter->SrbDataAllocationCount);
|
|
}
|
|
#endif
|
|
|
|
if((srbData == NULL) && (Request != NULL)) {
|
|
|
|
KIRQL oldIrql;
|
|
PSRB_DATA emergencySrbData;
|
|
|
|
//
|
|
// Use the emergency srb data if it's not already in use.
|
|
//
|
|
|
|
KeAcquireSpinLock(&Adapter->EmergencySrbDataSpinLock,
|
|
&oldIrql);
|
|
|
|
emergencySrbData =
|
|
(PSRB_DATA) InterlockedExchangePointer(
|
|
(PVOID) &(Adapter->EmergencySrbData),
|
|
NULL);
|
|
|
|
if(emergencySrbData == NULL) {
|
|
|
|
//
|
|
// It's in use - queue the request until an srb data block
|
|
// goes free.
|
|
//
|
|
|
|
InsertTailList(
|
|
&Adapter->SrbDataBlockedRequests,
|
|
&Request->Tail.Overlay.DeviceQueueEntry.DeviceListEntry);
|
|
} else {
|
|
|
|
//
|
|
// There is an SRB_DATA block available after all.
|
|
//
|
|
|
|
srbData = emergencySrbData;
|
|
|
|
#if TEST_LISTS
|
|
InterlockedIncrement64(&Adapter->SrbDataEmergencyFreeCount);
|
|
#endif
|
|
|
|
}
|
|
|
|
KeReleaseSpinLock(&Adapter->EmergencySrbDataSpinLock,
|
|
oldIrql);
|
|
}
|
|
|
|
return srbData;
|
|
}
|
|
|
|
|
|
VOID
|
|
FASTCALL
|
|
SpFreeSrbData(
|
|
IN PADAPTER_EXTENSION Adapter,
|
|
IN PSRB_DATA SrbData
|
|
)
|
|
|
|
{
|
|
PSRB_DATA emergencySrbData = NULL;
|
|
BOOLEAN startedRequest = FALSE;
|
|
LONG depth;
|
|
|
|
|
|
ASSERT_SRB_DATA(SrbData);
|
|
ASSERT(SrbData->CurrentIrp == NULL);
|
|
ASSERT(SrbData->CurrentSrb == NULL);
|
|
ASSERT(SrbData->CompletedRequests == NULL);
|
|
|
|
//
|
|
// Determine if there are any other instances of this routine running. If
|
|
// there are, don't start a blocked request.
|
|
//
|
|
|
|
depth = InterlockedIncrement(&Adapter->SrbDataFreeRunning);
|
|
|
|
//
|
|
// Clear out some of the flags so we don't get confused when we reuse this
|
|
// request.
|
|
//
|
|
|
|
SrbData->Flags = 0;
|
|
|
|
//
|
|
// See if we need to store away a new emergency SRB_DATA block
|
|
//
|
|
|
|
emergencySrbData = InterlockedCompareExchangePointer(
|
|
&(Adapter->EmergencySrbData),
|
|
SrbData,
|
|
NULL);
|
|
|
|
//
|
|
// If we stored this SRB_DATA block as the new emergency block AND if this
|
|
// routine is not recursively nested, check if there are any blocked
|
|
// requests waiting to be started.
|
|
//
|
|
|
|
if(emergencySrbData == NULL && depth == 1) {
|
|
|
|
KIRQL oldIrql;
|
|
|
|
CheckForBlockedRequest:
|
|
|
|
//
|
|
// We did - now grab the spinlock and see if we can use it to issue
|
|
// a new request.
|
|
//
|
|
|
|
KeAcquireSpinLock(&(Adapter->EmergencySrbDataSpinLock), &oldIrql);
|
|
|
|
//
|
|
// First check to see if we have a request to process.
|
|
//
|
|
|
|
if(IsListEmpty(&(Adapter->SrbDataBlockedRequests)) == TRUE) {
|
|
|
|
KeReleaseSpinLock(&(Adapter->EmergencySrbDataSpinLock), oldIrql);
|
|
InterlockedDecrement(&Adapter->SrbDataFreeRunning);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// make sure the emergency request is still there (doesn't really
|
|
// matter if it's the one we were called with or another one - just
|
|
// make sure one is available).
|
|
//
|
|
|
|
emergencySrbData = (PSRB_DATA)
|
|
InterlockedExchangePointer(
|
|
(PVOID) &(Adapter->EmergencySrbData),
|
|
NULL);
|
|
|
|
if(emergencySrbData == NULL) {
|
|
|
|
//
|
|
// Our work here is done.
|
|
//
|
|
|
|
KeReleaseSpinLock(&(Adapter->EmergencySrbDataSpinLock), oldIrql);
|
|
InterlockedDecrement(&Adapter->SrbDataFreeRunning);
|
|
return;
|
|
|
|
} else {
|
|
PLIST_ENTRY entry;
|
|
PIRP request;
|
|
PIO_STACK_LOCATION currentIrpStack;
|
|
PSCSI_REQUEST_BLOCK srb;
|
|
|
|
entry = RemoveHeadList(&(Adapter->SrbDataBlockedRequests));
|
|
|
|
ASSERT(entry != NULL);
|
|
|
|
request =
|
|
CONTAINING_RECORD(
|
|
entry,
|
|
IRP,
|
|
Tail.Overlay.DeviceQueueEntry);
|
|
|
|
KeReleaseSpinLock(&(Adapter->EmergencySrbDataSpinLock), oldIrql);
|
|
|
|
currentIrpStack = IoGetCurrentIrpStackLocation(request);
|
|
srb = currentIrpStack->Parameters.Scsi.Srb;
|
|
|
|
ASSERT_PDO(currentIrpStack->DeviceObject);
|
|
|
|
emergencySrbData->CurrentIrp = request;
|
|
emergencySrbData->CurrentSrb = srb;
|
|
emergencySrbData->LogicalUnit =
|
|
currentIrpStack->DeviceObject->DeviceExtension;
|
|
|
|
srb->OriginalRequest = emergencySrbData;
|
|
|
|
startedRequest = TRUE;
|
|
SpDispatchRequest(emergencySrbData->LogicalUnit,
|
|
request);
|
|
|
|
#if TEST_LISTS
|
|
InterlockedIncrement64(&Adapter->SrbDataResurrectionCount);
|
|
#endif
|
|
}
|
|
|
|
//
|
|
// If we started a blocked request, go back and see if another one
|
|
// needs to be started.
|
|
//
|
|
|
|
if (startedRequest == TRUE) {
|
|
startedRequest = FALSE;
|
|
goto CheckForBlockedRequest;
|
|
}
|
|
|
|
} else if (emergencySrbData != NULL) {
|
|
|
|
//
|
|
// We did not store this SRB_DATA block as the emergency block, so
|
|
// we need to free it back to the lookaside list.
|
|
//
|
|
|
|
ExFreeToNPagedLookasideList(
|
|
&Adapter->SrbDataLookasideList,
|
|
SrbData);
|
|
}
|
|
|
|
InterlockedDecrement(&Adapter->SrbDataFreeRunning);
|
|
return;
|
|
}
|
|
|
|
PVOID
|
|
SpAllocateSrbDataBackend(
|
|
IN POOL_TYPE PoolType,
|
|
IN ULONG NumberOfBytes,
|
|
IN ULONG AdapterIndex
|
|
)
|
|
|
|
{
|
|
KIRQL oldIrql;
|
|
PADAPTER_EXTENSION Adapter;
|
|
PSRB_DATA srbData;
|
|
ULONG tag;
|
|
|
|
KeAcquireSpinLock(&ScsiGlobalAdapterListSpinLock, &oldIrql);
|
|
|
|
Adapter = ScsiGlobalAdapterList[AdapterIndex]->DeviceExtension;
|
|
|
|
KeReleaseSpinLock(&ScsiGlobalAdapterListSpinLock, oldIrql);
|
|
|
|
ASSERT_FDO(Adapter->DeviceObject);
|
|
|
|
tag = SpAllocateQueueTag(Adapter);
|
|
|
|
if(tag == -1) {
|
|
return NULL;
|
|
}
|
|
|
|
srbData = SpAllocatePool(PoolType,
|
|
NumberOfBytes,
|
|
SCSIPORT_TAG_SRB_DATA,
|
|
Adapter->DeviceObject->DriverObject);
|
|
|
|
if(srbData == NULL) {
|
|
SpReleaseQueueTag(Adapter, tag);
|
|
return NULL;
|
|
}
|
|
|
|
RtlZeroMemory(srbData, sizeof(SRB_DATA));
|
|
srbData->Adapter = Adapter;
|
|
srbData->QueueTag = tag;
|
|
srbData->Type = SRB_DATA_TYPE;
|
|
srbData->Size = sizeof(SRB_DATA);
|
|
srbData->Flags = 0;
|
|
srbData->FreeRoutine = SpFreeSrbData;
|
|
|
|
return srbData;
|
|
}
|
|
|
|
VOID
|
|
SpFreeSrbDataBackend(
|
|
IN PSRB_DATA SrbData
|
|
)
|
|
{
|
|
ASSERT_SRB_DATA(SrbData);
|
|
ASSERT_FDO(SrbData->Adapter->DeviceObject);
|
|
ASSERT(SrbData->QueueTag != 0);
|
|
|
|
SpReleaseQueueTag(SrbData->Adapter, SrbData->QueueTag);
|
|
SrbData->Type = 0;
|
|
ExFreePool(SrbData);
|
|
return;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SpAllocateTagBitMap(
|
|
IN PADAPTER_EXTENSION Adapter
|
|
)
|
|
|
|
{
|
|
ULONG size; // number of bits
|
|
PRTL_BITMAP bitMap;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Initialize the queue tag bitMap.
|
|
//
|
|
|
|
if(Adapter->MaxQueueTag == 0) {
|
|
#if SMALL_QUEUE_TAG_BITMAP
|
|
if(Adapter->NumberOfRequests <= 240) {
|
|
Adapter->MaxQueueTag = (UCHAR) (Adapter->NumberOfRequests) + 10;
|
|
} else {
|
|
Adapter->MaxQueueTag = 254;
|
|
}
|
|
#else
|
|
Adapter->MaxQueueTag = 254;
|
|
#endif
|
|
} else if (Adapter->MaxQueueTag < Adapter->NumberOfRequests) {
|
|
DbgPrint("SpAllocateTagBitmap: MaxQueueTag %d < NumberOfRequests %d\n"
|
|
"This will negate the advantage of having increased the "
|
|
"number of requests.\n",
|
|
Adapter->MaxQueueTag,
|
|
Adapter->NumberOfRequests);
|
|
}
|
|
|
|
DebugPrint((1, "SpAllocateAdapterResources: %d bits in queue tag "
|
|
"bitMap\n",
|
|
Adapter->MaxQueueTag));
|
|
|
|
size = (Adapter->MaxQueueTag + 1);
|
|
size /= 8;
|
|
size += 1;
|
|
size *= sizeof(UCHAR);
|
|
size += sizeof(RTL_BITMAP);
|
|
|
|
bitMap = SpAllocatePool(NonPagedPool,
|
|
size,
|
|
SCSIPORT_TAG_QUEUE_BITMAP,
|
|
Adapter->DeviceObject->DriverObject);
|
|
|
|
if(bitMap == NULL) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlInitializeBitMap(bitMap,
|
|
(PULONG) (bitMap + 1),
|
|
Adapter->MaxQueueTag);
|
|
|
|
RtlClearAllBits(bitMap);
|
|
|
|
//
|
|
// Queue tag 0 is invalid and should never be returned by the allocator.
|
|
//
|
|
|
|
RtlSetBits(bitMap, 0, 1);
|
|
|
|
Adapter->QueueTagBitMap = bitMap;
|
|
Adapter->QueueTagHint = 1;
|
|
|
|
//
|
|
// Create a spinlock to protect our queue tag bitmap. There's no reason
|
|
// for this to contend with the regular port spinlock.
|
|
//
|
|
|
|
KeInitializeSpinLock(&(Adapter->QueueTagSpinLock));
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
ULONG
|
|
SpAllocateQueueTag(
|
|
IN PADAPTER_EXTENSION Adapter
|
|
)
|
|
{
|
|
KIRQL oldIrql;
|
|
ULONG tagValue;
|
|
|
|
ASSERT_FDO(Adapter->DeviceObject);
|
|
|
|
KeAcquireSpinLock(&(Adapter->QueueTagSpinLock), &oldIrql);
|
|
|
|
//
|
|
// Find an available queue tag.
|
|
//
|
|
|
|
tagValue = RtlFindClearBitsAndSet(Adapter->QueueTagBitMap,
|
|
1,
|
|
Adapter->QueueTagHint);
|
|
|
|
KeReleaseSpinLock(&(Adapter->QueueTagSpinLock), oldIrql);
|
|
|
|
ASSERT(Adapter->QueueTagHint < Adapter->MaxQueueTag);
|
|
ASSERT(tagValue != 0);
|
|
|
|
if(tagValue != -1) {
|
|
|
|
ASSERT(tagValue <= Adapter->MaxQueueTag);
|
|
|
|
//
|
|
// This we can do unsynchronized. if we nuke the hint accidentally it
|
|
// will just increase the cost of the next lookup which should
|
|
// hopefully occur rarely.
|
|
//
|
|
|
|
Adapter->QueueTagHint = (tagValue + 1) % Adapter->MaxQueueTag;
|
|
}
|
|
|
|
return tagValue;
|
|
}
|
|
|
|
VOID
|
|
SpReleaseQueueTag(
|
|
IN PADAPTER_EXTENSION Adapter,
|
|
IN ULONG QueueTag
|
|
)
|
|
{
|
|
KIRQL oldIrql;
|
|
|
|
KeAcquireSpinLock(&(Adapter->QueueTagSpinLock), &oldIrql);
|
|
RtlClearBits(Adapter->QueueTagBitMap,
|
|
QueueTag,
|
|
1);
|
|
KeReleaseSpinLock(&(Adapter->QueueTagSpinLock), oldIrql);
|
|
return;
|
|
}
|
|
|
|
|
|
INTERFACE_TYPE
|
|
SpGetPdoInterfaceType(
|
|
IN PDEVICE_OBJECT Pdo
|
|
)
|
|
{
|
|
ULONG value;
|
|
|
|
GUID busTypeGuid;
|
|
INTERFACE_TYPE interfaceType = InterfaceTypeUndefined;
|
|
ULONG result;
|
|
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
status = SpReadNumericInstanceValue(Pdo,
|
|
L"LegacyInterfaceType",
|
|
&value);
|
|
|
|
if(NT_SUCCESS(status)) {
|
|
interfaceType = value;
|
|
return interfaceType;
|
|
}
|
|
|
|
//
|
|
// Attempt to get and interpret the bus type GUID.
|
|
//
|
|
|
|
status = IoGetDeviceProperty(Pdo,
|
|
DevicePropertyBusTypeGuid,
|
|
sizeof(GUID),
|
|
&busTypeGuid,
|
|
&result);
|
|
|
|
if(NT_SUCCESS(status)) {
|
|
|
|
ULONG i;
|
|
|
|
for(i = 0;
|
|
(SpGuidInterfaceMappingList[i].InterfaceType !=
|
|
InterfaceTypeUndefined);
|
|
i++) {
|
|
|
|
if(RtlEqualMemory(&(SpGuidInterfaceMappingList[i].Guid),
|
|
&busTypeGuid,
|
|
sizeof(GUID))) {
|
|
|
|
//
|
|
// We have a legacy interface type for this guid already.
|
|
//
|
|
|
|
interfaceType = SpGuidInterfaceMappingList[i].InterfaceType;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(interfaceType != InterfaceTypeUndefined) {
|
|
return interfaceType;
|
|
}
|
|
|
|
status = IoGetDeviceProperty(Pdo,
|
|
DevicePropertyLegacyBusType,
|
|
sizeof(INTERFACE_TYPE),
|
|
&interfaceType,
|
|
&result);
|
|
|
|
if(NT_SUCCESS(status)) {
|
|
ASSERT(result == sizeof(INTERFACE_TYPE));
|
|
|
|
//
|
|
// Munge the interface type for the case of PCMCIA cards to allow SCSI
|
|
// pccards (i.e. sparrow) to be recognized. Much better would be a way
|
|
// to get the interface type correct before we enter this routine.
|
|
//
|
|
|
|
if (interfaceType == PCMCIABus) {
|
|
interfaceType = Isa;
|
|
}
|
|
|
|
}
|
|
|
|
if(interfaceType != InterfaceTypeUndefined) {
|
|
|
|
return interfaceType;
|
|
|
|
} else {
|
|
|
|
//
|
|
// No idea what the interface type is - guess isa.
|
|
//
|
|
|
|
DebugPrint((1, "SpGetPdoInterfaceType: Status %#08lx getting legacy "
|
|
"bus type - assuming device is ISA\n", status));
|
|
return Isa;
|
|
}
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SpReadNumericInstanceValue(
|
|
IN PDEVICE_OBJECT Pdo,
|
|
IN PWSTR ValueName,
|
|
OUT PULONG Value
|
|
)
|
|
{
|
|
ULONG value;
|
|
|
|
HANDLE baseKey = NULL;
|
|
HANDLE scsiportKey = NULL;
|
|
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(Value != NULL);
|
|
ASSERT(ValueName != NULL);
|
|
ASSERT(Pdo != NULL);
|
|
|
|
status = IoOpenDeviceRegistryKey(Pdo,
|
|
PLUGPLAY_REGKEY_DEVICE,
|
|
KEY_READ,
|
|
&baseKey);
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
try {
|
|
UNICODE_STRING unicodeKeyName;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
|
|
RtlInitUnicodeString(&unicodeKeyName, L"Scsiport");
|
|
InitializeObjectAttributes(&objectAttributes,
|
|
&unicodeKeyName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
baseKey,
|
|
NULL);
|
|
|
|
status = ZwOpenKey(&scsiportKey,
|
|
KEY_READ,
|
|
&objectAttributes);
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
leave;
|
|
} else {
|
|
UNICODE_STRING unicodeValueName;
|
|
|
|
UCHAR buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(ULONG)];
|
|
|
|
PKEY_VALUE_PARTIAL_INFORMATION keyValue =
|
|
(PKEY_VALUE_PARTIAL_INFORMATION) buffer;
|
|
|
|
ULONG result;
|
|
|
|
RtlInitUnicodeString(&unicodeValueName, ValueName);
|
|
|
|
status = ZwQueryValueKey(scsiportKey,
|
|
&unicodeValueName,
|
|
KeyValuePartialInformation,
|
|
keyValue,
|
|
sizeof(buffer),
|
|
&result);
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
leave;
|
|
}
|
|
|
|
if(keyValue->Type != REG_DWORD) {
|
|
status = STATUS_OBJECT_TYPE_MISMATCH;
|
|
leave;
|
|
}
|
|
|
|
if(result < sizeof(ULONG)) {
|
|
status = STATUS_OBJECT_TYPE_MISMATCH;
|
|
leave;
|
|
}
|
|
|
|
value = ((PULONG) (keyValue->Data))[0];
|
|
}
|
|
|
|
} finally {
|
|
if(baseKey != NULL) {ZwClose(baseKey);}
|
|
if(scsiportKey != NULL) {ZwClose(scsiportKey);}
|
|
}
|
|
|
|
*Value = value;
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SpWriteNumericInstanceValue(
|
|
IN PDEVICE_OBJECT Pdo,
|
|
IN PWSTR ValueName,
|
|
IN ULONG Value
|
|
)
|
|
{
|
|
ULONG value;
|
|
|
|
HANDLE baseKey = NULL;
|
|
HANDLE scsiportKey = NULL;
|
|
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(ValueName != NULL);
|
|
ASSERT(Pdo != NULL);
|
|
|
|
status = IoOpenDeviceRegistryKey(Pdo,
|
|
PLUGPLAY_REGKEY_DEVICE,
|
|
KEY_READ | KEY_WRITE,
|
|
&baseKey);
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
return status;
|
|
}
|
|
|
|
try {
|
|
UNICODE_STRING unicodeKeyName;
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
|
|
RtlInitUnicodeString(&unicodeKeyName, L"Scsiport");
|
|
InitializeObjectAttributes(&objectAttributes,
|
|
&unicodeKeyName,
|
|
OBJ_CASE_INSENSITIVE,
|
|
baseKey,
|
|
NULL);
|
|
|
|
status = ZwCreateKey(&scsiportKey,
|
|
KEY_READ | KEY_WRITE,
|
|
&objectAttributes,
|
|
0,
|
|
NULL,
|
|
REG_OPTION_NON_VOLATILE,
|
|
NULL
|
|
);
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
leave;
|
|
} else {
|
|
UNICODE_STRING unicodeValueName;
|
|
|
|
ULONG result;
|
|
|
|
RtlInitUnicodeString(&unicodeValueName, ValueName);
|
|
|
|
status = ZwSetValueKey(scsiportKey,
|
|
&unicodeValueName,
|
|
0,
|
|
REG_DWORD,
|
|
&Value,
|
|
sizeof(ULONG));
|
|
|
|
}
|
|
|
|
} finally {
|
|
if(baseKey != NULL) {ZwClose(baseKey);}
|
|
if(scsiportKey != NULL) {ZwClose(scsiportKey);}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
PMAPPED_ADDRESS
|
|
SpAllocateAddressMapping(
|
|
PADAPTER_EXTENSION Adapter
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will attempt to allocate a free address mapping block and
|
|
place it on the adapter's MappedAddressList. If there is an available
|
|
block in the free list then it will be used. Otherwise it will attempt
|
|
to allocate a block from non-paged pool.
|
|
|
|
Arguments:
|
|
|
|
Adapter - the adapter we are allocating the mapping for.
|
|
|
|
Preallocate - indicates that the caller is trying to preallocate buffers.
|
|
|
|
|
|
Return Value:
|
|
|
|
a pointer to the new mapping (which has been inserted into the address
|
|
mapping list) or NULL if none could be allocated.
|
|
|
|
--*/
|
|
{
|
|
PMAPPED_ADDRESS mapping;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// First check the free address mapping list. If there's one there
|
|
// unlink it and return.
|
|
//
|
|
|
|
if(Adapter->FreeMappedAddressList != NULL) {
|
|
mapping = Adapter->FreeMappedAddressList;
|
|
Adapter->FreeMappedAddressList = mapping->NextMappedAddress;
|
|
} else {
|
|
mapping = SpAllocatePool(NonPagedPool,
|
|
sizeof(MAPPED_ADDRESS),
|
|
SCSIPORT_TAG_MAPPING_LIST,
|
|
Adapter->DeviceObject->DriverObject);
|
|
}
|
|
|
|
if(mapping == NULL) {
|
|
DebugPrint((0, "SpAllocateAddressMapping: Unable to allocate "
|
|
"mapping\n"));
|
|
|
|
return NULL;
|
|
}
|
|
|
|
RtlZeroMemory(mapping, sizeof(MAPPED_ADDRESS));
|
|
|
|
mapping->NextMappedAddress = Adapter->MappedAddressList;
|
|
Adapter->MappedAddressList = mapping;
|
|
|
|
return mapping;
|
|
}
|
|
|
|
BOOLEAN
|
|
SpPreallocateAddressMapping(
|
|
PADAPTER_EXTENSION Adapter,
|
|
IN UCHAR NumberOfBlocks
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will allocate a number of address mapping structures and
|
|
place them on the free mapped address list.
|
|
|
|
Arguments:
|
|
|
|
Adapter - the adapter we are allocating the mapping for.
|
|
|
|
NumberOfBlocks - the number of blocks to allocate
|
|
|
|
|
|
Return Value:
|
|
|
|
TRUE if the requested number of blocks was successfully allocated,
|
|
|
|
FALSE if there was not sufficient memory to allocate them all. The caller
|
|
is still responsible for freeing them in this case.
|
|
|
|
--*/
|
|
{
|
|
PMAPPED_ADDRESS mapping;
|
|
ULONG i;
|
|
|
|
PAGED_CODE();
|
|
|
|
for(i = 0; i < NumberOfBlocks; i++) {
|
|
mapping = SpAllocatePool(NonPagedPool,
|
|
sizeof(MAPPED_ADDRESS),
|
|
SCSIPORT_TAG_MAPPING_LIST,
|
|
Adapter->DeviceObject->DriverObject);
|
|
|
|
if(mapping == NULL) {
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
RtlZeroMemory(mapping, sizeof(MAPPED_ADDRESS));
|
|
|
|
mapping->NextMappedAddress = Adapter->FreeMappedAddressList;
|
|
Adapter->FreeMappedAddressList = mapping;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
VOID
|
|
SpPurgeFreeMappedAddressList(
|
|
IN PADAPTER_EXTENSION Adapter
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine frees all of the mapped address blocks on the
|
|
FreeMappedAddressList.
|
|
|
|
Arguments:
|
|
|
|
Adapter - the adapter who's FreeMappedAddressList is to be dumped.
|
|
|
|
Return Value:
|
|
|
|
none
|
|
|
|
--*/
|
|
{
|
|
PMAPPED_ADDRESS mapping;
|
|
|
|
PAGED_CODE();
|
|
|
|
while(Adapter->FreeMappedAddressList != NULL) {
|
|
mapping = Adapter->FreeMappedAddressList;
|
|
Adapter->FreeMappedAddressList = mapping->NextMappedAddress;
|
|
|
|
ExFreePool(mapping);
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
SpFreeMappedAddress(
|
|
IN PADAPTER_EXTENSION Adapter,
|
|
IN PVOID MappedAddress
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will unmap the specified mapping and then return the mapping
|
|
block to the free list. If no mapped address was specified then this
|
|
will simply free the first mapping on the MappedAddressList.
|
|
|
|
Arguments:
|
|
|
|
Adapter - the adapter which has the mapping
|
|
|
|
MappedAddress - the base address of the mapping we're attempting to free.
|
|
ignored if FreeSpecificBlock is false.
|
|
|
|
Return Value:
|
|
|
|
TRUE if a matching list element was found.
|
|
FALSE otherwise.
|
|
|
|
--*/
|
|
{
|
|
PMAPPED_ADDRESS *mapping;
|
|
|
|
PAGED_CODE();
|
|
|
|
for(mapping = &(Adapter->MappedAddressList);
|
|
*mapping != NULL;
|
|
mapping = &((*mapping)->NextMappedAddress)) {
|
|
|
|
if((*mapping)->MappedAddress == MappedAddress) {
|
|
|
|
PMAPPED_ADDRESS tmp = *mapping;
|
|
|
|
//
|
|
// Unmap address.
|
|
//
|
|
|
|
MmUnmapIoSpace(tmp->MappedAddress, tmp->NumberOfBytes);
|
|
|
|
//
|
|
// Unlink this entry from the mapped address list. Stick it on
|
|
// the free mapped address list. Then return.
|
|
//
|
|
|
|
*mapping = tmp->NextMappedAddress;
|
|
|
|
tmp->NextMappedAddress = Adapter->FreeMappedAddressList;
|
|
Adapter->FreeMappedAddressList = tmp;
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
PMAPPED_ADDRESS
|
|
SpFindMappedAddress(
|
|
IN PADAPTER_EXTENSION Adapter,
|
|
IN LARGE_INTEGER IoAddress,
|
|
IN ULONG NumberOfBytes,
|
|
IN ULONG SystemIoBusNumber
|
|
)
|
|
{
|
|
PMAPPED_ADDRESS mapping;
|
|
|
|
for(mapping = Adapter->MappedAddressList;
|
|
mapping != NULL;
|
|
mapping = mapping->NextMappedAddress) {
|
|
|
|
if((mapping->IoAddress.QuadPart == IoAddress.QuadPart) &&
|
|
(mapping->NumberOfBytes == NumberOfBytes) &&
|
|
(mapping->BusNumber == SystemIoBusNumber)) {
|
|
return mapping;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
VOID
|
|
SpReleaseMappedAddresses(
|
|
IN PADAPTER_EXTENSION Adapter
|
|
)
|
|
{
|
|
ULONG i;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Iterate through the mapped address list and punt every entry onto the
|
|
// free list.
|
|
//
|
|
|
|
while(Adapter->MappedAddressList != NULL) {
|
|
SpFreeMappedAddress(Adapter, Adapter->MappedAddressList->MappedAddress);
|
|
}
|
|
|
|
//
|
|
// Now dump the free list.
|
|
//
|
|
|
|
SpPurgeFreeMappedAddressList(Adapter);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SpSignalCompletion(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp,
|
|
IN PKEVENT Event
|
|
)
|
|
{
|
|
KeSetEvent(Event, IO_NO_INCREMENT, FALSE);
|
|
return STATUS_MORE_PROCESSING_REQUIRED;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SpSendIrpSynchronous(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PIRP Irp
|
|
)
|
|
{
|
|
KEVENT event;
|
|
|
|
PAGED_CODE();
|
|
|
|
KeInitializeEvent(&event, SynchronizationEvent, FALSE);
|
|
|
|
IoCopyCurrentIrpStackLocationToNext(Irp);
|
|
IoSetCompletionRoutine(Irp,
|
|
SpSignalCompletion,
|
|
&event,
|
|
TRUE,
|
|
TRUE,
|
|
TRUE);
|
|
|
|
IoCallDriver(DeviceObject, Irp);
|
|
|
|
KeWaitForSingleObject(&event,
|
|
Executive,
|
|
KernelMode,
|
|
FALSE,
|
|
NULL);
|
|
|
|
return Irp->IoStatus.Status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
SpGetBusTypeGuid(
|
|
IN PADAPTER_EXTENSION Adapter
|
|
)
|
|
{
|
|
ULONG result;
|
|
|
|
NTSTATUS status;
|
|
|
|
PAGED_CODE();
|
|
|
|
//
|
|
// Grab the bus interface GUID and save it away in the adapter extension.
|
|
//
|
|
|
|
status = IoGetDeviceProperty(Adapter->LowerPdo,
|
|
DevicePropertyBusTypeGuid,
|
|
sizeof(GUID),
|
|
&(Adapter->BusTypeGuid),
|
|
&result);
|
|
|
|
if(!NT_SUCCESS(status)) {
|
|
RtlZeroMemory(&(Adapter->BusTypeGuid), sizeof(GUID));
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
SpDetermine64BitSupport(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine determines if 64-bit physical addressing is supported by
|
|
the system to be saved in the global Sp64BitPhysicalAddressing. Eventually
|
|
this routine can be removed and the scsiport global will just point to the
|
|
one exported by MM. However the global isn't hooked up for PAE36 at the
|
|
moment so we need to do some x86 specific tricks.
|
|
|
|
Arguments:
|
|
|
|
none
|
|
|
|
Return Value:
|
|
|
|
Does the system support 64-bit (or something over 32-bit) addresses?
|
|
|
|
--*/
|
|
|
|
{
|
|
PAGED_CODE();
|
|
|
|
if((*Mm64BitPhysicalAddress) == TRUE) {
|
|
DbgPrintEx(DPFLTR_SCSIPORT_ID,
|
|
DPFLTR_INFO_LEVEL,
|
|
"SpDetermine64BitSupport: Mm64BitPhysicalAddress is TRUE\n");
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
VOID
|
|
SpAdjustDisabledBit(
|
|
IN PLOGICAL_UNIT_EXTENSION LogicalUnit,
|
|
IN BOOLEAN Enable
|
|
)
|
|
{
|
|
ULONG newCount;
|
|
KIRQL oldIrql;
|
|
|
|
KeAcquireSpinLock(&(LogicalUnit->AdapterExtension->SpinLock), &oldIrql);
|
|
if(Enable) {
|
|
if(LogicalUnit->QueuePauseCount != 0) {
|
|
LogicalUnit->QueuePauseCount -= 1;
|
|
}
|
|
|
|
if(LogicalUnit->QueuePauseCount == 0) {
|
|
CLEAR_FLAG(LogicalUnit->LuFlags, LU_QUEUE_PAUSED);
|
|
}
|
|
} else {
|
|
LogicalUnit->QueuePauseCount += 1;
|
|
SET_FLAG(LogicalUnit->LuFlags, LU_QUEUE_PAUSED);
|
|
}
|
|
KeReleaseSpinLock(&(LogicalUnit->AdapterExtension->SpinLock), oldIrql);
|
|
return;
|
|
}
|
|
|
|
NTSTATUS
|
|
SpReadNumericValue(
|
|
IN OPTIONAL HANDLE Root,
|
|
IN OPTIONAL PUNICODE_STRING KeyName,
|
|
IN PUNICODE_STRING ValueName,
|
|
OUT PULONG Value
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine will read a REG_DWORD value from the specified registry
|
|
location. The caller can specify the key by providing a handle to a root
|
|
registry key and the name of a subkey.
|
|
|
|
The caller must supply either Root or KeyName. Both may be supplied.
|
|
|
|
Arguments:
|
|
|
|
Root - the key the value resides in (if KeyName is NULL), a parent
|
|
key of the one the value resides in, or NULL if KeyName specifies
|
|
the entire registry path.
|
|
|
|
KeyName - the name of the subkey (either from the root of the registry or
|
|
from the key specified in Root.
|
|
|
|
ValueName - the name of the value to be read
|
|
|
|
Value - returns the value in the key. this will be zero if an error occurs
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS if successful.
|
|
STATUS_UNSUCCESSFUL if the specified value is not a REG_DWORD value.
|
|
other status values explaining the cause of the failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
ULONG value = 0;
|
|
|
|
HANDLE key = Root;
|
|
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
|
|
PAGED_CODE();
|
|
|
|
ASSERT(Value != NULL);
|
|
ASSERT(ValueName != NULL);
|
|
|
|
ASSERT((KeyName != NULL) || (Root != NULL));
|
|
|
|
if(ARGUMENT_PRESENT(KeyName)) {
|
|
OBJECT_ATTRIBUTES objectAttributes;
|
|
|
|
InitializeObjectAttributes(&(objectAttributes),
|
|
KeyName,
|
|
OBJ_CASE_INSENSITIVE | OBJ_OPENIF,
|
|
Root,
|
|
NULL);
|
|
|
|
status = ZwOpenKey(&(key), KEY_QUERY_VALUE, &objectAttributes);
|
|
}
|
|
|
|
if(NT_SUCCESS(status)) {
|
|
UCHAR buffer[sizeof(KEY_VALUE_PARTIAL_INFORMATION) + sizeof(ULONG)];
|
|
PKEY_VALUE_PARTIAL_INFORMATION data;
|
|
ULONG result;
|
|
|
|
RtlZeroMemory(buffer, sizeof(buffer));
|
|
data = (PKEY_VALUE_PARTIAL_INFORMATION) buffer;
|
|
|
|
status = ZwQueryValueKey(key,
|
|
ValueName,
|
|
KeyValuePartialInformation,
|
|
data,
|
|
sizeof(buffer),
|
|
&result);
|
|
|
|
if(NT_SUCCESS(status)) {
|
|
if (data->Type != REG_DWORD) {
|
|
status = STATUS_UNSUCCESSFUL;
|
|
} else {
|
|
value = ((PULONG) data->Data)[0];
|
|
}
|
|
}
|
|
}
|
|
|
|
*Value = value;
|
|
|
|
if(key != Root) {
|
|
ZwClose(key);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
PMDL
|
|
SpBuildMdlForMappedTransfer(
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PDMA_ADAPTER AdapterObject,
|
|
IN PMDL OriginalMdl,
|
|
IN PVOID StartVa,
|
|
IN ULONG ByteCount,
|
|
IN PSRB_SCATTER_GATHER ScatterGatherList,
|
|
IN ULONG ScatterGatherEntries
|
|
)
|
|
{
|
|
ULONG size;
|
|
PMDL mdl;
|
|
|
|
ULONG pageCount;
|
|
|
|
PPFN_NUMBER pages;
|
|
ULONG sgPage;
|
|
ULONG mdlPage;
|
|
ULONG sgSpan;
|
|
|
|
mdl = SpAllocateMdl(StartVa,
|
|
ByteCount,
|
|
FALSE,
|
|
FALSE,
|
|
NULL,
|
|
DeviceObject->DriverObject);
|
|
|
|
if (mdl == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
pageCount = ADDRESS_AND_SIZE_TO_SPAN_PAGES(StartVa, ByteCount);
|
|
|
|
//
|
|
// Indicate that the memory has already been locked down.
|
|
//
|
|
|
|
//
|
|
// Indicate that the memory is "I/O space" so that MM doesn't won't
|
|
// reference the (nonexistent) PFNs for this buffer. We have to do this
|
|
// for the time being because MM isn't aware of the pages the HAL is using
|
|
// for bounce buffers.
|
|
//
|
|
|
|
SET_FLAG(mdl->MdlFlags, MDL_PAGES_LOCKED | MDL_IO_SPACE);
|
|
|
|
//
|
|
// Run through our scatter gather list and build the page list based
|
|
// on that.
|
|
//
|
|
|
|
pages = (PPFN_NUMBER) (mdl + 1);
|
|
|
|
for(sgPage = 0, mdlPage = 0; sgPage < ScatterGatherEntries; sgPage++) {
|
|
|
|
PVOID pa;
|
|
ULONG sgLength;
|
|
|
|
ASSERT(ScatterGatherList[sgPage].Length != 0);
|
|
|
|
pa = (PVOID) ScatterGatherList[sgPage].Address.QuadPart;
|
|
|
|
sgLength =
|
|
ADDRESS_AND_SIZE_TO_SPAN_PAGES(pa,
|
|
ScatterGatherList[sgPage].Length);
|
|
|
|
for(sgSpan = 0; sgSpan < sgLength; sgSpan++, mdlPage++) {
|
|
ULONGLONG pa;
|
|
pa = ScatterGatherList[sgPage].Address.QuadPart;
|
|
pa += sgSpan * PAGE_SIZE;
|
|
pa >>= PAGE_SHIFT;
|
|
pages[mdlPage] = (PFN_NUMBER) (pa);
|
|
}
|
|
}
|
|
pages = (PPFN_NUMBER) (mdl + 1);
|
|
pages = (PPFN_NUMBER) (OriginalMdl + 1);
|
|
|
|
ASSERT(mdlPage == pageCount);
|
|
|
|
return mdl;
|
|
}
|
|
|
|
#if defined(FORWARD_PROGRESS)
|
|
VOID
|
|
SpPrepareMdlForMappedTransfer(
|
|
IN PMDL mdl,
|
|
IN PDEVICE_OBJECT DeviceObject,
|
|
IN PDMA_ADAPTER AdapterObject,
|
|
IN PMDL OriginalMdl,
|
|
IN PVOID StartVa,
|
|
IN ULONG ByteCount,
|
|
IN PSRB_SCATTER_GATHER ScatterGatherList,
|
|
IN ULONG ScatterGatherEntries
|
|
)
|
|
{
|
|
ULONG size;
|
|
|
|
ULONG pageCount;
|
|
|
|
PPFN_NUMBER pages;
|
|
ULONG sgPage;
|
|
ULONG mdlPage;
|
|
ULONG sgSpan;
|
|
|
|
pageCount = ADDRESS_AND_SIZE_TO_SPAN_PAGES(StartVa, ByteCount);
|
|
|
|
//
|
|
// Indicate that the memory has already been locked down.
|
|
//
|
|
|
|
//
|
|
// Indicate that the memory is "I/O space" so that MM doesn't won't
|
|
// reference the (nonexistent) PFNs for this buffer. We have to do this
|
|
// for the time being because MM isn't aware of the pages the HAL is using
|
|
// for bounce buffers.
|
|
//
|
|
|
|
SET_FLAG(mdl->MdlFlags, MDL_PAGES_LOCKED | MDL_IO_SPACE);
|
|
|
|
//
|
|
// Run through our scatter gather list and build the page list based
|
|
// on that.
|
|
//
|
|
|
|
pages = (PPFN_NUMBER) (mdl + 1);
|
|
|
|
for(sgPage = 0, mdlPage = 0; sgPage < ScatterGatherEntries; sgPage++) {
|
|
|
|
PVOID pa;
|
|
ULONG sgLength;
|
|
|
|
ASSERT(ScatterGatherList[sgPage].Length != 0);
|
|
|
|
pa = (PVOID) ScatterGatherList[sgPage].Address.QuadPart;
|
|
|
|
sgLength =
|
|
ADDRESS_AND_SIZE_TO_SPAN_PAGES(pa,
|
|
ScatterGatherList[sgPage].Length);
|
|
|
|
for(sgSpan = 0; sgSpan < sgLength; sgSpan++, mdlPage++) {
|
|
ULONGLONG pa;
|
|
pa = ScatterGatherList[sgPage].Address.QuadPart;
|
|
pa += sgSpan * PAGE_SIZE;
|
|
pa >>= PAGE_SHIFT;
|
|
pages[mdlPage] = (PFN_NUMBER) (pa);
|
|
}
|
|
}
|
|
pages = (PPFN_NUMBER) (mdl + 1);
|
|
pages = (PPFN_NUMBER) (OriginalMdl + 1);
|
|
|
|
ASSERT(mdlPage == pageCount);
|
|
}
|
|
#endif
|
|
|
|
PSRB_DATA
|
|
FASTCALL
|
|
SpAllocateBypassSrbData(
|
|
IN PLOGICAL_UNIT_EXTENSION LogicalUnit
|
|
)
|
|
{
|
|
PSINGLE_LIST_ENTRY entry;
|
|
PSRB_DATA srbData;
|
|
|
|
entry = ExInterlockedPopEntrySList(&(LogicalUnit->BypassSrbDataList),
|
|
&(LogicalUnit->BypassSrbDataSpinLock));
|
|
|
|
if(entry == NULL) {
|
|
KeBugCheckEx(PORT_DRIVER_INTERNAL,
|
|
5,
|
|
NUMBER_BYPASS_SRB_DATA_BLOCKS,
|
|
(ULONG_PTR) LogicalUnit->BypassSrbDataBlocks,
|
|
0);
|
|
}
|
|
|
|
srbData = CONTAINING_RECORD(entry, SRB_DATA, Reserved);
|
|
|
|
srbData->Adapter = LogicalUnit->AdapterExtension;
|
|
srbData->QueueTag = SP_UNTAGGED;
|
|
srbData->Type = SRB_DATA_TYPE;
|
|
srbData->Size = sizeof(SRB_DATA);
|
|
srbData->Flags = SRB_DATA_BYPASS_REQUEST;
|
|
srbData->FreeRoutine = SpFreeBypassSrbData;
|
|
|
|
return srbData;
|
|
}
|
|
|
|
VOID
|
|
FASTCALL
|
|
SpFreeBypassSrbData(
|
|
IN PADAPTER_EXTENSION Adapter,
|
|
IN PSRB_DATA SrbData
|
|
)
|
|
|
|
{
|
|
PLOGICAL_UNIT_EXTENSION lu = SrbData->LogicalUnit;
|
|
|
|
ASSERT_SRB_DATA(SrbData);
|
|
ASSERT(SrbData->CurrentIrp == NULL);
|
|
ASSERT(SrbData->CurrentSrb == NULL);
|
|
ASSERT(SrbData->CompletedRequests == NULL);
|
|
ASSERT(TEST_FLAG(SrbData->Flags, SRB_DATA_BYPASS_REQUEST));
|
|
|
|
RtlZeroMemory(SrbData, sizeof(SRB_DATA));
|
|
|
|
ExInterlockedPushEntrySList(&(lu->BypassSrbDataList),
|
|
&(SrbData->Reserved),
|
|
&(lu->BypassSrbDataSpinLock));
|
|
return;
|
|
}
|
|
|
|
PVOID
|
|
SpAllocateErrorLogEntry(
|
|
IN PDRIVER_OBJECT DriverObject
|
|
)
|
|
{
|
|
PVOID Packet;
|
|
|
|
ASSERT(DriverObject);
|
|
Packet = IoAllocateErrorLogEntry(
|
|
DriverObject,
|
|
sizeof(IO_ERROR_LOG_PACKET) + sizeof(SCSIPORT_ALLOCFAILURE_DATA));
|
|
|
|
return Packet;
|
|
}
|
|
|
|
VOID
|
|
FASTCALL
|
|
SpLogAllocationFailureFn(
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN POOL_TYPE PoolType,
|
|
IN SIZE_T Size,
|
|
IN ULONG Tag,
|
|
IN ULONG FileId,
|
|
IN ULONG LineNumber
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine writes a message to the event log indicating that an
|
|
allocation failure has occurred.
|
|
|
|
Arguments:
|
|
|
|
DriverObject - pointer to the driver object for which the allocation
|
|
failure event is being logged.
|
|
|
|
PoolType - identifies the pool the failed allocation attempt was from.
|
|
|
|
Size - indicates the number of bytes that the failed allocation
|
|
attempt tried to obtain.
|
|
|
|
Tag - identifies the pool tag associated with the failed
|
|
allocation.
|
|
|
|
AllocId - uniquely identifies this allocation w/in scsiport.
|
|
|
|
Return Value:
|
|
|
|
VOID
|
|
|
|
--*/
|
|
|
|
{
|
|
NTSTATUS status;
|
|
PIO_ERROR_LOG_PACKET Packet;
|
|
PIO_ERROR_LOG_PACKET CurrentValue;
|
|
SCSIPORT_ALLOCFAILURE_DATA *Data;
|
|
// PSCSIPORT_ALLOCFAILURE_ENTRY Entry;
|
|
// PSCSIPORT_ALLOCFAILURE_ENTRY CurrentValue;
|
|
PSCSIPORT_DRIVER_EXTENSION DriverExtension;
|
|
|
|
DebugPrint((2, "SpLogAllocationFailureFn: DriverObject:%p\nId:%08X|%08X\n",
|
|
DriverObject,
|
|
FileId, LineNumber));
|
|
|
|
//
|
|
// Try to allocate a new error log event.
|
|
//
|
|
|
|
Packet = (PIO_ERROR_LOG_PACKET)
|
|
SpAllocateErrorLogEntry(DriverObject);
|
|
|
|
//
|
|
// If we could not allocate a log event, we check the driver extension to
|
|
// see if it has a reserve event we can use. If we cannot get the driver
|
|
// extension or if it does not contain a reserve event, we return
|
|
// without logging the allocation failure.
|
|
//
|
|
|
|
if (Packet == NULL) {
|
|
|
|
//
|
|
// See if there is a driver extension for this driver. It is possible
|
|
// that one has not been created yet, so this may fail, in which case
|
|
// we give up and return.
|
|
//
|
|
|
|
DriverExtension = IoGetDriverObjectExtension(
|
|
DriverObject,
|
|
ScsiPortInitialize
|
|
);
|
|
|
|
if (DriverExtension == NULL) {
|
|
DebugPrint((1, "SpLogAllocationFailureFn: no driver extension\n"));
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Get the reserve event in the driver extension. The reserve event
|
|
// may have already been used, so it's possible that it is NULL. If
|
|
// this is the case, we give up and return.
|
|
//
|
|
|
|
Packet = (PIO_ERROR_LOG_PACKET)
|
|
DriverExtension->ReserveAllocFailureLogEntry;
|
|
|
|
if (Packet == NULL) {
|
|
DebugPrint((1, "SpLogAllocationFailureFn: no reserve packet\n"));
|
|
return;
|
|
}
|
|
|
|
//
|
|
// We have to ensure that we are the only instance to use this
|
|
// event. To do so, we attempt to NULL the event in the driver
|
|
// extension. If somebody else beats us to it, they own the
|
|
// event and we have to give up.
|
|
//
|
|
|
|
CurrentValue = InterlockedCompareExchangePointer(
|
|
DriverExtension->ReserveAllocFailureLogEntry,
|
|
NULL,
|
|
Packet
|
|
);
|
|
|
|
if (Packet != CurrentValue) {
|
|
DebugPrint((1, "SpLogAllocationFailureFn: someone already owns packet\n"));
|
|
return;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Initialize the error log packet.
|
|
//
|
|
|
|
Packet->ErrorCode = IO_WARNING_ALLOCATION_FAILED;
|
|
Packet->SequenceNumber = 0;
|
|
Packet->MajorFunctionCode = 0;
|
|
Packet->RetryCount = 0;
|
|
Packet->UniqueErrorValue = 0x10;
|
|
Packet->FinalStatus = STATUS_INSUFFICIENT_RESOURCES;
|
|
Packet->DumpDataSize = sizeof(ULONG) * 4;
|
|
Packet->NumberOfStrings = 0;
|
|
Packet->DumpData[0] = Tag;
|
|
|
|
Data = (SCSIPORT_ALLOCFAILURE_DATA*) &Packet->DumpData[1];
|
|
|
|
Data->Size = (ULONG) Size;
|
|
Data->FileId = FileId;
|
|
Data->LineNumber = LineNumber;
|
|
|
|
//
|
|
// Queue the error log entry.
|
|
//
|
|
|
|
IoWriteErrorLogEntry(Packet);
|
|
}
|
|
|
|
PVOID
|
|
SpAllocatePoolEx(
|
|
IN POOL_TYPE PoolType,
|
|
IN SIZE_T NumberOfBytes,
|
|
IN ULONG Tag,
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN ULONG FileId,
|
|
IN ULONG LineNumber
|
|
)
|
|
{
|
|
PVOID Block;
|
|
|
|
Block = ExAllocatePoolWithTag(PoolType,
|
|
NumberOfBytes,
|
|
Tag);
|
|
if (Block == NULL) {
|
|
|
|
SpLogAllocationFailureFn(DriverObject,
|
|
PoolType,
|
|
NumberOfBytes,
|
|
Tag,
|
|
FileId,
|
|
LineNumber);
|
|
}
|
|
|
|
return Block;
|
|
}
|
|
|
|
PMDL
|
|
SpAllocateMdlEx(
|
|
IN PVOID VirtualAddress,
|
|
IN ULONG Length,
|
|
IN BOOLEAN SecondaryBuffer,
|
|
IN BOOLEAN ChargeQuota,
|
|
IN OUT PIRP Irp,
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN ULONG FileId,
|
|
IN ULONG LineNumber
|
|
)
|
|
{
|
|
PMDL mdl = IoAllocateMdl(VirtualAddress,
|
|
Length,
|
|
SecondaryBuffer,
|
|
ChargeQuota,
|
|
Irp);
|
|
if (mdl == NULL) {
|
|
SpLogAllocationFailureFn(DriverObject,
|
|
NonPagedPool,
|
|
0,
|
|
SCSIPORT_TAG_ALLOCMDL,
|
|
FileId,
|
|
LineNumber);
|
|
}
|
|
return mdl;
|
|
}
|
|
|
|
PIRP
|
|
SpAllocateIrpEx(
|
|
IN CCHAR StackSize,
|
|
IN BOOLEAN ChargeQuota,
|
|
IN PDRIVER_OBJECT DriverObject,
|
|
IN ULONG FileId,
|
|
IN ULONG LineNumber
|
|
)
|
|
{
|
|
PIRP irp = IoAllocateIrp(StackSize, ChargeQuota);
|
|
if (irp == NULL) {
|
|
SpLogAllocationFailureFn(DriverObject,
|
|
NonPagedPool,
|
|
0,
|
|
SCSIPORT_TAG_ALLOCIRP,
|
|
FileId,
|
|
LineNumber);
|
|
}
|
|
return irp;
|
|
}
|
|
|
|
#ifndef USE_DMA_MACROS
|
|
VOID
|
|
SpFreeSGList(
|
|
IN PADAPTER_EXTENSION Adapter,
|
|
IN PSRB_DATA SrbData
|
|
)
|
|
{
|
|
ASSERT(TEST_FLAG(SrbData->Flags,
|
|
(SRB_DATA_MEDIUM_SG_LIST |
|
|
SRB_DATA_SMALL_SG_LIST |
|
|
SRB_DATA_LARGE_SG_LIST)));
|
|
|
|
if (TEST_FLAG(SrbData->Flags, SRB_DATA_MEDIUM_SG_LIST)) {
|
|
|
|
//
|
|
// Release the scatter gather list back to the lookaside list.
|
|
//
|
|
|
|
ExFreeToNPagedLookasideList(
|
|
&(Adapter->MediumScatterGatherLookasideList),
|
|
(PVOID) SrbData->ScatterGatherList);
|
|
|
|
} else if(TEST_FLAG(SrbData->Flags, SRB_DATA_LARGE_SG_LIST)) {
|
|
|
|
//
|
|
// We allocated the SG list from pool, so free it.
|
|
//
|
|
|
|
ExFreePool(SrbData->ScatterGatherList);
|
|
|
|
} else {
|
|
|
|
//
|
|
// We used the preallocated SG list embedded in the SRB_DATA object,
|
|
// so we don't have to free anything.
|
|
//
|
|
|
|
NOTHING
|
|
}
|
|
|
|
//
|
|
// Clear the SG-related flags on the SRB_DATA object.
|
|
//
|
|
|
|
CLEAR_FLAG(SrbData->Flags, (SRB_DATA_LARGE_SG_LIST |
|
|
SRB_DATA_MEDIUM_SG_LIST |
|
|
SRB_DATA_SMALL_SG_LIST));
|
|
|
|
//
|
|
// NULL the SRB_DATA's SG list pointer.
|
|
//
|
|
|
|
SrbData->ScatterGatherList = NULL;
|
|
}
|
|
#endif
|