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
VirtIoHwInitialize(
    IN PVOID DeviceExtension
    )
{

    PADAPTER_EXTENSION adaptExt;
    BOOLEAN            ret = FALSE;
#ifdef MSI_SUPPORTED
    MESSAGE_INTERRUPT_INFORMATION msi_info;
#endif

    RhelDbgPrint(TRACE_LEVEL_VERBOSE, ("%s (%d)\n", __FUNCTION__, KeGetCurrentIrql()));

    adaptExt = (PADAPTER_EXTENSION)DeviceExtension;

    adaptExt->msix_vectors = 0;
#ifdef MSI_SUPPORTED
    while(StorPortGetMSIInfo(DeviceExtension, adaptExt->msix_vectors, &msi_info) == STOR_STATUS_SUCCESS) {
        RhelDbgPrint(TRACE_LEVEL_INFORMATION, ("MessageId = %x\n", msi_info.MessageId));
        RhelDbgPrint(TRACE_LEVEL_INFORMATION, ("MessageData = %x\n", msi_info.MessageData));
        RhelDbgPrint(TRACE_LEVEL_INFORMATION, ("InterruptVector = %x\n", msi_info.InterruptVector));
        RhelDbgPrint(TRACE_LEVEL_INFORMATION, ("InterruptLevel = %x\n", msi_info.InterruptLevel));
        RhelDbgPrint(TRACE_LEVEL_INFORMATION, ("InterruptMode = %s\n", msi_info.InterruptMode == LevelSensitive ? "LevelSensitive" : "Latched"));
        RhelDbgPrint(TRACE_LEVEL_INFORMATION, ("MessageAddress = %p\n\n", msi_info.MessageAddress));
        ++adaptExt->msix_vectors;
    }

    if(!adaptExt->dump_mode && (adaptExt->msix_vectors > 1)) {
        adaptExt->vq = FindVirtualQueue(adaptExt, 0, adaptExt->msix_vectors - 1);
    }
#endif

    if(!adaptExt->vq) {
        adaptExt->vq = FindVirtualQueue(adaptExt, 0, 0);
    }
    if (!adaptExt->vq) {
        LogError(DeviceExtension,
                SP_INTERNAL_ADAPTER_ERROR,
                __LINE__);

        RhelDbgPrint(TRACE_LEVEL_FATAL, ("Cannot find snd virtual queue\n"));
        VirtIODeviceAddStatus(&adaptExt->vdev, VIRTIO_CONFIG_S_FAILED);
        return ret;
    }

    RhelGetDiskGeometry(DeviceExtension);

    memset(&adaptExt->inquiry_data, 0, sizeof(INQUIRYDATA));

    adaptExt->inquiry_data.ANSIVersion = 4;
    adaptExt->inquiry_data.ResponseDataFormat = 2;
    adaptExt->inquiry_data.CommandQueue = 1;
    adaptExt->inquiry_data.DeviceType   = DIRECT_ACCESS_DEVICE;
    adaptExt->inquiry_data.Wide32Bit    = 1;
    adaptExt->inquiry_data.AdditionalLength = 91;
    ScsiPortMoveMemory(&adaptExt->inquiry_data.VendorId, "Red Hat ", sizeof("Red Hat "));
    ScsiPortMoveMemory(&adaptExt->inquiry_data.ProductId, "VirtIO", sizeof("VirtIO"));
    ScsiPortMoveMemory(&adaptExt->inquiry_data.ProductRevisionLevel, "0001", sizeof("0001"));
    ScsiPortMoveMemory(&adaptExt->inquiry_data.VendorSpecific, "0001", sizeof("0001"));

    if(!adaptExt->dump_mode && !adaptExt->sn_ok)
    {
        RhelGetSerialNumber(DeviceExtension);
    }

    ret = TRUE;

#ifdef USE_STORPORT
    if(!adaptExt->dump_mode && !adaptExt->dpc_ok)
    {
        ret = StorPortEnablePassiveInitialization(DeviceExtension, VirtIoPassiveInitializeRoutine);
    }
#endif

    if (ret) {
        VirtIODeviceAddStatus(&adaptExt->vdev, VIRTIO_CONFIG_S_DRIVER_OK);
    } else {
        VirtIODeviceAddStatus(&adaptExt->vdev, VIRTIO_CONFIG_S_FAILED);
    }

    return ret;
}
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;
}
UCHAR
RhelScsiGetInquiryData(
    IN PVOID DeviceExtension,
    IN OUT PSCSI_REQUEST_BLOCK Srb
    )
{

    PINQUIRYDATA InquiryData;
    ULONG dataLen;
    UCHAR SrbStatus = SRB_STATUS_INVALID_LUN;
    PCDB cdb = (PCDB)&Srb->Cdb[0];
    PADAPTER_EXTENSION adaptExt;

    adaptExt = (PADAPTER_EXTENSION)DeviceExtension;

    InquiryData = (PINQUIRYDATA)Srb->DataBuffer;
    dataLen = Srb->DataTransferLength;

    if (adaptExt->rescan_geometry) {
        RhelGetDiskGeometry(DeviceExtension);
        adaptExt->rescan_geometry = FALSE;
    }

    SrbStatus = SRB_STATUS_SUCCESS;
    if((cdb->CDB6INQUIRY3.PageCode != VPD_SUPPORTED_PAGES) &&
       (cdb->CDB6INQUIRY3.EnableVitalProductData == 0)) {
        Srb->ScsiStatus = SCSISTAT_CHECK_CONDITION;
    }
    else if ((cdb->CDB6INQUIRY3.PageCode == VPD_SUPPORTED_PAGES) &&
             (cdb->CDB6INQUIRY3.EnableVitalProductData == 1)) {

        PVPD_SUPPORTED_PAGES_PAGE SupportPages;
        SupportPages = (PVPD_SUPPORTED_PAGES_PAGE)Srb->DataBuffer;
        memset(SupportPages, 0, sizeof(VPD_SUPPORTED_PAGES_PAGE));
        SupportPages->PageCode = VPD_SUPPORTED_PAGES;
        SupportPages->PageLength = 3;
        SupportPages->SupportedPageList[0] = VPD_SUPPORTED_PAGES;
        SupportPages->SupportedPageList[1] = VPD_SERIAL_NUMBER;
        SupportPages->SupportedPageList[2] = VPD_DEVICE_IDENTIFIERS;
        Srb->DataTransferLength = sizeof(VPD_SUPPORTED_PAGES_PAGE) + SupportPages->PageLength;
    }
    else if ((cdb->CDB6INQUIRY3.PageCode == VPD_SERIAL_NUMBER) &&
             (cdb->CDB6INQUIRY3.EnableVitalProductData == 1)) {

        PVPD_SERIAL_NUMBER_PAGE SerialPage;
        SerialPage = (PVPD_SERIAL_NUMBER_PAGE)Srb->DataBuffer;
        SerialPage->PageCode = VPD_SERIAL_NUMBER;
        if (!adaptExt->sn_ok) {
           SerialPage->PageLength = 1;
           SerialPage->SerialNumber[0] = '0';
        } else {
           SerialPage->PageLength = BLOCK_SERIAL_STRLEN;
           ScsiPortMoveMemory(&SerialPage->SerialNumber, &adaptExt->sn, BLOCK_SERIAL_STRLEN);
        }
        Srb->DataTransferLength = sizeof(VPD_SERIAL_NUMBER_PAGE) + SerialPage->PageLength;
    }
    else if ((cdb->CDB6INQUIRY3.PageCode == VPD_DEVICE_IDENTIFIERS) &&
             (cdb->CDB6INQUIRY3.EnableVitalProductData == 1)) {

        PVPD_IDENTIFICATION_PAGE IdentificationPage;
        PVPD_IDENTIFICATION_DESCRIPTOR IdentificationDescr;
        IdentificationPage = (PVPD_IDENTIFICATION_PAGE)Srb->DataBuffer;
        memset(IdentificationPage, 0, sizeof(VPD_IDENTIFICATION_PAGE));
        IdentificationPage->PageCode = VPD_DEVICE_IDENTIFIERS;
        IdentificationPage->PageLength = sizeof(VPD_IDENTIFICATION_DESCRIPTOR) + 11;

        IdentificationDescr = (PVPD_IDENTIFICATION_DESCRIPTOR)IdentificationPage->Descriptors;
        memset(IdentificationDescr, 0, sizeof(VPD_IDENTIFICATION_DESCRIPTOR));
        IdentificationDescr->CodeSet = VpdCodeSetBinary;
        IdentificationDescr->IdentifierType = VpdIdentifierTypeEUI64;
        IdentificationDescr->IdentifierLength = 11;
        IdentificationDescr->Identifier[0] = '1';
        IdentificationDescr->Identifier[1] = 'A';
        IdentificationDescr->Identifier[2] = 'F';
        IdentificationDescr->Identifier[3] = '4';
        IdentificationDescr->Identifier[4] = '1';
        IdentificationDescr->Identifier[5] = '0';
        IdentificationDescr->Identifier[6] = '0';
        IdentificationDescr->Identifier[7] = '1';
        IdentificationDescr->Identifier[8] = '_';
        IdentificationDescr->Identifier[9] = '0' + (adaptExt->rescan_cnt / 10);
        IdentificationDescr->Identifier[10] = '0' + (adaptExt->rescan_cnt % 10);

        Srb->DataTransferLength = sizeof(VPD_IDENTIFICATION_PAGE) +
                                 IdentificationPage->PageLength;

    }
    else if (dataLen > sizeof(INQUIRYDATA)) {
        ScsiPortMoveMemory(InquiryData, &adaptExt->inquiry_data, sizeof(INQUIRYDATA));
        Srb->DataTransferLength = sizeof(INQUIRYDATA);
    } else {
        ScsiPortMoveMemory(InquiryData, &adaptExt->inquiry_data, dataLen);
        Srb->DataTransferLength = dataLen;
    }

    return SrbStatus;
}