576 lines
15 KiB
C
576 lines
15 KiB
C
|
/*++
|
|||
|
|
|||
|
Copyright (c) 1989 Microsoft Corporation
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
devquobj.c
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
This module implements the kernel device queue object. Functions are
|
|||
|
provided to initialize a device queue object and to insert and remove
|
|||
|
device queue entries in a device queue object.
|
|||
|
|
|||
|
Author:
|
|||
|
|
|||
|
David N. Cutler (davec) 1-Apr-1989
|
|||
|
|
|||
|
Environment:
|
|||
|
|
|||
|
Kernel mode only.
|
|||
|
|
|||
|
Revision History:
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include "ki.h"
|
|||
|
|
|||
|
//
|
|||
|
// The following assert macro is used to check that an input device queue
|
|||
|
// is really a kdevice_queue and not something else, like deallocated pool.
|
|||
|
//
|
|||
|
|
|||
|
#define ASSERT_DEVICE_QUEUE(E) { \
|
|||
|
ASSERT((E)->Type == DeviceQueueObject); \
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
VOID
|
|||
|
KeInitializeDeviceQueue (
|
|||
|
IN PKDEVICE_QUEUE DeviceQueue
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function initializes a kernel device queue object.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceQueue - Supplies a pointer to a control object of type device
|
|||
|
queue.
|
|||
|
|
|||
|
SpinLock - Supplies a pointer to an executive spin lock.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
None.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
//
|
|||
|
// Initialize standard control object header.
|
|||
|
//
|
|||
|
|
|||
|
DeviceQueue->Type = DeviceQueueObject;
|
|||
|
DeviceQueue->Size = sizeof(KDEVICE_QUEUE);
|
|||
|
|
|||
|
//
|
|||
|
// Initialize the device queue list head, spin lock, and busy indicator.
|
|||
|
//
|
|||
|
|
|||
|
InitializeListHead(&DeviceQueue->DeviceListHead);
|
|||
|
KeInitializeSpinLock(&DeviceQueue->Lock);
|
|||
|
DeviceQueue->Busy = FALSE;
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
KeInsertDeviceQueue (
|
|||
|
IN PKDEVICE_QUEUE DeviceQueue,
|
|||
|
IN PKDEVICE_QUEUE_ENTRY DeviceQueueEntry
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function inserts a device queue entry at the tail of the specified
|
|||
|
device queue. If the device is not busy, then it is set busy and the entry
|
|||
|
is not placed in the device queue. Otherwise the specified entry is placed
|
|||
|
at the end of the device queue.
|
|||
|
|
|||
|
N.B. This function can only be called from DISPATCH_LEVEL.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceQueue - Supplies a pointer to a control object of type device queue.
|
|||
|
|
|||
|
DeviceQueueEntry - Supplies a pointer to a device queue entry.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
If the device is not busy, then a value of FALSE is returned. Otherwise a
|
|||
|
value of TRUE is returned.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
BOOLEAN Inserted;
|
|||
|
KLOCK_QUEUE_HANDLE LockHandle;
|
|||
|
|
|||
|
ASSERT_DEVICE_QUEUE(DeviceQueue);
|
|||
|
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
|
|||
|
|
|||
|
//
|
|||
|
// Lock specified device queue.
|
|||
|
//
|
|||
|
|
|||
|
KeAcquireInStackQueuedSpinLockAtDpcLevel(&DeviceQueue->Lock, &LockHandle);
|
|||
|
|
|||
|
//
|
|||
|
// Insert the specified device queue entry at the end of the device queue
|
|||
|
// if the device queue is busy. Otherwise set the device queue busy and
|
|||
|
// don't insert the device queue entry.
|
|||
|
//
|
|||
|
|
|||
|
if (DeviceQueue->Busy == TRUE) {
|
|||
|
Inserted = TRUE;
|
|||
|
InsertTailList(&DeviceQueue->DeviceListHead,
|
|||
|
&DeviceQueueEntry->DeviceListEntry);
|
|||
|
|
|||
|
} else {
|
|||
|
DeviceQueue->Busy = TRUE;
|
|||
|
Inserted = FALSE;
|
|||
|
}
|
|||
|
|
|||
|
DeviceQueueEntry->Inserted = Inserted;
|
|||
|
|
|||
|
//
|
|||
|
// Unlock specified device queue.
|
|||
|
//
|
|||
|
|
|||
|
KeReleaseInStackQueuedSpinLockFromDpcLevel(&LockHandle);
|
|||
|
return Inserted;
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
KeInsertByKeyDeviceQueue (
|
|||
|
IN PKDEVICE_QUEUE DeviceQueue,
|
|||
|
IN PKDEVICE_QUEUE_ENTRY DeviceQueueEntry,
|
|||
|
IN ULONG SortKey
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function inserts a device queue entry into the specified device
|
|||
|
queue according to a sort key. If the device is not busy, then it is
|
|||
|
set busy and the entry is not placed in the device queue. Otherwise
|
|||
|
the specified entry is placed in the device queue at a position such
|
|||
|
that the specified sort key is greater than or equal to its predecessor
|
|||
|
and less than its successor.
|
|||
|
|
|||
|
N.B. This function can only be called from DISPATCH_LEVEL.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceQueue - Supplies a pointer to a control object of type device queue.
|
|||
|
|
|||
|
DeviceQueueEntry - Supplies a pointer to a device queue entry.
|
|||
|
|
|||
|
SortKey - Supplies the sort key by which the position to insert the device
|
|||
|
queue entry is to be determined.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
If the device is not busy, then a value of FALSE is returned. Otherwise a
|
|||
|
value of TRUE is returned.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
BOOLEAN Inserted;
|
|||
|
KLOCK_QUEUE_HANDLE LockHandle;
|
|||
|
PLIST_ENTRY NextEntry;
|
|||
|
PKDEVICE_QUEUE_ENTRY QueueEntry;
|
|||
|
|
|||
|
ASSERT_DEVICE_QUEUE(DeviceQueue);
|
|||
|
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
|
|||
|
|
|||
|
//
|
|||
|
// Lock specified device queue.
|
|||
|
//
|
|||
|
|
|||
|
KeAcquireInStackQueuedSpinLockAtDpcLevel(&DeviceQueue->Lock, &LockHandle);
|
|||
|
|
|||
|
//
|
|||
|
// Insert the specified device queue entry in the device queue at the
|
|||
|
// position specified by the sort key if the device queue is busy.
|
|||
|
// Otherwise set the device queue busy an don't insert the device queue
|
|||
|
// entry.
|
|||
|
//
|
|||
|
|
|||
|
DeviceQueueEntry->SortKey = SortKey;
|
|||
|
if (DeviceQueue->Busy == TRUE) {
|
|||
|
Inserted = TRUE;
|
|||
|
NextEntry = DeviceQueue->DeviceListHead.Flink;
|
|||
|
while (NextEntry != &DeviceQueue->DeviceListHead) {
|
|||
|
QueueEntry = CONTAINING_RECORD(NextEntry,
|
|||
|
KDEVICE_QUEUE_ENTRY,
|
|||
|
DeviceListEntry);
|
|||
|
|
|||
|
if (SortKey < QueueEntry->SortKey) {
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
NextEntry = NextEntry->Flink;
|
|||
|
}
|
|||
|
|
|||
|
NextEntry = NextEntry->Blink;
|
|||
|
InsertHeadList(NextEntry, &DeviceQueueEntry->DeviceListEntry);
|
|||
|
|
|||
|
} else {
|
|||
|
DeviceQueue->Busy = TRUE;
|
|||
|
Inserted = FALSE;
|
|||
|
}
|
|||
|
|
|||
|
DeviceQueueEntry->Inserted = Inserted;
|
|||
|
|
|||
|
//
|
|||
|
// Unlock specified device queue.
|
|||
|
//
|
|||
|
|
|||
|
KeReleaseInStackQueuedSpinLockFromDpcLevel(&LockHandle);
|
|||
|
return Inserted;
|
|||
|
}
|
|||
|
|
|||
|
PKDEVICE_QUEUE_ENTRY
|
|||
|
KeRemoveDeviceQueue (
|
|||
|
IN PKDEVICE_QUEUE DeviceQueue
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function removes an entry from the head of the specified device
|
|||
|
queue. If the device queue is empty, then the device is set Not-Busy
|
|||
|
and a NULL pointer is returned. Otherwise the next entry is removed
|
|||
|
from the head of the device queue and the address of device queue entry
|
|||
|
is returned.
|
|||
|
|
|||
|
N.B. This function can only be called from DISPATCH_LEVEL.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceQueue - Supplies a pointer to a control object of type device queue.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
A NULL pointer is returned if the device queue is empty. Otherwise a
|
|||
|
pointer to a device queue entry is returned.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
PKDEVICE_QUEUE_ENTRY DeviceQueueEntry;
|
|||
|
KLOCK_QUEUE_HANDLE LockHandle;
|
|||
|
PLIST_ENTRY NextEntry;
|
|||
|
|
|||
|
ASSERT_DEVICE_QUEUE(DeviceQueue);
|
|||
|
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
|
|||
|
|
|||
|
//
|
|||
|
// Lock specified device queue.
|
|||
|
//
|
|||
|
|
|||
|
KeAcquireInStackQueuedSpinLockAtDpcLevel(&DeviceQueue->Lock, &LockHandle);
|
|||
|
|
|||
|
//
|
|||
|
// If the device queue is not empty, then remove the first entry from
|
|||
|
// the queue. Otherwise set the device queue not busy.
|
|||
|
//
|
|||
|
|
|||
|
ASSERT(DeviceQueue->Busy == TRUE);
|
|||
|
|
|||
|
if (IsListEmpty(&DeviceQueue->DeviceListHead) == TRUE) {
|
|||
|
DeviceQueue->Busy = FALSE;
|
|||
|
DeviceQueueEntry = NULL;
|
|||
|
|
|||
|
} else {
|
|||
|
NextEntry = RemoveHeadList(&DeviceQueue->DeviceListHead);
|
|||
|
DeviceQueueEntry = CONTAINING_RECORD(NextEntry,
|
|||
|
KDEVICE_QUEUE_ENTRY,
|
|||
|
DeviceListEntry);
|
|||
|
|
|||
|
DeviceQueueEntry->Inserted = FALSE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Unlock specified device queue and return address of device queue
|
|||
|
// entry.
|
|||
|
//
|
|||
|
|
|||
|
KeReleaseInStackQueuedSpinLockFromDpcLevel(&LockHandle);
|
|||
|
return DeviceQueueEntry;
|
|||
|
}
|
|||
|
|
|||
|
PKDEVICE_QUEUE_ENTRY
|
|||
|
KeRemoveByKeyDeviceQueue (
|
|||
|
IN PKDEVICE_QUEUE DeviceQueue,
|
|||
|
IN ULONG SortKey
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function removes an entry from the specified device
|
|||
|
queue. If the device queue is empty, then the device is set Not-Busy
|
|||
|
and a NULL pointer is returned. Otherwise the an entry is removed
|
|||
|
from the device queue and the address of device queue entry
|
|||
|
is returned. The queue is search for the first entry which has a value
|
|||
|
greater than or equal to the SortKey. If no such entry is found then the
|
|||
|
first entry of the queue is returned.
|
|||
|
|
|||
|
N.B. This function can only be called from DISPATCH_LEVEL.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceQueue - Supplies a pointer to a control object of type device queue.
|
|||
|
|
|||
|
SortKey - Supplies the sort key by which the position to remove the device
|
|||
|
queue entry is to be determined.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
A NULL pointer is returned if the device queue is empty. Otherwise a
|
|||
|
pointer to a device queue entry is returned.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
PKDEVICE_QUEUE_ENTRY DeviceQueueEntry;
|
|||
|
KLOCK_QUEUE_HANDLE LockHandle;
|
|||
|
PLIST_ENTRY NextEntry;
|
|||
|
|
|||
|
ASSERT_DEVICE_QUEUE(DeviceQueue);
|
|||
|
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
|
|||
|
|
|||
|
//
|
|||
|
// Lock specified device queue.
|
|||
|
//
|
|||
|
|
|||
|
KeAcquireInStackQueuedSpinLockAtDpcLevel(&DeviceQueue->Lock, &LockHandle);
|
|||
|
|
|||
|
//
|
|||
|
// If the device queue is not empty, then remove the first entry from
|
|||
|
// the queue. Otherwise set the device queue not busy.
|
|||
|
//
|
|||
|
|
|||
|
ASSERT(DeviceQueue->Busy == TRUE);
|
|||
|
|
|||
|
if (IsListEmpty(&DeviceQueue->DeviceListHead) == TRUE) {
|
|||
|
DeviceQueue->Busy = FALSE;
|
|||
|
DeviceQueueEntry = NULL;
|
|||
|
|
|||
|
} else {
|
|||
|
NextEntry = DeviceQueue->DeviceListHead.Flink;
|
|||
|
while (NextEntry != &DeviceQueue->DeviceListHead) {
|
|||
|
DeviceQueueEntry = CONTAINING_RECORD(NextEntry,
|
|||
|
KDEVICE_QUEUE_ENTRY,
|
|||
|
DeviceListEntry);
|
|||
|
|
|||
|
if (SortKey <= DeviceQueueEntry->SortKey) {
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
NextEntry = NextEntry->Flink;
|
|||
|
}
|
|||
|
|
|||
|
if (NextEntry != &DeviceQueue->DeviceListHead) {
|
|||
|
RemoveEntryList(&DeviceQueueEntry->DeviceListEntry);
|
|||
|
|
|||
|
} else {
|
|||
|
NextEntry = RemoveHeadList(&DeviceQueue->DeviceListHead);
|
|||
|
DeviceQueueEntry = CONTAINING_RECORD(NextEntry,
|
|||
|
KDEVICE_QUEUE_ENTRY,
|
|||
|
DeviceListEntry);
|
|||
|
}
|
|||
|
|
|||
|
DeviceQueueEntry->Inserted = FALSE;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Unlock specified device queue and return address of device queue
|
|||
|
// entry.
|
|||
|
//
|
|||
|
|
|||
|
KeReleaseInStackQueuedSpinLockFromDpcLevel(&LockHandle);
|
|||
|
return DeviceQueueEntry;
|
|||
|
}
|
|||
|
|
|||
|
PKDEVICE_QUEUE_ENTRY
|
|||
|
KeRemoveByKeyDeviceQueueIfBusy (
|
|||
|
IN PKDEVICE_QUEUE DeviceQueue,
|
|||
|
IN ULONG SortKey
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function removes an entry from the specified device queue if and
|
|||
|
only if the device is currently busy. If the device queue is empty or
|
|||
|
the device is not busy, then the device is set Not-Busy and a NULL is
|
|||
|
returned. Otherwise, an entry is removed from the device queue and the
|
|||
|
address of device queue entry is returned. The queue is search for the
|
|||
|
first entry which has a value greater than or equal to the SortKey. If
|
|||
|
no such entry is found then the first entry of the queue is returned.
|
|||
|
|
|||
|
N.B. This function can only be called from DISPATCH_LEVEL.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceQueue - Supplies a pointer to a control object of type device queue.
|
|||
|
|
|||
|
SortKey - Supplies the sort key by which the position to remove the device
|
|||
|
queue entry is to be determined.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
A NULL pointer is returned if the device queue is empty. Otherwise a
|
|||
|
pointer to a device queue entry is returned.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
PKDEVICE_QUEUE_ENTRY DeviceQueueEntry;
|
|||
|
KLOCK_QUEUE_HANDLE LockHandle;
|
|||
|
PLIST_ENTRY NextEntry;
|
|||
|
|
|||
|
ASSERT_DEVICE_QUEUE(DeviceQueue);
|
|||
|
ASSERT(KeGetCurrentIrql() == DISPATCH_LEVEL);
|
|||
|
|
|||
|
//
|
|||
|
// Lock specified device queue.
|
|||
|
//
|
|||
|
|
|||
|
KeAcquireInStackQueuedSpinLockAtDpcLevel(&DeviceQueue->Lock, &LockHandle);
|
|||
|
|
|||
|
//
|
|||
|
// If the device queue is busy, then attempt to remove an entry from
|
|||
|
// the queue using the sort key. Otherwise, set the device queue not
|
|||
|
// busy.
|
|||
|
//
|
|||
|
|
|||
|
if (DeviceQueue->Busy != FALSE) {
|
|||
|
if (IsListEmpty(&DeviceQueue->DeviceListHead) != FALSE) {
|
|||
|
DeviceQueue->Busy = FALSE;
|
|||
|
DeviceQueueEntry = NULL;
|
|||
|
|
|||
|
} else {
|
|||
|
NextEntry = DeviceQueue->DeviceListHead.Flink;
|
|||
|
while (NextEntry != &DeviceQueue->DeviceListHead) {
|
|||
|
DeviceQueueEntry = CONTAINING_RECORD(NextEntry,
|
|||
|
KDEVICE_QUEUE_ENTRY,
|
|||
|
DeviceListEntry);
|
|||
|
|
|||
|
if (SortKey <= DeviceQueueEntry->SortKey) {
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
NextEntry = NextEntry->Flink;
|
|||
|
}
|
|||
|
|
|||
|
if (NextEntry != &DeviceQueue->DeviceListHead) {
|
|||
|
RemoveEntryList(&DeviceQueueEntry->DeviceListEntry);
|
|||
|
|
|||
|
} else {
|
|||
|
NextEntry = RemoveHeadList(&DeviceQueue->DeviceListHead);
|
|||
|
DeviceQueueEntry = CONTAINING_RECORD(NextEntry,
|
|||
|
KDEVICE_QUEUE_ENTRY,
|
|||
|
DeviceListEntry);
|
|||
|
}
|
|||
|
|
|||
|
DeviceQueueEntry->Inserted = FALSE;
|
|||
|
}
|
|||
|
|
|||
|
} else {
|
|||
|
DeviceQueueEntry = NULL;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Unlock specified device queue and return address of device queue
|
|||
|
// entry.
|
|||
|
//
|
|||
|
|
|||
|
KeReleaseInStackQueuedSpinLockFromDpcLevel(&LockHandle);
|
|||
|
return DeviceQueueEntry;
|
|||
|
}
|
|||
|
|
|||
|
BOOLEAN
|
|||
|
KeRemoveEntryDeviceQueue (
|
|||
|
IN PKDEVICE_QUEUE DeviceQueue,
|
|||
|
IN PKDEVICE_QUEUE_ENTRY DeviceQueueEntry
|
|||
|
)
|
|||
|
|
|||
|
/*++
|
|||
|
|
|||
|
Routine Description:
|
|||
|
|
|||
|
This function removes a specified entry from the the specified device
|
|||
|
queue. If the device queue entry is not in the device queue, then no
|
|||
|
operation is performed. Otherwise the specified device queue entry is
|
|||
|
removed from the device queue and its inserted status is set to FALSE.
|
|||
|
|
|||
|
Arguments:
|
|||
|
|
|||
|
DeviceQueue - Supplies a pointer to a control object of type device queue.
|
|||
|
|
|||
|
DeviceQueueEntry - Supplies a pointer to a device queue entry which is to
|
|||
|
be removed from its device queue.
|
|||
|
|
|||
|
Return Value:
|
|||
|
|
|||
|
A value of TRUE is returned if the device queue entry is removed from its
|
|||
|
device queue. Otherwise a value of FALSE is returned.
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
{
|
|||
|
|
|||
|
KLOCK_QUEUE_HANDLE LockHandle;
|
|||
|
BOOLEAN Removed;
|
|||
|
|
|||
|
ASSERT_DEVICE_QUEUE(DeviceQueue);
|
|||
|
ASSERT(KeGetCurrentIrql() <= DISPATCH_LEVEL);
|
|||
|
|
|||
|
//
|
|||
|
// Raise IRQL to dispatcher level and lock specified device queue.
|
|||
|
//
|
|||
|
|
|||
|
KeAcquireInStackQueuedSpinLock(&DeviceQueue->Lock, &LockHandle);
|
|||
|
|
|||
|
//
|
|||
|
// If the device queue entry is not in a device queue, then no operation
|
|||
|
// is performed. Otherwise remove the specified device queue entry from its
|
|||
|
// device queue.
|
|||
|
//
|
|||
|
|
|||
|
Removed = DeviceQueueEntry->Inserted;
|
|||
|
if (Removed == TRUE) {
|
|||
|
DeviceQueueEntry->Inserted = FALSE;
|
|||
|
RemoveEntryList(&DeviceQueueEntry->DeviceListEntry);
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Unlock specified device queue, lower IRQL to its previous level, and
|
|||
|
// return whether the device queue entry was removed from its queue.
|
|||
|
//
|
|||
|
|
|||
|
KeReleaseInStackQueuedSpinLock(&LockHandle);
|
|||
|
return Removed;
|
|||
|
}
|