512 lines
10 KiB
C
512 lines
10 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1995 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
control.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
Shie-Lin Tzong (shielint) Apr-23-1995
|
|||
|
Most of the code is adapted from PCI bus extender.
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
Kernel mode only.
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "busp.h"
|
|||
|
|
|||
|
VOID
|
|||
|
PipiCompleteDeviceControl (
|
|||
|
NTSTATUS Status,
|
|||
|
PHAL_DEVICE_CONTROL_CONTEXT Context,
|
|||
|
PDEVICE_INFORMATION DeviceData,
|
|||
|
PBOOLEAN Sync
|
|||
|
);
|
|||
|
|
|||
|
#ifdef ALLOC_PRAGMA
|
|||
|
#pragma alloc_text(PAGE,PipControlWorker)
|
|||
|
#pragma alloc_text(PAGE,PipCompleteDeviceControl)
|
|||
|
#endif
|
|||
|
|
|||
|
VOID
|
|||
|
PipStartWorker (
|
|||
|
VOID
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function is used to start a worker thread.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
ULONG workerQueued;
|
|||
|
|
|||
|
if (!PipWorkerQueued) {
|
|||
|
workerQueued = ExInterlockedExchangeUlong (
|
|||
|
&PipWorkerQueued,
|
|||
|
1,
|
|||
|
PipSpinLock
|
|||
|
);
|
|||
|
|
|||
|
if (!workerQueued) {
|
|||
|
ExQueueWorkItem (&PipWorkItem, DelayedWorkQueue);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
PipQueueCheckBus (
|
|||
|
IN PBUS_HANDLER BusHandler
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function enqueues Bus check request to buscheck list.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
BusHandler - supplies a pointer to the bus handler of the bus to be checked.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
ExInterlockedInsertTailList (
|
|||
|
&PipCheckBusList,
|
|||
|
&((PPI_BUS_EXTENSION)BusHandler->BusData)->CheckBus,
|
|||
|
&PipSpinlock
|
|||
|
);
|
|||
|
|
|||
|
PipStartWorker();
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
PipControlWorker (
|
|||
|
IN PVOID WorkerContext
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function is called by a system worker thread.
|
|||
|
|
|||
|
The worker thread dequeues any SlotControls which need to be
|
|||
|
processed and dispatches them.
|
|||
|
|
|||
|
It then checks for any check bus request.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
WorkerContext - supplies a pointer to a context for the worker. Here
|
|||
|
it is always NULL.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PLIST_ENTRY entry;
|
|||
|
PPI_BUS_EXTENSION busExtension;
|
|||
|
PHAL_DEVICE_CONTROL_CONTEXT context;
|
|||
|
|
|||
|
PAGED_CODE ();
|
|||
|
|
|||
|
//
|
|||
|
// process check bus
|
|||
|
//
|
|||
|
|
|||
|
for (; ;) {
|
|||
|
entry = ExInterlockedRemoveHeadList (
|
|||
|
&PipCheckBusList,
|
|||
|
&PipSpinlock
|
|||
|
);
|
|||
|
|
|||
|
if (!entry) {
|
|||
|
break;
|
|||
|
}
|
|||
|
busExtension = CONTAINING_RECORD (
|
|||
|
entry,
|
|||
|
PI_BUS_EXTENSION,
|
|||
|
CheckBus
|
|||
|
);
|
|||
|
|
|||
|
PipCheckBus (busExtension->BusHandler);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Reset worker item for next time
|
|||
|
//
|
|||
|
|
|||
|
ExInitializeWorkItem (&PipWorkItem, PipControlWorker, NULL);
|
|||
|
ExInterlockedExchangeUlong (&PipWorkerQueued, 0, PipSpinLock);
|
|||
|
|
|||
|
//
|
|||
|
// Dispatch pending device controls
|
|||
|
//
|
|||
|
|
|||
|
for (; ;) {
|
|||
|
entry = ExInterlockedRemoveHeadList (
|
|||
|
&PipControlWorkerList,
|
|||
|
&PipSpinlock
|
|||
|
);
|
|||
|
|
|||
|
if (!entry) {
|
|||
|
|
|||
|
//
|
|||
|
// All done, exit the loop.
|
|||
|
//
|
|||
|
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
context = CONTAINING_RECORD (
|
|||
|
entry,
|
|||
|
HAL_DEVICE_CONTROL_CONTEXT,
|
|||
|
ContextWorkQueue,
|
|||
|
);
|
|||
|
|
|||
|
PipDispatchControl (context);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
PipDispatchControl (
|
|||
|
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:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PDEVICE_CONTROL_HANDLER deviceControlHandler;
|
|||
|
PPI_BUS_EXTENSION busExtension;
|
|||
|
PDEVICE_INFORMATION deviceInfo;
|
|||
|
KIRQL oldIrql;
|
|||
|
BOOLEAN enqueueIt;
|
|||
|
PLIST_ENTRY link;
|
|||
|
|
|||
|
deviceControlHandler = (PDEVICE_CONTROL_HANDLER) Context->ContextControlHandler;
|
|||
|
deviceInfo = DeviceHandler2DeviceInfo (Context->DeviceControl.DeviceHandler);
|
|||
|
|
|||
|
//
|
|||
|
// Get access to the slot specific data.
|
|||
|
//
|
|||
|
|
|||
|
ExAcquireFastMutex(&PipMutex);
|
|||
|
|
|||
|
//
|
|||
|
// Verify the device data is still valid
|
|||
|
//
|
|||
|
|
|||
|
if (!(deviceInfo->Flags & DEVICE_FLAGS_VALID)) {
|
|||
|
|
|||
|
//
|
|||
|
// Caller has invalid handle, or handle to a different device
|
|||
|
//
|
|||
|
|
|||
|
DebugPrint ((DEBUG_MESSAGE, "PnpIsa: DeviceControl has invalid device handler \n" ));
|
|||
|
Context->DeviceControl.Status = STATUS_NO_SUCH_DEVICE;
|
|||
|
ExReleaseFastMutex(&PipMutex);
|
|||
|
HalCompleteDeviceControl (Context);
|
|||
|
return ;
|
|||
|
}
|
|||
|
busExtension = (PPI_BUS_EXTENSION)Context->Handler->BusData;
|
|||
|
|
|||
|
//
|
|||
|
// Check to see if this request can be begun now
|
|||
|
//
|
|||
|
|
|||
|
link = (PLIST_ENTRY) &Context->ContextWorkQueue;
|
|||
|
enqueueIt = PiBCtlSync (deviceInfo, Context);
|
|||
|
|
|||
|
if (enqueueIt) {
|
|||
|
|
|||
|
//
|
|||
|
// Enqueue this command to be handled when the slot is no longer busy.
|
|||
|
//
|
|||
|
|
|||
|
KeAcquireSpinLock (&PipSpinlock, &oldIrql);
|
|||
|
InsertTailList (&busExtension->DeviceControl, link);
|
|||
|
KeReleaseSpinLock (&PipSpinlock, oldIrql);
|
|||
|
ExReleaseFastMutex(&PipMutex);
|
|||
|
return ;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Dispatch the function to it's handler
|
|||
|
//
|
|||
|
|
|||
|
ExReleaseFastMutex(&PipMutex);
|
|||
|
deviceControlHandler->ControlHandler (deviceInfo, Context);
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
PipiCompleteDeviceControl (
|
|||
|
NTSTATUS Status,
|
|||
|
PHAL_DEVICE_CONTROL_CONTEXT Context,
|
|||
|
PDEVICE_INFORMATION DeviceInfo,
|
|||
|
PBOOLEAN Sync
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function is used to complete a SlotControl. If another SlotControl
|
|||
|
was delayed on this device, this function will dispatch them
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Status - supplies a NTSTATUS code for the completion.
|
|||
|
|
|||
|
Context - supplies a pointer to the original device control context.
|
|||
|
|
|||
|
DeviceInfo - supplies a pointer to the device info structure to be completed.
|
|||
|
|
|||
|
Sync - supplies a BOOLEAN variable to indicate
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
KIRQL oldIrql;
|
|||
|
PLIST_ENTRY link;
|
|||
|
PBOOLEAN busyFlag;
|
|||
|
BOOLEAN startWorker = FALSE;
|
|||
|
PPI_BUS_EXTENSION busExtension;
|
|||
|
PDEVICE_HANDLER_OBJECT deviceHandler;
|
|||
|
|
|||
|
busyFlag = (PBOOLEAN) Context->ContextBusyFlag;
|
|||
|
deviceHandler = DeviceInfo2DeviceHandler(DeviceInfo);
|
|||
|
busExtension = (PPI_BUS_EXTENSION)Context->Handler->BusData;
|
|||
|
|
|||
|
//
|
|||
|
// Pass it to the hal for completion
|
|||
|
//
|
|||
|
|
|||
|
Context->DeviceControl.Status = Status;
|
|||
|
HalCompleteDeviceControl (Context);
|
|||
|
|
|||
|
//
|
|||
|
// Get access to the slot specific data.
|
|||
|
//
|
|||
|
|
|||
|
KeAcquireSpinLock (&PipSpinlock, &oldIrql);
|
|||
|
|
|||
|
//
|
|||
|
// Clear appropiate busy flag
|
|||
|
//
|
|||
|
|
|||
|
*busyFlag = FALSE;
|
|||
|
|
|||
|
//
|
|||
|
// Check to see if there are any pending device controls for
|
|||
|
// this device. If so, requeue them to the worker thread
|
|||
|
//
|
|||
|
|
|||
|
for (link = busExtension->DeviceControl.Flink;
|
|||
|
link != &busExtension->DeviceControl;
|
|||
|
link = link->Flink) {
|
|||
|
|
|||
|
Context = CONTAINING_RECORD (link, HAL_DEVICE_CONTROL_CONTEXT, ContextWorkQueue);
|
|||
|
if (Context->DeviceControl.DeviceHandler == deviceHandler) {
|
|||
|
RemoveEntryList (link);
|
|||
|
InsertTailList (&PipControlWorkerList, link);
|
|||
|
startWorker = TRUE;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
KeReleaseSpinLock (&PipSpinlock, oldIrql);
|
|||
|
|
|||
|
if (startWorker) {
|
|||
|
PipStartWorker ();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
PipCompleteDeviceControl (
|
|||
|
NTSTATUS Status,
|
|||
|
PHAL_DEVICE_CONTROL_CONTEXT Context,
|
|||
|
PDEVICE_INFORMATION DeviceInfo
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
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();
|
|||
|
|
|||
|
PipiCompleteDeviceControl (
|
|||
|
Status,
|
|||
|
Context,
|
|||
|
DeviceInfo,
|
|||
|
&DeviceInfo->SyncBusy
|
|||
|
);
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
FASTCALL
|
|||
|
PiBCtlNone (
|
|||
|
PDEVICE_INFORMATION DeviceInfo,
|
|||
|
PHAL_DEVICE_CONTROL_CONTEXT Context
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function is used to indicate there is no synchronization for this
|
|||
|
device control function.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
Context - supplies a pointer to the device control context.
|
|||
|
|
|||
|
DeviceInfo - supplies a pointer to the device data to be completed.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
A boolean value to indicate if the request needs to be enqueued for later
|
|||
|
processing.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
//
|
|||
|
// No synchronization needed for this SlotControl
|
|||
|
//
|
|||
|
|
|||
|
Context->ContextBusyFlag = (ULONG) &PipNoBusyFlag;
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
FASTCALL
|
|||
|
PiBCtlSync (
|
|||
|
PDEVICE_INFORMATION DeviceInfo,
|
|||
|
PHAL_DEVICE_CONTROL_CONTEXT Context
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function is used to synchronize device control request. it checks the
|
|||
|
state (busy/not busy) of the slot and returns a boolean flag to indicate
|
|||
|
whether the request can be serviced immediately or it needs to be enqueued for
|
|||
|
later processing.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceInfo - supplies a pointer to the device data to be completed.
|
|||
|
|
|||
|
Context - supplies a pointer to the device control context.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
A boolean value to indicate if the request needs to be enqueued for later
|
|||
|
processing.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
//
|
|||
|
// This is a sync command, verify the slot is not busy with a different
|
|||
|
// command.
|
|||
|
//
|
|||
|
|
|||
|
if (DeviceInfo->SyncBusy) {
|
|||
|
|
|||
|
//
|
|||
|
// Enqueue this command to be handled when the slot is no longer busy.
|
|||
|
//
|
|||
|
|
|||
|
return TRUE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Don't enqueue, dispatch it now
|
|||
|
//
|
|||
|
|
|||
|
DeviceInfo->SyncBusy = TRUE;
|
|||
|
Context->ContextBusyFlag = (ULONG) &DeviceInfo->SyncBusy;
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
|
|||
|
VOID
|
|||
|
PiCtlQueryDeviceCapabilities (
|
|||
|
PDEVICE_INFORMATION DeviceInfo,
|
|||
|
PHAL_DEVICE_CONTROL_CONTEXT Context
|
|||
|
)
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function returns the BCTL_DEVICE_CAPABILITIES structure to the caller
|
|||
|
specified buffer.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceInfo - supplies a pointer to the device data to be completed.
|
|||
|
|
|||
|
Context - supplies a pointer to the device control context.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
{
|
|||
|
PBCTL_DEVICE_CAPABILITIES capabilities;
|
|||
|
|
|||
|
capabilities = (PBCTL_DEVICE_CAPABILITIES) Context->DeviceControl.Buffer;
|
|||
|
|
|||
|
capabilities->PowerSupported = FALSE;
|
|||
|
capabilities->ResumeSupported = FALSE;
|
|||
|
capabilities->LockSupported = FALSE;
|
|||
|
capabilities->EjectSupported = FALSE;
|
|||
|
PipCompleteDeviceControl (STATUS_SUCCESS, Context, DeviceInfo);
|
|||
|
}
|
|||
|
|