447 lines
9.4 KiB
C
447 lines
9.4 KiB
C
|
#include "internal.h"
|
||
|
|
||
|
#pragma alloc_text(PAGE,InitializePacketQueue)
|
||
|
|
||
|
VOID
|
||
|
InitializePacketQueue(
|
||
|
PPACKET_QUEUE PacketQueue,
|
||
|
PVOID Context,
|
||
|
PACKET_STARTER StarterRoutine
|
||
|
)
|
||
|
|
||
|
{
|
||
|
|
||
|
RtlZeroMemory(PacketQueue,sizeof(*PacketQueue));
|
||
|
|
||
|
KeInitializeSpinLock(&PacketQueue->Lock);
|
||
|
|
||
|
PacketQueue->Context=Context;
|
||
|
|
||
|
PacketQueue->Starter=StarterRoutine;
|
||
|
|
||
|
PacketQueue->Active=TRUE;
|
||
|
|
||
|
KeInitializeEvent(&PacketQueue->InactiveEvent,NotificationEvent,FALSE);
|
||
|
|
||
|
InitializeListHead(&PacketQueue->ListHead);
|
||
|
|
||
|
return;
|
||
|
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
IrpQueueCancelRoutine(
|
||
|
PDEVICE_OBJECT DeviceObject,
|
||
|
PIRP Irp
|
||
|
)
|
||
|
|
||
|
{
|
||
|
PPACKET_QUEUE PacketQueue;
|
||
|
KIRQL OldIrql;
|
||
|
|
||
|
IoReleaseCancelSpinLock(Irp->CancelIrql);
|
||
|
|
||
|
PacketQueue=Irp->Tail.Overlay.DriverContext[0];
|
||
|
|
||
|
KeAcquireSpinLock(&PacketQueue->Lock,&OldIrql);
|
||
|
|
||
|
if (Irp->Tail.Overlay.ListEntry.Flink == NULL) {
|
||
|
//
|
||
|
// the irp has been removed from the queue
|
||
|
//
|
||
|
} else {
|
||
|
//
|
||
|
// the irp is still in the queue, remove it
|
||
|
//
|
||
|
RemoveEntryList(
|
||
|
&Irp->Tail.Overlay.ListEntry
|
||
|
);
|
||
|
}
|
||
|
|
||
|
KeReleaseSpinLock(&PacketQueue->Lock,OldIrql);
|
||
|
|
||
|
Irp->IoStatus.Status=STATUS_CANCELLED;
|
||
|
Irp->IoStatus.Information=0;
|
||
|
|
||
|
IoCompleteRequest(Irp,IO_NO_INCREMENT);
|
||
|
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
PIRP
|
||
|
GetUseableIrp(
|
||
|
PLIST_ENTRY List
|
||
|
)
|
||
|
|
||
|
{
|
||
|
PIRP Packet=NULL;
|
||
|
|
||
|
while ( (Packet == NULL) && !IsListEmpty(List)) {
|
||
|
//
|
||
|
// there is a packet queued
|
||
|
//
|
||
|
PLIST_ENTRY ListEntry;
|
||
|
|
||
|
ListEntry=RemoveTailList(List);
|
||
|
|
||
|
Packet=CONTAINING_RECORD(ListEntry,IRP,Tail.Overlay.ListEntry);
|
||
|
|
||
|
if (IoSetCancelRoutine(Packet,NULL) == NULL) {
|
||
|
//
|
||
|
// The cancel rountine has run and is waiting on the queue spinlock,
|
||
|
// set the flink to null so the cancel routine knows not to try
|
||
|
// take the irp off the list
|
||
|
//
|
||
|
Packet->Tail.Overlay.ListEntry.Flink=NULL;
|
||
|
Packet=NULL;
|
||
|
|
||
|
//
|
||
|
// try to get another one
|
||
|
//
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return Packet;
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
VOID
|
||
|
QueuePacket(
|
||
|
PPACKET_QUEUE PacketQueue,
|
||
|
PIRP Packet,
|
||
|
BOOLEAN InsertAtFront
|
||
|
)
|
||
|
|
||
|
{
|
||
|
|
||
|
NTSTATUS Status;
|
||
|
KIRQL OldIrql;
|
||
|
KIRQL CancelIrql;
|
||
|
BOOLEAN Canceled=FALSE;
|
||
|
|
||
|
KeAcquireSpinLock(&PacketQueue->Lock,&OldIrql);
|
||
|
|
||
|
if ((PacketQueue->CurrentPacket == NULL) && PacketQueue->Active && (IsListEmpty(&PacketQueue->ListHead))) {
|
||
|
//
|
||
|
// not currently handling a packet and the queue is active and there are not other packets
|
||
|
// queued, so handle it now
|
||
|
//
|
||
|
|
||
|
PacketQueue->CurrentPacket=Packet;
|
||
|
|
||
|
KeReleaseSpinLock(&PacketQueue->Lock,OldIrql);
|
||
|
|
||
|
(*PacketQueue->Starter)(
|
||
|
PacketQueue->Context,
|
||
|
Packet
|
||
|
);
|
||
|
|
||
|
return;
|
||
|
|
||
|
}
|
||
|
|
||
|
Packet->Tail.Overlay.DriverContext[0]=PacketQueue;
|
||
|
|
||
|
IoAcquireCancelSpinLock(&CancelIrql);
|
||
|
|
||
|
if (Packet->Cancel) {
|
||
|
//
|
||
|
// the irp has already been canceled
|
||
|
//
|
||
|
Canceled=TRUE;
|
||
|
|
||
|
} else {
|
||
|
|
||
|
IoSetCancelRoutine(
|
||
|
Packet,
|
||
|
IrpQueueCancelRoutine
|
||
|
);
|
||
|
}
|
||
|
|
||
|
IoReleaseCancelSpinLock(CancelIrql);
|
||
|
|
||
|
|
||
|
//
|
||
|
// need to queue the packet
|
||
|
//
|
||
|
|
||
|
if (!Canceled) {
|
||
|
|
||
|
if (InsertAtFront) {
|
||
|
//
|
||
|
// this one is high priorty for some reason, put it at the front
|
||
|
//
|
||
|
InsertTailList(&PacketQueue->ListHead,&Packet->Tail.Overlay.ListEntry);
|
||
|
|
||
|
} else {
|
||
|
|
||
|
InsertHeadList(&PacketQueue->ListHead,&Packet->Tail.Overlay.ListEntry);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
KeReleaseSpinLock(&PacketQueue->Lock,OldIrql);
|
||
|
|
||
|
if (Canceled) {
|
||
|
//
|
||
|
// complete the canceled irp now
|
||
|
//
|
||
|
Packet->IoStatus.Status=STATUS_CANCELLED;
|
||
|
Packet->IoStatus.Information=0;
|
||
|
|
||
|
IoCompleteRequest(Packet,IO_NO_INCREMENT);
|
||
|
}
|
||
|
|
||
|
|
||
|
return;
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
VOID
|
||
|
StartNextPacket(
|
||
|
PPACKET_QUEUE PacketQueue
|
||
|
)
|
||
|
|
||
|
{
|
||
|
KIRQL OldIrql;
|
||
|
|
||
|
KeAcquireSpinLock(&PacketQueue->Lock,&OldIrql);
|
||
|
|
||
|
ASSERT(PacketQueue->CurrentPacket != NULL);
|
||
|
|
||
|
//
|
||
|
// done with this one
|
||
|
//
|
||
|
PacketQueue->CurrentPacket=NULL;
|
||
|
|
||
|
if (!PacketQueue->InStartNext) {
|
||
|
//
|
||
|
// not already in this function
|
||
|
//
|
||
|
PacketQueue->InStartNext=TRUE;
|
||
|
|
||
|
while ((PacketQueue->CurrentPacket == NULL) && PacketQueue->Active ) {
|
||
|
//
|
||
|
// there isn't a current packet and the queue is active
|
||
|
//
|
||
|
PIRP Packet;
|
||
|
|
||
|
Packet=GetUseableIrp(&PacketQueue->ListHead);
|
||
|
|
||
|
if (Packet != NULL) {
|
||
|
//
|
||
|
// we got an irp to use
|
||
|
//
|
||
|
// now the current one
|
||
|
//
|
||
|
PacketQueue->CurrentPacket=Packet;
|
||
|
|
||
|
KeReleaseSpinLock(&PacketQueue->Lock,OldIrql);
|
||
|
|
||
|
//
|
||
|
// start the processing
|
||
|
//
|
||
|
(*PacketQueue->Starter)(
|
||
|
PacketQueue->Context,
|
||
|
Packet
|
||
|
);
|
||
|
|
||
|
KeAcquireSpinLock(&PacketQueue->Lock,&OldIrql);
|
||
|
|
||
|
} else {
|
||
|
//
|
||
|
// queue is empty, break out of loop
|
||
|
//
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
if (!PacketQueue->Active && (PacketQueue->CurrentPacket == NULL)) {
|
||
|
//
|
||
|
// the queue has been paused and we don't have a current packet, signal the event
|
||
|
//
|
||
|
KeSetEvent(
|
||
|
&PacketQueue->InactiveEvent,
|
||
|
IO_NO_INCREMENT,
|
||
|
FALSE
|
||
|
);
|
||
|
}
|
||
|
|
||
|
PacketQueue->InStartNext=FALSE;
|
||
|
}
|
||
|
|
||
|
KeReleaseSpinLock(&PacketQueue->Lock,OldIrql);
|
||
|
|
||
|
return;
|
||
|
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
PausePacketProcessing(
|
||
|
PPACKET_QUEUE PacketQueue,
|
||
|
BOOLEAN WaitForInactive
|
||
|
)
|
||
|
|
||
|
{
|
||
|
KIRQL OldIrql;
|
||
|
BOOLEAN CurrentlyActive=FALSE;
|
||
|
|
||
|
KeAcquireSpinLock(&PacketQueue->Lock,&OldIrql);
|
||
|
|
||
|
PacketQueue->Active=FALSE;
|
||
|
|
||
|
if (PacketQueue->CurrentPacket != NULL) {
|
||
|
//
|
||
|
// there is a packet currently being processed
|
||
|
//
|
||
|
CurrentlyActive=TRUE;
|
||
|
|
||
|
KeClearEvent(&PacketQueue->InactiveEvent);
|
||
|
|
||
|
}
|
||
|
|
||
|
KeReleaseSpinLock(&PacketQueue->Lock,OldIrql);
|
||
|
|
||
|
if (WaitForInactive && CurrentlyActive) {
|
||
|
//
|
||
|
// the caller wants use to wait for the queue to inactive, and it was active when
|
||
|
// theis was called
|
||
|
//
|
||
|
KeWaitForSingleObject(
|
||
|
&PacketQueue->InactiveEvent,
|
||
|
Executive,
|
||
|
KernelMode,
|
||
|
FALSE,
|
||
|
NULL
|
||
|
);
|
||
|
|
||
|
}
|
||
|
|
||
|
return;
|
||
|
|
||
|
}
|
||
|
|
||
|
VOID
|
||
|
ActivatePacketProcessing(
|
||
|
PPACKET_QUEUE PacketQueue
|
||
|
)
|
||
|
|
||
|
{
|
||
|
|
||
|
KIRQL OldIrql;
|
||
|
|
||
|
KeAcquireSpinLock(&PacketQueue->Lock,&OldIrql);
|
||
|
|
||
|
PacketQueue->Active=TRUE;
|
||
|
|
||
|
if ((PacketQueue->CurrentPacket == NULL)) {
|
||
|
//
|
||
|
// No packet is currently being used
|
||
|
//
|
||
|
PIRP Packet;
|
||
|
|
||
|
Packet=GetUseableIrp(&PacketQueue->ListHead);
|
||
|
|
||
|
if (Packet != NULL) {
|
||
|
//
|
||
|
// we got an irp to use
|
||
|
//
|
||
|
// now the current one
|
||
|
//
|
||
|
PacketQueue->CurrentPacket=Packet;
|
||
|
|
||
|
KeReleaseSpinLock(&PacketQueue->Lock,OldIrql);
|
||
|
|
||
|
//
|
||
|
// start the processing
|
||
|
//
|
||
|
(*PacketQueue->Starter)(
|
||
|
PacketQueue->Context,
|
||
|
Packet
|
||
|
);
|
||
|
|
||
|
KeAcquireSpinLock(&PacketQueue->Lock,&OldIrql);
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
KeReleaseSpinLock(&PacketQueue->Lock,OldIrql);
|
||
|
|
||
|
return;
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
VOID
|
||
|
FlushQueuedPackets(
|
||
|
PPACKET_QUEUE PacketQueue,
|
||
|
UCHAR MajorFunction
|
||
|
)
|
||
|
|
||
|
{
|
||
|
KIRQL OldIrql;
|
||
|
PIRP Packet;
|
||
|
LIST_ENTRY TempList;
|
||
|
|
||
|
InitializeListHead(&TempList);
|
||
|
|
||
|
//
|
||
|
// dispose of all of the queue packets, don't touch the current one though
|
||
|
//
|
||
|
KeAcquireSpinLock(&PacketQueue->Lock,&OldIrql);
|
||
|
|
||
|
Packet=GetUseableIrp(&PacketQueue->ListHead);
|
||
|
|
||
|
while (Packet != NULL) {
|
||
|
|
||
|
PIO_STACK_LOCATION IrpSp=IoGetCurrentIrpStackLocation(Packet);
|
||
|
|
||
|
if ((MajorFunction == 0xff) || (MajorFunction==IrpSp->MajorFunction)) {
|
||
|
//
|
||
|
// either the caller wants all of irps completed, or they just want
|
||
|
// this specific type. In any case this is going to get completed
|
||
|
//
|
||
|
KeReleaseSpinLock(&PacketQueue->Lock,OldIrql);
|
||
|
|
||
|
Packet->IoStatus.Status=STATUS_CANCELLED;
|
||
|
Packet->IoStatus.Information=0;
|
||
|
|
||
|
IoCompleteRequest(Packet,IO_NO_INCREMENT);
|
||
|
|
||
|
KeAcquireSpinLock(&PacketQueue->Lock,&OldIrql);
|
||
|
|
||
|
} else {
|
||
|
//
|
||
|
// this one does not need to be completed, put it on the temp list
|
||
|
//
|
||
|
InsertHeadList(&TempList,&Packet->Tail.Overlay.ListEntry);
|
||
|
|
||
|
}
|
||
|
|
||
|
Packet=GetUseableIrp(&PacketQueue->ListHead);
|
||
|
}
|
||
|
|
||
|
while (!IsListEmpty(&TempList)) {
|
||
|
//
|
||
|
// move all the irps on the temp queue back to the real queue
|
||
|
//
|
||
|
PLIST_ENTRY ListEntry;
|
||
|
|
||
|
ListEntry=RemoveTailList(&TempList);
|
||
|
|
||
|
InsertHeadList(&PacketQueue->ListHead,ListEntry);
|
||
|
}
|
||
|
|
||
|
KeReleaseSpinLock(&PacketQueue->Lock,OldIrql);
|
||
|
|
||
|
return;
|
||
|
}
|