/*++

Copyright (C) Microsoft Corporation, 2000

Module Name:

    utils.c

Abstract:

    Utility routines for iScsi Port driver

Environment:

    kernel mode only

Revision History:

--*/

#include "port.h"


PVOID
iSpAllocatePool(
    IN POOL_TYPE PoolType,
    IN SIZE_T NumberOfBytes,
    IN ULONG Tag
    )
{
    PVOID Block;
     
    Block = ExAllocatePoolWithTag(PoolType,
                                  NumberOfBytes,
                                  Tag);
    if (Block != NULL) {
        RtlZeroMemory(Block, NumberOfBytes);
    }

    return Block;
}


NTSTATUS
iSpAllocateMdlAndIrp(
    IN PVOID Buffer,
    IN ULONG BufferLen,
    IN CCHAR StackSize,
    IN BOOLEAN NonPagedPool,
    OUT PIRP *Irp,
    OUT PMDL *Mdl
    )
{
    PMDL localMdl = NULL;
    PIRP localIrp = NULL;
    NTSTATUS status;

    //
    // Allocate an MDL for this request
    //
    localMdl = IoAllocateMdl(Buffer,
                             BufferLen,
                             FALSE,
                             FALSE,
                             NULL);
    if (localMdl == NULL) {
        DebugPrint((1, "iSpAllocateMdlAndIrp : Failed to allocate MDL\n"));
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    //
    // Initialize the MDL. If the buffer is from NonPaged pool
    // use MmBuildMdlForNonPagedPool. Else, use MmProbeAndLockPages
    //
    if (NonPagedPool == TRUE) {
        MmBuildMdlForNonPagedPool(localMdl);
    } else {

        try {
            MmProbeAndLockPages(localMdl, KernelMode, IoModifyAccess);
        } except(EXCEPTION_EXECUTE_HANDLER) {

              DebugPrint((1, 
                          "iSpAllocateMdlAndIrp : Failed to Lockpaged\n"));
              IoFreeMdl(localMdl);
              return STATUS_INSUFFICIENT_RESOURCES;
        }
    }

    //
    // Allocate an IRP
    //
    localIrp = IoAllocateIrp(StackSize, FALSE);
    if (localIrp == NULL) {
        DebugPrint((1, "iSpAllocateMdlAndIrp. Failed to allocate IRP\n"));
        IoFreeMdl(localMdl);
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    DebugPrint((3, "Allocated IRP 0x%08x and MDL 0x%08x\n",
                localIrp, localMdl));

    *Irp = localIrp;
    *Mdl = localMdl;

    return STATUS_SUCCESS;
}


VOID
iSpFreeMdlAndIrp(
    IN PMDL Mdl,
    IN PIRP Irp,
    BOOLEAN UnlockPages
    )
{
    PMDL tmpMdlPtr = NULL;

    if (Irp == NULL) {
        return;
    }

    //
    // Free any MDLs allocated for this IRP
    //
    if (Mdl != NULL) {
        while ((Irp->MdlAddress) != NULL) {
            tmpMdlPtr = (Irp->MdlAddress)->Next;

            if (UnlockPages) {
                MmUnlockPages(Irp->MdlAddress);
            }

            IoFreeMdl(Irp->MdlAddress);
            Irp->MdlAddress = tmpMdlPtr;
        }
    }

    IoFreeIrp(Irp);
}


NTSTATUS
iScsiPortStringArrayToMultiString(
    IN PDRIVER_OBJECT DriverObject,
    PUNICODE_STRING MultiString,
    PCSTR StringArray[]
    )

/*++

Routine Description:

    This routine will take a null terminated array of ascii strings and merge
    them together into a unicode multi-string block.

    This routine allocates memory for the string buffer - is the caller's
    responsibility to free it.

Arguments:

    MultiString - a UNICODE_STRING structure into which the multi string will
                  be built.

    StringArray - a NULL terminated list of narrow strings which will be combined
                  together.  This list may not be empty.

Return Value:

    status

--*/

{
    ANSI_STRING ansiEntry;

    UNICODE_STRING unicodeEntry;
    PWSTR unicodeLocation;

    UCHAR i;

    NTSTATUS status;

    PAGED_CODE();

    //
    // Make sure we aren't going to leak any memory
    //

    ASSERT(MultiString->Buffer == NULL);

    RtlInitUnicodeString(MultiString, NULL);

    for(i = 0; StringArray[i] != NULL; i++) {

        RtlInitAnsiString(&ansiEntry, StringArray[i]);

        MultiString->Length += (USHORT) RtlAnsiStringToUnicodeSize(&ansiEntry);
    }

    ASSERT(MultiString->Length != 0);

    MultiString->MaximumLength = MultiString->Length + sizeof(UNICODE_NULL);

    MultiString->Buffer = iSpAllocatePool(PagedPool,
                                          MultiString->MaximumLength,
                                          ISCSI_TAG_PNP_ID);

    if(MultiString->Buffer == NULL) {

        return STATUS_INSUFFICIENT_RESOURCES;

    }

    RtlZeroMemory(MultiString->Buffer, MultiString->MaximumLength);

    unicodeEntry = *MultiString;

    for(i = 0; StringArray[i] != NULL; i++) {

        RtlInitAnsiString(&ansiEntry, StringArray[i]);

        status = RtlAnsiStringToUnicodeString(
                    &unicodeEntry,
                    &ansiEntry,
                    FALSE);

        //
        // Since we're not allocating any memory the only failure possible
        // is if this function is bad
        //

        ASSERT(NT_SUCCESS(status));

        //
        // Push the buffer location up and reduce the maximum count
        //

        ((PSTR) unicodeEntry.Buffer) += unicodeEntry.Length + sizeof(WCHAR);
        unicodeEntry.MaximumLength -= unicodeEntry.Length + sizeof(WCHAR);

    };

    //
    // Stick the final NUL on the end of the multisz
    //

//    RtlZeroMemory(unicodeEntry.Buffer, unicodeEntry.MaximumLength);

    return STATUS_SUCCESS;
}


NTSTATUS
iSpMultiStringToStringArray(
    IN PDRIVER_OBJECT DriverObject,
    IN PUNICODE_STRING MultiString,
    OUT PWSTR *StringArray[],
    BOOLEAN Forward
    )

{
    ULONG stringCount = 0;
    ULONG stringNumber;
    ULONG i;
    PWSTR *stringArray;

    PAGED_CODE();

    //
    // Pass one: count the number of string elements.
    //

    for(i = 0; i < (MultiString->MaximumLength / sizeof(WCHAR)); i++) {
        if(MultiString->Buffer[i] == UNICODE_NULL) {
            stringCount++;
        }
    }

    //
    // Allocate the memory for a NULL-terminated string array.
    //

    stringArray = iSpAllocatePool(PagedPool,
                                  (stringCount + 1) * sizeof(PWSTR),
                                  ISCSI_TAG_PNP_ID);

    if(stringArray == NULL) {
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    RtlZeroMemory(stringArray, (stringCount + 1) * sizeof(PWSTR));

    //
    // Pass two : Put the string pointers in place.
    //

    i = 0;

    for(stringNumber = 0; stringNumber < stringCount; stringNumber++) {

        ULONG arrayNumber;

        if(Forward) {
            arrayNumber = stringNumber;
        } else {
            arrayNumber = stringCount - stringNumber - 1;
        }

        //
        // Put a pointer to the head of the string into the array.
        //

        stringArray[arrayNumber] = &MultiString->Buffer[i];

        //
        // Scan for the end of the string.
        //

        while((i < (MultiString->MaximumLength / sizeof(WCHAR))) &&
              (MultiString->Buffer[i] != UNICODE_NULL)) {
            i++;
        }

        //
        // Jump past the NULL.
        //

        i++;
    }

    *StringArray = stringArray;
    return STATUS_SUCCESS;
}