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

📄 readwrite.cpp

📁 Programming the Microsoft Windows Driver Model(2nd)
💻 CPP
字号:
// Read/Write request processors for polling driver

// Copyright (C) 1999 by Walter Oney

// All rights reserved



#include "stddcls.h"

#include "driver.h"



// See the comments near the end of the polling routine about the following includes.

// The project settings for this file need to include the WIN_ME include directory

// in the DDK because of them. Unfortunately, the WIN_ME headers were dropped from the

// .NET DDK during beta, so you need the win98 kit installed in order to compile this file.

// If you have the 2K or XP kits, change the project settings for this file so that the

// "Additional Include Directories" option of "C++/Preprocessor" specifies $(DDKPATH)\inc\win_me

// instead of $(98DDK)\inc\win98.

//

// Note that BUILD isn't sufficiently flexibile to get the right headers included in this

// circumstance. Consequently, I needed to hand-assemble the VxD calls so as to avoid these

// VxD includes. (I did this by generating an ASM listing in Visual Studio and seeing what

// the VxD service ordinals were.)



#ifdef _X86_

	#ifdef DDKBUILD

		#define VMMCall(service) \

			_asm _emit 0xcd \

			_asm _emit 0x20 \

			_asm _emit ((service) & 0xff) \

			_asm _emit ((service) >> 8) & 0xff \

			_asm _emit ((service) >> 16) & 0xff \

			_asm _emit ((service) >> 24) & 0xff \



		#define Get_Cur_VM_Handle 0x00010001

		#define Adjust_Thread_Exec_Priority 0x00010114

		#define HIGH_PRI_DEVICE_BOOST 0x00001000



	#else

		#include <basedef.h>

		#include <vmm.h>

	#endif // DDKBUILD

#endif // _X86_



VOID OnCancelReadWrite(PDEVICE_OBJECT fdo, PIRP Irp);

NTSTATUS StartPollingThread(PDEVICE_EXTENSION pdx);

VOID StopPollingThread(PDEVICE_EXTENSION pdx);



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



#pragma PAGEDCODE



NTSTATUS DispatchCleanup(PDEVICE_OBJECT fdo, PIRP Irp)

	{							// DispatchCleanup

	PAGED_CODE();

	KdPrint((DRIVERNAME " - IRP_MJ_CLEANUP\n"));

	PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;

	PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);

	CleanupRequests(&pdx->dqReadWrite, stack->FileObject, STATUS_CANCELLED);

	return CompleteRequest(Irp, STATUS_SUCCESS, 0);

	}							// DispatchCleanup



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



#pragma PAGEDCODE



NTSTATUS DispatchCreate(PDEVICE_OBJECT fdo, PIRP Irp)

	{							// DispatchCreate

	PAGED_CODE();

	KdPrint((DRIVERNAME " - IRP_MJ_CREATE\n"));

	PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;



	PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);



	NTSTATUS status = STATUS_SUCCESS;

	InterlockedIncrement(&pdx->handles);

	return CompleteRequest(Irp, status, 0);

	}							// DispatchCreate



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



#pragma PAGEDCODE



NTSTATUS DispatchClose(PDEVICE_OBJECT fdo, PIRP Irp)

	{							// DispatchClose

	PAGED_CODE();

	KdPrint((DRIVERNAME " - IRP_MJ_CLOSE\n"));

	PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;

	PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);

	InterlockedDecrement(&pdx->handles);

	return CompleteRequest(Irp, STATUS_SUCCESS, 0);

	}							// DispatchClose



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



#pragma PAGEDCODE



NTSTATUS DispatchReadWrite(PDEVICE_OBJECT fdo, PIRP Irp)

	{							// DispatchReadWrite

	PAGED_CODE();

	PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;



	IoMarkIrpPending(Irp);

	StartPacket(&pdx->dqReadWrite, fdo, Irp, OnCancelReadWrite);

	return STATUS_PENDING;

	}							// DispatchReadWrite



#pragma LOCKEDCODE



VOID OnCancelReadWrite(IN PDEVICE_OBJECT fdo, IN PIRP Irp)

	{							// OnCancelReadWrite

	PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;

	CancelRequest(&pdx->dqReadWrite, Irp);

	}							// OnCancelReadWrite



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



#pragma LOCKEDCODE				// because of raised IRQL later on



VOID PollingThreadRoutine(PDEVICE_EXTENSION pdx)

	{							// PollingThreadRoutine

	KdPrint(("POLLING - Starting polling thread\n"));

	NTSTATUS status;

	KTIMER timer;

	KeInitializeTimerEx(&timer, SynchronizationTimer);



	// Wait for a request to arrive at our StartIoRead routine or for

	// someone to kill this thread.



	PVOID mainevents[] = {

		(PVOID) &pdx->evKill,

		(PVOID) &pdx->evRequest,

		};



	PVOID pollevents[] = {

		(PVOID) &pdx->evKill,

		(PVOID) &timer,

		};



	C_ASSERT(arraysize(mainevents) <= THREAD_WAIT_OBJECTS);

	C_ASSERT(arraysize(pollevents) <= THREAD_WAIT_OBJECTS);



	BOOLEAN kill = FALSE;

	

	while (!kill)

		{						// until told to quit

		status = KeWaitForMultipleObjects(arraysize(mainevents),

			mainevents, WaitAny, Executive, KernelMode, FALSE, NULL, NULL);

		if (!NT_SUCCESS(status))

			{					// error in wait

			KdPrint(("POLLING - KeWaitForMultipleObjects failed - %X\n", status));

			break;

			}					// error in wait

		if (status == STATUS_WAIT_0)

			break;				// kill event was set



		ULONG numxfer = 0;



		// Starting the timer with a zero due time will cause us to perform the

		// first poll immediately. Thereafter, polls occur at the POLLING_INTERVAL

		// interval (measured in milliseconds).



		LARGE_INTEGER duetime = {0};



		#define POLLING_INTERVAL 500

		KeSetTimerEx(&timer, duetime, POLLING_INTERVAL, NULL);



		PIRP Irp = GetCurrentIrp(&pdx->dqReadWrite);



		while (TRUE)

			{					// read next byte



			// Check to see if we should continue processing this IRP



			if (!Irp)

				break;			// no current IRP (so why did evRequest get signalled??)

			

			if (Irp->Cancel)

				{				// someone wants to cancel this IRP

				status = STATUS_CANCELLED;

				break;

				}				// someone wants to cancel this IRP

			

			if ((status = AreRequestsBeingAborted(&pdx->dqReadWrite)))

				break;			// powering down or something



			// Block until time to poll again



			status = KeWaitForMultipleObjects(arraysize(pollevents),

				pollevents, WaitAny, Executive, KernelMode, FALSE, NULL, NULL);

			

			if (!NT_SUCCESS(status))

				{					// error in wait

				KdPrint(("POLLING - KeWaitForMultipleObjects failed - %X\n", status));

				kill = TRUE;

				break;				// from read next byte

				}					// error in wait

			

			if (status == STATUS_WAIT_0)

				{					// told to quit

				status = STATUS_DELETE_PENDING;

				kill = TRUE;

				break;				// from read next byte

				}					// told to quit



			// Poll the device. Read a data byte if one is ready.



			if (pdx->nbytes)

				{				// request not yet satisfied

				#define CTLPORT_DATA_READY 1

				if (READ_PORT_UCHAR(pdx->portbase) == CTLPORT_DATA_READY)

					{			// data byte is ready

					*pdx->buffer++ = READ_PORT_UCHAR(pdx->portbase + 1);

					--pdx->nbytes;

					++numxfer;

					}			// data byte is ready

				}				// request not yet satisfied



			if (!pdx->nbytes)

				break;			// request now satisfied, leave loop

			}					// read next byte



		KeCancelTimer(&timer);

		StartNextPacket(&pdx->dqReadWrite, pdx->DeviceObject);

		if (Irp)

			CompleteRequest(Irp, status, numxfer);

		}						// until told to quit



	KdPrint(("POLLING - Terminating polling thread\n"));



	// In win98/me, StopPollingThread will be waiting for us to set the evDead

	// event. Conceivably, it would preempt us when we set the event, leaving

	// us unterminated while the driver gets unloaded. This probably doesn't

	// cause any real grief. Just to make sure, though, bump our priority way

	// up so we keep control after that other thread unblocks.



#ifdef _X86_



	if (win98)

		{						// win98 exit



		// The following VxD code is only executed in Win98/Me and therefore

		// won't cause a bug check. However, having the code here means

		// we need to include header files from the WinMe part of the DDK.



		VMMCall(Get_Cur_VM_Handle)

		_asm mov eax, HIGH_PRI_DEVICE_BOOST

		VMMCall(Adjust_Thread_Exec_Priority)



		KeSetEvent(&pdx->evDead, IO_NO_INCREMENT, FALSE);	// for win98/me

		}						// win98 exit



#endif _X86_



	PsTerminateSystemThread(STATUS_SUCCESS);

	}							// PollingThreadRoutine



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



#pragma PAGEDCODE



NTSTATUS StartDevice(PDEVICE_OBJECT fdo, PCM_PARTIAL_RESOURCE_LIST raw, PCM_PARTIAL_RESOURCE_LIST translated)

	{							// StartDevice

	PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;

	NTSTATUS status;



	// Identify the I/O resources we're supposed to use.



	PHYSICAL_ADDRESS portbase;

	BOOLEAN gotport = FALSE;

	

	if (!translated)

		return STATUS_DEVICE_CONFIGURATION_ERROR;		// no resources assigned??



	PCM_PARTIAL_RESOURCE_DESCRIPTOR resource = translated->PartialDescriptors;

	ULONG nres = translated->Count;

	for (ULONG i = 0; i < nres; ++i, ++resource)

		{						// for each resource

		switch (resource->Type)

			{					// switch on resource type



		case CmResourceTypePort:

			portbase = resource->u.Port.Start;

			pdx->nports = resource->u.Port.Length;

			pdx->mappedport = (resource->Flags & CM_RESOURCE_PORT_IO) == 0;

			gotport = TRUE;

			break;



		default:

			KdPrint((DRIVERNAME " - Unexpected I/O resource type %d\n", resource->Type));

			break;

			}					// switch on resource type

		}						// for each resource



	if (!(TRUE

		&& gotport

		))

		{

		KdPrint((DRIVERNAME " - Didn't get expected I/O resources\n"));

		return STATUS_DEVICE_CONFIGURATION_ERROR;

		}



	if (pdx->mappedport)

		{						// map port address for RISC platform

		pdx->portbase = (PUCHAR) MmMapIoSpace(portbase, pdx->nports, MmNonCached);

		if (!pdx->portbase)

			{

			KdPrint((DRIVERNAME " - Unable to map port range %I64X, length %X\n", portbase, pdx->nports));

			return STATUS_INSUFFICIENT_RESOURCES;

			}

		}						// map port address for RISC platform

	else

		pdx->portbase = (PUCHAR) portbase.QuadPart;



	status = StartPollingThread(pdx);



	return status;

	}							// StartDevice



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



#pragma LOCKEDCODE



VOID StartIo(IN PDEVICE_OBJECT fdo, IN PIRP Irp)

	{							// StartIo

	PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;

	PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);



	pdx->buffer = (PUCHAR) Irp->AssociatedIrp.SystemBuffer;

	pdx->nbytes = stack->Parameters.Read.Length;



	KeSetEvent(&pdx->evRequest, 0, FALSE); // wake up polling thread

	}							// StartIo



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



#pragma PAGEDCODE



VOID StopDevice(IN PDEVICE_OBJECT fdo, BOOLEAN oktouch /* = FALSE */)

	{							// StopDevice

	PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;



	StopPollingThread(pdx);



	if (pdx->portbase && pdx->mappedport)

		MmUnmapIoSpace(pdx->portbase, pdx->nports);

	pdx->portbase = NULL;

	}							// StopDevice



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



#pragma PAGEDCODE



NTSTATUS StartPollingThread(PDEVICE_EXTENSION pdx)

	{							// StartPollingThread

	NTSTATUS status;

	HANDLE hthread;



	KeInitializeEvent(&pdx->evKill, NotificationEvent, FALSE);

	KeInitializeEvent(&pdx->evDead, NotificationEvent, FALSE);	// used in 98/me only

	KeInitializeEvent(&pdx->evRequest, SynchronizationEvent, FALSE);



#ifdef _X86_



	// Win98 fails PsCreateSystemThread with STATUS_INVALID_PARAMETER_3 if

	// an object attributes structure is used.



	if (win98)

		status = PsCreateSystemThread(&hthread, THREAD_ALL_ACCESS, NULL, NULL, NULL,

			(PKSTART_ROUTINE) PollingThreadRoutine, pdx);

	else

#endif // _X86_

		{						// NT



		// Make sure process handle is a kernel handle to prevent tampering by

		// user mode before we manage to get the KTHREAD pointer.



		OBJECT_ATTRIBUTES oa;

		InitializeObjectAttributes(&oa, NULL, OBJ_KERNEL_HANDLE, NULL, NULL);



		status = PsCreateSystemThread(&hthread, THREAD_ALL_ACCESS, &oa, NULL, NULL,

			(PKSTART_ROUTINE) PollingThreadRoutine, pdx);

		}						// NT



	if (!NT_SUCCESS(status))

		{

		KdPrint((DRIVERNAME " - PsCreateSystemThread failed - %X\n", status));

		return status;

		}



	status = ObReferenceObjectByHandle(hthread, THREAD_ALL_ACCESS, NULL,

		KernelMode, (PVOID*) &pdx->thread, NULL);



	ZwClose(hthread);



	return STATUS_SUCCESS;

	}							// StartPollingThread



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



#pragma PAGEDCODE



VOID StopPollingThread(PDEVICE_EXTENSION pdx)

	{							// StopPollingThread

	KeSetEvent(&pdx->evKill, 0, FALSE);

	if (pdx->thread)

		{						// wait for thread to die



	#ifdef _X86_



		if (win98)

			{					// wait for thread to die



			// In Win98/Me, wait for the thread routine to set the "dead" event to

			// indicate it's about to exit. (Waiting on the thread object will cause

			// a crash). The polling thread will raise its priority before setting

			// this event, which should allow it to exit without preemption by us.

			// Note also the workaround here for the "KeWaitForSingleObject sometimes returns

			// -1" bug in Win98/Me.



			while (KeWaitForSingleObject(&pdx->evDead, Executive, KernelMode, FALSE, NULL) == (NTSTATUS) -1)

				;

			}					// wait for thread to die



		else



	#endif // _X86_



			// In NT, wait on the thread object

	

			KeWaitForSingleObject(pdx->thread, Executive, KernelMode, FALSE, NULL);

		

		// The thread object was referenced in StartPollingThread, so dereference it now.

		

		ObDereferenceObject(pdx->thread);

		pdx->thread = NULL;

		}						// wait for thread to die

	}							// StopPollingThread



⌨️ 快捷键说明

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