/*++

Copyright (C) Microsoft Corporation, 2000

Module Name:

    internal.c

Abstract:

    This file contains internal routines 

Environment:

    kernel mode only

Revision History:

--*/

#include "port.h"


#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, iScsiPortFdoDeviceControl)
#pragma alloc_text(PAGE, iScsiPortFdoCreateClose)
#endif // ALLOC_PRAGMA

/*
PISCSI_PDO_EXTENSION
GetPdoExtension(
    IN PISCSI_FDO_EXTENSION fdoExtension
    );
*/


NTSTATUS
iScsiPortFdoDeviceControl(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )
{
    PISCSI_FDO_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
    PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
    NTSTATUS status;
    ULONG isRemoved;
    ULONG ioControlCode = irpStack->Parameters.DeviceIoControl.IoControlCode;

    DebugPrint((1, "FDO DeviceControl - IO control code : 0x%08x\n",
                ioControlCode));

    isRemoved = iSpAcquireRemoveLock(DeviceObject, Irp);
    if(isRemoved) {
        iSpReleaseRemoveLock(DeviceObject, Irp);

        Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;

        IoCompleteRequest(Irp, IO_NO_INCREMENT);

        return STATUS_DEVICE_DOES_NOT_EXIST;
    }

    switch (ioControlCode) {
        case IOCTL_STORAGE_QUERY_PROPERTY: {

            PSTORAGE_PROPERTY_QUERY query = Irp->AssociatedIrp.SystemBuffer;

            if(irpStack->Parameters.DeviceIoControl.InputBufferLength <
               sizeof(STORAGE_PROPERTY_QUERY)) {

                status = STATUS_INVALID_PARAMETER;
                break;
            }

            //
            // This routine will release the lock and complete the irp.
            //
            status = iScsiPortQueryProperty(DeviceObject, Irp);
            return status;

            break;
        }

        //
        // Get adapter capabilities.
        //
        case IOCTL_SCSI_GET_CAPABILITIES: {

            //
            // If the output buffer is equal to the size of the a PVOID then just
            // return a pointer to the buffer.
            //

            if (irpStack->Parameters.DeviceIoControl.OutputBufferLength
                == sizeof(PVOID)) {

                *((PVOID *)Irp->AssociatedIrp.SystemBuffer)
                    = &fdoExtension->IoScsiCapabilities;

                Irp->IoStatus.Information = sizeof(PVOID);
                status = STATUS_SUCCESS;
                break;

            }

            if (irpStack->Parameters.DeviceIoControl.OutputBufferLength
                < sizeof(IO_SCSI_CAPABILITIES)) {

                status = STATUS_BUFFER_TOO_SMALL;
                break;
            }

            RtlCopyMemory(Irp->AssociatedIrp.SystemBuffer,
                          &fdoExtension->IoScsiCapabilities,
                          sizeof(IO_SCSI_CAPABILITIES));

            Irp->IoStatus.Information = sizeof(IO_SCSI_CAPABILITIES);
            status = STATUS_SUCCESS;
            break;
        }

        case IOCTL_SCSI_PASS_THROUGH:
        case IOCTL_SCSI_PASS_THROUGH_DIRECT: {

            status = STATUS_NOT_SUPPORTED;
            break;
        }

        case IOCTL_SCSI_MINIPORT: {

            status = STATUS_NOT_SUPPORTED;
            break;
        }

        case IOCTL_SCSI_GET_DUMP_POINTERS: {

            status = STATUS_NOT_SUPPORTED;
            break;
        }

        case IOCTL_SCSI_RESCAN_BUS:
        case IOCTL_SCSI_GET_INQUIRY_DATA: {
            status = STATUS_NOT_SUPPORTED;
            break;
        }

        default: {
            DebugPrint((1,
                       "iScsiPortFdoDeviceControl: Unsupported IOCTL (%x)\n",
                       ioControlCode));

            status = STATUS_INVALID_DEVICE_REQUEST;

            break;
        }
    }
    //
    // Set status in Irp.
    //

    Irp->IoStatus.Status = status;

    iSpReleaseRemoveLock(DeviceObject, Irp);
    IoCompleteRequest(Irp, IO_NO_INCREMENT);
    return status;
}


NTSTATUS
iScsiPortFdoDispatch(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )
{
    PISCSI_FDO_EXTENSION fdoExtension = DeviceObject->DeviceExtension;
    PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);
    PSCSI_REQUEST_BLOCK srb = irpStack->Parameters.Scsi.Srb;
    PISCSI_PDO_EXTENSION pdoExtension;
    NTSTATUS status;
    ULONG isRemoved;
    BOOLEAN sendCommandToServer = FALSE;

    //
    // Should never get here. All SCSI requests are handled
    // by the PDO Dispatch routine
    //
    DebugPrint((0, "SRB function sent to FDO Dispatch\n"));

    Irp->IoStatus.Status = STATUS_INVALID_DEVICE_REQUEST;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);

    return STATUS_INVALID_DEVICE_REQUEST;

/*
    isRemoved = iSpAcquireRemoveLock(DeviceObject, Irp);

    if (isRemoved && !IS_CLEANUP_REQUEST(irpStack)) {

        Irp->IoStatus.Status = STATUS_DEVICE_DOES_NOT_EXIST;

        iSpReleaseRemoveLock(DeviceObject, Irp);

        IoCompleteRequest(Irp,
                          IO_NO_INCREMENT);

        return STATUS_DEVICE_DOES_NOT_EXIST;
    }

    if ((srb->Function) != SRB_FUNCTION_EXECUTE_SCSI) {
        DebugPrint((1, "FdoDispatch - SRB Function : 0x%x\n",
                    srb->Function));
    }

    pdoExtension = GetPdoExtension(fdoExtension);

    ASSERT(pdoExtension != NULL);

    switch (srb->Function) {

        case SRB_FUNCTION_SHUTDOWN:
        case SRB_FUNCTION_FLUSH:  
        case SRB_FUNCTION_LOCK_QUEUE:
        case SRB_FUNCTION_UNLOCK_QUEUE:
        case SRB_FUNCTION_IO_CONTROL:
        case SRB_FUNCTION_WMI:  {
    
            //
            // We won't handle these functions on the client
            // side for the timebeing. 
            //  
            status = STATUS_SUCCESS;
            srb->SrbStatus = SRB_STATUS_SUCCESS;

            break;
        }
    
        case SRB_FUNCTION_SHUTDOWN:
        case SRB_FUNCTION_FLUSH:  {

            //
            // Send SCSIOP_SYNCHRONIZE_CACHE command
            // to flush the queue
            //
            srb->CdbLength = 10;
            srb->Cdb[0] = SCSIOP_SYNCHRONIZE_CACHE;

            IoMarkIrpPending(Irp);
    
            sendCommandToServer = TRUE;

            status = STATUS_PENDING;

            break;
        }

        case SRB_FUNCTION_EXECUTE_SCSI: {
    
            //
            // Mark Irp status pending.
            //
    
            IoMarkIrpPending(Irp);
    
            sendCommandToServer = TRUE;

            status = STATUS_PENDING;
        }
    
        case SRB_FUNCTION_RELEASE_QUEUE:
        case SRB_FUNCTION_FLUSH_QUEUE: {
    
            //
            // These will be handled on the server
            // side. Here, just return STATUS_SUCCESS
            //
            status = STATUS_SUCCESS;
            srb->SrbStatus = SRB_STATUS_SUCCESS;

            break;
        }
    
        case SRB_FUNCTION_RESET_BUS: {
    
            DebugPrint((1, "FdoDospatch : Received reset request\n"));

            srb->SrbStatus = SRB_STATUS_SUCCESS;
            status = STATUS_SUCCESS;
    
            break;
        }
    
        case SRB_FUNCTION_ABORT_COMMAND: {
    
            DebugPrint((1, "FdoDispatch: SCSI Abort command\n"));

            srb->SrbStatus = SRB_STATUS_SUCCESS;
            status = STATUS_SUCCESS;    
    
            break;
        }
    
        case SRB_FUNCTION_ATTACH_DEVICE:
        case SRB_FUNCTION_CLAIM_DEVICE:
        case SRB_FUNCTION_RELEASE_DEVICE: {
    
            iSpAcquireRemoveLock((pdoExtension->CommonExtension.DeviceObject),
                                 Irp);
    
            status = iSpClaimLogicalUnit(fdoExtension, 
                                         pdoExtension,
                                         Irp);
    
            iSpReleaseRemoveLock((pdoExtension->CommonExtension.DeviceObject),
                                 Irp);
    
            break;
        }
    
        default: {
    
            //
            // Found unsupported SRB function.
            //
            DebugPrint((1,
                        "FdoDispatch: Unsupported function, SRB %p\n",
                        srb));
    
            srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
            status = STATUS_INVALID_DEVICE_REQUEST;
            break;
        }
    }

    if (sendCommandToServer == FALSE) {
        iSpReleaseRemoveLock(DeviceObject, Irp);
        Irp->IoStatus.Status = status;
        IoCompleteRequest(Irp, IO_NO_INCREMENT);
        return status;
    }

    DebugPrint((3, "FdoDispatch : Will send the command to the server\n"));

    status = iSpSendScsiCommand(DeviceObject, Irp);

    //
    // The lock will be released and IRP will be completed in
    // iSpSendScsiCommand.
    //
    if (NT_SUCCESS(status)) {
        DebugPrint((3, 
                    "FdoDispatch : Command sent to the server.\n"));
        status = STATUS_PENDING;
    } else {
        DebugPrint((1, 
                    "FdoDispatch : Failed to send the command. Status : %x\n",
                    status));
    }
    
    return status;
    */
}



NTSTATUS
iScsiPortFdoCreateClose(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )
{
    PCOMMON_EXTENSION commonExtension = DeviceObject->DeviceExtension;

    PIO_STACK_LOCATION irpStack = IoGetCurrentIrpStackLocation(Irp);

    NTSTATUS status = STATUS_SUCCESS;

    ULONG isRemoved;

    isRemoved = iSpAcquireRemoveLock(DeviceObject, Irp);
    if(irpStack->MajorFunction == IRP_MJ_CREATE) {

        if(isRemoved != NO_REMOVE) {
            status = STATUS_DEVICE_DOES_NOT_EXIST;
        } else if(commonExtension->CurrentPnpState != IRP_MN_START_DEVICE) {
            status = STATUS_DEVICE_NOT_READY;
        }
    }

    iSpReleaseRemoveLock(DeviceObject, Irp);
    status = STATUS_SUCCESS;
    Irp->IoStatus.Status = status;
    IoCompleteRequest(Irp, IO_NO_INCREMENT);

    return status;
}


NTSTATUS
iSpSetEvent(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp,
    IN PVOID Context
    )
{
    KeSetEvent((PKEVENT)Context, 0, FALSE);
    return STATUS_MORE_PROCESSING_REQUIRED;
}


NTSTATUS
iSpClaimLogicalUnit(
    IN PISCSI_FDO_EXTENSION FdoExtension,
    IN PISCSI_PDO_EXTENSION PdoExtension,
    IN PIRP Irp
    )
{
    PIO_STACK_LOCATION irpStack;
    PSCSI_REQUEST_BLOCK srb;
    PDEVICE_OBJECT saveDevice;

    NTSTATUS status;

    PAGED_CODE();

    DebugPrint((3, "Entering iSpClaimLogicalUnit\n"));

    //
    // Get SRB address from current IRP stack.
    //

    irpStack = IoGetCurrentIrpStackLocation(Irp);

    srb = (PSCSI_REQUEST_BLOCK) irpStack->Parameters.Others.Argument1;

    if (srb->Function == SRB_FUNCTION_RELEASE_DEVICE) {

        PdoExtension->IsClaimed = FALSE;
        srb->SrbStatus = SRB_STATUS_SUCCESS;
        return(STATUS_SUCCESS);
    }

    //
    // Check for a claimed device.
    //

    if (PdoExtension->IsClaimed) {

        DebugPrint((0, "Device already claimed\n"));
        srb->SrbStatus = SRB_STATUS_BUSY;
        return(STATUS_DEVICE_BUSY);
    }

    //
    // Save the current device object.
    //

    saveDevice = PdoExtension->CommonExtension.DeviceObject;

    //
    // Update the lun information based on the operation type.
    //

    if (srb->Function == SRB_FUNCTION_CLAIM_DEVICE) {
        PdoExtension->IsClaimed = TRUE;
    }

    if (srb->Function == SRB_FUNCTION_ATTACH_DEVICE) {
        ASSERT(FALSE);
        PdoExtension->CommonExtension.DeviceObject = srb->DataBuffer;
    }

    srb->DataBuffer = saveDevice;

    srb->SrbStatus = SRB_STATUS_SUCCESS;

    DebugPrint((3, "Successfully claimed the device\n"));
    return(STATUS_SUCCESS);
}

/*

PISCSI_PDO_EXTENSION
GetPdoExtension(
    IN PISCSI_FDO_EXTENSION fdoExtension
    )
{
    return ((fdoExtension->UpperPDO)->DeviceExtension);
}
*/


NTSTATUS
iSpProcessScsiRequest(
    IN PDEVICE_OBJECT LogicalUnit,
    IN PSCSI_REQUEST_BLOCK Srb
    )
{
    PISCSI_PDO_EXTENSION pdoExtension = LogicalUnit->DeviceExtension;
    PIRP irp;

    ULONG bytesReturned;

    NTSTATUS status;

    PAGED_CODE();

    irp = IoAllocateIrp((LogicalUnit->StackSize) + 1, FALSE);
    if (irp == NULL) {
        DebugPrint((1, "IssueInquiry : Failed to allocate IRP.\n"));
        return STATUS_INSUFFICIENT_RESOURCES;
    }

    IoInitializeIrp(irp,
                    IoSizeOfIrp((LogicalUnit->StackSize) + 1),
                    ((LogicalUnit->StackSize) + 1));

    status = iSpSendSrbSynchronous(LogicalUnit,
                                   Srb,
                                   irp,
                                   Srb->DataBuffer,
                                   Srb->DataTransferLength,
                                   Srb->SenseInfoBuffer,
                                   Srb->SenseInfoBufferLength,
                                   &bytesReturned
                                   );

    ASSERT(bytesReturned <= (Srb->DataTransferLength));

    if(!NT_SUCCESS(status)) {

        DebugPrint((1, "Failed to process SRB. Status : %x\n",
                    status));
    }

    IoFreeIrp(irp);

    return status;
}