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

📄 devqueue.cpp

📁 一本在讲述USB驱动程式的书 及其范例原码
💻 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 + -