|
|
/*++
Copyright (c) 1998-2000 Microsoft Corporation
Module Name:
state.c
Abstract:
This module the state manipulation engine for PCI
Author:
Adrian J. Oney (AdriaO) 20-Oct-1998
Revision History:
Environment:
NT Kernel Model Driver only
--*/
#include "pcip.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE, PciBeginStateTransition)
#pragma alloc_text(PAGE, PciCommitStateTransition)
#pragma alloc_text(PAGE, PciCancelStateTransition)
#pragma alloc_text(PAGE, PciIsInTransitionToState)
#endif
//
// This array marks Allowed, Disallowed, and Illegal state transitions.
//
// The horizontal axis represents the current state.
// The vertical axis represents the state we are being asked to transition into.
//
// There are four values in the table:
// STATUS_SUCCESS - State transition is possible
// STATUS_INVALID_DEVICE_STATE - We legally cannot do the state transition
// STATUS_INVALID_DEVICE_REQUEST - Illegal transition, but known OS bug.
// STATUS_FAIL_CHECK - Consistancy problem. We should ASSERT!
//
// Count is PciMaxObjectState of
//
// PciNotStarted, PciStarted, PciDeleted, PciStopped, PciSurpriseRemoved, and
// PciSynchronizedOperation
//
// The final state is used to initiate operations that synchronously with
// respect to it and other state transitions. Commiting PciSynchronizedOperation
// is strictly illegal, and we are never strickly "in" that state.
//
//
// We will use the following visually shorter notation (State Transition foo)
// for the table:
//
#define ST_OK STATUS_SUCCESS
#define ST_NOTOK STATUS_INVALID_DEVICE_STATE
#define ST_ERROR STATUS_FAIL_CHECK
#define ST_NTBUG STATUS_INVALID_DEVICE_REQUEST
#define ST_OSBUG STATUS_INVALID_DEVICE_REQUEST
#define ST_9XBUG STATUS_FAIL_CHECK // Change to STATUS_SUCCESS for 9x
NTSTATUS PnpStateTransitionArray[PciMaxObjectState][PciMaxObjectState] = { // at NotStarted, Started, Deleted, Stopped, SurpriseRemoved, SynchronizedOperation
{ ST_ERROR, ST_OK, ST_ERROR, ST_OK, ST_ERROR, ST_ERROR }, // entering PciNotStarted
{ ST_OK, ST_ERROR, ST_ERROR, ST_OK, ST_ERROR, ST_ERROR }, // entering PciStarted
{ ST_OK, ST_OK, ST_ERROR, ST_ERROR, ST_OK, ST_ERROR }, // entering PciDeleted
{ ST_OSBUG, ST_OK, ST_ERROR, ST_ERROR, ST_ERROR, ST_ERROR }, // entering PciStopped
{ ST_NTBUG, ST_OK, ST_ERROR, ST_OK, ST_ERROR, ST_ERROR }, // entering PciSurpriseRemoved
{ ST_OK, ST_OK, ST_NOTOK, ST_OK, ST_NOTOK, ST_ERROR } // entering PciSynchronizedOperation
};
//
// This array is used in debug to restrict which state transitions can be
// spuriously cancelled. We restrict this to Stops and Removes, which come
// through all the time due to the inability of PnP to differentiate which
// device in a stack failed a query.
//
#if DBG
// Cancelling NotStarted, Started, Removed, Stopped, SurpriseRemoved, SynchronizedOperation
NTSTATUS PnpStateCancelArray[PciMaxObjectState] = { ST_NTBUG, ST_ERROR, ST_NOTOK, ST_NOTOK, ST_ERROR, ST_ERROR };
//
// While here, declare the text we use for debug squirties...
//
PUCHAR PciTransitionText[] = { "PciNotStarted", "PciStarted", "PciDeleted", "PciStopped", "PciSurpriseRemoved", "PciSynchronizedOperation", "PciMaxObjectState" }; #endif
VOID PciInitializeState( IN PPCI_COMMON_EXTENSION DeviceExtension ) { DeviceExtension->DeviceState = PciNotStarted; DeviceExtension->TentativeNextState = PciNotStarted; }
NTSTATUS PciBeginStateTransition( IN PPCI_COMMON_EXTENSION DeviceExtension, IN PCI_OBJECT_STATE NewState ) { NTSTATUS status; PCI_OBJECT_STATE currentState;
#if DBG
PciDebugPrint( PciDbgInformative, "PCI Request to begin transition of Extension %p to %s ->", DeviceExtension, PciTransitionText[NewState] ); #endif
//
// Our "next" device state should be the same as our current device state.
//
PCI_ASSERT(DeviceExtension->TentativeNextState == DeviceExtension->DeviceState); currentState = DeviceExtension->DeviceState;
//
// One of three returns will wind their way out of this code:
// STATUS_SUCCESS - State transition is possible
// STATUS_INVALID_DEVICE_STATE - We legally cannot do the state transition
// STATUS_FAIL_CHECK - Consistancy problem. We should ASSERT!
//
PCI_ASSERT(currentState < PciMaxObjectState); PCI_ASSERT(NewState < PciMaxObjectState);
//
// Get plausibility and legality of requested state change.
//
status = PnpStateTransitionArray[NewState][currentState];
#if DBG
//
// State bug in PnP or driver. Investigation required.
//
if (status == STATUS_FAIL_CHECK) {
PciDebugPrint( PciDbgAlways, "ERROR\nPCI: Error trying to enter state \"%s\" from state \"%s\"\n", PciTransitionText[NewState], PciTransitionText[currentState] );
DbgBreakPoint();
} else if (status == STATUS_INVALID_DEVICE_REQUEST) {
PciDebugPrint( PciDbgInformative, "ERROR\nPCI: Illegal request to try to enter state \"%s\" from state \"%s\", rejecting", PciTransitionText[NewState], PciTransitionText[currentState] ); } #endif
//
// Someone tried to transition from A to A. We must fail the attemtpt
// (ie STATUS_INVALID_DEVICE_STATE). There is no known case where we
// should return success yet do nothing.
//
PCI_ASSERT((NewState!=DeviceExtension->DeviceState) || (!NT_SUCCESS(status)));
if (NT_SUCCESS(status)) { DeviceExtension->TentativeNextState = (UCHAR)NewState; }
PciDebugPrint(PciDbgInformative, "->%x\n", status); return status; }
VOID PciCommitStateTransition( IN PPCI_COMMON_EXTENSION DeviceExtension, IN PCI_OBJECT_STATE NewState ) { #if DBG
PciDebugPrint( PciDbgInformative, "PCI Commit transition of Extension %p to %s\n", DeviceExtension, PciTransitionText[NewState] ); #endif
//
// This state is illegal.
//
PCI_ASSERT(NewState != PciSynchronizedOperation);
//
// verify commit properly pairs with previous PciBeginStateTransition.
//
PCI_ASSERT(DeviceExtension->TentativeNextState == NewState);
DeviceExtension->DeviceState = (UCHAR)NewState; }
NTSTATUS PciCancelStateTransition( IN PPCI_COMMON_EXTENSION DeviceExtension, IN PCI_OBJECT_STATE StateNotEntered ) { #if DBG
PciDebugPrint( PciDbgInformative, "PCI Request to cancel transition of Extension %p to %s ->", DeviceExtension, PciTransitionText[StateNotEntered] ); #endif
//
// Spurious Cancel's are allowed in specific states. This is allowed
// because PnP can't help but send them.
//
if (DeviceExtension->TentativeNextState == DeviceExtension->DeviceState) {
PciDebugPrint(PciDbgInformative, "%x\n", STATUS_INVALID_DEVICE_STATE); PCI_ASSERT(StateNotEntered < PciMaxObjectState); PCI_ASSERT(PnpStateCancelArray[StateNotEntered] != STATUS_FAIL_CHECK); return STATUS_INVALID_DEVICE_STATE; }
//
// verify cancel properly pairs with previous PciBeginStateTransition.
//
PCI_ASSERT(DeviceExtension->TentativeNextState == StateNotEntered);
//
// OK, our tests say we are in a transition. Verify the mutex.
//
DeviceExtension->TentativeNextState = DeviceExtension->DeviceState;
PciDebugPrint(PciDbgInformative, "%x\n", STATUS_SUCCESS); return STATUS_SUCCESS; }
BOOLEAN PciIsInTransitionToState( IN PPCI_COMMON_EXTENSION DeviceExtension, IN PCI_OBJECT_STATE NextState ) { PCI_ASSERT(KeGetCurrentIrql() == PASSIVE_LEVEL); PCI_ASSERT(NextState < PciMaxObjectState);
//
// Are we in a state transition?
//
if (DeviceExtension->TentativeNextState == DeviceExtension->DeviceState) {
return FALSE; }
//
// OK, our tests say we are in a transition. Verify the mutex.
//
ASSERT_MUTEX_HELD(&DeviceExtension->StateMutex);
return (BOOLEAN)(DeviceExtension->TentativeNextState == NextState); }
|