BOOLEAN
VirtIoInterrupt(
    IN PVOID DeviceExtension
    )
{
    pblk_req            vbr;
    unsigned int        len;
    PADAPTER_EXTENSION  adaptExt;
    BOOLEAN             isInterruptServiced = FALSE;
    PSCSI_REQUEST_BLOCK Srb;
    ULONG               intReason = 0;
    adaptExt = (PADAPTER_EXTENSION)DeviceExtension;

    RhelDbgPrint(TRACE_LEVEL_VERBOSE, ("%s (%d)\n", __FUNCTION__, KeGetCurrentIrql()));
    intReason = VirtIODeviceISR((VirtIODevice*)DeviceExtension);
    if ( intReason == 1) {
        isInterruptServiced = TRUE;
        while((vbr = (pblk_req)adaptExt->vq->vq_ops->get_buf(adaptExt->vq, &len)) != NULL) {
           Srb = (PSCSI_REQUEST_BLOCK)vbr->req;
           if (Srb) {
              switch (vbr->status) {
              case VIRTIO_BLK_S_OK:
                 Srb->SrbStatus = SRB_STATUS_SUCCESS;
                 break;
              case VIRTIO_BLK_S_UNSUPP:
                 Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
                 break;
              default:
                 Srb->SrbStatus = SRB_STATUS_ERROR;
                 RhelDbgPrint(TRACE_LEVEL_ERROR, ("SRB_STATUS_ERROR\n"));
                 break;
              }
           }
           if (vbr->out_hdr.type == VIRTIO_BLK_T_FLUSH) {
#ifdef USE_STORPORT
              --adaptExt->in_fly;
#endif
              CompleteSRB(DeviceExtension, Srb);
           } else if (vbr->out_hdr.type == VIRTIO_BLK_T_GET_ID) {
              adaptExt->sn_ok = TRUE;
           } else if (Srb) {
#ifdef USE_STORPORT
              --adaptExt->in_fly;
#endif
              CompleteDPC(DeviceExtension, vbr, 0);
           }
        }
    } else if (intReason == 3) {
        adaptExt->rescan_geometry = TRUE;
        ScsiPortNotification( BusChangeDetected, DeviceExtension, 0);
        isInterruptServiced = TRUE;
    }
#ifdef USE_STORPORT
    if (adaptExt->in_fly > 0) {
        adaptExt->vq->vq_ops->kick(adaptExt->vq);
    }
#endif
    RhelDbgPrint(TRACE_LEVEL_VERBOSE, ("%s isInterruptServiced = %d\n", __FUNCTION__, isInterruptServiced));
    return isInterruptServiced;
}
BOOLEAN
VirtIoMSInterruptRoutine (
    IN PVOID  DeviceExtension,
    IN ULONG  MessageID
    )
{
    pblk_req            vbr;
    unsigned int        len;
    PADAPTER_EXTENSION  adaptExt;
    PSCSI_REQUEST_BLOCK Srb;
    BOOLEAN             isInterruptServiced = FALSE;

    adaptExt = (PADAPTER_EXTENSION)DeviceExtension;

    RhelDbgPrint(TRACE_LEVEL_VERBOSE,
                 ("<--->%s : MessageID 0x%x\n", __FUNCTION__, MessageID));

    if (MessageID == 0) {
       adaptExt->rescan_geometry = TRUE;
       StorPortNotification( BusChangeDetected, DeviceExtension, 0);
       return TRUE;
    }

    while((vbr = (pblk_req)adaptExt->vq->vq_ops->get_buf(adaptExt->vq, &len)) != NULL) {
        Srb = (PSCSI_REQUEST_BLOCK)vbr->req;
        if (Srb) {
           switch (vbr->status) {
           case VIRTIO_BLK_S_OK:
              Srb->SrbStatus = SRB_STATUS_SUCCESS;
              break;
           case VIRTIO_BLK_S_UNSUPP:
              Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
              break;
           default:
              Srb->SrbStatus = SRB_STATUS_ERROR;
              RhelDbgPrint(TRACE_LEVEL_ERROR, ("SRB_STATUS_ERROR\n"));
              break;
           }
        }
        if (vbr->out_hdr.type == VIRTIO_BLK_T_FLUSH) {
            --adaptExt->in_fly;
            CompleteSRB(DeviceExtension, Srb);
        } else if (vbr->out_hdr.type == VIRTIO_BLK_T_GET_ID) {
            adaptExt->sn_ok = TRUE;
        } else if (Srb) {
            --adaptExt->in_fly;
            CompleteDPC(DeviceExtension, vbr, MessageID);
        }
        isInterruptServiced = TRUE;
    }
    if (adaptExt->in_fly > 0) {
        adaptExt->vq->vq_ops->kick(adaptExt->vq);
    }
    return isInterruptServiced;
}
VOID
FORCEINLINE
CompleteDPC(
    IN PVOID DeviceExtension,
    IN pblk_req vbr,
    IN ULONG MessageID
    )
{
    PSCSI_REQUEST_BLOCK Srb      = (PSCSI_REQUEST_BLOCK)vbr->req;
    PADAPTER_EXTENSION  adaptExt = (PADAPTER_EXTENSION)DeviceExtension;
#ifndef USE_STORPORT
    PRHEL_SRB_EXTENSION srbExt   = (PRHEL_SRB_EXTENSION)Srb->SrbExtension;
    UNREFERENCED_PARAMETER( MessageID );
#endif
    RemoveEntryList(&vbr->list_entry);

#ifdef USE_STORPORT
    if(!adaptExt->dump_mode && adaptExt->dpc_ok) {
        InsertTailList(&adaptExt->complete_list, &vbr->list_entry);
        StorPortIssueDpc(DeviceExtension,
                         &adaptExt->completion_dpc,
                         ULongToPtr(MessageID),
                         NULL);
        return;
    }
    CompleteSRB(DeviceExtension, Srb);
#else
   if (Srb->DataTransferLength > srbExt->Xfer) {
       Srb->DataTransferLength = srbExt->Xfer;
       Srb->SrbStatus = SRB_STATUS_DATA_OVERRUN;
    }
    ScsiPortNotification(RequestComplete,
                         DeviceExtension,
                         Srb);
    if(srbExt->call_next) {
        ScsiPortNotification(NextLuRequest,
                         DeviceExtension,
                         Srb->PathId,
                         Srb->TargetId,
                         Srb->Lun);
    }
    if (adaptExt->flush_state == FlushRequested) {
        adaptExt->flush_state = FlushInflight;
        RhelDoFlush(DeviceExtension);
    }
#endif
}
BOOLEAN
VirtIoMSInterruptRoutine (
    IN PVOID  DeviceExtension,
    IN ULONG  MessageID
    )
{
    pblk_req            vbr;
    unsigned int        len;
    PADAPTER_EXTENSION  adaptExt;
    PSCSI_REQUEST_BLOCK Srb;
    BOOLEAN             isInterruptServiced = FALSE;
    PRHEL_SRB_EXTENSION srbExt;

    adaptExt = (PADAPTER_EXTENSION)DeviceExtension;

    RhelDbgPrint(TRACE_LEVEL_VERBOSE,
                 ("<--->%s : MessageID 0x%x\n", __FUNCTION__, MessageID));

    if (MessageID == 0) {
        RhelGetDiskGeometry(DeviceExtension);
        return TRUE;
    }

    while((vbr = (pblk_req)adaptExt->vq->vq_ops->get_buf(adaptExt->vq, &len)) != NULL) {
        Srb = (PSCSI_REQUEST_BLOCK)vbr->req;
        if (Srb) {
           switch (vbr->status) {
           case VIRTIO_BLK_S_OK:
              Srb->SrbStatus = SRB_STATUS_SUCCESS;
              break;
           case VIRTIO_BLK_S_UNSUPP:
              Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
              break;
           default:
              Srb->SrbStatus = SRB_STATUS_ERROR;
              RhelDbgPrint(TRACE_LEVEL_ERROR, ("SRB_STATUS_ERROR\n"));
              break;
           }
        }
        if (vbr->out_hdr.type == VIRTIO_BLK_T_FLUSH) {
            --adaptExt->in_fly;
            CompleteSRB(DeviceExtension, Srb);
        } else if (vbr->out_hdr.type == VIRTIO_BLK_T_GET_ID) {
            adaptExt->sn_ok = TRUE;
        } else if (Srb) {
            --adaptExt->in_fly;

            srbExt   = (PRHEL_SRB_EXTENSION)Srb->SrbExtension;
            if (srbExt->fua == TRUE) {
               RemoveEntryList(&vbr->list_entry);
               Srb->SrbStatus = SRB_STATUS_PENDING;
               Srb->ScsiStatus = SCSISTAT_GOOD;
               if (!RhelDoFlush(DeviceExtension, Srb, FALSE)) {
                    Srb->SrbStatus = SRB_STATUS_ERROR;
                    CompleteSRB(DeviceExtension, Srb);
                } else {
                    srbExt->fua = FALSE;
                }
            } else {
                CompleteDPC(DeviceExtension, vbr, MessageID);
            }
        }
        isInterruptServiced = TRUE;
    }
    if (adaptExt->in_fly > 0) {
        adaptExt->vq->vq_ops->kick(adaptExt->vq);
    }
    return isInterruptServiced;
}
BOOLEAN
VirtIoInterrupt(
    IN PVOID DeviceExtension
    )
{
    pblk_req            vbr;
    unsigned int        len;
    PADAPTER_EXTENSION  adaptExt;
    BOOLEAN             isInterruptServiced = FALSE;
    PSCSI_REQUEST_BLOCK Srb;
    ULONG               intReason = 0;
    PRHEL_SRB_EXTENSION srbExt;

    adaptExt = (PADAPTER_EXTENSION)DeviceExtension;

    RhelDbgPrint(TRACE_LEVEL_VERBOSE, ("%s (%d)\n", __FUNCTION__, KeGetCurrentIrql()));
    intReason = VirtIODeviceISR((VirtIODevice*)DeviceExtension);
    if ( intReason == 1) {
        isInterruptServiced = TRUE;
        while((vbr = (pblk_req)adaptExt->vq->vq_ops->get_buf(adaptExt->vq, &len)) != NULL) {
           Srb = (PSCSI_REQUEST_BLOCK)vbr->req;
           if (Srb) {
              switch (vbr->status) {
              case VIRTIO_BLK_S_OK:
                 Srb->SrbStatus = SRB_STATUS_SUCCESS;
                 break;
              case VIRTIO_BLK_S_UNSUPP:
                 Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
                 break;
              default:
                 Srb->SrbStatus = SRB_STATUS_ERROR;
                 RhelDbgPrint(TRACE_LEVEL_ERROR, ("SRB_STATUS_ERROR\n"));
                 break;
              }
           }
           if (vbr->out_hdr.type == VIRTIO_BLK_T_FLUSH) {
#ifdef USE_STORPORT
              --adaptExt->in_fly;
#endif
              CompleteSRB(DeviceExtension, Srb);
           } else if (vbr->out_hdr.type == VIRTIO_BLK_T_GET_ID) {
              adaptExt->sn_ok = TRUE;
           } else if (Srb) {
#ifdef USE_STORPORT
              --adaptExt->in_fly;
#endif
              srbExt = (PRHEL_SRB_EXTENSION)Srb->SrbExtension;
              if (srbExt->fua) {
                  RemoveEntryList(&vbr->list_entry);
                  Srb->SrbStatus = SRB_STATUS_PENDING;
                  Srb->ScsiStatus = SCSISTAT_GOOD;
                  if (!RhelDoFlush(DeviceExtension, Srb, FALSE)) {
                      Srb->SrbStatus = SRB_STATUS_ERROR;
                      CompleteSRB(DeviceExtension, Srb);
                  } else {
                      srbExt->fua = FALSE;
                  }
              } else {
                  CompleteDPC(DeviceExtension, vbr, 0);
              }
           }
        }
    } else if (intReason == 3) {
        RhelGetDiskGeometry(DeviceExtension);
        isInterruptServiced = TRUE;
    }
#ifdef USE_STORPORT
    if (adaptExt->in_fly > 0) {
        adaptExt->vq->vq_ops->kick(adaptExt->vq);
    }
#endif
    RhelDbgPrint(TRACE_LEVEL_VERBOSE, ("%s isInterruptServiced = %d\n", __FUNCTION__, isInterruptServiced));
    return isInterruptServiced;
}
BOOLEAN
VirtIoStartIo(
    IN PVOID DeviceExtension,
    IN PSCSI_REQUEST_BLOCK Srb
    )
{
    PCDB cdb = (PCDB)&Srb->Cdb[0];

    PADAPTER_EXTENSION adaptExt;

    adaptExt = (PADAPTER_EXTENSION)DeviceExtension;

    switch (Srb->Function) {
        case SRB_FUNCTION_EXECUTE_SCSI:
        case SRB_FUNCTION_IO_CONTROL: {
            break;
        }
        case SRB_FUNCTION_PNP:
        case SRB_FUNCTION_POWER:
        case SRB_FUNCTION_RESET_DEVICE:
        case SRB_FUNCTION_RESET_LOGICAL_UNIT: {
            Srb->SrbStatus = SRB_STATUS_SUCCESS;
            CompleteSRB(DeviceExtension, Srb);
            return TRUE;
        }
        case SRB_FUNCTION_FLUSH:
        case SRB_FUNCTION_SHUTDOWN: {
            Srb->SrbStatus = SRB_STATUS_PENDING;
            Srb->ScsiStatus = SCSISTAT_GOOD;
            if (!RhelDoFlush(DeviceExtension, Srb, TRUE)) {
                Srb->SrbStatus = SRB_STATUS_ERROR;
                CompleteSRB(DeviceExtension, Srb);
            }
            return TRUE;
        }

        default: {
            Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
            CompleteSRB(DeviceExtension, Srb);
            return TRUE;
        }
    }

    switch (cdb->CDB6GENERIC.OperationCode) {
        case SCSIOP_MODE_SENSE: {
            Srb->SrbStatus = RhelScsiGetModeSense(DeviceExtension, Srb);
            CompleteSRB(DeviceExtension, Srb);
            return TRUE;
        }
        case SCSIOP_INQUIRY: {
            Srb->SrbStatus = RhelScsiGetInquiryData(DeviceExtension, Srb);
            CompleteSRB(DeviceExtension, Srb);
            return TRUE;
        }

        case SCSIOP_READ_CAPACITY16:
        case SCSIOP_READ_CAPACITY: {
            Srb->SrbStatus = RhelScsiGetCapacity(DeviceExtension, Srb);
            CompleteSRB(DeviceExtension, Srb);
            return TRUE;
        }
        case SCSIOP_WRITE:
        case SCSIOP_WRITE16: {
            if (CHECKBIT(adaptExt->features, VIRTIO_BLK_F_RO)) {
                PSENSE_DATA senseBuffer = (PSENSE_DATA) Srb->SenseInfoBuffer;
                Srb->SrbStatus = SRB_STATUS_ERROR;
                Srb->ScsiStatus = SCSISTAT_CHECK_CONDITION;
                senseBuffer->SenseKey = SCSI_SENSE_DATA_PROTECT;
                senseBuffer->AdditionalSenseCode = SCSI_ADWRITE_PROTECT;
                CompleteSRB(DeviceExtension, Srb);
                return TRUE;
            }
        }
        case SCSIOP_READ:
        case SCSIOP_READ16: {
            Srb->SrbStatus = SRB_STATUS_PENDING;
            if(!RhelDoReadWrite(DeviceExtension, Srb)) {
                Srb->SrbStatus = SRB_STATUS_BUSY;
                CompleteSRB(DeviceExtension, Srb);
            }
            return TRUE;
        }
        case SCSIOP_START_STOP_UNIT: {
            Srb->SrbStatus = SRB_STATUS_SUCCESS;
            CompleteSRB(DeviceExtension, Srb);
            return TRUE;
        }
        case SCSIOP_REQUEST_SENSE:
        case SCSIOP_TEST_UNIT_READY:
        case SCSIOP_RESERVE_UNIT:
        case SCSIOP_RESERVE_UNIT10:
        case SCSIOP_RELEASE_UNIT:
        case SCSIOP_RELEASE_UNIT10:
        case SCSIOP_VERIFY:
        case SCSIOP_VERIFY16:
        case SCSIOP_MEDIUM_REMOVAL: {
            Srb->SrbStatus = SRB_STATUS_SUCCESS;
            Srb->ScsiStatus = SCSISTAT_GOOD;
            CompleteSRB(DeviceExtension, Srb);
            return TRUE;
        }
        case SCSIOP_SYNCHRONIZE_CACHE:
        case SCSIOP_SYNCHRONIZE_CACHE16: {
            Srb->SrbStatus = SRB_STATUS_PENDING;
            Srb->ScsiStatus = SCSISTAT_GOOD;
            if (!RhelDoFlush(DeviceExtension, Srb, TRUE)) {
                Srb->SrbStatus = SRB_STATUS_ERROR;
                CompleteSRB(DeviceExtension, Srb);
            }
            return TRUE;
        }
        default: {
            break;
        }
    }

    if (cdb->CDB12.OperationCode == SCSIOP_REPORT_LUNS) {
        Srb->SrbStatus = RhelScsiReportLuns(DeviceExtension, Srb);
        CompleteSRB(DeviceExtension, Srb);
        return TRUE;

    }

    Srb->SrbStatus = SRB_STATUS_INVALID_REQUEST;
    CompleteSRB(DeviceExtension, Srb);
    return TRUE;
}
BOOLEAN
VirtIoBuildIo(
    IN PVOID DeviceExtension,
    IN PSCSI_REQUEST_BLOCK Srb
    )
{
    PCDB                  cdb;
    ULONG                 i;
    ULONG                 dummy;
    ULONG                 sgElement;
    ULONG                 sgMaxElements;
    PADAPTER_EXTENSION    adaptExt;
    PRHEL_SRB_EXTENSION   srbExt;
    PSTOR_SCATTER_GATHER_LIST sgList;
    ULONGLONG             lba;
    ULONG                 blocks;

    cdb      = (PCDB)&Srb->Cdb[0];
    srbExt   = (PRHEL_SRB_EXTENSION)Srb->SrbExtension;
    adaptExt = (PADAPTER_EXTENSION)DeviceExtension;

    if(Srb->PathId || Srb->TargetId || Srb->Lun) {
        Srb->SrbStatus = SRB_STATUS_NO_DEVICE;
        ScsiPortNotification(RequestComplete,
                             DeviceExtension,
                             Srb);
        return FALSE;
    }

    switch (cdb->CDB6GENERIC.OperationCode) {
        case SCSIOP_READ:
        case SCSIOP_WRITE:
        case SCSIOP_WRITE_VERIFY:
        case SCSIOP_READ6:
        case SCSIOP_WRITE6:
        case SCSIOP_READ12:
        case SCSIOP_WRITE12:
        case SCSIOP_WRITE_VERIFY12:
        case SCSIOP_READ16:
        case SCSIOP_WRITE16:
        case SCSIOP_WRITE_VERIFY16: {
            break;
        }
        default: {
            Srb->SrbStatus = SRB_STATUS_SUCCESS;
            return TRUE;
        }
    }

    lba = RhelGetLba(DeviceExtension, cdb);
    blocks = (Srb->DataTransferLength + adaptExt->info.blk_size - 1) / adaptExt->info.blk_size;
    if (lba > adaptExt->lastLBA) {
        RhelDbgPrint(TRACE_LEVEL_ERROR, ("SRB_STATUS_BAD_SRB_BLOCK_LENGTH lba = %llu lastLBA= %llu\n", lba, adaptExt->lastLBA));
        Srb->SrbStatus = SRB_STATUS_BAD_SRB_BLOCK_LENGTH;
        CompleteSRB(DeviceExtension, Srb);
        return FALSE;
    }
    if ((lba + blocks) > adaptExt->lastLBA) {
        blocks = (ULONG)(adaptExt->lastLBA + 1 - lba);
        RhelDbgPrint(TRACE_LEVEL_ERROR, ("lba = %llu lastLBA= %llu blocks = %lu\n", lba, adaptExt->lastLBA, blocks));
        Srb->DataTransferLength = (ULONG)(blocks * adaptExt->info.blk_size);
    }

    sgList = StorPortGetScatterGatherList(DeviceExtension, Srb);
    sgMaxElements = min((MAX_PHYS_SEGMENTS + 1), sgList->NumberOfElements);
    srbExt->Xfer = 0;
    for (i = 0, sgElement = 1; i < sgMaxElements; i++, sgElement++) {
        srbExt->vbr.sg[sgElement].physAddr = sgList->List[i].PhysicalAddress;
        srbExt->vbr.sg[sgElement].length   = sgList->List[i].Length;
        srbExt->Xfer += sgList->List[i].Length;
    }

    srbExt->vbr.out_hdr.sector = lba;
    srbExt->vbr.out_hdr.ioprio = 0;
    srbExt->vbr.req            = (PVOID)Srb;
    srbExt->fua                = (cdb->CDB10.ForceUnitAccess == 1);

    if (Srb->SrbFlags & SRB_FLAGS_DATA_OUT) {
        srbExt->vbr.out_hdr.type = VIRTIO_BLK_T_OUT;
        srbExt->out = sgElement;
        srbExt->in = 1;
    } else {
        srbExt->vbr.out_hdr.type = VIRTIO_BLK_T_IN;
        srbExt->out = 1;
        srbExt->in = sgElement;
    }

    srbExt->vbr.sg[0].physAddr = ScsiPortGetPhysicalAddress(DeviceExtension, NULL, &srbExt->vbr.out_hdr, &dummy);
    srbExt->vbr.sg[0].length = sizeof(srbExt->vbr.out_hdr);

    srbExt->vbr.sg[sgElement].physAddr = ScsiPortGetPhysicalAddress(DeviceExtension, NULL, &srbExt->vbr.status, &dummy);
    srbExt->vbr.sg[sgElement].length = sizeof(srbExt->vbr.status);

    return TRUE;
}