windows-nt/Source/XPSP1/NT/drivers/storage/scsiwmi/wmilib.c

872 lines
27 KiB
C
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*++
Copyright (C) Microsoft Corporation, 1998 - 1999
Module Name:
wmilib.c
Abstract:
WMI library utility functions for SCSI miniports
CONSIDER adding the following functionality to the library:
* Dynamic instance names
* Different instance names for different guids
Author:
AlanWar
Environment:
kernel mode only
Notes:
Revision History:
--*/
#include "miniport.h"
#include "scsi.h"
#include "wmistr.h"
#include "scsiwmi.h"
typedef enum
{
ScsiProcessed, // Srb was processed and possibly completed
ScsiNotCompleted, // Srb was process and NOT completed
ScsiNotWmi, // Srb is not a WMI irp
ScsiForward // Srb is wmi irp, but targeted at another device object
} SYSCTL_SCSI_DISPOSITION, *PSYSCTL_SCSI_DISPOSITION;
BOOLEAN
ScsiWmipFindGuid(
IN PSCSIWMIGUIDREGINFO GuidList,
IN ULONG GuidCount,
IN LPGUID Guid,
OUT PULONG GuidIndex,
OUT PULONG InstanceCount
);
BOOLEAN
ScsiWmipFindGuid(
IN PSCSIWMIGUIDREGINFO GuidList,
IN ULONG GuidCount,
IN LPGUID Guid,
OUT PULONG GuidIndex,
OUT PULONG InstanceCount
)
/*++
Routine Description:
This routine will search the list of guids registered and return
the index for the one that was registered.
Arguments:
GuidList is the list of guids to search
GuidCount is the count of guids in the list
Guid is the guid being searched for
*GuidIndex returns the index to the guid
*InstanceCount returns the count of instances for the guid
Return Value:
TRUE if guid is found else FALSE
--*/
{
ULONG i;
for (i = 0; i < GuidCount; i++)
{
if (IsEqualGUID(Guid, GuidList[i].Guid))
{
*GuidIndex = i;
*InstanceCount = GuidList[i].InstanceCount;
return(TRUE);
}
}
return(FALSE);
}
UCHAR ScsiWmipPostProcess(
IN UCHAR MinorFunction,
IN PUCHAR Buffer,
IN ULONG BufferSize,
IN ULONG BufferUsed,
IN UCHAR Status,
OUT PULONG ReturnSize
)
{
ULONG retSize;
ULONG bufferNeeded;
switch(MinorFunction)
{
case IRP_MN_QUERY_ALL_DATA:
{
PWNODE_ALL_DATA wnode;
PWNODE_TOO_SMALL wnodeTooSmall;
ULONG bufferNeeded;
ULONG instanceCount;
POFFSETINSTANCEDATAANDLENGTH offsetInstanceDataAndLength;
ULONG i;
PULONG instanceLengthArray;
ULONG dataBlockOffset;
wnode = (PWNODE_ALL_DATA)Buffer;
dataBlockOffset = wnode->DataBlockOffset;
instanceCount = wnode->InstanceCount;
bufferNeeded = dataBlockOffset + BufferUsed;
if ((Status == SRB_STATUS_SUCCESS) &&
(bufferNeeded > BufferSize))
{
Status = SRB_STATUS_DATA_OVERRUN;
}
if (Status != SRB_STATUS_SUCCESS)
{
if (Status == SRB_STATUS_DATA_OVERRUN)
{
wnodeTooSmall = (PWNODE_TOO_SMALL)wnode;
wnodeTooSmall->WnodeHeader.BufferSize = sizeof(WNODE_TOO_SMALL);
wnodeTooSmall->WnodeHeader.Flags = WNODE_FLAG_TOO_SMALL;
wnodeTooSmall->SizeNeeded = bufferNeeded;
retSize = sizeof(WNODE_TOO_SMALL);
Status = SRB_STATUS_SUCCESS;
} else {
retSize = 0;
}
break;
}
instanceLengthArray = (PULONG)&wnode->OffsetInstanceDataAndLength[0];
offsetInstanceDataAndLength = (POFFSETINSTANCEDATAANDLENGTH)instanceLengthArray;
wnode->WnodeHeader.BufferSize = bufferNeeded;
retSize = bufferNeeded;
for (i = instanceCount; i != 0; i--)
{
offsetInstanceDataAndLength[i-1].LengthInstanceData = instanceLengthArray[i-1];
}
for (i = 0; i < instanceCount; i++)
{
offsetInstanceDataAndLength[i].OffsetInstanceData = dataBlockOffset;
dataBlockOffset = (dataBlockOffset + offsetInstanceDataAndLength[i].LengthInstanceData + 7) & ~7;
}
break;
}
case IRP_MN_QUERY_SINGLE_INSTANCE:
{
PWNODE_SINGLE_INSTANCE wnode;
PWNODE_TOO_SMALL wnodeTooSmall;
ULONG bufferNeeded;
wnode = (PWNODE_SINGLE_INSTANCE)Buffer;
bufferNeeded = wnode->DataBlockOffset + BufferUsed;
if (Status == SRB_STATUS_SUCCESS)
{
retSize = bufferNeeded;
wnode->WnodeHeader.BufferSize = bufferNeeded;
ASSERT(wnode->SizeDataBlock == BufferUsed);
} else if (Status == SRB_STATUS_DATA_OVERRUN) {
wnodeTooSmall = (PWNODE_TOO_SMALL)wnode;
wnodeTooSmall->WnodeHeader.BufferSize = sizeof(WNODE_TOO_SMALL);
wnodeTooSmall->WnodeHeader.Flags = WNODE_FLAG_TOO_SMALL;
wnodeTooSmall->SizeNeeded = bufferNeeded;
retSize = sizeof(WNODE_TOO_SMALL);
Status = SRB_STATUS_SUCCESS;
} else {
retSize = 0;
}
break;
}
case IRP_MN_EXECUTE_METHOD:
{
PWNODE_METHOD_ITEM wnode;
PWNODE_TOO_SMALL wnodeTooSmall;
ULONG bufferNeeded;
wnode = (PWNODE_METHOD_ITEM)Buffer;
bufferNeeded = wnode->DataBlockOffset + BufferUsed;
if (Status == SRB_STATUS_SUCCESS)
{
retSize = bufferNeeded;
wnode->WnodeHeader.BufferSize = bufferNeeded;
wnode->SizeDataBlock = BufferUsed;
} else if (Status == SRB_STATUS_DATA_OVERRUN) {
wnodeTooSmall = (PWNODE_TOO_SMALL)wnode;
wnodeTooSmall->WnodeHeader.BufferSize = sizeof(WNODE_TOO_SMALL);
wnodeTooSmall->WnodeHeader.Flags = WNODE_FLAG_TOO_SMALL;
wnodeTooSmall->SizeNeeded = bufferNeeded;
retSize = sizeof(WNODE_TOO_SMALL);
Status = SRB_STATUS_SUCCESS;
} else {
retSize = 0;
}
break;
}
default:
{
//
// All other requests don't return any data
retSize = 0;
break;
}
}
*ReturnSize = retSize;
return(Status);
}
VOID
ScsiPortWmiPostProcess(
IN PSCSIWMI_REQUEST_CONTEXT RequestContext,
IN UCHAR Status,
IN ULONG BufferUsed
)
{
ASSERT(RequestContext != NULL);
RequestContext->ReturnStatus = ScsiWmipPostProcess(
RequestContext->MinorFunction,
RequestContext->Buffer,
RequestContext->BufferSize,
BufferUsed,
Status,
&RequestContext->ReturnSize);
}
UCHAR
ScsiWmipProcessRequest(
IN PSCSI_WMILIB_CONTEXT WmiLibInfo,
IN UCHAR MinorFunction,
IN PVOID Context,
IN PSCSIWMI_REQUEST_CONTEXT RequestContext,
IN PVOID DataPath,
IN ULONG BufferSize,
IN PUCHAR Buffer,
OUT PULONG ReturnSize,
OUT PSYSCTL_SCSI_DISPOSITION IrpDisposition
)
{
UCHAR status;
ULONG retSize;
ULONG guidIndex;
ULONG instanceCount;
ULONG instanceIndex;
ASSERT(MinorFunction <= IRP_MN_EXECUTE_METHOD);
*IrpDisposition = ScsiProcessed;
if (MinorFunction != IRP_MN_REGINFO)
{
//
// For all requests other than query registration info we are passed
// a guid. Determine if the guid is one that is supported by the
// device.
ASSERT(WmiLibInfo->GuidList != NULL);
if (ScsiWmipFindGuid(WmiLibInfo->GuidList,
WmiLibInfo->GuidCount,
(LPGUID)DataPath,
&guidIndex,
&instanceCount))
{
status = SRB_STATUS_SUCCESS;
} else {
status = SRB_STATUS_ERROR;
}
if ((status == SRB_STATUS_SUCCESS) &&
((MinorFunction == IRP_MN_QUERY_SINGLE_INSTANCE) ||
(MinorFunction == IRP_MN_CHANGE_SINGLE_INSTANCE) ||
(MinorFunction == IRP_MN_CHANGE_SINGLE_ITEM) ||
(MinorFunction == IRP_MN_EXECUTE_METHOD)))
{
instanceIndex = ((PWNODE_SINGLE_INSTANCE)Buffer)->InstanceIndex;
if ( (((PWNODE_HEADER)Buffer)->Flags) &
WNODE_FLAG_STATIC_INSTANCE_NAMES)
{
if ( instanceIndex >= instanceCount )
{
status = SRB_STATUS_ERROR;
}
} else {
status = SRB_STATUS_ERROR;
}
}
//
// If we couldn't find the guid or the instance name index is out
// of range then return an error.
if (status != SRB_STATUS_SUCCESS)
{
*ReturnSize = 0;
*IrpDisposition = ScsiNotCompleted;
return(status);
}
}
switch(MinorFunction)
{
case IRP_MN_REGINFO:
{
ULONG guidCount;
PSCSIWMIGUIDREGINFO guidList;
PWMIREGINFOW wmiRegInfo;
PWMIREGGUIDW wmiRegGuid;
PWCHAR mofResourceName;
PWCHAR stringPtr;
ULONG mofResourceOffset;
USHORT mofResourceSize;
ULONG bufferNeeded;
ULONG i;
USHORT nameSize;
ULONG nameOffset, nameFlags;
USHORT mofResourceNameLen;
//
// Make sure that the required parts of the WMILIB_INFO structure
// are filled in.
ASSERT(WmiLibInfo->QueryWmiRegInfo != NULL);
ASSERT(WmiLibInfo->QueryWmiDataBlock != NULL);
status = WmiLibInfo->QueryWmiRegInfo(
Context,
RequestContext,
&mofResourceName);
if (status == SRB_STATUS_SUCCESS)
{
ASSERT(WmiLibInfo->GuidList != NULL);
guidList = WmiLibInfo->GuidList;
guidCount = WmiLibInfo->GuidCount;
nameOffset = sizeof(WMIREGINFO) +
guidCount * sizeof(WMIREGGUIDW);
nameFlags = WMIREG_FLAG_INSTANCE_BASENAME;
nameSize = sizeof(L"ScsiMiniPort");
mofResourceOffset = nameOffset + nameSize + sizeof(USHORT);
if (mofResourceName == NULL)
{
mofResourceSize = 0;
} else {
mofResourceNameLen = 0;
while (mofResourceName[mofResourceNameLen] != 0)
{
mofResourceNameLen++;
}
mofResourceSize = mofResourceNameLen * sizeof(WCHAR);
}
bufferNeeded = mofResourceOffset + mofResourceSize + sizeof(USHORT);
if (bufferNeeded <= BufferSize)
{
retSize = bufferNeeded;
wmiRegInfo = (PWMIREGINFO)Buffer;
wmiRegInfo->BufferSize = bufferNeeded;
wmiRegInfo->NextWmiRegInfo = 0;
wmiRegInfo->MofResourceName = mofResourceOffset;
wmiRegInfo->RegistryPath = 0;
wmiRegInfo->GuidCount = guidCount;
for (i = 0; i < guidCount; i++)
{
wmiRegGuid = &wmiRegInfo->WmiRegGuid[i];
wmiRegGuid->Guid = *guidList[i].Guid;
wmiRegGuid->Flags = guidList[i].Flags | nameFlags;
wmiRegGuid->InstanceInfo = nameOffset;
wmiRegGuid->InstanceCount = guidList[i].InstanceCount;
}
stringPtr = (PWCHAR)((PUCHAR)Buffer + nameOffset);
*stringPtr++ = nameSize;
ScsiPortMoveMemory(stringPtr,
L"ScsiMiniPort",
nameSize);
stringPtr = (PWCHAR)((PUCHAR)Buffer + mofResourceOffset);
*stringPtr++ = mofResourceSize;
ScsiPortMoveMemory(stringPtr,
mofResourceName,
mofResourceSize);
} else {
*((PULONG)Buffer) = bufferNeeded;
retSize = sizeof(ULONG);
}
} else {
// QueryWmiRegInfo failed
retSize = 0;
}
*ReturnSize = retSize;
*IrpDisposition = ScsiNotCompleted;
return(status);
}
case IRP_MN_QUERY_ALL_DATA:
{
PWNODE_ALL_DATA wnode;
ULONG bufferAvail;
PULONG instanceLengthArray;
PUCHAR dataBuffer;
ULONG instanceLengthArraySize;
ULONG dataBlockOffset;
wnode = (PWNODE_ALL_DATA)Buffer;
if (BufferSize < sizeof(WNODE_ALL_DATA))
{
//
// The buffer should never be smaller than the size of
// WNODE_ALL_DATA, however if it is then return with an
// error requesting the minimum sized buffer.
ASSERT(FALSE);
status = SRB_STATUS_ERROR;
*IrpDisposition = ScsiNotCompleted;
break;
}
wnode->InstanceCount = instanceCount;
wnode->WnodeHeader.Flags &= ~WNODE_FLAG_FIXED_INSTANCE_SIZE;
instanceLengthArraySize = instanceCount * sizeof(OFFSETINSTANCEDATAANDLENGTH);
dataBlockOffset = (FIELD_OFFSET(WNODE_ALL_DATA, OffsetInstanceDataAndLength) + instanceLengthArraySize + 7) & ~7;
wnode->DataBlockOffset = dataBlockOffset;
if (dataBlockOffset <= BufferSize)
{
instanceLengthArray = (PULONG)&wnode->OffsetInstanceDataAndLength[0];
dataBuffer = Buffer + dataBlockOffset;
bufferAvail = BufferSize - dataBlockOffset;
} else {
//
// There is not enough room in the WNODE to complete
// the query
instanceLengthArray = NULL;
dataBuffer = NULL;
bufferAvail = 0;
}
status = WmiLibInfo->QueryWmiDataBlock(
Context,
RequestContext,
guidIndex,
0,
instanceCount,
instanceLengthArray,
bufferAvail,
dataBuffer);
break;
}
case IRP_MN_QUERY_SINGLE_INSTANCE:
{
PWNODE_SINGLE_INSTANCE wnode;
ULONG dataBlockOffset;
wnode = (PWNODE_SINGLE_INSTANCE)Buffer;
dataBlockOffset = wnode->DataBlockOffset;
status = WmiLibInfo->QueryWmiDataBlock(
Context,
RequestContext,
guidIndex,
instanceIndex,
1,
&wnode->SizeDataBlock,
BufferSize - dataBlockOffset,
(PUCHAR)wnode + dataBlockOffset);
break;
}
case IRP_MN_CHANGE_SINGLE_INSTANCE:
{
PWNODE_SINGLE_INSTANCE wnode;
if (WmiLibInfo->SetWmiDataBlock != NULL)
{
wnode = (PWNODE_SINGLE_INSTANCE)Buffer;
status = WmiLibInfo->SetWmiDataBlock(
Context,
RequestContext,
guidIndex,
instanceIndex,
wnode->SizeDataBlock,
(PUCHAR)wnode + wnode->DataBlockOffset);
} else {
//
// If set callback is not filled in then it must be readonly
status = SRB_STATUS_ERROR;
*IrpDisposition = ScsiNotCompleted;
}
break;
}
case IRP_MN_CHANGE_SINGLE_ITEM:
{
PWNODE_SINGLE_ITEM wnode;
if (WmiLibInfo->SetWmiDataItem != NULL)
{
wnode = (PWNODE_SINGLE_ITEM)Buffer;
status = WmiLibInfo->SetWmiDataItem(
Context,
RequestContext,
guidIndex,
instanceIndex,
wnode->ItemId,
wnode->SizeDataItem,
(PUCHAR)wnode + wnode->DataBlockOffset);
} else {
//
// If set callback is not filled in then it must be readonly
status = SRB_STATUS_ERROR;
*IrpDisposition = ScsiNotCompleted;
}
break;
}
case IRP_MN_EXECUTE_METHOD:
{
PWNODE_METHOD_ITEM wnode;
if (WmiLibInfo->ExecuteWmiMethod != NULL)
{
wnode = (PWNODE_METHOD_ITEM)Buffer;
status = WmiLibInfo->ExecuteWmiMethod(
Context,
RequestContext,
guidIndex,
instanceIndex,
wnode->MethodId,
wnode->SizeDataBlock,
BufferSize - wnode->DataBlockOffset,
Buffer + wnode->DataBlockOffset);
} else {
//
// If method callback is not filled in then it must be error
status = SRB_STATUS_ERROR;
*IrpDisposition = ScsiNotCompleted;
}
break;
}
case IRP_MN_ENABLE_EVENTS:
{
if (WmiLibInfo->WmiFunctionControl != NULL)
{
status = WmiLibInfo->WmiFunctionControl(
Context,
RequestContext,
guidIndex,
ScsiWmiEventControl,
TRUE);
} else {
//
// If callback is not filled in then just succeed
status = SRB_STATUS_SUCCESS;
*IrpDisposition = ScsiNotCompleted;
}
break;
}
case IRP_MN_DISABLE_EVENTS:
{
if (WmiLibInfo->WmiFunctionControl != NULL)
{
status = WmiLibInfo->WmiFunctionControl(
Context,
RequestContext,
guidIndex,
ScsiWmiEventControl,
FALSE);
} else {
//
// If callback is not filled in then just succeed
status = SRB_STATUS_SUCCESS;
*IrpDisposition = ScsiNotCompleted;
}
break;
}
case IRP_MN_ENABLE_COLLECTION:
{
if (WmiLibInfo->WmiFunctionControl != NULL)
{
status = WmiLibInfo->WmiFunctionControl(
Context,
RequestContext,
guidIndex,
ScsiWmiDataBlockControl,
TRUE);
} else {
//
// If callback is not filled in then just succeed
status = SRB_STATUS_SUCCESS;
*IrpDisposition = ScsiNotCompleted;
}
break;
}
case IRP_MN_DISABLE_COLLECTION:
{
if (WmiLibInfo->WmiFunctionControl != NULL)
{
status = WmiLibInfo->WmiFunctionControl(
Context,
RequestContext,
guidIndex,
ScsiWmiDataBlockControl,
FALSE);
} else {
//
// If callback is not filled in then just succeed
status = SRB_STATUS_SUCCESS;
*IrpDisposition = ScsiNotCompleted;
}
break;
}
default:
{
ASSERT(FALSE);
status = SRB_STATUS_ERROR;
*IrpDisposition = ScsiNotCompleted;
break;
}
}
return(status);
}
BOOLEAN
ScsiPortWmiDispatchFunction(
IN PSCSI_WMILIB_CONTEXT WmiLibInfo,
IN UCHAR MinorFunction,
IN PVOID Context,
IN PSCSIWMI_REQUEST_CONTEXT RequestContext,
IN PVOID DataPath,
IN ULONG BufferSize,
IN PVOID Buffer
)
/*++
Routine Description:
Dispatch helper routine for WMI srb requests. Based on the Minor
function passed the WMI request is processed and this routine
invokes the appropriate callback in the WMILIB structure.
Arguments:
WmiLibInfo has the SCSI WMILIB information control block associated
with the adapter or logical unit
DeviceContext is miniport defined context value passed on to the callbacks
invoked by this api.
RequestContext is a pointer to a context structure that maintains
information about this WMI srb. This request context must remain
valid throughout the entire processing of the srb, at least until
ScsiPortWmiPostProcess returns with the final srb return status and
buffer size. If the srb can pend then memory for this buffer should
be allocated from the SRB extension. If not then the memory can be
allocated from a stack frame that does not go out of scope, perhaps
that of the caller to this api.
DataPath is value passed in wmi request
BufferSize is value passed in wmi request
Buffer is value passed in wmi request
Return Value:
TRUE if request is pending else FALSE
--*/
{
UCHAR status;
SYSCTL_SCSI_DISPOSITION irpDisposition;
ULONG retSize;
ASSERT(RequestContext != NULL);
//
// First ensure that the irp is a WMI irp
if (MinorFunction > IRP_MN_EXECUTE_METHOD)
{
//
// This is not a WMI irp, setup error return
status = SRB_STATUS_ERROR;
RequestContext->ReturnSize = 0;
RequestContext->ReturnStatus = status;
} else {
//
// Let SCSIWMI library have a crack at the SRB
RequestContext->MinorFunction = MinorFunction;
RequestContext->Buffer = Buffer;
RequestContext->BufferSize = BufferSize;
RequestContext->ReturnSize = 0;
status = ScsiWmipProcessRequest(WmiLibInfo,
MinorFunction,
Context,
RequestContext,
DataPath,
BufferSize,
Buffer,
&retSize,
&irpDisposition);
if (irpDisposition == ScsiNotCompleted)
{
//
// Some error occured while processing the SRB, for example
// guid not found. Setup the returned error
RequestContext->ReturnStatus = status;
if (status != SRB_STATUS_SUCCESS)
{
retSize = 0;
}
RequestContext->ReturnSize = retSize;
}
}
return(status == SRB_STATUS_PENDING);
}
VOID
ScsiPortWmiFireLogicalUnitEvent(
IN PVOID HwDeviceExtension,
IN UCHAR PathId,
IN UCHAR TargetId,
IN UCHAR Lun,
IN LPGUID Guid,
IN ULONG InstanceIndex,
IN ULONG EventDataSize,
IN PVOID EventData
)
/*++
Routine Description:
This routine will fire a WMI event using the data buffer passed. This
routine may be called at or below DPC level
Arguments:
HwDeviceExtension is the adapter device extension
PathId identifies the SCSI bus if a logical unit is firing the event
or is 0xff if the adapter is firing the event.
TargetId identifies the target controller or device on the bus
Lun identifies the logical unit number of the target device
Guid is pointer to the GUID that represents the event
InstanceIndex is the index of the instance of the event
EventDataSize is the number of bytes of data that is being fired with
with the event. This size specifies the size of the event data only
and does NOT include the 0x40 bytes of preceeding padding.
EventData is the data that is fired with the events. There must be exactly
0x40 bytes of padding preceeding the event data.
Return Value:
--*/
{
PWNODE_SINGLE_INSTANCE event;
UCHAR status;
ASSERT(EventData != NULL);
event = (PWNODE_SINGLE_INSTANCE)EventData;
event->WnodeHeader.Guid = *Guid;
event->WnodeHeader.Flags = WNODE_FLAG_SINGLE_INSTANCE |
WNODE_FLAG_EVENT_ITEM |
WNODE_FLAG_STATIC_INSTANCE_NAMES;
event->WnodeHeader.Linkage = 0;
event->InstanceIndex = InstanceIndex;
event->SizeDataBlock = EventDataSize;
event->DataBlockOffset = 0x40;
event->WnodeHeader.BufferSize = event->DataBlockOffset +
event->SizeDataBlock;
if (PathId != 0xff)
{
ScsiPortNotification(WMIEvent,
HwDeviceExtension,
event,
PathId,
TargetId,
Lun);
} else {
ScsiPortNotification(WMIEvent,
HwDeviceExtension,
event,
PathId);
}
}