⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 devqueue.cpp

📁 Programming the Microsoft Windows driver model.2nd 随书光盘。内有很多作者送的实用工具和随书源码。WDM编程
💻 CPP
📖 第 1 页 / 共 2 页
字号:
	KIRQL oldirql;

	KeAcquireSpinLock(&pdq->lock, &oldirql);



	if (pdq->notify)

		status = STATUS_INVALID_DEVICE_REQUEST;

	else

		{						// valid request

		InterlockedIncrement(&pdq->stallcount);

		if (pdq->CurrentIrp)

			{					// device is busy

			pdq->notify = notify;

			pdq->notifycontext = context;

			status = STATUS_PENDING;

			}					// device is busy

		else

			status = STATUS_SUCCESS; // device is idle

		}						// valid request



	KeReleaseSpinLock(&pdq->lock, oldirql);

	return status;

	}							// StallRequestsAndNotify



GENERICAPI NTSTATUS GENERIC_EXPORT StallAllRequestsAndNotify(PDEVQUEUE* q, ULONG nq, PQNOTIFYFUNC notify, PVOID context)

	{							// StallAllRequestsAndNotify

	NTSTATUS status;

	KIRQL oldirql;

	ULONG i;



	// Acquire all of the queue locks. We're not worried about a deadlock because

	// this is the only function that ever simultaneously locks more than one queue



	KeRaiseIrql(DISPATCH_LEVEL, &oldirql);

	for (i = 0; i < nq; ++i)

		KeAcquireSpinLockAtDpcLevel(&q[i]->lock);



	// Examine each of the queues in a manner similar to the single-queue version

	// of this function



	ULONG nbusy = 0;			// number of busy devices

	ULONG ibusy;				// index of last busy device



	for (i = 0; i < nq; ++i)

		{						// examine each queue

		PDEVQUEUE pdq = q[i];

		if (pdq->notify)

			break;

		else

			{					// notify not pending

			InterlockedIncrement(&pdq->stallcount);	// stall this queue

			if (pdq->CurrentIrp)

				++nbusy, ibusy = i;	// device busy with this queue

			}					// notify not pending

		}						// examine each queue



	// If we didn't finish the loop, we found a queue for which a notification is

	// already pending, which is an error. Unstall any queues that we just stalled

	// in order to backout from this function.



	if (i < nq)

		{						// backout from error

		for (--i; (int) i >= 0; --i)

			InterlockedDecrement(&q[i]->stallcount);

		status = STATUS_INVALID_DEVICE_REQUEST;	// indicate we have an error

		}						// backout from error



	// If none of the queues is busy, we can just return STATUS_SUCCESS



	else if (nbusy == 0)

		status = STATUS_SUCCESS;	// device not busy



	// If just one of the queues is busy, arrange for it call the notification

	// procedure once the current IRP finishes (whereupon somebody will call

	// StartNextPacket on this queue)



	else if (nbusy == 1)

		{							// one queue busy

		q[ibusy]->notify = notify;

		q[ibusy]->notifycontext = context;

		status = STATUS_PENDING;

		}							// one queue busy



	// More than one queue is currently busy. We need to arrange for each queue to

	// finish before calling the callback function.



	else

		{							// multiple queues busy

		PNOTIFY_CONTEXT ctx = (PNOTIFY_CONTEXT) ExAllocatePool(NonPagedPool, sizeof(NOTIFY_CONTEXT));

		if (!ctx)

			{						// can't allocate context block

			for (i = 0; i < nq; ++i)

				InterlockedDecrement(&q[i]->stallcount); // unstall the queues we stalled

			status = STATUS_INSUFFICIENT_RESOURCES;

			}						// can't allocate context block

		else

			{						// arrange for notifications

			ctx->context = context;

			ctx->notify = notify;

			ctx->count = nbusy;



			for (i = 0; i < nq; ++i)

				{					// for each queue

				PDEVQUEUE pdq = q[i];

				if (!pdq->CurrentIrp)

					continue;		// this queue not busy

				pdq->notify = (PQNOTIFYFUNC) NotificationCallback;

				pdq->notifycontext = (PVOID) ctx;

				}					// for each queue

			

			status = STATUS_PENDING;

			}						// arrange for notifications

		}							// mutliple queues busy



	// Release all the queue locks. [Note: there used to be a rookie mistake in

	// the following line of code -- "i" is unsigned, so the loop used to wrap

	// it around to FFFFFFFF and led to a crash. Reader and past student Mike

	// Rapp spent many hours isolating this problem.]



	for (i = nq - 1; (int) i >= 0; --i)

		KeReleaseSpinLockFromDpcLevel(&q[i]->lock);

	KeLowerIrql(oldirql);



	return status;

	}							// StallAllRequestsAndNotify



///////////////////////////////////////////////////////////////////////////////



GENERICAPI PIRP GENERIC_EXPORT StartNextPacket(PDEVQUEUE pdq, PDEVICE_OBJECT fdo)

	{							// StartNextPacket

	KIRQL oldirql;

	KeAcquireSpinLock(&pdq->lock, &oldirql);



	// Nullify the current IRP pointer after remembering the current one.

	// We'll return the current IRP pointer as our return value so that

	// a DPC routine has a way to know whether an active request got

	// aborted.



	PIRP CurrentIrp = (PIRP) InterlockedExchangePointer((PVOID*) &pdq->CurrentIrp, NULL);



	// If we just finished processing a request, set the event on which

	// WaitForCurrentIrp may be waiting in some other thread.



	if (CurrentIrp)

		KeSetEvent(&pdq->evStop, 0, FALSE);



	// If someone is waiting for notification that this IRP has finished,

	// we'll provide the notification after we release the spin lock. We shouldn't

	// find the queue unstalled if there is a notification routine in place, by

	// the way.



	PQNOTIFYFUNC notify = pdq->notify;

	PVOID notifycontext = pdq->notifycontext;

	pdq->notify = NULL;



	// Start the next IRP.



	while (!pdq->stallcount && !pdq->abortstatus && !IsListEmpty(&pdq->head))

		{						// start next packet

		PLIST_ENTRY next = RemoveHeadList(&pdq->head);

		PIRP Irp = CONTAINING_RECORD(next, IRP, Tail.Overlay.ListEntry);



		// (After Peretz & Hanrahan in part) Nullify the cancel pointer in this IRP. If it was

		// already NULL, someone is trying to cancel this IRP right now. Reinitialize

		// the link pointers so the cancel routine's call to RemoveEntryList won't

		// do anything harmful and look for another IRP. The cancel routine will

		// take over as soon as we release the spin lock



		if (!IoSetCancelRoutine(Irp, NULL))

			{					// IRP being cancelled right now

			ASSERT(Irp->Cancel);	// else CancelRoutine shouldn't be NULL!

			InitializeListHead(&Irp->Tail.Overlay.ListEntry);

			continue;			// with "start next packet"

			}					// IRP being cancelled right now



		pdq->CurrentIrp = Irp;

		KeReleaseSpinLockFromDpcLevel(&pdq->lock);

		(*pdq->StartIo)(fdo, Irp);

		KeLowerIrql(oldirql);

		return CurrentIrp;

		}						// start next packet



	KeReleaseSpinLock(&pdq->lock, oldirql);



	if (notify)

		(*notify)(notifycontext);



	return CurrentIrp;

	}							// StartNextPacket



///////////////////////////////////////////////////////////////////////////////



GENERICAPI VOID GENERIC_EXPORT StartPacket(PDEVQUEUE pdq, PDEVICE_OBJECT fdo, PIRP Irp, PDRIVER_CANCEL cancel)

	{							// StartPacket

	KIRQL oldirql;

	KeAcquireSpinLock(&pdq->lock, &oldirql);



	ASSERT(cancel);				// must supply a cancel routine

	ASSERT(Irp->CancelRoutine == NULL); // maybe left over from a higher level?



	// If the device has been removed by surprise, complete IRP immediately. Do not

	// pass GO. Do not collect $200.



	NTSTATUS abortstatus = pdq->abortstatus;

	if (abortstatus)

		{						// aborting all requests now

		KeReleaseSpinLock(&pdq->lock, oldirql);

		Irp->IoStatus.Status = abortstatus;

		IoCompleteRequest(Irp, IO_NO_INCREMENT);

		}						// aborting all requests now



	// If the device is busy with another request, or if the queue has

	// been stalled due to some PnP or power event, just put the new IRP

	// onto the queue and set a cancel routine pointer.



	else if (pdq->CurrentIrp || pdq->stallcount)

		{						// queue this irp



		// (After Peretz) See if this IRP was cancelled before it got to us. If so,

		// make sure either we or the cancel routine completes it



		IoSetCancelRoutine(Irp, cancel);

		if (Irp->Cancel && IoSetCancelRoutine(Irp, NULL))

			{					// IRP has already been cancelled

			KeReleaseSpinLock(&pdq->lock, oldirql);

			Irp->IoStatus.Status = STATUS_CANCELLED;

			IoCompleteRequest(Irp, IO_NO_INCREMENT);

			}					// IRP has already been cancelled

		else

			{					// queue IRP

			InsertTailList(&pdq->head, &Irp->Tail.Overlay.ListEntry);

			KeReleaseSpinLock(&pdq->lock, oldirql);

			}					// queue IRP

		}						// queue this irp



	// If the device is idle and not stalled, pass the IRP to the StartIo

	// routine associated with this queue



	else

		{						// start this irp

		pdq->CurrentIrp = Irp;

		KeReleaseSpinLock(&pdq->lock, DISPATCH_LEVEL);

		(*pdq->StartIo)(fdo, Irp);

		KeLowerIrql(oldirql);

		}						// start this irp

	}							// StartPacket



///////////////////////////////////////////////////////////////////////////////



GENERICAPI VOID GENERIC_EXPORT WaitForCurrentIrp(PDEVQUEUE pdq)

	{							// WaitForCurrentIrp



	// First reset the event that StartNextPacket sets each time.



	KeClearEvent(&pdq->evStop);



	// Under protection of our spin lock, check to see if there's a current IRP.

	// Since whoever called us should also have stalled requests, no-one can sneak

	// in after we release the spin lock and start a new request behind our back.



	ASSERT(pdq->stallcount != 0);	// should be stalled now!

	

	KIRQL oldirql;

	KeAcquireSpinLock(&pdq->lock, &oldirql);

	BOOLEAN mustwait = pdq->CurrentIrp != NULL;

	KeReleaseSpinLock(&pdq->lock, oldirql);



	if (mustwait)

		KeWaitForSingleObject(&pdq->evStop, Executive, KernelMode, FALSE, NULL);

	}							// WaitForCurrentIrp



GENERICAPI VOID GENERIC_EXPORT WaitForCurrentIrps(PDEVQUEUE* q, ULONG nq)

	{							// WaitForCurrentIrps

	for (ULONG i = 0; i < nq; ++i)

		WaitForCurrentIrp(q[i]);

	}							// WaitForCurrentIrps

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -