一本在讲述USB驱动程式的书 及其范例原码

源代码在线查看: control.cpp

软件大小: 10130 K
上传用户: fengkuangyidao
关键词: USB 驱动程式 范例
下载地址: 免注册下载 普通下载 VIP

相关代码

				// Control.cpp -- IOCTL handlers for pnpmon driver
				// Copyright (C) 1999 by Walter Oney
				// All rights reserved
				
				#include "stddcls.h"
				#include 
				#include 
				#include "driver.h"
				#include "ioctls.h"
				
				NTSTATUS Register(PDEVICE_EXTENSION pdx, PREGISTER_PARAMS p);
				NTSTATUS Deregister(PDEVICE_EXTENSION pdx, PUNREGISTER_PARAMS p);
				VOID DeregisterAll(PDEVICE_EXTENSION pdx);
				NTSTATUS OnPnpNotify(PPLUGPLAY_NOTIFICATION_HEADER p, PREG_RECORD reg);
				VOID KillRegRecord(PREG_RECORD reg);
				void SubscribeForTargetNotifications(PDEVICE_INTERFACE_CHANGE_NOTIFICATION q, PREG_RECORD reg);
				
				typedef struct _SUBSCRIBE_CALLBACK_CONTEXT {
					PIO_WORKITEM item;
					PREG_RECORD reg;
					WCHAR name[1];
					} SUBSCRIBE_CALLBACK_CONTEXT, *PSUBSCRIBE_CALLBACK_CONTEXT;
				
				void SubscribeCallback(PDEVICE_OBJECT DeviceObject, PSUBSCRIBE_CALLBACK_CONTEXT context);
				
				///////////////////////////////////////////////////////////////////////////////
				
				#pragma PAGEDCODE
				
				NTSTATUS DispatchControl(PDEVICE_OBJECT fdo, PIRP Irp)
					{							// DispatchControl
					PAGED_CODE();
					PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension;
				
					NTSTATUS status = STATUS_SUCCESS;
					ULONG info = 0;
				
					PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);
					ULONG cbin = stack->Parameters.DeviceIoControl.InputBufferLength;
					ULONG cbout = stack->Parameters.DeviceIoControl.OutputBufferLength;
					ULONG code = stack->Parameters.DeviceIoControl.IoControlCode;
				
					switch (code)
						{						// process request
				
					case IOCTL_REGISTER:				// code == 0x800
						{						// IOCTL_REGISTER
						if (cbin < sizeof(REGISTER_PARAMS))
							{
							status = STATUS_INVALID_PARAMETER;
							break;
							}
						status = Register(pdx, (REGISTER_PARAMS*) Irp->AssociatedIrp.SystemBuffer);
						break;
						}						// IOCTL_REGISTER
				
					case IOCTL_DEREGISTER:				// code == 0x801
						{						// IOCTL_DEREGISTER
						if (cbin < sizeof(UNREGISTER_PARAMS))
							{
							status = STATUS_INVALID_PARAMETER;
							break;
							}
						status = Deregister(pdx, (UNREGISTER_PARAMS*) Irp->AssociatedIrp.SystemBuffer);
						break;
						}						// IOCTL_DEREGISTER
				
					case IOCTL_GETEVENT:				// code == 0x802
						{						// IOCTL_GETEVENT
						if (cbout < sizeof(GETEVENT_PARAMS))
							{
							status = STATUS_INVALID_PARAMETER;
							break;
							}
				
						PLIST_ENTRY list = ExInterlockedRemoveHeadList(&pdx->events, &pdx->eventlock);
						if (!list)
							{
							status = STATUS_NO_MORE_ENTRIES;
							break;
							}
				
						PEVENT_RECORD evrec = CONTAINING_RECORD(list, EVENT_RECORD, ListEntry);
						*(PGETEVENT_PARAMS) Irp->AssociatedIrp.SystemBuffer = evrec->params;
						info = sizeof(GETEVENT_PARAMS);
						ExFreePool(evrec);
						break;
						}						// IOCTL_GETEVENT
				
					default:
						status = STATUS_INVALID_DEVICE_REQUEST;
						break;
				
						}						// process request
				
					return CompleteRequest(Irp, status, info);
					}							// DispatchControl
				
				///////////////////////////////////////////////////////////////////////////////
				
				#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)
						{						// too many opens
						InterlockedDecrement(&pdx->handles);
						status = STATUS_ACCESS_DENIED;
						}						// too many opens
				
					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;
					DeregisterAll(pdx);
					InterlockedDecrement(&pdx->handles);
					return CompleteRequest(Irp, STATUS_SUCCESS, 0);
					}							// DispatchClose
				
				///////////////////////////////////////////////////////////////////////////////
				
				#pragma PAGEDCODE
				
				NTSTATUS Register(PDEVICE_EXTENSION pdx, PREGISTER_PARAMS p)
					{							// Register
					NTSTATUS status;
				
					// Get a pointer to the event object we will set when notifications about
					// the specified GUID occur
					
					PKEVENT event;
					status = ObReferenceObjectByHandle(p->hevent, EVENT_MODIFY_STATE, *ExEventObjectType,
						KernelMode, (PVOID*) &event, NULL);
				
					if (!NT_SUCCESS(status))
						{
						KdPrint((DRIVERNAME " - ObReferenceObjectByHandle failed - %X\n", status));
						return status;
						}
				
					// Create an event registration structure
				
					PREG_RECORD reg = (PREG_RECORD) ExAllocatePool(NonPagedPool, sizeof(REG_RECORD));
				
					if (!reg)
						{
						KdPrint((DRIVERNAME " - unable to allocate %d bytes for registration record\n", sizeof(REG_RECORD)));
						ObDereferenceObject(event);
						return STATUS_INSUFFICIENT_RESOURCES;
						}
				
					RtlZeroMemory(reg, sizeof(REG_RECORD));
					reg->Event = event;
					reg->Guid = p->guid;
					reg->DeviceExtension = pdx;
				
					// Register for PnP notifications
				
					status = IoRegisterPlugPlayNotification(EventCategoryDeviceInterfaceChange,
						PNPNOTIFY_DEVICE_INTERFACE_INCLUDE_EXISTING_INTERFACES,
						&p->guid, pdx->DriverObject,
						(PDRIVER_NOTIFICATION_CALLBACK_ROUTINE) OnPnpNotify, reg, ®->InterfaceNotificationEntry);
					
					if (!NT_SUCCESS(status))
						{
						KdPrint((DRIVERNAME " - IoRegisterPlugPlayNotification failed - %X\n", status));
						ObDereferenceObject(event);
						ExFreePool(reg);
						return status;
						}
				
					// Put the registration record onto our list
				
					InsertTailList(&pdx->registered, ®->ListEntry);
					return STATUS_SUCCESS;
					}							// Register
				
				///////////////////////////////////////////////////////////////////////////////
				
				#pragma PAGEDCODE
				
				NTSTATUS Deregister(PDEVICE_EXTENSION pdx, PUNREGISTER_PARAMS p)
					{							// Deregister
					PLIST_ENTRY list;
					NTSTATUS status = STATUS_SUCCESS;
				
					for (list = pdx->registered.Flink; list != &pdx->registered; )
						{						// for each list element
						PLIST_ENTRY next = list->Flink;
						PREG_RECORD reg = CONTAINING_RECORD(list, REG_RECORD, ListEntry);
						if (reg->Guid == p->guid)
							{					// found a registration record
							RemoveEntryList(list);
							KillRegRecord(reg);
							break;
							}					// found a registration record
						list = next;
						}						// for each list element
				
					return status;
					}							// Deregister
				
				///////////////////////////////////////////////////////////////////////////////
				
				#pragma PAGEDCODE
				
				VOID DeregisterAll(PDEVICE_EXTENSION pdx)
					{							// DeregisterAll
					while (!IsListEmpty(&pdx->registered))
						{						// deregister all notifications
						PLIST_ENTRY next = RemoveHeadList(&pdx->registered);
						PREG_RECORD reg = CONTAINING_RECORD(next, REG_RECORD, ListEntry);
						KillRegRecord(reg);
						}						// deregister all notifications
					}							// DeregisterAll
				
				///////////////////////////////////////////////////////////////////////////////
				
				#pragma PAGEDCODE
				
				VOID KillRegRecord(PREG_RECORD reg)
					{							// KillRegRecord
					if (reg->InterfaceNotificationEntry)
						IoUnregisterPlugPlayNotification(reg->InterfaceNotificationEntry);
					reg->InterfaceNotificationEntry = NULL;
				
					if (reg->HandleNotificationEntry)
						IoUnregisterPlugPlayNotification(reg->HandleNotificationEntry);
					reg->HandleNotificationEntry = NULL;
				
					if (reg->TargetFileReferenced)
						ObDereferenceObject(reg->TargetFile);
					reg->TargetFileReferenced = FALSE;
				
					if (reg->Event)
						ObDereferenceObject(reg->Event);
					reg->Event = NULL;
				
					// Make sure we don't release the registration structure while a
					// work item is outstanding.
				
					if (reg->WorkItemCount == 0)
						ExFreePool(reg);
					else
						reg->DeferredRelease = TRUE;
					}							// KillRegRecord
				
				///////////////////////////////////////////////////////////////////////////////
				
				#pragma PAGEDCODE
				
				NTSTATUS OnPnpNotify(PPLUGPLAY_NOTIFICATION_HEADER p, PREG_RECORD reg)
					{							// OnPnpNotify
					PAGED_CODE();
					NTSTATUS status;
				
					// Report the event
				
				#if DBG
				
					char *msg;
					
					if (p->Event == GUID_DEVICE_INTERFACE_ARRIVAL)
						msg = "arrival";
					else if (p->Event == GUID_DEVICE_INTERFACE_REMOVAL)
						msg = "removal";
					else if (p->Event == GUID_TARGET_DEVICE_QUERY_REMOVE)
						msg = "target query remove";
					else if (p->Event == GUID_TARGET_DEVICE_REMOVE_CANCELLED)
						msg = "target remove cancelled";
					else if (p->Event == GUID_TARGET_DEVICE_REMOVE_COMPLETE)
						msg = "target remove complete";
					else 
						msg = "custom notification";
				
					KdPrint((DRIVERNAME " - PnP notification (%s event)\n", msg));
				
				#endif // DBG
				
					// Create an event record for reporting back to the application
				
					PDEVICE_EXTENSION pdx = reg->DeviceExtension;
					
					PEVENT_RECORD evrec = (PEVENT_RECORD) ExAllocatePool(NonPagedPool, sizeof(EVENT_RECORD));
					if (!evrec)
						{
						KdPrint((DRIVERNAME " - can't allocate %d bytes for event record\n", sizeof(EVENT_RECORD)));
						return STATUS_SUCCESS;
						}
					RtlZeroMemory(evrec, sizeof(EVENT_RECORD));
				
					evrec->params.event = p->Event;
				
					if (p->Event == GUID_DEVICE_INTERFACE_ARRIVAL || p->Event == GUID_DEVICE_INTERFACE_REMOVAL)
						{						// interface notification
						PDEVICE_INTERFACE_CHANGE_NOTIFICATION q = (PDEVICE_INTERFACE_CHANGE_NOTIFICATION) p;
						evrec->params.guid = q->InterfaceClassGuid;
				
						// N.B.: Don't just rely on an ASSERT like the following one in a production
						// driver -- handle the case where the name is too long in the free build too!
				
						ASSERT(q->SymbolicLinkName->Length + 2 LinkName));
						memcpy(reg->LinkName, q->SymbolicLinkName->Buffer, q->SymbolicLinkName->Length);
						reg->LinkName[q->SymbolicLinkName->Length/2] = 0;
						wcscpy(evrec->params.linkname, reg->LinkName);
				
						// For an arrival notification, we want to register for target device change notifications. To
						// do this, we must get a pointer to a file object for the device. Doing *that* entails opening
						// the device, which might cause a deadlock if the target device performs blocking PnP operations.
						// Therefore, arrange to do the open in another thread by queuing a work item.
				
						if (p->Event == GUID_DEVICE_INTERFACE_ARRIVAL)
							SubscribeForTargetNotifications(q, reg);
						}						// interface notification
				
					else if (p->Event == GUID_TARGET_DEVICE_QUERY_REMOVE
						|| p->Event == GUID_TARGET_DEVICE_REMOVE_COMPLETE)
						{						// target device notification
						PTARGET_DEVICE_REMOVAL_NOTIFICATION q = (PTARGET_DEVICE_REMOVAL_NOTIFICATION) p;
						wcscpy(evrec->params.linkname, reg->LinkName);
				
						// You need to close the file object when the query arrives,
						// or else the removal will get cancelled. Makes the idea
						// of getting a remove complete seem kind of silly, eh?
				
						if (reg->TargetFileReferenced)
							{					// dereference target
							if (reg->HandleNotificationEntry)
								IoUnregisterPlugPlayNotification(reg->HandleNotificationEntry);
							reg->HandleNotificationEntry = NULL;
							ObDereferenceObject(reg->TargetFile);
							reg->TargetFile = NULL;
							reg->TargetFileReferenced = FALSE;
							}					// dereference target
						}						// target device notification
				
					else if (p->Event == GUID_TARGET_DEVICE_REMOVE_CANCELLED)
						{						// remove cancelled
						PTARGET_DEVICE_REMOVAL_NOTIFICATION q = (PTARGET_DEVICE_REMOVAL_NOTIFICATION) p;
						wcscpy(evrec->params.linkname, reg->LinkName);
						}						// remove cancelled
				
					else
						{						// custom notification
						PTARGET_DEVICE_CUSTOM_NOTIFICATION q = (PTARGET_DEVICE_CUSTOM_NOTIFICATION) p;
						ASSERT(q->FileObject == reg->TargetFile);
						wcscpy(evrec->params.linkname, reg->LinkName);
						evrec->params.guid = q->Event;
						ULONG datasize = q->Size - FIELD_OFFSET(TARGET_DEVICE_CUSTOM_NOTIFICATION, CustomDataBuffer);
						
						// Don't just rely on an ASSERT for a production driver -- make it work even
						// in the free build!
				
						ASSERT(datasize params.data));
						memcpy(evrec->params.data, q->CustomDataBuffer, datasize);
						evrec->params.datasize = datasize;
						evrec->params.textoffset = q->NameBufferOffset;
						}						// custom notification
				
					ExInterlockedInsertTailList(&pdx->events, &evrec->ListEntry, &pdx->eventlock);
				
					KeSetEvent(reg->Event, IO_NO_INCREMENT, FALSE);
				
					return STATUS_SUCCESS;
					}							// OnPnpNotify
				
				///////////////////////////////////////////////////////////////////////////////
				
				#pragma PAGEDCODE
				
				void SubscribeCallback(PDEVICE_OBJECT DeviceObject, PSUBSCRIBE_CALLBACK_CONTEXT context)
					{							// SubscribeCallback
					PREG_RECORD reg = context->reg;
					PDEVICE_EXTENSION pdx = reg->DeviceExtension;
					InterlockedDecrement(®->WorkItemCount);
					IoFreeWorkItem(context->item);
				
					if (reg->DeferredRelease)
						{						// no-one interested anymore
						KillRegRecord(reg);
						ExFreePool(context);
						return;
						}						// no-one interested anymore
				
					PDEVICE_OBJECT junk;
					UNICODE_STRING name;
					RtlInitUnicodeString(&name, context->name);
					NTSTATUS status = IoGetDeviceObjectPointer(&name, 0, ®->TargetFile, &junk);
					if (NT_SUCCESS(status))
						{				// register for notifications
						reg->TargetFileReferenced = TRUE;
						status = IoRegisterPlugPlayNotification(EventCategoryTargetDeviceChange, 0,
							reg->TargetFile, pdx->DriverObject, (PDRIVER_NOTIFICATION_CALLBACK_ROUTINE) OnPnpNotify,
							reg, ®->HandleNotificationEntry);
						if (!NT_SUCCESS(status))
							{			// error registering
							KdPrint((DRIVERNAME " - IoRegisterPlugPlayNotification failed - %X\n", status));
							ObDereferenceObject(reg->TargetFile);
							reg->TargetFile = NULL;
							reg->TargetFileReferenced = FALSE;
							}			// error registering
						}				// register for notifications
					else
						KdPrint((DRIVERNAME " - IoGetDeviceObjectPointer failed - %X\n", status));
				
					ExFreePool(context);
					}							// SubscribeCallback
				
				///////////////////////////////////////////////////////////////////////////////
				
				#pragma PAGEDCODE
				
				void SubscribeForTargetNotifications(PDEVICE_INTERFACE_CHANGE_NOTIFICATION q, PREG_RECORD reg)
					{							// SubscribeForTargetNotifications
				
					// Use IoAllocateWorkItem so our driver can't disappear from memory while
					// the work item is outstanding. Note that we don't have access to a remove lock
					// in this driver, so we can't just call IoAcquireRemoveLock.
				
					PIO_WORKITEM item = IoAllocateWorkItem(reg->DeviceExtension->DeviceObject);
					if (!item)
						return;
				
					ULONG size = sizeof(SUBSCRIBE_CALLBACK_CONTEXT) + q->SymbolicLinkName->Length;
					PSUBSCRIBE_CALLBACK_CONTEXT context = (PSUBSCRIBE_CALLBACK_CONTEXT) ExAllocatePool(NonPagedPool, size);
					if (!context)
						{
						IoFreeWorkItem(item);
						return;
						}
				
					RtlZeroMemory(context, size);
				
					context->reg = reg;
					context->item = item;
					RtlCopyMemory(context->name, q->SymbolicLinkName->Buffer, q->SymbolicLinkName->Length);
				
					InterlockedIncrement(®->WorkItemCount);
				
					IoQueueWorkItem(item, (PIO_WORKITEM_ROUTINE) SubscribeCallback, DelayedWorkQueue, context);
					}							// SubscribeForTargetNotifications
				
				
							

相关资源