Sample Code

Windows Driver Samples/ iSCSI WMI Client/ C++/ src/ client/ wmisample.c/

/*++

Copyright (C) Microsoft Corporation, 2000

Module Name:

    wmi.c

Abstract:

    This file contains WMI routines for iSCSI miniport driver.

Environment:

    kernel mode only

--*/


UCHAR iScsiQuerySecurityCapabilities(
    PISCSI_ADAPTER_EXTENSION AdapterExtension,
    ULONG BufferAvail,
    PUCHAR Buffer,
    PULONG InstanceLength,
    PULONG SizeNeeded
    );

UCHAR iScsiQueryDiscoveryConfig(
    PISCSI_ADAPTER_EXTENSION AdapterExtension,
    ULONG BufferAvail,
    PUCHAR Buffer,
    PULONG InstanceLength,
    PULONG SizeNeeded
    );

UCHAR iScsiAdvanceString(
    OUT PWCHAR *String,
    OUT PUSHORT StringLen,
    IN OUT PUCHAR *Buffer,
    IN OUT PULONG BufferSize
    );

UCHAR iScsiSetDiscoveryConfig(
    PISCSI_ADAPTER_EXTENSION AdapterExtension,
    ULONG BufferSize,
    PUCHAR Buffer
    );

UCHAR iScsiAddPortalGroup(
    IN OUT PUCHAR *Buffer,
    IN OUT PULONG SizeLeft,
    IN OUT PULONG SizeNeeded,
    IN ULONG PortalCount
    );

VOID iScsiCopyUTF8ToCounted(
    PWCHAR p,
    PUTF8CHAR UTF8,
    ULONG UTF8Len
    );

UCHAR iScsiAddPortalToGroup(
    IN OUT PUCHAR *Buffer,
    IN OUT PULONG SizeLeft,
    IN OUT PULONG SizeNeeded,
    IN ULONG Address,
    IN PUTF8CHAR SymbolicName,
    IN USHORT Socket,
    IN ULONG SecurityBitmap,
    IN ULONG KeySize,
    IN PUCHAR Key
    );

UCHAR iScsiAddDiscoveredTarget(
    IN OUT PUCHAR *Buffer,
    IN OUT PULONG SizeLeft,
    IN OUT PULONG SizeNeeded,
    IN PUTF8CHAR Name,
    IN PUTF8CHAR Alias,
    IN PUTF8CHAR DDName,
    IN ULONG DDID,
    IN ULONG PortalGroupCount
    );

UCHAR iScsiReportDiscoveredTargets2(
    PISCSI_ADAPTER_EXTENSION AdapterExtension,
    ULONG BufferSize,
    PULONG SizeNeeded,
    PUCHAR Buffer
    );

UCHAR
iSpReadTCPConfigInfo(
    IN PISCSI_ADAPTER_EXTENSION AdapterExtension,
    OUT PUCHAR Buffer
   );

UCHAR
iSpReadNICConfigInfo(
    IN PISCSI_ADAPTER_EXTENSION AdapterExtension,
    OUT PUCHAR Buffer
   );

UCHAR
iSpReadNICPerfData(
    IN PISCSI_ADAPTER_EXTENSION AdapterExtension,
    OUT PUCHAR Buffer
   );

UCHAR
iSpSetTCPConfigInfo(
    IN PISCSI_ADAPTER_EXTENSION AdapterExtension,
    IN ULONG BufferSize,
    IN PUCHAR Buffer
    );

UCHAR
iScsiSetWmiDataBlock(
    IN PVOID Context,
    IN PSCSIWMI_REQUEST_CONTEXT DispatchConiScsiQueryWmiDataBlocktext,
    IN ULONG GuidIndex,
    IN ULONG InstanceIndex,
    IN ULONG BufferSize,
    IN PUCHAR Buffer
    );


GUID iSCSI_Operations_GUID = MSiSCSI_OperationsGuid;
GUID iSCSI_HBAInformation_GUID = MSiSCSI_HBAInformationGuid;
GUID iScsiInitiatorInstanceStatisticsGuid = MSiSCSI_InitiatorInstanceStatisticsGuid;
GUID iScsiInitiatorInstanceFailureEventGuid = MSiSCSI_InitiatorInstanceFailureEventGuid;
GUID iScsiConnectStatisticsGuid = MSiSCSI_ConnectionStatisticsGuid;
GUID iScsiSessionStatisticsGuid = MSiSCSI_SessionStatisticsGuid;
GUID iScsi_InitiatorSessionInfo_GUID = MSiSCSI_InitiatorSessionInfoGuid;
GUID iScsi_InitiatorLoginStatistics_GUID = MSiSCSI_InitiatorLoginStatisticsGuid;
GUID iScsiInitiatorNodeFailureEventGuid = MSiSCSI_InitiatorInstanceFailureEventGuid;
GUID iScsiAdapterEventGuid = MSiSCSI_AdapterEventGuid;
GUID iScsiPortalInfoClassGuid = MSiSCSI_PortalInfoClassGuid;
GUID iScsiTargetMappingClassGuid = MSiSCSI_TargetMappingsGuid;
GUID iScsiPersistentLoginsClassGuid = MSiSCSI_PersistentLoginsGuid;
GUID iScsiLBOperationsGuid = MSiSCSI_LB_OperationsGuid;
GUID iScsiQueryLBPolicyGuid = MSiSCSI_QueryLBPolicyGuid;
GUID iSCSIEventlogGuid = MSiSCSI_EventlogGuid;
GUID iSCSIRedirectPortalGuid = MSiSCSI_RedirectPortalInfoClassGuid;
GUID iSCSIReqProcTimeGuid = MSiSCSI_RequestTimeStatisticsGuid;
GUID iSCSIBootInfoGuid = MSiSCSI_BootInformationGuid;

GUID iScsiDiscoveryConfigGuid = MSiSCSI_DiscoveryConfigGuid;
GUID iSCSI_DiscoveryOperationsGuid = MSiSCSI_DiscoveryOperationsGuid;
GUID iSCSI_SecurityCapabilitiesGuid = MSiSCSI_SecurityCapabilitiesGuid;
GUID iSCSI_SecurityConfigOperationsGuid = MSiSCSI_SecurityConfigOperationsGuid;
GUID MSiScsiTCPConfigGuid = MSiSCSI_TCPIPConfigGuid;
GUID MSiScsiNICConfigGuid = MSiSCSI_NICConfigGuid;
GUID MSiScsiNICPerfGuid = MSiSCSI_NICPerformanceGuid;
GUID MSiSCSIManagementOperationsGuid= MSiSCSI_ManagementOperationsGuid;

//
// This GUID will be replaced with the GUID for WMI Tracing in the port driver
//
GUID iSCSIDummyGuid = { 0,0,0, { 0,0,0,0,0,0,0,0 } };

//
// The index numbers should correspond to the offset in
// iScsiWmiGuidList array given below.
//
#define iSCSI_Operations_GUID_Index                        0
#define iSCSI_HBAInformation_GUID_Index                    1
#define iScsi_InitiatorInstanceStatistics_GUID_INDEX       2
#define iScsi_InitiatorInstanceFailureEvent_GUID_INDEX     3
#define iScsi_ConnectStatistics_GUID_INDEX                 4
#define iScsi_SessionStatistics_GUID_INDEX                 5
#define iScsi_InitiatorSessionInfo_GUID_INDEX              6
#define iScsi_InitiatorLoginStatistics_GUID_INDEX          7
#define iScsi_InitiatorNodeFailureEvent_GUID_INDEX         8
#define iScsi_AdapterEvent_GUID_Index                      9
#define iScsi_PortalInfoClass_GUID_Index                   10
#define iScsi_TargetMapping_GUID_Index                     11
#define iScsi_PersistentLogin_GUID_Index                   12
#define iScsi_LB_Operations_GUID_Index                     13
#define iScsi_Query_LB_Policy_GUID_Index                   14
#define iScsi_Eventlog_GUID_Index                          15
#define iScsi_Redirect_Portal_GUID_Index                   16
#define iScsi_RequestProcessingTime_GUID_Index             17
#define iScsi_BootInfo_GUID_Index                          18

#define iScsi_MAX_GUID_INDEX                               iScsi_BootInfo_GUID_Index

#define iScsiDiscoveryConfigGuid_INDEX                     iScsi_MAX_GUID_INDEX + 1
#define iSCSI_DiscoveryOperationsGuid_INDEX                iScsi_MAX_GUID_INDEX + 2
#define iSCSI_SecurityCapabilitiesGuid_INDEX               iScsi_MAX_GUID_INDEX + 3
#define iSCSI_SecurityConfigOperationsGuid_INDEX           iScsi_MAX_GUID_INDEX + 4
#define iScsi_TCPConfig_GUID_Index                         iScsi_MAX_GUID_INDEX + 5
#define iScsi_NICConfig_GUID_Index                         iScsi_MAX_GUID_INDEX + 6
#define iScsi_NICPerf_GUID_Index                           iScsi_MAX_GUID_INDEX + 7
#define iScsi_ManagementOperation_GUID_Index               iScsi_MAX_GUID_INDEX + 8

//
// GUID List and number of GUIDs in the list
//
SCSIWMIGUIDREGINFO iScsiWmiGuidList[] =
{
    {
        &iSCSI_Operations_GUID,
        1,
        0
    },

    {
        &iSCSI_HBAInformation_GUID,
        1,
        0
    },

    {
        &iScsiInitiatorInstanceStatisticsGuid,
        1,
        0
    },

    {
        &iScsiInitiatorInstanceFailureEventGuid,
        1,
        WMIREG_FLAG_EVENT_ONLY_GUID
    },

    {
        &iScsiConnectStatisticsGuid,
        0xffffffff, //dynamic instance names
        0
     },

    {
        &iScsiSessionStatisticsGuid,
        0xffffffff, //dynamic instance names
        0
    },

    {
        &iScsi_InitiatorSessionInfo_GUID,
        1,
        0
    },

    {
        &iScsi_InitiatorLoginStatistics_GUID,
        1,
        0
    },

    {
        &iScsiInitiatorNodeFailureEventGuid,
        1,
        WMIREG_FLAG_EVENT_ONLY_GUID
    },

    {
        &iScsiAdapterEventGuid,
        1,
        WMIREG_FLAG_EVENT_ONLY_GUID
    },

    {
        &iScsiPortalInfoClassGuid,
        1,
        0
    },

    {
        &iScsiTargetMappingClassGuid,
        1,
        0
    },

    {
        &iScsiPersistentLoginsClassGuid,
        1,
        0
    },

    {
        &iScsiLBOperationsGuid,
        1,
        0
    },

    {
        &iScsiQueryLBPolicyGuid,
        1,
        0
    },

    {
        &iSCSIEventlogGuid,
        1,
        0
    },

    {
        &iSCSIRedirectPortalGuid,
        1,
        0
    },

    {
        &iSCSIReqProcTimeGuid,
        0xffffffff, //dynamic instance names
        0
    },

    {
        &iSCSIBootInfoGuid,
        1,
        0
    },

    {
        &iScsiDiscoveryConfigGuid,
        1,
        0
    },

    {
        &iSCSI_DiscoveryOperationsGuid,
        1,
        0
    },

    {
        &iSCSI_SecurityCapabilitiesGuid,
        1,
        0
    },

    {
        &iSCSI_SecurityConfigOperationsGuid,
        1,
        0
    },

    {
        &MSiScsiTCPConfigGuid,
        1,
        0
    },

    {
        &MSiScsiNICConfigGuid,
        1,
        0
    },

    {
        &MSiScsiNICPerfGuid,
        1,
        0
    },

    {
        &MSiSCSIManagementOperationsGuid,
        1,
        0
    },

};

#define iScsiWmiGuidCount (sizeof(iScsiWmiGuidList) / sizeof(SCSIWMIGUIDREGINFO))

//
// WMI Guid list for the PDO
//
GUID iSCSI_LUNMappingInformationGuid = MSiSCSI_LUNMappingInformationGuid;

#define MSiSCSI_LUNMappingInformation_Index 0

SCSIWMIGUIDREGINFO iScsiPDOWmiGuidList[] =
{
    {
        &iSCSI_LUNMappingInformationGuid,
        1,
        0
    }
};

#define iScsiPDOWmiGuidCount (sizeof(iScsiPDOWmiGuidList) / sizeof(SCSIWMIGUIDREGINFO))

VOID
iScsiWmiInitializeContext(
    IN PISCSI_ADAPTER_EXTENSION AdapterExtension
    )
/*+++

Routine Description:

    This routine will initialize the wmilib context structure with the
    guid list and the pointers to the wmilib callback functions.

Arguments:

    AdapterExtension - Adpater extension

Return Value:

    None.

--*/
{
    PSCSI_WMILIB_CONTEXT wmiLibContext;


    //
    // Initialize the wmilib context for the adapter
    //
    wmiLibContext = &(AdapterExtension->WmiLibContext);

    wmiLibContext->GuidList = iScsiWmiGuidList;
    wmiLibContext->GuidCount = iScsiWmiGuidCount;

    //
    // Set pointers to WMI callback routines
    //
    wmiLibContext->QueryWmiRegInfo = iScsiQueryWmiRegInfo;
    wmiLibContext->QueryWmiDataBlock = iScsiQueryWmiDataBlock;
    wmiLibContext->ExecuteWmiMethod = iScsiExecuteWmiMethod;
    wmiLibContext->WmiFunctionControl = NULL;
    wmiLibContext->SetWmiDataItem = NULL;


    wmiLibContext->SetWmiDataBlock = iScsiSetWmiDataBlock;

    //
    // Initialize discovery config
    //
    AdapterExtension->DiscoveryConfigFlags = (DISCOVERY_CONFIG_DO_DISCOVERY |
                                              DISCOVERY_CONFIG_FIND_SNS_AUTOMATICALLY);
    AdapterExtension->SNSServer[0] = 0;


    //
    // Initialize the wmilib context for the PDO
    //
    wmiLibContext = &(AdapterExtension->PDOWmiLibContext);

    wmiLibContext->GuidList = iScsiPDOWmiGuidList;
    wmiLibContext->GuidCount = iScsiPDOWmiGuidCount;

    //
    // Set pointers to WMI callback routines
    //
    wmiLibContext->QueryWmiRegInfo = iScsiQueryWmiRegInfo;
    wmiLibContext->QueryWmiDataBlock = iScsiPDOQueryWmiDataBlock;
    wmiLibContext->ExecuteWmiMethod = NULL;
    wmiLibContext->WmiFunctionControl = NULL;
    wmiLibContext->SetWmiDataItem = NULL;
    wmiLibContext->SetWmiDataBlock = NULL;

    return;
}

BOOLEAN
iScsiWmiSrb(
    IN     PISCSI_ADAPTER_EXTENSION    AdapterExtension,
    IN OUT PSCSI_WMI_REQUEST_BLOCK     Srb
    )
/*++

Routine Description:

   Called from StartIo routine to process an SRB_FUNCTION_WMI request.
   Main entry point for all WMI routines.

Arguments:

   AdapterExtension - ISCSI miniport driver's Adapter extension.

   Srb              - IO request packet.

Return Value:

   Always TRUE.

--*/
{
    PSCSIWMI_REQUEST_CONTEXT requestContext;
    PISCSI_SRB_EXTENSION srbExtension;
    BOOLEAN pending;
    BOOLEAN completeRequest = TRUE;
    BOOLEAN adapterRequest;


    //
    // Validate our assumptions.
    //
    NT_ASSERT(Srb->Function == SRB_FUNCTION_WMI);
    NT_ASSERT(Srb->Length == sizeof(SCSI_WMI_REQUEST_BLOCK));

    srbExtension = (PISCSI_SRB_EXTENSION) Srb->SrbExtension;

    requestContext = &(srbExtension->WmiRequestContext);

    //
    // Save the pointer to the SRB in UserContext
    // of SCSIWMI_REQUEST_CONTEXT
    //
    requestContext->UserContext = Srb;

    //
    // Check if the WMI SRB is targetted for
    // the iScsi adapter or one of the devices
    //
    adapterRequest = (Srb->WMIFlags & SRB_WMI_FLAGS_ADAPTER_REQUEST) == SRB_WMI_FLAGS_ADAPTER_REQUEST;

    //
    // Process the incoming WMI request.
    //
    DebugPrint((iScsiPrtDebugTrace,
        "Entering iScsiWmiSrb\n"));

    ETWDebugPrint(iScsiLevelTrace,
        iScsiFlagWmi,
        "Entering iScsiWmiSrb\n");

    pending = ScsiPortWmiDispatchFunction(adapterRequest ?
                                             &AdapterExtension->WmiLibContext :
                                             &AdapterExtension->PDOWmiLibContext,
                                          Srb->WMISubFunction,
                                          AdapterExtension,
                                          requestContext,
                                          Srb->DataPath,
                                          Srb->DataTransferLength,
                                          Srb->DataBuffer);
    if (pending == FALSE) {

        //
        // We can do this since we assume it is done synchronously
        //
        Srb->DataTransferLength = ScsiPortWmiGetReturnSize(requestContext);;

        //
        // Adapter ready for next request.
        //
        Srb->SrbStatus = ScsiPortWmiGetReturnStatus(requestContext);

    }  else {
        completeRequest = FALSE;
    }

    if (completeRequest == TRUE) {
        ScsiPortNotification(RequestComplete, AdapterExtension, Srb);
    }

    return TRUE;
}

UCHAR
iScsiQueryWmiRegInfo(
    IN PVOID Context,
    IN PSCSIWMI_REQUEST_CONTEXT RequestContext,
    _Out_ PWCHAR *MofResourceName
    )
/*+++

Routine Description:

    This routine returns MofResourceName for this driver.

--*/
{
    *MofResourceName = iScsiWmi_MofResourceName;

    return SRB_STATUS_SUCCESS;
}

BOOLEAN
iScsiQueryWmiDataBlock(
    IN PVOID Context,
    IN PSCSIWMI_REQUEST_CONTEXT DispatchContext,
    IN ULONG GuidIndex,
    IN ULONG InstanceIndex,
    IN ULONG InstanceCount,
    IN OUT PULONG InstanceLengthArray,
    IN ULONG BufferAvail,
    OUT PUCHAR Buffer
    )
/*+++

Routine Description :

    Called to query WMI Data blocks

--*/
{
    PISCSI_ADAPTER_EXTENSION adapterExtension;
    PSCSI_WMI_REQUEST_BLOCK  srb;
    UCHAR status;
    ULONG sizeNeeded = 0;
    NTSTATUS ntStatus;


    adapterExtension = (PISCSI_ADAPTER_EXTENSION) Context;
    srb = (PSCSI_WMI_REQUEST_BLOCK) DispatchContext->UserContext;

    switch (GuidIndex) {
        case iSCSI_Operations_GUID_Index:
        case iScsi_LB_Operations_GUID_Index: {

            //
            // Even though this class only has methods, we need to
            // respond to any queries for it since WMI expects that
            // there is an actual instance of the class on which to
            // execute the method
            //

            sizeNeeded = sizeof(ULONG);
            if (BufferAvail >= sizeNeeded) {
                *InstanceLengthArray = sizeNeeded;
                status = SRB_STATUS_SUCCESS;
            } else {
                status = SRB_STATUS_DATA_OVERRUN;
            }

            break;
        }

        case iSCSI_HBAInformation_GUID_Index: {

            PMSiSCSI_HBAInformation iScsiHBAInformation;

            sizeNeeded = sizeof(MSiSCSI_HBAInformation);
            if (BufferAvail >= sizeNeeded) {

                *InstanceLengthArray = sizeNeeded;

                iScsiHBAInformation = (PMSiSCSI_HBAInformation) Buffer;

                ntStatus = iSpBuildHBAInformationBuffer(adapterExtension, iScsiHBAInformation);

                status = NT_SUCCESS(ntStatus) ? SRB_STATUS_SUCCESS : SRB_STATUS_ERROR;

            } else {

                status = SRB_STATUS_DATA_OVERRUN;
            }

            break;
        }

        case iScsi_InitiatorInstanceStatistics_GUID_INDEX: {

            sizeNeeded = sizeof(MSiSCSI_InitiatorInstanceStatistics);

            if (BufferAvail >= sizeNeeded) {

                status =  iSpReadInitiatorInstanceStatistics(adapterExtension,
                                                             Buffer);

                if (status != SRB_STATUS_ERROR) {
                    *InstanceLengthArray = sizeNeeded;

                    status = SRB_STATUS_SUCCESS;
                }
            } else {
                status = SRB_STATUS_DATA_OVERRUN;
            }

            break;
        }

        case iScsi_ConnectStatistics_GUID_INDEX: {

            status = iSpBuildConnectionStatistics(adapterExtension,
                                                  DispatchContext,
                                                  GuidIndex,
                                                  InstanceIndex,
                                                  InstanceCount,
                                                  InstanceLengthArray,
                                                  BufferAvail,
                                                  Buffer,
                                                  &sizeNeeded);

            break;
        }

        case iScsi_RequestProcessingTime_GUID_Index: {

            status = iSpBuildRequestTimeStatistics(adapterExtension,
                                                   DispatchContext,
                                                   GuidIndex,
                                                   InstanceIndex,
                                                   InstanceCount,
                                                   InstanceLengthArray,
                                                   BufferAvail,
                                                   Buffer,
                                                   &sizeNeeded);

            break;
        }

        case iScsi_SessionStatistics_GUID_INDEX: {


            status = iSpBuildSessionStatistics(adapterExtension,
                                               DispatchContext,
                                               GuidIndex,
                                               InstanceIndex,
                                               InstanceCount,
                                               InstanceLengthArray,
                                               BufferAvail,
                                               Buffer,
                                               &sizeNeeded);
            break;
        }

        case iScsi_InitiatorLoginStatistics_GUID_INDEX: {
            PWMI_CONNECTION_LIST connectionList;
            ULONG numberOfSessions;
            ULONG numberOfConnections;

            sizeNeeded = sizeof(MSiSCSI_InitiatorLoginStatistics);
            if (BufferAvail >= sizeNeeded)
            {
                RtlZeroMemory(Buffer, sizeNeeded);

                connectionList = iSpGetConnectionList(adapterExtension,
                                                      &numberOfSessions,
                                                      &numberOfConnections);

                if (connectionList != NULL)
                {
                    status = iSpReadInitiatorLoginStatistics(adapterExtension,
                                                            connectionList,
                                                            numberOfSessions,
                                                            Buffer);

                    *InstanceLengthArray = sizeNeeded;

                    iSpReleaseConnectionReferences(connectionList,
                                                   numberOfSessions);

                } else {
                    status = SRB_STATUS_ERROR;
                }


            } else {
                status = SRB_STATUS_DATA_OVERRUN;
            }
            break;
        }

        case iScsi_InitiatorSessionInfo_GUID_INDEX: {

            status = iSpBuildInitiatorSessionInfo(adapterExtension,
                                                 DispatchContext,
                                                 BufferAvail,
                                                 Buffer,
                                                 &sizeNeeded);

            *InstanceLengthArray = sizeNeeded;

            break;

        }

        case iScsi_PortalInfoClass_GUID_Index: {
            status = iSpBuildPortalInfo(adapterExtension,
                                        InstanceLengthArray,
                                        BufferAvail,
                                        Buffer,
                                        &sizeNeeded);
            break;
        }

        case iScsi_TargetMapping_GUID_Index: {

            status = iSpBuildTargetMapping(adapterExtension,
                                           InstanceLengthArray,
                                           BufferAvail,
                                           Buffer,
                                           &sizeNeeded);

            break;
        }

        case iScsi_PersistentLogin_GUID_Index: {

            status = iSpBuildPersistentLoginInfo(adapterExtension,
                                                 InstanceLengthArray,
                                                 BufferAvail,
                                                 Buffer,
                                                 &sizeNeeded);
            *InstanceLengthArray = sizeNeeded;

            break;
        }

        case iScsi_Query_LB_Policy_GUID_Index: {

            status = iSpQueryLBPolicy(adapterExtension,
                                      InstanceLengthArray,
                                      BufferAvail,
                                      Buffer,
                                      &sizeNeeded);

            *InstanceLengthArray = sizeNeeded;

            break;
        }

        case iScsi_Redirect_Portal_GUID_Index: {

            status = iSpBuildRedirectTargetInfo(adapterExtension,
                                                InstanceLengthArray,
                                                BufferAvail,
                                                Buffer,
                                                &sizeNeeded);

            *InstanceLengthArray = sizeNeeded;

            break;
        }

        case iScsi_BootInfo_GUID_Index: {

            status = iSpBuildBootInfo(adapterExtension,
                                      InstanceLengthArray,
                                      BufferAvail,
                                      Buffer,
                                      &sizeNeeded);

            *InstanceLengthArray = sizeNeeded;

            break;
        }



        case iSCSI_SecurityCapabilitiesGuid_INDEX:
        {
            status = iScsiQuerySecurityCapabilities(adapterExtension,
                                              BufferAvail,
                                              Buffer,
                                              InstanceLengthArray,
                                              &sizeNeeded);
            break;
        }

        case iScsiDiscoveryConfigGuid_INDEX:
        {
            status = iScsiQueryDiscoveryConfig(adapterExtension,
                                               BufferAvail,
                                               Buffer,
                                               InstanceLengthArray,
                                               &sizeNeeded);
            break;
        }

        case iSCSI_SecurityConfigOperationsGuid_INDEX:
        case iSCSI_DiscoveryOperationsGuid_INDEX:
        case iScsi_ManagementOperation_GUID_Index:
        {
            //
            // Operations only has methods, but we need to support the
            // query since WMI needs to be able to query before
            // executing a method
            //
            sizeNeeded = sizeof(ULONG);
            if (BufferAvail >= sizeNeeded)
            {
                *InstanceLengthArray = sizeNeeded;
                status = SRB_STATUS_SUCCESS;
            } else {
                status = SRB_STATUS_DATA_OVERRUN;
            }
            break;
        }

        case iScsi_TCPConfig_GUID_Index:
        {
          sizeNeeded = sizeof(MSiSCSI_TCPIPConfig);
          if (BufferAvail >= sizeNeeded) {
              *InstanceLengthArray = sizeNeeded;
              status = iSpReadTCPConfigInfo(adapterExtension, Buffer);
          } else {
              status = SRB_STATUS_DATA_OVERRUN;
          }

          break;
        }

        case iScsi_NICConfig_GUID_Index:
        {
          sizeNeeded = sizeof(MSiSCSI_NICConfig);

          if (BufferAvail >= sizeNeeded)
          {
              *InstanceLengthArray = sizeNeeded;
              status = iSpReadNICConfigInfo(adapterExtension, Buffer);

          } else {

              status = SRB_STATUS_DATA_OVERRUN;
          }

          break;
        }

        case iScsi_NICPerf_GUID_Index:
        {
          sizeNeeded = sizeof(MSiSCSI_NICPerformance);

          if (BufferAvail >= sizeNeeded)
          {
              *InstanceLengthArray = sizeNeeded;
              status = iSpReadNICPerfData(adapterExtension, Buffer);

          } else {

              status = SRB_STATUS_DATA_OVERRUN;
          }

          break;
        }

        default: {
            status = SRB_STATUS_ERROR;
            break;
        }

    }

    iSpCompleteWmiRequest(adapterExtension, srb, DispatchContext, status, sizeNeeded);

    //
    // return SRB_STATUS_PENDING since the request has either already been
    // completed in iSpCompleteWmiRequest or the status is really
    // SRB_STATUS_PENDING. This implies that the iScsiWmiSrb routine
    // may not touch the srb since it is already completed.
    //
    return SRB_STATUS_PENDING;

}

VOID
iScsiBuildLUNMapping(
    PISCSI_ADAPTER_EXTENSION AdapterExtension,
    PMSiSCSI_LUNMappingInformation Mapping,
    PSCSI_WMI_REQUEST_BLOCK Srb
    )
/*+++

Routine Description :

    This routine will fill in the target mapping information for a
    particular lun

--*/
{
    PISCSI_SESSION iScsiSession;


    iScsiSession = AdapterExtension->SessionLookupTable[Srb->TargetId];
    if (iScsiSession != NULL)
    {
        Mapping->UniqueSessionId = iScsiSession->SessionId;
    }


    Mapping->UniqueAdapterId = (ULONGLONG) AdapterExtension;
    Mapping->OSBus = Srb->PathId;
    Mapping->OSTarget = Srb->TargetId;
    Mapping->OSLUN = Srb->Lun;
}

BOOLEAN
iScsiPDOQueryWmiDataBlock(
    IN PVOID Context,
    IN PSCSIWMI_REQUEST_CONTEXT DispatchContext,
    IN ULONG GuidIndex,
    IN ULONG InstanceIndex,
    IN ULONG InstanceCount,
    IN OUT PULONG InstanceLengthArray,
    IN ULONG BufferAvail,
    OUT PUCHAR Buffer
    )
/*+++

Routine Description :

    Called to query WMI Data blocks

--*/
{
    PISCSI_ADAPTER_EXTENSION adapterExtension;
    PSCSI_WMI_REQUEST_BLOCK  srb;
    UCHAR status;
    ULONG sizeNeeded = 0;
    PMSiSCSI_LUNMappingInformation mapping;


    adapterExtension = (PISCSI_ADAPTER_EXTENSION) Context;
    srb = (PSCSI_WMI_REQUEST_BLOCK) DispatchContext->UserContext;

    switch (GuidIndex) {
        case MSiSCSI_LUNMappingInformation_Index:
        {
          sizeNeeded = sizeof(MSiSCSI_LUNMappingInformation);

          if (BufferAvail >= sizeNeeded)
          {
              mapping = (PMSiSCSI_LUNMappingInformation)Buffer;

              iScsiBuildLUNMapping(adapterExtension, mapping, srb);

              *InstanceLengthArray = sizeNeeded;
              status = SRB_STATUS_SUCCESS;

          } else {

              status = SRB_STATUS_DATA_OVERRUN;
          }

          break;
        }

        default: {
            status = SRB_STATUS_ERROR;
            break;
        }

    }

    iSpCompleteWmiRequest(adapterExtension, srb, DispatchContext, status, sizeNeeded);

    //
    // return SRB_STATUS_PENDING since the request has either already been
    // completed in iSpCompleteWmiRequest or the status is really
    // SRB_STATUS_PENDING. This implies that the iScsiWmiSrb routine
    // may not touch the srb since it is already completed.
    //
    return SRB_STATUS_PENDING;

}



UCHAR
iSpBuildTargetMapping(
    IN PISCSI_ADAPTER_EXTENSION AdapterExtension,
    IN OUT PULONG InstanceLengthArray,
    IN ULONG BufferAvail,
    _Out_writes_bytes_(BufferAvail) OUT PUCHAR Buffer,
    OUT PULONG SizeNeeded
    )
{
    PISCSI_SESSION iScsiSession;
    PMSiSCSI_TargetMappings iSCSITargetMappings;
    PISCSI_TargetMapping    targetMapping;
    PISCSI_SESSION *sessionTable;
    NTSTATUS ntStatus;
    ULONG inx, jnx;
    ULONG targetCount;
    UCHAR status = SRB_STATUS_SUCCESS;

    *SizeNeeded = sizeof(MSiSCSI_TargetMappings);

    if (sessionTable != NULL) {

        NT_ASSERT(targetCount > 0);

        inx = 0;
        while (inx < targetCount) {

            iScsiSession = sessionTable[inx];
            if (iScsiSession != NULL) {

                ULONG lunCount = 0;

                jnx = 0;
                while (jnx < ISCSI_MAX_LOGICAL_UNITS) {

                    if (iScsiSession->TargetLUN[jnx] != ISCSI_INVALID_LUN) {
                        lunCount++;
                    }

                    jnx++;
                }

                *SizeNeeded += FIELD_OFFSET(ISCSI_TargetMapping, LUNList) +
                               (sizeof(ISCSI_LUNList) * lunCount);
            }

            inx++;
        }

        if (BufferAvail >= *SizeNeeded) {

            iSCSITargetMappings = (PMSiSCSI_TargetMappings) Buffer;

            RtlZeroMemory(iSCSITargetMappings, *SizeNeeded);

            iSCSITargetMappings->UniqueAdapterId = (ULONGLONG) AdapterExtension;

            iSCSITargetMappings->TargetMappingCount = targetCount;

            targetMapping = iSCSITargetMappings->TargetMappings;

            inx = 0;
            while (inx < targetCount) {

                iScsiSession = sessionTable[inx];
                if (iScsiSession != NULL) {

                    ULONG targetMappingSize;

                    targetMappingSize =
                        FIELD_OFFSET(ISCSI_TargetMapping, LUNList);

                    //
                    // Size needed is computed above and it is checked above
                    // that the buffer size provided to the function is greater
                    // than the size needed to fill the buffer
                    //
#pragma prefast ( suppress: __WARNING_POTENTIAL_BUFFER_OVERFLOW_HIGH_PRIORITY, "BufferAvail is being checked against SizeNeeded above")
                    targetMapping->OSBus = 0;

                    targetMapping->OSTarget = iScsiSession->OSTargetId;

                    targetMapping->UniqueSessionId = iScsiSession->SessionId;

                    targetMapping->FromPersistentLogin  = iScsiSession->PersistentLogin;

                    targetMapping->LUNCount = 0;

                    jnx = 0;
                    while (jnx < ISCSI_MAX_LOGICAL_UNITS) {

                        if (iScsiSession->TargetLUN[jnx] != ISCSI_INVALID_LUN) {

                            targetMapping->LUNList[targetMapping->LUNCount].OSLUN =
                                jnx;

                            targetMapping->LUNList[targetMapping->LUNCount].TargetLUN =
                                iScsiSession->TargetLUN[jnx];

                            targetMapping->LUNCount++;

                            targetMappingSize += sizeof(ISCSI_LUNList);
                        }

                        jnx++;
                    }

                    *(targetMapping->TargetName) = MAX_TARGET_NAME * sizeof(WCHAR);
                    RtlCopyMemory((targetMapping->TargetName + 1),
                                  iScsiSession->TargetName,
                                  (MAX_TARGET_NAME * sizeof(WCHAR)));

                    targetMapping =
                        (PISCSI_TargetMapping) (((PUCHAR)targetMapping) +
                                                  targetMappingSize);
                }

                inx++;
            }

            status = SRB_STATUS_SUCCESS;

        } else {
            status = SRB_STATUS_DATA_OVERRUN;
        }

    } else {
        if (BufferAvail >= *SizeNeeded) {
            RtlZeroMemory(Buffer, BufferAvail);
            status = SRB_STATUS_SUCCESS;
        } else {
            status = SRB_STATUS_DATA_OVERRUN;
        }
    }

    *InstanceLengthArray = *SizeNeeded;

    return status;
}


UCHAR
iSpBuildPortalInfo(
    IN PISCSI_ADAPTER_EXTENSION AdapterExtension,
    IN OUT PULONG InstanceLengthArray,
    IN ULONG BufferAvail,
    OUT PUCHAR Buffer,
    OUT PULONG SizeNeeded
    )
{
    PMSiSCSI_PortalInfoClass portalInfoClass;
    PISCSI_PortalInfo portalInfo;
    ULONG ntStatus;
    ULONG inx;
    UCHAR status = SRB_STATUS_SUCCESS;


    *SizeNeeded = FIELD_OFFSET(MSiSCSI_PortalInfoClass, PortalInformation);

    *InstanceLengthArray = *SizeNeeded;


        *SizeNeeded += sizeof(ISCSI_PortalInfo) * gNumberOfIPAddresses;

        if (BufferAvail >= *SizeNeeded) {

            portalInfoClass = (PMSiSCSI_PortalInfoClass) Buffer;

            RtlZeroMemory(portalInfoClass, *SizeNeeded);

            //
            // Number of entries in PortalInformation array
            //
            portalInfoClass->PortalInfoCount = gNumberOfIPAddresses;

            portalInfo = portalInfoClass->PortalInformation;

            inx = 0;
            while (inx < gNumberOfIPAddresses) {

                portalInfo->Index = inx;
                portalInfo->Port = inx;
                portalInfo->PortalTag = 0;
                portalInfo->Protocol = TCP;
                portalInfo->PortalType = InitiatorPortals;

                iSpCopyIPAddress(&portalInfo->IPAddr,
                                 &(gRegisteredTransports[inx]),
                                 NULL);

                *portalInfo->IPAddr.TextAddress = 256 * sizeof(WCHAR);

                portalInfo++;

                inx++;
            }

            *InstanceLengthArray = *SizeNeeded;

            status = SRB_STATUS_SUCCESS;
        } else {
            status = SRB_STATUS_DATA_OVERRUN;
        }

    } else {
        status = SRB_STATUS_ERROR;
        *InstanceLengthArray = 0;
    }

    return status;
}


NTSTATUS
iSpBuildHBAInformationBuffer(
    IN PISCSI_ADAPTER_EXTENSION AdapterExtension,
    OUT PMSiSCSI_HBAInformation IScsiHBAInformation
    )
{
    NTSTATUS status;


    //
    // Boolean fields in MSiSCSI_HBAInformation by default
    // are set to FALSE through RtlZeroMemory
    //
    RtlZeroMemory(IScsiHBAInformation, sizeof(MSiSCSI_HBAInformation));

    //
    // Unique Id we return is a pointer to
    // the miniport's adapater extension
    //
    IScsiHBAInformation->UniqueAdapterId = (ULONGLONG) AdapterExtension;

    //
    // We want WMI to send us IP Address in binary form.
    //
    IScsiHBAInformation->RequiresBinaryIpAddresses = TRUE;

    //
    // We use TCP/IP integrated with Windows
    //
    IScsiHBAInformation->IntegratedTCPIP = TRUE;

    IScsiHBAInformation->MultifunctionDevice = FALSE;

    IScsiHBAInformation->CacheValid = FALSE;

    //
    // iSCSI Version supported
    //
    IScsiHBAInformation->VersionMin = ISCSI_CLI_VERSION_MIN;
    IScsiHBAInformation->VersionMax = ISCSI_CLI_VERSION_MAX;

    IScsiHBAInformation->NumberOfPorts = 1;

    IScsiHBAInformation->MaxCDBLength = 16;

    IScsiHBAInformation->Status = 0;

    IScsiHBAInformation->FunctionalitySupported =
                             (ISCSI_HBA_PRESHARED_KEY_CACHE         |
                              ISCSI_HBA_ISCSI_AUTHENTICATION_CACHE  |
                              ISCSI_HBA_IPSEC_TUNNEL_MODE);

    *IScsiHBAInformation->VendorID = 255*sizeof(WCHAR);

    status = RtlStringCchCopyNW((IScsiHBAInformation->VendorID+1), 255,
                                ISCSI_VENDOR_ID, wcslen(ISCSI_VENDOR_ID));

    NT_ASSERT(NT_SUCCESS(status));

    if(NT_SUCCESS(status) == FALSE){
        return status;
    }

    *IScsiHBAInformation->VendorModel = 255*sizeof(WCHAR);

    status = RtlStringCchCopyNW((IScsiHBAInformation->VendorModel+1), 255,
                                ISCSI_VENDOR_MODEL,
                                wcslen(ISCSI_VENDOR_MODEL));
    
    NT_ASSERT(NT_SUCCESS(status));


    if(NT_SUCCESS(status) == FALSE){
        return status;
    }

    *IScsiHBAInformation->VendorVersion = 255*sizeof(WCHAR);

    status = RtlStringCchCopyNW((IScsiHBAInformation->VendorVersion+1), 255,
                                ISCSI_VENDOR_VERSION,
                                wcslen(ISCSI_VENDOR_VERSION));

    NT_ASSERT(NT_SUCCESS(status));

    if(NT_SUCCESS(status) == FALSE){
        return status;
    }

    *IScsiHBAInformation->FirmwareVersion = 255*sizeof(WCHAR);

    status = RtlStringCchCopyNW((IScsiHBAInformation->FirmwareVersion+1), 255,
                                ISCSI_VENDOR_VERSION,
                                wcslen(ISCSI_VENDOR_VERSION));

    NT_ASSERT(NT_SUCCESS(status));

    if(NT_SUCCESS(status) == FALSE){
        return status;
    }

    *IScsiHBAInformation->AsicVersion = 255*sizeof(WCHAR);

    status = RtlStringCchCopyNW((IScsiHBAInformation->AsicVersion+1), 255,
                                ISCSI_VENDOR_VERSION,
                                wcslen(ISCSI_VENDOR_VERSION));

    
    NT_ASSERT(NT_SUCCESS(status));


    if(NT_SUCCESS(status) == FALSE){
        return status;
    }

    *IScsiHBAInformation->OptionRomVersion = 255*sizeof(WCHAR);

    *IScsiHBAInformation->SerialNumber = 255*sizeof(WCHAR);

    status = RtlStringCchCopyNW((IScsiHBAInformation->SerialNumber+1), 255,
                                ISCSI_SERIAL_NUMBER,
                                wcslen(ISCSI_SERIAL_NUMBER));

    NT_ASSERT(NT_SUCCESS(status));

    if(NT_SUCCESS(status) == FALSE){
        return status;
    }

    *IScsiHBAInformation->DriverName = 255*sizeof(WCHAR);

    status = RtlStringCchCopyNW((IScsiHBAInformation->DriverName+1), 255,
                                ISCSI_INITIATOR_DRIVER_NAME,
                                wcslen(ISCSI_INITIATOR_DRIVER_NAME));


    NT_ASSERT(NT_SUCCESS(status));
    
    return status;
}

UCHAR
iSpBuildInitiatorSessionInfo(
    IN PISCSI_ADAPTER_EXTENSION AdapterExtension,
    IN PSCSIWMI_REQUEST_CONTEXT DispatchContext,
    IN ULONG BufferAvail,
    OUT PUCHAR Buffer,
    OUT PULONG SizeNeeded
    )
{
    PWMI_CONNECTION_LIST connectionList;
    ULONG size, sessionInx;
    ULONG numberOfSessions, numberOfConnections;
    UCHAR status;


    connectionList = iSpGetConnectionList(AdapterExtension,
                                          &numberOfSessions,
                                          &numberOfConnections);

    DebugPrint((iScsiPrtDebugInfo,
        "Number of Sessions %d, Number of Connections %d\n",
        numberOfSessions, numberOfConnections));

    ETWDebugPrint(iScsiLevelInfo,
        iScsiFlagWmi,
        "Number of Sessions %d, Number of Connections %d\n",
        numberOfSessions, numberOfConnections);

    //
    // determine the size of buffer needed to build all of the session
    // and connection data structures
    //

    //
    // Account for the fixed part of the iScsiInitiatorSessionInfo
    //
    size = FIELD_OFFSET(MSiSCSI_InitiatorSessionInfo,
                        SessionsList);

    if (connectionList == NULL) {
        if (BufferAvail >= size)
        {
            PMSiSCSI_InitiatorSessionInfo sessionInfo;

            sessionInfo = (PMSiSCSI_InitiatorSessionInfo)Buffer;
            sessionInfo->UniqueAdapterId = (ULONGLONG) AdapterExtension;
            sessionInfo->SessionCount = 0;
            *SizeNeeded = size;
            return(SRB_STATUS_SUCCESS);
        } else {
            *SizeNeeded = size;
            return(SRB_STATUS_DATA_OVERRUN);
        }
    }

    //
    // Loop over all sessions and account for the space needed for each
    // session plus all of the connections within each session
    //
    sessionInx = 0;
    while (sessionInx < numberOfSessions) {

        //
        // Since the session structure needs to be 8 bytes aligned, we
        // pad out to 8 bytes
        //
        size = (size + 7) & ~7;

        //
        // Add the fixed size needed for the session structure
        //
        size += FIELD_OFFSET(ISCSI_SessionStaticInfo,
                             ConnectionsList);

        //
        // Account for the size of all connection structures, being
        // sure that the connection structure is padded out to 8 bytes
        // in order to maintain 8 byte alignment
        //
        size += connectionList[sessionInx].Count *
                ( (sizeof(ISCSI_ConnectionStaticInfo)+7) & ~7);

        sessionInx++;
    }

    *SizeNeeded = size;
    if (size <= BufferAvail)
    {
        //
        // If we do have enough space to build the session info data
        // structures then we do so
        //
        RtlZeroMemory(Buffer, size);

        status = iSpReadInitiatorSessionInfo(
                            AdapterExtension,
                            connectionList,
                            numberOfSessions,
                            Buffer);

    } else {
        //
        // If there is not enough space to build the session info data
        // structures then return this error
        //
        status = SRB_STATUS_DATA_OVERRUN;
    }

    iSpReleaseConnectionReferences(connectionList,
                                   numberOfSessions);


    return status;
}

UCHAR
iSpBuildConnectionStatistics(
    IN PISCSI_ADAPTER_EXTENSION AdapterExtension,
    IN PSCSIWMI_REQUEST_CONTEXT DispatchContext,
    IN ULONG GuidIndex,
    IN ULONG InstanceIndex,
    IN ULONG InstanceCount,
    IN OUT PULONG InstanceLengthArray,
    IN ULONG BufferAvail,
    OUT PUCHAR Buffer,
    OUT PULONG SizeNeeded
    )
{
    PWMI_CONNECTION_LIST connectionList;
    PISCSI_CONNECTION iScsiConnection;
    PWCHAR NameOffset;
    PUCHAR currentDataPos;
    ULONG instanceInx;
    ULONG newOutBufferAvil;
    WMIString DynamicInstanceName;
    ULONG numberOfSessions;
    ULONG numberOfConnections;
    UCHAR srbStatus;


    srbStatus = SRB_STATUS_SUCCESS;

    *SizeNeeded = 0;

    connectionList = iSpGetConnectionList(AdapterExtension,
                                          &numberOfSessions,
                                          &numberOfConnections);

    if (connectionList == NULL) {
        DebugPrint((iScsiPrtDebugError,
            "Failed to allocate array for connections\n"));

        ETWDebugPrint(iScsiLevelError,
            iScsiFlagWmi,
            "Failed to allocate array for connections\n");

        return SRB_STATUS_ERROR;
    }

    DebugPrint((iScsiPrtDebugInfo,
        "Number of Sessions %d, Number of Connections %d\n",
        numberOfSessions,
        numberOfConnections));

    ETWDebugPrint(iScsiLevelInfo, iScsiFlagWmi,
        "Number of Sessions %d, Number of Connections %d\n",
        numberOfSessions,
        numberOfConnections);

    if (DispatchContext->MinorFunction == IRP_MN_QUERY_ALL_DATA) {

        ULONG sessionInx;
        ULONG connectionInx;
        ULONG connectionCount;
        BOOLEAN dynamicNameStatus;

        dynamicNameStatus = ScsiPortWmiSetInstanceCount(DispatchContext,
                                                        numberOfConnections,
                                                        &newOutBufferAvil,
                                                        SizeNeeded);
        if (!dynamicNameStatus)
        {
            DebugPrint((iScsiPrtDebugError,
                "iscsiprt: wnode is not for a dynamic instance named GUID\n"));

            ETWDebugPrint(iScsiLevelInfo,
                iScsiFlagWmi,
                "iscsiprt: wnode is not for a dynamic instance named GUID\n");

            *SizeNeeded = 0;

            srbStatus = SRB_STATUS_ERROR;

        } else {

            if (newOutBufferAvil == 0) {

                //
                // The buffer passed to return the data is too small
                //
                srbStatus = SRB_STATUS_DATA_OVERRUN;
            }

            //
            // For the data itself
            //
            instanceInx = 0;

            sessionInx = 0;
            *SizeNeeded = 0;
            while (sessionInx < numberOfSessions) {

                connectionCount = connectionList[sessionInx].Count;

                connectionInx = 0;
                while (connectionInx < connectionCount) {

                    iScsiConnection = connectionList[sessionInx].ISCSIConnection[connectionInx];

                    if (iScsiConnection != NULL) {

                        currentDataPos = (PUCHAR) ScsiPortWmiSetData(DispatchContext,
                                                                     instanceInx,
                                                                     sizeof(MSiSCSI_ConnectionStatistics),
                                                                     &newOutBufferAvil,
                                                                     SizeNeeded);
                        if (newOutBufferAvil == 0)
                        {
                            //
                            // The buffer passed to return the data is too small
                            //
                            srbStatus = SRB_STATUS_DATA_OVERRUN;
                        }


                        if ((srbStatus != SRB_STATUS_DATA_OVERRUN) &&
                            (currentDataPos != NULL)) {

                            srbStatus = iSpReadConnStatistics(AdapterExtension,
                                                              iScsiConnection,
                                                              currentDataPos);

                            if (srbStatus == SRB_STATUS_ERROR)
                            {
                                break;
                            }
                        }

                        RtlZeroMemory(&DynamicInstanceName,
                                      sizeof(DynamicInstanceName));

                        srbStatus = iSpGetDynamicConnectionInstanceName(
                                             iScsiConnection,
                                             &DynamicInstanceName);

                        if (srbStatus == SRB_STATUS_ERROR)
                        {
                            break;
                        }

                        NameOffset = ScsiPortWmiSetInstanceName(
                                             DispatchContext,
                                             instanceInx,
                                             DynamicInstanceName.Length+sizeof(USHORT),
                                             &newOutBufferAvil,
                                             SizeNeeded);

                        if ((newOutBufferAvil == 0) && (NameOffset == NULL))
                        {
                            //
                            // The buffer passed to return the data is too small
                            //
                            srbStatus = SRB_STATUS_DATA_OVERRUN;
                        }

                        if (srbStatus != SRB_STATUS_DATA_OVERRUN)
                        {
                            //
                            // copy the instance name into NameOffset
                            //
                            RtlCopyMemory(NameOffset, (PUCHAR)(&DynamicInstanceName),
                                          (DynamicInstanceName.Length + sizeof(USHORT)));
                        }

                        instanceInx++;
                    }

                    connectionInx++;
                }

                if (srbStatus == SRB_STATUS_ERROR)
                {
                    break;
                }

                sessionInx++;
            }
        }
    } else {

        PWMIString instanceName;
        ULONG givenInstanceNameLength = 0;
        ULONG dynInstanceNameLength = 0;
        ULONG instanceNameLength;
        ULONG sessionInx;
        ULONG connectionInx;
        ULONG connectionCount;
        BOOLEAN found = FALSE;

        //
        // single instance
        //

        srbStatus = SRB_STATUS_ERROR;

        //
        // get the instance name
        //
        instanceName = (PWMIString)ScsiPortWmiGetInstanceName(DispatchContext);

        if (instanceName != NULL)
        {
            givenInstanceNameLength = instanceName->Length / sizeof(instanceName->Buffer[0]);

            *SizeNeeded = sizeof(MSiSCSI_ConnectionStatistics);
            if (BufferAvail < *SizeNeeded)
            {
                //
                // The buffer passed to return the data is too small
                //
                srbStatus = SRB_STATUS_DATA_OVERRUN;

            } else {

                instanceInx = 0;

                sessionInx = 0;

                while (sessionInx < numberOfSessions) {

                    if (connectionList[sessionInx].Count) {

                        connectionInx = 0;

                        while (connectionInx < numberOfConnections) {
                            iScsiConnection = connectionList[sessionInx].ISCSIConnection[0];

                            if (iScsiConnection != NULL) {

                                RtlZeroMemory(&DynamicInstanceName,
                                              sizeof(DynamicInstanceName));

                                srbStatus = iSpGetDynamicConnectionInstanceName(
                                                    iScsiConnection,
                                                    &DynamicInstanceName);

                                if (srbStatus != SRB_STATUS_ERROR)
                                {
                                    dynInstanceNameLength = DynamicInstanceName.Length / sizeof(DynamicInstanceName.Buffer[0]);

                                    if ((givenInstanceNameLength == dynInstanceNameLength) &&
                                        !wcsncmp(
                                            DynamicInstanceName.Buffer,
                                            instanceName->Buffer,
                                            dynInstanceNameLength)) {

                                        srbStatus = iSpReadConnStatistics(AdapterExtension,
                                                                          iScsiConnection,
                                                                          Buffer);

                                        if (srbStatus == SRB_STATUS_SUCCESS)
                                        {
                                            *InstanceLengthArray = *SizeNeeded;
                                        }

                                        found = TRUE;

                                        break;
                                    }
                                }
                            }

                            connectionInx++;
                        }
                    }

                    if (found == TRUE) {
                        break;
                    }

                    sessionInx++;
                }
            }
        } else {
            srbStatus = SRB_STATUS_ERROR;
        }

        if(srbStatus != SRB_STATUS_SUCCESS &&
           srbStatus != SRB_STATUS_DATA_OVERRUN)
        {
            *SizeNeeded = 0;
        }
    }

    iSpReleaseConnectionReferences(connectionList,
                                   numberOfSessions);


    return srbStatus;
}

UCHAR
iSpBuildSessionStatistics(
    IN PISCSI_ADAPTER_EXTENSION AdapterExtension,
    IN PSCSIWMI_REQUEST_CONTEXT DispatchContext,
    IN ULONG GuidIndex,
    IN ULONG InstanceIndex,
    IN ULONG InstanceCount,
    IN OUT PULONG InstanceLengthArray,
    IN ULONG BufferAvail,
    OUT PUCHAR Buffer,
    OUT PULONG SizeNeeded
    )
{
    PWCHAR NameOffset;
    PUCHAR currentDataPos;
    WMIString DynamicInstanceName;
    ULONG connectionCount=0;
    ULONG instanceInx;
    ULONG newOutBufferAvil;
    ULONG initiatorIndx;
    ULONG instanceSize=0;
    WMIString WMIformatString;
    UCHAR srbStatus;

    PWMI_CONNECTION_LIST connectionList;
    ULONG numberOfSessions;
    ULONG numberOfConnections;


    srbStatus = SRB_STATUS_SUCCESS;
    *SizeNeeded = 0;

    connectionList = iSpGetConnectionList(AdapterExtension,
                                          &numberOfSessions,
                                          &numberOfConnections);

    if (connectionList == NULL) {
        DebugPrint((iScsiPrtDebugError,
            "Failed to allocate array for connections\n"));

        ETWDebugPrint(iScsiLevelError, iScsiFlagWmi,
            "Failed to allocate array for connections\n");

        return SRB_STATUS_ERROR;
    }

    DebugPrint((iScsiPrtDebugInfo,
        "Number of Sessions %d, Number of Connections %d\n",
        numberOfSessions,
        numberOfConnections));

    ETWDebugPrint(iScsiLevelInfo,
        iScsiFlagWmi,
        "Number of Sessions %d, Number of Connections %d\n",
        numberOfSessions,
        numberOfConnections);

    if (DispatchContext->MinorFunction == IRP_MN_QUERY_ALL_DATA)
    {

        ULONG sessionInx;
        BOOLEAN dynamicNameStatus;

        //
        // instance counts
        //
        dynamicNameStatus = ScsiPortWmiSetInstanceCount(DispatchContext,
                                                        numberOfSessions,
                                                        &newOutBufferAvil,
                                                        SizeNeeded);

        if (!dynamicNameStatus)
        {
            DebugPrint((iScsiPrtDebugError,
                "iscsiprt: wnode is not for a dynamic instance named GUID\n"));

            ETWDebugPrint(iScsiLevelError, iScsiFlagWmi,
                "iscsiprt: wnode is not for a dynamic instance named GUID\n");

            *SizeNeeded = 0;
            srbStatus = SRB_STATUS_ERROR;

        } else {

            if (numberOfSessions == 0)
            {
                //
                // no instances avaliable
                //
            } else {

                if (newOutBufferAvil == 0)
                {
                    //
                    // The buffer passed to return the data is too small
                    //
                    srbStatus = SRB_STATUS_DATA_OVERRUN;
                }

                //
                // for the data itself
                //
                instanceInx = 0;

                sessionInx = 0;
                
                *SizeNeeded = 0;

                while (sessionInx < numberOfSessions) {

                    PISCSI_SESSION iScsiSession;
                    PISCSI_CONNECTION iScsiConnection;

                    if (connectionList[sessionInx].Count > 0) {

                        iScsiConnection = connectionList[sessionInx].ISCSIConnection[0];
                        iScsiSession = iScsiConnection->ISCSISession;

                        currentDataPos = (PUCHAR) ScsiPortWmiSetData(DispatchContext,
                                                                     instanceInx,
                                                                     sizeof(MSiSCSI_SessionStatistics),
                                                                     &newOutBufferAvil,
                                                                     SizeNeeded);
                        if (newOutBufferAvil == 0)
                        {
                            //
                            // The buffer passed to return the data is too small
                            //
                            srbStatus = SRB_STATUS_DATA_OVERRUN;
                        }

                        if ((srbStatus != SRB_STATUS_DATA_OVERRUN) &&
                            (currentDataPos != NULL))
                        {

                            srbStatus = iSpReadSessionStatistics(AdapterExtension,
                                                                 iScsiSession,
                                                                 &connectionList[sessionInx],
                                                                 currentDataPos);

                        }

                        if (srbStatus == SRB_STATUS_ERROR)
                        {
                            break;
                        }

                        RtlZeroMemory(&DynamicInstanceName,
                                      sizeof(DynamicInstanceName));

                        srbStatus = iSpGetDynamicSessionInstanceName(iScsiSession,
                                                                     &DynamicInstanceName);

                        NameOffset = ScsiPortWmiSetInstanceName(DispatchContext,
                                                                instanceInx,
                                                                DynamicInstanceName.Length+sizeof(USHORT),
                                                                &newOutBufferAvil,
                                                                SizeNeeded);

                        if ((newOutBufferAvil == 0) && (NameOffset == NULL))
                        {
                            //
                            // The buffer passed to return the data is too small
                            //
                            srbStatus = SRB_STATUS_DATA_OVERRUN;
                        }

                        if (srbStatus != SRB_STATUS_DATA_OVERRUN) {

                            //
                            // copy the instance name into NameOffset
                            //
                            RtlCopyMemory(NameOffset,
                                          (PUCHAR)(&DynamicInstanceName),
                                          (DynamicInstanceName.Length + sizeof(USHORT)));
                        }

                        instanceInx++;
                    }

                    sessionInx++;
                }


            }
        }
    } else { //single instance

        PWMIString instanceName;
        ULONG givenInstanceNameLength = 0;
        ULONG dynInstanceNameLength = 0;
        ULONG instanceNameLength;
        ULONG sessionInx;
        BOOLEAN found = FALSE;

        srbStatus = SRB_STATUS_ERROR;

        //
        // get the instance name
        //
        instanceName = (PWMIString) ScsiPortWmiGetInstanceName(DispatchContext);

        if (instanceName != NULL)
        {
            givenInstanceNameLength = instanceName->Length / sizeof(instanceName->Buffer[0]);

            *SizeNeeded = sizeof(MSiSCSI_SessionStatistics);
            if (BufferAvail < *SizeNeeded)
            {
                //
                // The buffer passed to return the data is too small
                //
                srbStatus = SRB_STATUS_DATA_OVERRUN;
            } else {

                sessionInx = 0;
                while (sessionInx < numberOfSessions) {

                    PISCSI_SESSION iScsiSession;
                    PISCSI_CONNECTION iScsiConnection;

                    if (connectionList[sessionInx].Count > 0) {

                        iScsiConnection = connectionList[sessionInx].ISCSIConnection[0];
                        iScsiSession = iScsiConnection->ISCSISession;

                        RtlZeroMemory(&DynamicInstanceName, sizeof(DynamicInstanceName));
                        srbStatus = iSpGetDynamicSessionInstanceName(iScsiSession,
                                                                     &DynamicInstanceName);
                        if (srbStatus == SRB_STATUS_SUCCESS)
                        {
                            dynInstanceNameLength = DynamicInstanceName.Length / sizeof(DynamicInstanceName.Buffer[0]);

                            if (givenInstanceNameLength == dynInstanceNameLength
                                && !wcsncmp(DynamicInstanceName.Buffer,
                                          instanceName->Buffer,
                                          dynInstanceNameLength))
                            {
                                srbStatus = iSpReadSessionStatistics(AdapterExtension,
                                                                     iScsiSession,
                                                                     &connectionList[sessionInx],
                                                                     Buffer);

                                if (srbStatus == SRB_STATUS_SUCCESS)
                                {
                                    *InstanceLengthArray = *SizeNeeded;
                                }

                                break;
                            }
                        }
                    }

                    sessionInx++;
                }
            }
        } else {
            srbStatus = SRB_STATUS_ERROR;
        }

        if(srbStatus != SRB_STATUS_SUCCESS &&
           srbStatus != SRB_STATUS_DATA_OVERRUN)
        {
            *SizeNeeded = 0;
        }
    }


    return srbStatus;
}

UCHAR
iSpBuildRequestTimeStatistics(
    IN PISCSI_ADAPTER_EXTENSION AdapterExtension,
    IN PSCSIWMI_REQUEST_CONTEXT DispatchContext,
    IN ULONG GuidIndex,
    IN ULONG InstanceIndex,
    IN ULONG InstanceCount,
    IN OUT PULONG InstanceLengthArray,
    IN ULONG BufferAvail,
    OUT PUCHAR Buffer,
    OUT PULONG SizeNeeded
    )
{
    PWMI_CONNECTION_LIST connectionList;
    PISCSI_CONNECTION iScsiConnection;
    PWCHAR NameOffset;
    PUCHAR currentDataPos;
    ULONG instanceInx;
    ULONG newOutBufferAvil;
    WMIString DynamicInstanceName;
    ULONG numberOfSessions;
    ULONG numberOfConnections;
    UCHAR srbStatus;


    srbStatus = SRB_STATUS_SUCCESS;

    *SizeNeeded = 0;

    connectionList = iSpGetConnectionList(AdapterExtension,
                                          &numberOfSessions,
                                          &numberOfConnections);
    if (connectionList == NULL) {

        DebugPrint((iScsiPrtDebugError,
            "Failed to allocate array for connections\n"));

        ETWDebugPrint(iScsiLevelError, iScsiFlagWmi,
            "Failed to allocate array for connections\n");

        return SRB_STATUS_ERROR;
    }

    DebugPrint((iScsiPrtDebugInfo,
        "Number of Sessions %d, Number of Connections %d\n",
        numberOfSessions,
        numberOfConnections));

    ETWDebugPrint(iScsiLevelInfo,
        iScsiFlagWmi,
        "Number of Sessions %d, Number of Connections %d\n",
        numberOfSessions,
        numberOfConnections);

    if (DispatchContext->MinorFunction == IRP_MN_QUERY_ALL_DATA) {

        ULONG sessionInx;
        ULONG connectionInx;
        ULONG connectionCount;
        BOOLEAN dynamicNameStatus;

        dynamicNameStatus = ScsiPortWmiSetInstanceCount(DispatchContext,
                                                        numberOfConnections,
                                                        &newOutBufferAvil,
                                                        SizeNeeded);
        if (!dynamicNameStatus)
        {
            DebugPrint((iScsiPrtDebugError,
                "iscsiprt: wnode is not for a dynamic instance named GUID\n"));

            ETWDebugPrint(iScsiLevelError,
                iScsiFlagWmi,
                "iscsiprt: wnode is not for a dynamic instance named GUID\n");

            *SizeNeeded = 0;

            srbStatus = SRB_STATUS_ERROR;

        } else {

            if (newOutBufferAvil == 0) {

                //
                // The buffer passed to return the data is too small
                //
                srbStatus = SRB_STATUS_DATA_OVERRUN;
            }

            //
            // For the data itself
            //
            instanceInx = 0;

            sessionInx = 0;

            *SizeNeeded = 0;

            while (sessionInx < numberOfSessions) {

                connectionCount = connectionList[sessionInx].Count;

                connectionInx = 0;
                while (connectionInx < connectionCount) {

                    iScsiConnection = connectionList[sessionInx].ISCSIConnection[connectionInx];

                    if (iScsiConnection != NULL) {

                        currentDataPos = (PUCHAR) ScsiPortWmiSetData(DispatchContext,
                                                                     instanceInx,
                                                                     sizeof(MSiSCSI_RequestTimeStatistics),
                                                                     &newOutBufferAvil,
                                                                     SizeNeeded);
                        if (newOutBufferAvil == 0)
                        {
                            //
                            // The buffer passed to return the data is too small
                            //
                            srbStatus = SRB_STATUS_DATA_OVERRUN;
                        }


                        if ((srbStatus != SRB_STATUS_DATA_OVERRUN) &&
                            (currentDataPos != NULL)) {

                            srbStatus = iSpReadRequestTimeStatistics(AdapterExtension,
                                                                     iScsiConnection,
                                                                     currentDataPos);

                            if (srbStatus == SRB_STATUS_ERROR)
                            {
                                break;
                            }
                        }

                        RtlZeroMemory(&DynamicInstanceName,
                                      sizeof(DynamicInstanceName));

                        srbStatus = iSpGetDynamicConnectionInstanceName(
                                             iScsiConnection,
                                             &DynamicInstanceName);

                        if (srbStatus == SRB_STATUS_ERROR)
                        {
                            break;
                        }

                        NameOffset = ScsiPortWmiSetInstanceName(
                                             DispatchContext,
                                             instanceInx,
                                             DynamicInstanceName.Length+sizeof(USHORT),
                                             &newOutBufferAvil,
                                             SizeNeeded);

                        if ((newOutBufferAvil == 0) && (NameOffset == NULL))
                        {
                            //
                            // The buffer passed to return the data is too small
                            //
                            srbStatus = SRB_STATUS_DATA_OVERRUN;
                        }

                        if (srbStatus != SRB_STATUS_DATA_OVERRUN)
                        {
                            //
                            // copy the instance anme into NameOffset
                            //
                            RtlCopyMemory(NameOffset, (PUCHAR)(&DynamicInstanceName),
                                          (DynamicInstanceName.Length + sizeof(USHORT)));
                        }

                        instanceInx++;
                    }

                    connectionInx++;
                }

                if (srbStatus == SRB_STATUS_ERROR)
                {
                    break;
                }

                sessionInx++;
            }
        }
    } else {
        PWMIString instanceName;
        ULONG givenInstanceNameLength = 0;
        ULONG dynInstanceNameLength = 0;
        ULONG instanceNameLength;
        ULONG sessionInx;
        ULONG connectionInx;
        ULONG connectionCount;
        BOOLEAN found = FALSE;

        //
        // single instance
        //

        srbStatus = SRB_STATUS_ERROR;

        //
        // get the instance name
        //
        instanceName = (PWMIString)ScsiPortWmiGetInstanceName(DispatchContext);

        if (instanceName != NULL)
        {
            givenInstanceNameLength = instanceName->Length / sizeof(instanceName->Buffer[0]);

            *SizeNeeded = sizeof(MSiSCSI_RequestTimeStatistics);
            if (BufferAvail < *SizeNeeded)
            {
                //
                // The buffer passed to return the data is too small
                //
                srbStatus = SRB_STATUS_DATA_OVERRUN;

            } else {

                instanceInx = 0;

                sessionInx = 0;

                while (sessionInx < numberOfSessions) {

                    if (connectionList[sessionInx].Count) {

                        connectionInx = 0;

                        while (connectionInx < numberOfConnections) {
                            iScsiConnection = connectionList[sessionInx].ISCSIConnection[0];

                            if (iScsiConnection != NULL) {

                                srbStatus = iSpGetDynamicConnectionInstanceName(
                                                    iScsiConnection,
                                                    &DynamicInstanceName);

                                if (srbStatus != SRB_STATUS_ERROR)
                                {
                                    dynInstanceNameLength = DynamicInstanceName.Length / sizeof(DynamicInstanceName.Buffer[0]);

                                    if ((givenInstanceNameLength == dynInstanceNameLength) &&
                                        !wcsncmp(DynamicInstanceName.Buffer,
                                            instanceName->Buffer,
                                            dynInstanceNameLength))
                                    {
                                        srbStatus = iSpReadRequestTimeStatistics(AdapterExtension,
                                                                                 iScsiConnection,
                                                                                 Buffer);
                                        if (srbStatus == SRB_STATUS_SUCCESS)
                                        {
                                            *InstanceLengthArray = *SizeNeeded;
                                        }

                                        found = TRUE;

                                        break;
                                    }
                                }
                            }
                            connectionInx++;
                        }
                    }

                    if (found == TRUE) {
                        break;
                    }

                    sessionInx++;
                }
            }
        } else {
            srbStatus = SRB_STATUS_ERROR;
        }

        if(srbStatus != SRB_STATUS_SUCCESS &&
           srbStatus != SRB_STATUS_DATA_OVERRUN)
        {
            *SizeNeeded = 0;
        }
    }

    iSpReleaseConnectionReferences(connectionList,
                                   numberOfSessions);


    return srbStatus;
}


UCHAR iScsiQuerySecurityCapabilities(
    PISCSI_ADAPTER_EXTENSION AdapterExtension,
    ULONG BufferAvail,
    PUCHAR Buffer,
    PULONG InstanceLength,
    PULONG SizeNeeded
    )
/*+++

Routine Description :

    This routine implements the WMI query for the security capabilites
    class.

--*/
{
    UCHAR status;
    PMSiSCSI_SecurityCapabilities config;


    *SizeNeeded = sizeof(MSiSCSI_SecurityCapabilities);

    if (BufferAvail >= sizeof(MSiSCSI_SecurityCapabilities))
    {
        config = (PMSiSCSI_SecurityCapabilities)Buffer;
        RtlZeroMemory(Buffer, sizeof(MSiSCSI_SecurityCapabilities));

        config->ProtectiScsiTraffic = FALSE;
        config->ProtectiSNSTraffic = FALSE;
        config->CertificatesSupported = FALSE;

        config->EncryptionAvailableCount = 1;
        config->EncryptionAvailable[0] = ISCSI_ENCRYPT_NONE;

        *InstanceLength = *SizeNeeded;
        status = SRB_STATUS_SUCCESS;
    } else {
        status = SRB_STATUS_DATA_OVERRUN;
    }

    return(status);
}

UCHAR iScsiQueryDiscoveryConfig(
    PISCSI_ADAPTER_EXTENSION AdapterExtension,
    ULONG BufferAvail,
    PUCHAR Buffer,
    PULONG InstanceLength,
    PULONG SizeNeeded
    )
/*+++

Routine Description :

    This routine implements the WMI query for the discovery config
    class.

--*/
{
    NTSTATUS rtlStatus;
    UCHAR status;
    PMSiSCSI_DiscoveryConfig config;


    *SizeNeeded = sizeof(MSiSCSI_DiscoveryConfig);

    if (BufferAvail >= sizeof(MSiSCSI_DiscoveryConfig))
    {
        config = (PMSiSCSI_DiscoveryConfig)Buffer;
        RtlZeroMemory(Buffer, sizeof(MSiSCSI_DiscoveryConfig));
        if (AdapterExtension->DiscoveryConfigFlags & DISCOVERY_CONFIG_DO_DISCOVERY)
        {
            config->PerformiSNSDiscovery = TRUE;
        }

        if (AdapterExtension->DiscoveryConfigFlags & DISCOVERY_CONFIG_FIND_SNS_AUTOMATICALLY)
        {
            config->AutomaticiSNSDiscovery = TRUE;
        }

        config->InitiatorName[0] = 256 * sizeof(WCHAR);

        rtlStatus = RtlStringCchCopyNW(&config->InitiatorName[1],
                                       MAX_INITIATOR_NAME,
                                       AdapterExtension->InitiatorName,
                                       MAX_INITIATOR_NAME);

        config->iSNSServer.Type = ISCSI_IP_ADDRESS_TEXT;
        config->iSNSServer.TextAddress[0] = 256 * sizeof(WCHAR);
        rtlStatus = RtlStringCchCopyNW(&config->iSNSServer.TextAddress[1], 256,
                                       AdapterExtension->SNSServer, 256);

        *InstanceLength = *SizeNeeded;
        status = SRB_STATUS_SUCCESS;
    } else {
        status = SRB_STATUS_DATA_OVERRUN;
    }

    return(status);
}

UCHAR iScsiAdvanceString(
    OUT PWCHAR *String,
    OUT PUSHORT StringLen,
    IN OUT PUCHAR *Buffer,
    IN OUT PULONG BufferSize
    )
{
    UCHAR status = SRB_STATUS_ERROR;
    PUCHAR b;
    ULONG bs;
    USHORT len;

    b = *Buffer;
    bs = *BufferSize;

    if (bs >= sizeof(USHORT))
    {
        //
        // Get the length of the string and advance beyond it
        //
        len = *((PUSHORT)b);
        b += sizeof(USHORT);
        bs -= sizeof(USHORT);
        *String = (PWCHAR)b;
        *StringLen = len;

        if (bs >= len)
        {
            b += len;
            bs -= len;

            *Buffer = b;
            *BufferSize = bs;
            status = SRB_STATUS_SUCCESS;
        }
    }
    return(status);
}

UCHAR iScsiSetDiscoveryConfig(
    PISCSI_ADAPTER_EXTENSION AdapterExtension,
    ULONG BufferSize,
    PUCHAR Buffer
    )
/*+++

Routine Description :

    This routine implements the WMI set for the discovery config
    class.

--*/
{
    NTSTATUS rtlStatus;
    UCHAR status;
    PMSiSCSI_DiscoveryConfig config;
    USHORT stringLen, initiatorNameLen;
    PISCSI_IP_Address ipAddress;
    PWCHAR string;
    PWCHAR initiatorName;
    ULONG pad;

    //
    // Assume the buffer passed will be bad in some way
    //
    status = SRB_STATUS_ERROR;

    //
    // Validate that the buffer passed is large enough for the
    // beginning of the class
    //
    if (BufferSize >= FIELD_OFFSET(MSiSCSI_DiscoveryConfig,
                                  InitiatorName))
    {
        config = (PMSiSCSI_DiscoveryConfig)Buffer;

        //
        // Advance buffer pointer past beginning of config struct
        //
        Buffer += FIELD_OFFSET(MSiSCSI_DiscoveryConfig, InitiatorName);
        BufferSize -= FIELD_OFFSET(MSiSCSI_DiscoveryConfig, InitiatorName);

        //
        // Now validate that there is a valid InitiatorName string
        //
        status = iScsiAdvanceString(&initiatorName,
                                    &initiatorNameLen,
                                    &Buffer,
                                    &BufferSize);

        if (status == SRB_STATUS_SUCCESS)
        {
            status = SRB_STATUS_ERROR;

            //
            // Pad Buffer to a 4 byte boundry since
            // ISCSI_IP_Address is required to be on a 4 byte
            // boundry
            //
            pad = (ULONG)((((ULONG_PTR)Buffer + 3) &~3) - (ULONG_PTR)Buffer);
            if (BufferSize >= pad)
            {
                Buffer += pad;
                BufferSize -= pad;

                //
                // Now validate that there is enough room for the beginning of
                // the IP address struct
                //
                if (BufferSize >= FIELD_OFFSET(ISCSI_IP_Address,
                                               TextAddress))
                {
                    ipAddress = (PISCSI_IP_Address)Buffer;

                    //
                    // Advance buffer pointer beyond IP address struct
                    //
                    Buffer += FIELD_OFFSET(ISCSI_IP_Address, TextAddress);
                    BufferSize -= FIELD_OFFSET(ISCSI_IP_Address, TextAddress);

                    //
                    // Validate that the user passed a text addresss
                    //
                    if ((ipAddress->Type == ISCSI_IP_ADDRESS_TEXT) ||
                        (ipAddress->Type == ISCSI_IP_ADDRESS_IPV4))
                    {
                        //
                        // Validate that there is enough room for the string
                        // length
                        //
                        status = iScsiAdvanceString(&string,
                                                    &stringLen,
                                                    &Buffer,
                                                    &BufferSize);

                        if (status == SRB_STATUS_SUCCESS)
                        {
                            status = SRB_STATUS_ERROR;

                            //
                            // Finally copy out the values from the
                            // passed buffer into the device extension
                            //

                            //
                            // Validate that the input string is not too large
                            //
                            if (initiatorNameLen <= MAX_INITIATOR_NAME) {

                                rtlStatus = RtlStringCchCopyNW(AdapterExtension->InitiatorName,
                                                               MAX_INITIATOR_NAME,
                                                               initiatorName,
                                                               MAX_INITIATOR_NAME);
                                AdapterExtension->InitiatorName[(initiatorNameLen / sizeof(WCHAR))] = 0;

                                if (ipAddress->Type == ISCSI_IP_ADDRESS_TEXT)
                                {
                                    if (stringLen <= 256)
                                    {
                                        rtlStatus = RtlStringCchCopyNW(AdapterExtension->SNSServer,
                                                                       256, string, 256);
                                        AdapterExtension->SNSServer[(stringLen / sizeof(WCHAR))] = 0;
                                    }

                                } else {
                                    PUCHAR i;

                                    i = (PUCHAR)&ipAddress->IpV4Address;

                                    RtlStringCchPrintfW(AdapterExtension->SNSServer,
                                                        256,
                                                        L"%d.%d.%d.%d",
                                                        i[0], i[1], i[2], i[3]);
                                }
                                AdapterExtension->DiscoveryConfigFlags = (config->PerformiSNSDiscovery) ?
                                                                             DISCOVERY_CONFIG_DO_DISCOVERY :
                                                                             0;

                                AdapterExtension->DiscoveryConfigFlags |= (config->AutomaticiSNSDiscovery) ?
                                                                             DISCOVERY_CONFIG_FIND_SNS_AUTOMATICALLY :
                                                                             0;

                                status = SRB_STATUS_SUCCESS;
                            }
                        }
                    }
                }
            }
        }
    }
    return(status);
}

UCHAR iScsiAddPortalGroup(
    IN OUT PUCHAR *Buffer,
    IN OUT PULONG SizeLeft,
    IN OUT PULONG SizeNeeded,
    IN ULONG PortalCount
    )
/*+++

Routine Description :

    This routine will allocate space of an
    ISCSI_DiscoveredTargetPortalGroup structure that can be followed by
    a set of ISCSI_DiscoveredTargetPortal data structures that describe
    the portals associated with the portal group.

    *Buffer on entry has the current position in the buffer that the
        data structures are being built. On return it is updated beyond
        the ISCSI_DiscoveredTargetPortalGroup structure.

    *SizeLeft on entry has the number of bytes left in *Buffer. On
        return it is updated with the number of bytes consumed by the
        ISCSI_DiscoveredTargetPortalGroup structure. If there is not
        enough bytes for the ISCSI_DiscoveredTargetPortalGroup
        structure then it returns with 0 and SRB_STATUS_DATA_OVERRUN is
        the return value from this function. If there is enough bytes
        then SRB_STATUS_SUCCESS is returned. If a previous call to this
        or iScsiAddPortalToGroup returned *SizeLeft as 0, then it is
        permissible to keep calling so that *SizeNeeded gets updated so
        that an accurate size for the entire data structure can be
        computed.

    *SizeNeeded on entry has the number of bytes needed so far to complete the
        request. On return it is updated to include the number of bytes
        needed for the ISCSI_DiscoveredTargetPortalGroup structure.

    *PortalGroup returns pointing at the portal group for which space
        was allocated by this routine.

--*/
{
    UCHAR status;
    ULONG PadSize, GroupSize, TotalSize;
    PISCSI_DiscoveredTargetPortalGroup2 PortalGroup;

    //
    // Be sure that buffer is padded on a 4 byte boundry
    //
    PadSize = (ULONG)((((ULONG_PTR)*Buffer + 3) &~3) - ((ULONG_PTR)*Buffer));

    //
    // Compute size of portal groups
    //
    GroupSize = FIELD_OFFSET(ISCSI_DiscoveredTargetPortalGroup2,
                             Portals);
    TotalSize = PadSize + GroupSize;

    //
    // Update the size needed to complete the entire request
    //
    *SizeNeeded += TotalSize;

    if (*SizeLeft >= TotalSize)
    {
        //
        // There is enough room to build the portal group. Return the
        // pointer to the portal group.
        //
        RtlZeroMemory(*Buffer, TotalSize);

        PortalGroup = (PISCSI_DiscoveredTargetPortalGroup2)((PUCHAR)*Buffer +
                                                            PadSize);
        PortalGroup->PortalCount = PortalCount;

        //
        // Update pointer in current buffer and number of bytes left
        //
        *SizeLeft -= TotalSize;
        status = SRB_STATUS_SUCCESS;
    } else {
        //
        // There is not enough room left to build the portal group
        //
        *SizeLeft = 0;
        status = SRB_STATUS_DATA_OVERRUN;
    }

    //
    // Advance buffer pointer, do this even if there is not enough room
    // so the size calculations can take into account any padding
    //
    *Buffer += TotalSize;

    return(status);
}

VOID
iScsiCopyUTF8ToCounted(
    PWCHAR p,
    PUTF8CHAR UTF8,
    ULONG UTF8Len
    )
{
    ULONG i;

    *p++ = ((USHORT)UTF8Len * sizeof(WCHAR));
    for (i = 0; i < UTF8Len; i++)
    {
        //
        // This is a quick and dirty conversion from UTF8 to
        // UNICODE and is only valid for the US ANSI character set.
        // See RFC 2044 for more details on converting UTF8 to
        // UNICODE
        //
        *p++ = (WCHAR)*UTF8++;
    }
}

UCHAR iScsiAddPortalToGroup(
    IN OUT PUCHAR *Buffer,
    IN OUT PULONG SizeLeft,
    IN OUT PULONG SizeNeeded,
    IN ULONG Address,
    IN PUTF8CHAR SymbolicName,
    IN USHORT Socket,
    IN ULONG SecurityBitmap,
    IN ULONG KeySize,
    IN PUCHAR Key
    )
/*+++

Routine Description :

    This routine will allocate space of an
    ISCSI_DiscoveredTargetPortal structure and fill it with the
    parameters passed as appropriate.

    *Buffer on entry has the current position in the buffer that the
        data structures are being built. On return it is updated beyond
        the ISCSI_DiscoveredTargetPortal structure.

    *SizeLeft on entry has the number of bytes left in *Buffer. On
        return it is updated with the number of bytes consumed by the
        ISCSI_DiscoveredTargetPortal structure. If there is not
        enough bytes for the ISCSI_DiscoveredTargetPortal
        structure then it returns with 0 and SRB_STATUS_DATA_OVERRUN is
        the return value from this function. If there is enough bytes
        then SRB_STATUS_SUCCESS is returned. If a previous call to this
        or iScsiAddPortalGroup returned *SizeLeft as 0, then it is
        permissible to keep calling so that *SizeNeeded gets updated so
        that an accurate size for the entire data structure can be
        computed.

    *SizeNeeded on entry has the number of bytes needed so far to complete the
        request. On return it is updated to include the number of bytes
        needed for the ISCSI_DiscoveredTargetPortal structure.

    Address has the IP Address of the portal

    SymbolicName is a pointer to a UTF8 string that has the portal
        symbolic name.

    Socket has the socket number of the portal

    SecurityBitmap is the security bitmap

    KeySize is the number of bytes in the key

    Key is th preshared key

--*/
{
    UCHAR status;
    PISCSI_DiscoveredTargetPortal2 Portal;
    ULONG PadSize, SymbolicNamePos, SymbolicNameLen, PortalSize;
    ULONG TotalSize, i;
    PWCHAR p;
    PULONG pu;
    ULONG BitmapPos;

    //
    // Portal must be padded on a 4 byte boundry
    //
    PadSize = (ULONG)((((ULONG_PTR)*Buffer + 3) &~3) - ((ULONG_PTR)*Buffer));

    //
    // Compute the portal size out of the size of the individual
    // components. Since these structures are variable length we cannot
    // use sizeof() without being very wasteful of memory
    //
    BitmapPos = FIELD_OFFSET(ISCSI_DiscoveredTargetPortal2, Address) +
                      FIELD_OFFSET(ISCSI_IP_Address, TextAddress) +
                      sizeof(USHORT); // 0 length for IP_Address->TextAddress

    SymbolicNamePos = BitmapPos + (2 * sizeof(ULONG)) + KeySize;

    SymbolicNameLen = SymbolicName ? strlen(SymbolicName) : 0;
    PortalSize = SymbolicNamePos +
                                      // USHORT for length and size in WCHAR of
                                      // symbolic name
                 sizeof(USHORT) + (SymbolicNameLen * sizeof(WCHAR));

    TotalSize = PadSize + PortalSize;
    *SizeNeeded += TotalSize;

    if (*SizeLeft >= TotalSize)
    {
        //
        // There is enough room for the portal so build the portal data
        // structure
        //
        Portal = (PISCSI_DiscoveredTargetPortal2)((PUCHAR)*Buffer + PadSize);

        RtlZeroMemory(Portal, TotalSize);
        Portal->Socket = Socket;
        Portal->Address.Type = ISCSI_IP_ADDRESS_IPV4;
        Portal->Address.IpV4Address = Address;
        Portal->Address.TextAddress[0] = 0;

        pu = (PULONG)((PUCHAR)Portal + BitmapPos);

        *pu++ = SecurityBitmap;
        *pu++ = KeySize;

        memcpy(pu, Key, KeySize);

        //
        // FIll in symbolic name; length followed by unicode chars
        //
        p = (PWCHAR)((PUCHAR)Portal + SymbolicNamePos);

        iScsiCopyUTF8ToCounted(p, SymbolicName, SymbolicNameLen);

        //
        // Update our current buffer pointers
        //
        *SizeLeft -= TotalSize;

        status = SRB_STATUS_SUCCESS;
    } else {
        //
        // There is not enough room for the portal
        //
        *SizeLeft = 0;
        status = SRB_STATUS_DATA_OVERRUN;
    }

    //
    // Advance buffer pointer, do this even if there is not enough room
    // so the size calculations can take into account any padding
    //
    *Buffer += TotalSize;

    return(status);
}

UCHAR iScsiAddDiscoveredTarget(
    IN OUT PUCHAR *Buffer,
    IN OUT PULONG SizeLeft,
    IN OUT PULONG SizeNeeded,
    IN PUTF8CHAR Name,
    IN PUTF8CHAR Alias,
    IN PUTF8CHAR DDName,
    IN ULONG DDID,
    IN ULONG PortalGroupCount
    )
/*+++

Routine Description :

    This routine will allocate space of an
    ISCSI_DiscoveredTargetPortal structure and fill it with the
    parameters passed as appropriate.

    *Buffer on entry has the current position in the buffer that the
        data structures are being built. On return it is updated beyond
        the ISCSI_DiscoveredTargetPortal structure.

    *SizeLeft on entry has the number of bytes left in *Buffer. On
        return it is updated with the number of bytes consumed by the
        ISCSI_DiscoveredTargetPortal structure. If there is not
        enough bytes for the ISCSI_DiscoveredTargetPortal
        structure then it returns with 0 and SRB_STATUS_DATA_OVERRUN is
        the return value from this function. If there is enough bytes
        then SRB_STATUS_SUCCESS is returned. If a previous call to this
        or iScsiAddPortalGroup returned *SizeLeft as 0, then it is
        permissible to keep calling so that *SizeNeeded gets updated so
        that an accurate size for the entire data structure can be
        computed.

    *SizeNeeded on entry has the number of bytes needed so far to complete the
        request. On return it is updated to include the number of bytes
        needed for the ISCSI_DiscoveredTargetPortal structure.

    Name is the target name

    Alias is the target alias

    DDName is the Discovery domain name

    DDID is dicovery domain id

--*/
{
    UCHAR status;
    ULONG NameLen, AliasLen, DDNameLen, PadSize, TotalSize;
    PWCHAR p;
    PISCSI_DiscoveredTarget2 Target;

    NameLen = strlen(Name);
    AliasLen = Alias ? strlen(Alias) : 0;

    PadSize = (ULONG)((((ULONG_PTR)*Buffer + 3) &~3) - ((ULONG_PTR)*Buffer));

    TotalSize = FIELD_OFFSET(ISCSI_DiscoveredTarget2, TargetName) +
                (((NameLen+1) + (AliasLen+1) ) * sizeof(WCHAR)) +
                PadSize;

    *SizeNeeded += TotalSize;

    if (*SizeLeft >= TotalSize)
    {
        Target = (PISCSI_DiscoveredTarget2)((PUCHAR)*Buffer + PadSize);

        Target->TargetPortalGroupCount = PortalGroupCount;

        p = (PWCHAR)((PUCHAR)Target + FIELD_OFFSET(ISCSI_DiscoveredTarget2,
                                                   TargetName));
        iScsiCopyUTF8ToCounted(p, Name, NameLen);
        p += NameLen + 1;

        iScsiCopyUTF8ToCounted(p, Alias, AliasLen);
        p += AliasLen + 1;

        *SizeLeft -= TotalSize;
        status = SRB_STATUS_SUCCESS;
    } else {
        //
        // There is not enough room for the portal
        //
        *SizeLeft = 0;
        status = SRB_STATUS_DATA_OVERRUN;
    }

    //
    // Advance buffer pointer, do this even if there is not enough room
    // so the size calculations can take into account any padding
    //
    *Buffer += TotalSize;

    return(status);
}

UCHAR iScsiSetPresharedKeyForId(
    PISCSI_ADAPTER_EXTENSION AdapterExtension,
    ULONG InBufferSize,
    ULONG OutBufferSize,
    PULONG SizeNeeded,
    PUCHAR Buffer
    )
{
    PSetPresharedKeyForId_IN in;
    PSetPresharedKeyForId_OUT out;
    UCHAR status;
    ULONG sizeExpected;
    PCHAR key;

    *SizeNeeded = sizeof(SetPresharedKeyForId_OUT);

    if (OutBufferSize >= *SizeNeeded)
    {
        status = SRB_STATUS_ERROR;
        if (InBufferSize >= FIELD_OFFSET(SetPresharedKeyForId_IN,
                                         Id))
        {
            in = (PSetPresharedKeyForId_IN)Buffer;
            sizeExpected = FIELD_OFFSET(SetPresharedKeyForId_IN,
                                        Id) +
                in->IdSize +
                in->KeySize;
            if (InBufferSize >= sizeExpected)
            {
                key = OffsetToPtr(in, FIELD_OFFSET(SetPresharedKeyForId_IN,
                                                   Id) +
                                  in->IdSize);

                DebugPrint((iScsiPrtDebugError,
                    "iScsiSetPresharedKeyForId: in %p type %d idlen %x at %p KeyLen is %x at %p\n",
                    in,
                    in->IdType,
                    in->IdSize,
                    in->Id,
                    in->KeySize,
                    key));

                ETWDebugPrint(iScsiLevelError,
                    iScsiFlagWmi,
                    "iScsiSetPresharedKeyForId: in %p type %d idlen %x at %p KeyLen is %x at %p\n",
                    in,
                    in->IdType,
                    in->IdSize,
                    in->Id,
                    in->KeySize,
                    key);

                out = (PSetPresharedKeyForId_OUT)Buffer;
                out->Status = STATUS_SUCCESS;
                status = SRB_STATUS_SUCCESS;
            }
        }
    } else {
        status = SRB_STATUS_DATA_OVERRUN;
    }

    return(status);
}


UCHAR iScsiSetGroupPresharedKey(
    PISCSI_ADAPTER_EXTENSION AdapterExtension,
    ULONG InBufferSize,
    ULONG OutBufferSize,
    PULONG SizeNeeded,
    PUCHAR Buffer
    )
{
    PSetGroupPresharedKey_IN in;
    PSetGroupPresharedKey_OUT out;
    UCHAR status;
    ULONG sizeExpected;

    *SizeNeeded = sizeof(SetGroupPresharedKey_OUT);

    if (OutBufferSize >= *SizeNeeded)
    {
        status = SRB_STATUS_ERROR;
        if (InBufferSize >= FIELD_OFFSET(SetGroupPresharedKey_IN,
                                         Key))
        {
            in = (PSetGroupPresharedKey_IN)Buffer;
            sizeExpected = FIELD_OFFSET(SetGroupPresharedKey_IN,
                                        Key) + in->KeySize;
            if (InBufferSize >= sizeExpected)
            {
                DebugPrint((iScsiPrtDebugError,
                    "iScsiSetGroupPresharedKey: in %p KeyLen is %x at %p\n",
                    in,
                    in->KeySize,
                    in->Key));

                ETWDebugPrint(iScsiLevelError, iScsiFlagWmi,
                    "iScsiSetGroupPresharedKey: in %p KeyLen is %x at %p\n",
                    in,
                    in->KeySize,
                    in->Key);

                out = (PSetGroupPresharedKey_OUT)Buffer;
                out->Status = STATUS_SUCCESS;
                status = SRB_STATUS_SUCCESS;
            }
        }
    } else {
        status = SRB_STATUS_DATA_OVERRUN;
    }

    return(status);
}

UCHAR iScsiSetTunnelModeOuterAddress(
    PISCSI_ADAPTER_EXTENSION AdapterExtension,
    ULONG InBufferSize,
    ULONG OutBufferSize,
    PULONG SizeNeeded,
    PUCHAR Buffer
    )
{
    PSetTunnelModeOuterAddress_IN in;
    PSetTunnelModeOuterAddress_OUT out;
    UCHAR status;
    ULONG sizeExpected;

    *SizeNeeded = sizeof(SetTunnelModeOuterAddress_OUT);

    if (OutBufferSize >= *SizeNeeded)
    {
        status = SRB_STATUS_ERROR;
        if (InBufferSize >= sizeof(SetTunnelModeOuterAddress_IN))
        {
            in = (PSetTunnelModeOuterAddress_IN)Buffer;

            DebugPrint((iScsiPrtDebugError,
                "iScsiSetTunnelModeOuterAddress: in %p port %x\n",
                in,
                in->PortNumber));

            ETWDebugPrint(iScsiLevelError,
                iScsiFlagWmi,
                "iScsiSetTunnelModeOuterAddress: in %p port %x\n",
                in,
                in->PortNumber);

            out = (PSetTunnelModeOuterAddress_OUT)Buffer;
            out->Status = STATUS_SUCCESS;
            status = SRB_STATUS_SUCCESS;
        }
    } else {
        status = SRB_STATUS_DATA_OVERRUN;
    }

    return(status);
}

UCHAR iScsiReportDiscoveredTargets2(
    PISCSI_ADAPTER_EXTENSION AdapterExtension,
    ULONG BufferSize,
    PULONG SizeNeeded,
    PUCHAR Buffer
    )
{
    UCHAR status;
    PReportDiscoveredTargets2_OUT out;
    ULONG size;


    *SizeNeeded = FIELD_OFFSET(ReportDiscoveredTargets2_OUT,
                               Targets);
    if (BufferSize >= *SizeNeeded)
    {
        out = (PReportDiscoveredTargets2_OUT)Buffer;
        out->Status = STATUS_SUCCESS;
        out->TargetCount = 3;

        BufferSize -= *SizeNeeded;
    }

    //
    // Advance buffer pointer, do this even if there is not enough room
    // so the size calculations can take into account any padding
    //
    Buffer += *SizeNeeded;

    iScsiAddDiscoveredTarget(&Buffer,
                             &BufferSize,
                             SizeNeeded,
                             "Target1",
                             "Alias",
                             "DiscoveryDomain",
                             3,
                             1);

    iScsiAddPortalGroup(&Buffer,
                        &BufferSize,
                        SizeNeeded,
                        1);

    status = iScsiAddPortalToGroup(&Buffer,
                                   &BufferSize,
                                   SizeNeeded,
                                   0x12345678,
                                   "SymbolicName",
                                   5003,
                                   0x11223344,
                                   0,
                                   NULL
                                   );


    //
    // Target 2, 2 portal groups, 1 portal and 2 portals
    //
    iScsiAddDiscoveredTarget(&Buffer,
                             &BufferSize,
                             SizeNeeded,
                             "Target2",
                             NULL,
                             "DiscoveryDomain",
                             3,
                             2);

    iScsiAddPortalGroup(&Buffer,
                        &BufferSize,
                        SizeNeeded,
                        1);

    status = iScsiAddPortalToGroup(&Buffer,
                                   &BufferSize,
                                   SizeNeeded,
                                   0x11111111,
                                   "SymbolicName",
                                   5003,
                                   0x44332211,
                                   8,
                                   "12345678");

    iScsiAddPortalGroup(&Buffer,
                        &BufferSize,
                        SizeNeeded,
                        2);

    status = iScsiAddPortalToGroup(&Buffer,
                                   &BufferSize,
                                   SizeNeeded,
                                   0x12345678,
                                   "SymbolicName1",
                                   5003,
                                   0x23,
                                   8,
                                   "12345678");

    status = iScsiAddPortalToGroup(&Buffer,
                                   &BufferSize,
                                   SizeNeeded,
                                   0x12345679,
                                   "SymbolicName2",
                                   5003,
                                   0x34,
                                   8,
                                   "12345678");

    //
    // Target 3
    //
    iScsiAddDiscoveredTarget(&Buffer,
                             &BufferSize,
                             SizeNeeded,
                             "Target3",
                             NULL,
                             NULL,
                             3,
                             2);

    iScsiAddPortalGroup(&Buffer,
                        &BufferSize,
                        SizeNeeded,
                        5);

    status = iScsiAddPortalToGroup(&Buffer,
                                   &BufferSize,
                                   SizeNeeded,
                                   0x11111111,
                                   "SymbolicName1",
                                   5003,
                                   0x23,
                                   0,
                                   NULL);

    status = iScsiAddPortalToGroup(&Buffer,
                                   &BufferSize,
                                   SizeNeeded,
                                   0x11111113,
                                   "SymbolicName2",
                                   5003,
                                   0x23,
                                   0,
                                   NULL);

    status = iScsiAddPortalToGroup(&Buffer,
                                   &BufferSize,
                                   SizeNeeded,
                                   0x11111113,
                                   NULL,
                                   5003,
                                   0x23,
                                   0,
                                   NULL);

    status = iScsiAddPortalToGroup(&Buffer,
                                   &BufferSize,
                                   SizeNeeded,
                                   0x11111114,
                                   NULL,
                                   5003,
                                   0x23,
                                   0,
                                   NULL);

    status = iScsiAddPortalToGroup(&Buffer,
                                   &BufferSize,
                                   SizeNeeded,
                                   0x11111115,
                                   "SymbolicName5",
                                   5003,
                                   0x23,
                                   0,
                                   NULL);

    iScsiAddPortalGroup(&Buffer,
                        &BufferSize,
                        SizeNeeded,
                        2);

    status = iScsiAddPortalToGroup(&Buffer,
                                   &BufferSize,
                                   SizeNeeded,
                                   0x12345678,
                                   "SymbolicName1",
                                   5003,
                                   0x23,
                                   0,
                                   NULL);

    status = iScsiAddPortalToGroup(&Buffer,
                                   &BufferSize,
                                   SizeNeeded,
                                   0x12345679,
                                   "SymbolicName2",
                                   5003,
                                   0x23,
                                   0,
                                   NULL);

    return(status);
}

UCHAR
iSpReadTCPConfigInfo(
    IN PISCSI_ADAPTER_EXTENSION AdapterExtension,
    OUT PUCHAR Buffer
   )
/*++

Routine Description:

   Read the iScsi Initiator Instance TCP configuration into the OS buffer.

Arguments:

   AdapterExtension - miniport drivers storage
   Buffer  - Used to store all the information of a paticular initiator

Return Value:

   Status - SRB Status for this request

--*/
{
    PMSiSCSI_TCPIPConfig TCPConfigData;
    UCHAR status;


    TCPConfigData = (PMSiSCSI_TCPIPConfig)Buffer;
    RtlZeroMemory(TCPConfigData, sizeof(MSiSCSI_TCPIPConfig));

    RtlCopyMemory(TCPConfigData,
                  &(AdapterExtension->TCPConfigData),
                  sizeof(MSiSCSI_TCPIPConfig));

    if (TCPConfigData->IpAddress.TextAddress[0] == 0)
    {
        TCPConfigData->IpAddress.TextAddress[0] = 256 * sizeof(WCHAR);
    }

    if (TCPConfigData->DefaultGateway.TextAddress[0] == 0)
    {
        TCPConfigData->DefaultGateway.TextAddress[0] = 256 * sizeof(WCHAR);
    }

    if (TCPConfigData->SubnetMask.TextAddress[0] == 0)
    {
        TCPConfigData->SubnetMask.TextAddress[0] = 256 * sizeof(WCHAR);
    }

    if (TCPConfigData->PreferredDNSServer.TextAddress[0] == 0)
    {
        TCPConfigData->PreferredDNSServer.TextAddress[0] = 256 * sizeof(WCHAR);
    }

    if (TCPConfigData->AlternateDNSServer.TextAddress[0] == 0)
    {
        TCPConfigData->AlternateDNSServer.TextAddress[0] = 256 * sizeof(WCHAR);
    }

    return SRB_STATUS_SUCCESS;
}


UCHAR
iSpReadNICConfigInfo(
    IN PISCSI_ADAPTER_EXTENSION AdapterExtension,
    OUT PUCHAR Buffer
   )
/*++

Routine Description:

   Read the iScsi Initiator Instance statistics information into the OS buffer.

Arguments:

   AdapterExtension - miniport drivers storage
   Buffer                  - Used to store all the information of a paticular initiator

Return Value:

   Status - SRB Status for this request

--*/
{
    PMSiSCSI_NICConfig NICConfigData;
    UCHAR status;


    NICConfigData = (PMSiSCSI_NICConfig)Buffer;

    RtlZeroMemory(NICConfigData, sizeof(MSiSCSI_NICConfig));

    // Link Speed
    NICConfigData->LinkSpeed = 100000000;

    // Link State
    NICConfigData->LinkState = ISCSI_NIC_LINKSTATE_CONNECTED;

    NICConfigData->MaxFrameSize = 65536;

    // Ethernet MAC Address
    NICConfigData->MacAddress[0] = 0x31;
    NICConfigData->MacAddress[1] = 0x32;
    NICConfigData->MacAddress[2] = 0x33;
    NICConfigData->MacAddress[3] = 0x34;
    NICConfigData->MacAddress[4] = 0x35;
    NICConfigData->MacAddress[5] = 0x36;

    status = SRB_STATUS_SUCCESS;
    return status;
}


UCHAR
iSpSetTCPConfigInfo(
    IN PISCSI_ADAPTER_EXTENSION AdapterExtension,
    IN ULONG BufferSize,
    IN PUCHAR Buffer
    )
/*++

Routine Description:

   Sets the content in the Buffer to the drivers adapterExtension

Arguments:

   AdapterExtension - iScsi miniport driver's adapter data storage.

   BufferSize - Specifies the size in bytes of the buffer at Buffer.

   Buffer - Points to a buffer that contains new values for the instance.

Return Value:

   UCHAR - SRB Status for this request

--*/
{
    PMSiSCSI_TCPIPConfig PTCPConfigBuffer;
    PISCSI_IP_Address iScsiIPAddress;
    PISCSI_IP_Address destiScsiIPAddress;
    PUCHAR currentIpData;
    ULONG sizeNeeded;
    ULONG inx;
    UCHAR status = SRB_STATUS_SUCCESS;

    PTCPConfigBuffer = (PMSiSCSI_TCPIPConfig)Buffer;

    sizeNeeded = FIELD_OFFSET(MSiSCSI_TCPIPConfig, IpAddress);

    if ((Buffer == NULL) || (BufferSize < sizeNeeded)) {

        return SRB_STATUS_ERROR;
    }

    RtlZeroMemory(&(AdapterExtension->TCPConfigData),
                  sizeof(MSiSCSI_TCPIPConfig));

    AdapterExtension->TCPConfigData.UseLinkLocalAddress =
        PTCPConfigBuffer->UseLinkLocalAddress;

    AdapterExtension->TCPConfigData.EnableDHCP =
        PTCPConfigBuffer->EnableDHCP;

    BufferSize -= sizeNeeded;

    iScsiIPAddress = &(PTCPConfigBuffer->IpAddress);

    destiScsiIPAddress = &(AdapterExtension->TCPConfigData.IpAddress);

    //
    // 5 is the number of ISCSI_IP_Address entries
    // in MSiSCSI_TCPIPConfig structure
    //
    inx = 0;
    while (inx < 5) {

        status = SRB_STATUS_SUCCESS;

        sizeNeeded = FIELD_OFFSET(ISCSI_IP_Address, TextAddress) + sizeof(WCHAR);

        if (BufferSize < sizeNeeded) {

            status = SRB_STATUS_ERROR;
        } else {

            sizeNeeded += iScsiIPAddress->TextAddress[0];

            if (BufferSize < sizeNeeded) {
                status = SRB_STATUS_ERROR;
            }
        }

        if (status == SRB_STATUS_SUCCESS) {

            BufferSize -= sizeNeeded;

            destiScsiIPAddress->Type = iScsiIPAddress->Type;

            iScsiIPAddress->TextAddress[0] =
                MAX_ISCSI_TEXT_ADDRESS_LEN * sizeof(WCHAR);

            switch (iScsiIPAddress->Type) {
                case ISCSI_IP_ADDRESS_TEXT:
                {
                    RtlCopyMemory(&(destiScsiIPAddress->TextAddress[1]),
                                  &(iScsiIPAddress->TextAddress[1]),
                                  iScsiIPAddress->TextAddress[0]);

                    break;
                }

                case ISCSI_IP_ADDRESS_IPV4:
                {
                    destiScsiIPAddress->IpV4Address =
                        iScsiIPAddress->IpV4Address;

                    break;
                }

                case ISCSI_IP_ADDRESS_IPV6:
                {
                    RtlCopyMemory(destiScsiIPAddress->IpV6Address,
                                  iScsiIPAddress->IpV6Address,
                                  sizeof(iScsiIPAddress->IpV6Address));

                    destiScsiIPAddress->IpV6FlowInfo =
                        iScsiIPAddress->IpV6FlowInfo;

                    destiScsiIPAddress->IpV6ScopeId =
                        iScsiIPAddress->IpV6ScopeId;

                    break;
                }

                default:
                {
                    status = SRB_STATUS_ERROR;
                    return(status);
                    break;
                }
            }

            iScsiIPAddress = (PISCSI_IP_Address) ((PUCHAR)(iScsiIPAddress) +
                                                  ((sizeNeeded + 3) & ~3));

            destiScsiIPAddress++;

            inx++;

        } else {

            break;
        }
    }

    return status;
}

UCHAR
iScsiSetWmiDataBlock(
    IN PVOID Context,
    IN PSCSIWMI_REQUEST_CONTEXT DispatchContext,
    IN ULONG GuidIndex,
    IN ULONG InstanceIndex,
    IN ULONG BufferSize,
    IN PUCHAR Buffer
    )
/*+++

Routine Description:

    This routine is called to set a WMI data block.

--*/
{
    PISCSI_ADAPTER_EXTENSION adapterExtension;
    ULONG sizeNeeded = 0;
    PSCSI_WMI_REQUEST_BLOCK  srb;
    UCHAR status;

    DebugPrint((iScsiPrtDebugTrace,
       "SetWmiDataBlock : GuidIndex - %x\n",
       GuidIndex));

    ETWDebugPrint(iScsiLevelTrace,
        iScsiFlagWmi,
       "SetWmiDataBlock : GuidIndex - %x\n",
       GuidIndex);

    adapterExtension = (PISCSI_ADAPTER_EXTENSION) Context;
    srb = (PSCSI_WMI_REQUEST_BLOCK) DispatchContext->UserContext;

    switch (GuidIndex)
    {
        case iScsiDiscoveryConfigGuid_INDEX:
        {
            status = iScsiSetDiscoveryConfig(adapterExtension,
                                             BufferSize,
                                             Buffer);
            sizeNeeded = 0;
            break;
        }

        case iScsi_TCPConfig_GUID_Index:
        {
            status = iSpSetTCPConfigInfo(adapterExtension, BufferSize, Buffer);
            break;
        }

        default:
        {
            status = SRB_STATUS_ERROR;
            break;
        }
    }

    iSpCompleteWmiRequest(adapterExtension, srb, DispatchContext, status, sizeNeeded);

    //
    // return SRB_STATUS_PENDING since the request has either already been
    // completed in iSpCompleteWmiRequest or the status is really
    // SRB_STATUS_PENDING. This implies that the iScsiWmiSrb routine
    // may not touch the srb since it is already completed.
    //
    return SRB_STATUS_PENDING;

}

UCHAR iScsiPerformPing(
    PISCSI_ADAPTER_EXTENSION AdapterExtension,
    PISCSI_IP_Address IpAddress,
    ULONG RequestCount,
    ULONG RequestSize,
    ULONG Timeout,
    ISDSC_STATUS *Status,
    ULONG *ResponseCount
)
{
    //
    // Here is where the HBA should do the ping operations. Note that
    // this code doesn't pend, but your code should pend as the ping
    // operation could be lengthy.
    //
    *Status = STATUS_SUCCESS;
    *ResponseCount = RequestCount;

    return(SRB_STATUS_SUCCESS);
}

UCHAR iSCSIValidateIPAddress(
    PISCSI_IP_Address IpAddress,
    ULONG BufferSize
    )
{
    ULONG sizeUsed;
    UCHAR srbStatus;

    sizeUsed = (FIELD_OFFSET(ISCSI_IP_Address,
                                   TextAddress) +
                       sizeof(USHORT));

    if (BufferSize >= sizeUsed)
    {
        BufferSize -= sizeUsed;
        sizeUsed = IpAddress->TextAddress[0];

        if (BufferSize >= sizeUsed)
        {
            srbStatus = SRB_STATUS_SUCCESS;
        } else {
            srbStatus = SRB_STATUS_INVALID_REQUEST;
        }
    } else {
        srbStatus = SRB_STATUS_INVALID_REQUEST;
    }

    return(srbStatus);
}

UCHAR iSCSIPerformPingOperation(
    PISCSI_ADAPTER_EXTENSION AdapterExtension,
    ULONG InBufferSize,
    ULONG OutBufferSize,
    ULONG *SizeNeeded,
    PUCHAR Buffer
    )
{
    ULONG requestCount;
    ULONG requestSize;
    ULONG timeout;
    ULONG responseCount;
    PISCSI_IP_Address ipAddress;
    UCHAR srbStatus;
    PPingIPAddress_OUT pingOut;
    PPingIPAddress_IN pingIn;
    ISDSC_STATUS status;


    //
    // Make sure there is enough room in out buffer
    //
    *SizeNeeded = sizeof(PingIPAddress_OUT);

    if (OutBufferSize >= *SizeNeeded)
    {
        if (InBufferSize > FIELD_OFFSET(PingIPAddress_IN,
                                        Address))
        {
            pingIn = (PPingIPAddress_IN)Buffer;
            requestCount = pingIn->RequestCount;
            requestSize = pingIn->RequestSize;
            timeout = pingIn->Timeout;
            ipAddress = &pingIn->Address;

            srbStatus = iSCSIValidateIPAddress(ipAddress,
                                               InBufferSize -
                                               FIELD_OFFSET(PingIPAddress_IN,
                                                            Address)
                                              );

            if (srbStatus == SRB_STATUS_SUCCESS)
            {
                srbStatus = iScsiPerformPing(AdapterExtension,
                                          ipAddress,
                                          requestCount,
                                          requestSize,
                                          timeout,
                                          &status,
                                          &responseCount);

                //
                // return the results of the ping
                //
                pingOut = (PPingIPAddress_OUT)Buffer;
                pingOut->Status = status;
                pingOut->ResponsesReceived = responseCount;
            }

        } else {
            //
            // Input buffer is not correct size
            //
            srbStatus = SRB_STATUS_INVALID_REQUEST;
        }

    } else {
        srbStatus = SRB_STATUS_DATA_OVERRUN;
    }

    return(srbStatus);
}


UCHAR
iScsiExecuteWmiMethod(
    IN PVOID Context,
    IN PSCSIWMI_REQUEST_CONTEXT DispatchContext,
    IN ULONG GuidIndex,
    IN ULONG InstanceIndex,
    IN ULONG MethodId,
    IN ULONG InBufferSize,
    IN ULONG OutBufferSize,
    IN OUT PUCHAR Buffer
    )
/*+++

Routine Description:

    This routine is called to execute a WMI method.
    The methods here include Login, Logout, SendTargets, etc

--*/
{
    PISCSI_ADAPTER_EXTENSION adapterExtension;
    PSCSI_WMI_REQUEST_BLOCK  srb;
    PISCSI_SRB_EXTENSION srbExtension;
    ULONG sizeNeeded = 0;
    UCHAR status = SRB_STATUS_SUCCESS;

    DebugPrint((iScsiPrtDebugTrace,
        "ExecuteWMIMethod : GuidIndex - %x, MethodId - %x\n",
        GuidIndex,
        MethodId));

    ETWDebugPrint(iScsiLevelTrace,
        iScsiFlagWmi,
        "ExecuteWMIMethod : GuidIndex - %x, MethodId - %x\n",
        GuidIndex,
        MethodId);

    adapterExtension = (PISCSI_ADAPTER_EXTENSION) Context;

    srb = (PSCSI_WMI_REQUEST_BLOCK) DispatchContext->UserContext;

    srbExtension = (PISCSI_SRB_EXTENSION) srb->SrbExtension;

    //
    // Save the input parameters in the SRB extension
    //
    srbExtension->Buffer = Buffer;
    srbExtension->InBufferSize = InBufferSize;
    srbExtension->OutBufferSize = OutBufferSize;

    //
    // Note that in all the WMI calls, if we are setting the status
    // in Buffer (loginTargetOut, logoutTargetOut, etc), we should
    // always return SRB_STATUS_SUCCESS. Status field will be set
    // to STATUS_SUCCESS if the operation is successful, and will
    // be set to the approproate error code if there was an error.
    //
    switch (GuidIndex) {
        case iSCSI_Operations_GUID_Index: {

            DebugPrint((iScsiPrtDebugTrace,
                "iSCSI Operations method %d\n",
                MethodId));

            ETWDebugPrint(iScsiLevelTrace,
                iScsiFlagWmi,
                "iSCSI Operations method %d\n",
                MethodId);

            switch (MethodId) {
                case LoginToTarget: {

                    PLoginToTarget_IN  loginTargetIn;

                    sizeNeeded = sizeof(LoginToTarget_OUT);

                    if (InBufferSize < sizeof(LoginToTarget_IN)) {
                        status = SRB_STATUS_INVALID_REQUEST;
                        break;
                    }

                    if (OutBufferSize < sizeof(LoginToTarget_OUT)) {
                        status = SRB_STATUS_DATA_OVERRUN;
                        break;
                    }

                    loginTargetIn = (PLoginToTarget_IN) Buffer;

                    status = iSpProcessLoginToTarget(adapterExtension,
                                                     loginTargetIn,
                                                     srb,
                                                     InBufferSize);
                    break;
                }

                case LogoutFromTarget: {

                    PLogoutFromTarget_IN logoutTargetIn;

                    sizeNeeded = sizeof(LogoutFromTarget_OUT);

                    if (InBufferSize < sizeof(LogoutFromTarget_IN)) {
                        status = SRB_STATUS_INVALID_REQUEST;
                        break;
                    }

                    if (OutBufferSize < sizeof(LogoutFromTarget_OUT)) {
                        status = SRB_STATUS_DATA_OVERRUN;
                        break;
                    }

                    logoutTargetIn = (PLogoutFromTarget_IN) Buffer;

                    status = iSpProcessLogoutFromTarget(adapterExtension,
                                                        logoutTargetIn,
                                                        srb);
                    break;
                }

                case AddConnectionToSession: {

                    PAddConnectionToSession_IN addConnectionIn;

                    sizeNeeded = sizeof(AddConnectionToSession_OUT);

                    if (InBufferSize < sizeof(AddConnectionToSession_IN)) {
                        status = SRB_STATUS_INVALID_REQUEST;
                        break;
                    }

                    if (OutBufferSize < sizeof(AddConnectionToSession_OUT)) {
                        status = SRB_STATUS_DATA_OVERRUN;
                        break;
                    }

                    addConnectionIn = (PAddConnectionToSession_IN) Buffer;

                    status = iSpProcessAddConnectionToSession(adapterExtension,
                                                              addConnectionIn,
                                                              srb,
                                                              InBufferSize);
                    break;
                }

                case RemoveConnectionFromSession: {

                    PRemoveConnectionFromSession_IN removeConnectionIn;

                    if (InBufferSize < sizeof(RemoveConnectionFromSession_IN)) {

                        status = SRB_STATUS_INVALID_REQUEST;
                        break;
                    }

                    if (OutBufferSize < sizeof(RemoveConnectionFromSession_OUT)) {

                        status = SRB_STATUS_DATA_OVERRUN;
                        break;
                    }

                    removeConnectionIn = (PRemoveConnectionFromSession_IN) Buffer;

                    sizeNeeded = sizeof(RemoveConnectionFromSession_OUT);

                    status = iSpProcessRemoveConnectionToSession(adapterExtension,
                                                                 removeConnectionIn,
                                                                 srb,
                                                                 InBufferSize);
                    break;
                }

                case SendTargets: {

                    PSendTargets_IN sendTargetsIn;

                    sizeNeeded = sizeof(SendTargets_OUT);

                    if (InBufferSize < sizeof(SendTargets_IN)) {
                        status = SRB_STATUS_INVALID_REQUEST;
                        break;
                    }

                    if (OutBufferSize < sizeof(SendTargets_OUT)) {
                        status = SRB_STATUS_DATA_OVERRUN;
                        break;
                    }

                    sendTargetsIn = (PSendTargets_IN) Buffer;

                    status = iSpProcessSendTargets(adapterExtension,
                                                   sendTargetsIn,
                                                   srb);

                    break;
                }

                case ScsiInquiry: {

                    PScsiInquiry_IN scsiInquiryIn;

                    sizeNeeded = sizeof(ScsiInquiry_OUT);

                    if (InBufferSize < sizeof(ScsiInquiry_IN)) {
                        status = SRB_STATUS_INVALID_REQUEST;
                        break;
                    }

                    if (OutBufferSize < sizeof(ScsiInquiry_OUT)) {
                        status = SRB_STATUS_DATA_OVERRUN;
                        break;
                    }

                    scsiInquiryIn = (PScsiInquiry_IN) Buffer;

                    status = iSpProcessScsiInquiry(adapterExtension,
                                                   scsiInquiryIn,
                                                   srb);
                    break;
                }

                case ScsiReadCapacity: {

                    PScsiReadCapacity_IN scsiReadCapacityIn;

                    sizeNeeded = sizeof(ScsiReadCapacity_OUT);

                    if (InBufferSize < sizeof(ScsiReadCapacity_IN)) {
                        status = SRB_STATUS_INVALID_REQUEST;
                        break;
                    }

                    if (OutBufferSize < sizeof(ScsiReadCapacity_OUT)) {
                        status = SRB_STATUS_DATA_OVERRUN;
                        break;
                    }

                    scsiReadCapacityIn = (PScsiReadCapacity_IN) Buffer;

                    status = iSpProcessScsiReadCapacity(adapterExtension,
                                                        scsiReadCapacityIn,
                                                        srb);

                    break;
                }

                case ScsiReportLuns: {

                    PScsiReportLuns_IN scsiReportLunsIn;

                    sizeNeeded = sizeof(ScsiReportLuns_OUT);

                    if (InBufferSize < sizeof(ScsiReportLuns_IN)) {
                        status = SRB_STATUS_INVALID_REQUEST;
                        break;
                    }

                    if (OutBufferSize < sizeof(ScsiReportLuns_OUT)) {
                        status = SRB_STATUS_DATA_OVERRUN;
                        break;
                    }

                    scsiReportLunsIn = (PScsiReportLuns_IN) Buffer;

                    status = iSpProcessScsiReportLuns(adapterExtension,
                                                      scsiReportLunsIn,
                                                      srb);
                    break;
                }

                case SetCHAPSharedSecret: {

                    PSetCHAPSharedSecret_IN  setCHAPSecretIn;
                    PSetCHAPSharedSecret_OUT setCHAPSecretOut;
                    ULONG secretSize;
                    ULONG bufferSizeNeeded;

                    setCHAPSecretOut = (PSetCHAPSharedSecret_OUT) Buffer;

                    sizeNeeded = 0;

                    RtlZeroMemory(adapterExtension->InitiatorSecret,
                                  sizeof(adapterExtension->InitiatorSecret));

                    adapterExtension->InitiatorSecretLen = 0;

                    if (InBufferSize >= FIELD_OFFSET(SetCHAPSharedSecret_IN,
                                                     SharedSecret)) {

                        setCHAPSecretIn = (PSetCHAPSharedSecret_IN) Buffer;

                        secretSize = setCHAPSecretIn->SharedSecretSize;

                        if (secretSize == 0) {

                            setCHAPSecretOut->Status = STATUS_SUCCESS;

                            status = SRB_STATUS_SUCCESS;

                        } else if (secretSize > MAX_CHAP_SECRET_LENGTH) {

                            setCHAPSecretOut->Status = ISDSC_NON_SPECIFIC_ERROR;

                            status = SRB_STATUS_SUCCESS;

                        } else {

                            bufferSizeNeeded = secretSize +
                                               FIELD_OFFSET(SetCHAPSharedSecret_IN,
                                                            SharedSecret);

                            if (InBufferSize >= bufferSizeNeeded) {

                                RtlCopyMemory(adapterExtension->InitiatorSecret,
                                              setCHAPSecretIn->SharedSecret,
                                              secretSize);

                                adapterExtension->InitiatorSecretLen = secretSize;

                                status = SRB_STATUS_SUCCESS;

                                setCHAPSecretOut->Status = STATUS_SUCCESS;
                            } else {

                                status = SRB_STATUS_INVALID_REQUEST;

                                setCHAPSecretOut->Status = ISDSC_NON_SPECIFIC_ERROR;
                            }
                        }

                        sizeNeeded = sizeof(SetCHAPSharedSecret_OUT);

                    } else {

                        status = SRB_STATUS_INVALID_REQUEST;
                    }

                    break;
                }

                case SetRADIUSSharedSecret: {

                    PSetRADIUSSharedSecret_IN  setRADIUSSecretIn;
                    PSetRADIUSSharedSecret_OUT setRADIUSSecretOut;
                    ULONG secretSize;
                    ULONG bufferSizeNeeded;

                    setRADIUSSecretOut = (PSetRADIUSSharedSecret_OUT) Buffer;

                    sizeNeeded = 0;

                    RtlZeroMemory(adapterExtension->RadiusSecret,
                                  sizeof(adapterExtension->RadiusSecret));

                    adapterExtension->RadiusSecretLen = 0;

                    if (InBufferSize >= FIELD_OFFSET(SetRADIUSSharedSecret_IN,
                                                     SharedSecret)) {

                        setRADIUSSecretIn = (PSetRADIUSSharedSecret_IN) Buffer;

                        secretSize = setRADIUSSecretIn->SharedSecretSize;

                        if (secretSize == 0) {

                            setRADIUSSecretOut->Status = STATUS_SUCCESS;

                            status = SRB_STATUS_SUCCESS;

                        } else if (secretSize > MAX_RADIUS_SECRET_LENGTH) {

                            setRADIUSSecretOut->Status = ISDSC_NON_SPECIFIC_ERROR;

                            status = SRB_STATUS_SUCCESS;

                        } else {

                            bufferSizeNeeded = secretSize +
                                               FIELD_OFFSET(SetRADIUSSharedSecret_IN,
                                                            SharedSecret);

                            if (InBufferSize >= bufferSizeNeeded) {

                                RtlCopyMemory(adapterExtension->RadiusSecret,
                                              setRADIUSSecretIn->SharedSecret,
                                              secretSize);

                                adapterExtension->RadiusSecretLen = secretSize;

                                status = SRB_STATUS_SUCCESS;

                                setRADIUSSecretOut->Status = STATUS_SUCCESS;
                            } else {

                                status = SRB_STATUS_INVALID_REQUEST;

                                setRADIUSSecretOut->Status = ISDSC_NON_SPECIFIC_ERROR;
                            }
                        }

                        sizeNeeded = sizeof(SetRADIUSSharedSecret_OUT);

                    } else {

                        status = SRB_STATUS_INVALID_REQUEST;
                    }

                    break;
                }

                case RemovePersistentLogin: {

                    PRemovePersistentLogin_IN removePersistentLoginIn;

                    sizeNeeded = sizeof(RemovePersistentLogin_OUT);

                    if (InBufferSize < sizeof(RemovePersistentLogin_IN)) {
                        status = SRB_STATUS_INVALID_REQUEST;
                        break;
                    }

                    if (OutBufferSize < sizeof(RemovePersistentLogin_OUT)) {
                        status = SRB_STATUS_DATA_OVERRUN;
                        break;
                    }

                    removePersistentLoginIn = (PRemovePersistentLogin_IN) Buffer;

                    status = iSpRemovePersistentLogin(adapterExtension,
                                                      removePersistentLoginIn,
                                                      srb);
                    break;
                }

                case AddRADIUSServer: {

                    PAddRADIUSServer_IN  addRADIUSServerIn;
                    PAddRADIUSServer_OUT addRADIUSServerOut;
                    ULONG newServerNameLength;
                    PISCSI_IP_Address list;
                    ULONG count;
                    ULONG updatedCount;
                    ULONG size;
                    ULONG inx = 0;
                    BOOLEAN addressFound = FALSE;

                    sizeNeeded = sizeof(AddRADIUSServer_OUT);

                    if (InBufferSize < sizeof(AddRADIUSServer_IN)) {
                        status = SRB_STATUS_INVALID_REQUEST;
                        break;
                    }

                    if (OutBufferSize < sizeof(AddRADIUSServer_OUT)) {
                        status = SRB_STATUS_DATA_OVERRUN;
                        break;
                    }

                    addRADIUSServerIn = (PAddRADIUSServer_IN) Buffer;

                    addRADIUSServerOut = (PAddRADIUSServer_OUT) Buffer;

                    list = adapterExtension->RadiusServerList;
                    count = adapterExtension->RadiusServerListCount;

                    while (inx < count) {

                        if (RtlEqualMemory(&(list[inx]),
                                                         &(addRADIUSServerIn->RADIUSIPAddress),
                                                         sizeof(ISCSI_IP_Address))) {

                            addressFound = TRUE;

                            status = SRB_STATUS_SUCCESS;

                            addRADIUSServerOut->Status = STATUS_SUCCESS;

                            break;
                        }

                        inx++;
                    }

                    if (addressFound) {
                        break;
                    }

                    updatedCount = count + 1;
                    size = updatedCount * sizeof(ISCSI_IP_Address);

                    adapterExtension->RadiusServerList = ExAllocatePoolWithTag(NonPagedPool,
                                                             size,
                                                             'vrSR');

                    if (adapterExtension->RadiusServerList != NULL) {

                        RtlZeroMemory(adapterExtension->RadiusServerList,
                                      size);

                        RtlCopyMemory(adapterExtension->RadiusServerList,
                                      &(addRADIUSServerIn->RADIUSIPAddress),
                                      sizeof(ISCSI_IP_Address));

                        if (count > 0) {

                            RtlCopyMemory((adapterExtension->RadiusServerList + 1),
                                          list,
                                          (count * sizeof(ISCSI_IP_Address)));
                        }

                        adapterExtension->RadiusServerListCount = updatedCount;

                        if (list != NULL) {

                            ExFreePool(list);
                        }

                        status = SRB_STATUS_SUCCESS;

                        addRADIUSServerOut->Status = STATUS_SUCCESS;

                    } else {

                        adapterExtension->RadiusServerList = list;

                        status = SRB_STATUS_INVALID_REQUEST;

                        addRADIUSServerOut->Status = ISDSC_OUT_OF_RESOURCES;

                    }

                    break;
                }

                case RemoveRADIUSServer: {

                    PRemoveRADIUSServer_IN  removeRADIUSServerIn;
                    PRemoveRADIUSServer_OUT removeRADIUSServerOut;
                    PISCSI_IP_Address removeAddress;
                    PISCSI_IP_Address list;
                    ULONG count;
                    PWCHAR updatedList;
                    ULONG updatedCount;
                    ULONG itr;
                    PISCSI_IP_Address itrList;
                    BOOLEAN found = FALSE;
                    ULONG size = 0;


                    sizeNeeded = sizeof(RemoveRADIUSServer_OUT);

                    if (InBufferSize < sizeof(RemoveRADIUSServer_IN)) {
                        status = SRB_STATUS_INVALID_REQUEST;
                        break;
                    }

                    if (OutBufferSize < sizeof(RemoveRADIUSServer_OUT)) {
                        status = SRB_STATUS_DATA_OVERRUN;
                        break;
                    }

                    removeRADIUSServerIn = (PRemoveRADIUSServer_IN) Buffer;

                    removeRADIUSServerOut = (PRemoveRADIUSServer_OUT) Buffer;

                    list = adapterExtension->RadiusServerList;
                    count = adapterExtension->RadiusServerListCount;

                    itr = 0;
                    itrList = list;
                    removeAddress = &(removeRADIUSServerIn->RADIUSIPAddress);
                    while (itr < count) {

                        ULONG inx;

                        if (itrList->Type == removeAddress->Type) {

                            if (itrList->Type == ISCSI_IP_ADDRESS_IPV4) {
                                if (itrList->IpV4Address == removeAddress->IpV4Address) {
                                    found = TRUE;
                                }
                            }

                            if (itrList->Type == ISCSI_IP_ADDRESS_IPV6) {
                                for (inx = 0; inx < 16; inx++) {
                                    if (itrList->IpV6Address[inx] == removeAddress->IpV6Address[inx]) {
                                        found = TRUE;
                                    } else {
                                        found = FALSE;
                                        break;
                                    }
                                }
                            }

                            if (itrList->Type == ISCSI_IP_ADDRESS_TEXT) {
                                if (wcscmp(itrList->TextAddress, removeAddress->TextAddress) == 0) {
                                    found = TRUE;
                                }
                            }
                        }

                        if (found)
                            break;

                        itr++;
                        itrList++;
                    }

                    if (found) {

                        updatedCount = count - 1;
                        size = updatedCount * sizeof(ISCSI_IP_Address);

                        if (updatedCount > 0) {

                            adapterExtension->RadiusServerList = ExAllocatePoolWithTag(NonPagedPool,
                                                                         size,
                                                                         'vrSR');

                            if (adapterExtension->RadiusServerList != NULL) {

                                RtlZeroMemory(adapterExtension->RadiusServerList,
                                              size);

                                RtlCopyMemory(adapterExtension->RadiusServerList,
                                              list,
                                              (itr * sizeof(ISCSI_IP_Address)));

                                itr++;
                                itrList++;

                                if (itr < count) {

                                    RtlCopyMemory((adapterExtension->RadiusServerList) + (itr - 1),
                                                  itrList,
                                                  ((count - itr) * sizeof(ISCSI_IP_Address)));
                                }

                                adapterExtension->RadiusServerListCount = updatedCount;

                                ExFreePool(list);

                                status = SRB_STATUS_SUCCESS;

                                removeRADIUSServerOut->Status = STATUS_SUCCESS;

                            } else {

                                adapterExtension->RadiusServerList = list;

                                status = SRB_STATUS_INVALID_REQUEST;

                                removeRADIUSServerOut->Status = ISDSC_OUT_OF_RESOURCES;

                            }
                            } else {

                                adapterExtension->RadiusServerList = NULL;

                                adapterExtension->RadiusServerListCount = updatedCount;

                                ExFreePool(list);

                                status = SRB_STATUS_SUCCESS;

                             removeRADIUSServerOut->Status = STATUS_SUCCESS;
                            }

                    } else {

                        removeRADIUSServerOut->Status = ISDSC_NON_SPECIFIC_ERROR;

                        status = SRB_STATUS_INVALID_REQUEST;
                    }

                    break;
                }

                default: {

                    status = SRB_STATUS_INVALID_REQUEST;

                    break;
                }
            } // switch (MethodId)

            break;
        } // case iSCSI_Operations_GUID_Index:

        case iScsi_LB_Operations_GUID_Index: {

            DebugPrint((iScsiPrtDebugTrace,
                "Set LB policy\n"));

            ETWDebugPrint(iScsiLevelTrace,
                iScsiFlagWmi,
                "Set LB policy\n");

            switch (MethodId) {
                case SetLoadBalancePolicy: {

                    sizeNeeded = sizeof(SetLoadBalancePolicy_OUT);

                    if (OutBufferSize >= sizeNeeded) {

                        status = iSpSetLoadBalancePolicy(adapterExtension,
                                                         srb,
                                                         Buffer,
                                                         InBufferSize);
                    } else {

                        DebugPrint((iScsiPrtDebugError,
                            "Output buffer too small for SetLBPolicy\n"));

                        ETWDebugPrint(iScsiLevelError, iScsiFlagWmi,
                            "Output buffer too small for SetLBPolicy\n");

                        status = SRB_STATUS_DATA_OVERRUN;
                    }

                    break;
                }

                default: {
                    status = SRB_STATUS_INVALID_REQUEST;
                    break;
                }
            }

            break;
        } // iScsi_LB_Operations_GUID_Index

        case iSCSI_DiscoveryOperationsGuid_INDEX: {
            switch (MethodId)
            {
                case ReportDiscoveredTargets2:
                {
                    status = iScsiReportDiscoveredTargets2(adapterExtension,
                                                          OutBufferSize,
                                                          &sizeNeeded,
                                                          Buffer);
                    break;
                }

                default: {
                    status = SRB_STATUS_INVALID_REQUEST;
                    break;
                }
            }
            break;
        } // case iSCSI_DiscoveryOperationsGuid_INDEX

        case iScsi_ManagementOperation_GUID_Index: {
            switch (MethodId)
            {
                case PingIPAddress:
                {
                    status = iSCSIPerformPingOperation(adapterExtension,
                                                       InBufferSize,
                                                       OutBufferSize,
                                                       &sizeNeeded,
                                                       Buffer);
                    break;
                }

                default: {
                    status = SRB_STATUS_INVALID_REQUEST;
                    break;
                }
            }
            break;
        }

        case iSCSI_SecurityConfigOperationsGuid_INDEX:
        {
            switch (MethodId)
            {
                case SetPresharedKeyForId:
                {
                    status = iScsiSetPresharedKeyForId(adapterExtension,
                                                       InBufferSize,
                                                       OutBufferSize,
                                                       &sizeNeeded,
                                                       Buffer);
                    break;
                }

                case SetGroupPresharedKey:
                {
                    status = iScsiSetGroupPresharedKey(adapterExtension,
                                                       InBufferSize,
                                                       OutBufferSize,
                                                       &sizeNeeded,
                                                       Buffer);
                    break;
                }

                case SetTunnelModeOuterAddress:
                {
                    status = iScsiSetTunnelModeOuterAddress(adapterExtension,
                                                            InBufferSize,
                                                            OutBufferSize,
                                                            &sizeNeeded,
                                                            Buffer);
                    break;
                }

                default: {
                    status = SRB_STATUS_INVALID_REQUEST;
                    break;
                }
            }
            break;
        } // case iSCSI_SecurityConfigOperationsGuid_INDEX

        default: {

            status = SRB_STATUS_INVALID_REQUEST;

            break;
        }
    } // switch (GuidIndex)

    //
    // Complete the request here if the request is NOT pending
    //
    if (status != SRB_STATUS_PENDING) {

        iSpCompleteWmiRequest(adapterExtension,
                              srb,
                              DispatchContext,
                              status,
                              sizeNeeded);
    }

    //
    // return SRB_STATUS_PENDING since the request has either already been
    // completed in iSpCompleteWmiRequest or the status is really
    // SRB_STATUS_PENDING. This implies that the iScsiWmiSrb routine
    // may not touch the srb since it is already completed.
    //
    return SRB_STATUS_PENDING;
}


ISDSC_STATUS
iSpValidateLoginParameters(
    IN PISCSI_ADAPTER_EXTENSION AdapterExtension,
    IN PLoginToTarget_IN LoginTargetIn,
    IN ULONG InBufferSize
    )
{
    ULONG sizeNeeded;
    ISCSI_AUTH_TYPES authType;
    BOOLEAN inputValid;


    //
    // Ensure that the given buffer is large enough to hold
    // all login input data
    //
    sizeNeeded = FIELD_OFFSET(LoginToTarget_IN, Mappings) +
                 FIELD_OFFSET(ISCSI_TargetMapping, LUNList);

    if (InBufferSize >= sizeNeeded ) {

        sizeNeeded += (LoginTargetIn->Mappings.LUNCount * sizeof(ISCSI_LUNList)) +
                      LoginTargetIn->KeySize +
                      LoginTargetIn->UsernameSize +
                      LoginTargetIn->PasswordSize;
    }

    if (InBufferSize < sizeNeeded) {

        DebugPrint((iScsiPrtDebugError,
            "Buffer too small for login. Given %d, Needed %d\n",
            InBufferSize, sizeNeeded));

        ETWDebugPrint(iScsiLevelError,
            iScsiFlagProtocolLogInOut,
            "Buffer too small for login. Given %d, Needed %d\n",
            InBufferSize, sizeNeeded);

        return ISDSC_LOGIN_USER_INFO_BAD;
    }

    //
    // Validate session type
    //
    if (LoginTargetIn->SessionType > ISCSI_LOGINTARGET_DATA) {

        DebugPrint((iScsiPrtDebugError,
            "Invalid session type %d\n",
            LoginTargetIn->SessionType));

        ETWDebugPrint(iScsiLevelError,
            iScsiFlagProtocolLogInOut,
            "Invalid session type %d\n",
            LoginTargetIn->SessionType);

        return ISDSC_SESSION_TYPE_NOT_SUPPORTED;
    }

    //
    // Make sure the size of the username and password, if given,
    // are less than or equal to max length
    //
    if (LoginTargetIn->LoginOptions.InformationSpecified & ISCSI_LOGIN_OPTIONS_AUTH_TYPE) {
        authType = LoginTargetIn->LoginOptions.AuthType;
    } else {
        authType = ISCSI_NO_AUTH_TYPE;
    }

    if ((authType == ISCSI_CHAP_AUTH_TYPE) ||
        (authType == ISCSI_MUTUAL_CHAP_AUTH_TYPE)) {

        //
        // A non-zero CHAP Username must be provided for CHAP
        //
        if ((LoginTargetIn->UsernameSize == 0) ||
            (LoginTargetIn->UsernameSize > MAX_CHAP_USERNAME_LENGTH)) {

            DebugPrint((iScsiPrtDebugError,
                "Invalid username size %d\n",
                LoginTargetIn->UsernameSize));

            ETWDebugPrint(iScsiLevelError,
                iScsiFlagProtocolLogInOut,
                "Invalid username size %d\n",
                LoginTargetIn->UsernameSize);

            return ISDSC_INVALID_CHAP_USER_NAME;
        }

        //
        // If doing CHAP then we need to have the target's chap secret
        //
        if (((LoginTargetIn->PasswordSize == 0) && !(LoginTargetIn->LoginOptions.LoginFlags & ISCSI_LOGIN_FLAG_USE_RADIUS_RESPONSE))||
            (LoginTargetIn->PasswordSize > MAX_CHAP_SECRET_LENGTH)) {

            DebugPrint((iScsiPrtDebugError,
                "Invalid target secret size %d\n",
                LoginTargetIn->PasswordSize));

            ETWDebugPrint(iScsiLevelError,
                iScsiFlagProtocolLogInOut,
                "Invalid target secret size %d\n",
                LoginTargetIn->PasswordSize);

            return ISDSC_INVALID_TARGET_CHAP_SECRET;
        }

        //
        // If we are performing mutual authentication ensure
        // that initiator's secret is set, and it is not
        // same as the target's secret.
        //
        if (authType == ISCSI_MUTUAL_CHAP_AUTH_TYPE) {

            PUCHAR userNamePtr;
            PUCHAR targetSecret;
            ULONG secretLen;

            if (!(LoginTargetIn->LoginOptions.LoginFlags & ISCSI_LOGIN_FLAG_USE_RADIUS_VERIFICATION)) {

                iSpGetUserNameAndPasswordForLogin(LoginTargetIn,
                                                  &userNamePtr,
                                                  &targetSecret);

                secretLen = AdapterExtension->InitiatorSecretLen;
                if ((secretLen  == 0) ||
                    ((secretLen == LoginTargetIn->PasswordSize) &&
                     (RtlCompareMemory(AdapterExtension->InitiatorSecret,
                                       targetSecret,
                                       secretLen) == secretLen))) {

                    DebugPrint((iScsiPrtDebugError,
                        "Either no initiator secret is set or it is same as target secret\n"));

                    ETWDebugPrint(iScsiLevelError,
                        iScsiFlagCHAP,
                        "Either no initiator secret is set or it is same as target secret\n");

                    return ISDSC_INVALID_INITIATOR_CHAP_SECRET;
                }
            }
        }
    } else if (authType != ISCSI_NO_AUTH_TYPE) {

        //
        // Only chap and mutual chap are supported
        //

        DebugPrint((iScsiPrtDebugError,
            "Invalid authentication type %d\n",
            authType));

        ETWDebugPrint(iScsiLevelError,
            iScsiFlagProtocolLogInOut,
            "Invalid authentication type %d\n",
            authType);

        return ISDSC_INVALID_LOGON_AUTH_TYPE;
    }

    //
    // Check the address type. We currently only support IPv4 and IPv6
    //
    if ((LoginTargetIn->TargetPortal.Address.Type != ISCSI_IP_ADDRESS_IPV4) &&
        (LoginTargetIn->TargetPortal.Address.Type != ISCSI_IP_ADDRESS_IPV6)) {

        DebugPrint((iScsiPrtDebugError,
            "Invalid address type %d\n",
            LoginTargetIn->TargetPortal.Address.Type));

        ETWDebugPrint(iScsiLevelError,
            iScsiFlagProtocolLogInOut,
            "Invalid address type %d\n",
            LoginTargetIn->TargetPortal.Address.Type);

        return ISDSC_ADDRESS_TYPE_NOT_SUPPORTED;
    }

    inputValid = TRUE;

    //
    // Check if target mappings are valid
    //
    if ((LoginTargetIn->Mappings.OSBus != -1) &&
        (LoginTargetIn->Mappings.OSBus >= ISCSI_MAX_BUSES)) {

        DebugPrint((iScsiPrtDebugError,
            "Invalid OSBus %x\n",
            LoginTargetIn->Mappings.OSBus));

        ETWDebugPrint(iScsiLevelError,
            iScsiFlagProtocolLogInOut,
            "Invalid OSBus %x\n",
            LoginTargetIn->Mappings.OSBus);

        inputValid = FALSE;
    }

    if ((inputValid == TRUE) &&
        (LoginTargetIn->Mappings.OSTarget != -1) &&
        (LoginTargetIn->Mappings.OSTarget >= ISCSI_MAX_TARGETS)) {

        DebugPrint((iScsiPrtDebugError,
            "Invalid OSTarget %x\n",
            LoginTargetIn->Mappings.OSTarget));

        ETWDebugPrint(iScsiLevelError,
            iScsiFlagProtocolLogInOut,
            "Invalid OSTarget %x\n",
            LoginTargetIn->Mappings.OSTarget);

        inputValid = FALSE;
    }

    if (inputValid == TRUE) {

        PISCSI_LUNList lunList;
        ULONG inx;

        for (inx = 0; inx < LoginTargetIn->Mappings.LUNCount; inx++) {

            lunList = &(LoginTargetIn->Mappings.LUNList[inx]);

            if (lunList->OSLUN >= ISCSI_MAX_LOGICAL_UNITS) {

                DebugPrint((iScsiPrtDebugError,
                    "Invalid OSLun %x\n",
                    lunList->OSLUN));

                ETWDebugPrint(iScsiLevelError,
                    iScsiFlagProtocolLogInOut,
                    "Invalid OSLun %x\n",
                    lunList->OSLUN);

                inputValid = FALSE;

                break;
            }
        }
    }

    if (inputValid == FALSE) {
        return ISDSC_INVALID_TARGET_MAPPING;
    }

    return STATUS_SUCCESS;
}


ISDSC_STATUS
iSpValidateAddConnectionParameters(
    IN PISCSI_ADAPTER_EXTENSION   AdapterExtension,
    IN PAddConnectionToSession_IN AddConnectionIn,
    IN ULONG                      InBufferSize
    )
{
    ISDSC_STATUS status = STATUS_SUCCESS;
    ULONG sizeNeeded;
    ISCSI_AUTH_TYPES authType;


    //
    // Make sure the given buffer is large enough to hold
    // all add connection input data.
    //
    sizeNeeded = FIELD_OFFSET(AddConnectionToSession_IN, Key);
    if (sizeNeeded <= InBufferSize) {

        sizeNeeded += AddConnectionIn->KeySize +
                      AddConnectionIn->UsernameSize +
                      AddConnectionIn->PasswordSize;
    }

    if (sizeNeeded > InBufferSize) {

        DebugPrint((iScsiPrtDebugError,
            "Add connection: Size Needed %d, Given %d\n",
            sizeNeeded,
            InBufferSize));

        ETWDebugPrint(iScsiLevelError,
            iScsiFlagProtocolLogInOut,
            "Add connection: Size Needed %d, Given %d\n",
            sizeNeeded,
            InBufferSize);

        return ISDSC_LOGIN_USER_INFO_BAD;
    }

    //
    // Check the address type. We currently only support IPv4 and IPv6
    //
    if ((AddConnectionIn->TargetPortal.Address.Type != ISCSI_IP_ADDRESS_IPV4) &&
        (AddConnectionIn->TargetPortal.Address.Type != ISCSI_IP_ADDRESS_IPV6)) {

        DebugPrint((iScsiPrtDebugError,
            "Invalid address type %d\n",
            AddConnectionIn->TargetPortal.Address.Type));

        ETWDebugPrint(iScsiLevelError,
            iScsiFlagProtocolLogInOut,
            "Invalid address type %d\n",
            AddConnectionIn->TargetPortal.Address.Type);

        return ISDSC_ADDRESS_TYPE_NOT_SUPPORTED;
    }

    //
    // Make sure the size of the username and password, if given,
    // are less than or equal to max length
    //
    if (AddConnectionIn->LoginOptions.InformationSpecified & ISCSI_LOGIN_OPTIONS_AUTH_TYPE) {
        authType = AddConnectionIn->LoginOptions.AuthType;
    } else {
        authType = ISCSI_NO_AUTH_TYPE;
    }

    if ((authType == ISCSI_CHAP_AUTH_TYPE) ||
        (authType == ISCSI_MUTUAL_CHAP_AUTH_TYPE)) {

        //
        // A non-zero CHAP Username must be provided for CHAP
        //
        if ((AddConnectionIn->UsernameSize == 0) ||
            (AddConnectionIn->UsernameSize > MAX_CHAP_USERNAME_LENGTH)) {

            DebugPrint((iScsiPrtDebugError,
                "Invalid username size %d\n",
                AddConnectionIn->UsernameSize));

            ETWDebugPrint(iScsiLevelError,
                iScsiFlagProtocolLogInOut,
                "Invalid username size %d\n",
                AddConnectionIn->UsernameSize);

            return ISDSC_INVALID_CHAP_USER_NAME;
        }

        //
        // If doing CHAP then we need to have the target's chap secret
        //
        if (((AddConnectionIn->PasswordSize == 0) &&
             !(AddConnectionIn->LoginOptions.LoginFlags & ISCSI_LOGIN_FLAG_USE_RADIUS_RESPONSE)) ||
             (AddConnectionIn->PasswordSize > MAX_CHAP_SECRET_LENGTH)) {

            DebugPrint((iScsiPrtDebugError,
                "Invalid target secret size %d\n",
                AddConnectionIn->PasswordSize));

            ETWDebugPrint(iScsiLevelError,
                iScsiFlagProtocolLogInOut,
                "Invalid target secret size %d\n",
                AddConnectionIn->PasswordSize);

            return ISDSC_INVALID_TARGET_CHAP_SECRET;
        }

        //
        // If we are performing mutual authentication ensure
        // that initiator's secret is set, and it is not
        // same as the target's secret.
        //
        if (authType == ISCSI_MUTUAL_CHAP_AUTH_TYPE) {

            PUCHAR userNamePtr;
            PUCHAR targetSecret;
            ULONG secretLen;

            if (!(AddConnectionIn->LoginOptions.LoginFlags & ISCSI_LOGIN_FLAG_USE_RADIUS_VERIFICATION)) {

                iSpGetUserNameAndPasswordForAddConnection(AddConnectionIn,
                                                          &userNamePtr,
                                                          &targetSecret);

                secretLen = AdapterExtension->InitiatorSecretLen;
                if ((secretLen  == 0) ||
                    ((secretLen == AddConnectionIn->PasswordSize) &&
                     (RtlCompareMemory(AdapterExtension->InitiatorSecret,
                                       targetSecret,
                                       secretLen) == secretLen))) {

                    DebugPrint((iScsiPrtDebugError,
                        "Either no initiator secret is set or it is same as target secret\n"));

                    ETWDebugPrint(iScsiLevelError,
                        iScsiFlagProtocolLogInOut,
                        "Either no initiator secret is set or it is same as target secret\n");

                    return ISDSC_INVALID_INITIATOR_CHAP_SECRET;
                }
            }
        }
    } else if (authType != ISCSI_NO_AUTH_TYPE) {

        //
        // Only chap and mutual chap are supported
        //

        DebugPrint((iScsiPrtDebugError,
            "Invalid authentication type %d\n",
            authType));

        ETWDebugPrint(iScsiLevelError,
            iScsiFlagProtocolLogInOut,
            "Invalid authentication type %d\n",
            authType);

        return ISDSC_INVALID_LOGON_AUTH_TYPE;
    }

    return STATUS_SUCCESS;
}


ULONGLONG
iSpGetSessionID(
    PISCSI_ADAPTER_EXTENSION AdapterExtension,
    PISCSI_SESSION IScsiSession,
    USHORT UniqueSessionId
    )
/*+++

Routine Description:

    This routine returns a unique session id. The session id
    is made up of ISID and Microsoft signature byte

Arguements:

    AdapterExtension - Adapter Extension

    IScsiSession - Pointer to the session

    UniqueSessionId - Two byte session id passed by iSCSI Service

Return Value:

    8 Byte SessionId
--*/
{
    ULONGLONG retVal;
    ULONG tempUlong;

    NT_ASSERT((IScsiSession->InitiatorID[0] == 0) &&
           (IScsiSession->InitiatorID[1] == 0));

    //
    // If SessionId is not initialized, generate a new session id.
    // Else, return the saved session id
    //
    if (IScsiSession->SessionId == 0) {

        tempUlong = (ULONG) UniqueSessionId;

        SetUlongIn2ByteArray((IScsiSession->InitiatorID), tempUlong);

        //
        // The unique 64 Bit Session Id is composed this way -
        //
        // Out of 8 bytes :
        //
        //   Most Significant 4 Bytes are composed of the byte
        //   ISID_TYPE_ENTERPRISE_NUMBER and the value MSFT_ENTERPRISE_NUMBER
        //   The Least Significant 4 Bytes are taken from InitiatorSessionID
        //
        retVal = ((ULONGLONG) ISID_TYPE_ENTERPRISE_NUMBER << 24) | MSFT_ENTERPRISE_NUMBER;
        retVal <<= 32;

        retVal |= InterlockedIncrement(&AdapterExtension->InitiatorSessionID);

        IScsiSession->SessionId = retVal;
    } else {

        retVal = IScsiSession->SessionId;
    }

    DebugPrint((iScsiPrtDebugInfo,
        "Session Id returned - 0x%016I64x\n",
        retVal));

    ETWDebugPrint(iScsiLevelInfo,
        iScsiFlagWmi,
        "Session Id returned - 0x%016I64x\n",
        retVal);

    return retVal;
}

UCHAR
iSpReadInitiatorSessionInfo(
    IN PISCSI_ADAPTER_EXTENSION AdapterExtension,
    IN PWMI_CONNECTION_LIST ConnectionList,
    IN ULONG NumberOfSessions,
    OUT PUCHAR Buffer
)
/*++

Routine Description:

   Purpose of this routine is to gather initiatorNode information specified in the mof from
   the port driver and place them in the incoming Buffer.

Arguments:

   AdapterExtension - Adpater extension

   SessionListHead -  pointer to the first session we have got a remove lock for

   SessionListTail   -  pointer ot the last session we have got a remove lock for

   InitiatorName      -  Name of initiator, used to pick out the sessions wanted

   Buffer                  - Used to store all the information of a paticular initiator

Return Value:

   Status - SRB Status for this request

--*/
{
    PMSiSCSI_InitiatorSessionInfo initiatorSessionInfo;
    PISCSI_SessionStaticInfo sessionStaticInfo;
    PISCSI_ConnectionStaticInfo connectionStaticInfo;
    ULONG sessionIndex;
    ULONG connectionIndex;
    ULONG inx, skip;
    PWMIString WMIformatString;
    UCHAR status;


    status = SRB_STATUS_SUCCESS;

    initiatorSessionInfo = (PMSiSCSI_InitiatorSessionInfo) Buffer;

    initiatorSessionInfo->UniqueAdapterId = (ULONGLONG) AdapterExtension;

    //
    // initialize Session count to 0
    //
    initiatorSessionInfo->SessionCount=0;
    sessionStaticInfo = &(initiatorSessionInfo->SessionsList[0]);

    if (ConnectionList == NULL) {

        DebugPrint((iScsiPrtDebugError,
            "Connectionlist NULL in iSpReadInitiatorSessionInfo\n"));

        ETWDebugPrint(iScsiLevelError,
            iScsiFlagWmi,
            "Connectionlist NULL in iSpReadInitiatorSessionInfo\n");

        return status;
    }

    inx = 0;
    while (inx < NumberOfSessions) {

        PISCSI_SESSION iScsiSession;
        PISCSI_CONNECTION iScsiConnection;

        iScsiSession = ConnectionList[inx].ISCSISession;

        if (iScsiSession == NULL) {

            inx++;

            continue;
        }

        sessionStaticInfo->UniqueSessionId = iScsiSession->SessionId;

        WMIformatString = (PWMIString)sessionStaticInfo->InitiatoriSCSIName;
        WMIformatString->Length = (MAX_INITIATOR_NAME) * sizeof(WCHAR);
        RtlCopyMemory(WMIformatString->Buffer,
                      iScsiSession->InitiatorName,
                      (MAX_INITIATOR_NAME * sizeof(WCHAR)));

        WMIformatString = (PWMIString)sessionStaticInfo->TargetiSCSIName;
        WMIformatString->Length = (MAX_TARGET_NAME) * sizeof(WCHAR);
        RtlCopyMemory(WMIformatString->Buffer,
                      iScsiSession->TargetName,
                      (MAX_TARGET_NAME * sizeof(WCHAR)));

        sessionStaticInfo->TSID = iScsiSession->TSIH[1] + (iScsiSession->TSIH[0] << 8);

        sessionStaticInfo->ISID[0] = ISID_TYPE_ENTERPRISE_NUMBER;
        sessionStaticInfo->ISID[1] = (MSFT_ENTERPRISE_NUMBER & 0xFF0000) >> 16;
        sessionStaticInfo->ISID[2] = (MSFT_ENTERPRISE_NUMBER & 0xFF00) >> 8;
        sessionStaticInfo->ISID[3] = MSFT_ENTERPRISE_NUMBER & 0xFF;
        sessionStaticInfo->ISID[4] = iScsiSession->InitiatorID[0];
        sessionStaticInfo->ISID[5] = iScsiSession->InitiatorID[1];

        sessionStaticInfo->InitialR2t  = iScsiSession->IScsiSessionParams.InitialR2T;
        sessionStaticInfo->ImmediateData = iScsiSession->IScsiSessionParams.ImmediateData;

        sessionStaticInfo->Type = iScsiSession->LogonType;

        //False indicates that data PDU Sequences may
        //be transferred in any order.  True indicates that data PDU
        //Sequences must be transferred using continuously increasing offsets,
        //except during error recovery.
        sessionStaticInfo->DataSequenceInOrder = TRUE;

        //False indicates that data PDUs within Sequences may be in any order.
        //True indicates that data PDUs within sequences must be at continuously
        //increasing addresses, with no gaps or overlay between PDUs.
        sessionStaticInfo->DataPduInOrder = TRUE;


        //The level of error recovery negotiated between the initiator and the target.
        //Higher numbers represent more detailed recovery schemes.
        sessionStaticInfo->ErrorRecoveryLevel = (UCHAR) iScsiSession->IScsiSessionParams.ErrorRecoveryLevel;

        sessionStaticInfo->MaxOutstandingR2t = 1;

        //The maximum length supported for unsolicited data
        //sent within this session, in units of 512 bytes.
        sessionStaticInfo->FirstBurstLength = iScsiSession->IScsiSessionParams.FirstBurstLength;

        //The maximum length supported for unsolicited data
        //sent within this session, in units of 512 bytes.
        sessionStaticInfo->MaxBurstLength = iScsiSession->IScsiSessionParams.MaxBurstLength;

        //The maximum number of connections that will be
        //allowed within this session
        sessionStaticInfo->MaxConnections = iScsiSession->IScsiSessionParams.MaxConnections;

        //
        // initialize connection count for this session to 0
        //
        sessionStaticInfo->ConnectionCount = 0;

        connectionStaticInfo = &(sessionStaticInfo->ConnectionsList[0]);

        for (connectionIndex = 0;
             connectionIndex < ConnectionList[inx].Count;
             connectionIndex++)
        {

            iScsiConnection = ConnectionList[inx].ISCSIConnection[connectionIndex];
            if (iScsiConnection != NULL) {

                ULONG socket;

                connectionStaticInfo->UniqueConnectionId = iScsiConnection->UniqueConnectionID;
                connectionStaticInfo->CID = iScsiConnection->ConnectionID[1] +
                                            (iScsiConnection->ConnectionID[0] << 8);

                connectionStaticInfo->HeaderIntegrity =
                    (UCHAR) iScsiConnection->IScsiConnectionParams.HeaderDigest;

                connectionStaticInfo->DataIntegrity =
                    (UCHAR) iScsiConnection->IScsiConnectionParams.DataDigest;

                //
                // The maximum data payload size supported for command
                // or data PDUs within this session, in units of 512 bytes.
                //
                connectionStaticInfo->MaxRecvDataSegmentLength =
                    iScsiConnection->IScsiConnectionParams.MaxRecvDataLength;

                connectionStaticInfo->AuthType = iScsiConnection->AuthType;


                //login          - The TCP connection has been established, but a valid iScsi
                //                    login response with the final bit set has not been sent or received.
                //full            - A valid iScsi login response with the final bit set
                //                   has been sent or received.
                //logout       - A valid iScsi logout command has been sent or received, but
                //                  the TCP connection has not yet been closed.
                connectionStaticInfo->State =  iScsiConnection->CurrentProtocolState;
                //NOTE: CurrentProtocol enum is different then State enum !?!?!?!


                //The transport protocol over which this connection instance is running.
                connectionStaticInfo->Protocol = TCP;  //not saved in deviceExtension yet

                //The Local Inet address used by this connection instance
                iSpCopyIPAddress(&connectionStaticInfo->LocalAddr,
                                 &iScsiConnection->LocalIPAddress,
                                 &socket);

                //
                // Local TCP port is stored in host byte order in LocalIPAddress.
                // Convert that to network byte order here.
                //
                connectionStaticInfo->LocalPort = htons(socket);

                connectionStaticInfo->LocalAddr.TextAddress[0] = MAX_ISCSI_TEXT_ADDRESS_LEN*sizeof(WCHAR);


                //The Remote Inet address used by this connection instance

                iSpCopyIPAddress(&connectionStaticInfo->RemoteAddr,
                                 &iScsiConnection->TargetIPAddress,
                                 &connectionStaticInfo->RemotePort);

                connectionStaticInfo->RemoteAddr.TextAddress[0] = MAX_ISCSI_TEXT_ADDRESS_LEN*sizeof(WCHAR);

                connectionStaticInfo->EstimatedThroughput = iScsiConnection->EstimatedThroughput.QuadPart;

                connectionStaticInfo->MaxDatagramSize = iScsiConnection->MaxDatagramSize;

                //increment the connectionStaticInfo pointer to the next 4 byte alligned address;
                (PUCHAR) connectionStaticInfo += ((sizeof(ISCSI_ConnectionStaticInfo)+7) & ~7);

                //The number of TCP connections that currently belong to this session
                sessionStaticInfo->ConnectionCount ++;
            }
        }

        //
        // Advance to next place to write session information
        //
        skip = FIELD_OFFSET(ISCSI_SessionStaticInfo,
                         ConnectionsList) +
               (ConnectionList[inx].Count *
                ((sizeof(ISCSI_ConnectionStaticInfo)+7) & ~7));

        skip = (skip +7) & ~7;

        (PUCHAR)sessionStaticInfo += skip;

        initiatorSessionInfo->SessionCount++;

        inx++;
    }

    return status;
}

UCHAR
iSpReadConnStatistics(
   IN PISCSI_ADAPTER_EXTENSION AdapterExtension,
   IN PISCSI_CONNECTION IScsiConnection,
   OUT PUCHAR               Buffer
   )
/*++

Routine Description:

   Read the iScsi connection statistics information into the OS buffer.

Arguments:

   IScsiConnection - iScsi miniport driver's connection data storage.

   Buffer - Buffer to hold iScsi connection statistics information.

Return Value:

   Status - SRB Status for this request

--*/
{
    PMSiSCSI_ConnectionStatistics ConnectStatistics;
    PWMIString WMIformatString;
    UCHAR status;


    status = SRB_STATUS_SUCCESS;

    ConnectStatistics = (PMSiSCSI_ConnectionStatistics)Buffer;
    RtlZeroMemory(Buffer, sizeof(MSiSCSI_ConnectionStatistics));

    WMIformatString = (PWMIString)ConnectStatistics->iSCSIName;
    WMIformatString->Length = MAX_INITIATOR_NAME * sizeof(WCHAR);
    RtlCopyMemory(WMIformatString->Buffer,
                  IScsiConnection->ISCSISession->InitiatorName,
                  (MAX_INITIATOR_NAME * sizeof(WCHAR)));

    ConnectStatistics->CID = (IScsiConnection->ConnectionID[0] << 8) +
                                         IScsiConnection->ConnectionID[1];

    //
    // A uniquely generated session ID used only internally.
    // Do not mix this with ISID.
    //
    if (IScsiConnection->ISCSISession != NULL) {

        ConnectStatistics->USID = IScsiConnection->ISCSISession->SessionId;
    }

    ConnectStatistics->UniqueAdapterId = (ULONGLONG) AdapterExtension;

    ConnectStatistics->BytesSent = IScsiConnection->BytesSent;
    ConnectStatistics->BytesReceived = IScsiConnection->BytesReceived;
    ConnectStatistics->PDUCommandsSent = IScsiConnection->PDUCommandsSent;
    ConnectStatistics->PDUResponsesReceived = IScsiConnection->PDUResponsesReceived;

    return status;
}

UCHAR
iSpReadSessionStatistics(
   IN PISCSI_ADAPTER_EXTENSION AdapterExtension,
   IN PISCSI_SESSION IScsiSession,
   IN PWMI_CONNECTION_LIST ConnectionList,
   OUT PUCHAR               Buffer
   )
/*++

Routine Description:

   Read the iScsi session statistics information into the OS buffer.

Arguments:

   IScsiSession - iScsi miniport driver's session data storage.

   Buffer - Buffer to hold iScsi connection statistics information.

Return Value:

   Status - SRB Status for this request

--*/
{
    PMSiSCSI_SessionStatistics SessionStatistics;
    PISCSI_CONNECTION connection;
    PWMIString WMIformatString;
    ULONG inx;
    UCHAR status;


    status = SRB_STATUS_SUCCESS;

    SessionStatistics = (PMSiSCSI_SessionStatistics)Buffer;
    RtlZeroMemory(Buffer, sizeof(MSiSCSI_SessionStatistics));

    if (IScsiSession != NULL) {

        WMIformatString = (PWMIString)SessionStatistics->iSCSIName;
        WMIformatString->Length = MAX_INITIATOR_NAME * sizeof(WCHAR);
        RtlCopyMemory(WMIformatString->Buffer,
                      IScsiSession->InitiatorName,
                      (MAX_INITIATOR_NAME * sizeof(WCHAR)));

        SessionStatistics->USID = IScsiSession->SessionId;

        SessionStatistics->UniqueAdapterId = (ULONGLONG) AdapterExtension;

        SessionStatistics->BytesSent = 0;
        SessionStatistics->BytesReceived = 0;
        SessionStatistics->PDUCommandsSent = 0;
        SessionStatistics->PDUResponsesReceived = 0;

        inx = 0;
        while (inx < ConnectionList->Count)
        {
            PISCSI_CONNECTION iScsiConnection;

            iScsiConnection = ConnectionList->ISCSIConnection[inx];
            if (iScsiConnection != NULL) {
                SessionStatistics->BytesSent += iScsiConnection->BytesSent;
                SessionStatistics->BytesReceived += iScsiConnection->BytesReceived;
                SessionStatistics->PDUCommandsSent += iScsiConnection->PDUCommandsSent;
                SessionStatistics->PDUResponsesReceived += iScsiConnection->PDUResponsesReceived;
            }

            inx++;
        }
    }

    return status;
}

UCHAR
iSpReadInitiatorLoginStatistics(
    IN PISCSI_ADAPTER_EXTENSION AdapterExtension,
    IN PWMI_CONNECTION_LIST ConnectionList,
    IN ULONG NumberOfSessions,
    OUT PUCHAR Buffer
   )
/*++

Routine Description:

   Read the iScsi Initiator Node statistics information into the OS buffer.

Arguments:

   SessionListHead -  pointer to the first session we have got a remove lock for

   SessionListTail   -  pointer ot the last session we have got a remove lock for

   InitiatorName      -  Name of initiator, used to pick out the sessions wanted

   Buffer                  - Used to store all the information of a paticular initiator

Return Value:

   Status - SRB Status for this request

--*/
{
    PMSiSCSI_InitiatorLoginStatistics initiatorLoginStatistics;
    UCHAR status;
    ULONG inx;


    status = SRB_STATUS_SUCCESS;

    initiatorLoginStatistics = (PMSiSCSI_InitiatorLoginStatistics) Buffer;

    initiatorLoginStatistics->UniqueAdapterId = (ULONGLONG) AdapterExtension;

    initiatorLoginStatistics->LogoutNormals = AdapterExtension->LogoutNormals;

    initiatorLoginStatistics->LogoutOtherCodes = AdapterExtension->LogoutOtherRsps;

    inx = 0;
    while (inx < NumberOfSessions) {

        PISCSI_SESSION iScsiSession;
        PISCSI_CONNECTION iScsiConnection;

        if ((ConnectionList != NULL) && (ConnectionList[inx].Count > 0)) {

            iScsiConnection = ConnectionList[inx].ISCSIConnection[0];
            NT_ASSERT(iScsiConnection != NULL);

            iScsiSession = iScsiConnection->ISCSISession;

            if (iScsiSession != NULL) {

                initiatorLoginStatistics->LoginAcceptRsps += iScsiSession->LoginAcceptRsps;

                initiatorLoginStatistics->LoginOtherFailRsps += iScsiSession->LoginOtherFailRsps;

                initiatorLoginStatistics->LoginRedirectRsps += iScsiSession->LoginRedirectRsps;

                initiatorLoginStatistics->LoginAuthFailRsps += iScsiSession->LoginAuthFailRsps;

                initiatorLoginStatistics->LoginAuthenticateFails += iScsiSession->LoginAuthenticateFails;

                initiatorLoginStatistics->LoginNegotiateFails += iScsiSession->LoginNegotiateFails;

                initiatorLoginStatistics->LoginFailures += iScsiSession->LoginFailures;

            }
        }

        inx++;
    }

    return status;
}


VOID
iSpCompleteWmiRequest(
    PISCSI_ADAPTER_EXTENSION AdapterExtension,
    PSCSI_WMI_REQUEST_BLOCK Srb,
    PSCSIWMI_REQUEST_CONTEXT DispatchContext,
    UCHAR Status,
    ULONG SizeNeeded
    )
/*+++

Routine Description:

    This routine calls iScsiPrt to notify that a request
    is completed.

Arguements:

    AdapterExtension - Adapater extension for the initiator

    Srb - The request being completed

    DispatchContext - WMI request context.

    Status - SRB Status for this request

    SizeNeeded - Buffer size needed to process this request

Return Value:

    None
--*/
{
    //
    // Complete the request if the status is NOT pending or NOT
    // already completed within the callback.
    //
    if (Status != SRB_STATUS_PENDING) {

        //
        // Request completed successfully or there was an error.
        //
        ScsiPortWmiPostProcess(DispatchContext,
                               Status,
                               SizeNeeded);

        Srb->SrbStatus = ScsiPortWmiGetReturnStatus(DispatchContext);
        Srb->DataTransferLength = ScsiPortWmiGetReturnSize(DispatchContext);

        //
        // Adapter ready for next request.
        //
        ScsiPortNotification(RequestComplete, AdapterExtension, Srb);
    }
}

UCHAR
iSpGetDynamicConnectionInstanceName(
    IN PISCSI_CONNECTION IScsiConnection,
    IN OUT PWMIString InstanceName
    )
/*++

Routine Description:

   Get the instanceName for the specified connection.  Instance names are formatted as
    follows:  "targetname_#:#"  where the first # is the display session ID, and the second
    # is the CID.

Arguments:

   IScsiConnection - iScsi miniport driver's connection data storage.

   InstanceName - A pointer to WMIString, on return contains the dynamic instance name for the
                           specified connection.

Return Value:

   UCHAR - SRB Status for this request

--*/
{

    WCHAR uniCodeString[MAX_UNICODE_STR_LENGTH];
    PWCHAR currentPos;
    ULONG InstanceNameLength;
    PWMIString WMIformatString;
    KIRQL oldIrql;
    ULONG displaySessionID;
    PISCSI_SESSION sessionListHead;
    PISCSI_SESSION sessionListTail;
    PISCSI_SESSION iScsiSession;
    PISCSI_ADAPTER_EXTENSION adapterExtension;
    NTSTATUS rtlStatus;
    UCHAR status;
    USHORT convertedLength;
    USHORT maxBufferSize;
    UCHAR numString[MAX_UNICODE_STR_LENGTH];


    status = SRB_STATUS_SUCCESS;

    RtlZeroMemory(uniCodeString, sizeof(uniCodeString));

    maxBufferSize = sizeof(uniCodeString) / sizeof(WCHAR);

    rtlStatus = RtlStringCchCopyNW(uniCodeString, maxBufferSize,
                                   IScsiConnection->ISCSISession->TargetName,
                                   wcslen(IScsiConnection->ISCSISession->TargetName));
    if (rtlStatus != STATUS_SUCCESS) {
        return SRB_STATUS_ERROR;
    }

    convertedLength = (USHORT)wcslen(IScsiConnection->ISCSISession->TargetName);

    (PUCHAR)currentPos = (PUCHAR)uniCodeString + convertedLength*sizeof(WCHAR);

    maxBufferSize = (sizeof(uniCodeString)/sizeof(WCHAR)) - convertedLength;

    if (currentPos > uniCodeString + sizeof(uniCodeString)/sizeof(WCHAR)) {
        return SRB_STATUS_ERROR;
    }

    rtlStatus = RtlStringCchCopyW(currentPos, maxBufferSize, L"_");
    if (rtlStatus != STATUS_SUCCESS) {
        return SRB_STATUS_ERROR;
    }
    (PUCHAR)currentPos += sizeof(WCHAR);
    maxBufferSize--;

    if (currentPos > uniCodeString + sizeof(uniCodeString)/sizeof(WCHAR)) {
        return SRB_STATUS_ERROR;
    }

    displaySessionID = IScsiConnection->ISCSISession->DisplaySessionID;

    RtlZeroMemory(numString, sizeof(numString));
     _itoa(displaySessionID, numString, 10);

    convertedLength = AsciiStringToWideCharString(
                               currentPos, 
                               numString, 
                               maxBufferSize); 
    
    //
    // AsciiStringToWideCharString returns the number of characters including the terminating NULL.
    //
    maxBufferSize = maxBufferSize - (convertedLength - 1);
    (PUCHAR)currentPos += ((convertedLength -1 )* sizeof(WCHAR));

    if (currentPos > uniCodeString + sizeof(uniCodeString)/sizeof(WCHAR)) {
        return SRB_STATUS_ERROR;
    }

    rtlStatus = RtlStringCchCopyW(currentPos, maxBufferSize, L":");
    if (rtlStatus != STATUS_SUCCESS) {
        return SRB_STATUS_ERROR;
    }
    (PUCHAR)currentPos += sizeof(WCHAR);
    maxBufferSize--;

    if (currentPos > uniCodeString + sizeof(uniCodeString)/sizeof(WCHAR)) {
        return SRB_STATUS_ERROR;
    }

    RtlZeroMemory(numString, sizeof(numString));
    _itoa((IScsiConnection->ConnectionID[0]<<8) + IScsiConnection->ConnectionID[1], numString, 10);
    convertedLength = AsciiStringToWideCharString(currentPos,
                                                  numString,
                                                  (USHORT)maxBufferSize);

    //
    // AsciiStringToWideCharString returns the number of characters including the terminating NULL.
    //
    (PUCHAR)currentPos += ((convertedLength - 1)* sizeof(WCHAR));

    if (currentPos > uniCodeString + sizeof(uniCodeString)/sizeof(WCHAR)) {
        return SRB_STATUS_ERROR;
    }

    InstanceNameLength = (ULONG)((PUCHAR)currentPos - (PUCHAR)uniCodeString);

    InstanceName->Length = (USHORT)InstanceNameLength;

    RtlStringCchCopyW(InstanceName->Buffer, InstanceName->Length, uniCodeString);

    return status;

}

UCHAR
iSpGetDynamicSessionInstanceName(
    IN PISCSI_SESSION IScsiSession,
    IN OUT PWMIString InstanceName
    )
/*++

Routine Description:

   Get the instanceName for the specified session.  Instance names are formatted as
    follows:  "targetname_#"  where the  # is the display session ID

Arguments:

   IScsiSession - iScsi miniport driver's session data storage.

   InstanceName - A pointer to WMIString, on return contains the dynamic instance name for the
                           specified connection.

Return Value:

   UCHAR - SRB Status for this request

--*/
{

    WCHAR uniCodeString[MAX_UNICODE_STR_LENGTH + 1];
    PWCHAR currentPos;
    ULONG InstanceNameLength;
    PWMIString WMIformatString;
    KIRQL oldIrql;
    ULONG displaySessionID;
    PISCSI_SESSION sessionListHead;
    PISCSI_SESSION sessionListTail;
    PISCSI_SESSION iScsiSession;
    PISCSI_ADAPTER_EXTENSION adapterExtension;
    NTSTATUS rtlStatus;
    UCHAR status = SRB_STATUS_SUCCESS;
    USHORT convertedLength;
    USHORT maxBufferSize;
    UCHAR numString[MAX_UNICODE_STR_LENGTH];


    RtlZeroMemory(uniCodeString, sizeof(uniCodeString));

    convertedLength = (USHORT) wcslen(IScsiSession->TargetName);

    rtlStatus = RtlStringCchCopyNW(uniCodeString, MAX_UNICODE_STR_LENGTH,
                                   IScsiSession->TargetName,
                                   convertedLength);

    if (rtlStatus != STATUS_SUCCESS) {
        return SRB_STATUS_ERROR;
    }
    
    (PUCHAR)currentPos = (PUCHAR)uniCodeString + convertedLength*sizeof(WCHAR);

    maxBufferSize = (sizeof(uniCodeString)/sizeof(WCHAR)) - convertedLength;

    if (currentPos > uniCodeString + sizeof(uniCodeString)/sizeof(WCHAR)) {
        return SRB_STATUS_ERROR;
    }

    rtlStatus = RtlStringCchCopyW(currentPos, maxBufferSize, L"_");
    if (rtlStatus != STATUS_SUCCESS) {
        return SRB_STATUS_ERROR;
    }
    (PUCHAR)currentPos += sizeof(WCHAR);
    maxBufferSize--;

    if (currentPos > uniCodeString + sizeof(uniCodeString)/sizeof(WCHAR)) {
        return SRB_STATUS_ERROR;
    }


    displaySessionID = IScsiSession->DisplaySessionID;

    RtlZeroMemory(numString, sizeof(numString));
    _itoa(displaySessionID, numString, 10);
    convertedLength = AsciiStringToWideCharString(currentPos,
                                                  numString,
                                                  maxBufferSize);

    //
    // AsciiStringToWideCharString returns the number of characters including the terminating NULL.
    //
    (PUCHAR)currentPos += ((convertedLength -1) * sizeof(WCHAR));

    if (currentPos > uniCodeString + sizeof(uniCodeString)/sizeof(WCHAR)) {
        return SRB_STATUS_ERROR;
    }

    InstanceNameLength = (ULONG)((PUCHAR)currentPos - (PUCHAR)uniCodeString);

    if(InstanceNameLength > MAX_UNICODE_STR_LENGTH)
    {
        status = SRB_STATUS_ERROR;
    } else {

        rtlStatus = RtlStringCchCopyNW(
                        InstanceName->Buffer,
                        MAX_UNICODE_STR_LENGTH,
                        uniCodeString,
                        InstanceNameLength);

        if(NT_SUCCESS(rtlStatus) == FALSE)
        {
            status = SRB_STATUS_ERROR;
        } else {
            status = SRB_STATUS_SUCCESS;
        }
    }

    if(status != SRB_STATUS_SUCCESS)
    {
        InstanceName->Length = 0;
    } else {
        InstanceName->Length = (USHORT)InstanceNameLength;
    }

    return status;
}


VOID
iScsiFireAdapterEvent(
    PISCSI_ADAPTER_EXTENSION AdapterExtension,
    ISCSI_ADAPTER_EVENT_CODE EventCode
    )
{
    ScsiPortWmiFireAdapterEvent(AdapterExtension,
                    &iScsiAdapterEventGuid,
                    0,
                    sizeof(ULONG),
                    &EventCode);
}


UCHAR
iSpQueryLBPolicy(
    IN     PISCSI_ADAPTER_EXTENSION AdapterExtension,
    IN OUT PULONG InstanceLengthArray,
    IN     ULONG  BufferAvail,
    OUT    PUCHAR Buffer,
    OUT    PULONG SizeNeeded
    )
{
    PWMI_CONNECTION_LIST connectionList;
    ULONG size, sessionInx;
    ULONG numberOfSessions, numberOfConnections;
    UCHAR status = SRB_STATUS_SUCCESS;;


    *SizeNeeded = 0;

    connectionList = iSpGetConnectionList(AdapterExtension,
                                          &numberOfSessions,
                                          &numberOfConnections);

    DebugPrint((iScsiPrtDebugInfo,
        "Number of Sessions %d, Number of Connections %d\n",
        numberOfSessions,
        numberOfConnections));

    ETWDebugPrint(iScsiLevelInfo,
        iScsiFlagWmi,
        "Number of Sessions %d, Number of Connections %d\n",
        numberOfSessions,
        numberOfConnections);

    //
    // Determine the size of buffer needed to return LB policy settings
    // for all the sessions on this adapter.
    //

    //
    // First account for the fixed part of MSiSCSI_Supported_LB_Policies
    //
    size = FIELD_OFFSET(MSiSCSI_QueryLBPolicy,
                        LoadBalancePolicies);

    if (connectionList == NULL) {

        if (BufferAvail >= size) {
            PMSiSCSI_QueryLBPolicy queryLBPolicy;

            queryLBPolicy = (PMSiSCSI_QueryLBPolicy) Buffer;
            queryLBPolicy->UniqueAdapterId = (ULONGLONG) AdapterExtension;
            queryLBPolicy->SessionCount = 0;
            *SizeNeeded = size;

            return SRB_STATUS_SUCCESS;
        } else {

            *SizeNeeded = size;

            return SRB_STATUS_SUCCESS;
        }
    }

    //
    // Loop over all sessions and account for the space needed for each
    // session plus all of the connections within each session
    //
    sessionInx = 0;
    while (sessionInx < numberOfSessions) {

        //
        // Since the session structure needs to be 8 bytes aligned, we
        // pad out to 8 bytes
        //
        size = (size + 7) & ~7;

        //
        // Add the fixed size needed for the session structure
        //
        size += FIELD_OFFSET(ISCSI_Supported_LB_Policies,
                             iSCSI_Paths);

        //
        // Account for the size of all connection structures, being
        // sure that the connection structure is padded out to 8 bytes
        // in order to maintain 8 byte alignment
        //
        size += connectionList[sessionInx].Count *
                ((sizeof(ISCSI_Path) + 7) & ~7);

        sessionInx++;
    }

    *SizeNeeded = size;

    if (size <= BufferAvail) {

        //
        // There is enough space to build the LB policy info data
        // for all the sessions on this adapter.
        //
        RtlZeroMemory(Buffer, size);

        status = iSpReadLBPolicyInfo(AdapterExtension,
                                     connectionList,
                                     numberOfSessions,
                                     (PMSiSCSI_QueryLBPolicy) Buffer);

    } else {

        //
        // There is not enough space to build the LB policy info data
        // for all the sessions on this adapter.
        //
        status = SRB_STATUS_DATA_OVERRUN;
    }

    *InstanceLengthArray = *SizeNeeded;

    iSpReleaseConnectionReferences(connectionList,
                                   numberOfSessions);


    return status;
}

UCHAR
iSpReadLBPolicyInfo(
    IN  PISCSI_ADAPTER_EXTENSION AdapterExtension,
    IN  PWMI_CONNECTION_LIST ConnectionList,
    IN  ULONG NumberOfSessions,
    OUT PMSiSCSI_QueryLBPolicy QueryLBPolicy
    )
{
    PISCSI_Supported_LB_Policies supportedLBPolicies;
    PISCSI_Path iScsiPath;
    PISCSI_SESSION iScsiSession;
    PISCSI_CONNECTION iScsiConnection;
    ULONG sessionInx;
    ULONG connectionInx;
    ULONG skip;
    UCHAR status = SRB_STATUS_SUCCESS;

    QueryLBPolicy->UniqueAdapterId = (ULONGLONG) AdapterExtension;

    QueryLBPolicy->SessionCount = 0;

    supportedLBPolicies = QueryLBPolicy->LoadBalancePolicies;

    sessionInx = 0;
    while (sessionInx < NumberOfSessions) {

        iScsiSession = ConnectionList[sessionInx].ISCSISession;

        supportedLBPolicies->UniqueSessionId = iScsiSession->SessionId;

        supportedLBPolicies->LoadBalancePolicy = iScsiSession->LoadBalancePolicy;

        supportedLBPolicies->iSCSI_PathCount = 0;
        connectionInx = 0;
        while (connectionInx < ConnectionList[sessionInx].Count) {

            iScsiConnection = ConnectionList[sessionInx].ISCSIConnection[connectionInx];
            if (iScsiConnection != NULL) {

                iScsiPath = &(supportedLBPolicies->iSCSI_Paths[connectionInx]);

                iScsiPath->UniqueConnectionId = (ULONGLONG) iScsiConnection->UniqueConnectionID;

                iScsiPath->EstimatedLinkSpeed = iScsiConnection->EstimatedThroughput.QuadPart;

                iScsiPath->PathWeight = iScsiConnection->PathWeight;

                iScsiPath->PrimaryPath = iScsiConnection->CurrentPathStatus;

                iScsiPath->TCPOffLoadAvailable = 0;

                switch (iScsiConnection->ConnectionState) {
                    case ConnectionStateConnected : {
                        iScsiPath->ConnectionStatus = CONNECTION_STATE_CONNECTED;
                        break;
                    }

                    case ConnectionStateDisconnected :
                    case ConnectionStateStopping: {
                        iScsiPath->ConnectionStatus = CONNECTION_STATE_DISCONNECTED;
                        break;
                    }

                    case ConnectionStateConnecting : {
                        iScsiPath->ConnectionStatus = CONNECTION_STATE_RECONNECTING;
                        break;
                    }

                    default : {
                        iScsiPath->ConnectionStatus = CONNECTION_STATE_DISCONNECTED;
                        break;
                    }
                }

                supportedLBPolicies->iSCSI_PathCount++;
            }

            connectionInx++;
        }

        //
        // Advance to next place to write session information
        //
        skip = FIELD_OFFSET(ISCSI_Supported_LB_Policies, iSCSI_Paths) +
               (ConnectionList[sessionInx].Count * ((sizeof(ISCSI_Path) + 7) & ~7));

        skip = (skip + 7) & ~7;

        (PUCHAR)supportedLBPolicies += skip;

        QueryLBPolicy->SessionCount++;

        sessionInx++;
    }

    return status;
}

UCHAR
iSpSetLoadBalancePolicy(
    IN  PISCSI_ADAPTER_EXTENSION AdapterExtension,
    IN  PSCSI_WMI_REQUEST_BLOCK Srb,
    IN  PUCHAR Buffer,
    IN  ULONG  InBufferSize
    )
{
    PSetLoadBalancePolicy_IN  setLBPolicyIN;
    PSetLoadBalancePolicy_OUT setLBPolicyOUT;
    PISCSI_Supported_LB_Policies supportedLBPolicy;
    PISCSI_SESSION iScsiSession;
    PPERSISTENT_TARGET_LB_POLICY LBPolicy = NULL;
    PLB_POLICY_DATA LBPolicyData;
    ULONG iScsiStatus;
    ULONG inx;
    BOOLEAN persistLBPolicy = FALSE;
    KIRQL oldIrql;
    NTSTATUS ntStatus;

    setLBPolicyIN = (PSetLoadBalancePolicy_IN) Buffer;

    setLBPolicyOUT = (PSetLoadBalancePolicy_OUT) Buffer;

    iScsiStatus = iSpValidateLoadBalancePolicy(setLBPolicyIN,
                                               InBufferSize);
    if (iScsiStatus != STATUS_SUCCESS) {

        setLBPolicyOUT->Status = iScsiStatus;

        return SRB_STATUS_SUCCESS;
    }

    supportedLBPolicy = &(setLBPolicyIN->LoadBalancePolicies);

    iScsiSession = iSpGetIScsiSession(AdapterExtension,
                                      supportedLBPolicy->UniqueSessionId,
                                      Srb);
    if (iScsiSession == NULL) {

        DebugPrint((iScsiPrtDebugWarning,
            "Invalid session id given %I64x for SetLBPolicy\n",
            supportedLBPolicy->UniqueSessionId));

        ETWDebugPrint(iScsiLevelWarning,
            iScsiFlagWmi,
            "Invalid session id given %I64x for SetLBPolicy\n",
            supportedLBPolicy->UniqueSessionId);

        setLBPolicyOUT->Status = ISDSC_INVALID_SESSION_ID;

        return SRB_STATUS_SUCCESS;
    }

    KeAcquireSpinLock(&iScsiSession->SessionLock, &oldIrql);

    iScsiStatus = iSpValidateConnectionIds(iScsiSession, supportedLBPolicy);

    if (iScsiStatus == STATUS_SUCCESS) {

        switch (supportedLBPolicy->LoadBalancePolicy) {
            case MSiSCSI_LB_FAILOVER: {

                iScsiStatus = iSpSetLBFailOverOnly(iScsiSession,
                                                   supportedLBPolicy);

                break;
            }

            case MSiSCSI_LB_DYN_LEAST_QUEUE_DEPTH:
            case MSiSCSI_LB_WEIGHTED_PATHS: {

                iScsiStatus = iSpSetLB_LQD_WP(iScsiSession, supportedLBPolicy);

                break;
            }

            case MSiSCSI_LB_ROUND_ROBIN:
            case MSiSCSI_LB_ROUND_ROBIN_WITH_SUBSET: {

                iScsiStatus = iSpSetLBRoundRobinSubset(iScsiSession,
                                                       supportedLBPolicy);

                break;
            }

            default: {

                //
                // Should never get here since the input policy has already
                // been validated in iSpValidateLoadBalancePolicy.
                //
                iScsiStatus = ISDSC_INVALID_LOAD_BALANCE_POLICY;
            }
        }
    }

    //
    // Check if we need to persist the new LB policy
    //
    if ((iScsiStatus == STATUS_SUCCESS) &&
        (wcslen(iScsiSession->TargetKeyNameSuffix) > 0)) {

        ULONG sizeNeeded = 0;
        ULONG tempUlong;
        BOOLEAN overFlow = TRUE;

        if (ULongMult(sizeof(LB_POLICY_DATA),
                      (iScsiSession->NumberOfConnections - 1),
                      &tempUlong) == S_OK) {

            if (ULongAdd(sizeof(PERSISTENT_TARGET_LB_POLICY),
                         tempUlong, &sizeNeeded) == S_OK) {

                overFlow = FALSE;
            }
        }

        //
        // This is a persistent target. Need to save the new LB policy
        // in the registry for this persistent target.
        //
        if (!overFlow) {

            LBPolicy = iSpAllocatePool(NonPagedPool,
                                       sizeNeeded,
                                       ISCSI_TAG_LB_POLICY);
            if (LBPolicy != NULL) {

                PISCSI_Path iScsiPath;
                PISCSI_CONNECTION iScsiConnection;

                persistLBPolicy = TRUE;

                RtlStringCchCopyW(LBPolicy->TargetKeyName,
                                  MAX_TARGET_NAME + TARGET_KEYNAME_SUFFIX_SIZE + 1,
                                  iScsiSession->TargetName);

                RtlStringCchCatW(LBPolicy->TargetKeyName,
                                 MAX_TARGET_NAME + TARGET_KEYNAME_SUFFIX_SIZE + 1,
                                 iScsiSession->TargetKeyNameSuffix);

                LBPolicy->LoadBalancePolicy = supportedLBPolicy->LoadBalancePolicy;

                LBPolicy->ConnectionCount = iScsiSession->NumberOfConnections;

                for (inx = 0; inx < supportedLBPolicy->iSCSI_PathCount; inx++) {

                    iScsiPath = &(supportedLBPolicy->iSCSI_Paths[inx]);

                    iScsiConnection = (PISCSI_CONNECTION) iScsiPath->UniqueConnectionId;

                    LBPolicyData = &(LBPolicy->PolicyData[inx]);

                    LBPolicyData->ConnectionNumber = iScsiConnection->ConnectionNumber;

                    LBPolicyData->PathWeight = iScsiConnection->PathWeight;

                    LBPolicyData->PrimaryPath = iScsiConnection->CurrentPathStatus;
                }
            } else {

                DebugPrint((iScsiPrtDebugError,
                    "Failed to allocate memory for persisting LB policy\n"));

                ETWDebugPrint(iScsiLevelError,
                    iScsiFlagWmi,
                    "Failed to allocate memory for persisting LB policy\n");
            }
        }
    }

    KeReleaseSpinLock(&iScsiSession->SessionLock, oldIrql);

    if (persistLBPolicy) {

        NT_ASSERT(LBPolicy != NULL);

        ntStatus = iSpPersisteLBPolicy(AdapterExtension, LBPolicy);

        if(NT_SUCCESS(ntStatus) == FALSE)
        {
            iScsiStatus = ISDSC_FAILURE_TO_PERSIST_LB_POLICY;
        }

        ExFreePool(LBPolicy);
    }

    //
    // Release the reference taken in iSpGetIScsiSession
    //
    IoReleaseRemoveLock(iScsiSession->RemoveLock, REMOVELOCK_TAG(Srb));

    setLBPolicyOUT->Status = iScsiStatus;

    return SRB_STATUS_SUCCESS;
}

ULONG
iSpValidateLoadBalancePolicy(
    IN  PSetLoadBalancePolicy_IN SetLBPolicyIN,
    IN  ULONG                    InBufferSize
    )
{
    PISCSI_Supported_LB_Policies supportedLBPolicy;
    PISCSI_Path iScsiPath0;
    PISCSI_Path iScsiPath1;
    ULONG iScsiStatus = STATUS_SUCCESS;
    ULONG sizeNeeded;
    ULONG inx, jnx;
    KIRQL oldIrql;

    sizeNeeded = FIELD_OFFSET(ISCSI_Supported_LB_Policies, iSCSI_Paths);
    if (InBufferSize < sizeNeeded) {

        DebugPrint((iScsiPrtDebugError,
            "Input buffer too small for SetLBPolicy. Expected %d, Given %d\n",
            sizeNeeded,
            InBufferSize));

        ETWDebugPrint(iScsiLevelError,
            iScsiFlagWmi,
            "Input buffer too small for SetLBPolicy. Expected %d, Given %d\n",
            sizeNeeded,
            InBufferSize);

        return ISDSC_BUFFER_TOO_SMALL;
    }

    supportedLBPolicy = &(SetLBPolicyIN->LoadBalancePolicies);

    sizeNeeded += supportedLBPolicy->iSCSI_PathCount *
                  ((sizeof(ISCSI_Path) + 7) & ~7);

    if (InBufferSize < sizeNeeded) {

        DebugPrint((iScsiPrtDebugError,
            "Input buffer too small for SetLBPolicy. Expected %d, Given %d\n",
            sizeNeeded,
            InBufferSize));

        ETWDebugPrint(iScsiLevelError,
            iScsiFlagWmi,
            "Input buffer too small for SetLBPolicy. Expected %d, Given %d\n",
            sizeNeeded,
            InBufferSize);

        return ISDSC_BUFFER_TOO_SMALL;
    }

    if ((supportedLBPolicy->LoadBalancePolicy < MSiSCSI_LB_FAILOVER) ||
        (supportedLBPolicy->LoadBalancePolicy > MSiSCSI_LB_WEIGHTED_PATHS)){

        DebugPrint((iScsiPrtDebugError,
            "Invalid Load Balance Policy %d\n",
            supportedLBPolicy->LoadBalancePolicy));

        ETWDebugPrint(iScsiLevelError,
            iScsiFlagWmi,
            "Invalid Load Balance Policy %d\n",
            supportedLBPolicy->LoadBalancePolicy);

        return ISDSC_INVALID_LOAD_BALANCE_POLICY;
    }

    //
    // Make sure user did not provide duplicate path ids
    //
    for (inx = 0; inx < supportedLBPolicy->iSCSI_PathCount; inx++) {

        iScsiPath0 = &(supportedLBPolicy->iSCSI_Paths[inx]);

        for (jnx = 0; jnx < supportedLBPolicy->iSCSI_PathCount; jnx++) {

            iScsiPath1 = &(supportedLBPolicy->iSCSI_Paths[jnx]);

            if ((inx != jnx) &&
                (iScsiPath0->UniqueConnectionId == iScsiPath1->UniqueConnectionId)) {

                DebugPrint((iScsiPrtDebugError,
                    "Duplicate path ids at %d and %d\n",
                    inx,
                    jnx));

                ETWDebugPrint(iScsiLevelError,
                    iScsiFlagWmi,
                    "Duplicate path ids at %d and %d\n",
                    inx,
                    jnx);

                iScsiStatus = ISDSC_DUPLICATE_PATH_SPECIFIED;

                break;
            }

            jnx++;
        }

        if (iScsiStatus != STATUS_SUCCESS) {
            break;
        }

        inx++;
    }

    return iScsiStatus;
}

ULONG
iSpValidateConnectionIds(
    IN PISCSI_SESSION IScsiSession,
    IN PISCSI_Supported_LB_Policies SupportedLBPolicy
    )
/*+++

  Note: This routine should be called with IScsiSession->SessionLock held

--*/
{
    PISCSI_Path iScsiPath;
    PISCSI_CONNECTION iScsiConnection;
    PLIST_ENTRY connectionEntry;
    ULONG iScsiStatus = STATUS_SUCCESS;
    ULONG inx;
    BOOLEAN found;

    if (IScsiSession->NumberOfConnections != SupportedLBPolicy->iSCSI_PathCount) {

        DebugPrint((iScsiPrtDebugError,
            "Number of connections %d does not match path count %d\n",
            IScsiSession->NumberOfConnections,
            SupportedLBPolicy->iSCSI_PathCount));

        ETWDebugPrint(iScsiLevelError,
            iScsiFlagWmi,
            "Number of connections %d does not match path count %d\n",
            IScsiSession->NumberOfConnections,
            SupportedLBPolicy->iSCSI_PathCount);

        return ISDSC_PATH_COUNT_MISMATCH;
    }

    for (inx = 0; inx < SupportedLBPolicy->iSCSI_PathCount; inx++) {

        iScsiPath = &(SupportedLBPolicy->iSCSI_Paths[inx]);

        found = FALSE;

        connectionEntry = IScsiSession->ConnectionList.Flink;
        while (connectionEntry != &IScsiSession->ConnectionList) {

            iScsiConnection = CONTAINING_RECORD(connectionEntry,
                                                ISCSI_CONNECTION,
                                                NextConnection);

            connectionEntry = connectionEntry->Flink;

            //
            // Path Id that is returned in QueryLBPolicy is UniqueConnectionID
            //
            if (iScsiConnection->UniqueConnectionID == iScsiPath->UniqueConnectionId) {

                //
                // Set the pointer to iscsi_connection in UniqueConnectionId. This will
                // be used in SetLBPolicy routine to set the load balance policy.
                //
                iScsiPath->UniqueConnectionId = (ULONGLONG) iScsiConnection;

                found = TRUE;

                break;
            }
        }

        if (!found) {

            DebugPrint((iScsiPrtDebugError,
                "Path Id %I64x not found\n",
                iScsiPath->UniqueConnectionId));

            ETWDebugPrint(iScsiLevelError,
                iScsiFlagWmi,
                "Path Id %I64x not found\n",
                iScsiPath->UniqueConnectionId);

            iScsiStatus = ISDSC_INVALID_PATH_ID;

            break;
        }
    }

    return iScsiStatus;
}

ULONG
iSpSetLBFailOverOnly(
    IN PISCSI_SESSION IScsiSession,
    IN PISCSI_Supported_LB_Policies SupportedLBPolicy
    )
/*+++

  Note: This routine should be called with IScsiSession->SessionLock held

--*/
{
    PISCSI_Path iScsiPath;
    PISCSI_CONNECTION iScsiConnection;
    ULONG inx;
    ULONG iScsiStatus = STATUS_SUCCESS;
    BOOLEAN activeFound = FALSE;

    //
    // Save the current value of path status.
    //
    iSpSavePrimaryPath(SupportedLBPolicy);

    for (inx = 0; inx < SupportedLBPolicy->iSCSI_PathCount; inx++) {

        iScsiPath = &(SupportedLBPolicy->iSCSI_Paths[inx]);

        iScsiConnection = (PISCSI_CONNECTION) iScsiPath->UniqueConnectionId;

        if (iScsiPath->PrimaryPath) {

            //
            // Primary path specified. Make sure one hasn't been
            // specified earlier since only one primary path is
            // allowed for FailOverOnly policy.
            //
            if (!activeFound) {

                DebugPrint((iScsiPrtDebugInfo,
                    "%p specified as Primary Path\n",
                    iScsiConnection));

                ETWDebugPrint(iScsiLevelInfo,
                    iScsiFlagWmi,
                    "%p specified as Primary Path\n",
                    iScsiConnection);

                iScsiConnection->CurrentPathStatus = TRUE;

                activeFound = TRUE;
            } else {

                DebugPrint((iScsiPrtDebugError,
                    "Multiple primary paths given for LBFailOverOnly\n"));

                ETWDebugPrint(iScsiLevelError,
                    iScsiFlagWmi,
                    "Multiple primary paths given for LBFailOverOnly\n");

                iScsiStatus = ISDSC_MULTIPLE_PRIMARY_PATHS_SPECIFIED;
            }
        } else {

            //
            // This is a standby path
            //
            iScsiConnection->CurrentPathStatus = FALSE;
        }
    }

    if (!activeFound) {

        DebugPrint((iScsiPrtDebugError,
            "No active path given for FO Only\n"));

        ETWDebugPrint(iScsiLevelError,
            iScsiFlagWmi,
            "No active path given for FO Only\n");

        iScsiStatus = ISDSC_NO_PRIMARY_PATH_SPECIFIED;
    }

    if (NT_SUCCESS(iScsiStatus)) {

        DebugPrint((iScsiPrtDebugInfo,
            "FailOverOnly set successfully for %p\n",
            IScsiSession));

        ETWDebugPrint(iScsiLevelInfo,
            iScsiFlagWmi,
            "FailOverOnly set successfully for %p\n",
            IScsiSession);

        IScsiSession->LoadBalancePolicy = LB_FAIL_OVER_ONLY;
    } else {

        //
        // Failed to set user given LB settings. Restore the value of
        // path status from the saved value.
        //
        iSpRestorePrimaryPath(SupportedLBPolicy);
    }

    return iScsiStatus;
}

ULONG
iSpSetLB_LQD_WP(
    IN PISCSI_SESSION IScsiSession,
    IN PISCSI_Supported_LB_Policies SupportedLBPolicy
    )
/*+++

  Note: This routine should be called with IScsiSession->SessionLock held

--*/
{
    PISCSI_Path iScsiPath;
    PISCSI_CONNECTION iScsiConnection;
    ULONG inx;

    for (inx = 0; inx < SupportedLBPolicy->iSCSI_PathCount; inx++) {

        iScsiPath = &(SupportedLBPolicy->iSCSI_Paths[inx]);

        iScsiConnection = (PISCSI_CONNECTION) iScsiPath->UniqueConnectionId;

        iScsiConnection->CurrentPathStatus = TRUE;

        if (SupportedLBPolicy->LoadBalancePolicy == LB_LEAST_WEIGHT_PATH) {
            iScsiConnection->PathWeight = iScsiPath->PathWeight;
        }
    }

    IScsiSession->LoadBalancePolicy = SupportedLBPolicy->LoadBalancePolicy;

    return STATUS_SUCCESS;
}

ULONG
iSpSetLBRoundRobinSubset(
    IN PISCSI_SESSION IScsiSession,
    IN PISCSI_Supported_LB_Policies SupportedLBPolicy
    )
/*+++

  Note: This routine should be called with IScsiSession->SessionLock held

--*/
{

    PISCSI_Path iScsiPath;
    PISCSI_CONNECTION iScsiConnection;
    ULONG iScsiStatus = STATUS_SUCCESS;
    ULONG inx;
    BOOLEAN activeFound = FALSE;

    //
    // Save the current value of path status.
    //
    iSpSavePrimaryPath(SupportedLBPolicy);

    for (inx = 0; inx < SupportedLBPolicy->iSCSI_PathCount; inx++) {

        iScsiPath = &(SupportedLBPolicy->iSCSI_Paths[inx]);

        iScsiConnection = (PISCSI_CONNECTION) iScsiPath->UniqueConnectionId;

        if (iScsiPath->PrimaryPath) {

            iScsiConnection->CurrentPathStatus = TRUE;

            activeFound = TRUE;
        } else {

            iScsiConnection->CurrentPathStatus = FALSE;
        }
    }

    if (!activeFound) {

        DebugPrint((iScsiPrtDebugError,
            "No active path given for Round Robin.\n"));

        ETWDebugPrint(iScsiLevelError,
            iScsiFlagWmi,
            "No active path given for Round Robin.\n");

        iScsiStatus = ISDSC_NO_PRIMARY_PATH_SPECIFIED;
    }

    if (NT_SUCCESS(iScsiStatus)) {

        DebugPrint((iScsiPrtDebugInfo,
            "Successfully set LB policy\n"));

        ETWDebugPrint(iScsiLevelTrace,
            iScsiFlagWmi,
            "Successfully set LB policy\n");

        IScsiSession->LoadBalancePolicy = SupportedLBPolicy->LoadBalancePolicy;
    } else {

        //
        // Failed to set user given LB settings. Restore the value of
        // path status from the saved value.
        //
        iSpRestorePrimaryPath(SupportedLBPolicy);
    }

    return iScsiStatus;
}

FORCEINLINE
iSpSavePrimaryPath(
    IN PISCSI_Supported_LB_Policies SupportedLBPolicy
    )
/*+++

  Note: This routine should be called with IScsiSession->SessionLock held

--*/
{
    PISCSI_Path iScsiPath;
    PISCSI_CONNECTION iScsiConnection;
    ULONG inx;

    //
    // Save the current LB policy settings before setting new values.
    // In case the update fails, we'll restore the old LB settings.
    //
    for (inx = 0; inx < SupportedLBPolicy->iSCSI_PathCount; inx++) {

        iScsiPath = &(SupportedLBPolicy->iSCSI_Paths[inx]);

        iScsiConnection = (PISCSI_CONNECTION) iScsiPath->UniqueConnectionId;

        iScsiConnection->PrevPathStatus = iScsiConnection->CurrentPathStatus;
    }
}

FORCEINLINE
iSpRestorePrimaryPath(
    IN PISCSI_Supported_LB_Policies SupportedLBPolicy
    )
/*+++

  Note: This routine should be called with IScsiSession->SessionLock held

--*/
{
    PISCSI_Path iScsiPath;
    PISCSI_CONNECTION iScsiConnection;
    ULONG inx;

    //
    // Restore the LB settings from the saved value.
    //
    for (inx = 0; inx < SupportedLBPolicy->iSCSI_PathCount; inx++) {

        iScsiPath = &(SupportedLBPolicy->iSCSI_Paths[inx]);

        iScsiConnection = (PISCSI_CONNECTION) iScsiPath->UniqueConnectionId;

        iScsiConnection->CurrentPathStatus = iScsiConnection->PrevPathStatus;
    }
}


VOID
iSpLogError(
    IN PDEVICE_OBJECT              DeviceObject,
    _In_reads_bytes_opt_(ErrorDataSize) PUCHAR ErrorData,
    IN ULONG                       ErrorDataSize,
    _In_opt_ PWSTR                 TargetName,
    IN ULONG                       ErrorCode
    )
/*+++

Routine Description :

    This routine will build a WMI event for the MSiSCSI_Eventlog class
    and then fire it. The WMI eventlog consumer provider has classes
    that intercept the WMI event and will redirect it to the system
    eventlog.

Arguements:

    DeviceObject is the device object of the adapter

    ErrorData is the data to be placed in the additional data field of
        the eventlog

    ErrorDataSize is the number of bytes of additional data

    TargetName is the name of the target

    ErrorCode is the iSCSI eventlog error code (see iscsilog.h)

Return Value:

--*/
{
    PMSiSCSI_Eventlog eventLog;
    PUCHAR dumpData;
    ULONG totalSize;
    ULONG dumpDataSize;
    ULONG targetNameSize;
    NTSTATUS status;
    GUID eventLogGUID = MSiSCSI_EventlogGuid;

    if (TargetName != NULL) {
        targetNameSize = wcslen(TargetName) * sizeof(WCHAR);
    } else {
        targetNameSize = 0;
    }

    totalSize = sizeof(MSiSCSI_Eventlog) +
                ErrorDataSize +
                targetNameSize;

    //
    // Allocate memory for MSiSCSI_Eventlog object
    //


    if (eventLog != NULL) {
        dumpDataSize = totalSize - FIELD_OFFSET(MSiSCSI_Eventlog,
                                                AdditionalData);
        eventLog->Type = ErrorCode;
        eventLog->LogToEventlog = 1;

        dumpData = eventLog->AdditionalData;

        //
        // Copy the entire error data followed by the target name if given.
        //
        if ((ErrorData != NULL) && (ErrorDataSize > 0)) {
            RtlCopyMemory(dumpData, ErrorData, ErrorDataSize);
            dumpData += ErrorDataSize;
        }

        if (targetNameSize > 0) {
            RtlCopyMemory(dumpData, TargetName, targetNameSize);
        }

        eventLog->Size = dumpDataSize;

        //
        // Fire the WMI event using eventLogGUID GUID, and
        // Free the memory allocated for MSiSCSI_Eventlog object.
        //

    }
}



UCHAR
iSpBuildBootInfo(
    IN PISCSI_ADAPTER_EXTENSION AdapterExtension,
    IN PULONG InstanceLength,
    IN ULONG BufferAvail,
    OUT PUCHAR Buffer,
    OUT PULONG SizeNeeded
    )
{
    UCHAR status;
    ULONG size;

    size = sizeof(MSiSCSI_BootInformation);

    if (BufferAvail >= size) {
        PMSiSCSI_BootInformation bootInfo;

        bootInfo = (PMSiSCSI_BootInformation)Buffer;

        bootInfo->SharedSecretLength = AdapterExtension->BootSecretLength;
        RtlCopyMemory(bootInfo->SharedSecret, AdapterExtension->BootSecret, 255);
        RtlCopyMemory(bootInfo->NodeName,
                      AdapterExtension->BootNodeName,
                      MAX_INITIATOR_NAME);
        *SizeNeeded = size;

        status = SRB_STATUS_SUCCESS;

    } else {

        *SizeNeeded = size;

        status = SRB_STATUS_DATA_OVERRUN;
    }

    *InstanceLength = *SizeNeeded;

    return status;
}

UCHAR
iSpBuildRedirectTargetInfo(
    IN PISCSI_ADAPTER_EXTENSION AdapterExtension,
    IN PULONG InstanceLength,
    IN ULONG BufferAvail,
    OUT PUCHAR Buffer,
    OUT PULONG SizeNeeded
    )
{
    PWMI_CONNECTION_LIST connectionList;
    ULONG size, sessionInx;
    ULONG numberOfSessions, numberOfConnections;
    UCHAR status;


    connectionList = iSpGetConnectionList(AdapterExtension,
                                          &numberOfSessions,
                                          &numberOfConnections);

    DebugPrint((iScsiPrtDebugInfo,
        "Number of Sessions %d, Number of Connections %d\n",
        numberOfSessions,
        numberOfConnections));

    ETWDebugPrint(iScsiLevelInfo,
        iScsiFlagWmi,
        "Number of Sessions %d, Number of Connections %d\n",
        numberOfSessions,
        numberOfConnections);

    //
    // determine the size of buffer needed to build all of the session
    // and connection data structures
    //

    //
    // Account for the fixed part of the iScsiInitiatorSessionInfo
    //
    size = FIELD_OFFSET(MSiSCSI_RedirectPortalInfoClass, RedirectSessionList);

    if (connectionList == NULL) {

        if (BufferAvail >= size)
        {
            PMSiSCSI_RedirectPortalInfoClass redirectPortalInfo;

            redirectPortalInfo = (PMSiSCSI_RedirectPortalInfoClass)Buffer;

            redirectPortalInfo->UniqueAdapterId = (ULONGLONG) AdapterExtension;
            redirectPortalInfo->SessionCount = 0;
            *SizeNeeded = size;

            status = SRB_STATUS_SUCCESS;
        } else {

            *SizeNeeded = size;

            status = SRB_STATUS_DATA_OVERRUN;
        }

        *InstanceLength = *SizeNeeded;

        return status;
    }

    //
    // Loop over all sessions and account for the space needed for each
    // session plus all of the connections within each session
    //
    sessionInx = 0;
    while (sessionInx < numberOfSessions) {

        //
        // Since the session structure needs to be 8 bytes aligned, we
        // pad out to 8 bytes
        //
        size = (size + 7) & ~7;

        //
        // Add the fixed size needed for the session structure
        //
        size += FIELD_OFFSET(ISCSI_RedirectSessionInfo,
                             RedirectPortalList);

        //
        // Account for the size of all connection structures, being
        // sure that the connection structure is padded out to 8 bytes
        // in order to maintain 8 byte alignment
        //
        size += connectionList[sessionInx].Count *
                ((sizeof(ISCSI_RedirectPortalInfo) + 7) & ~7);

        sessionInx++;
    }

    *SizeNeeded = size;
    if (size <= BufferAvail)
    {
        //
        // If we do have enough space to build the session info data
        // structures then we do so
        //
        RtlZeroMemory(Buffer, size);

        status = iSpReadRedirectTargetPortalInfo(AdapterExtension,
                                                 connectionList,
                                                 numberOfSessions,
                                                 Buffer);

    } else {
        //
        // If there is not enough space to build the session info data
        // structures then return this error
        //
        status = SRB_STATUS_DATA_OVERRUN;
    }

    iSpReleaseConnectionReferences(connectionList,
                                   numberOfSessions);


    return status;
}

UCHAR
iSpReadRedirectTargetPortalInfo(
    IN PISCSI_ADAPTER_EXTENSION AdapterExtension,
    IN PWMI_CONNECTION_LIST ConnectionList,
    IN ULONG NumberOfSessions,
    IN PUCHAR Buffer
    )
{
    PISCSI_RedirectPortalInfo redirectPortalInfo;
    PISCSI_RedirectSessionInfo redirectSessionInfo;
    PMSiSCSI_RedirectPortalInfoClass redirectPortalInfoClass;
    ULONG sessionIndex;
    ULONG connectionIndex;
    ULONG inx, skip;
    PWMIString WMIformatString;
    UCHAR status = SRB_STATUS_SUCCESS;


    if (ConnectionList == NULL) {

        DebugPrint((iScsiPrtDebugError,
            "Connectionlist NULL in iSpReadInitiatorSessionInfo\n"));

        ETWDebugPrint(iScsiLevelError,
            iScsiFlagWmi,
            "Connectionlist NULL in iSpReadInitiatorSessionInfo\n");

        return status;
    }

    redirectPortalInfoClass = (PMSiSCSI_RedirectPortalInfoClass) Buffer;

    redirectPortalInfoClass->UniqueAdapterId = (ULONGLONG) AdapterExtension;

    //
    // initialize Session count to 0
    //
    redirectPortalInfoClass->SessionCount = 0;

    redirectSessionInfo = redirectPortalInfoClass->RedirectSessionList;

    inx = 0;
    while (inx < NumberOfSessions) {

        PISCSI_SESSION iScsiSession;
        PISCSI_CONNECTION iScsiConnection;

        iScsiSession = ConnectionList[inx].ISCSISession;

        if (iScsiSession == NULL) {

            inx++;

            continue;
        }

        redirectSessionInfo->UniqueSessionId = iScsiSession->SessionId;

        redirectSessionInfo->TargetPortalGroupTag = iScsiSession->TargetPortalGroupTag;

        redirectSessionInfo->ConnectionCount = 0;

        redirectPortalInfo = redirectSessionInfo->RedirectPortalList;

        for (connectionIndex = 0;
             connectionIndex < ConnectionList[inx].Count;
             connectionIndex++)
        {

            iScsiConnection = ConnectionList[inx].ISCSIConnection[connectionIndex];
            if (iScsiConnection != NULL) {

                redirectPortalInfo->UniqueConnectionId = iScsiConnection->UniqueConnectionID;

                iSpCopyIPAddress(&redirectPortalInfo->OriginalIPAddr,
                                 &iScsiConnection->PermanentTargetIPAddress,
                                 &redirectPortalInfo->OriginalPort);

                redirectPortalInfo->OriginalIPAddr.TextAddress[0] = MAX_ISCSI_TEXT_ADDRESS_LEN*sizeof(WCHAR);

                redirectPortalInfo->Redirected = iScsiConnection->TargetMoved;

                if (iScsiConnection->TargetMoved) {

                    iSpCopyIPAddress(&redirectPortalInfo->RedirectedIPAddr,
                                     &iScsiConnection->TargetIPAddress,
                                     &redirectPortalInfo->RedirectedPort);

                    redirectPortalInfo->RedirectedIPAddr.TextAddress[0] = MAX_ISCSI_TEXT_ADDRESS_LEN*sizeof(WCHAR);

                    redirectPortalInfo->TemporaryRedirect = iScsiConnection->TargetMoveTemporary;
                }

                //increment the connectionStaticInfo pointer to the next 4 byte alligned address;
                (PUCHAR) redirectPortalInfo += ((sizeof(ISCSI_RedirectPortalInfo)+7) & ~7);

                //The number of TCP connections that currently belong to this session
                redirectSessionInfo->ConnectionCount ++;
            }
        }

        //
        // Advance to next place to write session information
        //
        skip = FIELD_OFFSET(ISCSI_RedirectSessionInfo, RedirectPortalList) +
                            (ConnectionList[inx].Count *
                             ((sizeof(ISCSI_RedirectPortalInfo) + 7) & ~7));

        skip = (skip + 7) & ~7;

        (PUCHAR) redirectSessionInfo += skip;

        redirectPortalInfoClass->SessionCount++;

        inx++;
    }

    return status;
}

NTSTATUS
iSpFindPersistentTargetKey(
    IN PISCSI_ADAPTER_EXTENSION AdapterExtension,
    _In_ PWSTR TargetName,
    IN PISCSI_TargetPortal TargetPortal,
    IN ULONG PortNumber,
    OUT PHANDLE TargetKey,
    _Out_writes_opt_ (TargetKeyNameSize) PWCHAR TargetKeyName, OPTIONAL
    IN ULONG TargetKeyNameSize
    )
{
    HANDLE persistentTargetsKey = NULL;
    HANDLE pnpDriverKey;
    NTSTATUS status;

    *TargetKey = NULL;

    status = iSpOpenPnPDriverKey(AdapterExtension->PhysicalDeviceObject,
                                 PERSISTENT_TARGETS,
                                 KEY_ALL_ACCESS,
                                 &pnpDriverKey,
                                 &persistentTargetsKey);
    if (NT_SUCCESS(status)) {

        KEY_BASIC_INFORMATION keyInfo[ISCSI_KEY_BASIC_INFO_SIZE];
        ULONG resultSize;
        ULONG index;
        BOOLEAN found;

        index = 0;
        found = FALSE;

        while (!found) {

            resultSize = ISCSI_KEY_BASIC_INFO_SIZE * sizeof(KEY_BASIC_INFORMATION);

            RtlZeroMemory(keyInfo, resultSize);

            //
            // Enumerate all the keys under PersistentTargets key
            //
            status = ZwEnumerateKey(persistentTargetsKey,
                                    index,
                                    KeyBasicInformation,
                                    keyInfo,
                                    resultSize,
                                    &resultSize);
            if (NT_SUCCESS(status)) {

                PUCHAR regValue;
                HANDLE targetNameKey;
                OBJECT_ATTRIBUTES objectAttributes;
                UNICODE_STRING subKeyName;
                WCHAR targetKeyName[512];
                ULONG nameLength;
                ULONG targetnameLen;
                ULONG regValueSize;

                nameLength = keyInfo->NameLength;

                if (nameLength > ISCSI_KEY_BASIC_INFO_NAME_MAXLEN) {
                    nameLength = ISCSI_KEY_BASIC_INFO_NAME_MAXLEN;
                }

                //
                // Find the key which matches the given target name
                //

                status = RtlStringCchCopyNW(
                            targetKeyName,
                            sizeof(targetKeyName)/sizeof(targetKeyName[0]),
                            keyInfo->Name,
                            nameLength);
                
                NT_ASSERT(NT_SUCCESS(status));

                if(NT_SUCCESS(status) == FALSE)
                {
                    goto NextKey;
                }

                targetnameLen = wcslen(TargetName);

                if (!wcsncmp(TargetName, targetKeyName, targetnameLen)) {

                    //
                    // Open LoginTarget under this target key
                    //
                    RtlStringCchCatW(targetKeyName, 255, L"\\");

                    RtlStringCchCatW(targetKeyName, 255, LOGIN_TARGET_STR);

                    RtlInitUnicodeString(&subKeyName, targetKeyName);

                    DebugPrint((iScsiPrtDebugInfo,
                        "Persistent Target Key %ws\n",
                        targetKeyName));

                    ETWDebugPrint(iScsiLevelInfo,
                        iScsiFlagWmi,
                        "Persistent Target Key %ws\n",
                        targetKeyName);

                    RtlZeroMemory(&objectAttributes, sizeof(OBJECT_ATTRIBUTES));

                    InitializeObjectAttributes(&objectAttributes,
                                               &subKeyName,
                                               (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE),
                                               persistentTargetsKey,
                                               (PSECURITY_DESCRIPTOR) NULL);

                    status = ZwOpenKey(&targetNameKey,
                                       KEY_ALL_ACCESS,
                                       &objectAttributes);
                    if (NT_SUCCESS(status)) {

                        status = iSpQueryRegistryValue(targetNameKey, LOGIN_TARGET_IN,
                                                       ISCSI_TAG_LOGINTARGET_IN,
                                                       &regValue, &regValueSize);
                        if (NT_SUCCESS(status)) {
                            PLoginToTarget_IN loginTargetIN;
                            PISCSI_TargetPortal targetPortal;

                            loginTargetIN = (PLoginToTarget_IN) regValue;

                            targetPortal = &(loginTargetIN->TargetPortal);

                            if ((PortNumber == loginTargetIN->PortNumber) &&
                                (targetPortal->Address.Type != ISCSI_IP_ADDRESS_EMPTY) &&
                                (IsSamePortal(TargetPortal, targetPortal))) {

                                found = TRUE;

                                //
                                // Open the TargetName key and return a handle to that
                                //
                                status  = RtlStringCchCopyNW(
                                                targetKeyName,
                                                sizeof(targetKeyName)/sizeof(targetKeyName[0]),
                                                keyInfo->Name,
                                                nameLength);
                                
                                NT_ASSERT(NT_SUCCESS(status));
                                
                                if(NT_SUCCESS(status) == TRUE)
                                {
                                    RtlInitUnicodeString(&subKeyName, targetKeyName);

                                    RtlZeroMemory(&objectAttributes, sizeof(OBJECT_ATTRIBUTES));

                                    InitializeObjectAttributes(&objectAttributes,
                                                               &subKeyName,
                                                               (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE),
                                                               persistentTargetsKey,
                                                               (PSECURITY_DESCRIPTOR) NULL);

                                    status = ZwOpenKey(TargetKey,
                                                       KEY_ALL_ACCESS,
                                                       &objectAttributes);
                                    if (NT_SUCCESS(status)) {

                                        if (TargetKeyName != NULL) {

                                            NT_ASSERT(TargetKeyNameSize > 0);

                                            status = RtlStringCchCopyNW(
                                                            TargetKeyName,
                                                            TargetKeyNameSize,
                                                            targetKeyName,
                                                            sizeof(targetKeyName)/sizeof(targetKeyName[0]));

                                            NT_ASSERT(NT_SUCCESS(status));

                                            if(NT_SUCCESS(status) == FALSE)
                                            {
                                                DebugPrint((iScsiPrtDebugError,
                                                    "Failed to copy TargetKeyName %ws\n",
                                                    targetKeyName));

                                                ETWDebugPrint(iScsiLevelError,
                                                    iScsiFlagWmi,
                                                    "Failed to copy TargetKeyName %ws\n",
                                                    targetKeyName);

                                                *TargetKey = NULL;
                                            }
                                        }
                                    } else {
                                        *TargetKey = NULL;
                                    }
                                } else {

                                    DebugPrint((iScsiPrtDebugError,
                                        "Failed to copy target name from KeyInfo.\n"));

                                    ETWDebugPrint(iScsiLevelError,
                                        iScsiFlagWmi,
                                        "Failed to copy target name from KeyInfo.\n");
                                }
                            } else {

                                DebugPrint((iScsiPrtDebugError,
                                    "Portal mismatch. Port Numbers %x, %x\n",
                                    PortNumber, loginTargetIN->PortNumber));

                                ETWDebugPrint(iScsiLevelError,
                                    iScsiFlagWmi,
                                    "Portal mismatch. Port Numbers %x, %x\n",
                                    PortNumber, loginTargetIN->PortNumber);
                            }

                            ExFreePool(regValue);
                        }

                        ZwClose(targetNameKey);
                    }
                }
            } else if (status == STATUS_NO_MORE_ENTRIES) {

                DebugPrint((iScsiPrtDebugError,
                    "Enumerated all persistent targets\n"));

                ETWDebugPrint(iScsiLevelError,
                    iScsiFlagWmi,
                    "Enumerated all persistent targets\n");

                break;
            } else {

                DebugPrint((iScsiPrtDebugError,
                    "ZwEnumerateKey failed for persistent targets. Status %x\n",
                    status));

                ETWDebugPrint(iScsiLevelError,
                    iScsiFlagWmi,
                    "ZwEnumerateKey failed for persistent targets. Status %x\n",
                    status);

                break;
            }
NextKey:
            index++;
        }

        iSpClosePnPDriverKey(&pnpDriverKey, &persistentTargetsKey);
    }

    return status;
}

NTSTATUS
iSpDeleteKeyAndSubkeys(
    IN HANDLE RootKey
    )
{
    KEY_BASIC_INFORMATION keyInfo[ISCSI_KEY_BASIC_INFO_SIZE];
    NTSTATUS status = STATUS_SUCCESS;
    ULONG resultSize;
    ULONG index;
    BOOLEAN deleted = FALSE;

    index = 0;

    //
    // Enumerate all the keys under the given key and delete each one of them.
    // Note that this routine assumes there are no subkeys and the subkeys
    // to the Root Key. This routine can only delete a registry tree with
    // one sub level below the Root Key.
    //
    while (status == STATUS_SUCCESS) {

        resultSize = ISCSI_KEY_BASIC_INFO_SIZE * sizeof(KEY_BASIC_INFORMATION);

        RtlZeroMemory(keyInfo, resultSize);

        //
        // Enumerate all the keys under the given key
        //
        status = ZwEnumerateKey(RootKey,
                                index,
                                KeyBasicInformation,
                                keyInfo,
                                resultSize,
                                &resultSize);
        if (NT_SUCCESS(status)) {

            HANDLE subKeyHandle;
            UNICODE_STRING subKey;
            WCHAR subKeyName[ISCSI_KEY_BASIC_INFO_NAME_MAXLEN + 1]; // add 1 for null terminator
            OBJECT_ATTRIBUTES objectAttributes;
            ULONG nameLength;

            deleted = FALSE;

            nameLength = keyInfo->NameLength;
            if (nameLength > ISCSI_KEY_BASIC_INFO_NAME_MAXLEN) {
                nameLength = ISCSI_KEY_BASIC_INFO_NAME_MAXLEN;
            }

            status = RtlStringCchCopyNW(
                        subKeyName,
                        sizeof(subKeyName) / sizeof(subKeyName[0]),
                        keyInfo->Name,
                        nameLength);

            NT_ASSERT(NT_SUCCESS(status));

            if(NT_SUCCESS(status) == FALSE)
            {
                break;
            }

            RtlInitUnicodeString(&subKey, subKeyName);

            RtlZeroMemory(&objectAttributes, sizeof(OBJECT_ATTRIBUTES));

            InitializeObjectAttributes(&objectAttributes,
                                       &subKey,
                                       (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE),
                                       RootKey,
                                       (PSECURITY_DESCRIPTOR) NULL);

            //
            // Open this subkey and delete it.
            //
            status = ZwOpenKey(&subKeyHandle,
                               KEY_ALL_ACCESS,
                               &objectAttributes);
            if (NT_SUCCESS(status)) {

                //
                // Note that there should be no subkeys under this key. Otherwise
                // ZwDeleteKey will fail with error STATUS_CANNOT_DELETE.
                //
                status = ZwDeleteKey(subKeyHandle);
                if (NT_SUCCESS(status)) {

                    DebugPrint((iScsiPrtDebugInfo,
                        "Deleted subkey %ws\n",
                        subKeyName));

                    ETWDebugPrint(iScsiLevelTrace,
                        iScsiFlagGeneral,
                        "Deleted subkey %ws\n",
                        subKeyName);

                    deleted = TRUE;
                } else {

                    
                    DebugPrint((iScsiPrtDebugError, 
                                "Failed to delete subkey %ws. Status %x\n",
                                subKeyName, status));

                    ETWDebugPrint(iScsiLevelError,
                        iScsiFlagGeneral,
                        "Failed to delete subkey %ws. Status %x\n",
                        subKeyName, status);
                }
                
                ZwClose(subKeyHandle);

            }
        } else if (status != STATUS_NO_MORE_ENTRIES) {

            DebugPrint((iScsiPrtDebugError,
                "Failed to enumerate subkeys in delete key and subkeys. Status %x\n",
                status));

            ETWDebugPrint(iScsiLevelError,
                iScsiFlagGeneral,
                "Failed to enumerate subkeys in delete key and subkeys. Status %x\n",
                status);
        }

        //
        // If the key was successfully deleted do not increment the index since
        // the number of keys under RootKey is now lesser by one.
        //
        if (!deleted) {
            index++;
        }
    }

    if (!NT_SUCCESS(status) && (status == STATUS_NO_MORE_ENTRIES)) {

        //
        // This indicates that all the subkeys under Root Key have been
        // enumerated and they have been deleted successfully. It is now
        // safe to delete the Root Key.
        //
        status = STATUS_SUCCESS;

        status = ZwDeleteKey(RootKey);
        if (!NT_SUCCESS(status)) {

            DebugPrint((iScsiPrtDebugError,
                "Failed to delete Root Key. Status %x\n",
                status));

            ETWDebugPrint(iScsiLevelError,
                iScsiFlagGeneral,
                "Failed to delete Root Key. Status %x\n",
                status);
        } else {

            DebugPrint((iScsiPrtDebugTrace,
                "Successfully delete all keys and subkeys\n"));

            ETWDebugPrint(iScsiLevelTrace,
                iScsiFlagGeneral,
                "Successfully delete all keys and subkeys\n");
        }
    } else {

        DebugPrint((iScsiPrtDebugError,
            "Failed to delete key and all subkeys. Status %x\n",
            status));

        ETWDebugPrint(iScsiLevelError,
            iScsiFlagGeneral,
            "Failed to delete key and all subkeys. Status %x\n",
            status);
    }

    return status;
}

NTSTATUS
iSpPersisteLBPolicy(
    IN PISCSI_ADAPTER_EXTENSION AdapterExtension,
    IN PPERSISTENT_TARGET_LB_POLICY LBPolicy
    )
{
    HANDLE persistentTargetsKey;
    HANDLE pnpDriverKey;
    PLB_POLICY_DATA LBPolicyData;
    ULONG inx;
    ULONG lbPolicy;
    NTSTATUS status;

    PAGED_CODE();

    status = iSpOpenPnPDriverKey(AdapterExtension->PhysicalDeviceObject,
                                 PERSISTENT_TARGETS,
                                 KEY_ALL_ACCESS,
                                 &pnpDriverKey,
                                 &persistentTargetsKey);
    if (NT_SUCCESS(status)) {

        OBJECT_ATTRIBUTES objectAttributes;
        HANDLE subKeyHandle;
        HANDLE loginTargetKey;
        UNICODE_STRING subKey;
        WCHAR subKeyName[32];

        RtlZeroMemory(subKeyName,sizeof(subKeyName));

        RtlInitUnicodeString(&subKey, LBPolicy->TargetKeyName);

        RtlZeroMemory(&objectAttributes, sizeof(OBJECT_ATTRIBUTES));

        InitializeObjectAttributes(&objectAttributes,
                                   &subKey,
                                   (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE),
                                   persistentTargetsKey,
                                   (PSECURITY_DESCRIPTOR) NULL);

        status = ZwOpenKey(&subKeyHandle,
                           KEY_ALL_ACCESS,
                           &objectAttributes);

        if (NT_SUCCESS(status)) {

            //
            // Save the new Load Balance policy for this target
            //
            status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE,
                                           subKeyHandle,
                                           LOAD_BALANCE_POLICY,
                                           REG_DWORD,
                                           &LBPolicy->LoadBalancePolicy,
                                           sizeof(ISCSI_LOAD_BALANCE_POLICY));
            NT_ASSERT(NT_SUCCESS(status));

            for (inx = 0; NT_SUCCESS(status) && inx < LBPolicy->ConnectionCount; inx++) {

                //
                // Save the PrimaryPath and PathWeight for each connection
                //
                LBPolicyData = &(LBPolicy->PolicyData[inx]);

                if (LBPolicyData->ConnectionNumber == -1) {

                    RtlStringCchCopyW(subKeyName, 32, LOGIN_TARGET_STR);
                } else {

                    RtlStringCchPrintfW(subKeyName,
                                        ((sizeof(subKeyName) - sizeof(WCHAR))/sizeof(WCHAR)),
                                        L"%ws%d",
                                        ADD_CONNECTION_STR,
                                        LBPolicyData->ConnectionNumber);
                }

                DebugPrint((iScsiPrtDebugInfo,
                    "Will open subkey %ws\n",
                    subKeyName));

                ETWDebugPrint(iScsiLevelInfo,
                    iScsiFlagWmi,
                    "Will open subkey %ws\n",
                    subKeyName);

                RtlInitUnicodeString(&subKey, subKeyName);

                RtlZeroMemory(&objectAttributes, sizeof(OBJECT_ATTRIBUTES));

                InitializeObjectAttributes(&objectAttributes,
                                           &subKey,
                                           (OBJ_CASE_INSENSITIVE | OBJ_KERNEL_HANDLE),
                                           subKeyHandle,
                                           (PSECURITY_DESCRIPTOR) NULL);

                status = ZwOpenKey(&loginTargetKey,
                                   KEY_ALL_ACCESS,
                                   &objectAttributes);
                if (NT_SUCCESS(status)) {

                    //
                    // Save PathWeight and PrimaryPath
                    //
                    status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE,
                                                   loginTargetKey,
                                                   PATH_WEIGHT,
                                                   REG_DWORD,
                                                   &LBPolicyData->PathWeight,
                                                   sizeof(ULONG));
                    if (!NT_SUCCESS(status)) {

                        DebugPrint((iScsiPrtDebugError,
                            "Failed to save Path Weight. Status %x\n",
                            status));

                        ETWDebugPrint(iScsiLevelError,
                            iScsiFlagWmi,
                            "Failed to save Path Weight. Status %x\n",
                            status);
                    }

                    status = RtlWriteRegistryValue(RTL_REGISTRY_HANDLE,
                                                   loginTargetKey,
                                                   PRIMARY_PATH,
                                                   REG_DWORD,
                                                   &LBPolicyData->PrimaryPath,
                                                   sizeof(ULONG));
                    if (!NT_SUCCESS(status)) {

                        DebugPrint((iScsiPrtDebugError,
                            "Failed to save Path Weight. Status %x\n",
                            status));

                        ETWDebugPrint(iScsiLevelError,
                            iScsiFlagWmi,
                            "Failed to save Path Weight. Status %x\n",
                            status);
                    }

                    ZwClose(loginTargetKey);
                } else {

                    DebugPrint((iScsiPrtDebugError,
                        "Failed to open subkey subKeyName to save LB Policy\n"));

                    ETWDebugPrint(iScsiLevelError,
                        iScsiFlagWmi,
                        "Failed to open subkey subKeyName to save LB Policy\n");
                }
            }

            ZwClose(subKeyHandle);
        }

        iSpClosePnPDriverKey(&pnpDriverKey, &persistentTargetsKey);
    }

    return status;
}

NTSTATUS
iSpSetTargetNameSuffix(
    IN PISCSI_ADAPTER_EXTENSION AdapterExtension,
    IN PLoginToTarget_IN LoginTargetIn
    )
{
    PISCSI_SESSION iScsiSession = NULL;
    HANDLE removeTargetKey = NULL;
    WCHAR targetName[MAX_TARGET_NAME + 1];
    WCHAR targetKeyName[MAX_TARGET_NAME + TARGET_KEYNAME_SUFFIX_SIZE + 1];
    ULONG bytesToCopy;
    KIRQL oldIrql;
    NTSTATUS rtlStatus = ERROR_SUCCESS;

    if (LoginTargetIn->SessionType != ISCSI_LOGINTARGET_DATA) {
        return STATUS_INVALID_PARAMETER;
    }

    RtlZeroMemory(targetKeyName, sizeof(targetKeyName));
    RtlZeroMemory(targetName, sizeof(targetName));

    bytesToCopy = *(LoginTargetIn->TargetName);
    if (bytesToCopy > (MAX_TARGET_NAME * sizeof(WCHAR))) {
        bytesToCopy = MAX_TARGET_NAME * sizeof(WCHAR);
    }

    RtlCopyMemory(targetName,
                  (LoginTargetIn->TargetName + 1),
                  bytesToCopy);

    rtlStatus = iSpFindPersistentTargetKey(
                    AdapterExtension,
                    targetName,
                    &(LoginTargetIn->TargetPortal),
                    LoginTargetIn->PortNumber,
                    &removeTargetKey,
                    targetKeyName,
                    sizeof(targetKeyName)/sizeof(targetKeyName[0]));

    if (NT_SUCCESS(rtlStatus) && removeTargetKey != NULL) {

        PWCHAR suffixPtr;
        PISCSI_CONNECTION iScsiConnection;
        PLIST_ENTRY sessionEntry;
        PLIST_ENTRY connectionEntry;
        PISCSI_TRANSPORT_ADDRESS targetIPAddress;
        ISCSI_TargetPortal targetPortal;
        BOOLEAN found = FALSE;

        suffixPtr = wcschr(targetKeyName, L'#');
        if (suffixPtr != NULL) {

            DebugPrint((iScsiPrtDebugInfo,
                "Suffix is %ws\n",
                suffixPtr));

            ETWDebugPrint(iScsiLevelInfo,
                iScsiFlagProtocolLogInOut,
                "Suffix is %ws\n",
                suffixPtr);

            RtlZeroMemory(&targetPortal, sizeof(targetPortal));

            KeAcquireSpinLock(&AdapterExtension->AdapterLock, &oldIrql);

            sessionEntry = AdapterExtension->IScsiSessionList.Flink;
            while (sessionEntry != &AdapterExtension->IScsiSessionList) {

                iScsiSession = CONTAINING_RECORD(sessionEntry,
                                                 ISCSI_SESSION,
                                                 NextSession);

                sessionEntry = sessionEntry->Flink;

                if (wcsncmp(targetName,
                            iScsiSession->TargetName,
                            MAX_TARGET_NAME) == 0) {

                    connectionEntry = iScsiSession->ConnectionList.Flink;
                    while (connectionEntry != &iScsiSession->ConnectionList) {

                        iScsiConnection = CONTAINING_RECORD(connectionEntry,
                                                            ISCSI_CONNECTION,
                                                            NextConnection);

                        connectionEntry = connectionEntry->Flink;

                        targetIPAddress = &(iScsiConnection->TargetIPAddress);

                        if (targetIPAddress->AddressType == TDI_ADDRESS_TYPE_IP) {

                            targetPortal.Address.Type = ISCSI_IP_ADDRESS_IPV4;

                            targetPortal.Socket =
                                targetIPAddress->IPAddress.IPv4Address.sin_port;

                            targetPortal.Address.IpV4Address =
                                targetIPAddress->IPAddress.IPv4Address.in_addr;
                        } else {

                            targetPortal.Address.Type = ISCSI_IP_ADDRESS_IPV6;

                            targetPortal.Socket =
                                targetIPAddress->IPAddress.IPv6Address.sin6_port;

                            targetPortal.Address.IpV6FlowInfo =
                                targetIPAddress->IPAddress.IPv6Address.sin6_flowinfo;

                            targetPortal.Address.IpV6ScopeId =
                                targetIPAddress->IPAddress.IPv6Address.sin6_scope_id;

                            RtlCopyMemory(targetPortal.Address.IpV6Address,
                                          targetIPAddress->IPAddress.IPv6Address.sin6_addr,
                                          sizeof(targetPortal.Address.IpV6Address));
                        }

                        if (IsSamePortal(&targetPortal, &(LoginTargetIn->TargetPortal))) {

                            DebugPrint((iScsiPrtDebugInfo,
                                "Found session %p\n",
                                iScsiSession));

                            ETWDebugPrint(iScsiLevelInfo,
                                iScsiFlagProtocolLogInOut,
                                "Found session %p\n",
                                iScsiSession);

                            found = TRUE;
                            break;
                        }
                    }
                }

                if (found) {
                    break;
                }
            }

            KeReleaseSpinLock(&AdapterExtension->AdapterLock, oldIrql);

            if (found) {

                DebugPrint((iScsiPrtDebugInfo,
                    "Will copy suffix %ws to session %p\n",
                    suffixPtr,
                    iScsiSession));

                ETWDebugPrint(iScsiLevelInfo,
                    iScsiFlagProtocolLogInOut,
                    "Will copy suffix %ws to session %p\n",
                    suffixPtr,
                    iScsiSession);

                rtlStatus = RtlStringCchCopyNW(iScsiSession->TargetKeyNameSuffix,
                                               TARGET_KEYNAME_SUFFIX_SIZE,
                                               suffixPtr,
                                               (TARGET_KEYNAME_SUFFIX_SIZE - 1));

                iScsiSession->PersistentLogin = TRUE;
            }
        } else {

            DebugPrint((iScsiPrtDebugError,
                "No suffix found for target %ws\n",
                targetKeyName));

            ETWDebugPrint(iScsiLevelError,
                iScsiFlagProtocolLogInOut,
                "No suffix found for target %ws\n",
                targetKeyName);
        }

        ZwClose(removeTargetKey);
    } else {

        DebugPrint((iScsiPrtDebugError,
            "%ws is probably not a persistent target\n",
            targetName));

        ETWDebugPrint(iScsiLevelError,
            iScsiFlagProtocolLogInOut,
            "%ws is probably not a persistent target\n",
            targetName);

    }

    return rtlStatus;
}

Our Services

  • What our customers say about us?

© 2011-2024 All Rights Reserved. Joya Systems. 4425 South Mopac Building II Suite 101 Austin, TX 78735 Tel: 800-DEV-KERNEL

Privacy Policy. Terms of use. Valid XHTML & CSS