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

📄 readwrite.cpp

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

// Copyright (C) 1999 by Walter Oney

// All rights reserved



#include "stddcls.h"

#include "driver.h"



#if DBG

	#define MSGUSBSTRING(d,s,i) { \

		UNICODE_STRING sd; \

		if (i && NT_SUCCESS(GetStringDescriptor(d,i,&sd))) { \

			DbgPrint(s, sd.Buffer); \

			RtlFreeUnicodeString(&sd); \

		}}

#else

	#define MSGUSBSTRING(d,i,s)

#endif



typedef struct _RWCONTEXT {

	LIST_ENTRY list;			// list for queuing context structures

	PDEVICE_EXTENSION pdx;		// our device extension address

	PIRP mainirp;				// the main r/w IRP

	NTSTATUS status;			// ending status for main IRP

	ULONG numxfer;				// total number of bytes transferred

	ULONG numirps;				// total number of sub-irps

	LONG numpending;			// number of sub-irps not yet finished

	LONG refcnt;				// reference count (initially 2)

	struct {

		PIRP irp;				// subsidiary IRP

		PURB urb;				// URB packaged with sub-IRP

		PMDL mdl;				// partial MDL for the URB

		} sub[1];

	} RWCONTEXT, *PRWCONTEXT;



BOOLEAN DestroyContextStructure(PRWCONTEXT ctx);

ULONG GetCurrentFrame(PDEVICE_EXTENSION pdx);

VOID OnCancelReadWrite(PDEVICE_OBJECT fdo, PIRP Irp);

NTSTATUS OnReadWriteComplete(PDEVICE_OBJECT fdo, PIRP Irp, PRWCONTEXT context);

NTSTATUS OnStageComplete(PDEVICE_OBJECT fdo, PIRP Irp, PRWCONTEXT context);



NTSTATUS SelectAlternateInterface(PDEVICE_OBJECT fdo);

NTSTATUS SelectDefaultInterface(PDEVICE_OBJECT fdo);



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



#pragma LOCKEDCODE



BOOLEAN DestroyContextStructure(PRWCONTEXT ctx)

	{							// DestroyContextStructure



	// Decrement the reference count. If it goes to zero, delete

	// all the subsidiary IRPs and the context structure itself.



	if (InterlockedDecrement(&ctx->refcnt) > 0)

		return FALSE;					// still potentially in use

	for (ULONG i = 0; i < ctx->numirps; ++i)

		if (ctx->sub[i].irp)

			IoFreeIrp(ctx->sub[i].irp);

	ExFreePool(ctx);

	return TRUE;

	}							// DestroyContextStructure



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



#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;

	

	if (InterlockedIncrement(&pdx->handles) == 1)

		{					// no handles previously open

		status = SelectAlternateInterface(fdo);

		if (!NT_SUCCESS(status))

			{				// error selecting alternate interface

			InterlockedDecrement(&pdx->handles);

			IoReleaseRemoveLock(&pdx->RemoveLock, stack->FileObject);

			}				// error selecting alternate interface

		}					// no handles previously open



	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);

	if (InterlockedDecrement(&pdx->handles) == 0)

		SelectDefaultInterface(fdo);

	return CompleteRequest(Irp, STATUS_SUCCESS, 0);

	}							// DispatchClose



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



#pragma PAGEDCODE



NTSTATUS DispatchRead(PDEVICE_OBJECT fdo, PIRP Irp)

	{							// DispatchRead

	PAGED_CODE();

	PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;



	ASSERT(pdx->hinpipe);



	NTSTATUS status = IoAcquireRemoveLock(&pdx->RemoveLock, Irp);

	if (!NT_SUCCESS(status))

		return CompleteRequest(Irp, status, 0);



	ULONG length = Irp->MdlAddress ?  MmGetMdlByteCount(Irp->MdlAddress) : 0;

	if (!length)

		{						// zero-length read

		IoReleaseRemoveLock(&pdx->RemoveLock, Irp);

		return CompleteRequest(Irp, STATUS_SUCCESS, 0);

		}						// zero-length read



	// Calculate the number of IRPs that we need to submit in order to fulfill the

	// request. One of the ingredients of the calculation is the "packet size" the

	// endpoint uses during each frame. We have to know this number a priori, because

	// it doesn't have a fixed relationship to the endpoint's transfer size. Another

	// ingredient of the calculation is the fact that one URB can accomodate only 255

	// packets. The last ingredient is the maximum transfer size we declared when we

	// first configured the pipe. This driver left the default value in place. Given

	// the value of 16 used here for the packet size, we'll end up with a "segsize" of

	// 4080 bytes in fact.



	ULONG packsize = 16;		// known because we write the firmware

	ULONG segsize = USBD_DEFAULT_MAXIMUM_TRANSFER_SIZE; // default we left in place

	if (segsize / packsize > 255)

		segsize = 255 * packsize;	// maximum based on # packets allowed

	ULONG numirps = (length + segsize - 1) / segsize;



	// Allocate a context structure for use in the main IRP's completion and cancel routines.

	// Build as many IRPs (and URBs) as are required to fulfill the request.



	ULONG ctxsize = sizeof(RWCONTEXT) + (numirps - 1) * sizeof(((PRWCONTEXT) 0)->sub);

	PRWCONTEXT ctx = (PRWCONTEXT) ExAllocatePool(NonPagedPool, ctxsize);

	if (!ctx)

		{

		KdPrint((DRIVERNAME	" - unable to allocate %d bytes for record keeping\n", ctxsize));

		IoReleaseRemoveLock(&pdx->RemoveLock, Irp);

		return CompleteRequest(Irp, STATUS_INSUFFICIENT_RESOURCES, 0);

		}

	RtlZeroMemory(ctx, ctxsize);



	ctx->numirps = ctx->numpending = numirps;

	ctx->pdx = pdx;

	ctx->mainirp = Irp;

	ctx->refcnt = 2;			// one for OnReadWriteComplete, one forOnCancelReadWrite

	Irp->Tail.Overlay.DriverContext[0] = (PVOID) ctx; // for cancel routine



	ULONG frame = GetCurrentFrame(pdx) + 2;		// start first URB 2 frames from now

	KdPrint((DRIVERNAME " - First transfer scheduled for frame %8.8lX\n", frame));



	ULONG i;

	CCHAR stacksize = pdx->LowerDeviceObject->StackSize + 1; // 1 extra for OnStageComplete

	PUCHAR va = (PUCHAR) MmGetMdlVirtualAddress(Irp->MdlAddress);



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

		{						// for each IRP



		// Create a subsidiary IRP



		PIRP subirp = IoAllocateIrp(stacksize, FALSE);

		if (!subirp)

			break;				// can't create subsidiary IRP

		ctx->sub[i].irp = subirp;



		// Determine how many packets are required and allocate an URB big enough for all of them



		if (segsize > length)

			segsize = length;	// last transfer is short



		ULONG npackets = (segsize + packsize - 1) / packsize; // # packets needed in this stage

		ASSERT(npackets <= 255);	// else we didn't calculate segsize correctly

		ULONG size = GET_ISO_URB_SIZE(npackets); // size of URB needed for that many packets

		PURB urb = (PURB) ExAllocatePool(NonPagedPool, size);

		if (!urb)

			break;



		ctx->sub[i].urb = urb;



		// Create a partial MDL to map this segment. This is necessary because the offset fields in

		// the packet descriptors we'll build must begin at zero for each URB. (It would have been better

		// if they were just relative to whatever MDL in in the URB.)



		PMDL mdl = IoAllocateMdl((PVOID) va, segsize, FALSE, FALSE, NULL);

		if (!mdl)

			break;				// can't allocate memory for MDL

		IoBuildPartialMdl(Irp->MdlAddress, mdl, (PVOID) va, segsize);

		ctx->sub[i].mdl = mdl;

		va += segsize;			// for next iteration



		// Initialize the URB. Use a specific frame number rather than specifying the ASAP

		// flag. Much to my surprise, the bus driver will schedule a second ASAP transfer immediately

		// instead of waiting for current transfers to finish. This is contrary to what the DDK

		// doc says and is, I believe, a bug in XP.



		RtlZeroMemory(urb, size);

		urb->UrbIsochronousTransfer.Hdr.Length = (USHORT) size;

		urb->UrbIsochronousTransfer.Hdr.Function = URB_FUNCTION_ISOCH_TRANSFER;

		urb->UrbIsochronousTransfer.PipeHandle = pdx->hinpipe;

		urb->UrbIsochronousTransfer.TransferFlags = USBD_TRANSFER_DIRECTION_IN | USBD_SHORT_TRANSFER_OK;

		urb->UrbIsochronousTransfer.TransferBufferLength = segsize;

		urb->UrbIsochronousTransfer.TransferBufferMDL = mdl;

		urb->UrbIsochronousTransfer.NumberOfPackets = npackets;

		urb->UrbIsochronousTransfer.StartFrame = frame;



		// Initialize the buffer offset in each packet. The host controller will fill in the packet

		// length in one of two ways. For an input operation, it gets set to whatever the device

		// supplies. For an output operation, the host controller driver sets it based on the buffer

		// offsets.



		ULONG offset = 0;			// packet offset within subsidiary MDL



		for (ULONG j = 0; j < npackets; ++j)

			{						// for each packet

			urb->UrbIsochronousTransfer.IsoPacket[j].Offset = offset;

			ULONG packlen = length > packsize ? packsize : length;

			urb->UrbIsochronousTransfer.IsoPacket[j].Length = 0;



			length -= packlen;

			offset += packlen;

			}						// for each packet



		frame += npackets;			// frame for next URB



		// Initialize the subsidiary IRP. The first stack location is for use by OnStageComplete,

		// which needs several pieces of information it's inconvenient to pass in a single

		// context pointer. The second stack location will describe an internal IOCTL for USBD.



		IoSetNextIrpStackLocation(subirp);

		PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(subirp);

		stack->DeviceObject = fdo;

		stack->Parameters.Others.Argument1 = (PVOID) urb;

		stack->Parameters.Others.Argument2 = (PVOID) mdl;



		stack = IoGetNextIrpStackLocation(subirp);

		stack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;

		stack->Parameters.Others.Argument1 = (PVOID) urb;

		stack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB;

		

		IoSetCompletionRoutine(subirp, (PIO_COMPLETION_ROUTINE) OnStageComplete,

			(PVOID) ctx, TRUE, TRUE, TRUE);

		}						// for each IRP



	// If the IRP/URB construction loop finished early, there must have been a memory allocation

	// error.



	if (i < numirps)

		status = STATUS_INSUFFICIENT_RESOURCES;



	// See if the main IRP has already been cancelled.



	else

		{						// check for cancellation

		IoSetCancelRoutine(Irp, OnCancelReadWrite);

		if (Irp->Cancel)

			{

			status = STATUS_CANCELLED;



			// The main IRP has been cancelled. If the cancellation just happened and our

			// cancel routine was called, the following call to IoSetCancelRoutine will

			// return NULL. In this case, both our cleanup code (see below) and the cancel

			// routine will be calling DestroyContextStructure. One or the other of those

			// calls will finish the cleanup of the structure. Otherwise, it will no longer

			// be possible for our cancel routine to get called and we should get rid of

			// its now-unnecessary claim on the structure.



			if (IoSetCancelRoutine(Irp, NULL))

				--ctx->refcnt;

			}

		else

			status = STATUS_SUCCESS;

		}						// check for cancellation



	// Install a completion routine for the main IRP. It will normally be called when

	// the last stage IRP finishes. It might get called pretty soon, though, if we

	// fail the IRP before submitting the stage IRPs.



	IoSetCompletionRoutine(Irp, (PIO_COMPLETION_ROUTINE) OnReadWriteComplete,

		(PVOID) ctx, TRUE, TRUE, TRUE);

	IoMarkIrpPending(Irp);

	IoSetNextIrpStackLocation(Irp);	// so our completion routine will get called



	// If we need to fail the main IRP, cleanup and fail it now.



	if (!NT_SUCCESS(status))

		{						// abort now

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

			{					// release memory we were able to allocate

			if (ctx->sub[i].urb)

				ExFreePool(ctx->sub[i].urb);

			if (ctx->sub[i].mdl)

				IoFreeMdl(ctx->sub[i].mdl);

			}					// release memory we were able to allocate

		CompleteRequest(Irp, status, 0); // will trigger completion routine

		return STATUS_PENDING;

		}						// abort now



	ASSERT(length == 0);		// IRP construction loop didn't work right if not true



	// Install a completion routine for the main IRP. Once this is in place, submit the

	// subsidiary IRPs. Note that we can't touch the main IRP pointer after we submit the

	// subsidiary IRPs because the sub-IRP completion routine (OnStageComplete) may complete it.



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

		IoCallDriver(pdx->LowerDeviceObject, ctx->sub[i].irp);



	return STATUS_PENDING;

	}							// DispatchRead



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

// GetCurrentFrame obtains the current USB frame number



#pragma PAGEDCODE



ULONG GetCurrentFrame(PDEVICE_EXTENSION pdx)

	{							// GetCurrentFrame

	URB urb;

	urb.UrbGetCurrentFrameNumber.Hdr.Length = sizeof(struct _URB_GET_CURRENT_FRAME_NUMBER);

	urb.UrbGetCurrentFrameNumber.Hdr.Function = URB_FUNCTION_GET_CURRENT_FRAME_NUMBER;



	NTSTATUS status = SendAwaitUrb(pdx->DeviceObject, &urb);

	if (!NT_SUCCESS(status))

		{

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

		return 0;

		}



	return urb.UrbGetCurrentFrameNumber.FrameNumber;

	}							// GetCurrentFrame



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



#pragma PAGEDCODE



NTSTATUS GetStringDescriptor(PDEVICE_OBJECT fdo, UCHAR istring, PUNICODE_STRING s)

	{							// GetStringDescriptor

	NTSTATUS status;

	PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;

	URB urb;



	UCHAR data[256];			// maximum-length buffer



	// If this is the first time here, read string descriptor zero and arbitrarily select

	// the first language identifer as the one to use in subsequent get-descriptor calls.



	if (!pdx->langid)

		{						// determine default language id

		UsbBuildGetDescriptorRequest(&urb, sizeof(_URB_CONTROL_DESCRIPTOR_REQUEST), USB_STRING_DESCRIPTOR_TYPE,

			0, 0, data, NULL, sizeof(data), NULL);

		status = SendAwaitUrb(fdo, &urb);

		if (!NT_SUCCESS(status))

			return status;

		pdx->langid = *(LANGID*)(data + 2);

		}						// determine default language id



	// Fetch the designated string descriptor.



	UsbBuildGetDescriptorRequest(&urb, sizeof(_URB_CONTROL_DESCRIPTOR_REQUEST), USB_STRING_DESCRIPTOR_TYPE,

		istring, pdx->langid, data, NULL, sizeof(data), NULL);

	status = SendAwaitUrb(fdo, &urb);

	if (!NT_SUCCESS(status))

		return status;



	ULONG nchars = (data[0] - sizeof(WCHAR)) / sizeof(WCHAR);

	if (nchars > 127)

		nchars = 127;

	PWSTR p = (PWSTR) ExAllocatePool(PagedPool, (nchars + 1) * sizeof(WCHAR));

	if (!p)

		return STATUS_INSUFFICIENT_RESOURCES;



	memcpy(p, data + 2, nchars * sizeof(WCHAR));

	p[nchars] = 0;



	s->Length = (USHORT) (sizeof(WCHAR) * nchars);

	s->MaximumLength = (USHORT) ((sizeof(WCHAR) * nchars) + sizeof(WCHAR));

	s->Buffer = p;



	return STATUS_SUCCESS;

	}							// GetStringDescriptor



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



#pragma LOCKEDCODE



VOID OnCancelReadWrite(PDEVICE_OBJECT fdo, PIRP Irp)

	{							// OnCancelReadWrite

	

	IoReleaseCancelSpinLock(Irp->CancelIrql);



	// Attempt to cancel all the subsidiary IRPs for this IRP. The main IRP cannot

	// have progressed in the cancellation process past our own completion routine,

⌨️ 快捷键说明

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