/*++

Copyright (C) Microsoft Corporation, 2000

Module Name:

    protocol.c

Abstract:

    This file contains iSCSI protocol related routines.

Environment:

    kernel mode only

Revision History:

--*/

#include "port.h"

LONG  GlobalSessionID;
LONG  InitiatorSessionID;

#define ISCSI_TARGET "target:"
#define ISCSI_TARGET_LENGTH 7
#define ISCSI_USE_RTT "UseRtt:no"
#define ISCSI_USE_RTT_LENGTH 9

ULONG
iSpGetActiveClientRequestIndex(
    PISCSI_CONNECTION IScsiConnection,
    ULONG TaskTag
    );

ULONG
iSpGetReqIndexUsingCmdRN(
    PISCSI_CONNECTION IScsiConnection,
    ULONG CmdRN
    );


NTSTATUS
iSpSendLoginCommand(
    IN PISCSI_PDO_EXTENSION PdoExtension
    )
{
    PISCSI_CONNECTION iScsiConnection;
    PISCSI_LOGIN_COMMAND iscsiLoginCommand;
    PUCHAR loginParameters;
    NTSTATUS status;
    ULONG bytesSent;
    ULONG tempULong;
    ULONG packetSize;
    ULONG targetLength;

    iScsiConnection = PdoExtension->ClientNodeInfo;

    ASSERT((iScsiConnection != NULL));
    ASSERT((iScsiConnection->Type) == ISCSI_CONNECTION_TYPE);

    targetLength = strlen(PdoExtension->TargetName);
    packetSize = (sizeof(ISCSI_LOGIN_COMMAND) + ISCSI_TARGET_LENGTH + 
                  targetLength + ISCSI_USE_RTT_LENGTH + 2);

    iscsiLoginCommand = iSpAllocatePool(
                          NonPagedPool,
                          packetSize,
                          ISCSI_TAG_LOGIN_CMD);
    if (iscsiLoginCommand == NULL) {
        DebugPrint((0, "Failed to allocate logon packet\n"));
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    RtlZeroMemory(iscsiLoginCommand, 
                  packetSize);

    iscsiLoginCommand->OpCode = ISCSIOP_LOGIN_COMMAND;

    //
    // No authentication performed
    //
    iscsiLoginCommand->LoginType = ISCSI_LOGINTYPE_NONE;

    //
    // Connection ID for this session
    //
    tempULong =  InterlockedIncrement(&GlobalSessionID);
    iscsiLoginCommand->ConnectionID[0] = (UCHAR) ((tempULong & 0xFF00) >> 8);
    iscsiLoginCommand->ConnectionID[1] = (UCHAR) (tempULong & 0xFF);

    //
    // Command Reference number starting from 1
    //
    iscsiLoginCommand->InitCmdRN[3] = 1;

    tempULong = InterlockedIncrement(&InitiatorSessionID);
    iscsiLoginCommand->ISID[0]      = (UCHAR) ((tempULong & 0xFF00) >> 8);
    iscsiLoginCommand->ISID[1] = (UCHAR) (tempULong & 0xFF);

    //
    // Identifier for the target device is passed as parameters 
    // in the Login packet
    //
    // "target:<TargetName>"  -- Target device name 
    // "UseRtt:no"     -- Do NOT use RTT
    //
    loginParameters = (PUCHAR) (iscsiLoginCommand + 1);

    RtlCopyMemory(loginParameters, ISCSI_TARGET, ISCSI_TARGET_LENGTH);
    loginParameters += ISCSI_TARGET_LENGTH;

    RtlCopyMemory(loginParameters, PdoExtension->TargetName, targetLength);    
    loginParameters += targetLength + 1;

    RtlCopyMemory(loginParameters, ISCSI_USE_RTT, ISCSI_USE_RTT_LENGTH);

    iscsiLoginCommand->Length[3] = 
        ISCSI_TARGET_LENGTH + (UCHAR) targetLength + ISCSI_USE_RTT_LENGTH + 2;

    /*
    {
        ULONG inx0, inx1, len;

        DebugPrint((1, "\n Logon Packet\n"));

        len = (ISCSI_TARGET_LENGTH + targetLength + 
               ISCSI_USE_RTT_LENGTH + 2 + 48);
        inx0 = 0;
        while(inx0 < len) {
            inx1 = 0;
            DebugPrint((1, "\t"));
            while ((inx1 < 4) &&
                   ((inx0+inx1) < len)) {
                DebugPrint((1, "0x%02x ", 
                            ((PUCHAR)iscsiLoginCommand)[inx0+inx1]));
                inx1++;
            }
            DebugPrint((1, "\n"));

            inx0 += 4;
        }
        DebugPrint((1, "\n"));
    }
    */

    //
    // Save away the connection ID in our device extension
    //
    PdoExtension->SavedConnectionID[0] = iscsiLoginCommand->ConnectionID[0];
    PdoExtension->SavedConnectionID[1] = iscsiLoginCommand->ConnectionID[1];

    status = iSpSendData(iScsiConnection->ConnectionDeviceObject,
                         iScsiConnection->ConnectionFileObject,
                         iscsiLoginCommand,
                         packetSize,
                         &bytesSent);
    if (NT_SUCCESS(status)) {

        DebugPrint((3, "Send succeeded for logon. Bytes sent : %d\n", 
                    bytesSent));
    } else {
        DebugPrint((0, "Failed to logon packet. Status : %x\n",
                    status));

        PdoExtension->SavedConnectionID[0] = 0;
        PdoExtension->SavedConnectionID[1] = 0;
    }

    return status;
}


NTSTATUS
iSpSendScsiCommand(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )
{
    PCOMMON_EXTENSION commonExtension = DeviceObject->DeviceExtension;
    PISCSI_PDO_EXTENSION pdoExtension = NULL;
    PISCSI_CONNECTION iScsiConnection = NULL;

    PISCSI_SCSI_COMMAND iScsiScsiCommand = NULL;

    PACTIVE_REQUESTS currentRequest;

    PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
    PSCSI_REQUEST_BLOCK srb = irpStack->Parameters.Scsi.Srb;

    PVOID requestBuffer = NULL;
    PVOID originalDataBuffer = NULL;
    ULONG_PTR offset;

    PVOID receiveBuffer = NULL;
    ULONG receiveBufferSize = 0;

    ULONG cmdRN;
    ULONG expectedDataLen;

    ULONG packetSize;
    ULONG inx;
    ULONG bytesSent;

    NTSTATUS status;

    KIRQL oldIrql;

    BOOLEAN writeToDevice;

    ASSERT(commonExtension->IsPdo);
    pdoExtension = (PISCSI_PDO_EXTENSION)(DeviceObject->DeviceExtension);

    iScsiConnection = pdoExtension->ClientNodeInfo;

    if ((iScsiConnection->ConnectionState) != ConnectionStateConnected) {
        DebugPrint((0, "Not connected to target. Connection State : %d\n",
                    (iScsiConnection->ConnectionState)));

        Irp->IoStatus.Status = STATUS_DEVICE_NOT_CONNECTED;
        Irp->IoStatus.Information = 0L;
        IoCompleteRequest(Irp, IO_NO_INCREMENT);

        return STATUS_DEVICE_NOT_CONNECTED;
    }

    ASSERT((pdoExtension->CurrentProtocolState) == PSFullFeaturePhase);

    //
    // Get the lock to synchronize access to iSCSI
    // Connection structure
    //
    KeAcquireSpinLock(&(iScsiConnection->RequestLock), 
                      &oldIrql);

    if ((iScsiConnection->NumberOfReqsInProgress) >= 
        (iScsiConnection->MaxPendingRequests)) {

        //
        // Queue it to the request list for this connection
        //

        IoMarkIrpPending(Irp);

        ExInterlockedInsertTailList(&(iScsiConnection->RequestList),
                                    &(Irp->Tail.Overlay.ListEntry),
                                    &(iScsiConnection->ListSpinLock));

        KeReleaseSpinLock(&(iScsiConnection->RequestLock), 
                          oldIrql);

        return STATUS_PENDING;
    }

    expectedDataLen = 0;

    writeToDevice = FALSE;

    packetSize = sizeof(ISCSI_SCSI_COMMAND);

    if (srb->SrbFlags & SRB_FLAGS_DATA_IN) {

        expectedDataLen = srb->DataTransferLength;

    } else if (srb->SrbFlags & SRB_FLAGS_DATA_OUT) {

        //
        // If we are writing to the device, the data
        // is sent as immediate data
        //
        packetSize += srb->DataTransferLength;
        writeToDevice = TRUE;
    }

    if (Irp->MdlAddress) {

        offset = (ULONG_PTR) ((ULONG_PTR) srb->DataBuffer -
                         (ULONG_PTR) MmGetMdlVirtualAddress(Irp->MdlAddress));

        DebugPrint((3, "Srb DataBuffer : 0x%x, Offset into the MDL : 0x%x\n",
                    srb->DataBuffer, offset));

        requestBuffer = MmGetSystemAddressForMdlSafe(
                           Irp->MdlAddress,
                           ((Irp->RequestorMode == KernelMode) ?
                            HighPagePriority :
                            NormalPagePriority));
        if (requestBuffer != NULL) {
            UCHAR readChar;

            //
            // Save the original DataBuffer passed in the SRB
            //
            originalDataBuffer = srb->DataBuffer;

            DebugPrint((3, "SendCommand : Original DataBuffer - 0x%08x\n",
                        originalDataBuffer));

            srb->DataBuffer = (PVOID) ((ULONG_PTR) requestBuffer +
                                      (ULONG_PTR) offset);
            //
            // This is for catching the case where the Srb DataBuffer
            // we have generated is not valid
            //
            readChar = *((PUCHAR)(srb->DataBuffer));

            DebugPrint((3, 
                        "OpCode : %d, SRB DataBuffer : %x. ReadChar : %d\n", 
                        srb->Cdb[0],
                        srb->DataBuffer,
                        readChar));

            DebugPrint((3, "System address for requestBuffer : 0x%08x\n",
                        requestBuffer));
        } else {

            DebugPrint((1, "Failed to get System Address for MDL\n"));

            status = STATUS_INSUFFICIENT_RESOURCES;

            Irp->IoStatus.Status = status;
            Irp->IoStatus.Information = 0L;
            IoCompleteRequest(Irp, IO_NO_INCREMENT);

            goto iSpSendScsiCommandExit;
        }
    }

    iScsiScsiCommand = iSpAllocatePool(
                          NonPagedPool,
                          packetSize,
                          ISCSI_TAG_SCSI_CMD);
    if (iScsiScsiCommand == NULL) {

        DebugPrint((1, "Could not allocate iSCSI Command packet\n"));

        //
        // Restore the original DataBuffer in the SRB
        //
        if (originalDataBuffer != NULL) {
            srb->DataBuffer = originalDataBuffer;
        }

        status = STATUS_INSUFFICIENT_RESOURCES;

        Irp->IoStatus.Status = status;
        Irp->IoStatus.Information = 0L;
        IoCompleteRequest(Irp, IO_NO_INCREMENT);

        goto iSpSendScsiCommandExit;
    }

    RtlZeroMemory(iScsiScsiCommand, packetSize);

    iScsiScsiCommand->OpCode = ISCSIOP_SCSI_COMMAND;

    if (srb->SrbFlags & SRB_FLAGS_DATA_IN) {
        iScsiScsiCommand->Read = SETBITON;  
    } 

    if (writeToDevice == TRUE) {

        SetUlongInArray((iScsiScsiCommand->Length),
                        (srb->DataTransferLength));

        SetUlongInArray((iScsiScsiCommand->ExpDataXferLength),
                        (srb->DataTransferLength));

        //
        // Issue : nramas : 01/02/2001
        //    This should be later changed to chained MDLs
        //
        RtlCopyMemory((iScsiScsiCommand + 1),
                      (srb->DataBuffer),
                      srb->DataTransferLength);
    }

    if (expectedDataLen != 0) {
        SetUlongInArray((iScsiScsiCommand->ExpDataXferLength),
                        expectedDataLen);
    }

    SetUlongInArray((iScsiScsiCommand->CmdRN),
                    (iScsiConnection->CommandRefNum));

    SetUlongInArray((iScsiScsiCommand->ExpStatRN),
                    (iScsiConnection->CurrentStatusRefNum));

    //DebugPrint((3, "Exp StatRN : 0x%x\n", 
    //          (iScsiConnection->ExpStatusRefNum)));

    SetUlongInArray((iScsiScsiCommand->TaskTag),
                    (iScsiConnection->InitiatorTaskTag));

    ASSERT((srb->CdbLength) <= 16);

    DebugPrint((3, "CDB : "));
    for (inx = 0; inx < (srb->CdbLength); inx++) {
        DebugPrint((3, "0x%02x ", srb->Cdb[inx]));
    }
    DebugPrint((3, "\n"));

    RtlCopyMemory((iScsiScsiCommand->Cdb),
                  (srb->Cdb),
                  (srb->CdbLength));

    cmdRN = (iScsiConnection->CommandRefNum); 
    inx = cmdRN % (iScsiConnection->MaxPendingRequests);
    if (inx == 0) {
        inx = (iScsiConnection->MaxPendingRequests);
    }
    
    DebugPrint((3, "Request will be added to slot %d\n",
                inx));

    currentRequest = &(iScsiConnection->ActiveClientRequests[inx]);

    ASSERT((currentRequest->InUse) == FALSE);

    currentRequest->CommandRefNum = iScsiConnection->CommandRefNum;

    currentRequest->Irp = Irp;

    currentRequest->DeviceObject = DeviceObject;

    currentRequest->TaskTag = iScsiConnection->InitiatorTaskTag;

    if (originalDataBuffer != NULL) {
        currentRequest->OriginalDataBuffer = originalDataBuffer;
    }

    currentRequest->RequestBuffer = srb->DataBuffer;

    currentRequest->RequestBufferOffset = 0;

    currentRequest->InUse = TRUE;

    currentRequest->Completed = FALSE;

    (iScsiConnection->InitiatorTaskTag)++;
    if ((iScsiConnection->InitiatorTaskTag) == 0) {
        iScsiConnection->InitiatorTaskTag = 1;
    }

    (iScsiConnection->CommandRefNum)++;

    (iScsiConnection->NumberOfReqsInProgress)++;

    DebugPrint((3, "Number of requests in progress %d\n",
                (iScsiConnection->NumberOfReqsInProgress)));

    DebugPrint((3, 
                "CmdRN %d. PacketSize %d, Expected Xfer Length %d\n",
                cmdRN, packetSize, expectedDataLen));

    DebugPrint((3, "SCSI packet : %x\n", iScsiScsiCommand));
    status = iSpSendData(iScsiConnection->ConnectionDeviceObject,
                         iScsiConnection->ConnectionFileObject,
                         iScsiScsiCommand,
                         packetSize,
                         &bytesSent);

    if (NT_SUCCESS(status)) {

        //
        // Command packet successfully sent. Mark the IRP pending,
        // and return STATUS_PENDING
        //
        IoMarkIrpPending(Irp);

        status = STATUS_PENDING;

    } else {
        DebugPrint((0, "Failed to send SCSI Command. Status : 0x%08x\n",
                    status));

        if (currentRequest->OriginalDataBuffer) {
            srb->DataBuffer = currentRequest->OriginalDataBuffer;
        }

        (iScsiConnection->InitiatorTaskTag)--;
        (iScsiConnection->CommandRefNum)--;
        (iScsiConnection->NumberOfReqsInProgress)--;

        RtlZeroMemory(currentRequest, sizeof(ACTIVE_REQUESTS));

        Irp->IoStatus.Status = status;
        Irp->IoStatus.Information = 0L;
        IoCompleteRequest(Irp, IO_NO_INCREMENT);
    }
 
iSpSendScsiCommandExit:

    KeReleaseSpinLock(&(iScsiConnection->RequestLock), 
                      oldIrql);

    return status;
}


NTSTATUS
iSpProcessScsiResponse(
    PISCSI_CONNECTION IScsiConnection,
    PISCSI_SCSI_RESPONSE IScsiScsiResponse
    )
{
    PISCSI_PDO_EXTENSION pdoExtension;
    PCOMMON_EXTENSION commonExtension; 
    PIO_STACK_LOCATION irpStack; 
    PSCSI_REQUEST_BLOCK srb;
    PIRP  irp;
    PDEVICE_OBJECT deviceObject;
    PACTIVE_REQUESTS currentRequest;
    ULONG inx;
    ULONG taskTag;
    ULONG cmdStatus;
    ULONG iScsiStatus;
    ULONG statusRN;
    ULONG length;
    ULONG expCmdRefNum;
    NTSTATUS status;

    ASSERT((IScsiScsiResponse->OpCode) == ISCSIOP_SCSI_RESPONSE);

    ASSERT(IScsiConnection->Type == ISCSI_CONNECTION_TYPE);

    currentRequest = IScsiConnection->CurrentRequest;

    GetUlongFromArray((IScsiScsiResponse->TaskTag),
                      taskTag);

    GetUlongFromArray((IScsiScsiResponse->StatusRN),
                      statusRN);

    IScsiConnection->CurrentStatusRefNum = statusRN;

    DebugPrint((3, "iSpProcessScsiResponse - TaskTag 0x%08x\n",
                taskTag));

    (IScsiConnection->NumberOfReqsInProgress)--;

    commonExtension = 
        (currentRequest->DeviceObject)->DeviceExtension;
        
    pdoExtension = (PISCSI_PDO_EXTENSION) commonExtension;

    irp = currentRequest->Irp;
    deviceObject = currentRequest->DeviceObject;
    irpStack = IoGetCurrentIrpStackLocation(irp);
    srb = irpStack->Parameters.Scsi.Srb;

    cmdStatus = IScsiScsiResponse->CmdStatus;
    iScsiStatus = IScsiScsiResponse->iSCSIStatus;

    GetUlongFromArray((IScsiScsiResponse->ExpCmdRN),
                      (expCmdRefNum));

    GetUlongFromArray((IScsiScsiResponse->MaxCmdRN),
                      (IScsiConnection->MaxCommandRefNum));

    DebugPrint((3, 
                "SCSI Response : Expected CmdRefNum %d, MaxCmdRN  %d\n",
                expCmdRefNum,
                (IScsiConnection->MaxCommandRefNum)));

    //
    // Retrieve the size of immediate data
    //
    GetUlongFromArray((IScsiScsiResponse->Length),
                      length);

    if (cmdStatus == SCSISTAT_GOOD) {

        ASSERT(iScsiStatus == ISCSISTAT_GOOD);

        irp->IoStatus.Status = STATUS_SUCCESS;

        if ((srb->SrbFlags) & SRB_FLAGS_DATA_IN) {

            //
            // Read request. Fill the number of bytes read
            //
            irp->IoStatus.Information = currentRequest->ReceivedDataLength;

            srb->DataTransferLength = currentRequest->ReceivedDataLength;

        } else if ((srb->SrbFlags) & SRB_FLAGS_DATA_OUT) {

            //
            // Write request. Set IoStatus Information to
            // number of bytes written (srb->DataTransferLength)
            //
            irp->IoStatus.Information = srb->DataTransferLength;

        } else {

            //
            // No I/O involved in this request.
            //
            irp->IoStatus.Information = 0;
        }

        srb->SrbStatus = SRB_STATUS_SUCCESS;

        srb->ScsiStatus = SCSISTAT_GOOD;

        DebugPrint((3, "Info : 0x%x\n", irp->IoStatus.Information));

    } else {

        DebugPrint((0, "Command failed\n"));
     
        srb->ScsiStatus = SCSISTAT_CHECK_CONDITION;

        //
        // Should map SRB status error using sense data
        //
        srb->SrbStatus = SRB_STATUS_ERROR;

        //
        // If the upper driver passed a valid senseinfo buffer,
        // and the target sent sense data, copy it to the buffer
        //
        if ((srb->SenseInfoBufferLength) && (length > 0)) {

            ULONG senseInx;

            if (length > (srb->SenseInfoBufferLength)) {
                DebugPrint((0, 
                            "Sense info greater than buffer size. Size %d\n",
                            length));

                length = srb->SenseInfoBufferLength;
            }

            DebugPrint((0, "Command 0x%02x failed. Sense Data : ",
                        srb->Cdb[0]));
            for (senseInx = 0; senseInx < length; senseInx++) {
                DebugPrint((0, "%02x ", currentRequest->SenseData[senseInx]));
            }
            DebugPrint((0, "\n"));

            RtlCopyMemory(srb->SenseInfoBuffer,
                          currentRequest->SenseData,
                          length);

            srb->SrbStatus |= SRB_STATUS_AUTOSENSE_VALID;
            srb->SenseInfoBufferLength = (UCHAR) length;
        }

        //
        // ISSUE : nramas : 01/15/2001
        //         Need to determine the correct NTSTATUS here
        //
        irp->IoStatus.Status = STATUS_IO_DEVICE_ERROR;
        irp->IoStatus.Information = 0;

    }

    //
    // Restore the original DataBuffer in the SRB
    //
    if ((currentRequest->OriginalDataBuffer) != NULL) {

        DebugPrint((3, "ProcessResponse : Original DataBuffer - 0x%08x\n",
                    currentRequest->OriginalDataBuffer));

        srb->DataBuffer = currentRequest->OriginalDataBuffer;
    }

    DebugPrint((3, "Irp done : 0x%x\n", irp));

    IoCompleteRequest(irp, IO_NO_INCREMENT);

    RtlZeroMemory(currentRequest, sizeof(ACTIVE_REQUESTS));

    return STATUS_SUCCESS;
}


NTSTATUS
iSpProcessReceivedData(
    IN PISCSI_CONNECTION IScsiConnection,
    IN ULONG BytesIndicated,
    OUT ULONG *BytesTaken,
    IN PVOID DataBuffer
    )
{
    PUCHAR requestBuffer = NULL;
    PISCSI_GENERIC_HEADER iScsiHeader = NULL;
    PISCSI_SCSI_RESPONSE iScsiResponse = NULL;
    PISCSI_SCSI_DATA_READ iScsiDataRead = NULL;
    PACTIVE_REQUESTS currentRequest = NULL;
    ULONG length;
    ULONG receivedDataLen;
    ULONG inx;
    ULONG taskTag;
    ULONG statusRefNum;
    ULONG expCmdRN;
    LONG byteCount;
    NTSTATUS status = STATUS_SUCCESS;
    UCHAR opCode;
    KIRQL oldIrql;
   
    //
    // We always take all the data given to us 
    // in this routine. So set BytesTaken to
    // BytesIndicated
    *BytesTaken = BytesIndicated;

    byteCount = (LONG) BytesIndicated;

    DebugPrint((3, "Bytes indicated : %d\n", BytesIndicated));

    //
    // Get the lock to synchronize access to iSCSI
    // Connection structure
    //
    KeAcquireSpinLock(&(IScsiConnection->RequestLock), 
                      &oldIrql);

    while (byteCount > 0) {
        if ((IScsiConnection->ReceiveState) == ReceiveHeader) {

            DebugPrint((3, "Receiving header\n"));

            if ((IScsiConnection->CompleteHeaderReceived)== FALSE) {
                LONG bytesToCopy;
                BOOLEAN headerComplete = FALSE;

                bytesToCopy = sizeof(ISCSI_GENERIC_HEADER) -
                               (IScsiConnection->IScsiPacketOffset);

                DebugPrint((0, "CHR False. ToCopy : %d, Count : %d\n",
                            bytesToCopy, byteCount));

                if (byteCount < bytesToCopy) {
                    bytesToCopy = byteCount;
                } else {
                    headerComplete = TRUE;
                }
                
                RtlCopyMemory((IScsiConnection->IScsiPacket) +
                              (IScsiConnection->IScsiPacketOffset),
                              DataBuffer,
                              bytesToCopy);

                if (headerComplete == FALSE) {

                    DebugPrint((0, "CHR still FALSE\n"));

                    IScsiConnection->IScsiPacketOffset += bytesToCopy;
                    KeReleaseSpinLock(&(IScsiConnection->RequestLock), 
                                      oldIrql);

                    return STATUS_SUCCESS;
                } else {

                    DebugPrint((0, "Header complete\n"));

                    IScsiConnection->IScsiPacketOffset = 0;
                    IScsiConnection->CompleteHeaderReceived = TRUE;

                    byteCount -= bytesToCopy;
                    ASSERT(byteCount >= 0);
                    (PUCHAR) DataBuffer += bytesToCopy;
                }

            } else if (byteCount < sizeof(ISCSI_GENERIC_HEADER)) {
                DebugPrint((0, 
                            "Complete header NOT received. Count : %d\n",
                            byteCount));

                IScsiConnection->CompleteHeaderReceived = FALSE;

                RtlCopyMemory((IScsiConnection->IScsiPacket),
                              DataBuffer,
                              byteCount);

                IScsiConnection->IScsiPacketOffset = byteCount;

                KeReleaseSpinLock(&(IScsiConnection->RequestLock), 
                                  oldIrql);

                return STATUS_SUCCESS;
            } else {
                RtlCopyMemory((IScsiConnection->IScsiPacket),
                              DataBuffer,
                              sizeof(ISCSI_GENERIC_HEADER));

                byteCount -= sizeof(ISCSI_GENERIC_HEADER);
                ASSERT(byteCount >= 0);
                (PUCHAR) DataBuffer += sizeof(ISCSI_GENERIC_HEADER);
            }

            //
            // At this point, we should have the complete header
            // available
            //
            iScsiHeader = 
                (PISCSI_GENERIC_HEADER) (IScsiConnection->IScsiPacket);

            opCode = iScsiHeader->OpCode;

            //
            // Retrieve the length of immediate data
            // associated with this iSCSI packet
            //
            GetUlongFromArray((iScsiHeader->Length),
                              length);

            DebugPrint((3, "Opcode : %x, Length : %x\n",
                        opCode, length));

            switch (opCode) {
                case ISCSIOP_SCSI_DATA_READ: {
                    iScsiDataRead = (PISCSI_SCSI_DATA_READ) iScsiHeader;

                    GetUlongFromArray((iScsiDataRead->InitiatorTransferTag),
                                      taskTag);

                    DebugPrint((3, "XfrTag - 0x%08x\n", taskTag));

                    if ((IScsiConnection->CurrentRequest) == NULL) {

                        inx = iSpGetActiveClientRequestIndex(
                                        IScsiConnection,
                                        taskTag);

                        DebugPrint((3, "ActiveClientRequest index : %d\n", inx));

                        ASSERT((inx <= (IScsiConnection->MaxPendingRequests)));

                        currentRequest = &(IScsiConnection->ActiveClientRequests[inx]);

                        IScsiConnection->CurrentRequest = currentRequest;

                    } else {
                        DebugPrint((0, "CurrentRequest not null in data\n"));
                        currentRequest = IScsiConnection->CurrentRequest;
                    }

                    currentRequest->CommandStatus = iScsiDataRead->CommandStatus;

                    //
                    // If the command status is not SCSISTAT_GOOD, use
                    // SenseData buffer to read the sense info. Else, use
                    // RequestBuffer to read input data
                    //
                    if ((iScsiDataRead->CommandStatus) != SCSISTAT_GOOD) {
                        DebugPrint((0, "Command status is %x\n",
                                    iScsiDataRead->CommandStatus));
                        requestBuffer = currentRequest->SenseData;
                    } else {
                        requestBuffer = currentRequest->RequestBuffer;
                    }

                    //
                    // If immediate data is available, copy that
                    //
                    if (length != 0) {
                        ULONG receivedDataLen;

                        IScsiConnection->ReceiveState = ReceiveData;

                        if ((LONG)length <= byteCount ) {
                            receivedDataLen = length;
                        } else {
                            receivedDataLen = byteCount;
                        }

                        if ((iScsiDataRead->CommandStatus) != SCSISTAT_GOOD) {
                            ASSERT(receivedDataLen <= sizeof(currentRequest->SenseData));
                        }

                        RtlCopyMemory(requestBuffer,
                                      DataBuffer,
                                      receivedDataLen);

                        (PUCHAR) DataBuffer += receivedDataLen;
                        byteCount -= receivedDataLen;
                        ASSERT(byteCount >= 0);

                        if (byteCount != 0) {
                            DebugPrint((1, "More bytes available\n"));
                        }

                        currentRequest->ExpectedDataLength = length;

                        currentRequest->RequestBufferOffset = receivedDataLen;

                        currentRequest->ReceivedDataLength = receivedDataLen;

                        if ((currentRequest->ExpectedDataLength) ==
                            currentRequest->ReceivedDataLength) {
                            IScsiConnection->ReceiveState = ReceiveHeader;
                        }
                    } 

                    break;
                }

                case ISCSIOP_SCSI_RESPONSE: {
                    BOOLEAN responseComplete = FALSE;

                    iScsiResponse = (PISCSI_SCSI_RESPONSE) iScsiHeader;

                    IScsiConnection->ReceiveState = ReceiveHeader;

                    GetUlongFromArray((iScsiResponse->TaskTag),
                                      taskTag);

                    DebugPrint((3, "ResTag - 0x%08x\n", taskTag));

                    if ((IScsiConnection->CurrentRequest) == NULL) {

                        inx = iSpGetActiveClientRequestIndex(
                                        IScsiConnection,
                                        taskTag);

                        DebugPrint((3, "ActiveClientRequest index : %d\n", inx));

                        if (inx > (IScsiConnection->MaxPendingRequests)) {

                            ULONG cmdRN;

                            DebugPrint((0, 
                                        "Tag : %x. Will use cmdRN to search\n",
                                        taskTag));

                            GetUlongFromArray((iScsiResponse->ExpCmdRN),
                                              cmdRN);

                            inx = iSpGetReqIndexUsingCmdRN(IScsiConnection,
                                                           (cmdRN - 1));
                            DebugPrint((0, "Inx returned : 0x%x\n", inx));
                        }

                        ASSERT((inx <= (IScsiConnection->MaxPendingRequests)));

                        currentRequest = &(IScsiConnection->ActiveClientRequests[inx]);

                        IScsiConnection->CurrentRequest = currentRequest;

                    } else {
                        currentRequest = IScsiConnection->CurrentRequest;
                    }

                    currentRequest->CommandStatus = iScsiResponse->CmdStatus;

                    if ((iScsiResponse->CmdStatus) != SCSISTAT_GOOD) {
                        DebugPrint((0, "Command status is %x. Length : %d\n",
                                    iScsiResponse->CmdStatus,
                                    length));
                        requestBuffer = currentRequest->SenseData;
                    } else {
                        requestBuffer = currentRequest->RequestBuffer;
                    }

                    if (length != 0) {
                        ULONG receivedDataLen;

                        DebugPrint((3, "Non-zero length in response : %d\n",
                                    length));
                        IScsiConnection->ReceiveState = ReceiveData;

                        if ((LONG)length <= byteCount ) {
                            receivedDataLen = length;
                        } else {
                            receivedDataLen = byteCount;
                        }

                        if ((iScsiResponse->CmdStatus) != SCSISTAT_GOOD) { 
                            ASSERT(receivedDataLen <= sizeof(currentRequest->SenseData));
                        }

                        RtlCopyMemory(requestBuffer,
                                      DataBuffer,
                                      receivedDataLen);

                        (PUCHAR) DataBuffer += receivedDataLen;
                        byteCount -= receivedDataLen;
                        ASSERT(byteCount >= 0);

                        currentRequest->ExpectedDataLength = length;

                        currentRequest->RequestBufferOffset = receivedDataLen;

                        currentRequest->ReceivedDataLength = receivedDataLen;

                        if ((currentRequest->ExpectedDataLength) ==
                            currentRequest->ReceivedDataLength) {
                            IScsiConnection->ReceiveState = ReceiveHeader;

                            responseComplete = TRUE;
                            DebugPrint((3, "Response complete. Will process it\n"));
                        }
                    } else {
                        responseComplete = TRUE;
                    }

                    //
                    // Should use this field to determine
                    // Data Overrun\Underrun cases
                    //
                    if ((iScsiResponse->OverFlow) ||
                        (iScsiResponse->UnderFlow)) {
                        ULONG residualCount;

                        GetUlongFromArray((iScsiResponse->ResidualCount),
                                          residualCount);
                        DebugPrint((0, "Residualcount is : %d\n",
                                    residualCount));
                    }

                    if (responseComplete == TRUE) {

                        if ((iScsiResponse->CmdStatus) != SCSISTAT_GOOD) {

                            PUCHAR resBuff = 
                                (PUCHAR) (IScsiConnection->IScsiPacket);
                            ULONG inx0, inx1;
                            ULONG sizeRequired = sizeof(ISCSI_SCSI_RESPONSE);

                            DebugPrint((1, "\n Beginning Of Data\n"));
                            
                            inx0 = 0;
                            
                            while (inx0 < sizeRequired) {
                            
                                inx1 = 0;
                            
                                DebugPrint((1, "\t"));
                            
                                while ((inx1 < 4) && ((inx0+inx1) < sizeRequired)) {
                            
                                    DebugPrint((1, "%02x ",
                                                resBuff[inx0+inx1]));
                            
                                    inx1++;
                            
                                }
                            
                                DebugPrint((1, "\n"));
                            
                                inx0 += 4;
                            }
                            
                            DebugPrint((1, " End Of Data\n"));   

                        }

                        iSpProcessScsiResponse(
                            IScsiConnection,
                            (PISCSI_SCSI_RESPONSE)(IScsiConnection->IScsiPacket));

                        IScsiConnection->CurrentRequest = NULL;
                    }

                    break;
                }

                case ISCSIOP_NOP_IN_MESSAGE: {

                    PISCSI_NOP_IN iScsiNopIn = (PISCSI_NOP_IN) iScsiHeader;

                    IScsiConnection->ReceiveState = ReceiveHeader;

                    if (iScsiNopIn->Poll) {

                        //
                        // Need to handle this case. Should send
                        // response to the target
                        //
                        DebugPrint((0, "Target expects NOP OUT message\n"));
                    } else {
                        DebugPrint((1, "No NOP OUT message needed.\n"));
                    }

                    break;
                }

                default: {
                    ULONG inx0, inx1;

                    //
                    // Opcode that we don't currently handle.
                    // For the timebeing, just dump the iSCSI 
                    // packet.
                    //
                    DebugPrint((0, "Unknown opcode : 0x%02x\n", opCode));

                    inx0 = 0;
                    while(inx0 < 48) {
                        inx1 = 0;
                        DebugPrint((0, "\t"));
                        while ((inx1 < 4) &&
                               ((inx0+inx1) < 48)) {
                            DebugPrint((0, "0x%02x ", 
                                        ((PUCHAR)(iScsiHeader))[inx0+inx1]));
                            inx1++;
                        }
                        DebugPrint((0, "\n"));

                        inx0 += 4;
                    }
                    DebugPrint((0, "\n"));

                    break;
                }
            } // switch (opCode) 

        } else {
            ULONG bytesToCopy;
            UCHAR opCode;

            //
            // We are receiving the data portion of the packet
            //
            ASSERT((IScsiConnection->ReceiveState) == ReceiveData);

            ASSERT(IScsiConnection->CurrentRequest);

            DebugPrint((3, "Receiving data\n"));

            currentRequest = IScsiConnection->CurrentRequest;

            if ((currentRequest->CommandStatus) != SCSISTAT_GOOD) {
                requestBuffer = currentRequest->SenseData;
            } else {
                requestBuffer = currentRequest->RequestBuffer;
            }

            requestBuffer += currentRequest->RequestBufferOffset;

            bytesToCopy = ((currentRequest->ExpectedDataLength) -
                           (currentRequest->ReceivedDataLength));
            if ((LONG)bytesToCopy > byteCount)  {
                bytesToCopy = byteCount;
            } else {
                DebugPrint((3, "More bytes in current buffer than expected\n"));
            }

            RtlCopyMemory(requestBuffer, DataBuffer, bytesToCopy);
            byteCount -= bytesToCopy;
            (PUCHAR) DataBuffer += bytesToCopy;
            ASSERT(byteCount >= 0);

            currentRequest->RequestBufferOffset +=  bytesToCopy;

            currentRequest->ReceivedDataLength += bytesToCopy;

            if ((currentRequest->ExpectedDataLength) ==
                currentRequest->ReceivedDataLength) {

                DebugPrint((3, "Got all data. Bytes left %d\n", 
                            byteCount));

                IScsiConnection->ReceiveState = ReceiveHeader;

                opCode = IScsiConnection->IScsiPacket[0];
                if (opCode == ISCSIOP_SCSI_RESPONSE) {

                    DebugPrint((3, "Will process the response\n"));

                    iSpProcessScsiResponse(
                        IScsiConnection,
                        (PISCSI_SCSI_RESPONSE)(IScsiConnection->IScsiPacket));

                    IScsiConnection->CurrentRequest = NULL;
                }
            }
        }
    }

    KeReleaseSpinLock(&(IScsiConnection->RequestLock), 
                      oldIrql);

    return status;
}


ULONG
iSpGetActiveClientRequestIndex(
    PISCSI_CONNECTION IScsiConnection,
    ULONG TaskTag
    )
{
    ULONG retIndex = ~0;
    ULONG inx;

    DebugPrint((3, "Given Task Tag : 0x%08x\n", TaskTag));

    for (inx = 1; inx <= (IScsiConnection->MaxPendingRequests); inx++) {
        DebugPrint((3, "Index %d : CmdRN - 0x%08x\n",
                    inx, 
                    ((IScsiConnection->ActiveClientRequests[inx]).TaskTag)));

        if (((IScsiConnection->ActiveClientRequests[inx]).TaskTag)
            == TaskTag) {

            retIndex = inx;

            DebugPrint((1, "inx : 0x%04x\n", retIndex));

            break;
        }
    }

    if (retIndex > (IScsiConnection->MaxPendingRequests)) {
        DebugPrint((0, "Tag : Did not find the request for this response\n"));
    }

    return retIndex;
}



ULONG
iSpGetReqIndexUsingCmdRN(
    PISCSI_CONNECTION IScsiConnection,
    ULONG CmdRN
    )
{
    ULONG retIndex = ~0;
    ULONG inx;

    DebugPrint((3, "Given CmdRN : 0x%08x\n", CmdRN));

    for (inx = 1; inx <= (IScsiConnection->MaxPendingRequests); inx++) {

        if (((IScsiConnection->ActiveClientRequests[inx]).CommandRefNum)
            == CmdRN) {

            retIndex = inx;

            DebugPrint((1, "inx : 0x%04x\n", retIndex));

            break;
        }
    }

    if (retIndex > (IScsiConnection->MaxPendingRequests)) {
        ASSERTMSG("CmdRN : Did not find the request for this response\n", 
                  FALSE);
    }

    return retIndex;
}