Exemple #1
0
VOID
ScsiPortFlushDma(
    IN PVOID HwDeviceExtension
)

/*++

Routine Description:

    This routine checks to see if the perivious IoMapTransfer has been done
    started.  If it has not, then the PD_MAP_TRANSER flag is cleared, and the
    routine returns; otherwise, this routine schedules a DPC which will call
    IoFlushAdapter buffers.

Arguments:

    HwDeviceExtension - Supplies a the hardware device extension for the
        host bus adapter which will be doing the data transfer.


Return Value:

    None.

--*/

{

    PADAPTER_EXTENSION deviceExtension = GET_FDO_EXTENSION(HwDeviceExtension);

    if(Sp64BitPhysicalAddresses) {
        KeBugCheckEx(PORT_DRIVER_INTERNAL,
                     0,
                     STATUS_NOT_SUPPORTED,
                     (ULONG_PTR) HwDeviceExtension,
                     (ULONG_PTR) deviceExtension->DeviceObject->DriverObject);
    }

    if (deviceExtension->InterruptData.InterruptFlags & PD_MAP_TRANSFER) {

        //
        // The transfer has not been started so just clear the map transfer
        // flag and return.
        //

        deviceExtension->InterruptData.InterruptFlags &= ~PD_MAP_TRANSFER;
        return;
    }

    deviceExtension->InterruptData.InterruptFlags |= PD_FLUSH_ADAPTER_BUFFERS;

    //
    // Request a DPC be queued after the interrupt completes.
    //

    deviceExtension->InterruptData.InterruptFlags |= PD_NOTIFICATION_REQUIRED;

    return;

}
Exemple #2
0
PSCSI_REQUEST_BLOCK
ScsiPortGetSrb(
    IN PVOID HwDeviceExtension,
    IN UCHAR PathId,
    IN UCHAR TargetId,
    IN UCHAR Lun,
    IN LONG QueueTag
)

/*++

Routine Description:

    This routine retrieves an active SRB for a particuliar logical unit.

Arguments:

    HwDeviceExtension
    PathId, TargetId, Lun - identify logical unit on SCSI bus.
    QueueTag - -1 indicates request is not tagged.

Return Value:

    SRB, if one exists. Otherwise, NULL.

--*/

{
    PADAPTER_EXTENSION deviceExtension = GET_FDO_EXTENSION(HwDeviceExtension);
    PSRB_DATA srbData;
    PSCSI_REQUEST_BLOCK srb;
    UCHAR pathId;
    UCHAR targetId;
    UCHAR lun;

    srbData = SpGetSrbData(deviceExtension,
                           PathId,
                           TargetId,
                           Lun,
                           (UCHAR)QueueTag,
                           FALSE);

    if (srbData == NULL || srbData->CurrentSrb == NULL) {
        return(NULL);
    }

    srb = srbData->CurrentSrb;

    //
    // If the srb is not active then return NULL;
    //

    if (!(srb->SrbFlags & SRB_FLAGS_IS_ACTIVE)) {
        return(NULL);
    }

    return (srb);

} // end ScsiPortGetSrb()
Exemple #3
0
PVOID
ScsiPortGetVirtualAddress(
    IN PVOID HwDeviceExtension,
    IN SCSI_PHYSICAL_ADDRESS PhysicalAddress
)

/*++

Routine Description:

    This routine is returns a virtual address associated with a
    physical address, if the physical address was obtained by a
    call to ScsiPortGetPhysicalAddress.

Arguments:

    PhysicalAddress

Return Value:

    Virtual address

--*/

{
    PADAPTER_EXTENSION deviceExtension = GET_FDO_EXTENSION(HwDeviceExtension);
    PVOID address;
    ULONG smallphysicalBase;
    ULONG smallAddress;

    smallAddress = ScsiPortConvertPhysicalAddressToUlong(PhysicalAddress);

    smallphysicalBase = ScsiPortConvertPhysicalAddressToUlong(deviceExtension->PhysicalCommonBuffer);

    //
    // Check that the physical address is within the proper range.
    //

    if (smallAddress < smallphysicalBase ||
            smallAddress >= smallphysicalBase + deviceExtension->CommonBufferSize) {

        //
        // This is a bugous physical address return back NULL.
        //

        return(NULL);

    }

    address = smallAddress - smallphysicalBase +
              (PUCHAR) deviceExtension->SrbExtensionBuffer;

    return address;

} // end ScsiPortGetVirtualAddress()
Exemple #4
0
PDEVICE_OBJECT
FindScsiportFdo(
		IN OUT	PMINIPORT_DEVICE_EXTENSION	HwDeviceExtension
){
	// DeviceObject which is one of argument is always NULL.
	PADAPTER_EXTENSION		deviceExtension = GET_FDO_EXTENSION(HwDeviceExtension);

	ASSERT(deviceExtension->DeviceObject);

	return deviceExtension->DeviceObject;
}
Exemple #5
0
BOOLEAN
ScsiPortValidateRange(
    IN PVOID HwDeviceExtension,
    IN INTERFACE_TYPE BusType,
    IN ULONG SystemIoBusNumber,
    IN SCSI_PHYSICAL_ADDRESS IoAddress,
    IN ULONG NumberOfBytes,
    IN BOOLEAN InIoSpace
)

/*++

Routine Description:

    This routine should take an IO range and make sure that it is not already
    in use by another adapter. This allows miniport drivers to probe IO where
    an adapter could be, without worrying about messing up another card.

Arguments:

    HwDeviceExtension - Used to find scsi managers internal structures
    BusType - EISA, PCI, PC/MCIA, MCA, ISA, what?
    SystemIoBusNumber - Which system bus?
    IoAddress - Start of range
    NumberOfBytes - Length of range
    InIoSpace - Is range in IO space?

Return Value:

    TRUE if range not claimed by another driver.

--*/

{
    PADAPTER_EXTENSION deviceExtension = GET_FDO_EXTENSION(HwDeviceExtension);

    //
    // This is not implemented in NT.
    //

    return TRUE;
}
Exemple #6
0
VOID
ScsiPortCompleteRequest(
    IN PVOID HwDeviceExtension,
    IN UCHAR PathId,
    IN UCHAR TargetId,
    IN UCHAR Lun,
    IN UCHAR SrbStatus
)

/*++

Routine Description:

    Complete all active requests for the specified logical unit.

Arguments:

    DeviceExtenson - Supplies the HBA miniport driver's adapter data storage.

    TargetId, Lun and PathId - specify device address on a SCSI bus.

    SrbStatus - Status to be returned in each completed SRB.

Return Value:

    None.

--*/

{
    PADAPTER_EXTENSION deviceExtension = GET_FDO_EXTENSION(HwDeviceExtension);
    ULONG binNumber;

    for (binNumber = 0; binNumber < NUMBER_LOGICAL_UNIT_BINS; binNumber++) {

        PLOGICAL_UNIT_BIN bin = &deviceExtension->LogicalUnitList[binNumber];
        PLOGICAL_UNIT_EXTENSION logicalUnit;
        ULONG limit = 0;

        logicalUnit = bin->List;

        DebugPrint((2, "ScsiPortCompleteRequest: Completing requests in "
                    "bin %d [%#p]\n",
                    binNumber, bin));

        for(logicalUnit = bin->List;
                logicalUnit != NULL;
                logicalUnit = logicalUnit->NextLogicalUnit) {

            PLIST_ENTRY entry;

            ASSERT(limit++ < 1000);

            //
            // See if this logical unit matches the pattern.  Check for -1
            // first since this seems to be the most popular way to complete
            // requests.
            //

            if (((PathId == SP_UNTAGGED) || (PathId == logicalUnit->PathId)) &&
                    ((TargetId == SP_UNTAGGED) ||
                     (TargetId == logicalUnit->TargetId)) &&
                    ((Lun == SP_UNTAGGED) || (Lun == logicalUnit->Lun))) {

                //
                // Complete any pending abort reqeusts.
                //

                if (logicalUnit->AbortSrb != NULL) {
                    logicalUnit->AbortSrb->SrbStatus = SrbStatus;

                    ScsiPortNotification(
                        RequestComplete,
                        HwDeviceExtension,
                        logicalUnit->AbortSrb
                    );
                }

                if(logicalUnit->CurrentUntaggedRequest != NULL) {

                    SpCompleteSrb(deviceExtension,
                                  logicalUnit->CurrentUntaggedRequest,
                                  SrbStatus);
                }

                //
                // Complete each of the requests in the queue.
                //

                entry = logicalUnit->RequestList.Flink;
                while (entry != &logicalUnit->RequestList) {
                    PSRB_DATA srbData;

                    ASSERT(limit++ < 1000);
                    srbData = CONTAINING_RECORD(entry, SRB_DATA, RequestList);
                    SpCompleteSrb(deviceExtension,  srbData, SrbStatus);
                    entry = srbData->RequestList.Flink;
                }

            }
        }
    }

    return;

} // end ScsiPortCompleteRequest()
Exemple #7
0
VOID
ScsiPortLogError(
    IN PVOID HwDeviceExtension,
    IN PSCSI_REQUEST_BLOCK Srb OPTIONAL,
    IN UCHAR PathId,
    IN UCHAR TargetId,
    IN UCHAR Lun,
    IN ULONG ErrorCode,
    IN ULONG UniqueId
)

/*++

Routine Description:

    This routine saves the error log information, and queues a DPC if necessary.

Arguments:

    HwDeviceExtension - Supplies the HBA miniport driver's adapter data storage.

    Srb - Supplies an optional pointer to srb if there is one.

    TargetId, Lun and PathId - specify device address on a SCSI bus.

    ErrorCode - Supplies an error code indicating the type of error.

    UniqueId - Supplies a unique identifier for the error.

Return Value:

    None.

--*/

{
    PADAPTER_EXTENSION deviceExtension = GET_FDO_EXTENSION(HwDeviceExtension);
    PDEVICE_OBJECT DeviceObject = deviceExtension->CommonExtension.DeviceObject;
    PSRB_DATA srbData;
    PERROR_LOG_ENTRY errorLogEntry;

    //
    // If the error log entry is already full, then dump the error.
    //

    if (deviceExtension->InterruptData.InterruptFlags & PD_LOG_ERROR) {

#if SCSIDBG_ENABLED
        DebugPrint((1,"ScsiPortLogError: Dumping scsi error log packet.\n"));
        DebugPrint((1,
                    "PathId = %2x, TargetId = %2x, Lun = %2x, ErrorCode = %x, UniqueId = %x.",
                    PathId,
                    TargetId,
                    Lun,
                    ErrorCode,
                    UniqueId
                   ));
#endif
        return;
    }

    //
    // Save the error log data in the log entry.
    //

    errorLogEntry = &deviceExtension->InterruptData.LogEntry;

    errorLogEntry->ErrorCode = ErrorCode;
    errorLogEntry->TargetId = TargetId;
    errorLogEntry->Lun = Lun;
    errorLogEntry->PathId = PathId;
    errorLogEntry->UniqueId = UniqueId;

    //
    // Get the sequence number from the SRB data.
    //

    if (Srb != NULL) {

        srbData = Srb->OriginalRequest;

        ASSERT_SRB_DATA(srbData);

        errorLogEntry->SequenceNumber = srbData->SequenceNumber;
        errorLogEntry->ErrorLogRetryCount = srbData->ErrorLogRetryCount++;
    } else {
        errorLogEntry->SequenceNumber = 0;
        errorLogEntry->ErrorLogRetryCount = 0;
    }

    //
    // Indicate that the error log entry is in use.
    //

    deviceExtension->InterruptData.InterruptFlags |= PD_LOG_ERROR;

    //
    // Request a DPC be queued after the interrupt completes.
    //

    deviceExtension->InterruptData.InterruptFlags |= PD_NOTIFICATION_REQUIRED;

    return;

} // end ScsiPortLogError()
Exemple #8
0
PVOID
ScsiPortGetLogicalUnit(
    IN PVOID HwDeviceExtension,
    IN UCHAR PathId,
    IN UCHAR TargetId,
    IN UCHAR Lun
)

/*++

Routine Description:

    Walk port driver's logical unit extension list searching
    for entry.

Arguments:

    HwDeviceExtension - The port driver's device extension follows
        the miniport's device extension and contains a pointer to
        the logical device extension list.

    PathId, TargetId and Lun - identify which logical unit on the
        SCSI buses.

Return Value:

    If entry found return miniport driver's logical unit extension.
    Else, return NULL.

--*/

{
    PADAPTER_EXTENSION deviceExtension;
    PLOGICAL_UNIT_EXTENSION logicalUnit;

    DebugPrint((3, "ScsiPortGetLogicalUnit: TargetId %d\n",
                TargetId));

    //
    // Get pointer to port driver device extension.
    //

    deviceExtension = GET_FDO_EXTENSION(HwDeviceExtension);

    //
    // Get a pointer to the logical unit.
    //

    logicalUnit = GetLogicalUnitExtension(deviceExtension,
                                          PathId,
                                          TargetId,
                                          Lun,
                                          FALSE,
                                          FALSE);

    if(logicalUnit != NULL) {

        return logicalUnit->HwLogicalUnitExtension;
    }

    return NULL;

} // end ScsiPortGetLogicalUnit()
Exemple #9
0
VOID
ScsiPortIoMapTransfer(
    IN PVOID HwDeviceExtension,
    IN PSCSI_REQUEST_BLOCK Srb,
    IN PVOID LogicalAddress,
    IN ULONG Length
)
/*++

Routine Description:

    Saves the parameters for the call to IoMapTransfer and schedules the DPC
    if necessary.

Arguments:

    HwDeviceExtension - Supplies a the hardware device extension for the
        host bus adapter which will be doing the data transfer.

    Srb - Supplies the particular request that data transfer is for.

    LogicalAddress - Supplies the logical address where the transfer should
        begin.

    Length - Supplies the maximum length in bytes of the transfer.

Return Value:

   None.

--*/

{
    PADAPTER_EXTENSION deviceExtension = GET_FDO_EXTENSION(HwDeviceExtension);
    PSRB_DATA srbData = Srb->OriginalRequest;

    ASSERT_SRB_DATA(srbData);

    //
    // If this is a 64-bit system then this call is illegal.  Bugcheck.
    //

    if(Sp64BitPhysicalAddresses) {
        KeBugCheckEx(PORT_DRIVER_INTERNAL,
                     1,
                     STATUS_NOT_SUPPORTED,
                     (ULONG_PTR) HwDeviceExtension,
                     (ULONG_PTR) deviceExtension->DeviceObject->DriverObject);
    }

    //
    // Make sure this host bus adapter has an Dma adapter object.
    //

    if (deviceExtension->DmaAdapterObject == NULL) {

        //
        // No DMA adapter, no work.
        //

        return;
    }

    ASSERT((Srb->SrbFlags & SRB_FLAGS_UNSPECIFIED_DIRECTION) != SRB_FLAGS_UNSPECIFIED_DIRECTION);

    deviceExtension->InterruptData.MapTransferParameters.SrbData = srbData;

    deviceExtension->InterruptData.MapTransferParameters.LogicalAddress = LogicalAddress;
    deviceExtension->InterruptData.MapTransferParameters.Length = Length;
    deviceExtension->InterruptData.MapTransferParameters.SrbFlags = Srb->SrbFlags;

    deviceExtension->InterruptData.InterruptFlags |= PD_MAP_TRANSFER;

    //
    // Request a DPC be queued after the interrupt completes.
    //

    deviceExtension->InterruptData.InterruptFlags |= PD_NOTIFICATION_REQUIRED;

} // end ScsiPortIoMapTransfer()
Exemple #10
0
SCSI_PHYSICAL_ADDRESS
ScsiPortGetPhysicalAddress(
    IN PVOID HwDeviceExtension,
    IN PSCSI_REQUEST_BLOCK Srb,
    IN PVOID VirtualAddress,
    OUT ULONG *Length
)

/*++

Routine Description:

    Convert virtual address to physical address for DMA.

Arguments:

Return Value:

--*/

{
    PADAPTER_EXTENSION deviceExtension = GET_FDO_EXTENSION(HwDeviceExtension);
    ULONG             byteOffset;
    PHYSICAL_ADDRESS  address;
    ULONG             length;

    if (Srb == NULL || Srb->SenseInfoBuffer == VirtualAddress) {

        byteOffset = (ULONG)((PCCHAR) VirtualAddress - (PCCHAR)
                             deviceExtension->SrbExtensionBuffer);

        ASSERT(byteOffset < deviceExtension->CommonBufferSize);

        length = deviceExtension->CommonBufferSize - byteOffset;
        address.QuadPart = deviceExtension->PhysicalCommonBuffer.QuadPart + byteOffset;

    } else if (deviceExtension->MasterWithAdapter) {

#ifdef USE_DMA_MACROS
        PSCATTER_GATHER_ELEMENT scatterList;
#else
        PSRB_SCATTER_GATHER scatterList;
#endif
        PSRB_DATA srbData;

        //
        // A scatter/gather list has already been allocated use it to determine
        // the physical address and length.  Get the scatter/gather list.
        //

        srbData = Srb->OriginalRequest;

        ASSERT_SRB_DATA(srbData);

        scatterList =
#ifdef USE_DMA_MACROS
            (PSCATTER_GATHER_ELEMENT)
#endif // USE_DMA_MACROS
            srbData->ScatterGatherList;

        //
        // Calculate byte offset into the data buffer.
        //

        byteOffset = (ULONG)((PCHAR) VirtualAddress - (PCHAR) Srb->DataBuffer);

        //
        // Find the appropriate entry in the scatter/gatter list.
        //

        while (byteOffset >= scatterList->Length) {

            byteOffset -= scatterList->Length;
            scatterList++;
        }

        //
        // Calculate the physical address and length to be returned.
        //

        length = scatterList->Length - byteOffset;

#ifdef USE_DMA_MACROS

        address.QuadPart = scatterList->Address.QuadPart + byteOffset;
#else

        address.QuadPart = scatterList->PhysicalAddress.QuadPart + byteOffset;
#endif

    } else {
        *Length = 0;
        address.QuadPart = (LONGLONG)(SP_UNINITIALIZED_VALUE);
    }

    *Length = length;

    return address;

} // end ScsiPortGetPhysicalAddress()
Exemple #11
0
VOID
ScsiPortNotification(
    IN SCSI_NOTIFICATION_TYPE NotificationType,
    IN PVOID HwDeviceExtension,
    ...
)

/*++

Routine Description:

Arguments:

Return Value:

--*/

{
    PADAPTER_EXTENSION deviceExtension = GET_FDO_EXTENSION(HwDeviceExtension);

    PLOGICAL_UNIT_EXTENSION logicalUnit;
    PSRB_DATA               srbData;
    PSCSI_REQUEST_BLOCK     srb;
    UCHAR                   pathId;
    UCHAR                   targetId;
    UCHAR                   lun;
    va_list                 ap;

    va_start(ap, HwDeviceExtension);

    switch (NotificationType) {

    case NextRequest:

        //
        // Start next packet on adapter's queue.
        //

        deviceExtension->InterruptData.InterruptFlags |= PD_READY_FOR_NEXT_REQUEST;
        break;

    case RequestComplete:

        srb = va_arg(ap, PSCSI_REQUEST_BLOCK);

        ASSERT(srb->SrbStatus != SRB_STATUS_PENDING);

        ASSERT(srb->SrbStatus != SRB_STATUS_SUCCESS ||
               srb->ScsiStatus == SCSISTAT_GOOD ||
               srb->Function != SRB_FUNCTION_EXECUTE_SCSI);

        //
        // If this srb has already been completed then return, otherwise
        // clear the active flag.
        //

        if (srb->SrbFlags & SRB_FLAGS_IS_ACTIVE) {
            srb->SrbFlags &= ~SRB_FLAGS_IS_ACTIVE;
        } else {
            va_end(ap);
            return;
        }

        //
        // Treat abort completions as a special case.
        //

        if (srb->Function == SRB_FUNCTION_ABORT_COMMAND) {

            ASSERT(FALSE);
            logicalUnit = GetLogicalUnitExtension(deviceExtension,
                                                  srb->PathId,
                                                  srb->TargetId,
                                                  srb->Lun,
                                                  FALSE,
                                                  FALSE);

            logicalUnit->CompletedAbort =
                deviceExtension->InterruptData.CompletedAbort;

            deviceExtension->InterruptData.CompletedAbort = logicalUnit;

        } else {

            //
            // Validate the srb data.
            //

            srbData = srb->OriginalRequest;

#if DBG
            ASSERT_SRB_DATA(srbData);

            ASSERT(srbData->CurrentSrb == srb);

            ASSERT(srbData->CurrentSrb != NULL &&
                   srbData->CompletedRequests == NULL);

            if ((srb->SrbStatus == SRB_STATUS_SUCCESS) &&
                    ((srb->Cdb[0] == SCSIOP_READ) ||
                     (srb->Cdb[0] == SCSIOP_WRITE))) {
                ASSERT(srb->DataTransferLength);
            }
#endif

            if(((srb->SrbStatus == SRB_STATUS_SUCCESS) ||
                    (srb->SrbStatus == SRB_STATUS_DATA_OVERRUN)) &&
                    (TEST_FLAG(srb->SrbFlags, SRB_FLAGS_UNSPECIFIED_DIRECTION))) {
                ASSERT(srbData->OriginalDataTransferLength >=
                       srb->DataTransferLength);
            }

            srbData->CompletedRequests =
                deviceExtension->InterruptData.CompletedRequests;
            deviceExtension->InterruptData.CompletedRequests = srbData;

            //
            // Cache away the last logical unit we touched in the miniport.
            // This is cleared when we come out of the miniport
            // synchronization but provides a shortcut for finding the
            // logical unit before going into the hash table.
            //

            deviceExtension->CachedLogicalUnit = srbData->LogicalUnit;
        }

        break;

    case ResetDetected:

        //
        // Notifiy the port driver that a reset has been reported.
        //

        deviceExtension->InterruptData.InterruptFlags |=
            PD_RESET_REPORTED | PD_RESET_HOLD;
        break;

    case NextLuRequest:

        //
        // The miniport driver is ready for the next request and
        // can accept a request for this logical unit.
        //

        pathId = va_arg(ap, UCHAR);
        targetId = va_arg(ap, UCHAR);
        lun = va_arg(ap, UCHAR);

        //
        // A next request is impiled by this notification so set the
        // ready for next reqeust flag.
        //

        deviceExtension->InterruptData.InterruptFlags |= PD_READY_FOR_NEXT_REQUEST;

        logicalUnit = deviceExtension->CachedLogicalUnit;

        if((logicalUnit == NULL) ||
                (logicalUnit->TargetId != targetId) ||
                (logicalUnit->PathId != pathId) ||
                (logicalUnit->Lun != lun)) {

            logicalUnit = GetLogicalUnitExtension(deviceExtension,
                                                  pathId,
                                                  targetId,
                                                  lun,
                                                  FALSE,
                                                  FALSE);
        }

        if (logicalUnit != NULL && logicalUnit->ReadyLogicalUnit != NULL) {

            //
            // Since our ReadyLogicalUnit link field is not NULL we must
            // have already been linked onto a ReadyLogicalUnit list.
            // There is nothing to do.
            //

            break;
        }

        //
        // Don't process this as request for the next logical unit, if
        // there is a untagged request for active for this logical unit.
        // The logical unit will be started when untagged request completes.
        //

        if (logicalUnit->CurrentUntaggedRequest == NULL) {

            //
            // Add the logical unit to the chain of logical units that
            // another request maybe processed for.
            //

            logicalUnit->ReadyLogicalUnit =
                deviceExtension->InterruptData.ReadyLogicalUnit;
            deviceExtension->InterruptData.ReadyLogicalUnit = logicalUnit;
        }

        break;

    case CallDisableInterrupts:

        ASSERT(deviceExtension->InterruptData.InterruptFlags &
               PD_DISABLE_INTERRUPTS);

        //
        // The miniport wants us to call the specified routine
        // with interrupts disabled.  This is done after the current
        // HwRequestInterrutp routine completes. Indicate the call is
        // needed and save the routine to be called.
        //

        deviceExtension->Flags |= PD_DISABLE_CALL_REQUEST;

        deviceExtension->HwRequestInterrupt = va_arg(ap, PHW_INTERRUPT);

        break;

    case CallEnableInterrupts:

        //
        // The miniport wants us to call the specified routine
        // with interrupts enabled this is done from the DPC.
        // Disable calls to the interrupt routine, indicate the call is
        // needed and save the routine to be called.
        //

        deviceExtension->InterruptData.InterruptFlags |=
            PD_DISABLE_INTERRUPTS | PD_ENABLE_CALL_REQUEST;

        deviceExtension->HwRequestInterrupt = va_arg(ap, PHW_INTERRUPT);

        break;

    case RequestTimerCall:

        //
        // The driver wants to set the miniport timer.
        // Save the timer parameters.
        //

        deviceExtension->InterruptData.InterruptFlags |=
            PD_TIMER_CALL_REQUEST;
        deviceExtension->InterruptData.HwTimerRequest =
            va_arg(ap, PHW_INTERRUPT);
        deviceExtension->InterruptData.MiniportTimerValue =
            va_arg(ap, ULONG);
        break;

    case WMIEvent: {

        //
        // The miniport wishes to post a WMI event for the adapter
        // or a specified SCSI target.
        //

        PWMI_MINIPORT_REQUEST_ITEM lastMiniPortRequest;
        PWMI_MINIPORT_REQUEST_ITEM wmiMiniPortRequest;
        PWNODE_EVENT_ITEM          wnodeEventItem;
        PWNODE_EVENT_ITEM          wnodeEventItemCopy;

        wnodeEventItem     = va_arg(ap, PWNODE_EVENT_ITEM);
        pathId             = va_arg(ap, UCHAR);

        if (pathId != 0xFF) {
            targetId = va_arg(ap, UCHAR);
            lun      = va_arg(ap, UCHAR);
        }

        //
        // Validate the event first.  Then attempt to obtain a free
        // WMI_MINIPORT_REQUEST_ITEM structure so that we may store
        // this request and process it at DPC level later.  If none
        // are obtained or the event is bad, we ignore the request.
        //

        if ((wnodeEventItem == NULL) ||
                (wnodeEventItem->WnodeHeader.BufferSize >
                 WMI_MINIPORT_EVENT_ITEM_MAX_SIZE)) {

            va_end(ap);    //  size, no free WMI_MINIPORT_REQUEST_ITEMs left]
            return;
        }

        //
        // Remove the WMI_MINIPORT_REQUEST_ITEM from the free list.
        //
        wmiMiniPortRequest = SpWmiPopFreeRequestItem(deviceExtension);

        //
        // Log an error if a free request item could not be dequeued
        // (log only once in the lifetime of this adapter).
        //
        if (wmiMiniPortRequest == NULL) {

            if (!deviceExtension->WmiFreeMiniPortRequestsExhausted) {
                deviceExtension->WmiFreeMiniPortRequestsExhausted = TRUE;
                ScsiPortLogError(HwDeviceExtension,
                                 NULL,
                                 pathId,
                                 targetId,
                                 lun,
                                 SP_LOST_WMI_MINIPORT_REQUEST,
                                 0);
            }

            va_end(ap);
            return;
        }

        //
        // Save information pertaining to this WMI request for later
        // processing.
        //

        deviceExtension->InterruptData.InterruptFlags |= PD_WMI_REQUEST;

        wmiMiniPortRequest->TypeOfRequest = (UCHAR)WMIEvent;
        wmiMiniPortRequest->PathId        = pathId;
        wmiMiniPortRequest->TargetId      = targetId;
        wmiMiniPortRequest->Lun           = lun;

        RtlCopyMemory(wmiMiniPortRequest->WnodeEventItem,
                      wnodeEventItem,
                      wnodeEventItem->WnodeHeader.BufferSize);

        //
        // Queue the new WMI_MINIPORT_REQUEST_ITEM to the end of list in the
        // interrupt data structure.
        //
        wmiMiniPortRequest->NextRequest = NULL;

        lastMiniPortRequest =
            deviceExtension->InterruptData.WmiMiniPortRequests;

        if (lastMiniPortRequest) {

            while (lastMiniPortRequest->NextRequest) {
                lastMiniPortRequest = lastMiniPortRequest->NextRequest;
            }
            lastMiniPortRequest->NextRequest = wmiMiniPortRequest;

        } else {
            deviceExtension->InterruptData.WmiMiniPortRequests =
                wmiMiniPortRequest;
        }

        break;
    }

    case WMIReregister: {
        //
        // The miniport wishes to re-register the GUIDs for the adapter or
        // a specified SCSI target.
        //

        PWMI_MINIPORT_REQUEST_ITEM lastMiniPortRequest;
        PWMI_MINIPORT_REQUEST_ITEM wmiMiniPortRequest;

        pathId             = va_arg(ap, UCHAR);

        if (pathId != 0xFF) {
            targetId = va_arg(ap, UCHAR);
            lun      = va_arg(ap, UCHAR);
        }

        //
        // Attempt to obtain a free WMI_MINIPORT_REQUEST_ITEM structure
        // so that we may store this request and process it at DPC
        // level later. If none are obtained or the event is bad, we
        // ignore the request.
        //
        // Remove a WMI_MINPORT_REQUEST_ITEM from the free list.
        //
        wmiMiniPortRequest = SpWmiPopFreeRequestItem(deviceExtension);

        if (wmiMiniPortRequest == NULL) {

            //
            // Log an error if a free request item could not be dequeued
            // (log only once in the lifetime of this adapter).
            //
            if (!deviceExtension->WmiFreeMiniPortRequestsExhausted) {

                deviceExtension->WmiFreeMiniPortRequestsExhausted = TRUE;

                ScsiPortLogError(HwDeviceExtension,
                                 NULL,
                                 pathId,
                                 targetId,
                                 lun,
                                 SP_LOST_WMI_MINIPORT_REQUEST,
                                 0);
            }

            va_end(ap);
            return;
        }

        //
        // Save information pertaining to this WMI request for later
        // processing.
        //

        deviceExtension->InterruptData.InterruptFlags |= PD_WMI_REQUEST;
        wmiMiniPortRequest->TypeOfRequest = (UCHAR)WMIReregister;
        wmiMiniPortRequest->PathId        = pathId;
        wmiMiniPortRequest->TargetId      = targetId;
        wmiMiniPortRequest->Lun           = lun;

        //
        // Queue the new WMI_MINIPORT_REQUEST_ITEM to the end of list in the
        // interrupt data structure.
        //
        wmiMiniPortRequest->NextRequest = NULL;

        lastMiniPortRequest =
            deviceExtension->InterruptData.WmiMiniPortRequests;

        if (lastMiniPortRequest) {

            while (lastMiniPortRequest->NextRequest) {
                lastMiniPortRequest = lastMiniPortRequest->NextRequest;
            }
            lastMiniPortRequest->NextRequest = wmiMiniPortRequest;

        } else {
            deviceExtension->InterruptData.WmiMiniPortRequests =
                wmiMiniPortRequest;
        }

        break;
    }

    case BusChangeDetected: {

        SET_FLAG(deviceExtension->InterruptData.InterruptFlags,
                 PD_BUS_CHANGE_DETECTED);
        break;
    }

    default: {
        ASSERT(0);
        break;
    }
    }

    va_end(ap);

    //
    // Request a DPC be queued after the interrupt completes.
    //

    deviceExtension->InterruptData.InterruptFlags |= PD_NOTIFICATION_REQUIRED;

} // end ScsiPortNotification()