Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

662 lines
14 KiB

/*++
Copyright (c) 1995 Microsoft Corporation
Module Name:
control.c
Abstract:
Author:
Ken Reneris (kenr) March-13-1885
Environment:
Kernel mode only.
Revision History:
--*/
#include "pciport.h"
#ifdef ALLOC_PRAGMA
#pragma alloc_text(PAGE,PcipControlWorker)
#pragma alloc_text(PAGE,PciCtlEject)
#pragma alloc_text(PAGE,PciCtlLock)
#pragma alloc_text(PAGE,PciCtlForward)
#pragma alloc_text(PAGE,PcipCompleteDeviceControl)
#endif
VOID
PcipStartWorker (
VOID
)
/*++
Routine Description:
This function is used to verify a worker thread is dispatched
Arguments:
Return Value:
--*/
{
ULONG WorkerQueued;
if (!PcipWorkerQueued) {
WorkerQueued = ExInterlockedExchangeUlong (
&PcipWorkerQueued,
1,
PcipSpinLock
);
if (!WorkerQueued) {
ExQueueWorkItem (&PcipWorkItem, DelayedWorkQueue);
}
}
}
VOID
PcipQueueCheckBus (
PBUS_HANDLER Handler
)
{
PPCI_PORT PciPort;
PciPort = PCIPORTDATA(Handler);
ExInterlockedInsertTailList (
&PcipCheckBusList,
&PciPort->CheckBus,
&PcipSpinlock
);
PcipStartWorker();
}
VOID
PcipControlWorker (
IN PVOID WorkerContext
)
/*++
Routine Description:
This function is called by a system worker thread.
The worker thread dequeues any DeviceControls which need to be
processed and dispatches them.
It then checks for any
Arguments:
Return Value:
--*/
{
PLIST_ENTRY Entry;
PPCI_PORT PciPort;
PHAL_DEVICE_CONTROL_CONTEXT Context;
PAGED_CODE ();
//
// Dispatch pending slot controls
//
for (; ;) {
Entry = ExInterlockedRemoveHeadList (
&PcipControlWorkerList,
&PcipSpinlock
);
if (!Entry) {
break;
}
Context = CONTAINING_RECORD (
Entry,
HAL_DEVICE_CONTROL_CONTEXT,
ContextWorkQueue,
);
PcipDispatchControl (Context);
}
//
// Reset worker item for next time
//
ExInitializeWorkItem (&PcipWorkItem, PcipControlWorker, NULL);
ExInterlockedExchangeUlong (&PcipWorkerQueued, 0, PcipSpinLock);
//
// Process check buses
//
for (; ;) {
Entry = ExInterlockedRemoveHeadList (
&PcipCheckBusList,
&PcipSpinlock
);
if (!Entry) {
break;
}
PciPort = CONTAINING_RECORD (
Entry,
PCI_PORT,
CheckBus
);
PcipCheckBus (PciPort, FALSE);
}
}
VOID
PcipDispatchControl (
PHAL_DEVICE_CONTROL_CONTEXT Context
)
/*++
Routine Description:
This function dispatches a DeviceControl to the appropiate handler.
If the slot is busy, the DeviceControl may be queued for dispatching at
a later time
Arguments:
Context - The DeviceControl context to dispatch
Return Value:
--*/
{
PDEVICE_CONTROL_HANDLER DeviceControlHandler;
PDEVICE_DATA DeviceData;
PPCI_PORT PciPort;
KIRQL OldIrql;
BOOLEAN EnqueueIt;
PLIST_ENTRY Link;
DeviceControlHandler = (PDEVICE_CONTROL_HANDLER) Context->ContextControlHandler;
PciPort = PCIPORTDATA(Context->Handler);
DeviceData = DeviceHandler2DeviceData (Context->DeviceControl.DeviceHandler);
//
// Get access to the device specific data.
//
KeAcquireSpinLock (&PcipSpinlock, &OldIrql);
//
// Verify the device data is still valid
//
if (!DeviceData->Valid) {
//
// Caller has invalid handle, or handle to a different device
//
DebugPrint ((2, "PCI: DeviceControl has invalid device handler \n" ));
Context->DeviceControl.Status = STATUS_NO_SUCH_DEVICE;
KeReleaseSpinLock (&PcipSpinlock, OldIrql);
HalCompleteDeviceControl (Context);
return ;
}
//
// Check to see if this request can be begun now
//
Link = (PLIST_ENTRY) &Context->ContextWorkQueue;
EnqueueIt = DeviceControlHandler->BeginDeviceControl (DeviceData, Context);
if (EnqueueIt) {
//
// Enqueue this command to be handled when the slot is no longer busy.
//
InsertTailList (&PciPort->DeviceControl, Link);
KeReleaseSpinLock (&PcipSpinlock, OldIrql);
return ;
}
//
// Dispatch the function to it's handler
//
KeReleaseSpinLock (&PcipSpinlock, OldIrql);
DeviceControlHandler->ControlHandler (DeviceData, Context);
}
VOID
PcipiCompleteDeviceControl (
NTSTATUS Status,
PHAL_DEVICE_CONTROL_CONTEXT Context,
PDEVICE_DATA DeviceData,
PBOOLEAN Sync
)
/*++
Routine Description:
This function is used to complete a DeviceControl. If another DeviceControl
was delayed on this device, this function will dispatch them
Arguments:
Return Value:
--*/
{
KIRQL OldIrql;
PPCI_PORT PciPort;
PLIST_ENTRY Link;
PBOOLEAN BusyFlag;
BOOLEAN StartWorker;
PDEVICE_HANDLER_OBJECT DeviceHandler;
DeviceHandler = DeviceData2DeviceHandler(DeviceData);
BusyFlag = (PBOOLEAN) Context->ContextBusyFlag;
PciPort = PCIPORTDATA(Context->Handler);
//
// Pass it to the hal for completion
//
Context->DeviceControl.Status = Status;
HalCompleteDeviceControl (Context);
StartWorker = FALSE;
//
// Get access to the slot specific data.
//
KeAcquireSpinLock (&PcipSpinlock, &OldIrql);
//
// Clear appropiate busy flag
//
*BusyFlag = FALSE;
//
// Check to see if there are any pending slot controls for
// this device. If so, requeue them to the worker thread.
// (yes, this code is not efficient, but doing it this way
// saves a little on the nonpaged pool device_data structure size)
//
for (Link = PciPort->DeviceControl.Flink; Link != &PciPort->DeviceControl; Link = Link->Flink) {
Context = CONTAINING_RECORD (Link, HAL_DEVICE_CONTROL_CONTEXT, ContextWorkQueue);
if (Context->DeviceControl.DeviceHandler == DeviceHandler) {
RemoveEntryList (Link);
InsertTailList (&PcipControlWorkerList, Link);
StartWorker = TRUE;
break;
}
}
KeReleaseSpinLock (&PcipSpinlock, OldIrql);
if (StartWorker) {
PcipStartWorker ();
}
}
VOID
PcipCompleteDeviceControl (
NTSTATUS Status,
PHAL_DEVICE_CONTROL_CONTEXT Context,
PDEVICE_DATA DeviceData
)
/*++
Routine Description:
This function is used to complete a DeviceControl. If another DeviceControl
was delayed on this device, this function will dispatch them
Arguments:
Return Value:
--*/
{
PAGED_CODE();
PcipiCompleteDeviceControl (
Status,
Context,
DeviceData,
&DeviceData->SyncBusy
);
}
BOOLEAN
FASTCALL
PciBCtlPower (
PDEVICE_DATA DeviceData,
PHAL_DEVICE_CONTROL_CONTEXT Context
)
{
if ( *((PPOWER_STATE) Context->DeviceControl.Buffer) == PowerUp ) {
//
// This is a power on, there can only be one of these on the
// slot at any one time
//
ASSERT (DeviceData->AsyncBusy == FALSE);
//
// If PowerUp needs to pend, then queue the request
//
if (DeviceData->PendPowerUp) {
return TRUE;
}
DeviceData->AsyncBusy = TRUE;
Context->ContextBusyFlag = (ULONG) &DeviceData->AsyncBusy;
return FALSE;
}
//
// Something other then a PowerUp. Some form of power down,
// treat it like any other sync request
//
return PciBCtlSync (DeviceData, Context);
}
BOOLEAN
FASTCALL
PciBCtlSync (
PDEVICE_DATA DeviceData,
PHAL_DEVICE_CONTROL_CONTEXT Context
)
{
//
// This is a sync command, verify the slot is not busy with a different
// command.
//
if (DeviceData->SyncBusy || DeviceData->AsyncBusy) {
//
// Enqueue this command to be handled when the slot is no longer busy.
//
return TRUE;
}
//
// Don't enqueue, dispatch it now
//
DeviceData->SyncBusy = TRUE;
Context->ContextBusyFlag = (ULONG) &DeviceData->SyncBusy;
return FALSE;
}
BOOLEAN
FASTCALL
PciBCtlEject (
PDEVICE_DATA DeviceData,
PHAL_DEVICE_CONTROL_CONTEXT Context
)
{
BOOLEAN Busy;
//
// If Slot is busy, then wait
//
Busy = PciBCtlSync (DeviceData, Context);
if (!Busy) {
//
// Just trying to eject a device will invalidate the current
// device handler object for it...
//
DeviceData->Valid = FALSE;
DebugPrint ((5, "PCI: set handle invalid - slot %x\n", DeviceDataSlot(DeviceData)));
}
return Busy;
}
BOOLEAN
FASTCALL
PciBCtlLock (
PDEVICE_DATA DeviceData,
PHAL_DEVICE_CONTROL_CONTEXT Context
)
{
BOOLEAN Busy;
//
// If Slot is busy, then wait
//
Busy = PciBCtlSync (DeviceData, Context);
if (!Busy &&
((PBCTL_SET_CONTROL) Context->DeviceControl.Buffer) ) {
//
// About to perform an unlock, set PendPowerUp
// This will stop any async power up requests
//
ASSERT (DeviceData->PendPowerUp == FALSE);
DeviceData->PendPowerUp = TRUE;
}
return Busy;
}
VOID
PciCtlEject (
PDEVICE_DATA DeviceData,
PHAL_DEVICE_CONTROL_CONTEXT Context
)
{
NTSTATUS Status;
PAGED_CODE();
//
// We don't know how to lock or unlock, but we track various
// device state. Event attempting to ejecting a device effectively
// unlocks & powers it down.
//
DeviceData->Locked = FALSE;
Status = PcipPowerDownSlot (Context->Handler, DeviceData);
//
// Pass the eject request
//
if (NT_SUCCESS(Status)) {
Status = BugBugSubclass ();
}
DebugPrint ((2, "PCI: Eject complete - Slot %x, Status %x\n",
DeviceDataSlot(DeviceData), Status));
PcipCompleteDeviceControl (Status, Context, DeviceData);
}
VOID
PciCtlLock (
PDEVICE_DATA DeviceData,
PHAL_DEVICE_CONTROL_CONTEXT Context
)
{
BOOLEAN Lock;
NTSTATUS Status;
PAGED_CODE();
//
// We don't know how to lock or unlock, but we track the
// device's locked state
//
Lock = ((PBCTL_SET_CONTROL) Context->DeviceControl.Buffer)->Control;
//
// If this is an unlock request, powered down the slot
//
Status = STATUS_SUCCESS;
if (!Lock) {
Status = PcipPowerDownSlot (Context->Handler, DeviceData);
}
//
// Pass the lock request to miniport driver to lock/unlock the slot
//
if (NT_SUCCESS(Status)) {
Status = BugBugSubclass ();
}
//
// If it worked, set the new locked state
//
if (NT_SUCCESS(Status)) {
DeviceData->Locked = Lock;
}
//
// Allow power requests to continue
//
DeviceData->PendPowerUp = FALSE;
DebugPrint ((2, "PCI: %s complete - Slot %x, Status %x\n",
Lock ? "Lock" : "Unlock", DeviceDataSlot(DeviceData), Status));
PcipCompleteDeviceControl (Status, Context, DeviceData);
}
VOID
PciCtlPower (
PDEVICE_DATA DeviceData,
PHAL_DEVICE_CONTROL_CONTEXT Context
)
{
POWER_STATE Power;
NTSTATUS Status;
// not pagable
//
// We don't know how to power on or off the device, but we track the
// device's power state
//
Power = *((PPOWER_STATE) Context->DeviceControl.Buffer);
//
// If this is a power down request, complete it
//
if (Power != PowerUp) {
Status = PcipPowerDownSlot (Context->Handler, DeviceData);
PcipCompleteDeviceControl (Status, Context, DeviceData);
return ;
}
//
// Device must be locked, or the power up request should have
// received an invalid device error.
//
ASSERT (DeviceData->Locked);
//
// If the device already has power, then there's nothing to do
//
if (DeviceData->Power) {
PcipiCompleteDeviceControl (STATUS_SUCCESS, Context, DeviceData, &DeviceData->AsyncBusy);
return ;
}
// bugbug pass it to a child driver, for now just complete it
PcipCompletePowerUp (DeviceData, Context);
}
VOID
PcipCompletePowerUp (
PDEVICE_DATA DeviceData,
PHAL_DEVICE_CONTROL_CONTEXT Context
)
{
NTSTATUS Status;
//
// Put the device's prior configuration back.
//
DeviceData->Power = TRUE;
Status = PcipFlushConfig (Context->Handler, DeviceData);
if (!NT_SUCCESS(Status)) {
//
// The device's state could not be restored. The decodes
// for the device shouldn't be enabled, so there's no immidiate
// problem. But break the current handle to the device and
// kick off a bus check. This will cause us to assign the
// device a new handle, and to power it off.
//
DeviceData->Valid = FALSE;
PcipQueueCheckBus (Context->Handler);
}
if (Context->DeviceControl.ControlCode == BCTL_SET_POWER) {
DebugPrint ((2, "PCI: Powerup complete - Slot %x, Status %x\n",
DeviceDataSlot(DeviceData), Status));
PcipiCompleteDeviceControl (Status, Context, DeviceData, &DeviceData->AsyncBusy);
}
}
VOID
PciCtlForward (
PDEVICE_DATA DeviceData,
PHAL_DEVICE_CONTROL_CONTEXT Context
)
{
PAGED_CODE ();
DbgPrint ("PCI: BUGBUG Forward\n");
PcipCompleteDeviceControl (STATUS_NOT_IMPLEMENTED, Context, DeviceData);
}