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

📄 readwrite.cpp

📁 一本在讲述USB驱动程式的书 及其范例原码
💻 CPP
📖 第 1 页 / 共 2 页
字号:
// Read/Write request processors for loopback 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

struct _RWCONTEXT : public _URB
	{							// struct _RWCONTEXT
	ULONG_PTR va;				// virtual address for next segment of transfer
	ULONG length;				// length remaining to transfer
	PMDL mdl;					// partial MDL
	ULONG numxfer;				// cumulate transfer count
	BOOLEAN multistage;			// TRUE if multistage transfer

	// The following fields are used for error recovery only. Making this
	// structure bigger to contain them simply saves extra complication in the
	// code when we decide we need to recover the error

	PIO_WORKITEM rcitem;		// work item created for recovery
	PIRP Irp;					// the main IRP that we're going to fail
	};							// struct _RWCONTEXT

typedef struct _RWCONTEXT RWCONTEXT, *PRWCONTEXT;

ULONG GetStatus(PDEVICE_OBJECT fdo);
NTSTATUS OnReadWriteComplete(PDEVICE_OBJECT fdo, PIRP Irp, PRWCONTEXT ctx);
VOID OnCancelReadWrite(IN PDEVICE_OBJECT fdo, IN PIRP Irp);
VOID RecoverFromError(PDEVICE_OBJECT fdo, PRWCONTEXT ctx);

///////////////////////////////////////////////////////////////////////////////
// AbortPipe is called as part of an attempt to recover after an I/O error to
// abort pending requests for a given pipe.

#pragma PAGEDCODE

VOID AbortPipe(PDEVICE_OBJECT fdo, USBD_PIPE_HANDLE hpipe)
	{							// AbortPipe
	PAGED_CODE();
	PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;
	URB urb;

	urb.UrbHeader.Length = (USHORT) sizeof(_URB_PIPE_REQUEST);
	urb.UrbHeader.Function = URB_FUNCTION_ABORT_PIPE;
	urb.UrbPipeRequest.PipeHandle = hpipe;

	NTSTATUS status = SendAwaitUrb(fdo, &urb);
	if (!NT_SUCCESS(status))
		KdPrint((DRIVERNAME " - Error %X in AbortPipe\n", status));
	}							// AbortPipe

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

#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

///////////////////////////////////////////////////////////////////////////////
// I (WCO) revised DispatchReadWrite in SP-7 to use a queue. The
// code that used to be in this routine was moved to StartIo. A queue is
// needed to avoid mixing segments from different IRPs in the calls
// down to the bus driver.

#pragma PAGEDCODE

NTSTATUS DispatchReadWrite(PDEVICE_OBJECT fdo, PIRP Irp)
	{							// DispatchReadWrite
	PAGED_CODE();
	PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;

	// This device can only handle up to 4096 bytes in one transfer

	PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
	if (stack->Parameters.Read.Length > 4096)
		{
		KdPrint((DRIVERNAME " - Failing read/write bigger than 4096 bytes\n"));
		return CompleteRequest(Irp, STATUS_INVALID_PARAMETER, 0);
		}

	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

///////////////////////////////////////////////////////////////////////////////
// GetStatus is called during an attempt to recover after an error to determine
// the USBD status for the device.

#pragma PAGEDCODE

ULONG GetStatus(PDEVICE_OBJECT fdo)
	{							// GetStatus
	PAGED_CODE();
	PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;

	KEVENT event;
	KeInitializeEvent(&event, NotificationEvent, FALSE);
	IO_STATUS_BLOCK iostatus;
	ULONG portstatus;

	PIRP Irp = IoBuildDeviceIoControlRequest(IOCTL_INTERNAL_USB_GET_PORT_STATUS,
		pdx->LowerDeviceObject, NULL, 0, NULL, 0, TRUE, &event, &iostatus);
	if (!Irp)
		return 0;
	IoGetNextIrpStackLocation(Irp)->Parameters.Others.Argument1 = (PVOID) &portstatus;

	NTSTATUS status = IoCallDriver(pdx->LowerDeviceObject, Irp);
	if (status == STATUS_PENDING)
		{
		KeWaitForSingleObject(&event, Executive, KernelMode, FALSE, NULL);
		status = iostatus.Status;
		}
	
	if (!NT_SUCCESS(status))
		{
		KdPrint((DRIVERNAME " - Error %X trying to reset device\n", status));
		return 0;
		}

	return portstatus;
	}							// GetStatus

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

#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

///////////////////////////////////////////////////////////////////////////////
// This is the completion routine for IRP_MJ_READ and IRP_MJ_WRITE requests.

#pragma LOCKEDCODE

NTSTATUS OnReadWriteComplete(PDEVICE_OBJECT fdo, PIRP Irp, PRWCONTEXT ctx)
	{							// OnReadWriteComplete
	PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;
	BOOLEAN read = (ctx->UrbBulkOrInterruptTransfer.TransferFlags & USBD_TRANSFER_DIRECTION_IN) != 0;
	ctx->numxfer += ctx->UrbBulkOrInterruptTransfer.TransferBufferLength;

#if DBG
	USBD_STATUS urbstatus = URB_STATUS(ctx);
	if (!USBD_SUCCESS(urbstatus))
		KdPrint((DRIVERNAME " - %s failed with USBD status %X\n", read ? "Read" : "Write", urbstatus));
#endif

	// If this stage completed without error, resubmit the URB to perform the
	// next stage

	NTSTATUS status = Irp->IoStatus.Status;
	if (NT_SUCCESS(status) && ctx->length && !Irp->Cancel)
		{						// start next stage

		// Calculate length of next stage of the transfer

		ULONG seglen = ctx->length;
		if (seglen > pdx->maxtransfer)
			seglen = pdx->maxtransfer;

		// We're now in arbitrary thread context, so the virtual address of the
		// user buffer is currently meaningless. IoBuildPartialMdl copies physical
		// page numbers from the original IRP's probed-and-locked MDL, however,
		// so our process context doesn't matter.

		PMDL mdl = ctx->mdl;
		MmPrepareMdlForReuse(mdl);
		IoBuildPartialMdl(Irp->MdlAddress, mdl, (PVOID) ctx->va, seglen);

		// See the comment about this in DispatchReadWrite...

		if (!GenericGetSystemAddressForMdl(mdl))
			{						// can't map transfer segment
			KdPrint((DRIVERNAME " - Can't map memory for read or write\n"));
			status = STATUS_INSUFFICIENT_RESOURCES;
			Irp->IoStatus.Status = STATUS_INSUFFICIENT_RESOURCES;	// okay to change because DispatchReadWrite returned STATUS_PENDING
			goto FinishCompletion;
			}						// can't map transfer segment

		// Reinitialize the URB

		ctx->UrbBulkOrInterruptTransfer.TransferBufferLength = seglen;

		// The "next" stack location is the one belonging to the driver
		// underneath us. It's been mostly set to zero and must be reinitialized.

		PIO_STACK_LOCATION stack = IoGetNextIrpStackLocation(Irp);
		stack->MajorFunction = IRP_MJ_INTERNAL_DEVICE_CONTROL;
		stack->Parameters.Others.Argument1 = (PVOID) (PURB) ctx;
		stack->Parameters.DeviceIoControl.IoControlCode = IOCTL_INTERNAL_USB_SUBMIT_URB;
		IoSetCompletionRoutine(Irp, (PIO_COMPLETION_ROUTINE) OnReadWriteComplete,
			(PVOID) ctx, TRUE, TRUE, TRUE);

		ctx->va += seglen;
		ctx->length -= seglen;

		// Pass the recycled IRP down and ignore the return code from IoCallDriver. If
		// this stage gets completed synchronously, this very completion routine will
		// already have been called recursively to deal with it. If not, the IRP is
		// now active again. In either case, we want the current invocation IoCompleteRequest
		// to stop working on this IRP.

		IoCallDriver(pdx->LowerDeviceObject, Irp);
		return STATUS_MORE_PROCESSING_REQUIRED; // halt completion process in its tracks!
		}						// start next stage

	// The request is complete now

FinishCompletion:				// label reached if MDL locking fails
	if (NT_SUCCESS(status))
		Irp->IoStatus.Information = ctx->numxfer;
	else
		{					// had an error
		if (read)
			InterlockedIncrement(&pdx->inerror);
		else
			InterlockedIncrement(&pdx->outerror);

		KdPrint((DRIVERNAME " - %s finished with error %X\n", read ? "Read" : "Write", status));

		if (status != STATUS_CANCELLED)
			{					// recover after I/O error

			// Attempt to recover after an error. Whether or not the attempt succeeds, this IRP
			// is a goner and will eventually get completed with its current error status. Since
			// StartIo is serializing IRPs, we needn't worry about anyone else trying to read or
			// write the device while this is going on. We have to call RecoverFromError at PASSIVE_LEVEL,
			// though. If we were tricky and did that directly from here, we would need to
			// use IoSetCompletionRoutineEx because whatever unload protection our caller has
			// done expires as soon as the main IRP completes (and RecoverFromError completes the
			// main IRP.) Therefore, always queue a work item, which independently protects us
			// from unloading.

			ctx->rcitem = IoAllocateWorkItem(fdo);
			if (!ctx->rcitem)
				KdPrint((DRIVERNAME " - Can't allocate work item for error recovery\n"));
			else
				{			// try to recover
				ctx->Irp = Irp;
				IoQueueWorkItem(ctx->rcitem, (PIO_WORKITEM_ROUTINE) RecoverFromError, CriticalWorkQueue, (PVOID) ctx);
				return STATUS_MORE_PROCESSING_REQUIRED;	// defer completion a while longer
				}			// try to recover
			}					// recover after I/O error
		}					// had an error

	IoFreeMdl(ctx->mdl);
	ExFreePool(ctx);
	StartNextPacket(&pdx->dqReadWrite, fdo);
	IoReleaseRemoveLock(&pdx->RemoveLock, Irp);

	return STATUS_SUCCESS;		// allow IRP to continue completing
	}							// OnReadWriteComplete

///////////////////////////////////////////////////////////////////////////////
// RecoverFromError is called in a system worker thread to attempt recovery
// after a device error.

#pragma PAGEDCODE

VOID RecoverFromError(PDEVICE_OBJECT fdo, PRWCONTEXT ctx)
	{							// RecoverFromError
	PAGED_CODE();
	PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;
	BOOLEAN read = (ctx->UrbBulkOrInterruptTransfer.TransferFlags & USBD_TRANSFER_DIRECTION_IN) != 0;

	KdPrint((DRIVERNAME " - Beginning error recovery\n"));

	// Determine the status of our device

	ULONG portstatus = GetStatus(fdo);

	// If the port is connected but not enabled, try resetting it

	USBD_PIPE_HANDLE hpipe = read ? pdx->hinpipe : pdx->houtpipe;
	if (!(portstatus & USBD_PORT_ENABLED) && (portstatus & USBD_PORT_CONNECTED))
		{						// reset port
		KdPrint((DRIVERNAME " - Error recovery aborting pipe\n"));
		AbortPipe(fdo, hpipe);

		KdPrint((DRIVERNAME " - Error recovery resetting device\n"));
		ResetDevice(fdo);
		}						// reset port

	// Now reset the failed endpoint

	ResetPipe(fdo, hpipe);

	KdPrint((DRIVERNAME " - Finished with error recovery\n"));

	IoFreeWorkItem(ctx->rcitem);

	// Finish completing the failed IRP. Note that our completion routine will
	// not be called this time, so we have to do all the cleanup here

	PIRP Irp = ctx->Irp;
	IoFreeMdl(ctx->mdl);
	ExFreePool(ctx);
	StartNextPacket(&pdx->dqReadWrite, fdo);
	IoReleaseRemoveLock(&pdx->RemoveLock, Irp);
	IoCompleteRequest(Irp, IO_NO_INCREMENT);	// resume completion of failed IRP
	}							// RecoverFromError

///////////////////////////////////////////////////////////////////////////////
// ResetDevice is called during an attempt to recover after an error in order to
// reset the device.

#pragma PAGEDCODE

⌨️ 快捷键说明

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